]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'es/some-up-to-date-messages-must-stay'
authorJunio C Hamano <gitster@pobox.com>
Fri, 26 Jan 2024 16:54:45 +0000 (08:54 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 26 Jan 2024 16:54:45 +0000 (08:54 -0800)
Comment updates to help developers not to attempt to modify
messages from plumbing commands that must stay constant.

It might make sense to reassess the plumbing needs every few years,
but that should be done as a separate effort.

* es/some-up-to-date-messages-must-stay:
  messages: mark some strings with "up-to-date" not to touch

1725 files changed:
.cirrus.yml
.clang-format
.gitattributes
.github/workflows/check-whitespace.yml
.github/workflows/coverity.yml [new file with mode: 0644]
.github/workflows/l10n.yml
.github/workflows/main.yml
.gitignore
.gitlab-ci.yml [new file with mode: 0644]
.mailmap
Documentation/.gitignore
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/MyFirstContribution.txt
Documentation/MyFirstObjectWalk.txt
Documentation/RelNotes/1.6.2.txt
Documentation/RelNotes/1.6.3.txt
Documentation/RelNotes/1.6.4.txt
Documentation/RelNotes/1.6.5.txt
Documentation/RelNotes/1.6.6.txt
Documentation/RelNotes/2.30.8.txt
Documentation/RelNotes/2.39.0.txt [new file with mode: 0644]
Documentation/RelNotes/2.39.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.39.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.39.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.40.0.txt [new file with mode: 0644]
Documentation/RelNotes/2.40.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.41.0.txt [new file with mode: 0644]
Documentation/RelNotes/2.42.0.txt [new file with mode: 0644]
Documentation/RelNotes/2.42.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.43.0.txt [new file with mode: 0644]
Documentation/RelNotes/2.44.0.txt [new file with mode: 0644]
Documentation/ReviewingGuidelines.txt
Documentation/SubmittingPatches
Documentation/ToolsForGit.txt
Documentation/asciidoc.conf
Documentation/blame-options.txt
Documentation/build-docdep.perl
Documentation/config.txt
Documentation/config/add.txt
Documentation/config/advice.txt
Documentation/config/alias.txt
Documentation/config/apply.txt
Documentation/config/attr.txt [new file with mode: 0644]
Documentation/config/branch.txt
Documentation/config/bundle.txt [new file with mode: 0644]
Documentation/config/checkout.txt
Documentation/config/clean.txt
Documentation/config/clone.txt
Documentation/config/color.txt
Documentation/config/column.txt
Documentation/config/commit.txt
Documentation/config/core.txt
Documentation/config/credential.txt
Documentation/config/diff.txt
Documentation/config/difftool.txt
Documentation/config/extensions.txt
Documentation/config/fastimport.txt
Documentation/config/feature.txt
Documentation/config/fetch.txt
Documentation/config/format.txt
Documentation/config/fsck.txt
Documentation/config/fsmonitor--daemon.txt [new file with mode: 0644]
Documentation/config/gc.txt
Documentation/config/gpg.txt
Documentation/config/gui.txt
Documentation/config/http.txt
Documentation/config/i18n.txt
Documentation/config/imap.txt
Documentation/config/index.txt
Documentation/config/log.txt
Documentation/config/mailinfo.txt
Documentation/config/maintenance.txt
Documentation/config/man.txt
Documentation/config/merge.txt
Documentation/config/mergetool.txt
Documentation/config/notes.txt
Documentation/config/pack.txt
Documentation/config/push.txt
Documentation/config/rebase.txt
Documentation/config/receive.txt
Documentation/config/rerere.txt
Documentation/config/safe.txt
Documentation/config/sendemail.txt
Documentation/config/sequencer.txt
Documentation/config/splitindex.txt
Documentation/config/stash.txt
Documentation/config/status.txt
Documentation/config/submodule.txt
Documentation/config/trace2.txt
Documentation/config/transfer.txt
Documentation/config/user.txt
Documentation/config/versionsort.txt
Documentation/diff-generate-patch.txt
Documentation/diff-options.txt
Documentation/doc-diff
Documentation/fetch-options.txt
Documentation/fsck-msgids.txt [new file with mode: 0644]
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-annotate.txt
Documentation/git-apply.txt
Documentation/git-archive.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-bisect.txt
Documentation/git-blame.txt
Documentation/git-branch.txt
Documentation/git-bugreport.txt
Documentation/git-bundle.txt
Documentation/git-cat-file.txt
Documentation/git-check-attr.txt
Documentation/git-check-ignore.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout-index.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-clean.txt
Documentation/git-clone.txt
Documentation/git-commit-graph.txt
Documentation/git-commit.txt
Documentation/git-config.txt
Documentation/git-count-objects.txt
Documentation/git-credential-cache--daemon.txt
Documentation/git-credential-cache.txt
Documentation/git-credential-store.txt
Documentation/git-credential.txt
Documentation/git-cvsimport.txt
Documentation/git-cvsserver.txt
Documentation/git-daemon.txt
Documentation/git-describe.txt
Documentation/git-diff-files.txt
Documentation/git-diff-index.txt
Documentation/git-diff-tree.txt
Documentation/git-diff.txt
Documentation/git-difftool.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-fetch-pack.txt
Documentation/git-fetch.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-fsck.txt
Documentation/git-fsmonitor--daemon.txt
Documentation/git-gc.txt
Documentation/git-get-tar-commit-id.txt
Documentation/git-grep.txt
Documentation/git-hash-object.txt
Documentation/git-help.txt
Documentation/git-hook.txt
Documentation/git-http-backend.txt
Documentation/git-http-fetch.txt
Documentation/git-http-push.txt
Documentation/git-imap-send.txt
Documentation/git-index-pack.txt
Documentation/git-init.txt
Documentation/git-interpret-trailers.txt
Documentation/git-log.txt
Documentation/git-ls-files.txt
Documentation/git-ls-remote.txt
Documentation/git-ls-tree.txt
Documentation/git-mailsplit.txt
Documentation/git-maintenance.txt
Documentation/git-merge-base.txt
Documentation/git-merge-file.txt
Documentation/git-merge-tree.txt
Documentation/git-merge.txt
Documentation/git-mergetool--lib.txt
Documentation/git-mergetool.txt
Documentation/git-mktag.txt
Documentation/git-mktree.txt
Documentation/git-mv.txt
Documentation/git-name-rev.txt
Documentation/git-notes.txt
Documentation/git-pack-objects.txt
Documentation/git-pack-redundant.txt
Documentation/git-pack-refs.txt
Documentation/git-patch-id.txt
Documentation/git-prune-packed.txt
Documentation/git-prune.txt
Documentation/git-push.txt
Documentation/git-quiltimport.txt
Documentation/git-range-diff.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-receive-pack.txt
Documentation/git-reflog.txt
Documentation/git-remote-ext.txt
Documentation/git-remote-fd.txt
Documentation/git-repack.txt
Documentation/git-replace.txt
Documentation/git-replay.txt [new file with mode: 0644]
Documentation/git-request-pull.txt
Documentation/git-rerere.txt
Documentation/git-reset.txt
Documentation/git-restore.txt
Documentation/git-rev-list.txt
Documentation/git-rev-parse.txt
Documentation/git-revert.txt
Documentation/git-rm.txt
Documentation/git-send-email.txt
Documentation/git-send-pack.txt
Documentation/git-sh-setup.txt
Documentation/git-shortlog.txt
Documentation/git-show-branch.txt
Documentation/git-show-ref.txt
Documentation/git-show.txt
Documentation/git-sparse-checkout.txt
Documentation/git-stash.txt
Documentation/git-status.txt
Documentation/git-stripspace.txt
Documentation/git-submodule.txt
Documentation/git-switch.txt
Documentation/git-symbolic-ref.txt
Documentation/git-tag.txt
Documentation/git-update-index.txt
Documentation/git-update-ref.txt
Documentation/git-update-server-info.txt
Documentation/git-upload-archive.txt
Documentation/git-upload-pack.txt
Documentation/git-var.txt
Documentation/git-verify-commit.txt
Documentation/git-verify-pack.txt
Documentation/git-verify-tag.txt
Documentation/git-whatchanged.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcli.txt
Documentation/gitcore-tutorial.txt
Documentation/gitcredentials.txt
Documentation/gitdiffcore.txt
Documentation/giteveryday.txt
Documentation/gitformat-bundle.txt
Documentation/gitformat-chunk.txt
Documentation/gitformat-commit-graph.txt
Documentation/gitformat-index.txt
Documentation/gitformat-pack.txt
Documentation/gitformat-signature.txt
Documentation/githooks.txt
Documentation/gitignore.txt
Documentation/gitk.txt
Documentation/gitmodules.txt
Documentation/gitprotocol-capabilities.txt
Documentation/gitprotocol-common.txt
Documentation/gitprotocol-http.txt
Documentation/gitprotocol-pack.txt
Documentation/gitprotocol-v2.txt
Documentation/gitrepository-layout.txt
Documentation/gitsubmodules.txt
Documentation/gittutorial.txt
Documentation/gitweb.conf.txt
Documentation/gitweb.txt
Documentation/glossary-content.txt
Documentation/howto/coordinate-embargoed-releases.txt
Documentation/howto/keep-canonical-history-correct.txt
Documentation/howto/maintain-git.txt
Documentation/howto/new-command.txt
Documentation/howto/use-git-daemon.txt
Documentation/howto/using-merge-subtree.txt
Documentation/i18n.txt
Documentation/lint-fsck-msgids.perl [new file with mode: 0755]
Documentation/manpage-base-url.xsl.in [deleted file]
Documentation/manpage-normal.xsl
Documentation/manpage-quote-apos.xsl [deleted file]
Documentation/merge-options.txt
Documentation/mergetools/vimdiff.txt
Documentation/object-format-disclaimer.txt
Documentation/pretty-formats.txt
Documentation/pretty-options.txt
Documentation/pull-fetch-param.txt
Documentation/ref-storage-format.txt [new file with mode: 0644]
Documentation/rev-list-options.txt
Documentation/revisions.txt
Documentation/scalar.txt
Documentation/signoff-option.txt
Documentation/technical/api-index-skel.txt
Documentation/technical/api-merge.txt
Documentation/technical/api-simple-ipc.txt
Documentation/technical/api-trace2.txt
Documentation/technical/bitmap-format.txt
Documentation/technical/bundle-uri.txt
Documentation/technical/commit-graph.txt
Documentation/technical/hash-function-transition.txt
Documentation/technical/parallel-checkout.txt
Documentation/technical/partial-clone.txt
Documentation/technical/racy-git.txt
Documentation/technical/reftable.txt
Documentation/technical/remembering-renames.txt
Documentation/technical/repository-version.txt
Documentation/technical/rerere.txt
Documentation/technical/sparse-checkout.txt [new file with mode: 0644]
Documentation/technical/unit-tests.txt [new file with mode: 0644]
Documentation/urls-remotes.txt
Documentation/urls.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
INSTALL
Makefile
README.md
RelNotes
abspath.c
abspath.h [new file with mode: 0644]
add-interactive.c
add-patch.c
advice.c
advice.h
alias.c
alias.h
alloc.c
apply.c
apply.h
archive-tar.c
archive-zip.c
archive.c
archive.h
attr.c
attr.h
banned.h
base85.c
base85.h [new file with mode: 0644]
bisect.c
blame.c
blame.h
blob.c
blob.h
bloom.c
branch.c
branch.h
builtin.h
builtin/add.c
builtin/am.c
builtin/apply.c
builtin/archive.c
builtin/bisect.c [moved from builtin/bisect--helper.c with 78% similarity]
builtin/blame.c
builtin/branch.c
builtin/bugreport.c
builtin/bundle.c
builtin/cat-file.c
builtin/check-attr.c
builtin/check-ignore.c
builtin/check-mailmap.c
builtin/check-ref-format.c
builtin/checkout--worker.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/column.c
builtin/commit-graph.c
builtin/commit-tree.c
builtin/commit.c
builtin/config.c
builtin/count-objects.c
builtin/credential-cache--daemon.c
builtin/credential-cache.c
builtin/credential-store.c
builtin/credential.c
builtin/describe.c
builtin/diagnose.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fast-import.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/for-each-repo.c
builtin/fsck.c
builtin/fsmonitor--daemon.c
builtin/gc.c
builtin/get-tar-commit-id.c
builtin/grep.c
builtin/hash-object.c
builtin/help.c
builtin/hook.c
builtin/index-pack.c
builtin/init-db.c
builtin/interpret-trailers.c
builtin/log.c
builtin/ls-files.c
builtin/ls-remote.c
builtin/ls-tree.c
builtin/mailinfo.c
builtin/mailsplit.c
builtin/merge-base.c
builtin/merge-file.c
builtin/merge-index.c
builtin/merge-ours.c
builtin/merge-recursive.c
builtin/merge-tree.c
builtin/merge.c
builtin/mktag.c
builtin/mktree.c
builtin/multi-pack-index.c
builtin/mv.c
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
builtin/prune.c
builtin/pull.c
builtin/push.c
builtin/range-diff.c
builtin/read-tree.c
builtin/rebase.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote-ext.c
builtin/remote-fd.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/replay.c [new file with mode: 0644]
builtin/rerere.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/rm.c
builtin/send-pack.c
builtin/shortlog.c
builtin/show-branch.c
builtin/show-index.c
builtin/show-ref.c
builtin/sparse-checkout.c
builtin/stash.c
builtin/stripspace.c
builtin/submodule--helper.c
builtin/symbolic-ref.c
builtin/tag.c
builtin/unpack-file.c
builtin/unpack-objects.c
builtin/update-index.c
builtin/update-ref.c
builtin/update-server-info.c
builtin/upload-archive.c
builtin/upload-pack.c
builtin/var.c
builtin/verify-commit.c
builtin/verify-pack.c
builtin/verify-tag.c
builtin/worktree.c
builtin/write-tree.c
bulk-checkin.c
bulk-checkin.h
bundle-uri.c
bundle-uri.h
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h [deleted file]
cbtree.c
cbtree.h
chdir-notify.c
checkout.c
checkout.h
chunk-format.c
chunk-format.h
ci/config/README [new file with mode: 0644]
ci/config/allow-ref.sample [deleted file]
ci/install-docker-dependencies.sh
ci/lib.sh
ci/print-test-failures.sh
ci/run-build-and-tests.sh
ci/run-test-slice.sh
color.c
color.h
column.c
combine-diff.c
command-list.txt
commit-graph.c
commit-graph.h
commit-reach.c
commit-reach.h
commit-slab-impl.h
commit.c
commit.h
common-main.c
compat/disk.h
compat/fsmonitor/fsm-darwin-gcc.h
compat/fsmonitor/fsm-health-darwin.c
compat/fsmonitor/fsm-health-win32.c
compat/fsmonitor/fsm-ipc-darwin.c [new file with mode: 0644]
compat/fsmonitor/fsm-ipc-win32.c [new file with mode: 0644]
compat/fsmonitor/fsm-listen-darwin.c
compat/fsmonitor/fsm-listen-win32.c
compat/fsmonitor/fsm-path-utils-darwin.c [new file with mode: 0644]
compat/fsmonitor/fsm-path-utils-win32.c [new file with mode: 0644]
compat/fsmonitor/fsm-settings-darwin.c
compat/fsmonitor/fsm-settings-win32.c
compat/linux/procinfo.c
compat/mingw.c
compat/mingw.h
compat/nonblock.c
compat/pread.c
compat/precompose_utf8.c
compat/regcomp_enhanced.c [new file with mode: 0644]
compat/sha1-chunked.c
compat/simple-ipc/ipc-shared.c
compat/simple-ipc/ipc-unix-socket.c
compat/simple-ipc/ipc-win32.c
compat/terminal.c
compat/win32/headless.c [new file with mode: 0644]
compat/win32/pthread.c
compat/win32/pthread.h
compat/win32/trace2_win32_process_info.c
compat/winansi.c
config.c
config.h
config.mak.uname
configure.ac
connect.c
connect.h
connected.c
connected.h
contrib/README
contrib/buildsystems/CMakeLists.txt
contrib/buildsystems/Generators/Vcxproj.pm
contrib/buildsystems/engine.pl
contrib/coccinelle/.gitignore
contrib/coccinelle/README
contrib/coccinelle/array.cocci
contrib/coccinelle/config_fn_ctx.pending.cocci [new file with mode: 0644]
contrib/coccinelle/git_config_number.cocci [new file with mode: 0644]
contrib/coccinelle/hashmap.cocci
contrib/coccinelle/index-compatibility.cocci [new file with mode: 0644]
contrib/coccinelle/preincr.cocci
contrib/coccinelle/spatchcache [new file with mode: 0755]
contrib/coccinelle/strbuf.cocci
contrib/coccinelle/swap.cocci
contrib/coccinelle/tests/unused.c [deleted file]
contrib/coccinelle/tests/unused.res [deleted file]
contrib/coccinelle/the_repository.cocci [new file with mode: 0644]
contrib/coccinelle/the_repository.pending.cocci [deleted file]
contrib/coccinelle/unused.cocci [deleted file]
contrib/completion/git-completion.bash
contrib/completion/git-prompt.sh
contrib/credential/gnome-keyring/.gitignore [deleted file]
contrib/credential/gnome-keyring/Makefile [deleted file]
contrib/credential/gnome-keyring/git-credential-gnome-keyring.c [deleted file]
contrib/credential/libsecret/.gitignore [new file with mode: 0644]
contrib/credential/libsecret/git-credential-libsecret.c
contrib/credential/netrc/git-credential-netrc.perl
contrib/credential/osxkeychain/git-credential-osxkeychain.c
contrib/credential/wincred/git-credential-wincred.c
contrib/diff-highlight/DiffHighlight.pm
contrib/git-jump/README
contrib/git-jump/git-jump
contrib/mw-to-git/Git/Mediawiki.pm
contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
contrib/subtree/git-subtree.sh
contrib/subtree/git-subtree.txt
contrib/subtree/t/Makefile
contrib/subtree/t/t7900-subtree.sh
convert.c
convert.h
copy.c
copy.h [new file with mode: 0644]
credential.c
credential.h
csum-file.c
csum-file.h
ctype.c
daemon.c
date.c
date.h
decorate.c
decorate.h
delta-islands.c
delta-islands.h
detect-compiler
diagnose.c
diagnose.h
diff-lib.c
diff-merges.c
diff-no-index.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-order.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore-rotate.c
diffcore.h
dir-iterator.c
dir-iterator.h
dir.c
dir.h
editor.c
editor.h [new file with mode: 0644]
entry.c
entry.h
environment.c
environment.h
ewah/bitmap.c
ewah/ewah_bitmap.c
ewah/ewok.h
exec-cmd.c
fetch-pack.c
fetch-pack.h
fmt-merge-msg.c
fmt-merge-msg.h
fsck.c
fsck.h
fsmonitor--daemon.h
fsmonitor-ipc.c
fsmonitor-ipc.h
fsmonitor-ll.h [new file with mode: 0644]
fsmonitor-path-utils.h [new file with mode: 0644]
fsmonitor-settings.c
fsmonitor-settings.h
fsmonitor.c
fsmonitor.h
gettext.c
git-add--interactive.perl [deleted file]
git-archimport.perl
git-bisect.sh [deleted file]
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-difftool--helper.sh
git-gui/Makefile
git-gui/README.md
git-gui/git-gui.sh
git-gui/lib/choose_repository.tcl
git-gui/lib/encoding.tcl
git-gui/lib/shortcut.tcl
git-gui/po/README
git-instaweb.sh
git-mergetool--lib.sh
git-mergetool.sh
git-p4.py
git-request-pull.sh
git-send-email.perl
git-submodule.sh
git-svn.perl
git-zlib.c [moved from zlib.c with 99% similarity]
git-zlib.h [new file with mode: 0644]
git.c
gitk-git/gitk
gitweb/INSTALL
gitweb/gitweb.perl
gitweb/static/gitweb.css
gitweb/static/js/lib/common-lib.js
gpg-interface.c
gpg-interface.h
graph.c
graph.h
grep.c
grep.h
hash-ll.h [new file with mode: 0644]
hash-lookup.c
hash.h
hashmap.c
hashmap.h
help.c
hex-ll.c [new file with mode: 0644]
hex-ll.h [new file with mode: 0644]
hex.c
hex.h [new file with mode: 0644]
hook.c
hook.h
http-backend.c
http-fetch.c
http-push.c
http-walker.c
http.c
http.h
ident.c
ident.h [new file with mode: 0644]
imap-send.c
json-writer.c
json-writer.h
khash.h
kwset.c
kwset.h
levenshtein.c
line-log.c
line-log.h
line-range.c
linear-assignment.c
list-objects-filter-options.c
list-objects-filter-options.h
list-objects-filter.c
list-objects.c
list.h
lockfile.c
log-tree.c
log-tree.h
ls-refs.c
mailinfo.c
mailmap.c
mailmap.h
match-trees.c
match-trees.h [new file with mode: 0644]
mem-pool.c
merge-blobs.c
merge-ll.c [moved from ll-merge.c with 87% similarity]
merge-ll.h [moved from ll-merge.h with 100% similarity]
merge-ort-wrappers.c
merge-ort.c
merge-ort.h
merge-recursive.c
merge-recursive.h
merge.c
merge.h [new file with mode: 0644]
midx.c
midx.h
name-hash.c
name-hash.h [new file with mode: 0644]
negotiator/default.c
negotiator/noop.c
negotiator/skipping.c
neue [new file with mode: 0644]
notes-cache.c
notes-merge.c
notes-utils.c
notes.c
notes.h
object-file.c
object-file.h [new file with mode: 0644]
object-name.c
object-name.h [new file with mode: 0644]
object-store-ll.h [new file with mode: 0644]
object-store.h
object.c
object.h
oid-array.c
oidmap.c
oidmap.h
oidset.c
oidtree.c
oidtree.h
oss-fuzz/.gitignore [new file with mode: 0644]
oss-fuzz/fuzz-commit-graph.c [moved from fuzz-commit-graph.c with 96% similarity]
oss-fuzz/fuzz-date.c [new file with mode: 0644]
oss-fuzz/fuzz-pack-headers.c [moved from fuzz-pack-headers.c with 91% similarity]
oss-fuzz/fuzz-pack-idx.c [moved from fuzz-pack-idx.c with 81% similarity]
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
pack-check.c
pack-mtimes.c
pack-mtimes.h
pack-objects.c
pack-objects.h
pack-revindex.c
pack-revindex.h
pack-write.c
pack.h
packfile.c
packfile.h
pager.c
pager.h [new file with mode: 0644]
parallel-checkout.c
parse-options-cb.c
parse-options.c
parse-options.h
parse.c [new file with mode: 0644]
parse.h [new file with mode: 0644]
patch-ids.c
patch-ids.h
path.c
path.h
pathspec.c
pathspec.h
perl/FromCPAN/Error.pm
perl/Git.pm
perl/Git/I18N.pm
perl/Git/LoadCPAN.pm
perl/Git/LoadCPAN/Error.pm
perl/Git/LoadCPAN/Mail/Address.pm
perl/Git/Packet.pm
perl/Git/SVN.pm
pkt-line.c
pkt-line.h
po/README.md
po/TEAMS
po/bg.po
po/ca.po
po/de.po
po/fr.po
po/id.po
po/ru.po
po/sv.po
po/tr.po
po/uk.po [new file with mode: 0644]
po/zh_CN.po
po/zh_TW.po
preload-index.c
preload-index.h [new file with mode: 0644]
pretty.c
pretty.h
prio-queue.c
progress.c
promisor-remote.c
promisor-remote.h
prompt.c
protocol-caps.c
protocol.c
protocol.h
prune-packed.c
quote.c
quote.h
range-diff.c
reachable.c
read-cache-ll.h [new file with mode: 0644]
read-cache.c
read-cache.h [new file with mode: 0644]
rebase-interactive.c
rebase.c
ref-filter.c
ref-filter.h
reflog-walk.c
reflog-walk.h
reflog.c
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/iterator.c
refs/packed-backend.c
refs/ref-cache.c
refs/ref-cache.h
refs/refs-internal.h
refspec.c
reftable/block.c
reftable/block.h
reftable/block_test.c
reftable/blocksource.c
reftable/dump.c
reftable/error.c
reftable/generic.c
reftable/iter.h
reftable/merged.c
reftable/merged.h
reftable/merged_test.c
reftable/publicbasics.c
reftable/reader.c
reftable/readwrite_test.c
reftable/record.c
reftable/record_test.c
reftable/refname_test.c
reftable/reftable-record.h
reftable/stack.c
reftable/stack_test.c
reftable/system.h
reftable/test_framework.c
reftable/test_framework.h
reftable/tree.c
reftable/tree_test.c
reftable/writer.c
remote-curl.c
remote.c
remote.h
replace-object.c
replace-object.h
repo-settings.c
repository.c
repository.h
rerere.c
rerere.h
reset.c
reset.h
resolve-undo.c
resolve-undo.h
revision.c
revision.h
run-command.c
run-command.h
sane-ctype.h [new file with mode: 0644]
scalar.c
send-pack.c
sequencer.c
sequencer.h
serve.c
server-info.c
server-info.h [new file with mode: 0644]
setup.c
setup.h [new file with mode: 0644]
sh-i18n--envsubst.c
sha1/openssl.h [new file with mode: 0644]
sha1dc/sha1.c
sha1dc_git.c
sha1dc_git.h
sha256/gcrypt.h
sha256/openssl.h [new file with mode: 0644]
shallow.c
shallow.h
shared.mak
shell.c
shortlog.h
sideband.c
sigchain.c
sparse-index.c
sparse-index.h
split-index.c
split-index.h
statinfo.c [new file with mode: 0644]
statinfo.h [new file with mode: 0644]
strbuf.c
strbuf.h
streaming.c
streaming.h
string-list.c
string-list.h
strvec.c
sub-process.c
sub-process.h
submodule-config.c
submodule-config.h
submodule.c
submodule.h
symlinks.c
symlinks.h [new file with mode: 0644]
t/.gitattributes
t/Makefile
t/README
t/aggregate-results.sh
t/annotate-tests.sh
t/chainlint.pl
t/chainlint/blank-line-before-esac.expect
t/chainlint/blank-line.expect
t/chainlint/block-comment.expect
t/chainlint/block.expect
t/chainlint/case-comment.expect
t/chainlint/chain-break-background.expect
t/chainlint/chain-break-return-exit.expect
t/chainlint/chain-break-status.expect
t/chainlint/chained-subshell.expect
t/chainlint/close-subshell.expect
t/chainlint/command-substitution-subsubshell.expect
t/chainlint/comment.expect
t/chainlint/double-here-doc.expect
t/chainlint/dqstring-line-splice.expect
t/chainlint/dqstring-no-interpolate.expect
t/chainlint/empty-here-doc.expect
t/chainlint/exclamation.expect
t/chainlint/for-loop-abbreviated.expect
t/chainlint/for-loop.expect
t/chainlint/function.expect
t/chainlint/here-doc-close-subshell.expect
t/chainlint/here-doc-indent-operator.expect
t/chainlint/here-doc-multi-line-command-subst.expect
t/chainlint/here-doc-multi-line-string.expect
t/chainlint/here-doc.expect
t/chainlint/if-then-else.expect
t/chainlint/incomplete-line.expect
t/chainlint/inline-comment.expect
t/chainlint/loop-detect-status.expect
t/chainlint/nested-cuddled-subshell.expect
t/chainlint/nested-here-doc.expect
t/chainlint/nested-loop-detect-failure.expect
t/chainlint/nested-subshell-comment.expect
t/chainlint/nested-subshell.expect
t/chainlint/pipe.expect
t/chainlint/subshell-here-doc.expect
t/chainlint/subshell-one-liner.expect
t/chainlint/t7900-subtree.expect
t/chainlint/token-pasting.expect
t/chainlint/unclosed-here-doc-indent.expect [new file with mode: 0644]
t/chainlint/unclosed-here-doc-indent.test [new file with mode: 0644]
t/chainlint/unclosed-here-doc.expect [new file with mode: 0644]
t/chainlint/unclosed-here-doc.test [new file with mode: 0644]
t/chainlint/while-loop.expect
t/check-non-portable-shell.pl
t/helper/test-advise.c
t/helper/test-bitmap.c
t/helper/test-bloom.c
t/helper/test-bundle-uri.c [new file with mode: 0644]
t/helper/test-cache-tree.c [new file with mode: 0644]
t/helper/test-chmtime.c
t/helper/test-config.c
t/helper/test-crontab.c
t/helper/test-ctype.c
t/helper/test-date.c
t/helper/test-delta.c
t/helper/test-dir-iterator.c
t/helper/test-drop-caches.c
t/helper/test-dump-cache-tree.c
t/helper/test-dump-fsmonitor.c
t/helper/test-dump-split-index.c
t/helper/test-dump-untracked-cache.c
t/helper/test-env-helper.c [moved from builtin/env--helper.c with 71% similarity]
t/helper/test-example-decorate.c
t/helper/test-fake-ssh.c
t/helper/test-fast-rebase.c [deleted file]
t/helper/test-find-pack.c [new file with mode: 0644]
t/helper/test-fsmonitor-client.c
t/helper/test-genzeros.c
t/helper/test-hash-speed.c
t/helper/test-hash.c
t/helper/test-hashmap.c
t/helper/test-hexdump.c
t/helper/test-index-version.c [deleted file]
t/helper/test-json-writer.c
t/helper/test-lazy-init-name-hash.c
t/helper/test-match-trees.c
t/helper/test-mergesort.c
t/helper/test-oid-array.c
t/helper/test-oidmap.c
t/helper/test-oidtree.c
t/helper/test-online-cpus.c
t/helper/test-pack-mtimes.c
t/helper/test-parse-options.c
t/helper/test-parse-pathspec-file.c
t/helper/test-partial-clone.c
t/helper/test-path-utils.c
t/helper/test-pcre2-config.c
t/helper/test-pkt-line.c
t/helper/test-prio-queue.c
t/helper/test-proc-receive.c
t/helper/test-progress.c
t/helper/test-reach.c
t/helper/test-read-cache.c
t/helper/test-read-graph.c
t/helper/test-read-midx.c
t/helper/test-ref-store.c
t/helper/test-reftable.c
t/helper/test-regex.c
t/helper/test-repository.c
t/helper/test-revision-walking.c
t/helper/test-run-command.c
t/helper/test-scrap-cache-tree.c
t/helper/test-serve-v2.c
t/helper/test-sha1.c
t/helper/test-sha256.c
t/helper/test-sigchain.c
t/helper/test-simple-ipc.c
t/helper/test-strcmp-offset.c
t/helper/test-string-list.c
t/helper/test-submodule-config.c
t/helper/test-submodule-nested-repo-config.c
t/helper/test-submodule.c
t/helper/test-subprocess.c
t/helper/test-tool.c
t/helper/test-tool.h
t/helper/test-trace2.c
t/helper/test-truncate.c [new file with mode: 0644]
t/helper/test-userdiff.c
t/helper/test-wildmatch.c
t/helper/test-write-cache.c
t/helper/test-xml-encode.c
t/interop/interop-lib.sh
t/lib-bundle-uri-protocol.sh [new file with mode: 0644]
t/lib-chunk.sh [new file with mode: 0644]
t/lib-chunk/corrupt-chunk-file.pl [new file with mode: 0644]
t/lib-commit-graph.sh
t/lib-credential.sh
t/lib-diff-alternative.sh
t/lib-gpg.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-httpd/apply-one-time-perl.sh
t/lib-httpd/nph-custom-auth.sh [new file with mode: 0644]
t/lib-httpd/passwd
t/lib-httpd/proxy-passwd [new file with mode: 0644]
t/lib-httpd/ssl.cnf
t/lib-patch-mode.sh
t/lib-rebase.sh
t/lib-submodule-update.sh
t/perf/p0006-read-tree-checkout.sh
t/perf/p0090-cache-tree.sh [new file with mode: 0755]
t/perf/p1500-graph-walks.sh [new file with mode: 0755]
t/perf/p2000-sparse-operations.sh
t/perf/p5312-pack-bitmaps-revs.sh
t/perf/p5332-multi-pack-reuse.sh [new file with mode: 0755]
t/perf/p6300-for-each-ref.sh [new file with mode: 0755]
t/perf/p7102-reset.sh [new file with mode: 0755]
t/perf/p7822-grep-perl-character.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/perf/run
t/t0000-basic.sh
t/t0001-init.sh
t/t0002-gitfile.sh
t/t0003-attributes.sh
t/t0006-date.sh
t/t0007-git-var.sh
t/t0008-ignores.sh
t/t0012-help.sh
t/t0013-sha1dc.sh
t/t0014-alias.sh
t/t0017-env-helper.sh
t/t0020-crlf.sh
t/t0021-conversion.sh
t/t0023-crlf-am.sh
t/t0027-auto-crlf.sh
t/t0028-working-tree-encoding.sh
t/t0030-stripspace.sh
t/t0033-safe-directory.sh
t/t0035-safe-bare-repository.sh
t/t0040-parse-options.sh
t/t0041-usage.sh
t/t0055-beyond-symlinks.sh
t/t0060-path-utils.sh
t/t0061-run-command.sh
t/t0063-string-list.sh
t/t0066-dir-iterator.sh
t/t0068-for-each-repo.sh
t/t0070-fundamental.sh
t/t0080-unit-test-output.sh [new file with mode: 0755]
t/t0081-find-pack.sh [new file with mode: 0755]
t/t0091-bugreport.sh
t/t0100-previous.sh
t/t0202/test.pl
t/t0210-trace2-normal.sh
t/t0211-trace2-perf.sh
t/t0211/scrub_perf.perl
t/t0212-trace2-event.sh
t/t0300-credentials.sh
t/t0301-credential-cache.sh
t/t0303-credential-external.sh
t/t0410-partial-clone.sh
t/t0450-txt-doc-vs-help.sh [new file with mode: 0755]
t/t0450/txt-help-mismatches [new file with mode: 0644]
t/t1001-read-tree-m-2way.sh
t/t1002-read-tree-m-u-2way.sh
t/t1005-read-tree-reset.sh
t/t1006-cat-file.sh
t/t1007-hash-object.sh
t/t1010-mktree.sh
t/t1011-read-tree-sparse-checkout.sh
t/t1022-read-tree-partial-clone.sh
t/t1050-large.sh
t/t1060-object-corruption.sh
t/t1091-sparse-checkout-builtin.sh
t/t1092-sparse-checkout-compatibility.sh
t/t1300-config.sh
t/t1301-shared-repo.sh
t/t1302-repo-version.sh
t/t1304-default-acl.sh
t/t1307-config-blob.sh
t/t1308-config-set.sh
t/t1309-early-config.sh
t/t1310-config-default.sh
t/t1400-update-ref.sh
t/t1401-symbolic-ref.sh
t/t1403-show-ref.sh
t/t1404-update-ref-errors.sh
t/t1408-packed-refs.sh
t/t1409-avoid-packing-refs.sh
t/t1410-reflog.sh
t/t1413-reflog-detach.sh
t/t1416-ref-transaction-hooks.sh
t/t1417-reflog-updateref.sh
t/t1419-exclude-refs.sh [new file with mode: 0755]
t/t1430-bad-ref-name.sh
t/t1450-fsck.sh
t/t1451-fsck-buffer.sh [new file with mode: 0755]
t/t1500-rev-parse.sh
t/t1501-work-tree.sh
t/t1502-rev-parse-parseopt.sh
t/t1502/.gitattributes [new file with mode: 0644]
t/t1502/optionspec-neg [new file with mode: 0644]
t/t1502/optionspec-neg.help [new file with mode: 0644]
t/t1502/optionspec.help [new file with mode: 0755]
t/t1504-ceiling-dirs.sh
t/t1506-rev-parse-diagnosis.sh
t/t1507-rev-parse-upstream.sh
t/t1508-at-combinations.sh
t/t1509-root-work-tree.sh
t/t1512-rev-parse-disambiguation.sh
t/t1514-rev-parse-push.sh
t/t1600-index.sh
t/t1700-split-index.sh
t/t1800-hook.sh
t/t2004-checkout-cache-temp.sh
t/t2005-checkout-index-symlinks.sh
t/t2006-checkout-index-basic.sh
t/t2010-checkout-ambiguous.sh
t/t2011-checkout-invalid-head.sh
t/t2012-checkout-last.sh
t/t2015-checkout-unborn.sh
t/t2016-checkout-patch.sh
t/t2018-checkout-branch.sh
t/t2019-checkout-ambiguous-ref.sh
t/t2020-checkout-detach.sh
t/t2021-checkout-overwrite.sh
t/t2024-checkout-dwim.sh
t/t2025-checkout-no-overlay.sh
t/t2026-checkout-pathspec-file.sh
t/t2027-checkout-track.sh
t/t2030-unresolve-info.sh
t/t2060-switch.sh
t/t2070-restore.sh
t/t2072-restore-pathspec-file.sh
t/t2104-update-index-skip-worktree.sh
t/t2106-update-index-assume-unchanged.sh
t/t2107-update-index-basic.sh
t/t2200-add-update.sh
t/t2203-add-intent.sh
t/t2204-add-ignored.sh
t/t2400-worktree-add.sh
t/t2401-worktree-prune.sh
t/t2402-worktree-list.sh
t/t2403-worktree-move.sh
t/t2406-worktree-repair.sh
t/t2407-worktree-heads.sh
t/t3004-ls-files-basic.sh
t/t3007-ls-files-recurse-submodules.sh
t/t3009-ls-files-others-nonsubmodule.sh
t/t3010-ls-files-killed-modified.sh
t/t3013-ls-files-format.sh
t/t3050-subprojects-fetch.sh
t/t3060-ls-files-with-tree.sh
t/t3070-wildmatch.sh
t/t3101-ls-tree-dirname.sh
t/t3104-ls-tree-format.sh
t/t3200-branch.sh
t/t3202-show-branch.sh
t/t3203-branch-output.sh
t/t3204-branch-name-interpretation.sh
t/t3206-range-diff.sh
t/t3210-pack-refs.sh
t/t3301-notes.sh
t/t3305-notes-fanout.sh
t/t3309-notes-merge-auto-resolve.sh
t/t3310-notes-merge-manual-resolve.sh
t/t3320-notes-merge-worktrees.sh
t/t3321-notes-stripspace.sh [new file with mode: 0755]
t/t3400-rebase.sh
t/t3402-rebase-merge.sh
t/t3403-rebase-skip.sh
t/t3404-rebase-interactive.sh
t/t3405-rebase-malformed.sh
t/t3406-rebase-message.sh
t/t3409-rebase-environ.sh
t/t3412-rebase-root.sh
t/t3413-rebase-hook.sh
t/t3415-rebase-autosquash.sh
t/t3416-rebase-onto-threedots.sh
t/t3418-rebase-continue.sh
t/t3419-rebase-patch-id.sh
t/t3422-rebase-incompatible-options.sh
t/t3423-rebase-reword.sh
t/t3425-rebase-topology-merges.sh
t/t3427-rebase-subtree.sh
t/t3428-rebase-signoff.sh
t/t3429-rebase-edit-todo.sh
t/t3430-rebase-merges.sh
t/t3431-rebase-fork-point.sh
t/t3432-rebase-fast-forward.sh
t/t3433-rebase-across-mode-change.sh
t/t3437-rebase-fixup-options.sh
t/t3438-rebase-broken-files.sh
t/t3500-cherry.sh
t/t3501-revert-cherry-pick.sh
t/t3502-cherry-pick-merge.sh
t/t3503-cherry-pick-root.sh
t/t3506-cherry-pick-ff.sh
t/t3507-cherry-pick-conflict.sh
t/t3510-cherry-pick-sequence.sh
t/t3511-cherry-pick-x.sh
t/t3600-rm.sh
t/t3601-rm-pathspec-file.sh
t/t3650-replay-basics.sh [new file with mode: 0755]
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t3702-add-edit.sh
t/t3704-add-pathspec-file.sh
t/t3800-mktag.sh
t/t3900-i18n-commit.sh
t/t3901-i18n-patch.sh
t/t3903-stash.sh
t/t3905-stash-include-untracked.sh
t/t3909-stash-pathspec-file.sh
t/t3920-crlf-messages.sh
t/t4000-diff-format.sh
t/t4001-diff-rename.sh
t/t4002-diff-basic.sh
t/t4003-diff-rename-1.sh
t/t4004-diff-rename-symlink.sh
t/t4012-diff-binary.sh
t/t4013-diff-various.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4017-diff-retval.sh
t/t4018-diff-funcname.sh
t/t4018/java-class-brace-on-separate-line [new file with mode: 0644]
t/t4018/java-class-space-before-type-parameters [new file with mode: 0644]
t/t4018/java-class-type-parameters [new file with mode: 0644]
t/t4018/java-class-type-parameters-implements [new file with mode: 0644]
t/t4018/java-interface-type-parameters [new file with mode: 0644]
t/t4018/java-interface-type-parameters-extends [new file with mode: 0644]
t/t4018/java-non-sealed [new file with mode: 0644]
t/t4018/java-record [new file with mode: 0644]
t/t4018/java-record-space-before-components [new file with mode: 0644]
t/t4018/java-record-type-parameters [new file with mode: 0644]
t/t4018/java-sealed [new file with mode: 0644]
t/t4018/java-sealed-permits [new file with mode: 0644]
t/t4018/java-sealed-type-parameters [new file with mode: 0644]
t/t4018/java-sealed-type-parameters-implements-permits [new file with mode: 0644]
t/t4018/java-sealed-type-parameters-permits [new file with mode: 0644]
t/t4022-diff-rewrite.sh
t/t4023-diff-rename-typechange.sh
t/t4031-diff-rewrite-binary.sh
t/t4034-diff-words.sh
t/t4038-diff-combined.sh
t/t4040-whitespace-status.sh
t/t4044-diff-index-unique-abbrev.sh
t/t4045-diff-relative.sh
t/t4046-diff-unmerged.sh
t/t4047-diff-dirstat.sh
t/t4052-stat-output.sh
t/t4053-diff-no-index.sh
t/t4054-diff-bogus-tree.sh
t/t4055-diff-context.sh
t/t4058-diff-duplicates.sh
t/t4062-diff-pickaxe.sh
t/t4067-diff-partial-clone.sh
t/t4068-diff-symmetric-merge-base.sh
t/t4111-apply-subdir.sh
t/t4115-apply-symlink.sh
t/t4120-apply-popt.sh
t/t4122-apply-symlink-inside.sh
t/t4129-apply-samemode.sh
t/t4133-apply-filenames.sh
t/t4135-apply-weird-filenames.sh
t/t4141-apply-too-large.sh [new file with mode: 0755]
t/t4150-am.sh
t/t4151-am-abort.sh
t/t4152-am-subjects.sh
t/t4153-am-resume-override-opts.sh
t/t4200-rerere.sh
t/t4201-shortlog.sh
t/t4202-log.sh
t/t4203-mailmap.sh
t/t4204-patch-id.sh
t/t4205-log-pretty-formats.sh
t/t4206-log-follow-harder-copies.sh
t/t4207-log-decoration-colors.sh
t/t4208-log-magic-pathspec.sh
t/t4209-log-pickaxe.sh
t/t4211-line-log.sh
t/t4212-log-corrupt.sh
t/t4213-log-tabexpand.sh
t/t4214-log-graph-octopus.sh
t/t4215-log-skewed-merges.sh
t/t4216-log-bloom.sh
t/t4217-log-limit.sh
t/t4254-am-corrupt.sh
t/t4256-am-format-flowed.sh
t/t4257-am-interactive.sh
t/t4258/mbox
t/t4300-merge-tree.sh
t/t4301-merge-tree-write-tree.sh
t/t5000-tar-tree.sh
t/t5001-archive-attr.sh
t/t5004-archive-corner-cases.sh
t/t5100-mailinfo.sh
t/t5100/comment.expect
t/t5100/comment.in
t/t5100/msg0002
t/t5100/msg0003
t/t5100/msg0012--message-id
t/t5100/quoted-cr.mbox
t/t5100/sample.mbox
t/t5300-pack-object.sh
t/t5301-sliding-window.sh
t/t5302-pack-index.sh
t/t5303-pack-corruption-resilience.sh
t/t5304-prune.sh
t/t5306-pack-nobase.sh
t/t5310-pack-bitmaps.sh
t/t5311-pack-bitmaps-shallow.sh
t/t5312-prune-corruption.sh
t/t5313-pack-bounds-checks.sh
t/t5314-pack-cycle-detection.sh
t/t5317-pack-objects-filter-objects.sh
t/t5318-commit-graph.sh
t/t5319-multi-pack-index.sh
t/t5320-delta-islands.sh
t/t5324-split-commit-graph.sh
t/t5325-reverse-index.sh
t/t5326-multi-pack-bitmaps.sh
t/t5328-commit-graph-64bit-time.sh
t/t5329-pack-objects-cruft.sh
t/t5330-no-lazy-fetch-with-commit-graph.sh
t/t5331-pack-objects-stdin.sh [new file with mode: 0755]
t/t5332-multi-pack-reuse.sh [new file with mode: 0755]
t/t5351-unpack-large-objects.sh
t/t5401-update-hooks.sh
t/t5403-post-checkout-hook.sh
t/t5404-tracking-branches.sh
t/t5405-send-pack-rewind.sh
t/t5406-remote-rejects.sh
t/t5407-post-rewrite-hook.sh
t/t5411/test-0026-push-options.sh
t/t5411/test-0027-push-options--porcelain.sh
t/t5500-fetch-pack.sh
t/t5502-quickfetch.sh
t/t5504-fetch-receive-strict.sh
t/t5505-remote.sh
t/t5506-remote-groups.sh
t/t5507-remote-environment.sh
t/t5510-fetch.sh
t/t5512-ls-remote.sh
t/t5514-fetch-multiple.sh
t/t5516-fetch-push.sh
t/t5517-push-mirror.sh
t/t5520-pull.sh
t/t5521-pull-options.sh
t/t5522-pull-symlink.sh
t/t5523-push-upstream.sh
t/t5525-fetch-tagopt.sh
t/t5526-fetch-submodules.sh
t/t5527-fetch-odd-refs.sh
t/t5528-push-default.sh
t/t5529-push-errors.sh
t/t5530-upload-pack-error.sh
t/t5531-deep-submodule-push.sh
t/t5534-push-signed.sh
t/t5536-fetch-conflicts.sh
t/t5541-http-push-smart.sh
t/t5543-atomic-push.sh
t/t5544-pack-objects-hook.sh
t/t5545-push-options.sh
t/t5546-receive-limits.sh
t/t5547-push-quarantine.sh
t/t5550-http-fetch-dumb.sh
t/t5551-http-fetch-smart.sh
t/t5552-skipping-fetch-negotiator.sh
t/t5554-noop-fetch-negotiator.sh
t/t5558-clone-bundle-uri.sh
t/t5559-http-fetch-smart-http2.sh [new file with mode: 0755]
t/t5560-http-backend-noserver.sh
t/t5561-http-backend.sh
t/t5562-http-backend-content-length.sh
t/t5562/invoke-with-content-length.pl
t/t5563-simple-http-auth.sh [new file with mode: 0755]
t/t5564-http-proxy.sh [new file with mode: 0755]
t/t5570-git-daemon.sh
t/t5571-pre-push-hook.sh
t/t5572-pull-submodule.sh
t/t5573-pull-verify-signatures.sh
t/t5574-fetch-output.sh [new file with mode: 0755]
t/t5580-unc-paths.sh
t/t5583-push-branches.sh [new file with mode: 0755]
t/t5601-clone.sh
t/t5604-clone-reference.sh
t/t5605-clone-local.sh
t/t5606-clone-options.sh
t/t5607-clone-bundle.sh
t/t5610-clone-detached.sh
t/t5611-clone-config.sh
t/t5613-info-alternate.sh
t/t5614-clone-submodules-shallow.sh
t/t5616-partial-clone.sh
t/t5617-clone-submodules-remote.sh
t/t5618-alternate-refs.sh
t/t5700-protocol-v1.sh
t/t5701-git-serve.sh
t/t5702-protocol-v2.sh
t/t5703-upload-pack-ref-in-want.sh
t/t5704-protocol-violations.sh
t/t5705-session-id-in-capabilities.sh
t/t5730-protocol-v2-bundle-uri-file.sh [new file with mode: 0755]
t/t5731-protocol-v2-bundle-uri-git.sh [new file with mode: 0755]
t/t5732-protocol-v2-bundle-uri-http.sh [new file with mode: 0755]
t/t5750-bundle-uri-parse.sh [new file with mode: 0755]
t/t5801-remote-helpers.sh
t/t5810-proto-disable-local.sh
t/t5811-proto-disable-git.sh
t/t5812-proto-disable-http.sh
t/t5813-proto-disable-ssh.sh
t/t6000-rev-list-misc.sh
t/t6001-rev-list-graft.sh
t/t6003-rev-list-topo-order.sh
t/t6005-rev-list-count.sh
t/t6006-rev-list-format.sh
t/t6009-rev-list-parent.sh
t/t6011-rev-list-with-bad-commit.sh
t/t6014-rev-list-all.sh
t/t6017-rev-list-stdin.sh
t/t6018-rev-list-glob.sh
t/t6020-bundle-misc.sh
t/t6021-rev-list-exclude-hidden.sh [new file with mode: 0755]
t/t6022-rev-list-missing.sh [new file with mode: 0755]
t/t6030-bisect-porcelain.sh
t/t6040-tracking-info.sh
t/t6050-replace.sh
t/t6060-merge-index.sh
t/t6102-rev-list-unexpected-objects.sh
t/t6112-rev-list-filters-objects.sh
t/t6113-rev-list-bitmap-filters.sh
t/t6115-rev-list-du.sh
t/t6120-describe.sh
t/t6132-pathspec-exclude.sh
t/t6134-pathspec-in-submodule.sh
t/t6135-pathspec-with-attrs.sh
t/t6136-pathspec-in-bare.sh
t/t6300-for-each-ref.sh
t/t6301-for-each-ref-errors.sh
t/t6302-for-each-ref-filter.sh
t/t6401-merge-criss-cross.sh
t/t6402-merge-rename.sh
t/t6403-merge-file.sh
t/t6406-merge-attr.sh
t/t6407-merge-binary.sh
t/t6415-merge-dir-to-symlink.sh
t/t6416-recursive-corner-cases.sh
t/t6421-merge-partial-clone.sh
t/t6422-merge-rename-corner-cases.sh
t/t6423-merge-rename-directories.sh
t/t6424-merge-unrelated-index-changes.sh
t/t6425-merge-rename-delete.sh
t/t6426-merge-skip-unneeded-updates.sh
t/t6429-merge-sequence-rename-caching.sh
t/t6430-merge-recursive.sh
t/t6433-merge-toplevel.sh
t/t6435-merge-sparse.sh
t/t6436-merge-overwrite.sh
t/t6437-submodule-merge.sh
t/t6439-merge-co-error-msgs.sh
t/t6500-gc.sh
t/t6501-freshen-objects.sh
t/t6600-test-reach.sh
t/t6700-tree-depth.sh [new file with mode: 0755]
t/t7001-mv.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7030-verify-tag.sh
t/t7031-verify-tag-signed-ssh.sh
t/t7101-reset-empty-subdirs.sh
t/t7102-reset.sh
t/t7103-reset-bare.sh
t/t7105-reset-patch.sh
t/t7106-reset-unborn-branch.sh
t/t7107-reset-pathspec-file.sh
t/t7110-reset-merge.sh
t/t7111-reset-table.sh
t/t7201-co.sh
t/t7300-clean.sh
t/t7301-clean-interactive.sh
t/t7400-submodule-basic.sh
t/t7402-submodule-rebase.sh
t/t7403-submodule-sync.sh
t/t7406-submodule-update.sh
t/t7407-submodule-foreach.sh
t/t7409-submodule-detached-work-tree.sh
t/t7411-submodule-config.sh
t/t7412-submodule-absorbgitdirs.sh
t/t7413-submodule-is-active.sh
t/t7414-submodule-mistakes.sh
t/t7416-submodule-dash-url.sh
t/t7417-submodule-path-url.sh
t/t7418-submodule-sparse-gitmodules.sh
t/t7419-submodule-set-branch.sh
t/t7420-submodule-set-url.sh
t/t7422-submodule-output.sh [new file with mode: 0755]
t/t7450-bad-git-dotfiles.sh
t/t7500-commit-template-squash-signoff.sh
t/t7501-commit-basic-functionality.sh
t/t7502-commit-porcelain.sh
t/t7504-commit-msg-hook.sh
t/t7506-status-submodule.sh
t/t7507-commit-verbose.sh
t/t7508-status.sh
t/t7509-commit-authorship.sh
t/t7510-signed-commit.sh
t/t7512-status-help.sh
t/t7513-interpret-trailers.sh
t/t7516-commit-races.sh
t/t7517-per-repo-email.sh
t/t7518-ident-corner-cases.sh
t/t7519-status-fsmonitor.sh
t/t7520-ignored-hook-warning.sh
t/t7525-status-rename.sh
t/t7526-commit-pathspec-file.sh
t/t7527-builtin-fsmonitor.sh
t/t7528-signed-commit-ssh.sh
t/t7600-merge.sh
t/t7601-merge-pull-config.sh
t/t7602-merge-octopus-many.sh
t/t7603-merge-reduce-heads.sh
t/t7605-merge-resolve.sh
t/t7607-merge-state.sh
t/t7608-merge-messages.sh
t/t7610-mergetool.sh
t/t7611-merge-abort.sh
t/t7612-merge-verify-signatures.sh
t/t7614-merge-signoff.sh
t/t7700-repack.sh
t/t7701-repack-unpack-unreachable.sh
t/t7703-repack-geometric.sh
t/t7704-repack-cruft.sh [new file with mode: 0755]
t/t7800-difftool.sh
t/t7810-grep.sh
t/t7811-grep-open.sh
t/t7814-grep-recurse-submodules.sh
t/t7816-grep-binary-pattern.sh
t/t7900-maintenance.sh
t/t8003-blame-corner-cases.sh
t/t8013-blame-ignore-revs.sh
t/t9001-send-email.sh
t/t9002-column.sh
t/t9003-help-autocorrect.sh
t/t9004-example.sh
t/t9100-git-svn-basic.sh
t/t9104-git-svn-follow-parent.sh
t/t9106-git-svn-commit-diff-clobber.sh
t/t9114-git-svn-dcommit-merge.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9119-git-svn-info.sh
t/t9133-git-svn-nested-git-repo.sh
t/t9134-git-svn-ignore-paths.sh
t/t9140-git-svn-reset.sh
t/t9146-git-svn-empty-dirs.sh
t/t9147-git-svn-include-paths.sh
t/t9148-git-svn-propset.sh
t/t9160-git-svn-preserve-empty-dirs.sh
t/t9164-git-svn-dcommit-concurrent.sh
t/t9200-git-cvsexportcommit.sh
t/t9210-scalar.sh
t/t9211-scalar-clone.sh
t/t9300-fast-import.sh
t/t9304-fast-import-marks.sh
t/t9350-fast-export.sh
t/t9351-fast-export-anonymize.sh
t/t9400-git-cvsserver-server.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9700-perl-git.sh
t/t9700/test.pl
t/t9800-git-p4-basic.sh
t/t9801-git-p4-branch.sh
t/t9807-git-p4-submit.sh
t/t9814-git-p4-rename.sh
t/t9815-git-p4-submit-fail.sh
t/t9816-git-p4-locked.sh
t/t9902-completion.sh
t/t9903-bash-prompt.sh
t/test-lib-functions.sh
t/test-lib-github-workflow-markup.sh
t/test-lib-junit.sh
t/test-lib.sh
t/test-terminal.perl
t/unit-tests/.gitignore [new file with mode: 0644]
t/unit-tests/t-basic.c [new file with mode: 0644]
t/unit-tests/t-mem-pool.c [new file with mode: 0644]
t/unit-tests/t-strbuf.c [new file with mode: 0644]
t/unit-tests/test-lib.c [new file with mode: 0644]
t/unit-tests/test-lib.h [new file with mode: 0644]
t/valgrind/valgrind.sh
tag.c
tempfile.c
templates/hooks--pre-commit.sample
templates/hooks--sendemail-validate.sample [new file with mode: 0755]
thread-utils.c
tmp-objdir.c
tmp-objdir.h
trace.c
trace.h
trace2.c
trace2.h
trace2/tr2_cfg.c
trace2/tr2_cmd_name.c
trace2/tr2_ctr.c [new file with mode: 0644]
trace2/tr2_ctr.h [new file with mode: 0644]
trace2/tr2_dst.c
trace2/tr2_sid.c
trace2/tr2_sysenv.c
trace2/tr2_tbuf.c
trace2/tr2_tgt.h
trace2/tr2_tgt_event.c
trace2/tr2_tgt_normal.c
trace2/tr2_tgt_perf.c
trace2/tr2_tls.c
trace2/tr2_tls.h
trace2/tr2_tmr.c [new file with mode: 0644]
trace2/tr2_tmr.h [new file with mode: 0644]
trailer.c
trailer.h
transport-helper.c
transport-internal.h
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unicode-width.h
unix-socket.c
unix-stream-server.c
unpack-trees.c
unpack-trees.h
upload-pack.c
url.c
urlmatch.c
urlmatch.h
usage.c
userdiff.c
userdiff.h
utf8.c
utf8.h
versioncmp.c
versioncmp.h [new file with mode: 0644]
walker.c
wildmatch.c
wildmatch.h
worktree.c
worktree.h
wrapper.c
wrapper.h [new file with mode: 0644]
write-or-die.c
write-or-die.h [new file with mode: 0644]
ws.c
ws.h [new file with mode: 0644]
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
xdiff/xdiffi.c
xdiff/xemit.c

index 4860bebd32f8d3f34c2382f097ac50c0b972d3a0..b6280692d2f21aaa17548985979279594c94323e 100644 (file)
@@ -19,4 +19,4 @@ freebsd_12_task:
   build_script:
     - su git -c gmake
   test_script:
-    - su git -c 'gmake test'
+    - su git -c 'gmake DEFAULT_UNIT_TEST_TARGET=unit-tests-prove test unit-tests'
index c592dda681fecfaa6bf64fb3f539eafaf4123ed8..3ed4fac753a7c119bb988918cf8f8eb18ac926d9 100644 (file)
@@ -83,9 +83,9 @@ BinPackParameters: true
 BreakBeforeBraces: Linux
 
 # Break after operators
-# int valuve = aaaaaaaaaaaaa +
-#              bbbbbb -
-#              ccccccccccc;
+# int value = aaaaaaaaaaaaa +
+#             bbbbbb -
+#             ccccccccccc;
 BreakBeforeBinaryOperators: None
 BreakBeforeTernaryOperators: false
 
index b0044cf272fec9b987e99c600d6a95bc357261c3..158c3d45c4c10cb19c7d3fb02d6bfdaf7a1794f1 100644 (file)
@@ -1,17 +1,17 @@
 * whitespace=!indent,trail,space
 *.[ch] whitespace=indent,trail,space diff=cpp
-*.sh whitespace=indent,trail,space eol=lf
-*.perl eol=lf diff=perl
-*.pl eof=lf diff=perl
-*.pm eol=lf diff=perl
-*.py eol=lf diff=python
-*.bat eol=crlf
+*.sh whitespace=indent,trail,space text eol=lf
+*.perl text eol=lf diff=perl
+*.pl text eof=lf diff=perl
+*.pm text eol=lf diff=perl
+*.py text eol=lf diff=python
+*.bat text eol=crlf
 CODE_OF_CONDUCT.md -whitespace
-/Documentation/**/*.txt eol=lf
-/command-list.txt eol=lf
-/GIT-VERSION-GEN eol=lf
-/mergetools/* eol=lf
-/t/oid-info/* eol=lf
+/Documentation/**/*.txt text eol=lf
+/command-list.txt text eol=lf
+/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
index ad3466ad16e36cda7e6218811163adfbde74d266..a58e2dc8ad6279d792635c4034ed0d113d489695 100644 (file)
@@ -9,42 +9,83 @@ on:
   pull_request:
     types: [opened, synchronize]
 
+# Avoid unnecessary builds. Unlike the main CI jobs, these are not
+# ci-configurable (but could be).
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
 jobs:
   check-whitespace:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
       with:
         fetch-depth: 0
 
     - name: git log --check
       id: check_out
       run: |
-        log=
+        baseSha=${{github.event.pull_request.base.sha}}
+        problems=()
         commit=
-        while read dash etc
+        commitText=
+        commitTextmd=
+        goodparent=
+        while read dash sha etc
         do
           case "${dash}" in
           "---")
-            commit="${etc}"
+            if test -z "${commit}"
+            then
+              goodparent=${sha}
+            fi
+            commit="${sha}"
+            commitText="${sha} ${etc}"
+            commitTextmd="[${sha}](https://github.com/${{ github.repository }}/commit/${sha}) ${etc}"
             ;;
           "")
             ;;
           *)
             if test -n "${commit}"
             then
-              log="${log}\n${commit}"
+              problems+=("1) --- ${commitTextmd}")
               echo ""
-              echo "--- ${commit}"
+              echo "--- ${commitText}"
+              commit=
             fi
-            commit=
-            log="${log}\n${dash} ${etc}"
-            echo "${dash} ${etc}"
+            case "${dash}" in
+            *:[1-9]*:) # contains file and line number information
+              dashend=${dash#*:}
+              problems+=("[${dash}](https://github.com/${{ github.repository }}/blob/${{github.event.pull_request.head.ref}}/${dash%%:*}#L${dashend%:}) ${sha} ${etc}")
+              ;;
+            *)
+              problems+=("\`${dash} ${sha} ${etc}\`")
+              ;;
+            esac
+            echo "${dash} ${sha} ${etc}"
             ;;
           esac
-        done <<< $(git log --check --pretty=format:"---% h% s" ${{github.event.pull_request.base.sha}}..)
+        done <<< $(git log --check --pretty=format:"---% h% s" ${baseSha}..)
 
-        if test -n "${log}"
+        if test ${#problems[*]} -gt 0
         then
+          if test -z "${commit}"
+          then
+            goodparent=${baseSha: 0:7}
+          fi
+          echo "🛑 Please review the Summary output for further information."
+          echo "### :x: A whitespace issue was found in one or more of the commits." >$GITHUB_STEP_SUMMARY
+          echo "" >>$GITHUB_STEP_SUMMARY
+          echo "Run these commands to correct the problem:" >>$GITHUB_STEP_SUMMARY
+          echo "1. \`git rebase --whitespace=fix ${goodparent}\`" >>$GITHUB_STEP_SUMMARY
+          echo "1. \`git push --force\`" >>$GITHUB_STEP_SUMMARY
+          echo " " >>$GITHUB_STEP_SUMMARY
+          echo "Errors:" >>$GITHUB_STEP_SUMMARY
+          for i in "${problems[@]}"
+          do
+            echo "${i}" >>$GITHUB_STEP_SUMMARY
+          done
+
           exit 2
         fi
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
new file mode 100644 (file)
index 0000000..e5532d3
--- /dev/null
@@ -0,0 +1,163 @@
+name: Coverity
+
+# This GitHub workflow automates submitting builds to Coverity Scan. To enable it,
+# set the repository variable `ENABLE_COVERITY_SCAN_FOR_BRANCHES` (for details, see
+# https://docs.github.com/en/actions/learn-github-actions/variables) to a JSON
+# string array containing the names of the branches for which the workflow should be
+# run, e.g. `["main", "next"]`.
+#
+# In addition, two repository secrets must be set (for details how to add secrets, see
+# https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions):
+# `COVERITY_SCAN_EMAIL` and `COVERITY_SCAN_TOKEN`. The former specifies the
+# email to which the Coverity reports should be sent and the latter can be
+# obtained from the Project Settings tab of the Coverity project).
+#
+# The workflow runs on `ubuntu-latest` by default. This can be overridden by setting
+# the repository variable `ENABLE_COVERITY_SCAN_ON_OS` to a JSON string array specifying
+# the operating systems, e.g. `["ubuntu-latest", "windows-latest"]`.
+#
+# By default, the builds are submitted to the Coverity project `git`. To override this,
+# set the repository variable `COVERITY_PROJECT`.
+
+on:
+  push:
+
+defaults:
+  run:
+    shell: bash
+
+jobs:
+  coverity:
+    if: contains(fromJSON(vars.ENABLE_COVERITY_SCAN_FOR_BRANCHES || '[""]'), github.ref_name)
+    strategy:
+      matrix:
+        os: ${{ fromJSON(vars.ENABLE_COVERITY_SCAN_ON_OS || '["ubuntu-latest"]') }}
+    runs-on: ${{ matrix.os }}
+    env:
+      COVERITY_PROJECT: ${{ vars.COVERITY_PROJECT || 'git' }}
+      COVERITY_LANGUAGE: cxx
+      COVERITY_PLATFORM: overridden-below
+    steps:
+      - uses: actions/checkout@v3
+      - name: install minimal Git for Windows SDK
+        if: contains(matrix.os, 'windows')
+        uses: git-for-windows/setup-git-for-windows-sdk@v1
+      - run: ci/install-dependencies.sh
+        if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos')
+        env:
+          runs_on_pool: ${{ matrix.os }}
+
+      # The Coverity site says the tool is usually updated twice yearly, so the
+      # MD5 of download can be used to determine whether there's been an update.
+      - name: get the Coverity Build Tool hash
+        id: lookup
+        run: |
+          case "${{ matrix.os }}" in
+          *windows*)
+            COVERITY_PLATFORM=win64
+            COVERITY_TOOL_FILENAME=cov-analysis.zip
+            MAKEFLAGS=-j$(nproc)
+            ;;
+          *macos*)
+            COVERITY_PLATFORM=macOSX
+            COVERITY_TOOL_FILENAME=cov-analysis.dmg
+            MAKEFLAGS=-j$(sysctl -n hw.physicalcpu)
+            ;;
+          *ubuntu*)
+            COVERITY_PLATFORM=linux64
+            COVERITY_TOOL_FILENAME=cov-analysis.tgz
+            MAKEFLAGS=-j$(nproc)
+            ;;
+          *)
+            echo '::error::unhandled OS ${{ matrix.os }}' >&2
+            exit 1
+            ;;
+          esac
+          echo "COVERITY_PLATFORM=$COVERITY_PLATFORM" >>$GITHUB_ENV
+          echo "COVERITY_TOOL_FILENAME=$COVERITY_TOOL_FILENAME" >>$GITHUB_ENV
+          echo "MAKEFLAGS=$MAKEFLAGS" >>$GITHUB_ENV
+          MD5=$(curl https://scan.coverity.com/download/$COVERITY_LANGUAGE/$COVERITY_PLATFORM \
+                   --fail \
+                   --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+                   --form project="$COVERITY_PROJECT" \
+                   --form md5=1)
+          case $? in
+          0) ;; # okay
+          22) # 40x, i.e. access denied
+            echo "::error::incorrect token or project?" >&2
+            exit 1
+            ;;
+          *) # other error
+            echo "::error::Failed to retrieve MD5" >&2
+            exit 1
+            ;;
+          esac
+          echo "hash=$MD5" >>$GITHUB_OUTPUT
+
+      # Try to cache the tool to avoid downloading 1GB+ on every run.
+      # A cache miss will add ~30s to create, but a cache hit will save minutes.
+      - name: restore the Coverity Build Tool
+        id: cache
+        uses: actions/cache/restore@v3
+        with:
+          path: ${{ runner.temp }}/cov-analysis
+          key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
+      - name: download the Coverity Build Tool (${{ env.COVERITY_LANGUAGE }} / ${{ env.COVERITY_PLATFORM}})
+        if: steps.cache.outputs.cache-hit != 'true'
+        run: |
+          curl https://scan.coverity.com/download/$COVERITY_LANGUAGE/$COVERITY_PLATFORM \
+            --fail --no-progress-meter \
+            --output $RUNNER_TEMP/$COVERITY_TOOL_FILENAME \
+            --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+            --form project="$COVERITY_PROJECT"
+      - name: extract the Coverity Build Tool
+        if: steps.cache.outputs.cache-hit != 'true'
+        run: |
+          case "$COVERITY_TOOL_FILENAME" in
+          *.tgz)
+            mkdir $RUNNER_TEMP/cov-analysis &&
+            tar -xzf $RUNNER_TEMP/$COVERITY_TOOL_FILENAME --strip 1 -C $RUNNER_TEMP/cov-analysis
+            ;;
+          *.dmg)
+            cd $RUNNER_TEMP &&
+            attach="$(hdiutil attach $COVERITY_TOOL_FILENAME)" &&
+            volume="$(echo "$attach" | cut -f 3 | grep /Volumes/)" &&
+            mkdir cov-analysis &&
+            cd cov-analysis &&
+            sh "$volume"/cov-analysis-macosx-*.sh &&
+            ls -l &&
+            hdiutil detach "$volume"
+            ;;
+          *.zip)
+            cd $RUNNER_TEMP &&
+            mkdir cov-analysis-tmp &&
+            unzip -d cov-analysis-tmp $COVERITY_TOOL_FILENAME &&
+            mv cov-analysis-tmp/* cov-analysis
+            ;;
+          *)
+            echo "::error::unhandled archive type: $COVERITY_TOOL_FILENAME" >&2
+            exit 1
+            ;;
+          esac
+      - name: cache the Coverity Build Tool
+        if: steps.cache.outputs.cache-hit != 'true'
+        uses: actions/cache/save@v3
+        with:
+          path: ${{ runner.temp }}/cov-analysis
+          key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
+      - name: build with cov-build
+        run: |
+          export PATH="$RUNNER_TEMP/cov-analysis/bin:$PATH" &&
+          cov-configure --gcc &&
+          cov-build --dir cov-int make
+      - name: package the build
+        run: tar -czvf cov-int.tgz cov-int
+      - name: submit the build to Coverity Scan
+        run: |
+          curl \
+            --fail \
+            --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+            --form email='${{ secrets.COVERITY_SCAN_EMAIL }}' \
+            --form file=@cov-int.tgz \
+            --form version='${{ github.sha }}' \
+            "https://scan.coverity.com/builds?project=$COVERITY_PROJECT"
index f7ea0f00a449cfafd5dc006e59267e464f753387..6c3849658aa061b89ced8e0464b3b580519c3a34 100644 (file)
@@ -2,6 +2,12 @@ name: git-l10n
 
 on: [push, pull_request_target]
 
+# Avoid unnecessary builds. Unlike the main CI jobs, these are not
+# ci-configurable (but could be).
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
 jobs:
   git-po-helper:
     if: >-
index eed522f321deea1a64d83b873ad88428d9885d62..9fdbd5402898bf1c8a188bfd24262109c96e5e0f 100644 (file)
@@ -5,12 +5,27 @@ on: [push, pull_request]
 env:
   DEVELOPER: 1
 
+# If more than one workflow run is triggered for the very same commit hash
+# (which happens when multiple branches pointing to the same commit), only
+# the first one is allowed to run, the second will be kept in the "queued"
+# state. This allows a successful completion of the first run to be reused
+# in the second run via the `skip-if-redundant` logic in the `config` job.
+#
+# The only caveat is that if a workflow run is triggered for the same commit
+# hash that another run is already being held, that latter run will be
+# canceled. For more details about the `concurrency` attribute, see:
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency
+concurrency:
+  group: ${{ github.sha }}
+
 jobs:
   ci-config:
     name: config
+    if: vars.CI_BRANCHES == '' || contains(vars.CI_BRANCHES, github.ref_name)
     runs-on: ubuntu-latest
     outputs:
       enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
+      skip_concurrent: ${{ steps.check-ref.outputs.skip_concurrent }}
     steps:
       - name: try to clone ci-config branch
         run: |
@@ -29,12 +44,23 @@ jobs:
         name: check whether CI is enabled for ref
         run: |
           enabled=yes
-          if test -x config-repo/ci/config/allow-ref &&
-             ! config-repo/ci/config/allow-ref '${{ github.ref }}'
+          if test -x config-repo/ci/config/allow-ref
+          then
+            echo "::warning::ci/config/allow-ref is deprecated; use CI_BRANCHES instead"
+            if ! config-repo/ci/config/allow-ref '${{ github.ref }}'
+            then
+              enabled=no
+            fi
+          fi
+
+          skip_concurrent=yes
+          if test -x config-repo/ci/config/skip-concurrent &&
+             ! config-repo/ci/config/skip-concurrent '${{ github.ref }}'
           then
-            enabled=no
+            skip_concurrent=no
           fi
           echo "enabled=$enabled" >>$GITHUB_OUTPUT
+          echo "skip_concurrent=$skip_concurrent" >>$GITHUB_OUTPUT
       - name: skip if the commit or tree was already tested
         id: skip-if-redundant
         uses: actions/github-script@v6
@@ -82,6 +108,9 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     runs-on: windows-latest
+    concurrency:
+      group: windows-build-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v3
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
@@ -101,11 +130,14 @@ jobs:
   windows-test:
     name: win test
     runs-on: windows-latest
-    needs: [windows-build]
+    needs: [ci-config, windows-build]
     strategy:
       fail-fast: false
       matrix:
         nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+    concurrency:
+      group: windows-test-${{ matrix.nr }}-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - name: download tracked files and build artifacts
       uses: actions/download-artifact@v3
@@ -132,11 +164,14 @@ jobs:
   vs-build:
     name: win+VS build
     needs: ci-config
-    if: needs.ci-config.outputs.enabled == 'yes'
+    if: github.event.repository.owner.login == 'git-for-windows' && needs.ci-config.outputs.enabled == 'yes'
     env:
       NO_PERL: 1
       GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'"
     runs-on: windows-latest
+    concurrency:
+      group: vs-build-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v3
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
@@ -184,11 +219,14 @@ jobs:
   vs-test:
     name: win+VS test
     runs-on: windows-latest
-    needs: vs-build
+    needs: [ci-config, vs-build]
     strategy:
       fail-fast: false
       matrix:
         nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+    concurrency:
+      group: vs-test-${{ matrix.nr }}-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
     - name: download tracked files and build artifacts
@@ -218,13 +256,13 @@ jobs:
     name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}})
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
+    concurrency:
+      group: ${{ matrix.vector.jobname }}-${{ matrix.vector.pool }}-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     strategy:
       fail-fast: false
       matrix:
         vector:
-          - jobname: linux-clang
-            cc: clang
-            pool: ubuntu-latest
           - jobname: linux-sha256
             cc: clang
             pool: ubuntu-latest
@@ -238,17 +276,20 @@ jobs:
             pool: ubuntu-20.04
           - jobname: osx-clang
             cc: clang
-            pool: macos-12
+            pool: macos-13
           - jobname: osx-gcc
             cc: gcc
-            cc_package: gcc-9
-            pool: macos-12
+            cc_package: gcc-13
+            pool: macos-13
           - jobname: linux-gcc-default
             cc: gcc
             pool: ubuntu-latest
           - jobname: linux-leaks
             cc: gcc
             pool: ubuntu-latest
+          - jobname: linux-asan-ubsan
+            cc: clang
+            pool: ubuntu-latest
     env:
       CC: ${{matrix.vector.cc}}
       CC_PACKAGE: ${{matrix.vector.cc_package}}
@@ -259,8 +300,9 @@ jobs:
     - uses: actions/checkout@v3
     - run: ci/install-dependencies.sh
     - run: ci/run-build-and-tests.sh
-    - run: ci/print-test-failures.sh
+    - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
+      run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
       uses: actions/upload-artifact@v3
@@ -271,6 +313,9 @@ jobs:
     name: ${{matrix.vector.jobname}} (${{matrix.vector.image}})
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
+    concurrency:
+      group: dockerized-${{ matrix.vector.jobname }}-${{ matrix.vector.image }}-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     strategy:
       fail-fast: false
       matrix:
@@ -292,8 +337,9 @@ jobs:
       if: matrix.vector.jobname == 'linux32'
     - run: ci/install-docker-dependencies.sh
     - run: ci/run-build-and-tests.sh
-    - run: ci/print-test-failures.sh
+    - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
+      run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != '' && matrix.vector.jobname != 'linux32'
       uses: actions/upload-artifact@v3
@@ -312,6 +358,9 @@ jobs:
     env:
       jobname: StaticAnalysis
     runs-on: ubuntu-22.04
+    concurrency:
+      group: static-analysis-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v3
     - run: ci/install-dependencies.sh
@@ -323,6 +372,9 @@ jobs:
     env:
       jobname: sparse
     runs-on: ubuntu-20.04
+    concurrency:
+      group: sparse-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - name: Download a current `sparse` package
       # Ubuntu's `sparse` version is too old for us
@@ -341,6 +393,9 @@ jobs:
     name: documentation
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
+    concurrency:
+      group: documentation-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     env:
       jobname: Documentation
     runs-on: ubuntu-latest
index b3dcafcb3310e9f0bf6c03cd51741b8cb48831b9..612c0f6a0ff1aa37415b275424a4e01942f5a2dd 100644 (file)
@@ -1,7 +1,5 @@
-/fuzz-commit-graph
 /fuzz_corpora
-/fuzz-pack-headers
-/fuzz-pack-idx
+/GIT-BUILD-DIR
 /GIT-BUILD-OPTIONS
 /GIT-CFLAGS
 /GIT-LDFLAGS
 /GIT-PERL-HEADER
 /GIT-PYTHON-VARS
 /GIT-SCRIPT-DEFINES
+/GIT-SPATCH-DEFINES
 /GIT-USER-AGENT
 /GIT-VERSION-FILE
 /bin-wrappers/
 /git
 /git-add
-/git-add--interactive
 /git-am
 /git-annotate
 /git-apply
 /git-archimport
 /git-archive
 /git-bisect
-/git-bisect--helper
 /git-blame
 /git-branch
 /git-bugreport
@@ -61,7 +58,6 @@
 /git-difftool
 /git-difftool--helper
 /git-describe
-/git-env--helper
 /git-fast-export
 /git-fast-import
 /git-fetch
 /git-remote-ext
 /git-repack
 /git-replace
+/git-replay
 /git-request-pull
 /git-rerere
 /git-reset
 /TAGS
 /cscope*
 /compile_commands.json
+/.cache/
 *.hcc
 *.obj
 *.lib
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644 (file)
index 0000000..7932434
--- /dev/null
@@ -0,0 +1,63 @@
+default:
+  timeout: 2h
+
+workflow:
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+    - if: $CI_COMMIT_TAG
+    - if: $CI_COMMIT_REF_PROTECTED == "true"
+
+test:
+  image: $image
+  before_script:
+    - ./ci/install-docker-dependencies.sh
+  script:
+    - useradd builder --create-home
+    - chown -R builder "${CI_PROJECT_DIR}"
+    - sudo --preserve-env --set-home --user=builder ./ci/run-build-and-tests.sh
+  after_script:
+    - |
+      if test "$CI_JOB_STATUS" != 'success'
+      then
+        sudo --preserve-env --set-home --user=builder ./ci/print-test-failures.sh
+      fi
+  parallel:
+    matrix:
+      - jobname: linux-sha256
+        image: ubuntu:latest
+        CC: clang
+      - jobname: linux-gcc
+        image: ubuntu:20.04
+        CC: gcc
+        CC_PACKAGE: gcc-8
+      - jobname: linux-TEST-vars
+        image: ubuntu:20.04
+        CC: gcc
+        CC_PACKAGE: gcc-8
+      - jobname: linux-gcc-default
+        image: ubuntu:latest
+        CC: gcc
+      - jobname: linux-leaks
+        image: ubuntu:latest
+        CC: gcc
+      - jobname: linux-asan-ubsan
+        image: ubuntu:latest
+        CC: clang
+      - jobname: pedantic
+        image: fedora:latest
+      - jobname: linux-musl
+        image: alpine:latest
+  artifacts:
+    paths:
+      - t/failed-test-artifacts
+    when: on_failure
+
+static-analysis:
+  image: ubuntu:22.04
+  variables:
+    jobname: StaticAnalysis
+  before_script:
+    - ./ci/install-docker-dependencies.sh
+  script:
+    - ./ci/run-static-analysis.sh
+    - ./ci/check-directional-formatting.bash
index 07db36a9bb949c4c911d07baeb1c4c10c13bec4c..82129be449f94b9872087717a03f1e3d5e2fac1a 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -59,12 +59,13 @@ David Reiss <dreiss@facebook.com> <dreiss@dreiss-vmware.(none)>
 David S. Miller <davem@davemloft.net>
 David Turner <novalis@novalis.org> <dturner@twopensource.com>
 David Turner <novalis@novalis.org> <dturner@twosigma.com>
-Derrick Stolee <derrickstolee@github.com> <stolee@gmail.com>
-Derrick Stolee <derrickstolee@github.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
-Derrick Stolee <derrickstolee@github.com> <dstolee@microsoft.com>
+Derrick Stolee <stolee@gmail.com> <derrickstolee@github.com>
+Derrick Stolee <stolee@gmail.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
+Derrick Stolee <stolee@gmail.com> <dstolee@microsoft.com>
 Deskin Miller <deskinm@umich.edu>
 Đoàn Trần Công Danh <congdanhqx@gmail.com> Doan Tran Cong Danh
 Dirk Süsserott <newsletter@dirk.my1.cc>
+Emily Shaffer <nasamuffin@google.com> <emilyshaffer@google.com>
 Eric Blake <eblake@redhat.com> <ebb9@byu.net>
 Eric Hanchrow <eric.hanchrow@gmail.com> <offby1@blarg.net>
 Eric S. Raymond <esr@thyrsus.com>
@@ -79,6 +80,7 @@ Frank Lichtenheld <frank@lichtenheld.de> <flichtenheld@astaro.com>
 Fredrik Kuivinen <frekui@gmail.com> <freku045@student.liu.se>
 Frédéric Heitzmann <frederic.heitzmann@gmail.com>
 Garry Dolley <gdolley@ucla.edu> <gdolley@arpnetworks.com>
+Glen Choo <glencbz@gmail.com> <chooglen@google.com>
 Greg Price <price@mit.edu> <price@MIT.EDU>
 Greg Price <price@mit.edu> <price@ksplice.com>
 Heiko Voigt <hvoigt@hvoigt.net> <git-list@hvoigt.net>
@@ -165,6 +167,7 @@ Mark Rada <marada@uwaterloo.ca>
 Martin Langhoff <martin@laptop.org> <martin@catalyst.net.nz>
 Martin von Zweigbergk <martinvonz@gmail.com> <martin.von.zweigbergk@gmail.com>
 Masaya Suzuki <masayasuzuki@google.com> <draftcode@gmail.com>
+Matheus Tavares <matheus.tavb@gmail.com> <matheus.bernardino@usp.br>
 Matt Draisey <matt@draisey.ca> <mattdraisey@sympatico.ca>
 Matt Kraai <kraai@ftbfs.org> <matt.kraai@amo.abbott.com>
 Matt McCutchen <matt@mattmccutchen.net> <hashproduct@gmail.com>
index 1c3771e7d72f690d07f4a4ce49275a3a74709dfd..a48448de32f98b1a054b36e886ea920bccebc494 100644 (file)
@@ -10,7 +10,6 @@ howto-index.txt
 doc.dep
 cmds-*.txt
 mergetools-*.txt
-manpage-base-url.xsl
 SubmittingPatches.txt
 tmp-doc-diff/
 GIT-ASCIIDOCFLAGS
index 1d95a142b2780ad27636d3657d37f2608e9ac968..578587a47155e929457e12862cd648c9fdf81acd 100644 (file)
@@ -1,5 +1,5 @@
-Like other projects, we also have some guidelines to keep to the
-code.  For Git in general, a few rough rules are:
+Like other projects, we also have some guidelines for our code.  For
+Git in general, a few rough rules are:
 
  - Most importantly, we never say "It's in POSIX; we'll happily
    ignore your needs should your system not conform to it."
@@ -24,7 +24,7 @@ code.  For Git in general, a few rough rules are:
 
    "Once it _is_ in the tree, it's not really worth the patch noise to
    go and fix it up."
-   Cf. http://lkml.iu.edu/hypermail/linux/kernel/1001.3/01069.html
+   Cf. https://lore.kernel.org/all/20100126160632.3bdbe172.akpm@linux-foundation.org/
 
  - Log messages to explain your changes are as important as the
    changes themselves.  Clearly written code and in-code comments
@@ -40,7 +40,7 @@ As for more concrete guidelines, just imitate the existing code
 contributing to). It is always preferable to match the _local_
 convention. New code added to Git suite is expected to match
 the overall style of existing code. Modifications to existing
-code is expected to match the style the surrounding code already
+code are expected to match the style the surrounding code already
 uses (even if it doesn't match the overall style of existing code).
 
 But if you must have a list of rules, here are some language
@@ -162,8 +162,6 @@ For shell scripts specifically (not exhaustive):
 
    - We do not use \{m,n\};
 
-   - We do not use -E;
-
    - We do not use ? or + (which are \{0,1\} and \{1,\}
      respectively in BRE) but that goes without saying as these
      are ERE elements not BRE (note that \? and \+ are not even part
@@ -190,6 +188,10 @@ For shell scripts specifically (not exhaustive):
    hopefully nobody starts using "local" before they are reimplemented
    in C ;-)
 
+ - Use octal escape sequences (e.g. "\302\242"), not hexadecimal (e.g.
+   "\xc2\xa2") in printf format strings, since hexadecimal escape
+   sequences are not portable.
+
 
 For C programs:
 
@@ -444,8 +446,12 @@ For C programs:
    detail.
 
  - The first #include in C files, except in platform specific compat/
-   implementations, must be either "git-compat-util.h", "cache.h" or
-   "builtin.h".  You do not have to include more than one of these.
+   implementations and sha1dc/, must be either "git-compat-util.h" or
+   one of the approved headers that includes it first for you.  (The
+   approved headers currently include "builtin.h",
+   "t/helper/test-tool.h", "xdiff/xinclude.h", or
+   "reftable/system.h".)  You do not have to include more than one of
+   these.
 
  - A C file must directly include the header files that declare the
    functions and the types it uses, except for the functions and types
@@ -484,7 +490,7 @@ For Perl programs:
 
  - Most of the C guidelines above apply.
 
- - We try to support Perl 5.8 and later ("use Perl 5.008").
+ - We try to support Perl 5.8.1 and later ("use Perl 5.008001").
 
  - use strict and use warnings are strongly preferred.
 
@@ -512,7 +518,7 @@ For Perl programs:
 
 For Python scripts:
 
- - We follow PEP-8 (http://www.python.org/dev/peps/pep-0008/).
+ - We follow PEP-8 (https://peps.python.org/pep-0008/).
 
  - As a minimum, we aim to be compatible with Python 2.7.
 
@@ -572,7 +578,7 @@ Externally Visible Names
    . The variable name describes the effect of tweaking this knob.
 
    The section and variable names that consist of multiple words are
-   formed by concatenating the words without punctuations (e.g. `-`),
+   formed by concatenating the words without punctuation marks (e.g. `-`),
    and are broken using bumpyCaps in documentation as a hint to the
    reader.
 
@@ -665,8 +671,8 @@ Writing Documentation:
    (One or more of <file>.)
 
  Optional parts are enclosed in square brackets:
-   [<extra>]
-   (Zero or one <extra>.)
+   [<file>...]
+   (Zero or more of <file>.)
 
    --exec-path[=<path>]
    (Option with an optional argument.  Note that the "=" is inside the
@@ -680,6 +686,16 @@ Writing Documentation:
    [-q | --quiet]
    [--utf8 | --no-utf8]
 
+ Use spacing around "|" token(s), but not immediately after opening or
+ before closing a [] or () pair:
+   Do: [-q | --quiet]
+   Don't: [-q|--quiet]
+
+ Don't use spacing around "|" tokens when they're used to separate the
+ alternate arguments of an option:
+    Do: --track[=(direct|inherit)]
+    Don't: --track[=(direct | inherit)]
+
  Parentheses are used for grouping:
    [(<rev> | <range>)...]
    (Any number of either <rev> or <range>.  Parens are needed to make
index d47acb2e255051fba8cabb6e5f5cb6b55a1b2311..3f2383a12c77d04f027c64a0b6aed5ad7305e271 100644 (file)
@@ -122,6 +122,7 @@ TECH_DOCS += technical/scalar
 TECH_DOCS += technical/send-pack-pipeline
 TECH_DOCS += technical/shallow
 TECH_DOCS += technical/trivial-merge
+TECH_DOCS += technical/unit-tests
 SP_ARTICLES += $(TECH_DOCS)
 SP_ARTICLES += technical/api-index
 
@@ -144,14 +145,16 @@ man5dir = $(mandir)/man5
 man7dir = $(mandir)/man7
 # DESTDIR =
 
+GIT_DATE := $(shell git show --quiet --pretty='%as')
+
 ASCIIDOC = asciidoc
 ASCIIDOC_EXTRA =
 ASCIIDOC_HTML = xhtml11
 ASCIIDOC_DOCBOOK = docbook
 ASCIIDOC_CONF = -f asciidoc.conf
 ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA) $(ASCIIDOC_CONF) \
-               -amanversion=$(GIT_VERSION) \
-               -amanmanual='Git Manual' -amansource='Git'
+               -amanmanual='Git Manual' -amansource='Git $(GIT_VERSION)' \
+               -arevdate='$(GIT_DATE)'
 ASCIIDOC_DEPS = asciidoc.conf GIT-ASCIIDOCFLAGS
 TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
 TXT_TO_XML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_DOCBOOK)
@@ -189,15 +192,7 @@ endif
 ifndef MAN_BASE_URL
 MAN_BASE_URL = file://$(htmldir)/
 endif
-XMLTO_EXTRA += -m manpage-base-url.xsl
-
-# If your target system uses GNU groff, it may try to render
-# apostrophes as a "pretty" apostrophe using unicode.  This breaks
-# cut&paste, so you should set GNU_ROFF to force them to be ASCII
-# apostrophes.  Unfortunately does not work with non-GNU roff.
-ifdef GNU_ROFF
-XMLTO_EXTRA += -m manpage-quote-apos.xsl
-endif
+XMLTO_EXTRA += --stringparam man.base.url.for.relative.links='$(MAN_BASE_URL)'
 
 ifdef USE_ASCIIDOCTOR
 ASCIIDOC = asciidoctor
@@ -339,7 +334,6 @@ clean:
        $(RM) technical/*.html technical/api-index.txt
        $(RM) SubmittingPatches.txt
        $(RM) $(cmds_txt) $(mergetools_txt) *.made
-       $(RM) manpage-base-url.xsl
        $(RM) GIT-ASCIIDOCFLAGS
 
 $(MAN_HTML): %.html : %.txt $(ASCIIDOC_DEPS)
@@ -348,11 +342,15 @@ $(MAN_HTML): %.html : %.txt $(ASCIIDOC_DEPS)
 $(OBSOLETE_HTML): %.html : %.txto $(ASCIIDOC_DEPS)
        $(QUIET_ASCIIDOC)$(TXT_TO_HTML) -o $@ $<
 
-manpage-base-url.xsl: manpage-base-url.xsl.in
-       $(QUIET_GEN)sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
+manpage-prereqs := $(wildcard manpage*.xsl)
+manpage-cmd = $(QUIET_XMLTO)$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
 
-%.1 %.5 %.7 : %.xml manpage-base-url.xsl $(wildcard manpage*.xsl)
-       $(QUIET_XMLTO)$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
+%.1 : %.xml $(manpage-prereqs)
+       $(manpage-cmd)
+%.5 : %.xml $(manpage-prereqs)
+       $(manpage-cmd)
+%.7 : %.xml $(manpage-prereqs)
+       $(manpage-cmd)
 
 %.xml : %.txt $(ASCIIDOC_DEPS)
        $(QUIET_ASCIIDOC)$(TXT_TO_XML) -d manpage -o $@ $<
@@ -476,8 +474,19 @@ $(LINT_DOCS_MAN_SECTION_ORDER): .build/lint-docs/man-section-order/%.ok: %.txt
 .PHONY: lint-docs-man-section-order
 lint-docs-man-section-order: $(LINT_DOCS_MAN_SECTION_ORDER)
 
+.PHONY: lint-docs-fsck-msgids
+LINT_DOCS_FSCK_MSGIDS = .build/lint-docs/fsck-msgids.ok
+$(LINT_DOCS_FSCK_MSGIDS): lint-fsck-msgids.perl
+$(LINT_DOCS_FSCK_MSGIDS): ../fsck.h fsck-msgids.txt
+       $(call mkdir_p_parent_template)
+       $(QUIET_GEN)$(PERL_PATH) lint-fsck-msgids.perl \
+               ../fsck.h fsck-msgids.txt $@
+
+lint-docs-fsck-msgids: $(LINT_DOCS_FSCK_MSGIDS)
+
 ## Lint: list of targets above
 .PHONY: lint-docs
+lint-docs: lint-docs-fsck-msgids
 lint-docs: lint-docs-gitlink
 lint-docs: lint-docs-man-end-blurb
 lint-docs: lint-docs-man-section-order
index 1a4be8ee0adde5db48e5f53a8fe79dd17036b270..279f6a3e7ca4678f01196e9f60cdea372d0cc4f1 100644 (file)
@@ -160,10 +160,11 @@ in order to keep the declarations alphabetically sorted:
 int cmd_psuh(int argc, const char **argv, const char *prefix);
 ----
 
-Be sure to `#include "builtin.h"` in your `psuh.c`.
+Be sure to `#include "builtin.h"` in your `psuh.c`. You'll also need to
+`#include "gettext.h"` to use functions related to printing output text.
 
-Go ahead and add some throwaway printf to that function. This is a decent
-starting point as we can now add build rules and register the command.
+Go ahead and add some throwaway printf to the `cmd_psuh` function. This is a
+decent starting point as we can now add build rules and register the command.
 
 NOTE: Your throwaway text, as well as much of the text you will be adding over
 the course of this tutorial, is user-facing. That means it needs to be
@@ -736,7 +737,7 @@ the {lore}[Git mailing list archive]:
 2022-02-21  1:43     ` John Cai
 2022-02-21  1:50       ` Taylor Blau
 2022-02-23 19:50         ` John Cai
-2022-02-18 20:00   ` // other replies ellided
+2022-02-18 20:00   ` // other replies elided
 2022-02-18 18:40 ` [PATCH 2/3] reflog: call reflog_delete from reflog.c John Cai via GitGitGadget
 2022-02-18 19:15   ` Ævar Arnfjörð Bjarmason
 2022-02-18 20:26     ` Junio C Hamano
@@ -832,7 +833,7 @@ Johannes Schindelin to make life as a Git contributor easier for those used to
 the GitHub PR workflow. It allows contributors to open pull requests against its
 mirror of the Git project, and does some magic to turn the PR into a set of
 emails and send them out for you. It also runs the Git continuous integration
-suite for you. It's documented at http://gitgitgadget.github.io.
+suite for you. It's documented at https://gitgitgadget.github.io/.
 
 [[create-fork]]
 === Forking `git/git` on GitHub
@@ -1164,28 +1165,28 @@ After you run this command, `format-patch` will output the patches to the `psuh/
 directory, alongside the v1 patches. Using a single directory makes it easy to
 refer to the old v1 patches while proofreading the v2 patches, but you will need
 to be careful to send out only the v2 patches. We will use a pattern like
-"psuh/v2-*.patch" (not "psuh/*.patch", which would match v1 and v2 patches).
+`psuh/v2-*.patch` (not `psuh/*.patch`, which would match v1 and v2 patches).
 
 Edit your cover letter again. Now is a good time to mention what's different
 between your last version and now, if it's something significant. You do not
 need the exact same body in your second cover letter; focus on explaining to
 reviewers the changes you've made that may not be as visible.
 
-You will also need to go and find the Message-Id of your previous cover letter.
+You will also need to go and find the Message-ID of your previous cover letter.
 You can either note it when you send the first series, from the output of `git
 send-email`, or you can look it up on the
 https://lore.kernel.org/git[mailing list]. Find your cover letter in the
-archives, click on it, then click "permalink" or "raw" to reveal the Message-Id
+archives, click on it, then click "permalink" or "raw" to reveal the Message-ID
 header. It should match:
 
 ----
-Message-Id: <foo.12345.author@example.com>
+Message-ID: <foo.12345.author@example.com>
 ----
 
-Your Message-Id is `<foo.12345.author@example.com>`. This example will be used
-below as well; make sure to replace it with the correct Message-Id for your
-**previous cover letter** - that is, if you're sending v2, use the Message-Id
-from v1; if you're sending v3, use the Message-Id from v2.
+Your Message-ID is `<foo.12345.author@example.com>`. This example will be used
+below as well; make sure to replace it with the correct Message-ID for your
+**previous cover letter** - that is, if you're sending v2, use the Message-ID
+from v1; if you're sending v3, use the Message-ID from v2.
 
 While you're looking at the email, you should also note who is CC'd, as it's
 common practice in the mailing list to keep all CCs on a thread. You can add
@@ -1256,6 +1257,38 @@ index 88f126184c..38da593a60 100644
 [[now-what]]
 == My Patch Got Emailed - Now What?
 
+Please give reviewers enough time to process your initial patch before
+sending an updated version. That is, resist the temptation to send a new
+version immediately, because others may have already started reviewing
+your initial version.
+
+While waiting for review comments, you may find mistakes in your initial
+patch, or perhaps realize a different and better way to achieve the goal
+of the patch. In this case you may communicate your findings to other
+reviewers as follows:
+
+ - If the mistakes you found are minor, send a reply to your patch as if
+   you were a reviewer and mention that you will fix them in an
+   updated version.
+
+ - On the other hand, if you think you want to change the course so
+   drastically that reviews on the initial patch would be a waste of
+   time (for everyone involved), retract the patch immediately with
+   a reply like "I am working on a much better approach, so please
+   ignore this patch and wait for the updated version."
+
+Now, the above is a good practice if you sent your initial patch
+prematurely without polish.  But a better approach of course is to avoid
+sending your patch prematurely in the first place.
+
+Please be considerate of the time needed by reviewers to examine each
+new version of your patch. Rather than seeing the initial version right
+now (followed by several "oops, I like this version better than the
+previous one" patches over 2 days), reviewers would strongly prefer if a
+single polished version came 2 days later instead, and that version with
+fewer mistakes were the only one they would need to review.
+
+
 [[reviewing]]
 === Responding to Reviews
 
index eee513e86f4d645a47afcae4921de89b413fc1be..c68cdb11b9d5a53ddc11361d0f1c889edeb24536 100644 (file)
@@ -41,6 +41,7 @@ Open up a new file `builtin/walken.c` and set up the command handler:
  */
 
 #include "builtin.h"
+#include "trace.h"
 
 int cmd_walken(int argc, const char **argv, const char *prefix)
 {
@@ -49,12 +50,13 @@ int cmd_walken(int argc, const char **argv, const char *prefix)
 }
 ----
 
-NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or
-off at runtime. For the purposes of this tutorial, we will write `walken` as
-though it is intended for use as a "plumbing" command: that is, a command which
-is used primarily in scripts, rather than interactively by humans (a "porcelain"
-command). So we will send our debug output to `trace_printf()` instead. When
-running, enable trace output by setting the environment variable `GIT_TRACE`.
+NOTE: `trace_printf()`, defined in `trace.h`, differs from `printf()` in
+that it can be turned on or off at runtime. For the purposes of this
+tutorial, we will write `walken` as though it is intended for use as
+a "plumbing" command: that is, a command which is used primarily in
+scripts, rather than interactively by humans (a "porcelain" command).
+So we will send our debug output to `trace_printf()` instead.
+When running, enable trace output by setting the environment variable `GIT_TRACE`.
 
 Add usage text and `-h` handling, like all subcommands should consistently do
 (our test suite will notice and complain if you fail to do so).
@@ -124,7 +126,7 @@ parameters provided by the user over the CLI.
 
 `nr` represents the number of `rev_cmdline_entry` present in the array.
 
-`alloc` is used by the `ALLOC_GROW` macro. Check `cache.h` - this variable is
+`alloc` is used by the `ALLOC_GROW` macro. Check `alloc.h` - this variable is
 used to track the allocated size of the list.
 
 Per entry, we find:
@@ -341,6 +343,10 @@ the walk loop below the `prepare_revision_walk()` call within your
 `walken_commit_walk()`:
 
 ----
+#include "pretty.h"
+
+...
+
 static void walken_commit_walk(struct rev_info *rev)
 {
        struct commit *commit;
@@ -754,6 +760,10 @@ reachable objects are walked in order to populate the list.
 First, add the `struct oidset` and related items we will use to iterate it:
 
 ----
+#include "oidset.h"
+
+...
+
 static void walken_object_walk(
        ...
 
@@ -805,6 +815,10 @@ just walks of commits. First, we'll make our handlers chattier - modify
 go:
 
 ----
+#include "hex.h"
+
+...
+
 static void walken_show_commit(struct commit *cmt, void *buf)
 {
        trace_printf("commit: %s\n", oid_to_hex(&cmt->object.oid));
index 980adfb31546974b69e4aff8ef91ab05e00da41f..166d73c60fb11e021b77afc0b759de52cfba0495 100644 (file)
@@ -10,7 +10,7 @@ To ease the transition plan, the receiving repository of such a
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
index 4bcff945e019d92f58cef08517fa1e7a70553b66..bbf177fc3c5ba4f76a1e002623e32b9106204ee5 100644 (file)
@@ -10,7 +10,7 @@ To ease the transition plan, the receiving repository of such a
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
index a2a34b43a75d1b213eb51afae09d06454607e644..0fccfb0bf0bddf99a181653648d0af630d631b95 100644 (file)
@@ -10,7 +10,7 @@ To ease the transition plan, the receiving repository of such a
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
index 6c7f7da7eb9a10971c42552cafc5e85f6b226319..79cb1b2b6df88a5ccdf9a237f83379839227057b 100644 (file)
@@ -21,7 +21,7 @@ To ease the transition plan, the receiving repository of such a
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
index 3ed1e01433745aed586a5de3770f88b8cdf08233..88b86a827e807e5463a3a353ec004a47b52ecb52 100644 (file)
@@ -63,7 +63,7 @@ users will fare this time.
 
    Please refer to:
 
-   http://git.or.cz/gitwiki/GitFaq#non-bare
+   https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
    https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
    for more details on the reason why this change is needed and the
index 38c23e0345551c8c0c6145441ac912ef3a1118f7..5ed3efbd6af4bf799c7cae233d25da7cacd11c66 100644 (file)
@@ -49,4 +49,3 @@ Taylor Blau (3):
       t5619: demonstrate clone_local() with ambiguous transport
       clone: delay picking a transport until after get_repo_path()
       dir-iterator: prevent top-level symlinks without FOLLOW_SYMLINKS
-
diff --git a/Documentation/RelNotes/2.39.0.txt b/Documentation/RelNotes/2.39.0.txt
new file mode 100644 (file)
index 0000000..9bf00ec
--- /dev/null
@@ -0,0 +1,346 @@
+Git v2.39 Release Notes
+=======================
+
+UI, Workflows & Features
+------------------------
+
+ * "git grep" learned to expand the sparse-index more lazily and on
+   demand in a sparse checkout.
+
+ * By default, use of fsmonitor on a repository on networked
+   filesystem is disabled. Add knobs to make it workable on macOS.
+
+ * After checking out a "branch" that is a symbolic-ref that points at
+   another branch, "git symbolic-ref HEAD" reports the underlying
+   branch, not the symbolic-ref the user gave checkout as argument.
+   The command learned the "--no-recurse" option to stop after
+   dereferencing a symbolic-ref only once.
+
+ * "git branch --edit-description @{-1}" is now a way to edit branch
+   description of the branch you were on before switching to the
+   current branch.
+
+ * "git merge-tree --stdin" is a new way to request a series of merges
+   and report the merge results.
+
+ * "git shortlog" learned to group by the "format" string.
+
+ * A new "--include-whitespace" option is added to "git patch-id", and
+   existing bugs in the internal patch-id logic that did not match
+   what "git patch-id" produces have been corrected.
+
+ * Enable gc.cruftpacks by default for those who opt into
+   feature.experimental setting.
+
+ * "git repack" learns to send cruft objects out of the way into
+   packfiles outside the repository.
+
+ * 'scalar reconfigure -a' is taught to automatically remove
+   scalar.repo entires which no longer exist.
+
+ * Redact headers from cURL's h2h3 module in GIT_CURL_VERBOSE and
+   others.
+
+ * 'git maintenance register' is taught to write configuration to an
+   arbitrary path, and 'git for-each-repo' is taught to expand tilde
+   characters in paths.
+
+ * When creating new notes, the template used to get a stray empty
+   newline, which has been removed.
+
+ * "git receive-pack" used to use all the local refs as the boundary for
+   checking connectivity of the data "git push" sent, but now it uses
+   only the refs that it advertised to the pusher. In a repository with
+   the .hideRefs configuration, this reduces the resources needed to
+   perform the check.
+
+ * With '--recurse-submodules=on-demand', all submodules are
+   recursively pushed.
+
+
+Performance, Internal Implementation, Development Support etc.
+--------------------------------------------------------------
+
+ * With a bit of header twiddling, use the native regexp library on
+   macOS instead of the compat/ one.
+
+ * Prepare for GNU [ef]grep that throw warning of their uses.
+
+ * Sources related to fuzz testing have been moved down to their own
+   directory.
+
+ * Most credential helpers ignored unknown entries in a credential
+   description, but a few died upon seeing them.  The latter were
+   taught to ignore them, too
+
+ * "scalar unregister" in a repository that is already been
+   unregistered reported an error.
+
+ * Remove error detection from a function that fetches from promisor
+   remotes, and make it die when such a fetch fails to bring all the
+   requested objects, to give an early failure to various operations.
+
+ * Update CodingGuidelines to clarify what features to use and avoid
+   in C99.
+
+ * Avoid false-positive from LSan whose assumption may be broken with
+   higher optimization levels.
+
+ * Enable address and undefined sanitizer tasks at GitHub Actions CI.
+
+ * More UNUSED annotation to help using -Wunused option with the
+   compiler.
+   (merge 4b992f0a24 jk/unused-anno-more later to maint).
+
+ * Rewrite a deep recursion in the skipping negotiator to use a loop
+   with on-heap prio queue to avoid stack wastage.
+
+ * Add documentation for message IDs in fsck error messages.
+
+ * Define the logical elements of a "bundle list", data structure to
+   store them in-core, format to transfer them, and code to parse
+   them.
+
+ * The role the security mailing list plays in an embargoed release
+   has been documented.
+
+ * Two new facilities, "timer" and "counter", are introduced to the
+   trace2 API.
+
+ * Code simplification by using strvec_pushf() instead of building an
+   argument in a separate strbuf.
+
+ * Make sure generated dependency file is stably sorted to help
+   developers debugging their build issues.
+
+ * The glossary entries for "commit-graph file" and "reachability
+   bitmap" have been added.
+
+ * Various tests exercising the transfer.credentialsInUrl
+   configuration are taught to avoid making requests which require
+   resolving localhost to reduce CI-flakiness.
+
+ * A redundant diagnostic message is dropped from test_path_is_missing().
+
+ * Simplify the run-command API.
+
+ * Update the actions/github-script dependency in CI to avoid a
+   deprecation warning.
+
+ * Progress on being able to initialize a rev_info struct with a
+   macro.
+
+ * Add trace2 counters to the region to clear skip worktree bits in a
+   sparse checkout.
+
+ * Modernize test script to avoid "test -f" and friends.
+
+ * Avoid calling 'cache_tree_update()' when doing so would be
+   redundant.
+
+ * Update the credential-cache documentation to provide a more
+   realistic example.
+
+ * Makefile comments updates and reordering to clarify knobs used to
+   choose SHA implementations.
+
+ * A design document for sparse-checkout's future directions has been
+   added.
+
+ * Teach chainlint.pl to annotate the original test definition instead
+   of the token stream.
+
+ * "make coccicheck" is time consuming. It has been made to run more
+   incrementally.
+
+ * `parse_object()` has been hardened to check for the existence of a
+   suspected blob object.
+
+ * The build procedure has been adjusted to GNUmake version 4.4, which
+   made some changes to how pattern rule with multiple targets are
+   handled.
+
+
+Fixes since v2.38
+-----------------
+
+ * The codepath that reads from the index v4 had unaligned memory
+   accesses, which has been corrected.
+
+ * Fix messages incorrectly marked for translation.
+
+ * "git fsck" failed to release contents of tree objects already used
+   from the memory, which has been fixed.
+
+ * "git clone" did not like to see the "--bare" and the "--origin"
+   options used together without a good reason.
+
+ * "git remote rename" failed to rename a remote without fetch
+   refspec, which has been corrected.
+
+ * Documentation on various Boolean GIT_* environment variables have
+   been clarified.
+
+ * "git rebase -i" can mistakenly attempt to apply a fixup to a commit
+   itself, which has been corrected.
+
+ * "git multi-pack-index repack/expire" used to repack unreachable
+   cruft into a new pack, which have been corrected.
+
+ * In read-only repositories, "git merge-tree" tried to come up with a
+   merge result tree object, which it failed (which is not wrong) and
+   led to a segfault (which is bad), which has been corrected.
+
+ * Force C locale while running tests around httpd to make sure we can
+   find expected error messages in the log.
+
+ * Fix a logic in "mailinfo -b" that miscomputed the length of a
+   substring, which lead to an out-of-bounds access.
+
+ * The codepath to sign learned to report errors when it fails to read
+   from "ssh-keygen".
+
+ * Code clean-up that results in plugging a leak.
+
+ * "GIT_EDITOR=: git branch --edit-description" resulted in failure,
+   which has been corrected.
+
+ * The code to clean temporary object directories (used for
+   quarantine) tried to remove them inside its signal handler, which
+   was a no-no.
+
+ * Update comment in the Makefile about the RUNTIME_PREFIX config knob.
+
+ * Clarify that "the sentence after <area>: prefix does not begin with
+   a capital letter" rule applies only to the commit title.
+
+ * "git branch --edit-description" on an unborn branch misleadingly
+   said that no such branch exists, which has been corrected.
+
+ * Work around older clang that warns against C99 zero initialization
+   syntax for struct.
+
+ * Giving "--invert-grep" and "--all-match" without "--grep" to the
+   "git log" command resulted in an attempt to access grep pattern
+   expression structure that has not been allocated, which has been
+   corrected.
+   (merge db84376f98 ab/grep-simplify-extended-expression later to maint).
+
+ * "git diff rev^!" did not show combined diff to go to the rev from
+   its parents.
+   (merge a79c6b6081 rs/diff-caret-bang-with-parents later to maint).
+
+ * Allow configuration files in "protected" scopes to include other
+   configuration files.
+   (merge ecec57b3c9 gc/bare-repo-discovery later to maint).
+
+ * Give a bit more diversity to macOS CI by using sha1dc in one of the
+   jobs (the other one tests Apple Common Crypto).
+   (merge 1ad5c3df35 jc/ci-osx-with-sha1dc later to maint).
+
+ * A bugfix with tracing support in midx codepath
+   (merge e9c3839944 tb/midx-bitmap-selection-fix later to maint).
+
+ * When geometric repacking feature is in use together with the
+   --pack-kept-objects option, we lost packs marked with .keep files.
+   (merge 197443e80a tb/save-keep-pack-during-geometric-repack later to maint).
+
+ * Move a global variable added as a hack during regression fixes to
+   its proper place in the API.
+   (merge 0b0ab95f17 ab/run-hook-api-cleanup later to maint).
+
+ * Update to build procedure with VS using CMake/CTest.
+   (merge c858750b41 js/cmake-updates later to maint).
+
+ * The short-help text shown by "git cmd -h" and the synopsis text
+   shown at the beginning of "git help cmd" have been made more
+   consistent.
+
+ * When creating a multi-pack bitmap, remove per-pack bitmap files
+   unconditionally as they will never be consulted.
+   (merge 55d902cd61 tb/remove-unused-pack-bitmap later to maint).
+
+ * Fix a longstanding syntax error in Git.pm error codepath.
+
+ * "git diff --stat" etc. were invented back when everything was ASCII
+   and strlen() was a way to measure the display width of a string;
+   adjust them to compute the display width assuming UTF-8 pathnames.
+   (merge ce8529b2bb tb/diffstat-with-utf8-strwidth later to maint).
+
+ * "git branch --edit-description" can exit with status -1 which is
+   not a good practice; it learned to use 1 as everybody else instead.
+
+ * "git apply" limits its input to a bit less than 1 GiB.
+
+ * Merging a branch with directory renames into a branch that changes
+   the directory to a symlink was mishandled by the ort merge
+   strategy, which has been corrected.
+
+ * A bugfix to "git subtree" in its split and merge features.
+
+ * Fix some bugs in the reflog messages when rebasing and changes the
+   reflog messages of "rebase --apply" to match "rebase --merge" with
+   the aim of making the reflog easier to parse.
+
+ * "git rebase --keep-base" used to discard the commits that are
+   already cherry-picked to the upstream, even when "keep-base" meant
+   that the base, on top of which the history is being rebuilt, does
+   not yet include these cherry-picked commits.  The --keep-base
+   option now implies --reapply-cherry-picks and --no-fork-point
+   options.
+
+ * The way "git repack" created temporary files when it received a
+   signal was prone to deadlocking, which has been corrected.
+
+ * Various tests exercising the transfer.credentialsInUrl
+   configuration are taught to avoid making requests which require
+   resolving localhost to reduce CI-flakiness.
+
+ * The adjust_shared_perm() helper function learned to refrain from
+   setting the "g+s" bit on directories when it is not necessary.
+
+ * "git archive" mistakenly complained twice about a missing
+   executable, which has been corrected.
+
+ * Fix a bug where `git branch -d` did not work on an orphaned HEAD.
+
+ * `git rebase --update-refs` would delete references when all
+   `update-ref` commands in the sequencer were removed, which has been
+   corrected.
+
+ * Fix a regression in the bisect-helper which mistakenly treats
+   arguments to the command given to 'git bisect run' as arguments to
+   the helper.
+
+ * Correct an error where `git rebase` would mistakenly use a branch or
+   tag named "refs/rewritten/xyz" when missing a rebase label.
+
+ * Assorted fixes of parsing end-user input as integers.
+   (merge 14770cf0de pw/config-int-parse-fixes later to maint).
+
+ * "git prune" may try to iterate over .git/objects/pack for trash
+   files to remove in it, and loudly fail when the directory is
+   missing, which is not necessary.  The command has been taught to
+   ignore such a failure.
+   (merge 6974765352 ew/prune-with-missing-objects-pack later to maint).
+
+ * Add one more candidate directory that may house httpd modules while
+   running tests.
+   (merge 1c7dc23d41 es/locate-httpd-module-location-in-test later to maint).
+
+ * A handful of leaks in the line-log machinery have been plugged.
+
+ * The format of a line in /proc/cpuinfo that describes a CPU on s390x
+   looked different from everybody else, and the code in chainlint.pl
+   failed to parse it.
+   (merge 1f51b77f4f ah/chainlint-cpuinfo-parse-fix later to maint).
+
+ * Adjust the GitHub CI to newer ubuntu release.
+   (merge 0d3507f3e7 jx/ci-ubuntu-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 413bc6d20a ds/cmd-main-reorder later to maint).
+   (merge 8d2863e4ed nw/t1002-cleanup later to maint).
+   (merge 7c2dc122f9 rs/list-objects-filter-leakfix later to maint).
+   (merge 288fcb1c94 zk/push-use-bitmaps later to maint).
+   (merge 42db324c0f km/merge-recursive-typofix later to maint).
diff --git a/Documentation/RelNotes/2.39.1.txt b/Documentation/RelNotes/2.39.1.txt
new file mode 100644 (file)
index 0000000..60c86f4
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.39.1 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.39.2.txt b/Documentation/RelNotes/2.39.2.txt
new file mode 100644 (file)
index 0000000..ebb9900
--- /dev/null
@@ -0,0 +1,7 @@
+Git v2.39.2 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8, v2.31.7,
+v2.32.6, v2.33.7, v2.34.7, v2.35.7, v2.36.5, v2.37.6 and v2.38.4
+to address the security issues CVE-2023-22490 and CVE-2023-23946;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.39.3.txt b/Documentation/RelNotes/2.39.3.txt
new file mode 100644 (file)
index 0000000..66351b6
--- /dev/null
@@ -0,0 +1,64 @@
+Git v2.39.3 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.30.9, v2.31.8,
+v2.32.7, v2.33.8, v2.34.8, v2.35.8, v2.36.6, v2.37.7 and v2.38.5 to
+address the security issues CVE-2023-25652, CVE-2023-25815, and
+CVE-2023-29007; see the release notes for these versions for
+details.
+
+This release also merges fixes that have accumulated on the 'master'
+front to prepare for the 2.40 release that are still relevant to
+2.39.x maintenance track.
+
+Fixes since v2.39.2
+-------------------
+
+ * Stop running win+VS build by default.
+
+ * CI updates.  We probably want a clean-up to move the long shell
+   script embedded in yaml file into a separate file, but that can
+   come later.
+
+ * Avoid unnecessary builds in CI, with settings configured in
+   ci-config.
+
+ * Redefining system functions for a few functions did not follow our
+   usual "implement git_foo() and #define foo(args) git_foo(args)"
+   pattern, which has broken build for some folks.
+
+ * Deal with a few deprecation warning from cURL library.
+
+ * Newer regex library macOS stopped enabling GNU-like enhanced BRE,
+   where '\(A\|B\)' works as alternation, unless explicitly asked with
+   the REG_ENHANCED flag.  "git grep" now can be compiled to do so, to
+   retain the old behaviour.
+
+ * When given a pattern that matches an empty string at the end of a
+   line, the code to parse the "git diff" line-ranges fell into an
+   infinite loop, which has been corrected.
+
+ * Fix the sequence to fsync $GIT_DIR/packed-refs file that forgot to
+   flush its output to the disk..
+
+ * "git diff --relative" did not mix well with "git diff --ext-diff",
+   which has been corrected.
+
+ * The logic to see if we are using the "cone" mode by checking the
+   sparsity patterns has been tightened to avoid mistaking a pattern
+   that names a single file as specifying a cone.
+
+ * Doc update for environment variables set when hooks are invoked.
+
+ * Document ORIG_HEAD a bit more.
+
+ * "git ls-tree --format='%(path) %(path)' $tree $path" showed the
+   path three times, which has been corrected.
+
+ * Document that "branch -f <branch>" disables only the safety to
+   avoid recreating an existing branch.
+
+ * Clarify how "checkout -b/-B" and "git branch [-f]" are similar but
+   different in the documentation.
+
+Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.40.0.txt b/Documentation/RelNotes/2.40.0.txt
new file mode 100644 (file)
index 0000000..3ea445b
--- /dev/null
@@ -0,0 +1,320 @@
+Git v2.40 Release Notes
+=======================
+
+UI, Workflows & Features
+
+ * "merge-tree" learns a new `--merge-base` option.
+
+ * "git jump" (in contrib/) learned to present the "quickfix list" to
+   its standard output (instead of letting it consumed by the editor
+   it invokes), and learned to also drive emacs/emacsclient.
+
+ * "git var UNKNOWN_VARIABLE" and "git var VARIABLE" with the variable
+   given an empty value used to behave identically.  Now the latter
+   just gives an empty output, while the former still gives an error
+   message.
+
+ * Introduce a case insensitive mode to the Bash completion helpers.
+
+ * The advice message given by "git status" when it takes long time to
+   enumerate untracked paths has been updated.
+
+ * Just like "git var GIT_EDITOR" abstracts the complex logic to
+   choose which editor gets used behind it, "git var" now give support
+   to GIT_SEQUENCE_EDITOR.
+
+ * "git format-patch" learned to honor format.mboxrd even when sending
+   patches to the standard output stream,
+
+ * 'cat-file' gains mailmap support for its '--batch-check' and '-s'
+   options.
+
+ * Conditionally skip the pre-applypatch and applypatch-msg hooks when
+   applying patches with 'git am'.
+
+ * Introduce an optional configuration to allow the trailing hash that
+   protects the index file from bit flipping.
+
+ * "git check-attr" learned to take an optional tree-ish to read the
+   .gitattributes file from.
+
+ * "scalar" learned to give progress bar.
+
+ * "grep -P" learned to use Unicode Character Property to grok
+   character classes when processing \b and \w etc.
+
+ * "git rebase" often ignored incompatible options instead of
+   complaining, which has been corrected.
+
+ * "scalar" warns but continues when its periodic maintenance
+   feature cannot be enabled.
+
+ * The bundle-URI subsystem adds support for creation-token heuristics
+   to help incremental fetches.
+
+ * Userdiff regexp update for Java language.
+
+ * "git fetch --jobs=0" used to hit a BUG(), which has been corrected
+   to use the available CPUs.
+
+ * An invalid label or ref in the "rebase -i" todo file used to
+   trigger an runtime error. SUch an error is now diagnosed while the
+   todo file is parsed.
+
+ * The "diff" drivers specified by the "diff" attribute attached to
+   paths can now specify which algorithm (e.g. histogram) to use.
+
+ * "git range-diff" learned --abbrev=<num> option.
+
+ * "git archive HEAD^{tree}" records the paths with the current
+   timestamp in the archive, making it harder to obtain a stable
+   output.  The command learned the --mtime option to specify an
+   arbitrary timestamp (e.g. --mtime="@0 +0000" for the epoch).
+
+ * The credential subsystem learned that a password may have an
+   explicit expiration.
+
+ * The format.attach configuration variable lacked a way to override a
+   value defined in a lower-priority configuration file (e.g. the
+   system one) by redefining it in a higher-priority configuration
+   file.  Now, setting format.attach to an empty string means show the
+   patch inline in the e-mail message, without using MIME attachment.
+
+   This is a backward incompatible change.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * `git bisect` becomes a builtin.
+
+ * The pack-bitmap machinery is taught to log the paths of redundant
+   bitmap(s) to trace2 instead of stderr.
+
+ * Use the SHA1DC implementation on macOS, just like other platforms,
+   by default.
+
+ * Even in a repository with promisor remote, it is useless to
+   attempt to lazily attempt fetching an object that is expected to be
+   commit, because no "filter" mode omits commit objects.  Take
+   advantage of this assumption to fail fast on errors.
+
+ * Stop using "git --super-prefix" and narrow the scope of its use to
+   the submodule--helper.
+
+ * Stop running win+VS build by default.
+
+ * CI updates.  We probably want a clean-up to move the long shell
+   script embedded in yaml file into a separate file, but that can
+   come later.
+
+ * Use `git diff --no-index` as a test_cmp on Windows.
+
+   We'd probably need to revisit "do we really want to, and have to,
+   lose CRLF vs LF?" later, at which time we may be able to further
+   clean this up by replacing "git diff --no-index" with "diff -u".
+
+ * Avoid unnecessary builds in CI, with settings configured in
+   ci-config.
+
+ * Plug leaks in sequencer subsystem and its users.
+
+ * In-tree .gitattributes update to match the way we recommend our
+   users to mark a file as text.
+   (merge 1f34e0cd3d po/attributes-text later to maint).
+
+ * Finally retire the scripted "git add -p/-i" implementation and have
+   everybody use the one reimplemented in C.
+
+
+Fixes since v2.39
+-----------------
+
+ * Various leak fixes.
+
+ * Fix a bug where `pack-objects` would not respect multiple `--filter`
+   arguments when invoked directly.
+   (merge d4f7036887 rs/multi-filter-args later to maint).
+
+ * Make fsmonitor more robust to avoid the flakiness seen in t7527.
+   (merge 6692d45477 jh/t7527-unflake-by-forcing-cookie later to maint).
+
+ * Stop using deprecated macOS API in fsmonitor.
+   (merge b0226007f0 jh/fsmonitor-darwin-modernize later to maint).
+
+ * Redefining system functions for a few functions did not follow our
+   usual "implement git_foo() and #define foo(args) git_foo(args)"
+   pattern, which has broken build for some folks.
+
+ * The way the diff machinery prepares the options array for the
+   parse_options API has been refactored to avoid resource leaks.
+   (merge 189e97bc4b rs/diff-parseopts later to maint).
+
+ * Correct pthread API usage.
+   (merge 786e67611d sx/pthread-error-check-fix later to maint).
+
+ * The code to auto-correct a misspelt subcommand unnecessarily called
+   into git_default_config() from the early config codepath, which was
+   a no-no.  This has bee corrected.
+   (merge 0918d08887 sg/help-autocorrect-config-fix later to maint).
+
+ * "git http-fetch" (which is rarely used) forgot to identify itself
+   in the trace2 output.
+   (merge 7abb43cbc8 jt/http-fetch-trace2-report-name later to maint).
+
+ * The output from "git diff --stat" on an unmerged path lost the
+   terminating LF in Git 2.39, which has been corrected.
+   (merge 209d9cb011 pg/diff-stat-unmerged-regression-fix later to maint).
+
+ * "git pull -v --recurse-submodules" attempted to pass "-v" down to
+   underlying "git submodule update", which did not understand the
+   request and barfed, which has been corrected.
+   (merge 6f65f84766 ss/pull-v-recurse-fix later to maint).
+
+ * When given a pattern that matches an empty string at the end of a
+   line, the code to parse the "git diff" line-ranges fell into an
+   infinite loop, which has been corrected.
+
+ * Fix the sequence to fsync $GIT_DIR/packed-refs file that forgot to
+   flush its output to the disk..
+
+ * Fix to a small regression in 2.38 days.
+
+ * "git diff --relative" did not mix well with "git diff --ext-diff",
+   which has been corrected.
+
+ * The logic to see if we are using the "cone" mode by checking the
+   sparsity patterns has been tightened to avoid mistaking a pattern
+   that names a single file as specifying a cone.
+
+ * Deal with a few deprecation warning from cURL library.
+
+ * Doc update for environment variables set when hooks are invoked.
+
+ * Document ORIG_HEAD a bit more.
+
+ * "git ls-tree --format='%(path) %(path)' $tree $path" showed the
+   path three times, which has been corrected.
+
+ * Remove "git env--helper" and demote it to a test-tool subcommand.
+   (merge 4a1baacd46 ab/test-env-helper later to maint).
+
+ * Newer regex library macOS stopped enabling GNU-like enhanced BRE,
+   where '\(A\|B\)' works as alternation, unless explicitly asked with
+   the REG_ENHANCED flag.  "git grep" now can be compiled to do so, to
+   retain the old behaviour.
+
+ * Pthread emulation on Win32 leaked thread handle when a thread is
+   joined.
+   (merge 238a9dfe86 sk/win32-close-handle-upon-pthread-join later to maint).
+
+ * "git send-email -v 3" used to be expanded to "git send-email
+   --validate 3" when the user meant to pass them down to
+   "format-patch", which has been corrected.
+   (merge 8774aa56ad km/send-email-with-v-reroll-count later to maint).
+
+ * Document that "branch -f <branch>" disables only the safety to
+   avoid recreating an existing branch.
+
+ * "git fetch <group>", when "<group>" of remotes lists the same
+   remote twice, unnecessarily failed when parallel fetching was
+   enabled, which has been corrected.
+   (merge 06a668cb90 cw/fetch-remote-group-with-duplication later to maint).
+
+ * Clarify how "checkout -b/-B" and "git branch [-f]" are similar but
+   different in the documentation.
+
+ * "git hash-object" now checks that the resulting object is well
+   formed with the same code as "git fsck".
+   (merge 8e4309038f jk/hash-object-fsck later to maint).
+
+ * Improve the error message given when private key is not loaded in
+   the ssh agent in the codepath to sign with an ssh key.
+   (merge dce7b31126 as/ssh-signing-improve-key-missing-error later to maint).
+
+ * Adjust "git request-pull" to strip embedded signature from signed
+   tags to notice non-PGP signatures.
+   (merge a9cad02538 gm/request-pull-with-non-pgp-signed-tags later to maint).
+
+ * Remove support for MSys, which now lags way behind MSys2.
+   (merge 2987407f3c hj/remove-msys-support later to maint).
+
+ * Fix use of CreateThread() API call made early in the windows
+   start-up code.
+   (merge 592bcab61b sk/winansi-createthread-fix later to maint).
+
+ * "git pack-objects" learned to release delta-island bitmap data when
+   it is done using it, saving peak heap memory usage.
+   (merge 647982bb71 ew/free-island-marks later to maint).
+
+ * In an environment where dynamically generated code is prohibited to
+   run (e.g. SELinux), failure to JIT pcre patterns is expected.  Fall
+   back to interpreted execution in such a case.
+   (merge 50b6ad55b0 cb/grep-fallback-failing-jit later to maint).
+
+ * "git name-rev" heuristics update.
+   (merge b2182a8730 en/name-rev-make-taggerdate-much-less-important later to maint).
+
+ * Remove more remaining uses of macros that relies on the_index
+   singleton instance without explicitly spelling it out.
+
+ * Remove unnecessary explicit sizing of strbuf.
+   (merge 93ea118bed rs/cache-tree-strbuf-growth-fix later to maint).
+
+ * Doc update.
+   (merge d9ec3b0dc0 jk/doc-ls-remote-matching later to maint).
+
+ * Error messages given upon a signature verification failure used to
+   discard the errors from underlying gpg program, which has been
+   corrected.
+   (merge ad6b320756 js/gpg-errors later to maint).
+
+ * Update --date=default documentation.
+   (merge 9deef088ae rd/doc-default-date-format later to maint).
+
+ * A test helper had a single write(2) of 256kB, which was too big for
+   some platforms (e.g. NonStop), which has been corrected by using
+   xwrite() wrapper appropriately.
+   (merge 58eab6ff13 jc/genzeros-avoid-raw-write later to maint).
+
+ * sscanf(3) used in "git symbolic-ref --short" implementation found
+   to be not working reliably on macOS in UTF-8 locales.  Rewrite the
+   code to avoid sscanf() altogether to work it around.
+   (merge 613bef56b8 jk/shorten-unambiguous-ref-wo-sscanf later to maint).
+
+ * Various fix-ups on HTTP tests.
+   (merge 8f2146dbf1 jk/http-test-fixes later to maint).
+
+ * Fixes to code that parses the todo file used in "rebase -i".
+   (merge 666b6e1135 pw/rebase-i-parse-fix later to maint).
+
+ * Test library clean-up.
+   (merge c600a91c94 ar/test-lib-remove-stale-comment later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 4eb1ccecd4 dh/mingw-ownership-check-typofix later to maint).
+   (merge f95526419b ar/typofix-gitattributes-doc later to maint).
+   (merge 27875aeec9 km/doc-branch-start-point later to maint).
+   (merge 35c194dc57 es/t1509-root-fixes later to maint).
+   (merge 7b341645e3 pw/ci-print-failure-name-fix later to maint).
+   (merge bcb71d45bf jx/t1301-updates later to maint).
+   (merge ebdc46c242 jc/doc-diff-patch.txt later to maint).
+   (merge a87a20cbb4 ar/test-cleanup later to maint).
+   (merge f5156f1885 ar/bisect-doc-update later to maint).
+   (merge fca2d86c97 jk/interop-error later to maint).
+   (merge cf4936ed74 tl/ls-tree-code-clean-up later to maint).
+   (merge dcb47e52b0 en/t6426-todo-cleanup later to maint).
+   (merge 5b8db44bdd jc/format-patch-v-unleak later to maint).
+   (merge 590b636737 jk/hash-object-literally-fd-leak later to maint).
+   (merge 5458ba0a4d tb/t0003-invoke-dd-more-portably later to maint).
+   (merge 70661d288b ar/markup-em-dash later to maint).
+   (merge e750951e74 en/ls-files-doc-update later to maint).
+   (merge 4f542975d1 mh/doc-credential-cache-only-in-core later to maint).
+   (merge 3a2ebaebc7 gc/index-format-doc later to maint).
+   (merge b08edf709d jk/httpd-test-updates later to maint).
+   (merge d85e9448dd wl/new-command-doc later to maint).
+   (merge d912a603ed kf/t5000-modernise later to maint).
+   (merge e65b868d07 rs/size-t-fixes later to maint).
+   (merge 3eb1e1ca9a ab/config-h-remove-unused later to maint).
+   (merge d390e08076 cw/doc-pushurl-vs-url later to maint).
+   (merge 567342fc77 rs/ctype-test later to maint).
+   (merge d35d8f2e7a ap/t2015-style-update later to maint).
diff --git a/Documentation/RelNotes/2.40.1.txt b/Documentation/RelNotes/2.40.1.txt
new file mode 100644 (file)
index 0000000..e72f6b1
--- /dev/null
@@ -0,0 +1,8 @@
+Git v2.40.1 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.30.9, v2.31.8,
+v2.32.7, v2.33.8, v2.34.8, v2.35.8, v2.36.6, v2.37.7, v2.38.5
+and v2.39.3 to address the security issues CVE-2023-25652,
+CVE-2023-25815, and CVE-2023-29007; see the release notes for these
+versions for details.
diff --git a/Documentation/RelNotes/2.41.0.txt b/Documentation/RelNotes/2.41.0.txt
new file mode 100644 (file)
index 0000000..8a9e170
--- /dev/null
@@ -0,0 +1,399 @@
+Git v2.41 Release Notes
+=======================
+
+UI, Workflows & Features
+
+ * Allow information carried on the WWW-Authenticate header to be
+   passed to the credential helpers.
+
+ * A new "fetch.hideRefs" option can be used to exclude specified refs
+   from "rev-list --objects --stdin --not --all" traversal for
+   checking object connectivity, most useful when there are many
+   unrelated histories in a single repository.
+
+ * "git push" has been taught to allow deletion of refs with one-level
+   names to help repairing a repository who acquired such a ref by
+   mistake.  In general, we don't encourage use of such a ref, and
+   creation or update to such a ref is rejected as before.
+
+ * Allow "git bisect reset" to check out the original branch when the
+   branch is already checked out in a different worktree linked to the
+   same repository.
+
+ * A few subcommands have been taught to stop users from working on a
+   branch that is being used in another worktree linked to the same
+   repository.
+
+ * "git format-patch" learned to write a log-message only output file
+   for empty commits.
+
+ * "git format-patch" honors the src/dst prefixes set to nonstandard
+   values with configuration variables like "diff.noprefix", causing
+   receiving end of the patch that expects the standard -p1 format to
+   break.  "format-patch" has been taught to ignore end-user configuration
+   and always use the standard prefixes.
+
+   This is a backward compatibility breaking change.
+
+ * Lift the limitation that colored prompts can only be used with
+   PROMPT_COMMAND mode.
+
+ * "git blame --contents=<file> <rev> -- <path>" used to be forbidden,
+   but now it finds the origins of lines starting at <file> contents
+   through the history that leads to <rev>.
+
+ * "git pack-redundant" gave a warning when run, as the command has
+   outlived its usefulness long ago and is nominated for future
+   removal.  Now we escalate to give an error.
+
+ * "git clone" from an empty repository learned to propagate the
+   choice of the hash algorithm from the source repository to the
+   newly created repository over any one of the v0/v1/v2 protocol.
+
+ * "git mergetool" and "git difftool" learns a new configuration
+   guiDefault to optionally favor configured guitool over non-gui-tool
+   automatically when $DISPLAY is set.
+
+ * "git branch -d origin/master" would say "no such branch", but it is
+   likely a missed "-r" if refs/remotes/origin/master exists.  The
+   command has been taught to give such a hint in its error message.
+
+ * Clean-up of the code path that deals with merge strategy option
+   handling in "git rebase".
+
+ * "git clone --local" stops copying from an original repository that
+   has symbolic links inside its $GIT_DIR; an error message when that
+   happens has been updated.
+
+ * The "--format=..." option of "git for-each-ref", "git branch", and
+   "git tag" commands learn "--omit-empty" to hide refs whose
+   formatting results in an empty string from the output.
+
+ * The sendemail-validate validate hook learned to pass the total
+   number of input files and where in the sequence each invocation is
+   via environment variables.
+
+ * When "gc" needs to retain unreachable objects, packing them into
+   cruft packs (instead of exploding them into loose object files) has
+   been offered as a more efficient option for some time.  Now the use
+   of cruft packs has been made the default and no longer considered
+   an experimental feature.
+
+ * The output given by "git blame" that attributes a line to contents
+   taken from the file specified by the "--contents" option shows it
+   differently from a line attributed to the working tree file.
+
+ * "git send-email" learned to give the e-mail headers to the validate
+   hook by passing an extra argument from the command line.
+
+ * The credential subsystem learns to help OAuth framework.
+
+ * The titles of manual pages used to be chomped at an unreasonably
+   short limit, which has been removed.
+
+ * Error messages given when working on an unborn branch that is
+   checked out in another worktree have been improved.
+
+ * The documentation was misleading about the interaction between
+   GIT_DEFAULT_HASH and "git clone", which has been clarified to
+   stress that the variable is to be ignored by the command.
+
+ * "git send-email" learned "--header-cmd=<cmd>" that can inject
+   arbitrary e-mail header lines to the outgoing messages.
+
+ * "git fsck" learned to detect bit-flip breakages in the reachability
+   bitmap files.
+
+ * The "--stdin" option of "git name-rev" has been replaced with
+   the "--annotate-stdin" option more than a year ago.  We stop
+   advertising it in the "git name-rev -h" output.
+
+ * "git push --all" gained an alias "git push --branches".
+
+ * "git fetch" learned the "--porcelain" option that emits what it did
+   in a machine-parseable format.
+
+ * "git --attr-source=<tree> cmd $args" is a new way to have any
+   command to read attributes not from the working tree but from the
+   given tree object.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Code clean-up to clarify directory traversal API.
+
+ * Code clean-up to clarify the rule that "git-compat-util.h" must be
+   the first to be included.
+
+ * More work towards -Wunused.
+
+ * Instead of forcing each command to choose to honor GPG related
+   configuration variables, make the subsystem lazily initialize
+   itself.
+
+ * Remove workaround for ancient versions of DocBook to make it work
+   correctly with groff, which has not been necessary since docbook
+   1.76 from 2010.
+
+ * Code clean-up to include and/or uninclude parse-options.h file as
+   needed.
+
+ * The code path that reports what "git fetch" did to each ref has
+   been cleaned up.
+
+ * Assorted config API updates.
+
+ * A few configuration variables to tell the cURL library that
+   different types of ssl-cert and ssl-key are in use have been added.
+
+ * Split key function and data structure definitions out of cache.h to
+   new header files and adjust the users.
+
+ * "git fetch --all" does not have to download and handle the same
+   bundleURI over and over, which has been corrected.
+
+ * "git sparse-checkout" command learns a debugging aid for the sparse
+   rule definitions.
+
+ * "git write-tree" learns to work better with sparse-index.
+
+ * The on-disk reverse index that allows mapping from the pack offset
+   to the object name for the object stored at the offset has been
+   enabled by default.
+
+ * "git fsck" learned to validate the on-disk pack reverse index files.
+
+ * strtok() and strtok_r() are banned in this codebase.
+
+ * The detect-compilers script to help auto-tweaking the build system
+   had trouble working with compilers whose version number has extra
+   suffixes.  The script has been taught that certain suffixes (like
+   "-win32" in "gcc 10-win32") can be safely stripped as they share
+   the same features and bugs with the version without the suffix.
+
+ * ctype tests have been taught to test EOF, too.
+
+ * The implementation of credential helpers used fgets() over fixed
+   size buffers to read protocol messages, causing the remainder of
+   the folded long line to trigger unexpected behaviour, which has
+   been corrected.
+
+ * The implementation of the default "negotiator", used to find common
+   ancestor over the network for object tranfer, used to be recursive;
+   it was updated to be iterative to conserve stackspace usage.
+
+ * Our custom callout formatter is no longer used in the documentation
+   formatting toolchain, as the upstream default ones give better
+   output these days.
+
+ * The tracing mechanism learned to notice and report when
+   auto-discovered bare repositories are being used, as allowing so
+   without explicitly stating the user intends to do so (with setting
+   GIT_DIR for example) can be used with social engineering as an
+   attack vector.
+
+ * "git diff-files" learned not to expand sparse-index unless needed.
+
+
+Fixes since v2.40
+-----------------
+
+ * "git fsck" learned to check the index files in other worktrees,
+   just like "git gc" honors them as anchoring points.
+   (merge 8d3e7eac52 jk/fsck-indices-in-worktrees later to maint).
+
+ * Fix a segfaulting loop.  The function and its caller may need
+   further clean-up.
+   (merge c5773dc078 ew/commit-reach-clean-up-flags-fix later to maint).
+
+ * "git restore" supports options like "--ours" that are only
+   meaningful during a conflicted merge, but these options are only
+   meaningful when updating the working tree files.  These options are
+   marked to be incompatible when both "--staged" and "--worktree" are
+   in effect.
+   (merge ee8a88826a ak/restore-both-incompatible-with-conflicts later to maint).
+
+ * Simplify UI to control progress meter given by "git bundle" command.
+   (merge 8b95521edb jk/bundle-progress later to maint).
+
+ * "git bundle" learned that "-" is a common way to say that the input
+   comes from the standard input and/or the output goes to the
+   standard output.  It used to work only for output and only from the
+   root level of the working tree.
+   (merge 0bbe10313e jk/bundle-use-dash-for-stdfiles later to maint).
+
+ * Once we start running, we assumed that the list of alternate object
+   databases would never change.  Hook into the machinery used to
+   update the list of packfiles during runtime to update this list as
+   well.
+   (merge e2d003dbed ds/reprepare-alternates-when-repreparing-packfiles later to maint).
+
+ * The code to parse "git rebase -X<opt>" was not prepared to see an
+   unparsable option string, which has been corrected.
+   (merge 15a4cc912e ab/fix-strategy-opts-parsing later to maint).
+
+ * "git add -p" while the index is unmerged sometimes failed to parse
+   the diff output it internally produces and died, which has been
+   corrected.
+   (merge 28d1122f9c jk/add-p-unmerged-fix later to maint).
+
+ * Fix for a "ls-files --format="%(path)" that produced nonsense
+   output, which was a bug in 2.38.
+   (merge cfb62dd006 aj/ls-files-format-fix later to maint).
+
+ * "git receive-pack" that responds to "git push" requests failed to
+   clean a stale lockfile when killed in the middle, which has been
+   corrected.
+   (merge c55c30669c ps/receive-pack-unlock-before-die later to maint).
+
+ * "git rev-parse --quiet foo@{u}", or anything that asks @{u} to be
+   parsed with GET_OID_QUIETLY option, did not quietly fail, which has
+   been corrected.
+   (merge dfbfdc521d fc/oid-quietly-parse-upstream later to maint).
+
+ * Transports that do not support protocol v2 did not correctly fall
+   back to protocol v0 under certain conditions, which has been
+   corrected.
+   (merge eaa0fd6584 jk/fix-proto-downgrade-to-v0 later to maint).
+
+ * time(2) on glib 2.31+, especially on Linux, goes out of sync with
+   higher resolution timers used for gettimeofday(2) and by the
+   filesystem.  Replace all calls to it with a git_time() wrapper and
+   (merge 370ddcbc89 pe/time-use-gettimeofday later to maint).
+
+ * Code clean-up to use designated initializers in parse-options API.
+   (merge 353e6d4554 sg/parse-options-h-initializers later to maint).
+
+ * A recent-ish change to allow unicode character classes to be used
+   with "grep -P" triggered a JIT bug in older pcre2 libraries.
+   The problematic change in Git built with these older libraries has
+   been disabled to work around the bug.
+   (merge 14b9a04479 mk/workaround-pcre-jit-ucp-bug later to maint).
+
+ * The wildmatch library code unlearns exponential behaviour it
+   acquired some time ago since it was borrowed from rsync.
+   (merge 3dc0b7f0dc pw/wildmatch-fixes later to maint).
+
+ * The index files can become corrupt under certain conditions when
+   the split-index feature is in use, especially together with
+   fsmonitor, which have been corrected.
+   (merge 061dd722dc js/split-index-fixes later to maint).
+
+ * Document what the pathname-looking strings in "rev-list --object"
+   output are for and what they mean.
+   (merge 15364d2a3c jk/document-rev-list-object-name later to maint).
+
+ * Fix unnecessary truncation of generation numbers used in-core.
+   (merge d3af1c193d ps/ahead-behind-truncation-fix later to maint).
+
+ * Code clean-up around the use of the_repository.
+   (merge 4a93b899c1 ab/remove-implicit-use-of-the-repository later to maint).
+
+ * Consistently spell "Message-ID" as such, not "Message-Id".
+   (merge ba4324c4e1 jc/spell-id-in-both-caps-in-message-id later to maint).
+
+ * Correct use of an uninitialized structure member.
+   (merge dc12ee77ab jx/cap-object-info-uninitialized-fix later to maint).
+
+ * Tests had a few places where we ignored PERL_PATH and blindly used
+   /usr/bin/perl, which have been corrected.
+   (merge c1917156a0 jk/use-perl-path-consistently later to maint).
+
+ * Documentation mark-up fix.
+   (merge 78b6369e67 la/mfc-markup-fix later to maint).
+
+ * Doc toolchain update to remove old workaround for AsciiDoc.
+   (merge 8806120de6 fc/remove-header-workarounds-for-asciidoc later to maint).
+
+ * The userdiff regexp patterns for various filetypes that are built
+   into the system have been updated to avoid triggering regexp errors
+   from UTF-8 aware regex engines.
+   (merge be39144954 rs/userdiff-multibyte-regex later to maint).
+
+ * The approxidate() API has been simplified by losing an extra
+   function that did the same thing as another one.
+   (merge 8a7f0b666f rs/remove-approxidate-relative later to maint).
+
+ * Code clean-up to replace a hardcoded constant with a CPP macro.
+   (merge c870de6502 rs/get-tar-commit-id-use-defined-const later to maint).
+
+ * Doc build simplification.
+   (merge 9a09ed3229 fc/doc-stop-using-manversion later to maint).
+
+ * "git archive" run from a subdirectory mishandled attributes and
+   paths outside the current directory.
+   (merge 92b1dd1b9e rs/archive-from-subdirectory-fixes later to maint).
+
+ * The code to parse capability list for v0 on-wire protocol fell into
+   an infinite loop when a capability appears multiple times, which
+   has been corrected.
+
+ * Geometric repacking ("git repack --geometric=<n>") in a repository
+   that borrows from an alternate object database had various corner
+   case bugs, which have been corrected.
+   (merge d85cd18777 ps/fix-geom-repack-with-alternates later to maint).
+
+ * The "%GT" placeholder for the "--format" option of "git log" and
+   friends caused BUG() to trigger on a commit signed with an unknown
+   key, which has been corrected.
+   (merge 7891e46585 jk/gpg-trust-level-fix later to maint).
+
+ * The completion script used to use bare "read" without the "-r"
+   option to read the contents of various state files, which risked
+   getting confused with backslashes in them.  This has been
+   corrected.
+   (merge 197152098a ek/completion-use-read-r-to-read-literally later to maint).
+
+ * A small API fix to the ort merge strategy backend.
+   (merge 000c4ceca7 en/ort-finalize-after-0-merges-fix later to maint).
+
+ * The commit object parser has been taught to be a bit more lenient
+   to parse timestamps on the author/committer line with a malformed
+   author/committer ident.
+   (merge 90ef0f14eb jk/parse-commit-with-malformed-ident later to maint).
+
+ * Retitle a test script with an overly narrow name.
+   (merge 8bb19c14fb ob/t3501-retitle later to maint).
+
+ * Doc update to clarify how text and eol attributes interact to
+   specify the end-of-line conversion.
+   (merge 6696077ace ah/doc-attributes-text later to maint).
+
+ * Gitk updates from GfW project.
+   (merge 99e70f3077 js/gitk-fixes-from-gfw later to maint).
+
+ * "git diff --dirstat" leaked memory, which has been plugged.
+   (merge 83973981eb jc/dirstat-plug-leaks later to maint).
+
+ * "git merge-tree" reads the basic configuration, which can be used
+    by git forges to disable replace-refs feature.
+   (merge b6551feadf ds/merge-tree-use-config later to maint).
+
+ * A few bugs in the sequencer machinery that results in miscounting
+   the steps have been corrected.
+   (merge 170eea9750 js/rebase-count-fixes later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge f7111175df as/doc-markup-fix later to maint).
+   (merge 90ff7c9898 fc/test-aggregation-clean-up later to maint).
+   (merge 9b0c7f308a jc/am-doc-refer-to-format-patch later to maint).
+   (merge b10cbdac4c bb/unicode-width-table-15 later to maint).
+   (merge 3457b50e8c ab/retire-scripted-add-p later to maint).
+   (merge d52fcf493b ds/p2000-fix-grep-sparse later to maint).
+   (merge ec063d2591 ss/hashmap-typofix later to maint).
+   (merge 1aaed69d11 rs/archive-mtime later to maint).
+   (merge 2da2cc9b28 ob/rollback-after-commit-lock-failure later to maint).
+   (merge 54dbd0933b ob/sequencer-save-head-simplify later to maint).
+   (merge a93cbe8d78 ar/test-cleanup-unused-file-creation later to maint).
+   (merge cc48ddd937 jk/chainlint-fixes later to maint).
+   (merge 4833b08426 ow/ref-format-remove-unused-member later to maint).
+   (merge d0ea2ca1cf dw/doc-submittingpatches-grammofix later to maint).
+   (merge fd72637423 ar/t2024-checkout-output-fix later to maint).
+   (merge d45cbe3fe0 ob/sequencer-i18n-fix later to maint).
+   (merge b734fe49fd ob/messages-capitalize-exception later to maint).
+   (merge ad353d7e77 ma/gittutorial-fixes later to maint).
+   (merge a5855fd8d4 ar/test-cleanup-unused-file-creation-part2 later to maint).
+   (merge 0c5308af30 sd/doc-gitignore-and-rm-cached later to maint).
+   (merge cbb83daeaf kh/doc-interpret-trailers-updates later to maint).
+   (merge 3d77fbb664 ar/config-count-tests-updates later to maint).
+   (merge b7cf25c8f4 jc/t9800-fix-use-of-show-s-raw later to maint).
diff --git a/Documentation/RelNotes/2.42.0.txt b/Documentation/RelNotes/2.42.0.txt
new file mode 100644 (file)
index 0000000..0f1897a
--- /dev/null
@@ -0,0 +1,329 @@
+Git v2.42 Release Notes
+=======================
+
+UI, Workflows & Features
+
+ * "git pack-refs" learns "--include" and "--exclude" to tweak the ref
+   hierarchy to be packed using pattern matching.
+
+ * 'git worktree add' learned how to create a worktree based on an
+   orphaned branch with `--orphan`.
+
+ * "git pack-objects" learned to invoke a new hook program that
+   enumerates extra objects to be used as anchoring points to keep
+   otherwise unreachable objects in cruft packs.
+
+ * Add more "git var" for toolsmiths to learn various locations Git is
+   configured with either via the configuration or hard-coded defaults.
+
+ * 'git notes append' was taught '--separator' to specify string to insert
+   between paragraphs.
+
+ * The "git for-each-ref" family of commands learned placeholders
+   related to GPG signature verification.
+
+ * "git diff --no-index" learned to read from named pipes as if they
+   were regular files, to allow "git diff <(process) <(substitution)"
+   some shells support.
+
+ * Help newbies by suggesting that there are cases where force-pushing
+   is a valid and sensible thing to update a branch at a remote
+   repository, rather than reconciling with merge/rebase.
+
+ * "git blame --contents=file" has been taught to work in a bare
+   repository.
+
+ * "git branch -f X" to repoint the branch X said that X was "checked
+   out" in another worktree, even when branch X was not and instead
+   being bisected or rebased.  The message was reworded to say the
+   branch was "in use".
+
+ * Tone down the warning on SHA-256 repositories being an experimental
+   curiosity.  We do not have support for them to interoperate with
+   traditional SHA-1 repositories, but at this point, we do not plan
+   to make breaking changes to SHA-256 repositories and there is no
+   longer need for such a strongly phrased warning.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * "git diff-tree" has been taught to take advantage of the
+   sparse-index feature.
+
+ * Clang's sanitizer implementation seems to work better than GCC's.
+   (merge d88d727143 jk/ci-use-clang-for-sanitizer-jobs later to maint).
+
+ * The object traversal using reachability bitmap done by
+   "pack-object" has been tweaked to take advantage of the fact that
+   using "boundary" commits as representative of all the uninteresting
+   ones can save quite a lot of object enumeration.
+
+ * discover_git_directory() no longer touches the_repository.
+
+ * "git worktree" learned to work better with sparse index feature.
+
+ * When the external merge driver is killed by a signal, its output
+   should not be trusted as a resolution with conflicts that is
+   proposed by the driver, but the code did.
+
+ * The set-up code for the get_revision() API now allows feeding
+   options like --all and --not in the --stdin mode.
+
+ * Move functions that are not about pure string manipulation out of
+   strbuf.[ch]
+
+ * "imap-send" codepaths got cleaned up to get rid of unused
+   parameters.
+
+ * Enumerating refs in the packed-refs file, while excluding refs that
+   match certain patterns, has been optimized.
+
+ * Mark-up unused parameters in the code so that we can eventually
+   enable -Wunused-parameter by default.
+
+ * Instead of inventing a custom counter variables for debugging,
+   use existing trace2 facility in the fsync customization codepath.
+
+ * "git branch --list --format=<format>" and friends are taught
+   a new "%(describe)" placeholder.
+
+ * Clarify how to choose the starting point for a new topic in
+   developer guidance document.
+
+ * The implementation of "get_sha1_hex()" that reads a hexadecimal
+   string that spells a full object name has been extended to cope
+   with any hash function used in the repository, but the "sha1" in
+   its name survived.  Rename it to get_hash_hex(), a name that is
+   more consistent within its friends like get_hash_hex_algop().
+
+ * Command line parser fix, and a small parse-options API update.
+
+
+Fixes since v2.41
+-----------------
+
+ * "git tag" learned to leave the "$GIT_DIR/TAG_EDITMSG" file when the
+   command failed, so that the user can salvage what they typed.
+   (merge 08c12ec1d0 kh/keep-tag-editmsg-upon-failure later to maint).
+
+ * The "-s" (silent, squelch) option of the "diff" family of commands
+   did not interact with other options that specify the output format
+   well.  This has been cleaned up so that it will clear all the
+   formatting options given before.
+   (merge 9d484b92ed jc/diff-s-with-other-options later to maint).
+
+ * Update documentation regarding Coccinelle patches.
+   (merge 3bd0097cfc gc/doc-cocci-updates later to maint).
+
+ * Some atoms that can be used in "--format=<format>" for "git ls-tree"
+   were not supported by "git ls-files", even though they were relevant
+   in the context of the latter.
+   (merge 4d28c4f75f zh/ls-files-format-atoms later to maint).
+
+ * Document more pseudo-refs and teach the command line completion
+   machinery to complete AUTO_MERGE.
+   (merge 982ff3a649 pb/complete-and-document-auto-merge-and-friends later to maint).
+
+ * "git submodule" code trusted the data coming from the config (and
+   the in-tree .gitmodules file) too much without validating, leading
+   to NULL dereference if the user mucks with a repository (e.g.
+   submodule.<name>.url is removed).  This has been corrected.
+   (merge fbc806acd1 tb/submodule-null-deref-fix later to maint).
+
+ * The value of config.worktree is per-repository, but has been kept
+   in a singleton global variable per process. This has been OK as
+   most Git operations interacted with a single repository at a time,
+   but not right for operations like recursive "grep" that want to
+   access multiple repositories from a single process without forking.
+
+   The global variable has been eliminated and made into a member in
+   the per-repository data structure.
+   (merge 3867f6d650 vd/worktree-config-is-per-repository later to maint).
+
+ * "git [-c log.follow=true] log [--follow] ':(glob)f**'" used to barf.
+   (merge 8260bc5902 jk/log-follow-with-non-literal-pathspec later to maint).
+
+ * Introduce a mechanism to disable replace refs globally and per
+   repository.
+   (merge 9c7d1b057f ds/disable-replace-refs later to maint).
+
+ * "git cat-file --batch" and friends learned "-Z" that uses NUL
+   delimiter for both input and output.
+   (merge f79e18849b ps/cat-file-null-output later to maint).
+
+ * The reimplemented "git add -i" did not honor color.ui configuration.
+   (merge 6f74648cea ds/add-i-color-configuration-fix later to maint).
+
+ * Compilation fix for platforms without D_TYPE in struct dirent.
+   (merge 03bf92b9bf as/dtype-compilation-fix later to maint).
+
+ * Suggest to refrain from using hex literals that are non-portable
+   when writing printf(1) format strings.
+   (merge f0b68f0546 jt/doc-use-octal-with-printf later to maint).
+
+ * Simplify error message when run-command fails to start a command.
+   (merge 6d224ac286 rs/run-command-exec-error-on-noent later to maint).
+
+ * Gracefully deal with a stale MIDX file that lists a packfile that
+   no longer exists.
+   (merge 06f3867865 tb/open-midx-bitmap-fallback later to maint).
+
+ * Even when diff.ignoreSubmodules tells us to ignore submodule
+   changes, "git commit" with an index that already records changes to
+   submodules should include the submodule changes in the resulting
+   commit, but it did not.
+   (merge 5768478edc js/defeat-ignore-submodules-config-with-explicit-addition later to maint).
+
+ * When "git commit --trailer=..." invokes the interpret-trailers
+   machinery, it knows what it feeds to interpret-trailers is a full
+   log message without any patch, but failed to express that by
+   passing the "--no-divider" option, which has been corrected.
+   (merge be3d654343 jk/commit-use-no-divider-with-interpret-trailers later to maint).
+
+ * Avoid breakage of "git pack-objects --cruft" due to inconsistency
+   between the way the code enumerates packfiles in the repository.
+   (merge 73320e49ad tb/collect-pack-filenames-fix later to maint).
+
+ * We create .pack and then .idx, we consider only packfiles that have
+   .idx usable (those with only .pack are not ready yet), so we should
+   remove .idx before removing .pack for consistency.
+   (merge 0dd1324a73 ds/remove-idx-before-pack later to maint).
+
+ * Partially revert a sanity check that the rest of the config code
+   was not ready, to avoid triggering it in a corner case.
+   (merge a53f43f900 gc/config-partial-submodule-kvi-fix later to maint).
+
+ * "git apply" punts when it is fed too large a patch input; the error
+   message it gives when it happens has been clarified.
+   (merge 42612e18d2 pw/apply-too-large later to maint).
+
+ * During a cherry-pick or revert session that works on multiple
+   commits, "git status" did not give correct information, which has
+   been corrected.
+   (merge a096a889f4 jk/cherry-pick-revert-status later to maint).
+
+ * A few places failed to differentiate the case where the index is
+   truly empty (nothing added) and we haven't yet read from the
+   on-disk index file, which have been corrected.
+   (merge 2ee045eea1 js/empty-index-fixes later to maint).
+
+ * "git bugreport" tests did not test what it wanted to test, which
+   has been corrected.
+   (merge 1aa92b8500 ma/t0091-fixup later to maint).
+
+ * Code snippets in a tutorial document no longer compiled after
+   recent header shuffling, which have been corrected.
+   (merge bbd7c7b7c0 vd/adjust-mfow-doc-to-updated-headers later to maint).
+
+ * "git ls-files '(attr:X)D/'" that triggers the common prefix
+   optimization codepath failed to read from "D/.gitattributes",
+   which has been corrected.
+   (merge f4a8fde057 jc/pathspec-match-with-common-prefix later to maint).
+
+ * "git fsck --no-progress" still spewed noise from the commit-graph
+   subsystem, which has been corrected.
+   (merge 9281cd07f0 tb/fsck-no-progress later to maint).
+
+ * Various offset computation in the code that accesses the packfiles
+   and other data in the object layer has been hardened against
+   arithmetic overflow, especially on 32-bit systems.
+   (merge 9a25cad7e0 tb/object-access-overflow-protection later to maint).
+
+ * Names of MinGW header files are spelled in mixed case in some
+   source files, but the build host can be using case sensitive
+   filesystem with header files with their name spelled in all
+   lowercase.
+   (merge 4a53d0d0bc mh/mingw-case-sensitive-build later to maint).
+
+ * Update message mark-up for i18n in "git bundle".
+   (merge bbb6acd998 dk/bundle-i18n-more later to maint).
+
+ * "git tag --list --points-at X" showed tags that directly refers to
+   object X, but did not list a tag that points at such a tag, which
+   has been corrected.
+
+ * "./configure --with-expat=no" did not work as a way to refuse use
+   of the expat library on a system with the library installed, which
+   has been corrected.
+   (merge fb8f7269c2 ah/autoconf-fixes later to maint).
+
+ * When the user edits "rebase -i" todo file so that it starts with a
+   "fixup", which would make it invalid, the command truncated the
+   rest of the file before giving an error and returning the control
+   back to the user.  Stop truncating to make it easier to correct
+   such a malformed todo file.
+   (merge 9645a087c2 ah/sequencer-rewrite-todo-fix later to maint).
+
+ * Rewrite the description of giving a custom command to the
+   submodule.<name>.update configuration variable.
+   (merge 7cebc5bd78 pv/doc-submodule-update-settings later to maint).
+
+ * Adjust to OpenSSL 3+, which deprecates its SHA-1 functions based on
+   its traditional API, by using its EVP API instead.
+   (merge bda9c12073 ew/hash-with-openssl-evp later to maint).
+
+ * Exclude "." from the set of characters to be removed from the
+   beginning and the end of the human-readable name.
+   (merge 1c04cb0744 bc/ident-dot-is-no-longer-crud-letter later to maint).
+
+ * "git bisect visualize" stopped running "gitk" on Git for Windows
+   when the command was reimplemented in C around Git 2.34 timeframe.
+   This has been corrected.
+   (merge fff1594fa7 ma/locate-in-path-for-windows later to maint).
+
+ * "git rebase -i" with a series of squash/fixup, when one of the
+   steps stopped in conflicts and ended up getting skipped, did not
+   handle the accumulated commit log messages, which has been
+   corrected.
+   (merge 6ce7afe163 pw/rebase-skip-commit-message-fix later to maint).
+
+ * Adjust to newer Term::ReadLine to prevent it from breaking
+   the interactive prompt code in send-email.
+   (merge c016726c2d jk/send-email-with-new-readline later to maint).
+
+ * Windows updates.
+   (merge 0050f8e401 ds/maintenance-on-windows-fix later to maint).
+
+ * Correct use of lstat() that assumed a failing call would not
+   clobber the statbuf.
+   (merge 72695d8214 st/mv-lstat-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 51f9d2e563 sa/doc-ls-remote later to maint).
+   (merge c6d26a9dda jk/format-patch-message-id-unleak later to maint).
+   (merge f7e063f326 ps/fetch-cleanups later to maint).
+   (merge e4cf013468 tl/quote-problematic-arg-for-clarity later to maint).
+   (merge 20025fdfc7 tz/test-ssh-verifytime-fix later to maint).
+   (merge e48a21df65 tz/test-fix-pthreads-prereq later to maint).
+   (merge 68b51172e3 mh/commit-reach-get-reachable-plug-leak later to maint).
+   (merge aeee1408ce kh/use-default-notes-doc later to maint).
+   (merge 3b8724bce6 jc/test-modernization later to maint).
+   (merge 447a3b7331 jc/test-modernization-2 later to maint).
+   (merge d57fa7fc73 la/doc-interpret-trailers later to maint).
+   (merge 548afb0d9a la/docs-typofixes later to maint).
+   (merge 3744ffcbcd rs/doc-ls-tree-hex-literal later to maint).
+   (merge 6c26da8404 mh/credential-erase-improvements later to maint).
+   (merge 78e56cff69 tz/lib-gpg-prereq-fix later to maint).
+   (merge 80d32e84b5 rj/leakfixes later to maint).
+   (merge 0a868031ed pb/complete-diff-options later to maint).
+   (merge d4f28279ad jc/doc-hash-object-types later to maint).
+   (merge 1876a5ae15 ks/t4205-test-describe-with-abbrev-fix later to maint).
+   (merge 6e6a529b57 jk/fsck-indices-in-worktrees later to maint).
+   (merge 3e81b896f7 rs/packet-length-simplify later to maint).
+   (merge 4c9cb51fe7 mh/doc-credential-helpers later to maint).
+   (merge 3437f549dd jr/gitignore-doc-example-markup later to maint).
+   (merge 947ebd62a0 jc/am-parseopt-fix later to maint).
+   (merge e12cb98e1e jc/branch-parseopt-fix later to maint).
+   (merge d6f598e443 jc/gitignore-doc-pattern-markup later to maint).
+   (merge a2dad4868b jc/transport-parseopt-fix later to maint).
+   (merge 68cbb20e73 jc/parse-options-show-branch later to maint).
+   (merge 3821eb6c3d jc/parse-options-reset later to maint).
+   (merge c48af99a3e bb/trace2-comment-fix later to maint).
+   (merge c95ae3ff9c rs/describe-parseopt-fix later to maint).
+   (merge 36f76d2a25 rs/pack-objects-parseopt-fix later to maint).
+   (merge 30c8c55cbf jc/tree-walk-drop-base-offset later to maint).
+   (merge d089a06421 rs/bundle-parseopt-cleanup later to maint).
+   (merge 823839bda1 ew/sha256-gcrypt-leak-fixes later to maint).
+   (merge a5c01603b3 bc/ignore-clangd-cache later to maint).
+   (merge 12009a182b js/allow-t4000-to-be-indented-with-spaces later to maint).
+   (merge b3dcd24b8a jc/send-email-pre-process-fix later to maint).
diff --git a/Documentation/RelNotes/2.42.1.txt b/Documentation/RelNotes/2.42.1.txt
new file mode 100644 (file)
index 0000000..3d391b7
--- /dev/null
@@ -0,0 +1,88 @@
+Git 2.42.1 Release Notes
+========================
+
+There is nothing exciting to see here.  Relative to Git 2.42, this
+release contains the fixes that have already been merged to the
+'master' branch of the development towards Git 2.43 that has been
+tagged as Git 2.43.0-rc0.
+
+Fixes since Git 2.42.0
+----------------------
+
+ * Tests that are known to pass with LSan are now marked as such.
+
+ * Flaky "git p4" tests, as well as "git svn" tests, are now skipped
+   in the (rather expensive) sanitizer CI job.
+
+ * Tests with LSan from time to time seem to emit harmless message
+   that makes our tests unnecessarily flaky; we work it around by
+   filtering the uninteresting output.
+
+ * GitHub CI workflow has learned to trigger Coverity check.
+
+ * Overly long label names used in the sequencer machinery are now
+   chopped to fit under filesystem limitation.
+
+ * Scalar updates.
+
+ * Tweak GitHub Actions CI so that pushing the same commit to multiple
+   branch tips at the same time will not waste building and testing
+   the same thing twice.
+
+ * The commit-graph verification code that detects mixture of zero and
+   non-zero generation numbers has been updated.
+
+ * "git diff -w --exit-code" with various options did not work
+   correctly, which is being addressed.
+
+ * transfer.unpackLimit ought to be used as a fallback, but overrode
+   fetch.unpackLimit and receive.unpackLimit instead.
+
+ * The use of API between two calls to require_clean_work_tree() from
+   the sequencer code has been cleaned up for consistency.
+
+ * "git diff --no-such-option" and other corner cases around the exit
+   status of the "diff" command has been corrected.
+
+ * "git for-each-ref --sort='contents:size'" sorts the refs according
+   to size numerically, giving a ref that points at a blob twelve-byte
+   (12) long before showing a blob hundred-byte (100) long.
+
+ * Various fixes to the behavior of "rebase -i" when the command got
+   interrupted by conflicting changes.
+
+ * References from description of the `--patch` option in various
+   manual pages have been simplified and improved.
+
+ * "git grep -e A --no-or -e B" is accepted, even though the negation
+   of "or" did not mean anything, which has been tightened.
+
+ * The completion script (in contrib/) has been taught to treat the
+   "-t" option to "git checkout" and "git switch" just like the
+   "--track" option, to complete remote-tracking branches.
+
+ * "git diff --no-index -R <(one) <(two)" did not work correctly,
+   which has been corrected.
+
+ * Update "git maintenance" timers' implementation based on systemd
+   timers to work with WSL.
+
+ * "git diff --cached" codepath did not fill the necessary stat
+   information for a file when fsmonitor knows it is clean and ended
+   up behaving as if it is not clean, which has been corrected.
+
+ * Clarify how "alias.foo = : git cmd ; aliased-command-string" should
+   be spelled with necessary whitespaces around punctuation marks to
+   work.
+
+ * HTTP Header redaction code has been adjusted for a newer version of
+   cURL library that shows its traces differently from earlier
+   versions.
+
+ * An error message given by "git send-email" when given a malformed
+   address did not give correct information, which has been corrected.
+
+ * UBSan options were not propagated through the test framework to git
+   run via the httpd, unlike ASan options, which has been corrected.
+
+Also contains various documentation updates, code clean-ups and minor fixups.
diff --git a/Documentation/RelNotes/2.43.0.txt b/Documentation/RelNotes/2.43.0.txt
new file mode 100644 (file)
index 0000000..e0e5b53
--- /dev/null
@@ -0,0 +1,323 @@
+Git v2.43 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+ * The "--rfc" option of "git format-patch" used to be a valid way to
+   override an earlier "--subject-prefix=<something>" on the command
+   line and replace it with "[RFC PATCH]", but from this release, it
+   merely prefixes the string "RFC " in front of the given subject
+   prefix.  If you are negatively affected by this change, please use
+   "--subject-prefix=PATCH --rfc" as a replacement.
+
+ * In Git 2.42, "git rev-list --stdin" learned to take non-revisions
+   (like "--not") from the standard input, but the way such a "--not" was
+   handled was quite confusing, which has been rethought.  The updated
+   rule is that "--not" given from the command line only affects revs
+   given from the command line that comes but not revs read from the
+   standard input, and "--not" read from the standard input affects
+   revs given from the standard input and not revs given from the
+   command line.
+
+UI, Workflows & Features
+
+ * A message written in olden time prevented a branch from getting
+   checked out, saying it is already checked out elsewhere. But these
+   days, we treat a branch that is being bisected or rebased just like
+   a branch that is checked out and protect it from getting modified
+   with the same codepath.  The message has been rephrased to say that
+   the branch is "in use" to avoid confusion.
+
+ * Hourly and other schedules of "git maintenance" jobs are randomly
+   distributed now.
+
+ * "git cmd -h" learned to signal which options can be negated by
+   listing such options like "--[no-]opt".
+
+ * The way authentication related data other than passwords (e.g.,
+   oauth token and password expiration data) are stored in libsecret
+   keyrings has been rethought.
+
+ * Update the libsecret and wincred credential helpers to correctly
+   match which credential to erase; they erased the wrong entry in
+   some cases.
+
+ * Git GUI updates.
+
+ * "git format-patch" learned a new "--description-file" option that
+   lets cover letter description to be fed; this can be used on
+   detached HEAD where there is no branch description available, and
+   also can override the branch description if there is one.
+
+ * Use of the "--max-pack-size" option to allow multiple packfiles to
+   be created is now supported even when we are sending unreachable
+   objects to cruft packs.
+
+ * "git format-patch --rfc --subject-prefix=<foo>" used to ignore the
+   "--subject-prefix" option and used "[RFC PATCH]"; now we will add
+   "RFC" prefix to whatever subject prefix is specified.
+
+ * "git log --format" has been taught the %(decorate) placeholder for
+   further customization over what the "--decorate" option offers.
+
+ * The default log message created by "git revert", when reverting a
+   commit that records a revert, has been tweaked, to encourage people
+   to describe complex "revert of revert of revert" situations better in
+   their own words.
+
+ * The command-line completion support (in contrib/) learned to
+   complete "git commit --trailer=" for possible trailer keys.
+
+ * "git update-index" learned the "--show-index-version" option to
+   inspect the index format version used by the on-disk index file.
+
+ * "git diff" learned the "diff.statNameWidth" configuration variable,
+   to give the default width for the name part in the "--stat" output.
+
+ * "git range-diff --notes=foo" compared "log --notes=foo --notes" of
+   the two ranges, instead of using just the specified notes tree,
+   which has been corrected to use only the specified notes tree.
+
+ * The command line completion script (in contrib/) can be told to
+   complete aliases by including ": git <cmd> ;" in the alias to tell
+   it that the alias should be completed in a similar way to how "git
+   <cmd>" is completed.  The parsing code for the alias has been
+   loosened to allow ';' without an extra space before it.
+
+ * "git for-each-ref" and friends learned to apply mailmap to
+   authorname and other fields in a more flexible way than using
+   separate placeholder letters like %a[eElL] every time we want to
+   come up with small variants.
+
+ * "git repack" machinery learned to pay attention to the "--filter="
+   option.
+
+ * "git repack" learned the "--max-cruft-size" option to prevent cruft
+   packs from growing without bounds.
+
+ * "git merge-tree" learned to take strategy backend specific options
+   via the "-X" option, like "git merge" does.
+
+ * "git log" and friends learned the "--dd" option that is a
+   short-hand for "--diff-merges=first-parent -p".
+
+ * The attribute subsystem learned to honor the "attr.tree"
+   configuration variable that specifies which tree to read the
+   .gitattributes files from.
+
+ * "git merge-file" learns a mode to read three variants of the
+   contents to be merged from blob objects.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * "git check-attr" has been taught to work better with sparse-index.
+
+ * It may be tempting to leave the help text NULL for a command line
+   option that is either hidden or too obvious, but "git subcmd -h"
+   and "git subcmd --help-all" would have segfaulted if done so.  Now
+   the help text is truly optional.
+
+ * Tests that are known to pass with LSan are now marked as such.
+
+ * Flaky "git p4" tests, as well as "git svn" tests, are now skipped
+   in the (rather expensive) sanitizer CI job.
+
+ * Tests with LSan from time to time seem to emit harmless messages
+   that make our tests unnecessarily flaky; we work around it by
+   filtering the uninteresting output.
+
+ * Unused parameters to functions are marked as such, and/or removed,
+   in order to bring us closer to "-Wunused-parameter" clean.
+
+ * The code to keep track of existing packs in the repository while
+   repacking has been refactored.
+
+ * The "streaming" interface used for bulk-checkin codepath has been
+   narrowed to take only blob objects for now, with no real loss of
+   functionality.
+
+ * GitHub CI workflow has learned to trigger Coverity check.
+
+ * Test coverage for trailers has been improved.
+
+ * The code to iterate over loose references has been optimized to
+   reduce the number of lstat() system calls.
+
+ * The codepaths that read "chunk" formatted files have been corrected
+   to pay attention to the chunk size and notice broken files.
+
+ * Replace macos-12 used at GitHub CI with macos-13.
+   (merge 682a868f67 js/ci-use-macos-13 later to maint).
+
+
+Fixes since v2.42
+-----------------
+
+ * Overly long label names used in the sequencer machinery are now
+   chopped to fit under filesystem limitation.
+
+ * Scalar updates.
+
+ * Tweak GitHub Actions CI so that pushing the same commit to multiple
+   branch tips at the same time will not waste building and testing
+   the same thing twice.
+
+ * The commit-graph verification code that detects a mixture of zero and
+   non-zero generation numbers has been updated.
+
+ * "git diff -w --exit-code" with various options did not work
+   correctly, which has been corrected.
+
+ * The "transfer.unpackLimit" configuration variable ought to be used
+   as a fallback, but overrode the more specific "fetch.unpackLimit"
+   and "receive.unpackLimit" configuration variables by mistake, which
+   has been corrected.
+
+ * The use of API between two calls to require_clean_work_tree() from
+   the sequencer code has been cleaned up for consistency.
+
+ * "git diff --no-such-option" and other corner cases around the exit
+   status of the "diff" command have been corrected.
+
+ * "git for-each-ref --sort='contents:size'" sorted the refs according
+   to size numerically, giving a ref that points at a blob twelve-byte
+   (12) long before showing a blob hundred-byte (100) long, which has
+   been corrected.
+
+ * We now limit the depth of the tree objects and maximum length of
+   pathnames recorded in tree objects.
+   (merge 4d5693ba05 jk/tree-name-and-depth-limit later to maint).
+
+ * Various fixes to the behavior of "rebase -i", when the command got
+   interrupted by conflicting changes, have been made.
+
+ * References from a description of the `--patch` option in various
+   manual pages have been simplified and improved.
+
+ * "git grep -e A --no-or -e B" is accepted, even though the negation
+   of the "--or" option did not mean anything, which has been tightened.
+
+ * The completion script (in contrib/) has been taught to treat the
+   "-t" option to "git checkout" and "git switch" just like the
+   "--track" option, to complete remote-tracking branches.
+
+ * "git diff --no-index -R <(one) <(two)" did not work correctly,
+   which has been corrected.
+
+ * "git maintenance" timers' implementation has been updated, based on
+   systemd timers, to work with WSL.
+
+ * "git diff --cached" codepath did not fill the necessary stat
+   information for a file when fsmonitor knows it is clean and ended
+   up behaving as if it were not clean, which has been corrected.
+
+ * How "alias.foo = : git cmd ; aliased-command-string" should be
+   spelled with necessary whitespace around punctuation marks to work
+   has been more clearly documented (but this will be moot with newer
+   versions of Git where the parsing rules have been improved).
+
+ * HTTP Header redaction code has been adjusted for a newer version of
+   cURL library that shows its traces differently from earlier
+   versions.
+
+ * An error message given by "git send-email", when given a malformed
+   address, did not show the offending address, which has been corrected.
+
+ * UBSan options were not propagated through the test framework to git
+   run via the httpd, unlike ASan options, which has been corrected.
+
+ * "checkout --merge -- path" and "update-index --unresolve path" did
+   not resurrect conflicted state that was resolved to remove path,
+   but now they do.
+   (merge 5bdedac3c7 jc/unresolve-removal later to maint).
+
+ * The display width table for unicode characters has been updated for
+   Unicode 15.1
+   (merge 872976c37e bb/unicode-width-table-15 later to maint).
+
+ * Update mailmap entry for Derrick.
+   (merge 6e5457d8c7 ds/mailmap-entry-update later to maint).
+
+ * In the ".gitmodules" files, submodules are keyed by their names,
+   and the path to the submodule whose name is $name is specified by
+   the submodule.$name.path variable.  There were a few codepaths that
+   mixed the name and path up when consulting the submodule database,
+   which have been corrected.  It took long for these bugs to be found
+   as the name of a submodule initially is the same as its path, and
+   the problem does not surface until it is moved to a different path,
+   which apparently happens very rarely.
+
+ * "git diff --merge-base X other args..." insisted that X must be a
+   commit and errored out when given an annotated tag that peels to a
+   commit, but we only need it to be a committish.  This has been
+   corrected.
+   (merge 4adceb5a29 ar/diff-index-merge-base-fix later to maint).
+
+ * "git merge-tree" used to segfault when the "--attr-source"
+   option is used, which has been corrected.
+   (merge e95bafc52f jc/merge-ort-attr-index-fix later to maint).
+
+ * Unlike "git log --pretty=%D", "git log --pretty="%(decorate)" did
+   not auto-initialize the decoration subsystem, which has been
+   corrected.
+
+ * Feeding "git stash store" with a random commit that was not created
+   by "git stash create" now errors out.
+   (merge d9b6634589 jc/fail-stash-to-store-non-stash later to maint).
+
+ * The index file has room only for the lower 32-bit of the file size in
+   the cached stat information, which means cached stat information
+   will have 0 in its sd_size member for a file whose size is a multiple
+   of 4GiB.  This is mistaken for a racily clean path.  Avoid it by
+   storing a bogus sd_size value instead for such files.
+   (merge 5143ac07b1 bc/racy-4gb-files later to maint).
+
+ * "git p4" tried to store symlinks to LFS when told, but has been
+   fixed not to do so, because it does not make sense.
+   (merge 10c89a02b0 mm/p4-symlink-with-lfs later to maint).
+
+ * The codepath to handle recipient addresses `git send-email
+   --compose` learns from the user was completely broken, which has
+   been corrected.
+   (merge 3ec6167567 jk/send-email-fix-addresses-from-composed-messages later to maint).
+
+ * "cd sub && git grep -f patterns" tried to read "patterns" file at
+   the top level of the working tree; it has been corrected to read
+   "sub/patterns" instead.
+
+ * "git reflog expire --single-worktree" has been broken for the past
+   20 months or so, which has been corrected.
+
+ * "git send-email" did not have certain pieces of data computed yet
+   when it tried to validate the outgoing messages and its recipient
+   addresses, which has been sorted out.
+
+ * "git bugreport" learned to complain when it received a command line
+   argument that it will not use.
+
+ * The codepath to traverse the commit-graph learned to notice that a
+   commit is missing (e.g., corrupt repository lost an object), even
+   though it knows something about the commit (like its parents) from
+   what is in commit-graph.
+   (merge 7a5d604443 ps/do-not-trust-commit-graph-blindly-for-existence later to maint).
+
+ * "git rev-list --missing" did not work for missing commit objects,
+   which has been corrected.
+
+ * "git rev-list --unpacked --objects" failed to exclude packed
+   non-commit objects, which has been corrected.
+   (merge 7b3c8e9f38 tb/rev-list-unpacked-fix later to maint).
+
+ * "To dereference" and "to peel" were sometimes used in in-code
+   comments and documentation but without description in the glossary.
+   (merge 893dce2ffb vd/glossary-dereference-peel later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge c2c349a15c xz/commit-title-soft-limit-doc later to maint).
+   (merge 1bd809938a tb/format-pack-doc-update later to maint).
+   (merge 8f81532599 an/clang-format-typofix later to maint).
+   (merge 3ca86adc2d la/strvec-header-fix later to maint).
+   (merge 6789275d37 jc/test-i18ngrep later to maint).
+   (merge 9972cd6004 ps/leakfixes later to maint).
+   (merge 46edab516b tz/send-email-helpfix later to maint).
diff --git a/Documentation/RelNotes/2.44.0.txt b/Documentation/RelNotes/2.44.0.txt
new file mode 100644 (file)
index 0000000..4dda977
--- /dev/null
@@ -0,0 +1,216 @@
+Git v2.44 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+ * "git chekcout -B <branch>" used to allow switching to a branch that
+   is in use on another worktree, but this was by mistake.  The users
+   need to use "--ignore-other-worktrees" option.
+
+
+UI, Workflows & Features
+
+ * "git add" and "git stash" learned to support the ":(attr:...)"
+   magic pathspec.
+
+ * "git rebase --autosquash" is now enabled for non-interactive rebase,
+   but it is still incompatible with the apply backend.
+
+ * Introduce "git replay", a tool meant on the server side without
+   working tree to recreate a history.
+
+ * "git merge-file" learned to take the "--diff-algorithm" option to
+   use algorithm different from the default "myers" diff.
+
+ * Command line completion (in contrib/) learned to complete path
+   arguments to the "add/set" subcommands of "git sparse-checkout"
+   better.
+
+ * "git checkout -B <branch> [<start-point>]" allowed a branch that is
+   in use in another worktree to be updated and checked out, which
+   might be a bit unexpected.  The rule has been tightened, which is a
+   breaking change.  "--ignore-other-worktrees" option is required to
+   unbreak you, if you are used to the current behaviour that "-B"
+   overrides the safety.
+
+ * The builtin_objectmode attribute is populated for each path
+   without adding anything in .gitattributes files, which would be
+   useful in magic pathspec, e.g., ":(attr:builtin_objectmode=100755)"
+   to limit to executables.
+
+ * "git fetch" learned to pay attention to "fetch.all" configuration
+   variable, which pretends as if "--all" was passed from the command
+   line when no remote parameter was given.
+
+ * In addition to (rather cryptic) Security Identifiers, show username
+   and domain in the error message when we barf on mismatch between
+   the Git directory and the current user on Windows.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Process to add some form of low-level unit tests has started.
+
+ * Add support for GitLab CI.
+
+ * "git for-each-ref --no-sort" still sorted the refs alphabetically
+   which paid non-trivial cost.  It has been redefined to show output
+   in an unspecified order, to allow certain optimizations to take
+   advantage of.
+
+ * Simplify API implementation to delete references by eliminating
+   duplication.
+
+ * Subject approxidate() and show_date() machinery to OSS-Fuzz.
+
+ * A new helper to let us pretend that we called lstat() when we know
+   our cache_entry is up-to-date via fsmonitor.
+
+ * The optimization based on fsmonitor in the "diff --cached"
+   codepath is resurrected with the "fake-lstat" introduced earlier.
+
+ * Test balloon to use C99 "bool" type from <stdbool.h> has been
+   added.
+
+ * "git clone" has been prepared to allow cloning a repository with
+   non-default hash function into a repository that uses the reftable
+   backend.
+
+ * Streaming spans of packfile data used to be done only from a
+   single, primary, pack in a repository with multiple packfiles.  It
+   has been extended to allow reuse from other packfiles, too.
+
+
+Fixes since v2.43
+-----------------
+
+ * The way CI testing used "prove" could lead to running the test
+   suite twice needlessly, which has been corrected.
+   (merge e7e03ef995 js/ci-discard-prove-state later to maint).
+
+ * Update ref-related tests.
+
+ * "git format-patch --encode-email-headers" ignored the option when
+   preparing the cover letter, which has been corrected.
+
+ * Newer versions of Getopt::Long started giving warnings against our
+   (ab)use of it in "git send-email".  Bump the minimum version
+   requirement for Perl to 5.8.1 (from September 2002) to allow
+   simplifying our implementation.
+   (merge 6ff658cc78 tz/send-email-negatable-options later to maint).
+
+ * Earlier we stopped relying on commit-graph that (still) records
+   information about commits that are lost from the object store,
+   which has negative performance implications.  The default has been
+   flipped to disable this pessimization.
+   (merge b1df3b3867 ps/commit-graph-less-paranoid later to maint).
+
+ * Stale URLs have been updated to their current counterparts (or
+   archive.org) and HTTP links are replaced with working HTTPS links.
+   (merge 62b4f7b9c6 js/update-urls-in-doc-and-comment later to maint).
+
+ * trace2 streams used to record the URLs that potentially embed
+   authentication material, which has been corrected.
+   (merge 16fa3eebc0 jh/trace2-redact-auth later to maint).
+
+ * The sample pre-commit hook that tries to catch introduction of new
+   paths that use potentially non-portable characters did not notice
+   an existing path getting renamed to such a problematic path, when
+   rename detection was enabled.
+   (merge d9fd71fa2a jp/use-diff-index-in-pre-commit-sample later to maint).
+
+ * The command line parser for the "log" family of commands was too
+   loose when parsing certain numbers, e.g., silently ignoring the
+   extra 'q' in "git log -n 1q" without complaining, which has been
+   tightened up.
+   (merge 71a1e94821 jc/revision-parse-int later to maint).
+
+ * "git $cmd --end-of-options --rev -- --path" for some $cmd failed
+   to interpret "--rev" as a rev, and "--path" as a path.  This was
+   fixed for many programs like "reset" and "checkout".
+   (merge 9385174627 jk/end-of-options later to maint).
+
+ * "git bisect reset" has been taught to clean up state files and refs
+   even when BISECT_START file is gone.
+   (merge daaa03e54c jk/bisect-reset-fix later to maint).
+
+ * Some codepaths did not correctly parse configuration variables
+   specified with valueless "true", which has been corrected.
+   (merge d49cb162fa jk/implicit-true later to maint).
+
+ * Code clean-up for sanity checking of command line options for "git
+   show-ref".
+   (merge 7382497372 rs/show-ref-incompatible-options later to maint).
+
+ * The code to parse the From e-mail header has been updated to avoid
+   recursion.
+   (merge dee182941f jk/mailinfo-iterative-unquote-comment later to maint).
+
+ * "git fetch --atomic" issued an unnecessary empty error message,
+   which has been corrected.
+   (merge 18ce48918c jx/fetch-atomic-error-message-fix later to maint).
+
+ * Command line completion script (in contrib/) learned to work better
+   with the reftable backend.
+   (merge 44dbb3bf29 sh/completion-with-reftable later to maint).
+
+ * "git status" is taught to show both the branch being bisected and
+   being rebased when both are in effect at the same time.
+   (merge 990adccbdf rj/status-bisect-while-rebase later to maint).
+
+ * "git archive --list extra garbage" silently ignored excess command
+   line parameters, which has been corrected.
+   (merge d6b6cd1393 jc/archive-list-with-extra-args later to maint).
+
+ * "git sparse-checkout set" added default patterns even when the
+   patterns are being fed from the standard input, which has been
+   corrected.
+   (merge 53ded839ae jc/sparse-checkout-set-default-fix later to maint).
+
+ * "git sparse-checkout (add|set) --[no-]cone --end-of-options" did
+   not handle "--end-of-options" correctly after a recent update.
+
+ * Unlike other environment variables that took the usual
+   true/false/yes/no as well as 0/1, GIT_FLUSH only understood 0/1,
+   which has been corrected.
+   (merge 556e68032f cp/git-flush-is-an-env-bool later to maint).
+
+ * Clearing in-core repository (happens during e.g., "git fetch
+   --recurse-submodules" with commit graph enabled) made in-core
+   commit object in an inconsistent state by discarding the necessary
+   data from commit-graph too early, which has been corrected.
+   (merge d70f554cdf jk/commit-graph-slab-clear-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 50f1abcff6 js/packfile-h-typofix later to maint).
+   (merge cbf498eb53 jb/reflog-expire-delete-dry-run-options later to maint).
+   (merge 7854bf4960 rs/i18n-cannot-be-used-together later to maint).
+   (merge cd3c28c53a rs/column-leakfix later to maint).
+   (merge 866a1b9026 ps/ref-tests-update-more later to maint).
+   (merge e4299d26d4 mk/doc-gitfile-more later to maint).
+   (merge 792b86283b rs/incompatible-options-messages later to maint).
+   (merge ea8f9494ab jk/config-cleanup later to maint).
+   (merge d1bd3a8c34 jk/mailinfo-oob-read-fix later to maint).
+   (merge c0cadb0576 ps/reftable-fixes later to maint).
+   (merge 647b5e0998 ps/chainlint-self-check-update later to maint).
+   (merge 68fcebfb1a es/add-doc-list-short-form-of-all-in-synopsis later to maint).
+   (merge bc62d27d5c jc/doc-most-refs-are-not-that-special later to maint).
+   (merge 6d6f1cd7ee jc/doc-misspelt-refs-fix later to maint).
+   (merge 37e8d795be sp/test-i18ngrep later to maint).
+   (merge fbc6526ea6 rs/t6300-compressed-size-fix later to maint).
+   (merge 45184afb4d rs/rebase-use-strvec-pushf later to maint).
+   (merge a762af3dfd jc/retire-cas-opt-name-constant later to maint).
+   (merge de7c27a186 la/trailer-cleanups later to maint).
+   (merge d44b517137 jc/orphan-unborn later to maint).
+   (merge 63956c553d ml/doc-merge-updates later to maint).
+   (merge d57c671a51 en/header-cleanup later to maint).
+   (merge 5b7eec4bc5 rs/fast-import-simplify-mempool-allocation later to maint).
+   (merge 291873e5d6 js/contributor-docs-updates later to maint).
+   (merge 54d8a2531b jk/t1006-cat-file-objectsize-disk later to maint).
+   (merge 7033d5479b jx/sideband-chomp-newline-fix later to maint).
+   (merge 9cd30af991 ms/rebase-insnformat-doc-fix later to maint).
+   (merge 03bcc93769 cp/sideband-array-index-comment-fix later to maint).
+   (merge 993d38a066 jk/index-pack-lsan-false-positive-fix later to maint).
+   (merge 25aec06326 ib/rebase-reschedule-doc later to maint).
+   (merge 5aea3955bc rj/clarify-branch-doc-m later to maint).
+   (merge 9cce3be2df bk/bisect-doc-fix later to maint).
index 0e323d54779a7c680ee8471330c85609f4644d43..515d470d23c1f178a60a2ea7734ba8930da42cba 100644 (file)
@@ -19,7 +19,7 @@ Principles
 Selecting patch(es) to review
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 If you are looking for a patch series in need of review, start by checking
-latest "What's cooking in git.git" email
+the latest "What's cooking in git.git" email
 (https://lore.kernel.org/git/xmqqilm1yp3m.fsf@gitster.g/[example]). The "What's
 cooking" emails & replies can be found using the query `s:"What's cooking"` on
 the https://lore.kernel.org/git/[`lore.kernel.org` mailing list archive];
@@ -126,7 +126,7 @@ Terminology
 -----------
 nit: ::
        Denotes a small issue that should be fixed, such as a typographical error
-       or mis-alignment of conditions in an `if()` statement.
+       or misalignment of conditions in an `if()` statement.
 
 aside: ::
 optional: ::
index 927f7329a557bab6c316fa8fa1e478fa54b7e6a7..e734a3f0f175795f0633e93c07afd13f9183efc8 100644 (file)
@@ -3,45 +3,101 @@ Submitting Patches
 
 == Guidelines
 
-Here are some guidelines for people who want to contribute their code to this
-software. There is also a link:MyFirstContribution.html[step-by-step tutorial]
+Here are some guidelines for contributing back to this
+project. There is also a link:MyFirstContribution.html[step-by-step tutorial]
 available which covers many of these same guidelines.
 
-[[base-branch]]
-=== Decide what to base your work on.
-
-In general, always base your work on the oldest branch that your
-change is relevant to.
-
-* A bugfix should be based on `maint` in general. If the bug is not
-  present in `maint`, base it on `master`. For a bug that's not yet
-  in `master`, find the topic that introduces the regression, and
-  base your work on the tip of the topic.
-
-* A new feature should be based on `master` in general. If the new
-  feature depends on other topics that are in `next`, but not in
-  `master`, fork a branch from the tip of `master`, merge these topics
-  to the branch, and work on that branch.  You can remind yourself of
-  how you prepared the base with `git log --first-parent master..`.
-
-* Corrections and enhancements to a topic not yet in `master` should
-  be based on the tip of that topic. If the topic has not been merged
-  to `next`, it's alright to add a note to squash minor corrections
-  into the series.
-
-* In the exceptional case that a new feature depends on several topics
-  not in `master`, start working on `next` or `seen` privately and
-  send out patches only for discussion. Once your new feature starts
-  to stabilize, you would have to rebase it (see the "depends on other
-  topics" above).
-
-* Some parts of the system have dedicated maintainers with their own
-  repositories (see the section "Subsystems" below).  Changes to
-  these parts should be based on their trees.
-
-To find the tip of a topic branch, run `git log --first-parent
-master..seen` and look for the merge commit. The second parent of this
-commit is the tip of the topic branch.
+[[choose-starting-point]]
+=== Choose a starting point.
+
+As a preliminary step, you must first choose a starting point for your
+work. Typically this means choosing a branch, although technically
+speaking it is actually a particular commit (typically the HEAD, or tip,
+of the branch).
+
+There are several important branches to be aware of. Namely, there are
+four integration branches as discussed in linkgit:gitworkflows[7]:
+
+* maint
+* master
+* next
+* seen
+
+The branches lower on the list are typically descendants of the ones
+that come before it. For example, `maint` is an "older" branch than
+`master` because `master` usually has patches (commits) on top of
+`maint`.
+
+There are also "topic" branches, which contain work from other
+contributors.  Topic branches are created by the Git maintainer (in
+their fork) to organize the current set of incoming contributions on
+the mailing list, and are itemized in the regular "What's cooking in
+git.git" announcements.  To find the tip of a topic branch, run `git log
+--first-parent master..seen` and look for the merge commit. The second
+parent of this commit is the tip of the topic branch.
+
+There is one guiding principle for choosing the right starting point: in
+general, always base your work on the oldest integration branch that
+your change is relevant to (see "Merge upwards" in
+linkgit:gitworkflows[7]).  What this principle means is that for the
+vast majority of cases, the starting point for new work should be the
+latest HEAD commit of `maint` or `master` based on the following cases:
+
+* If you are fixing bugs in the released version, use `maint` as the
+  starting point (which may mean you have to fix things without using
+  new API features on the cutting edge that recently appeared in
+  `master` but were not available in the released version).
+
+* Otherwise (such as if you are adding new features) use `master`.
+
+
+NOTE: In exceptional cases, a bug that was introduced in an old
+version may have to be fixed for users of releases that are much older
+than the recent releases.  `git describe --contains X` may describe
+`X` as `v2.30.0-rc2-gXXXXXX` for the commit `X` that introduced the
+bug, and the bug may be so high-impact that we may need to issue a new
+maintenance release for Git 2.30.x series, when "Git 2.41.0" is the
+current release.  In such a case, you may want to use the tip of the
+maintenance branch for the 2.30.x series, which may be available in the
+`maint-2.30` branch in https://github.com/gitster/git[the maintainer's
+"broken out" repo].
+
+This also means that `next` or `seen` are inappropriate starting points
+for your work, if you want your work to have a realistic chance of
+graduating to `master`.  They are simply not designed to be used as a
+base for new work; they are only there to make sure that topics in
+flight work well together. This is why both `next` and `seen` are
+frequently re-integrated with incoming patches on the mailing list and
+force-pushed to replace previous versions of themselves. A topic that is
+literally built on top of `next` cannot be merged to `master` without
+dragging in all the other topics in `next`, some of which may not be
+ready.
+
+For example, if you are making tree-wide changes, while somebody else is
+also making their own tree-wide changes, your work may have severe
+overlap with the other person's work.  This situation may tempt you to
+use `next` as your starting point (because it would have the other
+person's work included in it), but doing so would mean you'll not only
+depend on the other person's work, but all the other random things from
+other contributors that are already integrated into `next`.  And as soon
+as `next` is updated with a new version, all of your work will need to
+be rebased anyway in order for them to be cleanly applied by the
+maintainer.
+
+Under truly exceptional circumstances where you absolutely must depend
+on a select few topic branches that are already in `next` but not in
+`master`, you may want to create your own custom base-branch by forking
+`master` and merging the required topic branches into it. You could then
+work on top of this base-branch.  But keep in mind that this base-branch
+would only be known privately to you.  So when you are ready to send
+your patches to the list, be sure to communicate how you created it in
+your cover letter.  This critical piece of information would allow
+others to recreate your base-branch on their end in order for them to
+try out your work.
+
+Finally, note that some parts of the system have dedicated maintainers
+with their own separate source code repositories (see the section
+"Subsystems" below).
 
 [[separate-commits]]
 === Make separate commits for logically separate changes.
@@ -210,7 +266,7 @@ date)", like this:
        noticed that ...
 ....
 
-The "Copy commit summary" command of gitk can be used to obtain this
+The "Copy commit reference" command of gitk can be used to obtain this
 format (with the subject enclosed in a pair of double-quotes), or this
 invocation of `git show`:
 
@@ -299,9 +355,21 @@ If you like, you can put extra tags at the end:
   patch after a detailed analysis.
 . `Tested-by:` is used to indicate that the person applied the patch
   and found it to have the desired effect.
-
-You can also create your own tag or use one that's in common usage
-such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:".
+. `Co-authored-by:` is used to indicate that people exchanged drafts
+   of a patch before submitting it.
+. `Helped-by:` is used to credit someone who suggested ideas for
+  changes without providing the precise changes in patch form.
+. `Mentored-by:` is used to credit someone with helping develop a
+  patch as part of a mentorship program (e.g., GSoC or Outreachy).
+. `Suggested-by:` is used to credit someone with suggesting the idea
+  for a patch.
+
+While you can also create your own trailer if the situation warrants it, we
+encourage you to instead use one of the common trailers in this project
+highlighted above.
+
+Only capitalize the very first letter of tags, i.e. favor
+"Signed-off-by" over "Signed-Off-By" and "Acked-by:" over "Acked-By".
 
 [[git-tools]]
 === Generate your patch using Git tools out of your commits.
@@ -317,10 +385,13 @@ Please make sure your patch does not add commented out debugging code,
 or include any extra files which do not relate to what your patch
 is trying to achieve. Make sure to review
 your patch after generating it, to ensure accuracy.  Before
-sending out, please make sure it cleanly applies to the base you
-have chosen in the "Decide what to base your work on" section,
-and unless it targets the `master` branch (which is the default),
-mark your patches as such.
+sending out, please make sure it cleanly applies to the starting point you
+have chosen in the "Choose a starting point" section.
+
+NOTE: From the perspective of those reviewing your patch, the `master`
+branch is the default expected starting point.  So if you have chosen a
+different starting point, please communicate this choice in your cover
+letter.
 
 
 [[send-patches]]
@@ -334,8 +405,8 @@ mailing list{security-ml}, instead of the public mailing list.
 
 Learn to use format-patch and send-email if possible.  These commands
 are optimized for the workflow of sending patches, avoiding many ways
-your existing e-mail client that is optimized for "multipart/*" mime
-type e-mails to corrupt and render your patches unusable.
+your existing e-mail client (often optimized for "multipart/*" MIME
+type e-mails) might render your patches unusable.
 
 People on the Git mailing list need to be able to read and
 comment on the changes you are submitting.  It is important for
@@ -456,8 +527,8 @@ repositories.
 
        git://git.ozlabs.org/~paulus/gitk
 
-   Those who are interested in improve gitk can volunteer to help Paul
-   in maintaining it cf. <YntxL/fTplFm8lr6@cleo>.
+   Those who are interested in improving gitk can volunteer to help Paul
+   maintain it, cf. <YntxL/fTplFm8lr6@cleo>.
 
 - `po/` comes from the localization coordinator, Jiang Xin:
 
@@ -497,7 +568,7 @@ help you find out who they are.
 
 In any time between the (2)-(3) cycle, the maintainer may pick it up
 from the list and queue it to `seen`, in order to make it easier for
-people play with it without having to pick up and apply the patch to
+people to play with it without having to pick up and apply the patch to
 their trees themselves.
 
 [[patch-status]]
@@ -511,7 +582,7 @@ their trees themselves.
   master).
 
 * Read the Git mailing list, the maintainer regularly posts messages
-  entitled "What's cooking in git.git" and "What's in git.git" giving
+  entitled "What's cooking in git.git" giving
   the status of various proposed changes.
 
 == GitHub CI[[GHCI]]
@@ -531,11 +602,12 @@ After the initial setup, CI will run whenever you push new changes
 to your fork of Git on GitHub.  You can monitor the test state of all your
 branches here: `https://github.com/<Your GitHub handle>/git/actions/workflows/main.yml`
 
-If a branch did not pass all test cases then it is marked with a red
-cross. In that case you can click on the failing job and navigate to
-"ci/run-build-and-tests.sh" and/or "ci/print-test-failures.sh". You
-can also download "Artifacts" which are tarred (or zipped) archives
-with test data relevant for debugging.
+If a branch does not pass all test cases then it will be marked with a
+red +x+, instead of a green check. In that case, you can click on the
+failing job and navigate to "ci/run-build-and-tests.sh" and/or
+"ci/print-test-failures.sh". You can also download "Artifacts" which
+are zip archives containing tarred (or zipped) archives with test data
+relevant for debugging.
 
 Then fix the problem and push your fix to your GitHub fork. This will
 trigger a new CI build to ensure all tests pass.
@@ -543,7 +615,7 @@ trigger a new CI build to ensure all tests pass.
 [[mua]]
 == MUA specific hints
 
-Some of patches I receive or pick up from the list share common
+Some of the patches I receive or pick up from the list share common
 patterns of breakage.  Please make sure your MUA is set up
 properly not to corrupt whitespaces.
 
@@ -627,7 +699,7 @@ message to an external program, and this is a handy way to drive
 `git am`.  However, if the message is MIME encoded, what is
 piped into the program is the representation you see in your
 `*Article*` buffer after unwrapping MIME.  This is often not what
-you would want for two reasons.  It tends to screw up non ASCII
+you would want for two reasons.  It tends to screw up non-ASCII
 characters (most notably in people's names), and also
 whitespaces (fatal in patches).  Running "C-u g" to display the
 message in raw form before using "|" to run the pipe can work
index 5060d0d2314c316364b7579236a1c2572c62ba64..ae7690b45d08b3c3ba9775d17d43d0f658a71360 100644 (file)
@@ -5,7 +5,7 @@ Tools for developing Git
 [[summary]]
 == Summary
 
-This document gathers tips, scripts and configuration file to help people
+This document gathers tips, scripts, and configuration files to help people
 working on Git's codebase use their favorite tools while following Git's
 coding style.
 
@@ -32,7 +32,7 @@ information on using the script.
 
 This is adapted from Linux's suggestion in its CodingStyle document:
 
-- To follow rules of the CodingGuideline, it's useful to put the following in
+- To follow the rules in CodingGuidelines, it's useful to put the following in
 GIT_CHECKOUT/.dir-locals.el, assuming you use cperl-mode:
 ----
 ;; note the first part is useful for C editing, too
index 3e4c13971b4a7c02a769e87685331822c844e238..60f76f43edab759a5587835fc5cb58ce0d8a7b4d 100644 (file)
@@ -51,25 +51,6 @@ ifdef::doctype-manpage[]
 endif::doctype-manpage[]
 endif::backend-docbook[]
 
-ifdef::doctype-manpage[]
-ifdef::backend-docbook[]
-[header]
-template::[header-declarations]
-<refentry>
-<refmeta>
-<refentrytitle>{mantitle}</refentrytitle>
-<manvolnum>{manvolnum}</manvolnum>
-<refmiscinfo class="source">{mansource}</refmiscinfo>
-<refmiscinfo class="version">{manversion}</refmiscinfo>
-<refmiscinfo class="manual">{manmanual}</refmiscinfo>
-</refmeta>
-<refnamediv>
-  <refname>{manname}</refname>
-  <refpurpose>{manpurpose}</refpurpose>
-</refnamediv>
-endif::backend-docbook[]
-endif::doctype-manpage[]
-
 ifdef::backend-xhtml11[]
 [attributes]
 git-relative-html-prefix=
index 9a663535f443c0f46a0a244c4dc454acdf5ffdef..552dcc60f2a41e24260fd4f6f8d6090a3df660e1 100644 (file)
@@ -64,11 +64,9 @@ include::line-range-format.txt[]
        manual page.
 
 --contents <file>::
-       When <rev> is not specified, the command annotates the
-       changes starting backwards from the working tree copy.
-       This flag makes the command pretend as if the working
-       tree copy has the contents of the named file (specify
-       `-` to make the command read from the standard input).
+       Annotate using the contents from the named file, starting from <rev>
+       if it is specified, and HEAD otherwise. You may specify '-' to make
+       the command read from the standard input for the file contents.
 
 --date <format>::
        Specifies the format used to output dates. If --date is not
index ba4205e0302a267a5da6bef504f3e69eb0c4aa6d..1b3ac8fdd95fa1d57afbdffb1a9743f36ed22fdd 100755 (executable)
@@ -38,9 +38,10 @@ while ($changed) {
     }
 }
 
-while (my ($text, $included) = each %include) {
+foreach my $text (sort keys %include) {
+    my $included = $include{$text};
     if (! exists $included{$text} &&
        (my $base = $text) =~ s/\.txt$//) {
-       print "$base.html $base.xml : ", join(" ", keys %$included), "\n";
+       print "$base.html $base.xml : ", join(" ", sort keys %$included), "\n";
     }
 }
index 5b5b9765699933c406af47f6c1b4ee9ed885f0ba..e3a74dd1c19db44b3a0980cc778bf0ab4ba60b07 100644 (file)
@@ -11,7 +11,7 @@ file. The file `/etc/gitconfig` can be used to store a system-wide
 default configuration.
 
 The configuration variables are used by both the Git plumbing
-and the porcelains. The variables are divided into sections, wherein
+and the porcelain commands. The variables are divided into sections, wherein
 the fully qualified variable name of the variable itself is the last
 dot-separated segment and the section name is everything before the last
 dot. The variable names are case-insensitive, allow only alphanumeric
@@ -103,7 +103,7 @@ was found.  See below for examples.
 Conditional includes
 ~~~~~~~~~~~~~~~~~~~~
 
-You can include a config file from another conditionally by setting a
+You can conditionally include a config file from another by setting an
 `includeIf.<condition>.path` variable to the name of the file to be
 included.
 
@@ -118,7 +118,7 @@ are:
        pattern, the include condition is met.
 +
 The .git location may be auto-discovered, or come from `$GIT_DIR`
-environment variable. If the repository is auto discovered via a .git
+environment variable. If the repository is auto-discovered via a .git
 file (e.g. from submodules, or a linked worktree), the .git location
 would be the final location where the .git directory is, not where the
 .git file is.
@@ -182,7 +182,7 @@ included, Git breaks the cycle by prohibiting these files from affecting
 the resolution of these conditions (thus, prohibiting them from
 declaring remote URLs).
 +
-As for the naming of this keyword, it is for forwards compatibiliy with
+As for the naming of this keyword, it is for forwards compatibility with
 a naming scheme that supports more variable-based include conditions,
 but currently Git only supports the exact keyword described above.
 
@@ -371,6 +371,8 @@ other popular tools, and describe them in your documentation.
 
 include::config/advice.txt[]
 
+include::config/attr.txt[]
+
 include::config/core.txt[]
 
 include::config/add.txt[]
@@ -387,6 +389,8 @@ include::config/branch.txt[]
 
 include::config/browser.txt[]
 
+include::config/bundle.txt[]
+
 include::config/checkout.txt[]
 
 include::config/clean.txt[]
@@ -423,6 +427,8 @@ include::config/filter.txt[]
 
 include::config/fsck.txt[]
 
+include::config/fsmonitor--daemon.txt[]
+
 include::config/gc.txt[]
 
 include::config/gitcvs.txt[]
index 3e859f34197bc43c8d266ec04d569e5353dae27e..e0354ceaed9e870abce7b59468ed08439267b17a 100644 (file)
@@ -7,6 +7,7 @@ add.ignore-errors (deprecated)::
        variables.
 
 add.interactive.useBuiltin::
-       Set to `false` to fall back to the original Perl implementation of
-       the interactive version of linkgit:git-add[1] instead of the built-in
-       version. Is `true` by default.
+       Unused configuration variable. Used in Git versions v2.25.0 to
+       v2.36.0 to enable the built-in version of linkgit:git-add[1]'s
+       interactive mode, which then became the default in Git
+       versions v2.37.0 to v2.39.0.
index a00d0100a82ba710ea52470764b047e2aeb38d56..4d7e5d875998fe1c3aa20c0bb5efdcf9fb6fad91 100644 (file)
@@ -5,7 +5,7 @@ advice.*::
 +
 --
        ambiguousFetchRefspec::
-               Advice shown when fetch refspec for multiple remotes map to
+               Advice shown when a fetch refspec for multiple remotes maps to
                the same remote-tracking branch namespace and causes branch
                tracking set-up to fail.
        fetchShowForcedUpdates::
@@ -63,7 +63,7 @@ advice.*::
                the template shown when writing commit messages in
                linkgit:git-commit[1], and in the help message shown
                by linkgit:git-switch[1] or
-               linkgit:git-checkout[1] when switching branch.
+               linkgit:git-checkout[1] when switching branches.
        statusUoption::
                Advise to consider using the `-u` option to linkgit:git-status[1]
                when the command takes more than 2 seconds to enumerate untracked
@@ -87,7 +87,7 @@ advice.*::
        detachedHead::
                Advice shown when you used
                linkgit:git-switch[1] or linkgit:git-checkout[1]
-               to move to the detach HEAD state, to instruct how to
+               to move to the detached HEAD state, to instruct how to
                create a local branch after the fact.
        suggestDetachingHead::
                Advice shown when linkgit:git-switch[1] refuses to detach HEAD
@@ -101,7 +101,7 @@ advice.*::
                otherwise caused a remote-tracking branch to be
                checked out. See the `checkout.defaultRemote`
                configuration variable for how to set a given remote
-               to used by default in some situations where this
+               to be used by default in some situations where this
                advice would be printed.
        amWorkDir::
                Advice that shows the location of the patch file when
@@ -136,4 +136,10 @@ advice.*::
                Advice shown when either linkgit:git-add[1] or linkgit:git-rm[1]
                is asked to update index entries outside the current sparse
                checkout.
+       diverging::
+               Advice shown when a fast-forward is not possible.
+       worktreeAddOrphan::
+               Advice shown when a user tries to create a worktree from an
+               invalid reference, to instruct how to create a new unborn
+               branch instead.
 --
index f1ca739d574293fd001322a2cc272e2cc0510344..01df96fab3df11f1c804e26ed7c312cd7490e2fd 100644 (file)
@@ -4,7 +4,7 @@ alias.*::
        `git last` is equivalent to `git cat-file commit HEAD`. To avoid
        confusion and troubles with script usage, aliases that
        hide existing Git commands are ignored. Arguments are split by
-       spaces, the usual shell quoting and escaping is supported.
+       spaces, the usual shell quoting and escaping are supported.
        A quote pair or a backslash can be used to quote them.
 +
 Note that the first word of an alias does not necessarily have to be a
index 8fb8ef763dfdf2475af3addafa3cefd7d859ad8a..f9908e210a838dfaf78315b1ce146655a7cf19ca 100644 (file)
@@ -2,10 +2,10 @@ apply.ignoreWhitespace::
        When set to 'change', tells 'git apply' to ignore changes in
        whitespace, in the same way as the `--ignore-space-change`
        option.
-       When set to one of: no, none, never, false tells 'git apply' to
+       When set to one of: no, none, never, false, it tells 'git apply' to
        respect all whitespace differences.
        See linkgit:git-apply[1].
 
 apply.whitespace::
-       Tells 'git apply' how to handle whitespaces, in the same way
+       Tells 'git apply' how to handle whitespace, in the same way
        as the `--whitespace` option. See linkgit:git-apply[1].
diff --git a/Documentation/config/attr.txt b/Documentation/config/attr.txt
new file mode 100644 (file)
index 0000000..1a482d6
--- /dev/null
@@ -0,0 +1,7 @@
+attr.tree::
+       A reference to a tree in the repository from which to read attributes,
+       instead of the `.gitattributes` file in the working tree. In a bare
+       repository, this defaults to `HEAD:.gitattributes`. If the value does
+       not resolve to a valid tree object, an empty tree is used instead.
+       When the `GIT_ATTR_SOURCE` environment variable or `--attr-source`
+       command line option are used, this configuration variable has no effect.
index 445341a906b241ab1ed11093b1b0fa908e0954da..432b9cd2c0e66721c135a14bd4e3f09dee6bcc92 100644 (file)
@@ -36,7 +36,7 @@ branch.sort::
 
 branch.<name>.remote::
        When on branch <name>, it tells 'git fetch' and 'git push'
-       which remote to fetch from/push to.  The remote to push to
+       which remote to fetch from or push to.  The remote to push to
        may be overridden with `remote.pushDefault` (for all branches).
        The remote to push to, for the current branch, may be further
        overridden by `branch.<name>.pushRemote`.  If no remote is
@@ -64,7 +64,7 @@ branch.<name>.merge::
        handled like the remote part of a refspec, and must match a
        ref which is fetched from the remote given by
        "branch.<name>.remote".
-       The merge information is used by 'git pull' (which at first calls
+       The merge information is used by 'git pull' (which first calls
        'git fetch') to lookup the default branch for merging. Without
        this option, 'git pull' defaults to merge the first refspec fetched.
        Specify multiple values to get an octopus merge.
@@ -99,5 +99,5 @@ for details).
 branch.<name>.description::
        Branch description, can be edited with
        `git branch --edit-description`. Branch description is
-       automatically added in the format-patch cover letter or
+       automatically added to the format-patch cover letter or
        request-pull summary.
diff --git a/Documentation/config/bundle.txt b/Documentation/config/bundle.txt
new file mode 100644 (file)
index 0000000..3faae38
--- /dev/null
@@ -0,0 +1,31 @@
+bundle.*::
+       The `bundle.*` keys may appear in a bundle list file found via the
+       `git clone --bundle-uri` option. These keys currently have no effect
+       if placed in a repository config file, though this will change in the
+       future. See link:technical/bundle-uri.html[the bundle URI design
+       document] for more details.
+
+bundle.version::
+       This integer value advertises the version of the bundle list format
+       used by the bundle list. Currently, the only accepted value is `1`.
+
+bundle.mode::
+       This string value should be either `all` or `any`. This value describes
+       whether all of the advertised bundles are required to unbundle a
+       complete understanding of the bundled information (`all`) or if any one
+       of the listed bundle URIs is sufficient (`any`).
+
+bundle.heuristic::
+       If this string-valued key exists, then the bundle list is designed to
+       work well with incremental `git fetch` commands. The heuristic signals
+       that there are additional keys available for each bundle that help
+       determine which subset of bundles the client should download. The
+       only value currently understood is `creationToken`.
+
+bundle.<id>.*::
+       The `bundle.<id>.*` keys are used to describe a single item in the
+       bundle list, grouped under `<id>` for identification purposes.
+
+bundle.<id>.uri::
+       This string value defines the URI by which Git can reach the contents
+       of this `<id>`. This URI may be a bundle file or another bundle list.
index bfbca90f0e90bdcac2fb814ab90c74d2d7f1b0d7..a3230229938055323ead98e6d6bdfd032b911b1b 100644 (file)
@@ -30,7 +30,7 @@ checkout.workers::
        all commands that perform checkout. E.g. checkout, clone, reset,
        sparse-checkout, etc.
 +
-Note: parallel checkout usually delivers better performance for repositories
+Note: Parallel checkout usually delivers better performance for repositories
 located on SSDs or over NFS. For repositories on spinning disks and/or machines
 with a small number of cores, the default sequential checkout often performs
 better. The size and compression level of a repository might also influence how
@@ -39,6 +39,6 @@ well the parallel version performs.
 checkout.thresholdForParallelism::
        When running parallel checkout with a small number of files, the cost
        of subprocess spawning and inter-process communication might outweigh
-       the parallelization gains. This setting allows to define the minimum
+       the parallelization gains. This setting allows you to define the minimum
        number of files for which parallel checkout should be attempted. The
        default is 100.
index a807c925b9ca8ce4ca783fe2880ec986ef0d5b83..f05b9403b5ad903ea68d68e896acaff253bfde37 100644 (file)
@@ -1,3 +1,3 @@
 clean.requireForce::
        A boolean to make git-clean do nothing unless given -f,
-       -i or -n.   Defaults to true.
+       -i, or -n.  Defaults to true.
index 26f4fb137a738ead96e0a9848df9a383aa432305..d037b57f729e5e10549235b6278c123754d0aee8 100644 (file)
@@ -4,8 +4,8 @@ clone.defaultRemoteName::
        option to linkgit:git-clone[1].
 
 clone.rejectShallow::
-       Reject to clone a repository if it is a shallow one, can be overridden by
-       passing option `--reject-shallow` in command line. See linkgit:git-clone[1]
+       Reject cloning a repository if it is a shallow one; this can be overridden by
+       passing the `--reject-shallow` option on the command line. See linkgit:git-clone[1]
 
 clone.filterSubmodules::
        If a partial clone filter is provided (see `--filter` in
index 1795b2d16be2f01ba10ecfd6a2128444b99a85ec..2f2275ac6975f2a47cafc719d886e2f2b8c71a71 100644 (file)
@@ -106,7 +106,7 @@ color.grep.<slot>::
        matching text in context lines
 `matchSelected`;;
        matching text in selected lines. Also, used to customize the following
-       linkgit:git-log[1] subcommands: `--grep`, `--author` and `--committer`.
+       linkgit:git-log[1] subcommands: `--grep`, `--author`, and `--committer`.
 `selected`;;
        non-matching text in selected lines. Also, used to customize the
        following linkgit:git-log[1] subcommands: `--grep`, `--author` and
index 76aa2f29dc2108fa20d575c4bc49791bc9c02c96..01e4198429ed46c96b7e25a70ed7b58c4844a29a 100644 (file)
@@ -43,7 +43,7 @@ column.branch::
        See `column.ui` for details.
 
 column.clean::
-       Specify the layout when list items in `git clean -i`, which always
+       Specify the layout when listing items in `git clean -i`, which always
        shows files and directories in columns. See `column.ui` for details.
 
 column.status::
@@ -51,5 +51,5 @@ column.status::
        See `column.ui` for details.
 
 column.tag::
-       Specify whether to output tag listing in `git tag` in columns.
+       Specify whether to output tag listings in `git tag` in columns.
        See `column.ui` for details.
index 2c95573930bedf8d5aed0a0b19927c73ac8e39f7..62f0d92fda51de04a75b4fc074befaa96c554851 100644 (file)
@@ -2,7 +2,7 @@ commit.cleanup::
        This setting overrides the default of the `--cleanup` option in
        `git commit`. See linkgit:git-commit[1] for details. Changing the
        default can be useful when you always want to keep lines that begin
-       with comment character `#` in your log message, in which case you
+       with the comment character `#` in your log message, in which case you
        would do `git config commit.cleanup whitespace` (note that you will
        have to remove the help lines that begin with `#` in the commit log
        template yourself, if you do this).
@@ -25,5 +25,5 @@ commit.template::
        new commit messages.
 
 commit.verbose::
-       A boolean or int to specify the level of verbose with `git commit`.
+       A boolean or int to specify the level of verbosity with `git commit`.
        See linkgit:git-commit[1].
index 37afbaf5a419d17f22e6598607cc13d121a8020e..0e8c2832bf9b8a5251c51f346ecfcf47c7e8d530 100644 (file)
@@ -618,7 +618,7 @@ but risks losing recent work in the event of an unclean system shutdown.
 * `loose-object` hardens objects added to the repo in loose-object form.
 * `pack` hardens objects added to the repo in packfile form.
 * `pack-metadata` hardens packfile bitmaps and indexes.
-* `commit-graph` hardens the commit graph file.
+* `commit-graph` hardens the commit-graph file.
 * `index` hardens the index when it is modified.
 * `objects` is an aggregate option that is equivalent to
   `loose-object,pack`.
@@ -736,3 +736,9 @@ core.abbrev::
        If set to "no", no abbreviation is made and the object names
        are shown in their full length.
        The minimum length is 4.
+
+core.maxTreeDepth::
+       The maximum depth Git is willing to recurse while traversing a
+       tree (e.g., "a/b/cde/f" has a depth of 4). This is a fail-safe
+       to allow Git to abort cleanly, and should not generally need to
+       be adjusted. The default is 4096.
index 512f31876e17ed6892ff7f1858a891f0f7938b29..0221c3e620da89abb62f26110f400ecb0d6ff71a 100644 (file)
@@ -21,7 +21,7 @@ credential.username::
 
 credential.<url>.*::
        Any of the credential.* options above can be applied selectively to
-       some credentials. For example "credential.https://example.com.username"
+       some credentials. For example, "credential.https://example.com.username"
        would set the default username only for https connections to
        example.com. See linkgit:gitcredentials[7] for details on how URLs are
        matched.
@@ -31,6 +31,6 @@ credentialCache.ignoreSIGHUP::
 
 credentialStore.lockTimeoutMS::
        The length of time, in milliseconds, for git-credential-store to retry
-       when trying to lock the credentials file. Value 0 means not to retry at
+       when trying to lock the credentials file. A value of 0 means not to retry at
        all; -1 means to try indefinitely. Default is 1000 (i.e., retry for
        1s).
index 35a7bf86d7774c67dbd129cc29b009aae46c81a9..bd5ae0c3378adbd5661a202dc1f8fcfbf27ac4a1 100644 (file)
@@ -1,6 +1,6 @@
 diff.autoRefreshIndex::
        When using 'git diff' to compare with work tree
-       files, do not consider stat-only change as changed.
+       files, do not consider stat-only changes as changed.
        Instead, silently run `git update-index --refresh` to
        update the cached stat information for paths whose
        contents in the work tree match the contents in the
@@ -52,6 +52,10 @@ directories with less than 10% of the total amount of changed files,
 and accumulating child directory counts in the parent directories:
 `files,10,cumulative`.
 
+diff.statNameWidth::
+       Limit the width of the filename part in --stat output. If set, applies
+       to all commands generating --stat output except format-patch.
+
 diff.statGraphWidth::
        Limit the width of the graph part in --stat output. If set, applies
        to all commands generating --stat output except format-patch.
index a3f821121020c0b6a4c66fd74ad52a974ac07207..447c40d85a289dbb58397d9c9f1e2875084a0f50 100644 (file)
@@ -34,3 +34,10 @@ See the `--trust-exit-code` option in linkgit:git-difftool[1] for more details.
 
 difftool.prompt::
        Prompt before each invocation of the diff tool.
+
+difftool.guiDefault::
+       Set `true` to use the `diff.guitool` by default (equivalent to specifying
+       the `--gui` argument), or `auto` to select `diff.guitool` or `diff.tool`
+       depending on the presence of a `DISPLAY` environment variable value. The
+       default is `false`, where the `--gui` argument must be provided
+       explicitly for the `diff.guitool` to be used.
index bccaec7a963679f8262d7c8d056fa4429b9586d1..66db0e15da7db82819de941626b7abc5983862c5 100644 (file)
@@ -7,6 +7,17 @@ Note that this setting should only be set by linkgit:git-init[1] or
 linkgit:git-clone[1].  Trying to change it after initialization will not
 work and will produce hard-to-diagnose issues.
 
+extensions.refStorage::
+       Specify the ref storage format to use. The acceptable values are:
++
+include::../ref-storage-format.txt[]
++
+It is an error to specify this key unless `core.repositoryFormatVersion` is 1.
++
+Note that this setting should only be set by linkgit:git-init[1] or
+linkgit:git-clone[1]. Trying to change it after initialization will not
+work and will produce hard-to-diagnose issues.
+
 extensions.worktreeConfig::
        If enabled, then worktrees will load config settings from the
        `$GIT_DIR/config.worktree` file in addition to the
index c1166e330d55da332d4fa591b4083f86f2adb606..903677d7efefa03d607c9b076ac1a7bf14304105 100644 (file)
@@ -1,8 +1,8 @@
 fastimport.unpackLimit::
        If the number of objects imported by linkgit:git-fast-import[1]
        is below this limit, then the objects will be unpacked into
-       loose object files.  However if the number of imported objects
-       equals or exceeds this limit then the pack will be stored as a
+       loose object files.  However, if the number of imported objects
+       equals or exceeds this limit, then the pack will be stored as a
        pack.  Storing the pack from a fast-import can make the import
        operation complete faster, especially on slow filesystems.  If
        not set, the value of `transfer.unpackLimit` is used instead.
index cdecd04e5bb7ea05efee6124a669da72ad0c5384..bf9546fca4f693f01b39252d12def7df9b51a52f 100644 (file)
@@ -14,12 +14,20 @@ feature.experimental::
 +
 * `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
 skipping more commits at a time, reducing the number of round trips.
++
+* `pack.useBitmapBoundaryTraversal=true` may improve bitmap traversal times by
+walking fewer objects.
 
 feature.manyFiles::
        Enable config options that optimize for repos with many files in the
        working directory. With many files, commands such as `git status` and
        `git checkout` may be slow and these new defaults improve performance:
 +
+* `index.skipHash=true` speeds up index writes by not computing a trailing
+  checksum. Note that this will cause Git versions earlier than 2.13.0 to
+  refuse to parse the index and Git versions earlier than 2.40.0 will report
+  a corrupted index during `git fsck`.
++
 * `index.version=4` enables path-prefix compression in the index.
 +
 * `core.untrackedCache=true` enables the untracked cache. This setting assumes
index cd65d236b43ffc3a7c08fa9df480fcbe6c82e68c..d7dc461bd16ad782933a40e060c523b063cb1aca 100644 (file)
@@ -50,10 +50,16 @@ fetch.pruneTags::
        refs. See also `remote.<name>.pruneTags` and the PRUNING
        section of linkgit:git-fetch[1].
 
+fetch.all::
+       If true, fetch will attempt to update all available remotes.
+       This behavior can be overridden by passing `--no-all` or by
+       explicitly specifying one or more remote(s) to fetch from.
+       Defaults to false.
+
 fetch.output::
        Control how ref update status is printed. Valid values are
-       `full` and `compact`. Default value is `full`. See section
-       OUTPUT in linkgit:git-fetch[1] for detail.
+       `full` and `compact`. Default value is `full`. See the
+       OUTPUT section in linkgit:git-fetch[1] for details.
 
 fetch.negotiationAlgorithm::
        Control how information about the commits in the local repository
@@ -96,3 +102,27 @@ fetch.writeCommitGraph::
        merge and the write may take longer. Having an updated commit-graph
        file helps performance of many Git commands, including `git merge-base`,
        `git push -f`, and `git log --graph`. Defaults to false.
+
+fetch.bundleURI::
+       This value stores a URI for downloading Git object data from a bundle
+       URI before performing an incremental fetch from the origin Git server.
+       This is similar to how the `--bundle-uri` option behaves in
+       linkgit:git-clone[1]. `git clone --bundle-uri` will set the
+       `fetch.bundleURI` value if the supplied bundle URI contains a bundle
+       list that is organized for incremental fetches.
++
+If you modify this value and your repository has a `fetch.bundleCreationToken`
+value, then remove that `fetch.bundleCreationToken` value before fetching from
+the new bundle URI.
+
+fetch.bundleCreationToken::
+       When using `fetch.bundleURI` to fetch incrementally from a bundle
+       list that uses the "creationToken" heuristic, this config value
+       stores the maximum `creationToken` value of the downloaded bundles.
+       This value is used to prevent downloading bundles in the future
+       if the advertised `creationToken` is not strictly larger than this
+       value.
++
+The creation token values are chosen by the provider serving the specific
+bundle URI. If you modify the URI at `fetch.bundleURI`, then be sure to
+remove the value for the `fetch.bundleCreationToken` value before fetching.
index c7303d8d9f004b5e413c4e5fe0dbb2f0df0c9171..7410e930e530fdf4b795da076dc6a3b3f1f4b0f1 100644 (file)
@@ -3,7 +3,8 @@ format.attach::
        'format-patch'.  The value can also be a double quoted string
        which will enable attachments as the default and set the
        value as the boundary.  See the --attach option in
-       linkgit:git-format-patch[1].
+       linkgit:git-format-patch[1].  To countermand an earlier
+       value, set it to an empty string.
 
 format.from::
        Provides the default value for the `--from` option to format-patch.
@@ -67,7 +68,7 @@ format.encodeEmailHeaders::
        Defaults to true.
 
 format.pretty::
-       The default pretty format for log/show/whatchanged command,
+       The default pretty format for log/show/whatchanged command.
        See linkgit:git-log[1], linkgit:git-show[1],
        linkgit:git-whatchanged[1].
 
@@ -118,7 +119,7 @@ format.notes::
        `--notes=<ref>`, where `ref` is the non-boolean value. Defaults
        to false.
 +
-If one wishes to use the ref `ref/notes/true`, please use that literal
+If one wishes to use the ref `refs/notes/true`, please use that literal
 instead.
 +
 This configuration can be specified multiple times in order to allow
@@ -139,3 +140,14 @@ For example,
 ------------
 +
 will only show notes from `refs/notes/bar`.
+
+format.mboxrd::
+       A boolean value which enables the robust "mboxrd" format when
+       `--stdout` is in use to escape "^>+From " lines.
+
+format.noprefix::
+       If set, do not show any source or destination prefix in patches.
+       This is equivalent to the `diff.noprefix` option used by `git
+       diff` (but which is not respected by `format-patch`). Note that
+       by setting this, the receiver of any patches you generate will
+       have to apply them using the `-p0` option.
index 450e8c38e34338170a806f9e43a99ac37b2e8f2f..8e9e508933f8949ff6aa738d3f7460faeaa289db 100644 (file)
@@ -11,13 +11,13 @@ to clone or fetch it set `fetch.fsck.<msg-id>`.
 +
 The rest of the documentation discusses `fsck.*` for brevity, but the
 same applies for the corresponding `receive.fsck.*` and
-`fetch.<msg-id>.*`. variables.
+`fetch.fsck.*`. variables.
 +
-Unlike variables like `color.ui` and `core.editor` the
+Unlike variables like `color.ui` and `core.editor`, the
 `receive.fsck.<msg-id>` and `fetch.fsck.<msg-id>` variables will not
 fall back on the `fsck.<msg-id>` configuration if they aren't set. To
-uniformly configure the same fsck settings in different circumstances
-all three of them they must all set to the same values.
+uniformly configure the same fsck settings in different circumstances,
+all three of them must be set to the same values.
 +
 When `fsck.<msg-id>` is set, errors can be switched to warnings and
 vice versa by configuring the `fsck.<msg-id>` setting where the
@@ -35,16 +35,20 @@ allow new instances of the same breakages go unnoticed.
 Setting an unknown `fsck.<msg-id>` value will cause fsck to die, but
 doing the same for `receive.fsck.<msg-id>` and `fetch.fsck.<msg-id>`
 will only cause git to warn.
++
+See the `Fsck Messages` section of linkgit:git-fsck[1] for supported
+values of `<msg-id>`.
+
 
 fsck.skipList::
        The path to a list of object names (i.e. one unabbreviated SHA-1 per
        line) that are known to be broken in a non-fatal way and should
-       be ignored. On versions of Git 2.20 and later comments ('#'), empty
-       lines, and any leading and trailing whitespace is ignored. Everything
+       be ignored. On versions of Git 2.20 and later, comments ('#'), empty
+       lines, and any leading and trailing whitespace are ignored. Everything
        but a SHA-1 per line will error out on older versions.
 +
 This feature is useful when an established project should be accepted
-despite early commits containing errors that can be safely ignored
+despite early commits containing errors that can be safely ignored,
 such as invalid committer email addresses.  Note: corrupt objects
 cannot be skipped with this setting.
 +
@@ -54,11 +58,11 @@ Like `fsck.<msg-id>` this variable has corresponding
 Unlike variables like `color.ui` and `core.editor` the
 `receive.fsck.skipList` and `fetch.fsck.skipList` variables will not
 fall back on the `fsck.skipList` configuration if they aren't set. To
-uniformly configure the same fsck settings in different circumstances
-all three of them they must all set to the same values.
+uniformly configure the same fsck settings in different circumstances,
+all three of them must be set to the same values.
 +
 Older versions of Git (before 2.20) documented that the object names
-list should be sorted. This was never a requirement, the object names
+list should be sorted. This was never a requirement; the object names
 could appear in any order, but when reading the list we tracked whether
 the list was sorted for the purposes of an internal binary search
 implementation, which could save itself some work with an already sorted
diff --git a/Documentation/config/fsmonitor--daemon.txt b/Documentation/config/fsmonitor--daemon.txt
new file mode 100644 (file)
index 0000000..671f9b9
--- /dev/null
@@ -0,0 +1,11 @@
+fsmonitor.allowRemote::
+    By default, the fsmonitor daemon refuses to work with network-mounted
+    repositories. Setting `fsmonitor.allowRemote` to `true` overrides this
+    behavior.  Only respected when `core.fsmonitor` is set to `true`.
+
+fsmonitor.socketDir::
+    This Mac OS-specific option, if set, specifies the directory in
+    which to create the Unix domain socket used for communication
+    between the fsmonitor daemon and various Git commands. The directory must
+    reside on a native Mac OS filesystem.  Only respected when `core.fsmonitor`
+    is set to `true`.
index 38fea076a26d0f1f7bf137723baba945675901fc..664a3c28747c8e508e78caa97a607aa909e3efa1 100644 (file)
@@ -24,7 +24,7 @@ gc.auto::
        default value is 6700.
 +
 Setting this to 0 disables not only automatic packing based on the
-number of loose objects, but any other heuristic `git gc --auto` will
+number of loose objects, but also any other heuristic `git gc --auto` will
 otherwise use to determine if there's work to do, such as
 `gc.autoPackLimit`.
 
@@ -39,15 +39,15 @@ See the `gc.bigPackThreshold` configuration variable below. When in
 use, it'll affect how the auto pack limit works.
 
 gc.autoDetach::
-       Make `git gc --auto` return immediately and run in background
+       Make `git gc --auto` return immediately and run in the background
        if the system supports it. Default is true.
 
 gc.bigPackThreshold::
-       If non-zero, all packs larger than this limit are kept when
-       `git gc` is run. This is very similar to `--keep-largest-pack`
-       except that all packs that meet the threshold are kept, not
-       just the largest pack. Defaults to zero. Common unit suffixes of
-       'k', 'm', or 'g' are supported.
+       If non-zero, all non-cruft packs larger than this limit are kept
+       when `git gc` is run. This is very similar to
+       `--keep-largest-pack` except that all non-cruft packs that meet
+       the threshold are kept, not just the largest pack. Defaults to
+       zero. Common unit suffixes of 'k', 'm', or 'g' are supported.
 +
 Note that if the number of kept packs is more than gc.autoPackLimit,
 this configuration variable is ignored, all packs except the base pack
@@ -84,7 +84,13 @@ gc.packRefs::
 gc.cruftPacks::
        Store unreachable objects in a cruft pack (see
        linkgit:git-repack[1]) instead of as loose objects. The default
-       is `false`.
+       is `true`.
+
+gc.maxCruftSize::
+       Limit the size of new cruft packs when repacking. When
+       specified in addition to `--max-cruft-size`, the command line
+       option takes priority. See the `--max-cruft-size` option of
+       linkgit:git-repack[1].
 
 gc.pruneExpire::
        When 'git gc' is run, it will call 'prune --expire 2.weeks.ago'
@@ -130,6 +136,37 @@ or rebase occurring.  Since these changes are not part of the current
 project most users will want to expire them sooner, which is why the
 default is more aggressive than `gc.reflogExpire`.
 
+gc.recentObjectsHook::
+       When considering whether or not to remove an object (either when
+       generating a cruft pack or storing unreachable objects as
+       loose), use the shell to execute the specified command(s).
+       Interpret their output as object IDs which Git will consider as
+       "recent", regardless of their age. By treating their mtimes as
+       "now", any objects (and their descendants) mentioned in the
+       output will be kept regardless of their true age.
++
+Output must contain exactly one hex object ID per line, and nothing
+else. Objects which cannot be found in the repository are ignored.
+Multiple hooks are supported, but all must exit successfully, else the
+operation (either generating a cruft pack or unpacking unreachable
+objects) will be halted.
+
+gc.repackFilter::
+       When repacking, use the specified filter to move certain
+       objects into a separate packfile.  See the
+       `--filter=<filter-spec>` option of linkgit:git-repack[1].
+
+gc.repackFilterTo::
+       When repacking and using a filter, see `gc.repackFilter`, the
+       specified location will be used to create the packfile
+       containing the filtered out objects. **WARNING:** The
+       specified location should be accessible, using for example the
+       Git alternates mechanism, otherwise the repo could be
+       considered corrupt by Git as it migh not be able to access the
+       objects in that packfile. See the `--filter-to=<dir>` option
+       of linkgit:git-repack[1] and the `objects/info/alternates`
+       section of linkgit:gitrepository-layout[5].
+
 gc.rerereResolved::
        Records of conflicted merge you resolved earlier are
        kept for this many days when 'git rerere gc' is run.
index 86f6308c4c051814cc4f32a2d7b5d20aabb276e1..5cf32b179dc8bd5559be08e040b14a96ce158fd7 100644 (file)
@@ -4,7 +4,7 @@ gpg.program::
        same command-line interface as GPG, namely, to verify a detached
        signature, "`gpg --verify $signature - <$file`" is run, and the
        program is expected to signal a good signature by exiting with
-       code 0, and to generate an ASCII-armored detached signature, the
+       code 0.  To generate an ASCII-armored detached signature, the
        standard input of "`gpg -bsau $key`" is fed with the contents to be
        signed, and the program is expected to send the result to its
        standard output.
@@ -12,6 +12,9 @@ gpg.program::
 gpg.format::
        Specifies which key format to use when signing with `--gpg-sign`.
        Default is "openpgp". Other possible values are "x509", "ssh".
++
+See linkgit:gitformat-signature[5] for the signature format, which differs
+based on the selected `gpg.format`.
 
 gpg.<format>.program::
        Use this to customize the program used for the signing format you
@@ -22,7 +25,7 @@ gpg.<format>.program::
 gpg.minTrustLevel::
        Specifies a minimum trust level for signature verification.  If
        this option is unset, then signature verification for merge
-       operations require a key with at least `marginal` trust.  Other
+       operations requires a key with at least `marginal` trust.  Other
        operations that perform signature verification require a key
        with at least `undefined` trust.  Setting this option overrides
        the required trust-level for all operations.  Supported values,
@@ -35,7 +38,7 @@ gpg.minTrustLevel::
 * `ultimate`
 
 gpg.ssh.defaultKeyCommand::
-       This command that will be run when user.signingkey is not set and a ssh
+       This command will be run when user.signingkey is not set and a ssh
        signature is requested. On successful exit a valid ssh public key
        prefixed with `key::` is expected in the first line of its output.
        This allows for a script doing a dynamic lookup of the correct public
index 0c087fd8c9313e5e68dcd7cd7a91b6900936febc..171be774d243fd2822fa51875feab38618d82193 100644 (file)
@@ -24,7 +24,7 @@ gui.matchTrackingBranch::
        not. Default: "false".
 
 gui.newBranchTemplate::
-       Is used as suggested name when creating new branches using the
+       Is used as suggested name when creating new branches using the
        linkgit:git-gui[1].
 
 gui.pruneDuringFetch::
index afeeccfbfa7a7073a19088a625b03fe2abee5801..2d4e0c9b869b567200cfc5805600b51deaa41669 100644 (file)
@@ -246,20 +246,21 @@ significantly since the entire buffer is allocated even for small
 pushes.
 
 http.lowSpeedLimit, http.lowSpeedTime::
-       If the HTTP transfer speed is less than 'http.lowSpeedLimit'
-       for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
+       If the HTTP transfer speed, in bytes per second, is less than
+       'http.lowSpeedLimit' for longer than 'http.lowSpeedTime' seconds,
+       the transfer is aborted.
        Can be overridden by the `GIT_HTTP_LOW_SPEED_LIMIT` and
        `GIT_HTTP_LOW_SPEED_TIME` environment variables.
 
 http.noEPSV::
        A boolean which disables using of EPSV ftp command by curl.
-       This can helpful with some "poor" ftp servers which don't
+       This can be helpful with some "poor" ftp servers which don't
        support EPSV mode. Can be overridden by the `GIT_CURL_FTP_NO_EPSV`
        environment variable. Default is false (curl will use EPSV).
 
 http.userAgent::
        The HTTP USER_AGENT string presented to an HTTP server.  The default
-       value represents the version of the client Git such as git/1.7.1.
+       value represents the version of the Git client such as git/1.7.1.
        This option allows you to override this value to a more common value
        such as Mozilla/4.0.  This may be necessary, for instance, if
        connecting through a firewall that restricts HTTP connections to a set
index cc256217317c666f79cc8d39604643d1fcb32b6a..6e72fdb45bd0cde5ca5e90ba189d8a6696df8ef2 100644 (file)
@@ -2,7 +2,7 @@ i18n.commitEncoding::
        Character encoding the commit messages are stored in; Git itself
        does not care per se, but this information is necessary e.g. when
        importing commits from emails or in the gitk graphical history
-       browser (and possibly at other places in the future or in other
+       browser (and possibly in other places in the future or in other
        porcelains). See e.g. linkgit:git-mailinfo[1]. Defaults to 'utf-8'.
 
 i18n.logOutputEncoding::
index 06166fb5c04fe9e1d83d51990f945b11ce1877e9..3d28f7264374e69f45afc9efb201136cb2049c8b 100644 (file)
@@ -4,7 +4,7 @@ imap.folder::
        "[Gmail]/Drafts". Required.
 
 imap.tunnel::
-       Command used to setup a tunnel to the IMAP server through which
+       Command used to set up a tunnel to the IMAP server through which
        commands will be piped instead of using a direct network connection
        to the server. Required when imap.host is not set.
 
@@ -37,7 +37,7 @@ imap.preformattedHTML::
        format=fixed email.  Default is `false`.
 
 imap.authMethod::
-       Specify authenticate method for authentication with IMAP server.
+       Specify the authentication method for authenticating with the IMAP server.
        If Git was built with the NO_CURL option, or if your curl version is older
        than 7.34.0, or if you're running git-imap-send with the `--no-curl`
        option, the only supported method is 'CRAM-MD5'. If this is not set
index 75f3a2d10541460a912946bbbf9987aa18275a02..3eff42036033ea90fb751b364a14ced17f5a25ad 100644 (file)
@@ -23,10 +23,21 @@ index.threads::
        Specifies the number of threads to spawn when loading the index.
        This is meant to reduce index load time on multiprocessor machines.
        Specifying 0 or 'true' will cause Git to auto-detect the number of
-       CPU's and set the number of threads accordingly. Specifying 1 or
+       CPUs and set the number of threads accordingly. Specifying 1 or
        'false' will disable multithreading. Defaults to 'true'.
 
 index.version::
        Specify the version with which new index files should be
        initialized.  This does not affect existing repositories.
        If `feature.manyFiles` is enabled, then the default is 4.
+
+index.skipHash::
+       When enabled, do not compute the trailing hash for the index file.
+       This accelerates Git commands that manipulate the index, such as
+       `git add`, `git commit`, or `git status`. Instead of storing the
+       checksum, write a trailing set of bytes with value zero, indicating
+       that the computation was skipped.
++
+If you enable `index.skipHash`, then Git clients older than 2.13.0 will
+refuse to parse the index and Git clients older than 2.40.0 will report an
+error during `git fsck`.
index 5f96cf87fb96ce7366f11f80b48f9c9bf9fd13f6..9003a8219143ab75524391303df2f88ca8922e86 100644 (file)
@@ -9,7 +9,7 @@ log.date::
        `--date` option.  See linkgit:git-log[1] for details.
 +
 If the format is set to "auto:foo" and the pager is in use, format
-"foo" will be the used for the date format. Otherwise "default" will
+"foo" will be used for the date format. Otherwise, "default" will
 be used.
 
 log.decorate::
index 3854d4ae37cd77efca402a3911220baf4f0eb37d..ec3a5d81f7255aab044eea2b3667bb35c01236f2 100644 (file)
@@ -1,6 +1,6 @@
 mailinfo.scissors::
        If true, makes linkgit:git-mailinfo[1] (and therefore
        linkgit:git-am[1]) act by default as if the --scissors option
-       was provided on the command-line. When active, this features
+       was provided on the command-line. When active, this feature
        removes everything from the message body before a scissors
        line (i.e. consisting mainly of ">8", "8<" and "-").
index 18f056213145e595d0a57792e716f29521122997..69a4f05153e3ba08fda7d181d5d8defc3351cbd4 100644 (file)
@@ -12,7 +12,7 @@ maintenance.strategy::
        then that value is used instead of the one provided by
        `maintenance.strategy`. The possible strategy strings are:
 +
-* `none`: This default setting implies no task are run at any schedule.
+* `none`: This default setting implies no tasks are run at any schedule.
 * `incremental`: This setting optimizes for performing small maintenance
   activities that do not delete any data. This does not schedule the `gc`
   task, but runs the `prefetch` and `commit-graph` tasks hourly, the
index a727d987a8d48c3f4e6b43acd852d77da8f8b108..5a0f82cc2327a2007df24f4582e05a223959e7a0 100644 (file)
@@ -5,7 +5,7 @@ man.viewer::
 man.<tool>.cmd::
        Specify the command to invoke the specified man viewer. The
        specified command is evaluated in shell with the man page
-       passed as argument. (See linkgit:git-help[1].)
+       passed as an argument. (See linkgit:git-help[1].)
 
 man.<tool>.path::
        Override the path for the given tool that may be used to
index 99e83dd36e53e6079721a5a123fdf6392f4fc2b8..8851b6cedef980c106b9e86541862b517aa1f2ad 100644 (file)
@@ -7,7 +7,7 @@ merge.conflictStyle::
        marker and the original text before the `=======` marker.  The
        "merge" style tends to produce smaller conflict regions than diff3,
        both because of the exclusion of the original text, and because
-       when a subset of lines match on the two sides they are just pulled
+       when a subset of lines match on the two sides, they are just pulled
        out of the conflict region.  Another alternate style, "zdiff3", is
        similar to diff3 but removes matching lines on the two sides from
        the conflict region when those matching lines appear near either
index e779a122d8a78f10adb6381a9d00e9041b2c5b70..294f61efd12fdf863e648b076462f4a9a1aa75b7 100644 (file)
@@ -22,8 +22,8 @@ mergetool.<tool>.trustExitCode::
        For a custom merge command, specify whether the exit code of
        the merge command can be used to determine whether the merge was
        successful.  If this is not set to true then the merge target file
-       timestamp is checked and the merge assumed to have been successful
-       if the file has been updated, otherwise the user is prompted to
+       timestamp is checked, and the merge is assumed to have been successful
+       if the file has been updated; otherwise, the user is prompted to
        indicate the success of the merge.
 
 mergetool.meld.hasOutput::
@@ -37,7 +37,7 @@ mergetool.meld.hasOutput::
 
 mergetool.meld.useAutoMerge::
        When the `--auto-merge` is given, meld will merge all non-conflicting
-       parts automatically, highlight the conflicting parts and wait for
+       parts automatically, highlight the conflicting parts, and wait for
        user decision.  Setting `mergetool.meld.useAutoMerge` to `true` tells
        Git to unconditionally use the `--auto-merge` option with `meld`.
        Setting this value to `auto` makes git detect whether `--auto-merge`
@@ -47,7 +47,7 @@ mergetool.meld.useAutoMerge::
 
 mergetool.vimdiff.layout::
        The vimdiff backend uses this variable to control how its split
-       windows look like. Applies even if you are using Neovim (`nvim`) or
+       windows appear. Applies even if you are using Neovim (`nvim`) or
        gVim (`gvim`) as the merge tool. See BACKEND SPECIFIC HINTS section
 ifndef::git-mergetool[]
        in linkgit:git-mergetool[1].
@@ -55,7 +55,7 @@ endif::[]
        for details.
 
 mergetool.hideResolved::
-       During a merge Git will automatically resolve as many conflicts as
+       During a merge, Git will automatically resolve as many conflicts as
        possible and write the 'MERGED' file containing conflict markers around
        any conflicts that it cannot resolve; 'LOCAL' and 'REMOTE' normally
        represent the versions of the file from before Git's conflict
@@ -74,7 +74,7 @@ mergetool.keepTemporaries::
        When invoking a custom merge tool, Git uses a set of temporary
        files to pass to the tool. If the tool returns an error and this
        variable is set to `true`, then these temporary files will be
-       preserved, otherwise they will be removed after the tool has
+       preserved; otherwise, they will be removed after the tool has
        exited. Defaults to `false`.
 
 mergetool.writeToTemp::
@@ -85,3 +85,10 @@ mergetool.writeToTemp::
 
 mergetool.prompt::
        Prompt before each invocation of the merge resolution program.
+
+mergetool.guiDefault::
+       Set `true` to use the `merge.guitool` by default (equivalent to
+       specifying the `--gui` argument), or `auto` to select `merge.guitool`
+       or `merge.tool` depending on the presence of a `DISPLAY` environment
+       variable value. The default is `false`, where the `--gui` argument
+       must be provided explicitly for the `merge.guitool` to be used.
index c7c4811734b5c935c89b52eb3b3ddef58f49689d..43db8e808d7ab763898ce5a860b8d4644bbc9f69 100644 (file)
@@ -1,7 +1,7 @@
 notes.mergeStrategy::
        Which merge strategy to choose by default when resolving notes
        conflicts.  Must be one of `manual`, `ours`, `theirs`, `union`, or
-       `cat_sort_uniq`.  Defaults to `manual`.  See "NOTES MERGE STRATEGIES"
+       `cat_sort_uniq`.  Defaults to `manual`.  See the "NOTES MERGE STRATEGIES"
        section of linkgit:git-notes[1] for more information on each strategy.
 +
 This setting can be overridden by passing the `--strategy` option to
index 53093d99969cc090e4727a570eb667a6fba445d4..9c630863e6ff18372172111619d4ad2a54f3264f 100644 (file)
@@ -28,11 +28,17 @@ all existing objects. You can force recompression by passing the -F option
 to linkgit:git-repack[1].
 
 pack.allowPackReuse::
-       When true, and when reachability bitmaps are enabled,
-       pack-objects will try to send parts of the bitmapped packfile
-       verbatim. This can reduce memory and CPU usage to serve fetches,
-       but might result in sending a slightly larger pack. Defaults to
-       true.
+       When true or "single", and when reachability bitmaps are
+       enabled, pack-objects will try to send parts of the bitmapped
+       packfile verbatim. When "multi", and when a multi-pack
+       reachability bitmap is available, pack-objects will try to send
+       parts of all packs in the MIDX.
++
+       If only a single pack bitmap is available, and
+       `pack.allowPackReuse` is set to "multi", reuse parts of just the
+       bitmapped packfile. This can reduce memory and CPU usage to
+       serve fetches, but might result in sending a slightly larger
+       pack. Defaults to true.
 
 pack.island::
        An extended regular expression configuring a set of delta
@@ -74,7 +80,7 @@ pack.threads::
        warning. This is meant to reduce packing time on multiprocessor
        machines. The required amount of memory for the delta search window
        is however multiplied by the number of threads.
-       Specifying 0 will cause Git to auto-detect the number of CPU's
+       Specifying 0 will cause Git to auto-detect the number of CPUs
        and set the number of threads accordingly.
 
 pack.indexVersion::
@@ -83,11 +89,11 @@ pack.indexVersion::
        the new pack index with capabilities for packs larger than 4 GB
        as well as proper protection against the repacking of corrupted
        packs.  Version 2 is the default.  Note that version 2 is enforced
-       and this config option ignored whenever the corresponding pack is
+       and this config option is ignored whenever the corresponding pack is
        larger than 2 GB.
 +
 If you have an old Git that does not understand the version 2 `*.idx` file,
-cloning or fetching over a non native protocol (e.g. "http")
+cloning or fetching over a non-native protocol (e.g. "http")
 that will copy both `*.pack` file and corresponding `*.idx` file from the
 other side may give you a repository that cannot be accessed with your
 older version of Git. If the `*.pack` file is smaller than 2 GB, however,
@@ -102,8 +108,8 @@ pack.packSizeLimit::
        in the creation of multiple packfiles.
 +
 Note that this option is rarely useful, and may result in a larger total
-on-disk size (because Git will not store deltas between packs), as well
-as worse runtime performance (object lookup within multiple packs is
+on-disk size (because Git will not store deltas between packs) and
+worse runtime performance (object lookup within multiple packs is
 slower than a single pack, and optimizations like reachability bitmaps
 cannot cope with multiple packs).
 +
@@ -123,6 +129,23 @@ pack.useBitmaps::
        true. You should not generally need to turn this off unless
        you are debugging pack bitmaps.
 
+pack.useBitmapBoundaryTraversal::
+       When true, Git will use an experimental algorithm for computing
+       reachability queries with bitmaps. Instead of building up
+       complete bitmaps for all of the negated tips and then OR-ing
+       them together, consider negated tips with existing bitmaps as
+       additive (i.e. OR-ing them into the result if they exist,
+       ignoring them otherwise), and build up a bitmap at the boundary
+       instead.
++
+When using this algorithm, Git may include too many objects as a result
+of not opening up trees belonging to certain UNINTERESTING commits. This
+inexactness matches the non-bitmap traversal algorithm.
++
+In many cases, this can provide a speed-up over the exact algorithm,
+particularly when there is poor bitmap coverage of the negated side of
+the query.
+
 pack.useSparse::
        When true, git will default to using the '--sparse' option in
        'git pack-objects' when the '--revs' option is present. This
@@ -171,9 +194,15 @@ pack.writeBitmapLookupTable::
        beneficial in repositories that have relatively large bitmap
        indexes. Defaults to false.
 
+pack.readReverseIndex::
+       When true, git will read any .rev file(s) that may be available
+       (see: linkgit:gitformat-pack[5]). When false, the reverse index
+       will be generated from scratch and stored in memory. Defaults to
+       true.
+
 pack.writeReverseIndex::
        When true, git will write a corresponding .rev file (see:
        linkgit:gitformat-pack[5])
        for each new packfile that it writes in all places except for
        linkgit:git-fast-import[1] and in the bulk checkin mechanism.
-       Defaults to false.
+       Defaults to true.
index 7386fea225ae4d3215839eb42df28e414ccdf38b..0acbbea18a320f8adabf08db4f4570ef58a5edaf 100644 (file)
@@ -35,7 +35,7 @@ push.default::
 
 * `tracking` - This is a deprecated synonym for `upstream`.
 
-* `simple` - pushes the current branch with the same name on the remote.
+* `simple` - push the current branch with the same name on the remote.
 +
 If you are working on a centralized workflow (pushing to the same repository you
 pull from, which is typically `origin`), then you need to configure an upstream
@@ -67,7 +67,7 @@ new default).
 --
 
 push.followTags::
-       If set to true enable `--follow-tags` option by default.  You
+       If set to true, enable `--follow-tags` option by default.  You
        may override this configuration at time of push by specifying
        `--no-follow-tags`.
 
@@ -110,18 +110,8 @@ This will result in only b (a and c are cleared).
 ----
 
 push.recurseSubmodules::
-       Make sure all submodule commits used by the revisions to be pushed
-       are available on a remote-tracking branch. If the value is 'check'
-       then Git will verify that all submodule commits that changed in the
-       revisions to be pushed are available on at least one remote of the
-       submodule. If any commits are missing, the push will be aborted and
-       exit with non-zero status. If the value is 'on-demand' then all
-       submodules that changed in the revisions to be pushed will be
-       pushed. If on-demand was not able to push all necessary revisions
-       it will also be aborted and exit with non-zero status. If the value
-       is 'no' then default behavior of ignoring submodules when pushing
-       is retained. You may override this configuration at time of push by
-       specifying '--recurse-submodules=check|on-demand|no'.
+       May be "check", "on-demand", "only", or "no", with the same behavior
+       as that of "push --recurse-submodules".
        If not set, 'no' is used by default, unless 'submodule.recurse' is
        set (in which case a 'true' value means 'on-demand').
 
index f19bd0e04079051d6be21ee461d1a98c9a632629..c6187ab28b2fb731fad341c7d3c193872558645d 100644 (file)
@@ -9,7 +9,9 @@ rebase.stat::
        rebase. False by default.
 
 rebase.autoSquash::
-       If set to true enable `--autosquash` option by default.
+       If set to true, enable the `--autosquash` option of
+       linkgit:git-rebase[1] by default for interactive mode.
+       This can be overridden with the `--no-autosquash` option.
 
 rebase.autoStash::
        When set to true, automatically create a temporary stash entry
@@ -38,7 +40,7 @@ rebase.missingCommitsCheck::
 rebase.instructionFormat::
        A format string, as specified in linkgit:git-log[1], to be used for the
        todo list during an interactive rebase.  The format will
-       automatically have the long commit hash prepended to the format.
+       automatically have the commit hash prepended to the format.
 
 rebase.abbreviateCommands::
        If set to true, `git rebase` will use abbreviated command names in the
@@ -67,3 +69,19 @@ rebase.rescheduleFailedExec::
 
 rebase.forkPoint::
        If set to false set `--no-fork-point` option by default.
+
+rebase.rebaseMerges::
+       Whether and how to set the `--rebase-merges` option by default. Can
+       be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+       true or to `no-rebase-cousins` is equivalent to
+       `--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
+       equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
+       equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+       command line, with or without an argument, overrides any
+       `rebase.rebaseMerges` configuration.
+
+rebase.maxLabelLength::
+       When generating label names from commit subjects, truncate the names to
+       this length. By default, the names are truncated to a little less than
+       `NAME_MAX` (to allow e.g. `.lock` files to be written for the
+       corresponding loose refs).
index 85d5b5a3d2d8bf3952398628c188e4030cc3312d..c77e55b1cdd217e062450679920223a733995f77 100644 (file)
@@ -14,12 +14,12 @@ receive.autogc::
 
 receive.certNonceSeed::
        By setting this variable to a string, `git receive-pack`
-       will accept a `git push --signed` and verifies it by using
+       will accept a `git push --signed` and verify it by using
        a "nonce" protected by HMAC using this string as a secret
        key.
 
 receive.certNonceSlop::
-       When a `git push --signed` sent a push certificate with a
+       When a `git push --signed` sends a push certificate with a
        "nonce" that was issued by a receive-pack serving the same
        repository within this many seconds, export the "nonce"
        found in the certificate to `GIT_PUSH_CERT_NONCE` to the
index 40abdf6a6b5d8872772991c167c879cbb63f0ce5..3a78b5ebb1dc02e1f6cec1647ab94709018df7ff 100644 (file)
@@ -1,7 +1,7 @@
 rerere.autoUpdate::
        When set to true, `git-rerere` updates the index with the
        resulting contents after it cleanly resolves conflicts using
-       previously recorded resolution.  Defaults to false.
+       previously recorded resolutions.  Defaults to false.
 
 rerere.enabled::
        Activate recording of resolved conflicts, so that identical
index bde7f31459b98136a31a7eb1d457c5c43fddea54..577df40223a095b999b38a41df6c3a1cd1252de2 100644 (file)
@@ -14,7 +14,7 @@ repository that contains a bare repository and running a Git command
 within that directory.
 +
 This config setting is only respected in protected configuration (see
-<<SCOPES>>). This prevents the untrusted repository from tampering with
+<<SCOPES>>). This prevents untrusted repositories from tampering with
 this value.
 
 safe.directory::
@@ -32,7 +32,7 @@ override any such directories specified in the system config), add a
 `safe.directory` entry with an empty value.
 +
 This config setting is only respected in protected configuration (see
-<<SCOPES>>). This prevents the untrusted repository from tampering with this
+<<SCOPES>>). This prevents untrusted repositories from tampering with this
 value.
 +
 The value of this setting is interpolated, i.e. `~/<path>` expands to a
index 51da7088a844d3f148d78317e792efd485a2c0af..7fc770ee9e69d77b3743c8bff1b8bef35bb96f36 100644 (file)
@@ -36,7 +36,7 @@ sendemail.aliasesFile::
 
 sendemail.aliasFileType::
        Format of the file(s) specified in sendemail.aliasesFile. Must be
-       one of 'mutt', 'mailrc', 'pine', 'elm', or 'gnus', or 'sendmail'.
+       one of 'mutt', 'mailrc', 'pine', 'elm', 'gnus', or 'sendmail'.
 +
 What an alias file in each format looks like can be found in
 the documentation of the email program of the same name. The
@@ -61,6 +61,7 @@ sendemail.ccCmd::
 sendemail.chainReplyTo::
 sendemail.envelopeSender::
 sendemail.from::
+sendemail.headerCmd::
 sendemail.signedoffbycc::
 sendemail.smtpPass::
 sendemail.suppresscc::
@@ -90,7 +91,7 @@ sendemail.smtpBatchSize::
        See also the `--batch-size` option of linkgit:git-send-email[1].
 
 sendemail.smtpReloginDelay::
-       Seconds wait before reconnecting to smtp server.
+       Seconds to wait before reconnecting to the smtp server.
        See also the `--relogin-delay` option of linkgit:git-send-email[1].
 
 sendemail.forbidSendmailVariables::
index b48d532a96976ba2b9d967212f9ec2943291375f..e664eef01d10dede9b6a334e84635346e539955a 100644 (file)
@@ -2,4 +2,4 @@ sequence.editor::
        Text editor used by `git rebase -i` for editing the rebase instruction file.
        The value is meant to be interpreted by the shell when it is used.
        It can be overridden by the `GIT_SEQUENCE_EDITOR` environment variable.
-       When not configured the default commit message editor is used instead.
+       When not configured, the default commit message editor is used instead.
index afdb186df8ba6d39819d75b0542dcf4d89aafa18..cfaa29610b5a0051bed8119337c6f70ca994747d 100644 (file)
@@ -3,10 +3,10 @@ splitIndex.maxPercentChange::
        percent of entries the split index can contain compared to the
        total number of entries in both the split index and the shared
        index before a new shared index is written.
-       The value should be between 0 and 100. If the value is 0 then
-       a new shared index is always written, if it is 100 a new
+       The value should be between 0 and 100. If the value is 0, then
+       a new shared index is always written; if it is 100, a new
        shared index is never written.
-       By default the value is 20, so a new shared index is written
+       By default, the value is 20, so a new shared index is written
        if the number of entries in the split index would be greater
        than 20 percent of the total number of entries.
        See linkgit:git-update-index[1].
index b9f609ed76b7f3ff41aef03ecf0ea22fd1d355ea..ec1edaeba68aa3351331f03f981ceff9412d0899 100644 (file)
@@ -1,14 +1,14 @@
 stash.showIncludeUntracked::
        If this is set to true, the `git stash show` command will show
        the untracked files of a stash entry.  Defaults to false. See
-       description of 'show' command in linkgit:git-stash[1].
+       the description of the 'show' command in linkgit:git-stash[1].
 
 stash.showPatch::
        If this is set to true, the `git stash show` command without an
        option will show the stash entry in patch form.  Defaults to false.
-       See description of 'show' command in linkgit:git-stash[1].
+       See the description of the 'show' command in linkgit:git-stash[1].
 
 stash.showStat::
        If this is set to true, the `git stash show` command without an
-       option will show diffstat of the stash entry.  Defaults to true.
-       See description of 'show' command in linkgit:git-stash[1].
+       option will show diffstat of the stash entry.  Defaults to true.
+       See the description of the 'show' command in linkgit:git-stash[1].
index 0fc704ab80b2239ed03752846158c59dff56c31b..2ff8237f8fc4585e7a2a4c9e7a27f121bbd9d7e2 100644 (file)
@@ -47,7 +47,7 @@ status.showUntrackedFiles::
        contain only untracked files, are shown with the directory name
        only. Showing untracked files means that Git needs to lstat() all
        the files in the whole repository, which might be slow on some
-       systems. So, this variable controls how the commands displays
+       systems. So, this variable controls how the commands display
        the untracked files. Possible values are:
 +
 --
@@ -62,7 +62,7 @@ of linkgit:git-status[1] and linkgit:git-commit[1].
 
 status.submoduleSummary::
        Defaults to false.
-       If this is set to a non zero number or true (identical to -1 or an
+       If this is set to a non-zero number or true (identical to -1 or an
        unlimited number), the submodule summary will be enabled and a
        summary of commits for modified submodules will be shown (see
        --summary-limit option of linkgit:git-submodule[1]). Please note
index 6490527b45bcd4819be3fe412bcfc0aab7740ee6..0672d9911724d184b7b49ee0e50de3e992a0eac5 100644 (file)
@@ -2,7 +2,7 @@ submodule.<name>.url::
        The URL for a submodule. This variable is copied from the .gitmodules
        file to the git config via 'git submodule init'. The user can change
        the configured URL before obtaining the submodule via 'git submodule
-       update'. If neither submodule.<name>.active or submodule.active are
+       update'. If neither submodule.<name>.active nor submodule.active are
        set, the presence of this variable is used as a fallback to indicate
        whether the submodule is of interest to git commands.
        See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
@@ -35,7 +35,7 @@ submodule.<name>.ignore::
        a submodule as modified. When set to "all", it will never be considered
        modified (but it will nonetheless show up in the output of status and
        commit when it has been staged), "dirty" will ignore all changes
-       to the submodules work tree and
+       to the submodule's work tree and
        takes only differences between the HEAD of the submodule and the commit
        recorded in the superproject into account. "untracked" will additionally
        let submodules with modified tracked files in their work tree show up.
index fe1642f0d40251d466c78639fff44475edb6b25b..3b6bca2b7ae44c99404699745c4da3bfd2633681 100644 (file)
@@ -66,6 +66,6 @@ trace2.destinationDebug::
 
 trace2.maxFiles::
        Integer.  When writing trace files to a target directory, do not
-       write additional traces if we would exceed this many files. Instead,
+       write additional traces if doing so would exceed this many files. Instead,
        write a sentinel file that will block further tracing to this
        directory. Defaults to 0, which disables this check.
index 264812cca4db9a0a7b70be3eed7703d3439c9f46..a9cbdb88a1fe11f364da005bdfe490cd2e28dc0b 100644 (file)
@@ -7,7 +7,7 @@ transfer.credentialsInUrl::
        and any other direct use of the configured URL.
 +
 Note that this is currently limited to detecting credentials in
-`remote.<name>.url` configuration, it won't detect credentials in
+`remote.<name>.url` configuration; it won't detect credentials in
 `remote.<name>.pushurl` configuration.
 +
 You might want to enable this to prevent inadvertent credentials
@@ -21,12 +21,12 @@ exposure, e.g. because:
   system.
 * The git programs will pass the full URL to one another as arguments
   on the command-line, meaning the credentials will be exposed to other
-  users on OS's or systems that allow other users to see the full
+  unprivileged users on systems that allow them to see the full
   process list of other users. On linux the "hidepid" setting
   documented in procfs(5) allows for configuring this behavior.
 +
 If such concerns don't apply to you then you probably don't need to be
-concerned about credentials exposure due to storing that sensitive
+concerned about credentials exposure due to storing sensitive
 data in git's configuration files. If you do want to use this, set
 `transfer.credentialsInUrl` to one of these values:
 +
@@ -115,3 +115,9 @@ transfer.unpackLimit::
 transfer.advertiseSID::
        Boolean. When true, client and server processes will advertise their
        unique session IDs to their remote counterpart. Defaults to false.
+
+transfer.bundleURI::
+       When `true`, local `git clone` commands will request bundle
+       information from the remote server (if advertised) and download
+       bundles before continuing the clone through the Git protocol.
+       Defaults to `false`.
index ec9233b060a82c41dd9a01785a6f1e748ddf0ad9..2ffc38d164786fbe7be6a5d3ee770e6869ba7825 100644 (file)
@@ -5,14 +5,14 @@ author.email::
 committer.name::
 committer.email::
        The `user.name` and `user.email` variables determine what ends
-       up in the `author` and `committer` field of commit
+       up in the `author` and `committer` fields of commit
        objects.
        If you need the `author` or `committer` to be different, the
-       `author.name`, `author.email`, `committer.name` or
+       `author.name`, `author.email`, `committer.name`, or
        `committer.email` variables can be set.
-       Also, all of these can be overridden by the `GIT_AUTHOR_NAME`,
+       All of these can be overridden by the `GIT_AUTHOR_NAME`,
        `GIT_AUTHOR_EMAIL`, `GIT_COMMITTER_NAME`,
-       `GIT_COMMITTER_EMAIL` and `EMAIL` environment variables.
+       `GIT_COMMITTER_EMAIL`, and `EMAIL` environment variables.
 +
 Note that the `name` forms of these variables conventionally refer to
 some form of a personal name.  See linkgit:git-commit[1] and the
@@ -40,7 +40,7 @@ user.signingKey::
        your private ssh key or the public key when ssh-agent is used.
        Alternatively it can contain a public key prefixed with `key::`
        directly (e.g.: "key::ssh-rsa XXXXXX identifier"). The private key
-       needs to be available via ssh-agent. If not set git will call
+       needs to be available via ssh-agent. If not set Git will call
        gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the
        first key available. For backward compatibility, a raw key which
        begins with "ssh-", such as "ssh-rsa XXXXXX identifier", is treated
index 6c7cc054fad2503dabf21073624875d2b6035abe..0cff0908193a29ff5e482cbd3a6780460b3399eb 100644 (file)
@@ -19,14 +19,14 @@ with those suffixes.  E.g. if "-pre" appears before "-rc" in the
 configuration, then all "1.0-preX" tags will be listed before any
 "1.0-rcX" tags.  The placement of the main release tag relative to tags
 with various suffixes can be determined by specifying the empty suffix
-among those other suffixes.  E.g. if the suffixes "-rc", "", "-ck" and
+among those other suffixes.  E.g. if the suffixes "-rc", "", "-ck", and
 "-bfs" appear in the configuration in this order, then all "v4.8-rcX" tags
 are listed first, followed by "v4.8", then "v4.8-ckX" and finally
 "v4.8-bfsX".
 +
-If more than one suffixes match the same tagname, then that tagname will
+If more than one suffix matches the same tagname, then that tagname will
 be sorted according to the suffix which starts at the earliest position in
-the tagname.  If more than one different matching suffixes start at
+the tagname.  If more than one different matching suffix starts at
 that earliest position, then that tagname will be sorted according to the
 longest of those suffixes.
 The sorting order between different suffixes is undefined if they are
index c78063d4f74a30dc58c98a7af5db7fcdeeab69f3..4b5aa5c2e045f5c135b96fb9ec03b06aad0b91ea 100644 (file)
@@ -1,3 +1,4 @@
+[[generate_patch_text_with_p]]
 Generating patch text with -p
 -----------------------------
 
@@ -16,7 +17,7 @@ You can customize the creation of patch text via the
 What the -p option produces is slightly different from the traditional
 diff format:
 
-1.   It is preceded with a "git diff" header that looks like this:
+1.   It is preceded by a "git diff" header that looks like this:
 
        diff --git a/file1 b/file2
 +
@@ -24,9 +25,9 @@ The `a/` and `b/` filenames are the same unless rename/copy is
 involved.  Especially, even for a creation or a deletion,
 `/dev/null` is _not_ used in place of the `a/` or `b/` filenames.
 +
-When rename/copy is involved, `file1` and `file2` show the
+When rename/copy is involved, `file1` and `file2` show the
 name of the source file of the rename/copy and the name of
-the file that rename/copy produces, respectively.
+the file that the rename/copy produces, respectively.
 
 2.   It is followed by one or more extended header lines:
 
@@ -76,7 +77,7 @@ separate lines indicate the old and the new mode.
 
 5.  Hunk headers mention the name of the function to which the hunk
     applies.  See "Defining a custom hunk-header" in
-    linkgit:gitattributes[5] for details of how to tailor to this to
+    linkgit:gitattributes[5] for details of how to tailor this to
     specific languages.
 
 
@@ -88,7 +89,7 @@ produce a 'combined diff' when showing a merge. This is the default
 format when showing merges with linkgit:git-diff[1] or
 linkgit:git-show[1]. Note also that you can give suitable
 `--diff-merges` option to any of these commands to force generation of
-diffs in specific format.
+diffs in specific format.
 
 A "combined diff" format looks like this:
 
@@ -122,7 +123,7 @@ index fabadb8,cc95eb0..4866510
                for_each_ref(get_name);
 ------------
 
-1.   It is preceded with a "git diff" header, that looks like
+1.   It is preceded by a "git diff" header, that looks like
      this (when the `-c` option is used):
 
        diff --combined file
@@ -141,22 +142,22 @@ or like this (when the `--cc` option is used):
 +
 The `mode <mode>,<mode>..<mode>` line appears only if at least one of
 the <mode> is different from the rest. Extended headers with
-information about detected contents movement (renames and
-copying detection) are designed to work with diff of two
+information about detected content movement (renames and
+copying detection) are designed to work with the diff of two
 <tree-ish> and are not used by combined diff format.
 
-3.   It is followed by two-line from-file/to-file header
+3.   It is followed by a two-line from-file/to-file header:
 
        --- a/file
        +++ b/file
 +
-Similar to two-line header for traditional 'unified' diff
+Similar to the two-line header for the traditional 'unified' diff
 format, `/dev/null` is used to signal created or deleted
 files.
 +
 However, if the --combined-all-paths option is provided, instead of a
-two-line from-file/to-file you get a N+1 line from-file/to-file header,
-where N is the number of parents in the merge commit
+two-line from-file/to-file, you get an N+1 line from-file/to-file header,
+where N is the number of parents in the merge commit:
 
        --- a/file
        --- a/file
@@ -196,7 +197,7 @@ added, from the point of view of that parent).
 In the above example output, the function signature was changed
 from both files (hence two `-` removals from both file1 and
 file2, plus `++` to mean one line that was added does not appear
-in either file1 or file2).  Also eight other lines are the same
+in either file1 or file2).  Also, eight other lines are the same
 from file1 but do not appear in file2 (hence prefixed with `+`).
 
 When shown by `git diff-tree -c`, it compares the parents of a
index 3674ac48e92c29e2a9fe5db0ccf8ce72e7727290..53ec3c9a3476bd12b8a8f4b6c31552b2767d8366 100644 (file)
@@ -22,78 +22,94 @@ ifndef::git-format-patch[]
 -p::
 -u::
 --patch::
-       Generate patch (see section on generating patches).
+       Generate patch (see <<generate_patch_text_with_p>>).
 ifdef::git-diff[]
        This is the default.
 endif::git-diff[]
 
 -s::
 --no-patch::
-       Suppress diff output. Useful for commands like `git show` that
-       show the patch by default, or to cancel the effect of `--patch`.
+       Suppress all output from the diff machinery.  Useful for
+       commands like `git show` that show the patch by default to
+       squelch their output, or to cancel the effect of options like
+       `--patch`, `--stat` earlier on the command line in an alias.
+
 endif::git-format-patch[]
 
 ifdef::git-log[]
---diff-merges=(off|none|on|first-parent|1|separate|m|combined|c|dense-combined|cc|remerge|r)::
+-m::
+       Show diffs for merge commits in the default format. This is
+       similar to '--diff-merges=on', except `-m` will
+       produce no output unless `-p` is given as well.
+
+-c::
+       Produce combined diff output for merge commits.
+       Shortcut for '--diff-merges=combined -p'.
+
+--cc::
+       Produce dense combined diff output for merge commits.
+       Shortcut for '--diff-merges=dense-combined -p'.
+
+--dd::
+       Produce diff with respect to first parent for both merge and
+       regular commits.
+       Shortcut for '--diff-merges=first-parent -p'.
+
+--remerge-diff::
+       Produce remerge-diff output for merge commits.
+       Shortcut for '--diff-merges=remerge -p'.
+
 --no-diff-merges::
+       Synonym for '--diff-merges=off'.
+
+--diff-merges=<format>::
        Specify diff format to be used for merge commits. Default is
-       {diff-merges-default} unless `--first-parent` is in use, in which case
-       `first-parent` is the default.
+       {diff-merges-default} unless `--first-parent` is in use, in
+       which case `first-parent` is the default.
 +
---diff-merges=(off|none):::
---no-diff-merges:::
+The following formats are supported:
++
+--
+off, none::
        Disable output of diffs for merge commits. Useful to override
        implied value.
 +
---diff-merges=on:::
---diff-merges=m:::
--m:::
-       This option makes diff output for merge commits to be shown in
-       the default format. `-m` will produce the output only if `-p`
-       is given as well. The default format could be changed using
-       `log.diffMerges` configuration parameter, which default value
+on, m::
+       Make diff output for merge commits to be shown in the default
+       format. The default format can be changed using
+       `log.diffMerges` configuration variable, whose default value
        is `separate`.
 +
---diff-merges=first-parent:::
---diff-merges=1:::
-       This option makes merge commits show the full diff with
-       respect to the first parent only.
+first-parent, 1::
+       Show full diff with respect to first parent. This is the same
+       format as `--patch` produces for non-merge commits.
++
+separate::
+       Show full diff with respect to each of parents.
+       Separate log entry and diff is generated for each parent.
++
+combined, c::
+       Show differences from each of the parents to the merge
+       result simultaneously instead of showing pairwise diff between
+       a parent and the result one at a time. Furthermore, it lists
+       only files which were modified from all parents.
 +
---diff-merges=separate:::
-       This makes merge commits show the full diff with respect to
-       each of the parents. Separate log entry and diff is generated
-       for each parent.
+dense-combined, cc::
+       Further compress output produced by `--diff-merges=combined`
+       by omitting uninteresting hunks whose contents in the parents
+       have only two variants and the merge result picks one of them
+       without modification.
 +
---diff-merges=remerge:::
---diff-merges=r:::
---remerge-diff:::
-       With this option, two-parent merge commits are remerged to
-       create a temporary tree object -- potentially containing files
-       with conflict markers and such.  A diff is then shown between
-       that temporary tree and the actual merge commit.
+remerge, r::
+       Remerge two-parent merge commits to create a temporary tree
+       object--potentially containing files with conflict markers
+       and such.  A diff is then shown between that temporary tree
+       and the actual merge commit.
 +
 The output emitted when this option is used is subject to change, and
 so is its interaction with other options (unless explicitly
 documented).
-+
---diff-merges=combined:::
---diff-merges=c:::
--c:::
-       With this option, diff output for a merge commit shows the
-       differences from each of the parents to the merge result
-       simultaneously instead of showing pairwise diff between a
-       parent and the result one at a time. Furthermore, it lists
-       only files which were modified from all parents. `-c` implies
-       `-p`.
-+
---diff-merges=dense-combined:::
---diff-merges=cc:::
---cc:::
-       With this option the output produced by
-       `--diff-merges=combined` is further compressed by omitting
-       uninteresting hunks whose contents in the parents have only
-       two variants and the merge result picks one of them without
-       modification.  `--cc` implies `-p`.
+--
 
 --combined-all-paths::
        This flag causes combined diffs (used for merge commits) to
@@ -201,14 +217,15 @@ have to use `--diff-algorithm=default` option.
        part. Maximum width defaults to terminal width, or 80 columns
        if not connected to a terminal, and can be overridden by
        `<width>`. The width of the filename part can be limited by
-       giving another width `<name-width>` after a comma. The width
-       of the graph part can be limited by using
-       `--stat-graph-width=<width>` (affects all commands generating
-       a stat graph) or by setting `diff.statGraphWidth=<width>`
-       (does not affect `git format-patch`).
-       By giving a third parameter `<count>`, you can limit the
-       output to the first `<count>` lines, followed by `...` if
-       there are more.
+       giving another width `<name-width>` after a comma or by setting
+       `diff.statNameWidth=<width>`. The width of the graph part can be
+       limited by using `--stat-graph-width=<width>` or by setting
+       `diff.statGraphWidth=<width>`. Using `--stat` or
+       `--stat-graph-width` affects all commands generating a stat graph,
+       while setting `diff.statNameWidth` or `diff.statGraphWidth`
+       does not affect `git format-patch`.
+       By giving a third parameter `<count>`, you can limit the output to
+       the first `<count>` lines, followed by `...` if there are more.
 +
 These parameters can also be set individually with `--stat-width=<width>`,
 `--stat-name-width=<name-width>` and `--stat-count=<count>`.
@@ -297,7 +314,7 @@ ifndef::git-format-patch[]
 
 -z::
 ifdef::git-log[]
-       Separate the commits with NULs instead of with new newlines.
+       Separate the commits with NULs instead of newlines.
 +
 Also, when `--raw` or `--numstat` has been given, do not munge
 pathnames and use NULs as output field terminators.
@@ -729,7 +746,7 @@ matches "`fooasdfbar`" and "`foo/bar/baz/asdf`" but not "`foobarx`".
 --rotate-to=<file>::
        Discard the files before the named <file> from the output
        (i.e. 'skip to'), or move them to the end of the output
-       (i.e. 'rotate to').  These were invented primarily for use
+       (i.e. 'rotate to').  These options were invented primarily for the use
        of the `git difftool` command, and may not be very useful
        otherwise.
 
@@ -846,6 +863,11 @@ endif::git-format-patch[]
 --no-prefix::
        Do not show any source or destination prefix.
 
+--default-prefix::
+       Use the default source and destination prefixes ("a/" and "b/").
+       This is usually the default already, but may be used to override
+       config such as `diff.noprefix`.
+
 --line-prefix=<prefix>::
        Prepend an additional prefix to every line of output.
 
index 1694300e50bfcfa58cc7798614a254fec2e219a5..fb09e0ac0ebcac9f88a0fbe08c258398586ba0fe 100755 (executable)
@@ -153,7 +153,7 @@ render_tree () {
                make -j$parallel -C "$tmp/worktree" \
                        $makemanflags \
                        GIT_VERSION=omitted \
-                       SOURCE_DATE_EPOCH=0 \
+                       GIT_DATE=1970-01-01 \
                        DESTDIR="$tmp/installed/$dname+" \
                        install-man &&
                mv "$tmp/installed/$dname+" "$tmp/installed/$dname"
index 622bd84768b056e23fa0f432e745aaffd651c734..54ebb4452e997f8c62c9dd2421948086249102bd 100644 (file)
@@ -1,5 +1,6 @@
---all::
-       Fetch all remotes.
+--[no-]all::
+       Fetch all remotes. This overrides the configuration variable
+       `fetch.all`.
 
 -a::
 --append::
@@ -43,7 +44,7 @@ the current repository has the same history as the source repository.
 --update-shallow::
        By default when fetching from a shallow repository,
        `git fetch` refuses refs that require updating
-       .git/shallow. This option updates .git/shallow and accept such
+       .git/shallow. This option updates .git/shallow and accepts such
        refs.
 
 --negotiation-tip=<commit|glob>::
@@ -78,6 +79,13 @@ linkgit:git-config[1].
 --dry-run::
        Show what would be done, without making any changes.
 
+--porcelain::
+       Print the output to standard output in an easy-to-parse format for
+       scripts. See section OUTPUT in linkgit:git-fetch[1] for details.
++
+This is incompatible with `--recurse-submodules=[yes|on-demand]` and takes
+precedence over the `fetch.output` config option.
+
 ifndef::git-pull[]
 --[no-]write-fetch-head::
        Write the list of remote refs fetched in the `FETCH_HEAD`
@@ -89,7 +97,7 @@ endif::git-pull[]
 
 -f::
 --force::
-       When 'git fetch' is used with `<src>:<dst>` refspec it may
+       When 'git fetch' is used with `<src>:<dst>` refspec, it may
        refuse to update the local branch as discussed
 ifdef::git-pull[]
        in the `<refspec>` part of the linkgit:git-fetch[1]
diff --git a/Documentation/fsck-msgids.txt b/Documentation/fsck-msgids.txt
new file mode 100644 (file)
index 0000000..f643585
--- /dev/null
@@ -0,0 +1,180 @@
+`badDate`::
+       (ERROR) Invalid date format in an author/committer line.
+
+`badDateOverflow`::
+       (ERROR) Invalid date value in an author/committer line.
+
+`badEmail`::
+       (ERROR) Invalid email format in an author/committer line.
+
+`badFilemode`::
+       (INFO) A tree contains a bad filemode entry.
+
+`badName`::
+       (ERROR) An author/committer name is empty.
+
+`badObjectSha1`::
+       (ERROR) An object has a bad sha1.
+
+`badParentSha1`::
+       (ERROR) A commit object has a bad parent sha1.
+
+`badTagName`::
+       (INFO) A tag has an invalid format.
+
+`badTimezone`::
+       (ERROR) Found an invalid time zone in an author/committer line.
+
+`badTree`::
+       (ERROR) A tree cannot be parsed.
+
+`badTreeSha1`::
+       (ERROR) A tree has an invalid format.
+
+`badType`::
+       (ERROR) Found an invalid object type.
+
+`duplicateEntries`::
+       (ERROR) A tree contains duplicate file entries.
+
+`emptyName`::
+       (WARN) A path contains an empty name.
+
+`extraHeaderEntry`::
+       (IGNORE) Extra headers found after `tagger`.
+
+`fullPathname`::
+       (WARN) A path contains the full path starting with "/".
+
+`gitattributesBlob`::
+       (ERROR) A non-blob found at `.gitattributes`.
+
+`gitattributesLarge`::
+       (ERROR) The `.gitattributes` blob is too large.
+
+`gitattributesLineLength`::
+       (ERROR) The `.gitattributes` blob contains too long lines.
+
+`gitattributesMissing`::
+       (ERROR) Unable to read `.gitattributes` blob.
+
+`gitattributesSymlink`::
+       (INFO) `.gitattributes` is a symlink.
+
+`gitignoreSymlink`::
+       (INFO) `.gitignore` is a symlink.
+
+`gitmodulesBlob`::
+       (ERROR) A non-blob found at `.gitmodules`.
+
+`gitmodulesLarge`::
+       (ERROR) The `.gitmodules` file is too large to parse.
+
+`gitmodulesMissing`::
+       (ERROR) Unable to read `.gitmodules` blob.
+
+`gitmodulesName`::
+       (ERROR) A submodule name is invalid.
+
+`gitmodulesParse`::
+       (INFO) Could not parse `.gitmodules` blob.
+
+`gitmodulesLarge`;
+       (ERROR) `.gitmodules` blob is too large to parse.
+
+`gitmodulesPath`::
+       (ERROR) `.gitmodules` path is invalid.
+
+`gitmodulesSymlink`::
+       (ERROR) `.gitmodules` is a symlink.
+
+`gitmodulesUpdate`::
+       (ERROR) Found an invalid submodule update setting.
+
+`gitmodulesUrl`::
+       (ERROR) Found an invalid submodule url.
+
+`hasDot`::
+       (WARN) A tree contains an entry named `.`.
+
+`hasDotdot`::
+       (WARN) A tree contains an entry named `..`.
+
+`hasDotgit`::
+       (WARN) A tree contains an entry named `.git`.
+
+`largePathname`::
+       (WARN) A tree contains an entry with a very long path name. If
+       the value of `fsck.largePathname` contains a colon, that value
+       is used as the maximum allowable length (e.g., "warn:10" would
+       complain about any path component of 11 or more bytes). The
+       default value is 4096.
+
+`mailmapSymlink`::
+       (INFO) `.mailmap` is a symlink.
+
+`missingAuthor`::
+       (ERROR) Author is missing.
+
+`missingCommitter`::
+       (ERROR) Committer is missing.
+
+`missingEmail`::
+       (ERROR) Email is missing in an author/committer line.
+
+`missingNameBeforeEmail`::
+       (ERROR) Missing name before an email in an author/committer line.
+
+`missingObject`::
+       (ERROR) Missing `object` line in tag object.
+
+`missingSpaceBeforeDate`::
+       (ERROR) Missing space before date in an author/committer line.
+
+`missingSpaceBeforeEmail`::
+       (ERROR) Missing space before the email in an author/committer line.
+
+`missingTag`::
+       (ERROR) Unexpected end after `type` line in a tag object.
+
+`missingTagEntry`::
+       (ERROR) Missing `tag` line in a tag object.
+
+`missingTaggerEntry`::
+       (INFO) Missing `tagger` line in a tag object.
+
+`missingTree`::
+       (ERROR) Missing `tree` line in a commit object.
+
+`missingType`::
+       (ERROR) Invalid type value on the `type` line in a tag object.
+
+`missingTypeEntry`::
+       (ERROR) Missing `type` line in a tag object.
+
+`multipleAuthors`::
+       (ERROR) Multiple author lines found in a commit.
+
+`nulInCommit`::
+       (WARN) Found a NUL byte in the commit object body.
+
+`nulInHeader`::
+       (FATAL) NUL byte exists in the object header.
+
+`nullSha1`::
+       (WARN) Tree contains entries pointing to a null sha1.
+
+`treeNotSorted`::
+       (ERROR) A tree is not properly sorted.
+
+`unknownType`::
+       (ERROR) Found an unknown object type.
+
+`unterminatedHeader`::
+       (FATAL) Missing end-of-line in the object header.
+
+`zeroPaddedDate`::
+       (ERROR) Found a zero padded date in an author/committer line.
+
+`zeroPaddedFilemode`::
+       (WARN) Found a zero padded filemode in a tree.
index a030d33c6e704adaf0b12168cb7ef0d062a1ef19..3d2e6707168b69d591e3bf2a50abe3a62fc2ee23 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
-         [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse]
+         [--edit | -e] [--[no-]all | -A | --[no-]ignore-removal | [--update | -u]] [--sparse]
          [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
          [--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]]
          [--] [<pathspec>...]
@@ -274,7 +274,7 @@ status::
 ------------
               staged     unstaged path
      1:       binary      nothing foo.png
-     2:     +403/-35        +1/-1 git-add--interactive.perl
+     2:     +403/-35        +1/-1 add-interactive.c
 ------------
 +
 It shows that foo.png has differences from HEAD (but that is
@@ -282,7 +282,7 @@ binary so line count cannot be shown) and there is no
 difference between indexed copy and the working tree
 version (if the working tree version were also different,
 'binary' would have been shown in place of 'nothing').  The
-other file, git-add{litdd}interactive.perl, has 403 lines added
+other file, add-interactive.c, has 403 lines added
 and 35 lines deleted if you commit what is in the index, but
 working tree file has further modifications (one addition and
 one deletion).
@@ -303,7 +303,7 @@ like this:
 ------------
            staged     unstaged path
   1:       binary      nothing foo.png
-* 2:     +403/-35        +1/-1 git-add--interactive.perl
+* 2:     +403/-35        +1/-1 add-interactive.c
 ------------
 +
 To remove selection, prefix the input with `-`
index 326276e51ce5734675f842771d41290057c53f90..e080458d6c45891ec1db74b9df155aeab111e79f 100644 (file)
@@ -9,10 +9,10 @@ git-am - Apply a series of patches from a mailbox
 SYNOPSIS
 --------
 [verse]
-'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8]
+'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--no-verify]
         [--[no-]3way] [--interactive] [--committer-date-is-author-date]
         [--ignore-date] [--ignore-space-change | --ignore-whitespace]
-        [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
+        [--whitespace=<action>] [-C<n>] [-p<n>] [--directory=<dir>]
         [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
         [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
         [--quoted-cr=<action>]
@@ -22,9 +22,11 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Splits mail messages in a mailbox into commit log message,
-authorship information and patches, and applies them to the
-current branch.
+Splits mail messages in a mailbox into commit log messages,
+authorship information, and patches, and applies them to the
+current branch. You could think of it as a reverse operation
+of linkgit:git-format-patch[1] run on a branch with a straight
+history without merges.
 
 OPTIONS
 -------
@@ -67,7 +69,7 @@ OPTIONS
 --empty=(stop|drop|keep)::
        By default, or when the option is set to 'stop', the command
        errors out on an input e-mail message lacking a patch
-       and stops into the middle of the current am session. When this
+       and stops in the middle of the current am session. When this
        option is set to 'drop', skip such an e-mail message instead.
        When this option is set to 'keep', create an empty commit,
        recording the contents of the e-mail message as its log.
@@ -92,7 +94,7 @@ OPTIONS
        Pass `-u` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
        The proposed commit log message taken from the e-mail
        is re-coded into UTF-8 encoding (configuration variable
-       `i18n.commitEncoding` can be used to specify project's
+       `i18n.commitEncoding` can be used to specify the project's
        preferred encoding if it is not UTF-8).
 +
 This was optional in prior versions of git, but now it is the
@@ -116,7 +118,7 @@ include::rerere-options.txt[]
 
 --ignore-space-change::
 --ignore-whitespace::
---whitespace=<option>::
+--whitespace=<action>::
 -C<n>::
 -p<n>::
 --directory=<dir>::
@@ -132,12 +134,18 @@ include::rerere-options.txt[]
        automatically. This option allows the user to bypass the automatic
        detection and specify the patch format that the patch(es) should be
        interpreted as. Valid formats are mbox, mboxrd,
-       stgit, stgit-series and hg.
+       stgit, stgit-series, and hg.
 
 -i::
 --interactive::
        Run interactively.
 
+-n::
+--no-verify::
+       By default, the pre-applypatch and applypatch-msg hooks are run.
+       When any of `--no-verify` or `-n` is given, these are bypassed.
+       See also linkgit:githooks[5].
+
 --committer-date-is-author-date::
        By default the command records the date from the e-mail
        message as the commit author date, and uses the time of
@@ -184,7 +192,7 @@ include::rerere-options.txt[]
 
 --abort::
        Restore the original branch and abort the patching operation.
-       Revert contents of files involved in the am operation to their
+       Revert the contents of files involved in the am operation to their
        pre-am state.
 
 --quit::
@@ -267,7 +275,8 @@ include::config/am.txt[]
 
 SEE ALSO
 --------
-linkgit:git-apply[1].
+linkgit:git-apply[1],
+linkgit:git-format-patch[1].
 
 GIT
 ---
index e44a831339d4a6d7f9ca3d9879dbb5a9b4ffac0a..5ae8aabe0f88c69dbf70cf0075f2a29041437ccd 100644 (file)
@@ -8,7 +8,7 @@ git-annotate - Annotate file lines with commit information
 SYNOPSIS
 --------
 [verse]
-'git annotate' [<options>] <file> [<revision>]
+'git annotate' [<options>] [<rev-opts>] [<rev>] [--] <file>
 
 DESCRIPTION
 -----------
index 1d478cbe9b5b873566fd9fc34e5d837bd0a4595c..9cce68a38be10f3a4ff3130adb1a2b300835cdcb 100644 (file)
@@ -23,8 +23,8 @@ DESCRIPTION
 Reads the supplied diff output (i.e. "a patch") and applies it to files.
 When running from a subdirectory in a repository, patched paths
 outside the directory are ignored.
-With the `--index` option the patch is also applied to the index, and
-with the `--cached` option the patch is only applied to the index.
+With the `--index` option, the patch is also applied to the index, and
+with the `--cached` option, the patch is only applied to the index.
 Without these options, the command applies the patch only to files,
 and does not require them to be in a Git repository.
 
@@ -52,7 +52,7 @@ OPTIONS
 --summary::
        Instead of applying the patch, output a condensed
        summary of information obtained from git diff extended
-       headers, such as creations, renames and mode changes.
+       headers, such as creations, renames, and mode changes.
        Turns off "apply".
 
 --check::
@@ -140,7 +140,7 @@ linkgit:git-config[1]).
        applying a diff generated with `--unified=0`. To bypass these
        checks use `--unidiff-zero`.
 +
-Note, for the reasons stated above usage of context-free patches is
+Note, for the reasons stated above, the usage of context-free patches is
 discouraged.
 
 --apply::
@@ -159,9 +159,9 @@ discouraged.
 
 --allow-binary-replacement::
 --binary::
-       Historically we did not allow binary patch applied
+       Historically we did not allow binary patch application
        without an explicit permission from the user, and this
-       flag was the way to do so.  Currently we always allow binary
+       flag was the way to do so.  Currently, we always allow binary
        patch application, so this is a no-op.
 
 --exclude=<path-pattern>::
@@ -208,7 +208,7 @@ behavior:
 * `warn` outputs warnings for a few such errors, but applies the
   patch as-is (default).
 * `fix` outputs warnings for a few such errors, and applies the
-  patch after fixing them (`strip` is a synonym --- the tool
+  patch after fixing them (`strip` is a synonym -- the tool
   used to consider only trailing whitespace characters as errors, and the
   fix involved 'stripping' them, but modern Gits do more).
 * `error` outputs warnings for a few such errors, and refuses
@@ -257,7 +257,7 @@ the `--unsafe-paths` option to override this safety check.  This option
 has no effect when `--index` or `--cached` is in use.
 
 --allow-empty::
-       Don't return error for patches containing no diff. This includes
+       Don't return an error for patches containing no diff. This includes
        empty patches and patches with commit text only.
 
 CONFIGURATION
index 60c040988bb803cbca608b9e58647b75a75ac173..98526f2bebad16e2814f084005c12c2359378ec2 100644 (file)
@@ -21,14 +21,14 @@ structure for the named tree, and writes it out to the standard
 output.  If <prefix> is specified it is
 prepended to the filenames in the archive.
 
-'git archive' behaves differently when given a tree ID versus when
-given a commit ID or tag ID.  In the first case the current time is
-used as the modification time of each file in the archive.  In the latter
-case the commit time as recorded in the referenced commit object is
-used instead.  Additionally the commit ID is stored in a global
-extended pax header if the tar format is used; it can be extracted
-using 'git get-tar-commit-id'. In ZIP files it is stored as a file
-comment.
+'git archive' behaves differently when given a tree ID as opposed to a
+commit ID or tag ID. When a tree ID is provided, the current time is
+used as the modification time of each file in the archive. On the
+other hand, when a commit ID or tag ID is provided, the commit time as
+recorded in the referenced commit object is used instead.
+Additionally the commit ID is stored in a global extended pax header
+if the tar format is used; it can be extracted using 'git
+get-tar-commit-id'. In ZIP files it is stored as a file comment.
 
 OPTIONS
 -------
@@ -86,6 +86,11 @@ cases, write an untracked file and use `--add-file` instead.
        Look for attributes in .gitattributes files in the working tree
        as well (see <<ATTRIBUTES>>).
 
+--mtime=<time>::
+       Set modification time of archive entries.  Without this option
+       the committer time is used if `<tree-ish>` is a commit or tag,
+       and the current time if it is a tree.
+
 <extra>::
        This can be any options that the archiver backend understands.
        See next section.
index f3d9566c898818bac6219ecf324e4dd3f188498d..0bc165788e77bd7dc21e1f9f5aec8d8dbc65a77e 100644 (file)
@@ -1347,8 +1347,8 @@ author to given a talk and for publishing this paper.
 References
 ----------
 
-- [[[1]]] https://www.nist.gov/sites/default/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'.  Nist Planning Report 02-3], see Executive Summary and Chapter 8.
-- [[[2]]] http://www.oracle.com/technetwork/java/codeconvtoc-136057.html['Code Conventions for the Java Programming Language'. Sun Microsystems.]
+- [[[1]]] https://web.archive.org/web/20091206032101/http://www.nist.gov/public_affairs/releases/n02-10.htm['Software Errors Cost U.S. Economy $59.5 Billion Annually'. Nist News Release.] See also https://www.nist.gov/system/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'.  Nist Planning Report 02-3], Executive Summary and Chapter 8.
+- [[[2]]] https://www.oracle.com/java/technologies/javase/codeconventions-introduction.html['Code Conventions for the Java Programming Language: 1. Introduction'. Sun Microsystems.]
 - [[[3]]] https://en.wikipedia.org/wiki/Software_maintenance['Software maintenance'. Wikipedia.]
 - [[[4]]] https://lore.kernel.org/git/7vps5xsbwp.fsf_-_@assigned-by-dhcp.cox.net/[Junio C Hamano. 'Automated bisect success story'.]
 - [[[5]]] https://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.]
index fbb39fbdf5d62a455c8b62d4870817998d0434ad..8e01f1d6189d523fc4c5a196c64349117ea389fe 100644 (file)
@@ -16,8 +16,8 @@ DESCRIPTION
 The command takes various subcommands, and different options depending
 on the subcommand:
 
- git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]
-                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
+ git bisect start [--term-(new|bad)=<term-new> --term-(old|good)=<term-old>]
+                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
  git bisect (bad|new|<term-new>) [<rev>]
  git bisect (good|old|<term-old>) [<rev>...]
  git bisect terms [--term-good | --term-bad]
@@ -26,7 +26,7 @@ on the subcommand:
  git bisect (visualize|view)
  git bisect replay <logfile>
  git bisect log
- git bisect run <cmd>...
+ git bisect run <cmd> [<arg>...]
  git bisect help
 
 This command uses a binary search algorithm to find which commit in
@@ -204,9 +204,14 @@ as an alternative to `visualize`):
 $ git bisect visualize
 ------------
 
-If the `DISPLAY` environment variable is not set, 'git log' is used
-instead.  You can also give command-line options such as `-p` and
-`--stat`.
+Git detects a graphical environment through various environment variables:
+`DISPLAY`, which is set in X Window System environments on Unix systems.
+`SESSIONNAME`, which is set under Cygwin in interactive desktop sessions.
+`MSYSTEM`, which is set under Msys2 and Git for Windows.
+`SECURITYSESSIONID`, which may be set on macOS in interactive desktop sessions.
+
+If none of these environment variables is set, 'git log' is used instead.
+You can also give command-line options such as `-p` and `--stat`.
 
 ------------
 $ git bisect visualize --stat
@@ -294,7 +299,7 @@ Cutting down bisection by giving more parameters to bisect start
 
 You can further cut down the number of trials, if you know what part of
 the tree is involved in the problem you are tracking down, by specifying
-path parameters when issuing the `bisect start` command:
+pathspec parameters when issuing the `bisect start` command:
 
 ------------
 $ git bisect start -- arch/i386 include/asm-i386
@@ -357,7 +362,7 @@ OPTIONS
 --no-checkout::
 +
 Do not checkout the new working tree at each iteration of the bisection
-process. Instead just update a special reference named `BISECT_HEAD` to make
+process. Instead just update the reference named `BISECT_HEAD` to make
 it point to the commit that should be tested.
 +
 This option may be useful when the test you would perform in each step
index 4400a17330b4204227050b76d380bc84b017628a..5720d04ffe4e7434c1798cad4833e233fc71f534 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
            [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
            [--ignore-rev <rev>] [--ignore-revs-file <file>]
            [--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
-           [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>
+           [ --contents <file> ] [<rev> | --reverse <rev>..<rev>] [--] <file>
 
 DESCRIPTION
 -----------
@@ -77,7 +77,7 @@ include::blame-options.txt[]
 
 -e::
 --show-email::
-       Show the author email instead of author name (Default: off).
+       Show the author email instead of the author name (Default: off).
        This can also be controlled via the `blame.showEmail` config
        option.
 
@@ -100,7 +100,7 @@ When neither `--porcelain` nor `--incremental` option is specified,
 `git blame` will output annotation for each line with:
 
 - abbreviated object name for the commit the line came from;
-- author ident (by default author name and date, unless `-s` or `-e`
+- author ident (by default the author name and date, unless `-s` or `-e`
   is specified); and
 - line number
 
@@ -128,7 +128,7 @@ at least once for each commit:
 - the filename in the commit that the line is attributed to.
 - the first line of the commit log message ("summary").
 
-The contents of the actual line is output after the above
+The contents of the actual line are output after the above
 header, prefixed by a TAB. This is to allow adding more
 header elements later.
 
@@ -170,7 +170,7 @@ which limits the annotation to the body of the `hello` subroutine.
 
 When you are not interested in changes older than version
 v2.6.18, or changes older than 3 weeks, you can use revision
-range specifiers  similar to 'git rev-list':
+range specifiers similar to 'git rev-list':
 
        git blame v2.6.18.. -- foo
        git blame --since=3.weeks -- foo
index 12c5f84e3bef5c83c80edec9850315dd334beffa..0b08442932354fcdcbb321f0c45b80a3a5bc4fdf 100644 (file)
@@ -116,13 +116,17 @@ OPTIONS
 
 -f::
 --force::
-       Reset <branchname> to <startpoint>, even if <branchname> exists
+       Reset <branchname> to <start-point>, even if <branchname> 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
+in another worktree linked to the same repository.
 
 -m::
 --move::
@@ -152,6 +156,10 @@ OPTIONS
 --ignore-case::
        Sorting and filtering branches are case insensitive.
 
+--omit-empty::
+       Do not print a newline after formatted refs where the format expands
+       to the empty string.
+
 --column[=<options>]::
 --no-column::
        Display branch listing in columns. See configuration variable
@@ -304,7 +312,8 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
        option is omitted, the current HEAD will be used instead.
 
 <oldbranch>::
-       The name of an existing branch to rename.
+       The name of an existing branch.  If this option is omitted,
+       the name of the current branch will be used instead.
 
 <newbranch>::
        The new name for an existing branch. The same restrictions as for
@@ -316,7 +325,7 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
        multiple times, in which case the last key becomes the primary
        key. The keys supported are the same as those in `git
        for-each-ref`. Sort order defaults to the value configured for the
-       `branch.sort` variable if exists, or to sorting based on the
+       `branch.sort` variable if it exists, or to sorting based on the
        full refname (including `refs/...` prefix). This lists
        detached HEAD (if present) first, then local branches and
        finally remote-tracking branches. See linkgit:git-config[1].
index eca726e57911af2cc1f0643cafdcf887c20bfa32..392d9eb6aec6b0d43930c93a322f415dc75b4c5c 100644 (file)
@@ -13,10 +13,11 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Captures information about the user's machine, Git client, and repository state,
-as well as a form requesting information about the behavior the user observed,
-into a single text file which the user can then share, for example to the Git
-mailing list, in order to report an observed bug.
+Collects information about the user's machine, Git client, and repository
+state, in addition to a form requesting information about the behavior the
+user observed, and stores it in a single text file which the user can then
+share, for example to the Git mailing list, in order to report an observed
+bug.
 
 The following information is requested from the user:
 
index 18a022b4b40c345040c23e83bed1fd1983bd725e..3ab42a19cae4aca0e6a3a83db92d25a4d88bf927 100644 (file)
@@ -9,7 +9,7 @@ git-bundle - Move objects and refs by archive
 SYNOPSIS
 --------
 [verse]
-'git bundle' create [-q | --quiet | --progress | --all-progress] [--all-progress-implied]
+'git bundle' create [-q | --quiet | --progress]
                    [--version=<version>] <file> <git-rev-list-args>
 'git bundle' verify [-q | --quiet] <file>
 'git bundle' list-heads <file> [<refname>...]
@@ -66,7 +66,7 @@ create [options] <file> <git-rev-list-args>::
        Used to create a bundle named 'file'.  This requires the
        '<git-rev-list-args>' arguments to define the bundle contents.
        'options' contains the options specific to the 'git bundle create'
-       subcommand.
+       subcommand. If 'file' is `-`, the bundle is written to stdout.
 
 verify <file>::
        Used to check that a bundle file is valid and will apply
@@ -77,12 +77,13 @@ verify <file>::
        Finally, information about additional capabilities, such as "object
        filter", is printed. See "Capabilities" in linkgit:gitformat-bundle[5]
        for more information. The exit code is zero for success, but will
-       be nonzero if the bundle file is invalid.
+       be nonzero if the bundle file is invalid. If 'file' is `-`, the
+       bundle is read from stdin.
 
 list-heads <file>::
        Lists the references defined in the bundle.  If followed by a
        list of references, only references matching those given are
-       printed out.
+       printed out. If 'file' is `-`, the bundle is read from stdin.
 
 unbundle <file>::
        Passes the objects in the bundle to 'git index-pack'
@@ -90,6 +91,7 @@ unbundle <file>::
        defined references. If a list of references is given, only
        references matching those in the list are printed. This command is
        really plumbing, intended to be called only by 'git fetch'.
+       If 'file' is `-`, the bundle is read from stdin.
 
 <git-rev-list-args>::
        A list of arguments, acceptable to 'git rev-parse' and
@@ -115,22 +117,6 @@ unbundle <file>::
        is specified. This flag forces progress status even if
        the standard error stream is not directed to a terminal.
 
---all-progress::
-       When --stdout is specified then progress report is
-       displayed during the object count and compression phases
-       but inhibited during the write-out phase. The reason is
-       that in some cases the output stream is directly linked
-       to another command which may wish to display progress
-       status of its own as it processes incoming pack data.
-       This flag is like --progress except that it forces progress
-       report for the write-out phase as well even if --stdout is
-       used.
-
---all-progress-implied::
-       This is used to imply --all-progress whenever progress display
-       is activated.  Unlike --all-progress this flag doesn't actually
-       force any progress display by itself.
-
 --version=<version>::
        Specify the bundle version.  Version 2 is the older format and can only be
        used with SHA-1 repositories; the newer version 3 contains capabilities that
index ec30b5c5743fd64ea9d42f4f451dc6ce0627d80a..bd95a6c10a7d0b1ee16b619c4b546c8d9c0b0e13 100644 (file)
@@ -3,8 +3,7 @@ git-cat-file(1)
 
 NAME
 ----
-git-cat-file - Provide content or type and size information for repository objects
-
+git-cat-file - Provide contents or details of repository objects
 
 SYNOPSIS
 --------
@@ -12,25 +11,24 @@ SYNOPSIS
 'git cat-file' <type> <object>
 'git cat-file' (-e | -p) <object>
 'git cat-file' (-t | -s) [--allow-unknown-type] <object>
-'git cat-file' (--batch | --batch-check | --batch-command) [--batch-all-objects]
-            [--buffer] [--follow-symlinks] [--unordered]
-            [--textconv | --filters] [-z]
 'git cat-file' (--textconv | --filters)
             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]
+'git cat-file' (--batch | --batch-check | --batch-command) [--batch-all-objects]
+            [--buffer] [--follow-symlinks] [--unordered]
+            [--textconv | --filters] [-Z]
 
 DESCRIPTION
 -----------
-In its first form, the command provides the content or the type of an object in
-the repository. The type is required unless `-t` or `-p` is used to find the
-object type, or `-s` is used to find the object size, or `--textconv` or
-`--filters` is used (which imply type "blob").
-
-In the second form, a list of objects (separated by linefeeds) is provided on
-stdin, and the SHA-1, type, and size of each object is printed on stdout. The
-output format can be overridden using the optional `<format>` argument. If
-either `--textconv` or `--filters` was specified, the input is expected to
-list the object names followed by the path name, separated by a single
-whitespace, so that the appropriate drivers can be determined.
+Output the contents or other properties such as size, type or delta
+information of one or more objects.
+
+This command can operate in two modes, depending on whether an option
+from the `--batch` family is specified.
+
+In non-batch mode, the command provides information on an object
+named on the command line.
+
+In batch mode, arguments are read from standard input.
 
 OPTIONS
 -------
@@ -45,12 +43,14 @@ OPTIONS
 
 -s::
        Instead of the content, show the object size identified by
-       `<object>`.
+       `<object>`. If used with `--use-mailmap` option, will show
+       the size of updated object after replacing idents using the
+       mailmap mechanism.
 
 -e::
        Exit with zero status if `<object>` exists and is a valid
-       object. If `<object>` is of an invalid format exit with non-zero and
-       emits an error on stderr.
+       object. If `<object>` is of an invalid format, exit with non-zero
+       status and emit an error on stderr.
 
 -p::
        Pretty-print the contents of `<object>` based on its type.
@@ -89,26 +89,54 @@ OPTIONS
 --batch::
 --batch=<format>::
        Print object information and contents for each object provided
-       on stdin.  May not be combined with any other options or arguments
-       except `--textconv` or `--filters`, in which case the input lines
-       also need to specify the path, separated by whitespace.  See the
-       section `BATCH OUTPUT` below for details.
+       on stdin. May not be combined with any other options or arguments
+       except `--textconv`, `--filters`, or `--use-mailmap`.
++
+--
+       * When used with `--textconv` or `--filters`, the input lines
+         must specify the path, separated by whitespace. See the section
+         `BATCH OUTPUT` below for details.
+
+       * When used with `--use-mailmap`, for commit and tag objects, the
+         contents part of the output shows the identities replaced using the
+         mailmap mechanism, while the information part of the output shows
+         the size of the object as if it actually recorded the replacement
+         identities.
+--
 
 --batch-check::
 --batch-check=<format>::
-       Print object information for each object provided on stdin.  May
-       not be combined with any other options or arguments except
-       `--textconv` or `--filters`, in which case the input lines also
-       need to specify the path, separated by whitespace.  See the
-       section `BATCH OUTPUT` below for details.
+       Print object information for each object provided on stdin. May not be
+       combined with any other options or arguments except `--textconv`, `--filters`
+       or `--use-mailmap`.
++
+--
+       * When used with `--textconv` or `--filters`, the input lines must
+        specify the path, separated by whitespace. See the section
+        `BATCH OUTPUT` below for details.
+
+       * When used with `--use-mailmap`, for commit and tag objects, the
+         printed object information shows the size of the object as if the
+         identities recorded in it were replaced by the mailmap mechanism.
+--
 
 --batch-command::
 --batch-command=<format>::
        Enter a command mode that reads commands and arguments from stdin. May
-       only be combined with `--buffer`, `--textconv` or `--filters`. In the
-       case of `--textconv` or `--filters`, the input lines also need to specify
-       the path, separated by whitespace. See the section `BATCH OUTPUT` below
-       for details.
+       only be combined with `--buffer`, `--textconv`, `--use-mailmap` or
+       `--filters`.
++
+--
+       * When used with `--textconv` or `--filters`, the input lines must
+         specify the path, separated by whitespace. See the section
+         `BATCH OUTPUT` below for details.
+
+       * When used with `--use-mailmap`, for commit and tag objects, the
+         `contents` command shows the identities replaced using the
+         mailmap mechanism, while the `info` command shows the size
+         of the object as if it actually recorded the replacement
+         identities.
+--
 +
 `--batch-command` recognizes the following commands:
 +
@@ -213,10 +241,16 @@ respectively print:
        /etc/passwd
 --
 
+-Z::
+       Only meaningful with `--batch`, `--batch-check`, or
+       `--batch-command`; input and output is NUL-delimited instead of
+       newline-delimited.
+
 -z::
        Only meaningful with `--batch`, `--batch-check`, or
        `--batch-command`; input is NUL-delimited instead of
-       newline-delimited.
+       newline-delimited. This option is deprecated in favor of
+       `-Z` as the output can otherwise be ambiguous.
 
 
 OUTPUT
@@ -354,6 +388,11 @@ notdir SP <size> LF
 is printed when, during symlink resolution, a file is used as a
 directory name.
 
+Alternatively, when `-Z` is passed, the line feeds in any of the above examples
+are replaced with NUL terminators. This ensures that output will be parsable if
+the output itself would contain a linefeed and is thus recommended for
+scripting purposes.
+
 CAVEATS
 -------
 
index 84f41a8e82590f54bca103a067a95348093844b5..cb5a6c8f335e128408e9b6b54cbb4037751e7932 100644 (file)
@@ -9,8 +9,8 @@ git-check-attr - Display gitattributes information
 SYNOPSIS
 --------
 [verse]
-'git check-attr' [-a | --all | <attr>...] [--] <pathname>...
-'git check-attr' --stdin [-z] [-a | --all | <attr>...]
+'git check-attr' [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>...
+'git check-attr' --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]
 
 DESCRIPTION
 -----------
@@ -29,13 +29,18 @@ OPTIONS
 
 --stdin::
        Read pathnames from the standard input, one per line,
-       instead of from the command-line.
+       instead of from the command line.
 
 -z::
        The output format is modified to be machine-parsable.
        If `--stdin` is also given, input paths are separated
        with a NUL character instead of a linefeed character.
 
+--source=<tree-ish>::
+       Check attributes against the specified tree-ish. It is common to
+       specify the source tree by naming a commit, branch, or tag associated
+       with it.
+
 \--::
        Interpret all preceding arguments as attributes and all following
        arguments as path names.
@@ -55,7 +60,7 @@ unless `-z` is in effect, in which case NUL is used as delimiter:
 
 
 <path> is the path of a file being queried, <attribute> is an attribute
-being queried and <info> can be either:
+being queried, and <info> can be either:
 
 'unspecified';; when the attribute is not defined for the path.
 'unset';;      when the attribute is defined as false.
index 2892799e32f9855bba3b5244e7051d11618fa846..3e3b4e344629d951e7318dace83f38d5752fc76c 100644 (file)
@@ -50,7 +50,7 @@ linkgit:gitignore[5].
        with a NUL character instead of a linefeed character.
 
 -n, --non-matching::
-       Show given paths which don't match any pattern.  This only
+       Show given paths which don't match any pattern.  This only
        makes sense when `--verbose` is enabled, otherwise it would
        not be possible to distinguish between paths which match a
        pattern and those which don't.
index ee6a4144fbef1aebf422c2e393b6d38c8fb4fb60..2aacfd18088d6506bfd8aa3ca7380888e8dbcffa 100644 (file)
@@ -48,7 +48,7 @@ Git imposes the following rules on how references are named:
 
 . They cannot begin or end with a slash `/` or contain multiple
   consecutive slashes (see the `--normalize` option below for an
-  exception to this rule)
+  exception to this rule).
 
 . They cannot end with a dot `.`.
 
@@ -85,7 +85,7 @@ The rule `git check-ref-format --branch $name` implements
 may be stricter than what `git check-ref-format refs/heads/$name`
 says (e.g. a dash may appear at the beginning of a ref component,
 but it is explicitly forbidden at the beginning of a branch name).
-When run with `--branch` option in a repository, the input is first
+When run with the `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
 was checked out using "git switch" or "git checkout" operation.
index 01dbd5cbf540ea96de2a1fd9b9ce0580077c29fa..faf8d6ca36fb7c3bfd7a8e880efe284746cdc94d 100644 (file)
@@ -18,7 +18,7 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Will copy all files listed from the index to the working directory
+Copies all listed files from the index to the working directory
 (not overwriting existing files).
 
 OPTIONS
@@ -53,11 +53,11 @@ OPTIONS
 
 --stage=<number>|all::
        Instead of checking out unmerged entries, copy out the
-       files from named stage.  <number> must be between 1 and 3.
+       files from the named stage.  <number> must be between 1 and 3.
        Note: --stage=all automatically implies --temp.
 
 --temp::
-       Instead of copying the files to the working directory
+       Instead of copying the files to the working directory,
        write the content to temporary files.  The temporary name
        associations will be written to stdout.
 
@@ -66,8 +66,8 @@ OPTIONS
        set.
 
 --stdin::
-       Instead of taking list of paths from the command line,
-       read list of paths from the standard input.  Paths are
+       Instead of taking list of paths from the command line,
+       read the list of paths from the standard input.  Paths are
        separated by LF (i.e. one path per line) by default.
 
 -z::
index 4cb9d555b4b436ae6aef8e14c1729599da0d74e8..8bdfa54ab09b4c4e8e4cb61767527e2ec06900b2 100644 (file)
@@ -12,8 +12,10 @@ SYNOPSIS
 'git checkout' [-q] [-f] [-m] --detach [<branch>]
 'git checkout' [-q] [-f] [-m] [--detach] <commit>
 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new-branch>] [<start-point>]
-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...
-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]
+'git checkout' [-f] <tree-ish> [--] <pathspec>...
+'git checkout' [-f] <tree-ish> --pathspec-from-file=<file> [--pathspec-file-nul]
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [--] <pathspec>...
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] --pathspec-from-file=<file> [--pathspec-file-nul]
 'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]
 
 DESCRIPTION
@@ -41,7 +43,7 @@ $ git checkout -b <branch> --track <remote>/<branch>
 You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
-if exists, for the current branch.
+if it exists, for the current branch.
 
 'git checkout' -b|-B <new-branch> [<start-point>]::
 
@@ -61,7 +63,9 @@ $ git checkout <branch>
 ------------
 +
 that is to say, the branch is not reset/created unless "git checkout" is
-successful.
+successful (e.g., when the branch is in use in another worktree, not
+just the current branch stays the same, but the branch is not reset to
+the start-point, either).
 
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
@@ -146,14 +150,16 @@ on your side branch as `theirs` (i.e. "one contributor's work on top
 of it").
 
 -b <new-branch>::
-       Create a new branch named `<new-branch>` and start it at
-       `<start-point>`; see linkgit:git-branch[1] for details.
+       Create a new branch named `<new-branch>`, start it at
+       `<start-point>`, and check the resulting branch out;
+       see linkgit:git-branch[1] for details.
 
 -B <new-branch>::
-       Creates the branch `<new-branch>` and start it at `<start-point>`;
-       if it already exists, then reset it to `<start-point>`. This is
-       equivalent to running "git branch" with "-f"; see
-       linkgit:git-branch[1] for details.
+       Creates the branch `<new-branch>`, start it at `<start-point>`;
+       if it already exists, then reset it to `<start-point>`. And then
+       check the resulting branch out.  This is equivalent to running
+       "git branch" with "-f" followed by "git checkout" of that branch;
+       see linkgit:git-branch[1] for details.
 
 -t::
 --track[=(direct|inherit)]::
@@ -211,7 +217,7 @@ variable.
        below for details.
 
 --orphan <new-branch>::
-       Create a new 'orphan' branch, named `<new-branch>`, started from
+       Create a new unborn branch, named `<new-branch>`, started from
        `<start-point>` and switch to it.  The first commit made on this
        new branch will have no parents and it will be the root of a new
        history totally disconnected from all the other branches and
@@ -258,7 +264,8 @@ and mark the resolved paths with `git add` (or `git rm` if the merge
 should result in deletion of the path).
 +
 When checking out paths from the index, this option lets you recreate
-the conflicted merge in the specified paths.
+the conflicted merge in the specified paths.  This option cannot be
+used when checking out paths from a tree-ish.
 +
 When switching branches with `--merge`, staged changes may be lost.
 
@@ -477,18 +484,15 @@ before that happens. If we have not yet moved away from commit `f`,
 any of these will create a reference to it:
 
 ------------
-$ git checkout -b foo   <1>
-$ git branch foo        <2>
-$ git tag foo           <3>
+$ git checkout -b foo  # or "git switch -c foo"  <1>
+$ git branch foo                                 <2>
+$ git tag foo                                    <3>
 ------------
-
 <1> creates a new branch `foo`, which refers to commit `f`, and then
     updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
     be in detached `HEAD` state after this command.
-
 <2> similarly creates a new branch `foo`, which refers to commit `f`,
     but leaves `HEAD` detached.
-
 <3> creates a new tag `foo`, which refers to commit `f`,
     leaving `HEAD` detached.
 
@@ -517,84 +521,89 @@ to checkout these paths out of the index.
 EXAMPLES
 --------
 
-. The following sequence checks out the `master` branch, reverts
-  the `Makefile` to two revisions back, deletes `hello.c` by
-  mistake, and gets it back from the index.
-+
+=== 1. Paths
+
+The following sequence checks out the `master` branch, reverts
+the `Makefile` to two revisions back, deletes `hello.c` by
+mistake, and gets it back from the index.
+
 ------------
 $ git checkout master             <1>
 $ git checkout master~2 Makefile  <2>
 $ rm -f hello.c
 $ git checkout hello.c            <3>
 ------------
-+
 <1> switch branch
 <2> take a file out of another commit
 <3> restore `hello.c` from the index
-+
+
 If you want to check out _all_ C source files out of the index,
 you can say
-+
+
 ------------
 $ git checkout -- '*.c'
 ------------
-+
+
 Note the quotes around `*.c`.  The file `hello.c` will also be
 checked out, even though it is no longer in the working tree,
 because the file globbing is used to match entries in the index
 (not in the working tree by the shell).
-+
+
 If you have an unfortunate branch that is named `hello.c`, this
 step would be confused as an instruction to switch to that branch.
 You should instead write:
-+
+
 ------------
 $ git checkout -- hello.c
 ------------
 
-. After working in the wrong branch, switching to the correct
-  branch would be done using:
-+
+=== 2. Merge
+
+After working in the wrong branch, switching to the correct
+branch would be done using:
+
 ------------
 $ git checkout mytopic
 ------------
-+
+
 However, your "wrong" branch and correct `mytopic` branch may
 differ in files that you have modified locally, in which case
 the above checkout would fail like this:
-+
+
 ------------
 $ git checkout mytopic
 error: You have local changes to 'frotz'; not switching branches.
 ------------
-+
+
 You can give the `-m` flag to the command, which would try a
 three-way merge:
-+
+
 ------------
 $ git checkout -m mytopic
 Auto-merging frotz
 ------------
-+
+
 After this three-way merge, the local modifications are _not_
 registered in your index file, so `git diff` would show you what
 changes you made since the tip of the new branch.
 
-. When a merge conflict happens during switching branches with
-  the `-m` option, you would see something like this:
-+
+=== 3. Merge conflict
+
+When a merge conflict happens during switching branches with
+the `-m` option, you would see something like this:
+
 ------------
 $ git checkout -m mytopic
 Auto-merging frotz
 ERROR: Merge conflict in frotz
 fatal: merge program failed
 ------------
-+
+
 At this point, `git diff` shows the changes cleanly merged as in
 the previous example, as well as the changes in the conflicted
 files.  Edit and resolve the conflict and mark it resolved with
 `git add` as usual:
-+
+
 ------------
 $ edit frotz
 $ git add frotz
index 1e8ac9df60274067dcf57d3f9a82ba6f270ea7d9..fdcad3d2006c8ad59333f38a3161e2060e75a8ee 100644 (file)
@@ -219,7 +219,7 @@ again, this time exercising more care about matching up context lines.
 ------------
 $ git cherry-pick topic^             <1>
 $ git diff                           <2>
-$ git reset --merge ORIG_HEAD        <3>
+$ git cherry-pick --abort            <3>
 $ git cherry-pick -Xpatience topic^  <4>
 ------------
 <1> apply the change that would be shown by `git show topic^`.
index 91742633fa878922d23bc78e1aeec9a638994d38..69331e3f05a13eed6ffb084e292f8452b241f13c 100644 (file)
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -20,16 +20,16 @@ Normally, only files unknown to Git are removed, but if the `-x`
 option is specified, ignored files are also removed. This can, for
 example, be useful to remove all build products.
 
-If any optional `<path>...` arguments are given, only those paths
-are affected.
+If any optional `<pathspec>...` arguments are given, only those paths
+that match the pathspec are affected.
 
 OPTIONS
 -------
 -d::
-       Normally, when no <path> is specified, git clean will not
+       Normally, when no <pathspec> is specified, git clean will not
        recurse into untracked directories to avoid removing too much.
        Specify -d to have it recurse into such directories as well.
-       If any paths are specified, -d is irrelevant; all untracked
+       If a <pathspec> is specified, -d is irrelevant; all untracked
        files matching the specified paths (with exceptions for nested
        git directories mentioned under `--force`) will be removed.
 
@@ -103,7 +103,7 @@ filter by pattern::
    This shows the files and directories to be deleted and issues an
    "Input ignore patterns>>" prompt. You can input space-separated
    patterns to exclude files and directories from deletion.
-   E.g. "*.c *.h" will excludes files end with ".c" and ".h" from
+   E.g. "*.c *.h" will exclude files ending with ".c" and ".h" from
    deletion. When you are satisfied with the filtered result, press
    ENTER (empty) back to the main menu.
 
@@ -127,7 +127,7 @@ ask each::
 
 quit::
 
-  This lets you quit without do cleaning.
+  This lets you quit without doing any cleaning.
 
 help::
 
index d6434d262d6e2945ffe98f397ab8500a53990750..6e43eb9c205371548655c5abae5e59bb963c959a 100644 (file)
@@ -58,6 +58,11 @@ never use the local optimizations).  Specifying `--no-local` will
 override the default when `/path/to/repo` is given, using the regular
 Git transport instead.
 +
+If the repository's `$GIT_DIR/objects` has symbolic links or is a
+symbolic link, the clone will fail. This is a security measure to
+prevent the unintentional copying of files by dereferencing the symbolic
+links.
++
 *NOTE*: this operation can race with concurrent modification to the
 source repository, similar to running `cp -r src dst` while modifying
 `src`.
@@ -306,6 +311,12 @@ or `--mirror` is given)
        The result is Git repository can be separated from working
        tree.
 
+--ref-format=<ref-format::
+
+Specify the given ref storage format for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
 -j <n>::
 --jobs <n>::
        The number of submodules fetched at the same time.
index 36fe56c2c7192906add8bc04d43c1c5cca4aa372..c8dbceba014639524ccf2b7b1897190a9fc3c0a0 100644 (file)
@@ -10,7 +10,10 @@ SYNOPSIS
 --------
 [verse]
 'git commit-graph verify' [--object-dir <dir>] [--shallow] [--[no-]progress]
-'git commit-graph write' <options> [--object-dir <dir>] [--[no-]progress]
+'git commit-graph write' [--object-dir <dir>] [--append]
+                       [--split[=<strategy>]] [--reachable | --stdin-packs | --stdin-commits]
+                       [--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress]
+                       <split options>
 
 
 DESCRIPTION
index 225c6c9f2e5f8f513e1e56ae77bc45d8c2097da4..a6cef5d82038771c5f7e91f788f14de6394de158 100644 (file)
@@ -541,7 +541,7 @@ DISCUSSION
 ----------
 
 Though not required, it's a good idea to begin the commit message
-with a single short (less than 50 character) line summarizing the
+with a single short (no more than 50 characters) line summarizing the
 change, followed by a blank line and then a more thorough description.
 The text up to the first blank line in a commit message is treated
 as the commit title, and that title is used throughout Git.
index 7a2bcb2f6cb6f94b677d47de49ae8c6d0b98f2b8..b1caac887ae2dc5ce683ab4c6d91eed849403afe 100644 (file)
@@ -201,7 +201,7 @@ Valid `<type>`'s include:
   1073741824 upon input.
 - 'bool-or-int': canonicalize according to either 'bool' or 'int', as described
   above.
-- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and
+- 'path': canonicalize by expanding a leading `~` to the value of `$HOME` and
   `~user` to the home directory for the specified user. This specifier has no
   effect when setting the value (but you can use `git config section.variable
   ~/` from the command line to let your shell do the expansion.)
index cb9b4d2e460adaa46ee863c37283840c2bdb9333..97f9f1261010f5473b6748dc3dc0a2e0a93fd4f5 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-This counts the number of unpacked object files and disk space consumed by
+Counts the number of unpacked object files and disk space consumed by
 them, to help you decide when it is a good time to repack.
 
 
@@ -20,7 +20,7 @@ OPTIONS
 -------
 -v::
 --verbose::
-       Report in more detail:
+       Provide more detailed reports:
 +
 count: the number of loose objects
 +
@@ -33,7 +33,7 @@ size-pack: disk space consumed by the packs, in KiB (unless -H is specified)
 prune-packable: the number of loose objects that are also present in
 the packs. These objects could be pruned using `git prune-packed`.
 +
-garbage: the number of files in object database that are neither valid loose
+garbage: the number of files in the object database that are neither valid loose
 objects nor valid packs
 +
 size-garbage: disk space consumed by garbage files, in KiB (unless -H is
index 01e1c214dd82e1564b0459c4d6fe5b9f98ba1082..650a15a7edfa3f066c8adf2204fa3700959030d0 100644 (file)
@@ -8,7 +8,7 @@ git-credential-cache--daemon - Temporarily store user credentials in memory
 SYNOPSIS
 --------
 [verse]
-'git credential-cache{litdd}daemon' [--debug] <socket>
+'git credential-cache{litdd}daemon' [--debug] <socket-path>
 
 DESCRIPTION
 -----------
@@ -16,7 +16,7 @@ DESCRIPTION
 NOTE: You probably don't want to invoke this command yourself; it is
 started automatically when you use linkgit:git-credential-cache[1].
 
-This command listens on the Unix domain socket specified by `<socket>`
+This command listens on the Unix domain socket specified by `<socket-path>`
 for `git-credential-cache` clients. Clients may store and retrieve
 credentials. Each credential is held for a timeout specified by the
 client; once no credentials are held, the daemon exits.
index 0216c18ef80c9a9d32095ec3969091aff1c20344..487cc557a87feaa688d5e333dcecd2f83eb1ec48 100644 (file)
@@ -14,10 +14,13 @@ git config credential.helper 'cache [<options>]'
 DESCRIPTION
 -----------
 
-This command caches credentials in memory for use by future Git
-programs. The stored credentials never touch the disk, and are forgotten
-after a configurable timeout.  The cache is accessible over a Unix
-domain socket, restricted to the current user by filesystem permissions.
+This command caches credentials for use by future Git programs.
+The stored credentials are kept in memory of the cache-daemon
+process (instead of being written to a file) and are forgotten after a
+configurable timeout. Credentials are forgotten sooner if the
+cache-daemon dies, for example if the system restarts. The cache
+is accessible over a Unix domain socket, restricted to the current
+user by filesystem permissions.
 
 You probably don't want to invoke this command directly; it is meant to
 be used as a credential helper by other parts of Git. See
@@ -69,10 +72,10 @@ $ git push http://example.com/repo.git
 ------------------------------------
 
 You can provide options via the credential.helper configuration
-variable (this example drops the cache time to 5 minutes):
+variable (this example increases the cache time to 1 hour):
 
 -------------------------------------------------------
-$ git config credential.helper 'cache --timeout=300'
+$ git config credential.helper 'cache --timeout=3600'
 -------------------------------------------------------
 
 GIT
index 76b0798856336fe739e8325ed1b5716abb099bfe..71864a872642e85734d86f1c52479e7a22b1dfe0 100644 (file)
@@ -33,7 +33,7 @@ OPTIONS
 
        Use `<path>` to lookup and store credentials. The file will have its
        filesystem permissions set to prevent other users on the system
-       from reading it, but will not be encrypted or otherwise
+       from reading it, but it will not be encrypted or otherwise
        protected. If not specified, credentials will be searched for from
        `~/.git-credentials` and `$XDG_CONFIG_HOME/git/credentials`, and
        credentials will be written to `~/.git-credentials` if it exists, or
index f18673017f577f523b1190ce87ce2d448e4dbd70..918a0aa42b24156ce814ba61cd2006928807ee08 100644 (file)
@@ -39,7 +39,7 @@ for later use.
 
 If the action is `reject`, git-credential will send the description to
 any configured credential helpers, which may erase any stored
-credential matching the description.
+credentials matching the description.
 
 If the action is `approve` or `reject`, no output should be emitted.
 
@@ -94,7 +94,7 @@ unlocked) before it returned `password=secr3t`.
      that `git credential` will ask for a new password in its next
      invocation. In either case, `git credential` should be fed with
      the credential description obtained from step (2) (which also
-     contain the ones provided in step (1)).
+     contains the fields provided in step (1)).
 
 [[IOFMT]]
 INPUT/OUTPUT FORMAT
@@ -113,7 +113,13 @@ separated by an `=` (equals) sign, followed by a newline.
 The key may contain any bytes except `=`, newline, or NUL. The value may
 contain any bytes except newline or NUL.
 
-In both cases, all bytes are treated as-is (i.e., there is no quoting,
+Attributes with keys that end with C-style array brackets `[]` can have
+multiple values. Each instance of a multi-valued attribute forms an
+ordered list of values - the order of the repeated attributes defines
+the order of the values. An empty multi-valued attribute (`key[]=\n`)
+acts to clear any previous entries and reset the list.
+
+In all cases, all bytes are treated as-is (i.e., there is no quoting,
 and one cannot transmit a value with newline or NUL in it). The list of
 attributes is terminated by a blank line or end-of-file.
 
@@ -144,6 +150,18 @@ Git understands the following attributes:
 
        The credential's password, if we are asking it to be stored.
 
+`password_expiry_utc`::
+
+       Generated passwords such as an OAuth access token may have an expiry date.
+       When reading credentials from helpers, `git credential fill` ignores expired
+       passwords. Represented as Unix time UTC, seconds since 1970.
+
+`oauth_refresh_token`::
+
+       An OAuth refresh token may accompany a password that is an OAuth access
+       token. Helpers must treat this attribute as confidential like the password
+       attribute. Git itself has no special behaviour for this attribute.
+
 `url`::
 
        When this special attribute is read by `git credential`, the
@@ -160,6 +178,19 @@ empty string.
 Components which are missing from the URL (e.g., there is no
 username in the example above) will be left unset.
 
+`wwwauth[]`::
+
+       When an HTTP response is received by Git that includes one or more
+       'WWW-Authenticate' authentication headers, these will be passed by Git
+       to credential helpers.
++
+Each 'WWW-Authenticate' header value is passed as a multi-valued
+attribute 'wwwauth[]', where the order of the attributes is the same as
+they appear in the HTTP response. This attribute is 'one-way' from Git
+to pass additional information to credential helpers.
+
+Unrecognised attributes are silently discarded.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index b3f27671a0c6eb20bfd2bce88bcc74079691fe70..90fdc2551a349939a0cbf9ab12d792ab20f0b765 100644 (file)
@@ -22,7 +22,7 @@ DESCRIPTION
 deprecated; it does not work with cvsps version 3 and later.  If you are
 performing a one-shot import of a CVS repository consider using
 http://cvs2svn.tigris.org/cvs2git.html[cvs2git] or
-http://www.catb.org/esr/cvs-fast-export/[cvs-fast-export].
+https://gitlab.com/esr/cvs-fast-export[cvs-fast-export].
 
 Imports a CVS repository into Git. It will either create a new
 repository, or incrementally import into an existing one.
@@ -221,7 +221,7 @@ Problems related to tags:
 If you suspect that any of these issues may apply to the repository you
 want to import, consider using cvs2git:
 
-* cvs2git (part of cvs2svn), `http://subversion.apache.org/`
+* cvs2git (part of cvs2svn), `https://subversion.apache.org/`
 
 GIT
 ---
index 53f111bc0acaf08a0a208501a32973cd420d0b35..cf4a5a283ecd68f49fb3051b2f27007074eac4ef 100644 (file)
@@ -118,7 +118,7 @@ for example:
    myuser:$5$.NqmNH1vwfzGpV8B$znZIcumu1tNLATgV2l6e1/mY8RzhUDHMOaVOeL1cxV3
 ------
 You can use the 'htpasswd' facility that comes with Apache to make these
-files, but only with the -d option (or -B if your system suports it).
+files, but only with the -d option (or -B if your system supports it).
 
 Preferably use the system specific utility that manages password hash
 creation in your platform (e.g. mkpasswd in Linux, encrypt in OpenBSD or
index 236df516c7313d702f9908229afa4d4030e154ab..e064f91c9e35899abd70f78a6f37158a0794f113 100644 (file)
@@ -138,7 +138,7 @@ otherwise `stderr`.
 --user-path::
 --user-path=<path>::
        Allow {tilde}user notation to be used in requests.  When
-       specified with no parameter, requests to
+       specified with no parameter, a request to
        git://host/{tilde}alice/foo is taken as a request to access
        'foo' repository in the home directory of user `alice`.
        If `--user-path=path` is specified, the same request is
index c6a79c2a0f29ccc1f87c331382b484ab9a1c5a62..08ff715709ccd1190cf9769c84a49322300a308e 100644 (file)
@@ -140,7 +140,7 @@ at the end.
 
 The number of additional commits is the number
 of commits which would be displayed by "git log v1.0.4..parent".
-The hash suffix is "-g" + an unambigous abbreviation for the tip commit
+The hash suffix is "-g" + an unambiguous abbreviation for the tip commit
 of parent (which was `2414721b194453f058079d897d13c4e377f92dc6`). The
 length of the abbreviation scales as the repository grows, using the
 approximate number of objects in the repository and a bit of math
@@ -203,7 +203,7 @@ BUGS
 
 Tree objects as well as tag objects not pointing at commits, cannot be described.
 When describing blobs, the lightweight tags pointing at blobs are ignored,
-but the blob is still described as <committ-ish>:<path> despite the lightweight
+but the blob is still described as <commit-ish>:<path> despite the lightweight
 tag being favorable.
 
 GIT
index bf1febb9ae72e72230c92d67ae719e7cf19039e3..bf78e3143138136f4e2c3998799a16a19f2dc3bb 100644 (file)
@@ -9,7 +9,7 @@ git-diff-files - Compares files in the working tree and the index
 SYNOPSIS
 --------
 [verse]
-'git diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common-diff-options>] [<path>...]
+'git diff-files' [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]
 
 DESCRIPTION
 -----------
@@ -26,7 +26,7 @@ include::diff-options.txt[]
 -2 --ours::
 -3 --theirs::
 -0::
-       Diff against the "base" version, "our branch" or "their
+       Diff against the "base" version, "our branch", or "their
        branch" respectively.  With these options, diffs for
        merged entries are not shown.
 +
@@ -37,12 +37,12 @@ omit diff output for unmerged entries and just show "Unmerged".
 -c::
 --cc::
        This compares stage 2 (our branch), stage 3 (their
-       branch) and the working tree file and outputs a combined
+       branch), and the working tree file and outputs a combined
        diff, similar to the way 'diff-tree' shows a merge
        commit with these flags.
 
 -q::
-       Remain silent even on nonexistent files
+       Remain silent even for nonexistent files
 
 
 include::diff-format.txt[]
index c30d8f0da8a28ff309185bf3878e3a462074d627..4de1d4c8f11e6065da2c60c0112b72f85af7daa3 100644 (file)
@@ -13,10 +13,10 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Compares the content and mode of the blobs found in a tree object
+Compare the content and mode of the blobs found in a tree object
 with the corresponding tracked files in the working tree, or with the
 corresponding paths in the index.  When <path> arguments are present,
-compares only paths matching those patterns.  Otherwise all tracked
+compare only paths matching those patterns.  Otherwise all tracked
 files are compared.
 
 OPTIONS
index 274d5eaba93dab2cb0374c64ea95934693c73ddf..143318c411a0761777d8a318450b4a71b69870d7 100644 (file)
@@ -15,7 +15,7 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Compares the content and mode of the blobs found via two tree objects.
+Compare the content and mode of blobs found via two tree objects.
 
 If there is only one <tree-ish> given, the commit is compared with its parents
 (see --stdin below).
@@ -34,10 +34,10 @@ include::diff-options.txt[]
        matching one of the provided pathspecs.
 
 -r::
-        recurse into sub-trees
+       Recurse into sub-trees.
 
 -t::
-       show tree entry itself as well as subtrees.  Implies -r.
+       Show tree entry itself as well as subtrees.  Implies -r.
 
 --root::
        When `--root` is specified the initial commit will be shown as a big
@@ -78,7 +78,7 @@ commits (but not trees).
        By default, 'git diff-tree --stdin' shows differences,
        either in machine-readable form (without `-p`) or in patch
        form (with `-p`).  This output can be suppressed.  It is
-       only useful with `-v` flag.
+       only useful with the `-v` flag.
 
 -v::
        This flag causes 'git diff-tree --stdin' to also show
@@ -104,10 +104,10 @@ include::pretty-options.txt[]
        This flag changes the way a merge commit patch is displayed,
        in a similar way to the `-c` option. It implies the `-c`
        and `-p` options and further compresses the patch output
-       by omitting uninteresting hunks whose the contents in the parents
+       by omitting uninteresting hunks whose contents in the parents
        have only two variants and the merge result picks one of them
        without modification.  When all hunks are uninteresting, the commit
-       itself and the commit log message is not shown, just like in any other
+       itself and the commit log message are not shown, just like in any other
        "empty diff" case.
 
 --combined-all-paths::
index 85ae6d6d08a2623a95830041ab076f1526dc6417..c065f023eca3fb1ce60c4c41d60d35c14b5c916b 100644 (file)
@@ -79,10 +79,10 @@ If --merge-base is given, use the merge base of the two commits for the
 
        This form is to view the results of a merge commit.  The first
        listed <commit> must be the merge itself; the remaining two or
-       more commits should be its parents.  A convenient way to produce
-       the desired set of revisions is to use the `^@` suffix.
-       For instance, if `master` names a merge commit, `git diff master
-       master^@` gives the same combined diff as `git show master`.
+       more commits should be its parents.  Convenient ways to produce
+       the desired set of revisions are to use the suffixes `^@` and
+       `^!`.  If A is a merge commit, then `git diff A A^@`,
+       `git diff A^!` and `git show A` all give the same combined diff.
 
 'git diff' [<options>] <commit>..<commit> [--] [<path>...]::
 
@@ -102,7 +102,11 @@ If --merge-base is given, use the merge base of the two commits for the
 Just in case you are doing something exotic, it should be
 noted that all of the <commit> in the above description, except
 in the `--merge-base` case and in the last two forms that use `..`
-notations, can be any <tree>.
+notations, can be any <tree>. A tree of interest is the one pointed to
+by the ref named `AUTO_MERGE`, which is written by the 'ort' merge
+strategy upon hitting merge conflicts (see linkgit:git-merge[1]).
+Comparing the working tree with `AUTO_MERGE` shows changes you've made
+so far to resolve textual conflicts (see the examples below).
 
 For a more complete list of ways to spell <commit>, see
 "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
@@ -152,6 +156,7 @@ Various ways to check your working tree::
 $ git diff            <1>
 $ git diff --cached   <2>
 $ git diff HEAD       <3>
+$ git diff AUTO_MERGE <4>
 ------------
 +
 <1> Changes in the working tree not yet staged for the next commit.
@@ -159,6 +164,8 @@ $ git diff HEAD       <3>
     would be committing if you run `git commit` without `-a` option.
 <3> Changes in the working tree since your last commit; what you
     would be committing if you run `git commit -a`
+<4> Changes in the working tree you've made to resolve textual
+    conflicts so far.
 
 Comparing with arbitrary commits::
 +
index 9d14c3c9f099aab8d225767ef6d308ecee2e6713..50cb080085e7708eb117e58fc42920c710452b31 100644 (file)
@@ -36,7 +36,7 @@ OPTIONS
 
 --rotate-to=<file>::
        Start showing the diff for the given path,
-       the paths before it will move to end and output.
+       the paths before it will move to the end and output.
 
 --skip-to=<file>::
        Start showing the diff for the given path, skipping all
@@ -78,7 +78,7 @@ with custom merge tool commands and has the same value as `$MERGED`.
        Print a list of diff tools that may be used with `--tool`.
 
 --[no-]symlinks::
-       'git difftool''s default behavior is create symlinks to the
+       'git difftool''s default behavior is to create symlinks to the
        working tree when run in `--dir-diff` mode and the right-hand
        side of the comparison yields the same content as the file in
        the working tree.
@@ -97,10 +97,12 @@ instead.  `--no-symlinks` is the default on Windows.
 --[no-]gui::
        When 'git-difftool' is invoked with the `-g` or `--gui` option
        the default diff tool will be read from the configured
-       `diff.guitool` variable instead of `diff.tool`. The `--no-gui`
-       option can be used to override this setting. If `diff.guitool`
-       is not set, we will fallback in the order of `merge.guitool`,
-       `diff.tool`, `merge.tool` until a tool is found.
+       `diff.guitool` variable instead of `diff.tool`. This may be
+       selected automatically using the configuration variable
+       `difftool.guiDefault`. The `--no-gui` option can be used to
+       override these settings. If `diff.guitool` is not set, we will
+       fallback in the order of `merge.guitool`, `diff.tool`,
+       `merge.tool` until a tool is found.
 
 --[no-]trust-exit-code::
        'git-difftool' invokes a diff tool individually on each file.
index 1978dbdc6add12c87fd73673816219968f29dedf..4643ddbe68fd0c0eeb541df356ca3fc2bd2ee9d4 100644 (file)
@@ -9,7 +9,7 @@ git-fast-export - Git data exporter
 SYNOPSIS
 --------
 [verse]
-'git fast-export [<options>]' | 'git fast-import'
+'git fast-export' [<options>] | 'git fast-import'
 
 DESCRIPTION
 -----------
index 8b5dd6add006d111688fc1193d77ebe6b45422a5..bd7b1e0a2eaf3ebf595475580d81d6771b8b4166 100644 (file)
@@ -622,7 +622,7 @@ in octal.  Git only supports the following modes:
 * `100755` or `755`: A normal, but executable, file.
 * `120000`: A symlink, the content of the file will be the link target.
 * `160000`: A gitlink, SHA-1 of the object refers to a commit in
-  another repository. Git links can only be specified by SHA or through
+  another repository. Git links can only be specified either by SHA or through
   a commit mark. They are used to implement submodules.
 * `040000`: A subdirectory.  Subdirectories can only be specified by
   SHA or through a tree mark set with `--import-marks`.
@@ -1353,7 +1353,7 @@ the marks back to the source repository, it is easy to verify the
 accuracy and completeness of the import by comparing each Git
 commit to the corresponding source revision.
 
-Coming from a system such as Perforce or Subversion this should be
+Coming from a system such as Perforce or Subversion, this should be
 quite simple, as the fast-import mark can also be the Perforce changeset
 number or the Subversion revision number.
 
index 46747d5f429164f817444cd502d8bff4dc5cbe4d..b3467664d30bde111af06ec0e915e6def2b1b4b2 100644 (file)
@@ -69,7 +69,7 @@ be in a separate packet, and the list must end with a flush packet.
 
 --upload-pack=<git-upload-pack>::
        Use this to specify the path to 'git-upload-pack' on the
-       remote side, if is not found on your $PATH.
+       remote side, if it is not found on your $PATH.
        Installations of sshd ignores the user's environment
        setup scripts for login shells (e.g. .bash_profile) and
        your privately installed git may not be found on the system
index 63d9569e16444237a7583b8c9dafe232eb1d2e42..f123139c581001d2bcbd94dcc187775e58e8fd6c 100644 (file)
@@ -204,6 +204,15 @@ representing the status of a single ref. Each line is of the form:
  <flag> <summary> <from> -> <to> [<reason>]
 -------------------------------
 
+When using `--porcelain`, the output format is intended to be
+machine-parseable. In contrast to the human-readable output formats it
+thus prints to standard output instead of standard error. Each line is
+of the form:
+
+-------------------------------
+<flag> <old-object-id> <new-object-id> <local-reference>
+-------------------------------
+
 The status of up-to-date refs is shown only if the --verbose option is
 used.
 
@@ -251,10 +260,10 @@ EXAMPLES
 $ git fetch origin
 ------------------------------------------------
 +
-The above command copies all branches from the remote refs/heads/
-namespace and stores them to the local refs/remotes/origin/ namespace,
-unless the branch.<name>.fetch option is used to specify a non-default
-refspec.
+The above command copies all branches from the remote `refs/heads/`
+namespace and stores them to the local `refs/remotes/origin/` namespace,
+unless the `remote.<repository>.fetch` option is used to specify a
+non-default refspec.
 
 * Using refspecs explicitly:
 +
index 6da899c62964275a97854c0b338f83f51db4bdfe..be9543f6840be3c6a477fb8537d014b0f7dfba7b 100644 (file)
@@ -9,10 +9,12 @@ SYNOPSIS
 --------
 [verse]
 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
-                  [(--sort=<key>)...] [--format=<format>] [<pattern>...]
+                  [(--sort=<key>)...] [--format=<format>]
+                  [ --stdin | <pattern>... ]
                   [--points-at=<object>]
                   [--merged[=<object>]] [--no-merged[=<object>]]
                   [--contains[=<object>]] [--no-contains[=<object>]]
+                  [--exclude=<pattern> ...]
 
 DESCRIPTION
 -----------
@@ -32,6 +34,10 @@ OPTIONS
        literally, in the latter case matching completely or from the
        beginning up to a slash.
 
+--stdin::
+       If `--stdin` is supplied, then the list of patterns is read from
+       standard input instead of from the argument list.
+
 --count=<count>::
        By default the command shows all refs that match
        `<pattern>`.  This option makes it stop after showing
@@ -45,17 +51,14 @@ OPTIONS
        key.
 
 --format=<format>::
-       A string that interpolates `%(fieldname)` from a ref being shown
-       and the object it points at.  If `fieldname`
-       is prefixed with an asterisk (`*`) and the ref points
-       at a tag object, use the value for the field in the object
-       which the tag object refers to (instead of the field in the tag object).
-       When unspecified, `<format>` defaults to
-       `%(objectname) SPC %(objecttype) TAB %(refname)`.
-       It also interpolates `%%` to `%`, and `%xx` where `xx`
-       are hex digits interpolates to character with hex code
-       `xx`; for example `%00` interpolates to `\0` (NUL),
-       `%09` to `\t` (TAB) and `%0a` to `\n` (LF).
+       A string that interpolates `%(fieldname)` from a ref being shown and
+       the object it points at. In addition, the string literal `%%`
+       renders as `%` and `%xx` - where `xx` are hex digits - renders as
+       the character with hex code `xx`. For example, `%00` interpolates to
+       `\0` (NUL), `%09` to `\t` (TAB), and `%0a` to `\n` (LF).
++
+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)
+TAB %(refname)`.
 
 --color[=<when>]::
        Respect any colors specified in the `--format` option. The
@@ -93,6 +96,15 @@ OPTIONS
 --ignore-case::
        Sorting and filtering refs are case insensitive.
 
+--omit-empty::
+       Do not print a newline after formatted refs where the format expands
+       to the empty string.
+
+--exclude=<pattern>::
+       If one or more patterns are given, only refs which do not match
+       any excluded pattern(s) are shown. Matching is done using the
+       same rules as `<pattern>` above.
+
 FIELD NAMES
 -----------
 
@@ -212,11 +224,66 @@ symref::
        `:lstrip` and `:rstrip` options in the same way as `refname`
        above.
 
+signature::
+       The GPG signature of a commit.
+
+signature:grade::
+       Show "G" for a good (valid) signature, "B" for a bad
+       signature, "U" for a good signature with unknown validity, "X"
+       for a good signature that has expired, "Y" for a good
+       signature made by an expired key, "R" for a good signature
+       made by a revoked key, "E" if the signature cannot be
+       checked (e.g. missing key) and "N" for no signature.
+
+signature:signer::
+       The signer of the GPG signature of a commit.
+
+signature:key::
+       The key of the GPG signature of a commit.
+
+signature:fingerprint::
+       The fingerprint of the GPG signature of a commit.
+
+signature:primarykeyfingerprint::
+       The primary key fingerprint of the GPG signature of a commit.
+
+signature:trustlevel::
+       The trust level of the GPG signature of a commit. Possible
+       outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`.
+
 worktreepath::
        The absolute path to the worktree in which the ref is checked
        out, if it is checked out in any linked worktree. Empty string
        otherwise.
 
+ahead-behind:<committish>::
+       Two integers, separated by a space, demonstrating the number of
+       commits ahead and behind, respectively, when comparing the output
+       ref to the `<committish>` specified in the format.
+
+describe[:options]::
+       A human-readable name, like linkgit:git-describe[1];
+       empty string for undescribable commits. The `describe` string may
+       be followed by a colon and one or more comma-separated options.
++
+--
+tags=<bool-value>;;
+       Instead of only considering annotated tags, consider
+       lightweight tags as well; see the corresponding option in
+       linkgit:git-describe[1] for details.
+abbrev=<number>;;
+       Use at least <number> hexadecimal digits; see the corresponding
+       option in linkgit:git-describe[1] for details.
+match=<pattern>;;
+       Only consider tags matching the given `glob(7)` pattern,
+       excluding the "refs/tags/" prefix; see the corresponding option
+       in linkgit:git-describe[1] for details.
+exclude=<pattern>;;
+       Do not consider tags matching the given `glob(7)` pattern,
+       excluding the "refs/tags/" prefix; see the corresponding option
+       in linkgit:git-describe[1] for details.
+--
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
@@ -228,12 +295,20 @@ fields will correspond to the appropriate date or name-email-date tuple
 from the `committer` or `tagger` fields depending on the object type.
 These are intended for working on a mix of annotated and lightweight tags.
 
+For tag objects, a `fieldname` prefixed with an asterisk (`*`) expands to
+the `fieldname` value of the peeled object, rather than that of the tag
+object itself.
+
 Fields that have name-email-date tuple as its value (`author`,
 `committer`, and `tagger`) can be suffixed with `name`, `email`,
 and `date` to extract the named component.  For email fields (`authoremail`,
 `committeremail` and `taggeremail`), `:trim` can be appended to get the email
 without angle brackets, and `:localpart` to get the part before the `@` symbol
-out of the trimmed email.
+out of the trimmed email. In addition to these, the `:mailmap` option and the
+corresponding `:mailmap,trim` and `:mailmap,localpart` can be used (order does
+not matter) to get values of the name and email according to the .mailmap file
+or according to the file set in the mailmap.file or mailmap.blob configuration
+variable (see linkgit:gitmailmap[5]).
 
 The raw data in an object is `raw`.
 
index dfcc7da4c211706570cd1ce7e1fd8552fb8947d9..414da6b73e7dc9bee177cd91227acb36c6ed070a 100644 (file)
@@ -55,7 +55,7 @@ A "message" generated by the command consists of three parts:
 * The "patch", which is the "diff -p --stat" output (see
   linkgit:git-diff[1]) between the commit and its parent.
 
-The log message and the patch is separated by a line with a
+The log message and the patch are separated by a line with a
 three-dash line.
 
 There are two ways to specify which commits to operate on.
@@ -99,7 +99,7 @@ To omit patch numbers from the subject, use `-N`.
 
 If given `--thread`, `git-format-patch` will generate `In-Reply-To` and
 `References` headers to make the second and subsequent patch mails appear
-as replies to the first mail; this also generates a `Message-Id` header to
+as replies to the first mail; this also generates a `Message-ID` header to
 reference.
 
 OPTIONS
@@ -163,7 +163,7 @@ include::diff-options.txt[]
 --no-thread::
        Controls addition of `In-Reply-To` and `References` headers to
        make the second and subsequent mails appear as replies to the
-       first.  Also controls generation of the `Message-Id` header to
+       first.  Also controls generation of the `Message-ID` header to
        reference.
 +
 The optional <style> argument can be either `shallow` or `deep`.
@@ -173,8 +173,7 @@ series, where the head is chosen from the cover letter, the
 threading makes every mail a reply to the previous one.
 +
 The default is `--no-thread`, unless the `format.thread` configuration
-is set.  If `--thread` is specified without a style, it defaults to the
-style specified by `format.thread` if any, or else `shallow`.
+is set.  `--thread` without an argument is equivalent to `--thread=shallow`.
 +
 Beware that the default for 'git send-email' is to thread emails
 itself.  If you want `git format-patch` to take care of threading, you
@@ -216,11 +215,21 @@ is greater than 100 bytes, then the mode will be `message`, otherwise
 If `<mode>` is `none`, both the cover letter subject and body will be
 populated with placeholder text.
 
+--description-file=<file>::
+       Use the contents of <file> instead of the branch's description
+       for generating the cover letter.
+
 --subject-prefix=<subject prefix>::
        Instead of the standard '[PATCH]' prefix in the subject
-       line, instead use '[<subject prefix>]'. This
-       allows for useful naming of a patch series, and can be
-       combined with the `--numbered` option.
+       line, instead use '[<subject prefix>]'. This can be used
+       to name a patch series, and can be combined with the
+       `--numbered` option.
++
+The configuration variable `format.subjectPrefix` may also be used
+to configure a subject prefix to apply to a given repository for
+all patches. This is often useful on mailing lists which receive
+patches for several repositories and can be used to disambiguate
+the patches (with a value of e.g. "PATCH my-project").
 
 --filename-max-length=<n>::
        Instead of the standard 64 bytes, chomp the generated output
@@ -230,9 +239,9 @@ populated with placeholder text.
        variable, or 64 if unconfigured.
 
 --rfc::
-       Alias for `--subject-prefix="RFC PATCH"`. RFC means "Request For
-       Comments"; use this when sending an experimental patch for
-       discussion rather than application.
+       Prepends "RFC" to the subject prefix (producing "RFC PATCH" by
+       default). RFC means "Request For Comments"; use this when sending
+       an experimental patch for discussion rather than application.
 
 -v <n>::
 --reroll-count=<n>::
@@ -246,7 +255,7 @@ populated with placeholder text.
        or "--reroll-count=4rev2" are allowed), but the downside of
        using such a reroll-count is that the range-diff/interdiff
        with the previous version does not state exactly which
-       version the new interation is compared against.
+       version the new iteration is compared against.
 
 --to=<email>::
        Add a `To:` header to the email headers. This is in addition
@@ -601,8 +610,8 @@ Approach #3 (external editor)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 The following Thunderbird extensions are needed:
-AboutConfig from http://aboutconfig.mozdev.org/ and
-External Editor from http://globs.org/articles.php?lng=en&pg=8
+AboutConfig from https://mjg.github.io/AboutConfig/ and
+External Editor from https://globs.org/articles.php?lng=en&pg=8
 
 1. Prepare the patch as a text file using your method of choice.
 
index 29318ea957ef8fda0aee500d795c6f04fce0cae2..5b82e4605c2e91dc647330409af81728b665a80a 100644 (file)
@@ -24,7 +24,7 @@ OPTIONS
        An object to treat as the head of an unreachability trace.
 +
 If no objects are given, 'git fsck' defaults to using the
-index file, all SHA-1 references in `refs` namespace, and all reflogs
+index file, all SHA-1 references in the `refs` namespace, and all reflogs
 (unless --no-reflogs is given) as heads.
 
 --unreachable::
@@ -64,7 +64,7 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs
 --connectivity-only::
        Check only the connectivity of reachable objects, making sure
        that any objects referenced by a reachable tag, commit, or tree
-       is present. This speeds up the operation by avoiding reading
+       are present. This speeds up the operation by avoiding reading
        blobs entirely (though it does still check that referenced blobs
        exist). This will detect corruption in commits and trees, but
        not do any semantic checks (e.g., for format errors). Corruption
@@ -79,7 +79,7 @@ care about this output and want to speed it up further.
        recorded with g+w bit set, which was created by older
        versions of Git.  Existing repositories, including the
        Linux kernel, Git itself, and sparse repository have old
-       objects that triggers this check, but it is recommended
+       objects that trigger this check, but it is recommended
        to check new projects with this flag.
 
 --verbose::
@@ -152,6 +152,18 @@ hash mismatch <object>::
        object database value.
        This indicates a serious data integrity problem.
 
+
+FSCK MESSAGES
+-------------
+
+The following lists the types of errors `git fsck` detects and what
+each error means, with their default severity.  The severity of the
+error, other than those that are marked as "(FATAL)", can be tweaked
+by setting the corresponding `fsck.<msg-id>` configuration variable.
+
+include::fsck-msgids.txt[]
+
+
 Environment Variables
 ---------------------
 
index cc142fb8612c7279d7b74eaf8bea5edc10252d88..8585d19f4d89870027df745197d6dc85a1944db4 100644 (file)
@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)
 
 NAME
 ----
-git-fsmonitor--daemon - A Built-in File System Monitor
+git-fsmonitor--daemon - A Built-in Filesystem Monitor
 
 SYNOPSIS
 --------
@@ -17,7 +17,7 @@ DESCRIPTION
 -----------
 
 A daemon to watch the working directory for file and directory
-changes using platform-specific file system notification facilities.
+changes using platform-specific filesystem notification facilities.
 
 This daemon communicates directly with commands like `git status`
 using the link:technical/api-simple-ipc.html[simple IPC] interface
@@ -63,13 +63,44 @@ CAVEATS
 -------
 
 The fsmonitor daemon does not currently know about submodules and does
-not know to filter out file system events that happen within a
+not know to filter out filesystem events that happen within a
 submodule.  If fsmonitor daemon is watching a super repo and a file is
 modified within the working directory of a submodule, it will report
 the change (as happening against the super repo).  However, the client
 will properly ignore these extra events, so performance may be affected
 but it will not cause an incorrect result.
 
+By default, the fsmonitor daemon refuses to work with network-mounted
+repositories; this may be overridden by setting `fsmonitor.allowRemote` to
+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work
+correctly with all network-mounted repositories, so such use is considered
+experimental.
+
+On Mac OS, the inter-process communication (IPC) between various Git
+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a
+special type of file -- which is supported by native Mac OS filesystems,
+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems
+may or may not have the needed support; the fsmonitor daemon is not guaranteed
+to work with these filesystems and such use is considered experimental.
+
+By default, the socket is created in the `.git` directory.  However, if the
+`.git` directory is on a network-mounted filesystem, it will instead be
+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a
+network-mounted filesystem, in which case you must set the configuration
+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native
+filesystem in which to create the socket file.
+
+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)
+is on a native Mac OS file filesystem the fsmonitor daemon will report an
+error that will cause the daemon and the currently running command to exit.
+
+CONFIGURATION
+-------------
+
+include::includes/cmd-config-section-all.txt[]
+
+include::config/fsmonitor--daemon.txt[]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index a65c9aa62d64112dfd03335af82a680e54d00c58..b5561c458a101c32a51ccca8599d4898e166b174 100644 (file)
@@ -54,9 +54,17 @@ other housekeeping tasks (e.g. rerere, working trees, reflog...) will
 be performed as well.
 
 
---cruft::
+--[no-]cruft::
        When expiring unreachable objects, pack them separately into a
-       cruft pack instead of storing them as loose objects.
+       cruft pack instead of storing them as loose objects. `--cruft`
+       is on by default.
+
+--max-cruft-size=<n>::
+       When packing unreachable objects into a cruft pack, limit the
+       size of new cruft packs to be at most `<n>` bytes. Overrides any
+       value specified via the `gc.maxCruftSize` configuration. See
+       the `--max-cruft-size` option of linkgit:git-repack[1] for
+       more.
 
 --prune=<date>::
        Prune loose objects older than date (default is 2 weeks ago,
@@ -77,9 +85,10 @@ be performed as well.
        instance running on this repository.
 
 --keep-largest-pack::
-       All packs except the largest pack and those marked with a
-       `.keep` files are consolidated into a single pack. When this
-       option is used, `gc.bigPackThreshold` is ignored.
+       All packs except the largest non-cruft pack, any packs marked
+       with a `.keep` file, and any cruft pack(s) are consolidated into
+       a single pack. When this option is used, `gc.bigPackThreshold`
+       is ignored.
 
 AGGRESSIVE
 ----------
index ac44d85b0b5c7f12a2d5473b3be0b0e4511cf58f..b537bb45b138f49e8298a9fdf8f26866fe8e9b72 100644 (file)
@@ -20,7 +20,7 @@ and extract the commit ID stored in it.  It reads only the first
 1024 bytes of input, thus its runtime is not influenced by the size
 of the tar archive very much.
 
-If no commit ID is found, 'git get-tar-commit-id' quietly exists with a
+If no commit ID is found, 'git get-tar-commit-id' quietly exits with a
 return code of 1.  This can happen if the archive had not been created
 using 'git archive' or if the first parameter of 'git archive' had been
 a tree ID instead of a commit ID or tag.
index dabdbe8471de5d36bf55589aa74a270901ff882e..0d0103c780af8c454db02a33c909343fb7bd51be 100644 (file)
@@ -337,7 +337,7 @@ The `--threads` option (and the grep.threads configuration) will be ignored when
 
 When grepping the object store (with `--cached` or giving tree objects), running
 with multiple threads might perform slower than single threaded if `--textconv`
-is given and there're too many text conversions. So if you experience low
+is given and there are too many text conversions. So if you experience low
 performance in this case, it might be desirable to use `--threads=1`.
 
 CONFIGURATION
index df9e2c58bdbc5f31edaf25577df744868e16f3de..ef4719ae41c700f5dde933a69ae6c8cf8c5fd3bf 100644 (file)
@@ -3,13 +3,14 @@ git-hash-object(1)
 
 NAME
 ----
-git-hash-object - Compute object ID and optionally creates a blob from a file
+git-hash-object - Compute object ID and optionally create an object from a file
 
 
 SYNOPSIS
 --------
 [verse]
-'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin [--literally]] [--] <file>...
+'git hash-object' [-t <type>] [-w] [--path=<file> | --no-filters]
+               [--stdin [--literally]] [--] <file>...
 'git hash-object' [-t <type>] [-w] --stdin-paths [--no-filters]
 
 DESCRIPTION
@@ -24,7 +25,8 @@ OPTIONS
 -------
 
 -t <type>::
-       Specify the type (default: "blob").
+       Specify the type of object to be created (default: "blob"). Possible
+       values are `commit`, `tree`, `blob`, and `tag`.
 
 -w::
        Actually write the object into the object database.
@@ -37,10 +39,10 @@ OPTIONS
        of from the command-line.
 
 --path::
-       Hash object as it were located at the given path. The location of
-       file does not directly influence on the hash value, but path is
-       used to determine what Git filters should be applied to the object
-       before it can be placed to the object database, and, as result of
+       Hash object as if it were located at the given path. The location of
+       the file does not directly influence the hash value, but the path is
+       used to determine which Git filters should be applied to the object
+       before it can be placed in the object database.  As a result of
        applying filters, the actual blob put into the object database may
        differ from the given file. This option is mainly useful for hashing
        temporary files located outside of the working directory or files
index 2b0b5e390dcb94a651ca92ded8c1542a175f7f09..f0bedc1f96433e6d7667519c1c7d307c46d17dc3 100644 (file)
@@ -42,13 +42,13 @@ former is internally converted into the latter.
 
 To display the linkgit:git[1] man page, use `git help git`.
 
-This page can be displayed with 'git help help' or `git help --help`
+This page can be displayed with 'git help help' or `git help --help`.
 
 OPTIONS
 -------
 -a::
 --all::
-       Prints all the available commands on the standard output.
+       Print all the available commands on the standard output.
 
 --no-external-commands::
        When used with `--all`, exclude the listing of external "git-*"
@@ -59,7 +59,7 @@ OPTIONS
        aliases.
 
 --verbose::
-       When used with `--all` print description for all recognized
+       When used with `--all`, print description for all recognized
        commands. This is the default.
 
 -c::
@@ -69,10 +69,10 @@ OPTIONS
 
 -g::
 --guides::
-       Prints a list of the Git concept guides on the standard output.
+       Print a list of the Git concept guides on the standard output.
 
 --user-interfaces::
-       Prints a list of the repository, command and file interfaces
+       Print a list of the repository, command and file interfaces
        documentation on the standard output.
 +
 In-repository file interfaces such as `.git/info/exclude` are
@@ -85,7 +85,7 @@ pseudo-configuration such as the file-based `.git/hooks/*` interface
 described in linkgit:githooks[5].
 
 --developer-interfaces::
-       Print list of file formats, protocols and other developer
+       Print list of file formats, protocols and other developer
        interfaces documentation on the standard output.
 
 -i::
@@ -109,7 +109,7 @@ other display programs (see below).
        format. A web browser will be used for that purpose.
 +
 The web browser can be specified using the configuration variable
-`help.browser`, or `web.browser` if the former is not set. If none of
+`help.browser`, or `web.browser` if the former is not set. If neither of
 these config variables is set, the 'git web{litdd}browse' helper script
 (called by 'git help') will pick a suitable default. See
 linkgit:git-web{litdd}browse[1] for more information about this.
@@ -129,8 +129,8 @@ line option:
 * "info" corresponds to '-i|--info',
 * "web" or "html" correspond to '-w|--web'.
 
-help.browser, web.browser and browser.<tool>.path
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help.browser, web.browser, and browser.<tool>.path
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The `help.browser`, `web.browser` and `browser.<tool>.path` will also
 be checked if the 'web' format is chosen (either by command-line
index 77c3a8ad909def201ea48034fe88eb88cf2a3a97..f6cc72d2ca9c7090c5391618d67abbfe1a1dd8e6 100644 (file)
@@ -8,12 +8,12 @@ git-hook - Run git hooks
 SYNOPSIS
 --------
 [verse]
-'git hook' run [--ignore-missing] <hook-name> [-- <hook-args>]
+'git hook' run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
 
 DESCRIPTION
 -----------
 
-A command interface to running git hooks (see linkgit:githooks[5]),
+A command interface for running git hooks (see linkgit:githooks[5]),
 for use by other scripted git commands.
 
 SUBCOMMANDS
@@ -31,6 +31,11 @@ linkgit:githooks[5] for arguments hooks might expect (if any).
 OPTIONS
 -------
 
+--to-stdin::
+       For "run"; specify a file which will be streamed into the
+       hook's stdin. The hook will receive the entire file from
+       beginning to EOF.
+
 --ignore-missing::
        Ignore any missing hook by quietly returning zero. Used for
        tools that want to do a blind one-shot run of a hook that may
index 0c5c0dde19f0b0f4fe271c1d9e7a5dcc01efe48e..f37ddaded82b42b5093ad9b1ea791c2979d743f1 100644 (file)
@@ -23,7 +23,7 @@ discussion of `GIT_PROTOCOL` in the ENVIRONMENT section below.
 It verifies that the directory has the magic file
 "git-daemon-export-ok", and it will refuse to export any Git directory
 that hasn't explicitly been marked for export this way (unless the
-`GIT_HTTP_EXPORT_ALL` environmental variable is set).
+`GIT_HTTP_EXPORT_ALL` environment variable is set).
 
 By default, only the `upload-pack` service is enabled, which serves
 'git fetch-pack' and 'git ls-remote' clients, which are invoked from
@@ -42,12 +42,12 @@ http.getanyfile::
        any file within the repository, including objects that are
        no longer reachable from a branch but are still present.
        It is enabled by default, but a repository can disable it
-       by setting this configuration item to `false`.
+       by setting this configuration value to `false`.
 
 http.uploadpack::
        This serves 'git fetch-pack' and 'git ls-remote' clients.
        It is enabled by default, but a repository can disable it
-       by setting this configuration item to `false`.
+       by setting this configuration value to `false`.
 
 http.receivepack::
        This serves 'git send-pack' clients, allowing push.  It is
@@ -265,12 +265,12 @@ by the invoking web server, including:
 * QUERY_STRING
 * REQUEST_METHOD
 
-The `GIT_HTTP_EXPORT_ALL` environmental variable may be passed to
+The `GIT_HTTP_EXPORT_ALL` environment variable may be passed to
 'git-http-backend' to bypass the check for the "git-daemon-export-ok"
 file in each repository before allowing export of that repository.
 
 The `GIT_HTTP_MAX_REQUEST_BUFFER` environment variable (or the
-`http.maxRequestBuffer` config variable) may be set to change the
+`http.maxRequestBuffer` config option) may be set to change the
 largest ref negotiation request that git will handle during a fetch; any
 fetch requiring a larger buffer will not succeed.  This value should not
 normally need to be changed, but may be helpful if you are fetching from
index 319062c021bb2c44686cd993898d502405531646..4ec7c68d3b9ecd18b3358563c2c1de4addff6eb0 100644 (file)
@@ -31,7 +31,7 @@ commit-id::
        Report what is downloaded.
 
 -w <filename>::
-        Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
+       Writes the commit-id into the specified filename under $GIT_DIR/refs/<filename> on
         the local end after the transfer is complete.
 
 --stdin::
index 7c6a6dd7f6a7fc9c75bf0ae595b12732d5c17765..ce0d80821259277f9112ad28b002f4c72bdb8348 100644 (file)
@@ -13,12 +13,12 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Sends missing objects to remote repository, and updates the
+Sends missing objects to the remote repository, and updates the
 remote branch.
 
 *NOTE*: This command is temporarily disabled if your libcurl
 is older than 7.16, as the combination has been reported
-not to work and sometimes corrupts repository.
+not to work and sometimes corrupts the repository.
 
 OPTIONS
 -------
@@ -44,7 +44,7 @@ OPTIONS
 -d::
 -D::
        Remove <ref> from remote repository.  The specified branch
-       cannot be the remote HEAD.  If -d is specified the following
+       cannot be the remote HEAD.  If -d is specified, the following
        other conditions must also be met:
 
        - Remote HEAD must resolve to an object that exists locally
@@ -83,8 +83,8 @@ and where it is pushed is determined by using the destination side.
 Without `--force`, the <src> ref is stored at the remote only if
 <dst> does not exist, or <dst> is a proper subset (i.e. an
 ancestor) of <src>.  This check, known as "fast-forward check",
-is performed in order to avoid accidentally overwriting the
-remote ref and lose other peoples' commits from there.
+is performed to avoid accidentally overwriting the
+remote ref and losing other peoples' commits from there.
 
 With `--force`, the fast-forward check is disabled for all refs.
 
index f7b185151415b54b08fb21f54062842baf64e636..c8a89d7243bfbd43d68e3922998a378f68eb6c66 100644 (file)
@@ -135,7 +135,7 @@ flames ridiculing you if you don't check this.
 
 Thunderbird in particular is known to be problematic.  Thunderbird
 users may wish to visit this web page for more information:
-  http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
+  https://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
 
 SEE ALSO
 --------
index 4e71c256ecb08fe44ef656c85b7246e6f2f458b1..6486620c3d8f2a2d7a9bb623c362d02465b06be4 100644 (file)
@@ -16,10 +16,10 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Reads a packed archive (.pack) from the specified file, and
-builds a pack index file (.idx) for it. Optionally writes a
+Reads a packed archive (.pack) from the specified file,
+builds a pack index file (.idx) for it, and optionally writes a
 reverse-index (.rev) for the specified pack. The packed
-archive together with the pack index can then be placed in
+archive, together with the pack index, can then be placed in
 the objects/pack/ directory of a Git repository.
 
 
@@ -68,8 +68,8 @@ OPTIONS
        updated to use objects contained in the pack.
 
 --keep=<msg>::
-       Like --keep create a .keep file before moving the index into
-       its final destination, but rather than creating an empty file
+       Like --keep, create a .keep file before moving the index into
+       its final destination.  However, instead of creating an empty file
        place '<msg>' followed by an LF into the .keep file.  The '<msg>'
        message can later be searched for within all .keep files to
        locate any which have outlived their usefulness.
index 160dea1372cd9ae87f1bad9744e2ae045ed1b5b6..e8dc645bb59a8664865c9eed00698ee4374ea4a3 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
          [--separate-git-dir <git-dir>] [--object-format=<format>]
+         [--ref-format=<format>]
          [-b <branch-name> | --initial-branch=<branch-name>]
          [--shared[=<permissions>]] [<directory>]
 
@@ -29,7 +30,7 @@ to use instead of `./.git` for the base of the repository.
 
 If the object storage directory is specified via the
 `$GIT_OBJECT_DIRECTORY` environment variable then the sha1 directories
-are created underneath - otherwise the default `$GIT_DIR/objects`
+are created underneath; otherwise, the default `$GIT_DIR/objects`
 directory is used.
 
 Running 'git init' in an existing repository is safe. It will not
@@ -57,6 +58,12 @@ values are 'sha1' and (if enabled) 'sha256'.  'sha1' is the default.
 +
 include::object-format-disclaimer.txt[]
 
+--ref-format=<format>::
+
+Specify the given ref storage format for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
 --template=<template-directory>::
 
 Specify the directory from which templates will be used.  (See the "TEMPLATE
@@ -66,10 +73,10 @@ DIRECTORY" section below.)
 
 Instead of initializing the repository as a directory to either `$GIT_DIR` or
 `./.git/`, create a text file there containing the path to the actual
-repository.  This file acts as filesystem-agnostic Git symbolic link to the
+repository.  This file acts as filesystem-agnostic Git symbolic link to the
 repository.
 +
-If this is reinitialization, the repository will be moved to the specified path.
+If this is reinitialization, the repository will be moved to the specified path.
 
 -b <branch-name>::
 --initial-branch=<branch-name>::
@@ -99,7 +106,7 @@ specified.
 
 'group' (or 'true')::
 
-Make the repository group-writable, (and g+sx, since the git group may be not
+Make the repository group-writable, (and g+sx, since the git group may not be
 the primary group of all users). This is used to loosen the permissions of an
 otherwise safe umask(2) value. Note that the umask still applies to the other
 permission bits (e.g. if umask is '0022', using 'group' will not remove read
@@ -115,7 +122,7 @@ Same as 'group', but make the repository readable by all users.
 '<perm>' is a 3-digit octal number prefixed with `0` and each file
 will have mode '<perm>'. '<perm>' will override users' umask(2)
 value (and not only loosen permissions as 'group' and 'all'
-does). '0640' will create a repository which is group-readable, but
+do). '0640' will create a repository which is group-readable, but
 not group-writable or accessible to others. '0660' will create a repo
 that is readable and writable to the current user and group, but
 inaccessible to others (directories and executable files get their
index 6d6197cd0a41ec08954e3b19ab1eb355c01dc5ea..418265f044d65000e9ae22235b9801644f9c2170 100644 (file)
@@ -8,68 +8,106 @@ git-interpret-trailers - Add or parse structured information in commit messages
 SYNOPSIS
 --------
 [verse]
-'git interpret-trailers' [<options>] [(--trailer <token>[(=|:)<value>])...] [<file>...]
-'git interpret-trailers' [<options>] [--parse] [<file>...]
+'git interpret-trailers' [--in-place] [--trim-empty]
+                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]
+                       [--parse] [<file>...]
 
 DESCRIPTION
 -----------
-Help parsing or adding 'trailers' lines, that look similar to RFC 822 e-mail
+Add or parse 'trailer' lines that look similar to RFC 822 e-mail
 headers, at the end of the otherwise free-form part of a commit
-message.
+message. For example, in the following commit message
 
-This command reads some patches or commit messages from either the
-<file> arguments or the standard input if no <file> is specified. If
-`--parse` is specified, the output consists of the parsed trailers.
+------------------------------------------------
+subject
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 
-Otherwise, this command applies the arguments passed using the
-`--trailer` option, if any, to the commit message part of each input
-file. The result is emitted on the standard output.
+Signed-off-by: Alice <alice@example.com>
+Signed-off-by: Bob <bob@example.com>
+------------------------------------------------
+
+the last two lines starting with "Signed-off-by" are trailers.
+
+This command reads commit messages from either the
+<file> arguments or the standard input if no <file> is specified.
+If `--parse` is specified, the output consists of the parsed trailers
+coming from the input, without influencing them with any command line
+options or configuration variables.
+
+Otherwise, this command applies `trailer.*` configuration variables
+(which could potentially add new trailers, as well as reposition them),
+as well as any command line arguments that can override configuration
+variables (such as `--trailer=...` which could also add new trailers),
+to each input file. The result is emitted on the standard output.
+
+This command can also operate on the output of linkgit:git-format-patch[1],
+which is more elaborate than a plain commit message. Namely, such output
+includes a commit message (as above), a "---" divider line, and a patch part.
+For these inputs, the divider and patch parts are not modified by
+this command and are emitted as is on the output, unless
+`--no-divider` is specified.
 
 Some configuration variables control the way the `--trailer` arguments
-are applied to each commit message and the way any existing trailer in
-the commit message is changed. They also make it possible to
+are applied to each input and the way any existing trailer in
+the input is changed. They also make it possible to
 automatically add some trailers.
 
-By default, a '<token>=<value>' or '<token>:<value>' argument given
+By default, a '<key>=<value>' or '<key>:<value>' argument given
 using `--trailer` will be appended after the existing trailers only if
-the last trailer has a different (<token>, <value>) pair (or if there
-is no existing trailer). The <token> and <value> parts will be trimmed
+the last trailer has a different (<key>, <value>) pair (or if there
+is no existing trailer). The <key> and <value> parts will be trimmed
 to remove starting and trailing whitespace, and the resulting trimmed
-<token> and <value> will appear in the message like this:
+<key> and <value> will appear in the output like this:
 
 ------------------------------------------------
-token: value
+key: value
 ------------------------------------------------
 
-This means that the trimmed <token> and <value> will be separated by
+This means that the trimmed <key> and <value> will be separated by
 `': '` (one colon followed by one space).
 
+For convenience, a <keyAlias> can be configured to make using `--trailer`
+shorter to type on the command line. This can be configured using the
+'trailer.<keyAlias>.key' configuration variable. The <keyAlias> must be a prefix
+of the full <key> string, although case sensitivity does not matter. For
+example, if you have
+
+------------------------------------------------
+trailer.sign.key "Signed-off-by: "
+------------------------------------------------
+
+in your configuration, you only need to specify `--trailer="sign: foo"`
+on the command line instead of `--trailer="Signed-off-by: foo"`.
+
 By default the new trailer will appear at the end of all the existing
 trailers. If there is no existing trailer, the new trailer will appear
-after the commit message part of the output, and, if there is no line
-with only spaces at the end of the commit message part, one blank line
-will be added before the new trailer.
+at the end of the input. A blank line will be added before the new
+trailer if there isn't one already.
 
-Existing trailers are extracted from the input message by looking for
+Existing trailers are extracted from the input by looking for
 a group of one or more lines that (i) is all trailers, or (ii) contains at
 least one Git-generated or user-configured trailer and consists of at
 least 25% trailers.
 The group must be preceded by one or more empty (or whitespace-only) lines.
-The group must either be at the end of the message or be the last
+The group must either be at the end of the input or be the last
 non-whitespace lines before a line that starts with '---' (followed by a
-space or the end of the line). Such three minus signs start the patch
-part of the message. See also `--no-divider` below.
+space or the end of the line).
 
 When reading trailers, there can be no whitespace before or inside the
-token, but any number of regular space and tab characters are allowed
-between the token and the separator. There can be whitespaces before,
-inside or after the value. The value may be split over multiple lines
+<key>, but any number of regular space and tab characters are allowed
+between the <key> and the separator. There can be whitespaces before,
+inside or after the <value>. The <value> may be split over multiple lines
 with each subsequent line starting with at least one whitespace, like
-the "folding" in RFC 822.
+the "folding" in RFC 822. Example:
 
-Note that 'trailers' do not follow and are not intended to follow many
-rules for RFC 822 headers. For example they do not follow
-the encoding rules and probably many other rules.
+------------------------------------------------
+key: This is a very long value, with spaces and
+  newlines in it.
+------------------------------------------------
+
+Note that trailers do not follow (nor are they intended to follow) many of the
+rules for RFC 822 headers. For example they do not follow the encoding rule.
 
 OPTIONS
 -------
@@ -78,38 +116,47 @@ OPTIONS
 
 --trim-empty::
        If the <value> part of any trailer contains only whitespace,
-       the whole trailer will be removed from the resulting message.
+       the whole trailer will be removed from the output.
        This applies to existing trailers as well as new trailers.
 
---trailer <token>[(=|:)<value>]::
-       Specify a (<token>, <value>) pair that should be applied as a
-       trailer to the input messages. See the description of this
+--trailer <key>[(=|:)<value>]::
+       Specify a (<key>, <value>) pair that should be applied as a
+       trailer to the inputs. See the description of this
        command.
 
 --where <placement>::
 --no-where::
        Specify where all new trailers will be added.  A setting
-       provided with '--where' overrides all configuration variables
+       provided with '--where' overrides the `trailer.where` and any
+       applicable `trailer.<keyAlias>.where` configuration variables
        and applies to all '--trailer' options until the next occurrence of
-       '--where' or '--no-where'. Possible values are `after`, `before`,
-       `end` or `start`.
+       '--where' or '--no-where'. Upon encountering '--no-where', clear the
+       effect of any previous use of '--where', such that the relevant configuration
+       variables are no longer overridden. Possible placements are `after`,
+       `before`, `end` or `start`.
 
 --if-exists <action>::
 --no-if-exists::
        Specify what action will be performed when there is already at
-       least one trailer with the same <token> in the message.  A setting
-       provided with '--if-exists' overrides all configuration variables
+       least one trailer with the same <key> in the input.  A setting
+       provided with '--if-exists' overrides the `trailer.ifExists` and any
+       applicable `trailer.<keyAlias>.ifExists` configuration variables
        and applies to all '--trailer' options until the next occurrence of
-       '--if-exists' or '--no-if-exists'. Possible actions are `addIfDifferent`,
+       '--if-exists' or '--no-if-exists'. Upon encountering '--no-if-exists, clear the
+       effect of any previous use of '--if-exists, such that the relevant configuration
+       variables are no longer overridden. Possible actions are `addIfDifferent`,
        `addIfDifferentNeighbor`, `add`, `replace` and `doNothing`.
 
 --if-missing <action>::
 --no-if-missing::
        Specify what action will be performed when there is no other
-       trailer with the same <token> in the message.  A setting
-       provided with '--if-missing' overrides all configuration variables
+       trailer with the same <key> in the input.  A setting
+       provided with '--if-missing' overrides the `trailer.ifMissing` and any
+       applicable `trailer.<keyAlias>.ifMissing` configuration variables
        and applies to all '--trailer' options until the next occurrence of
-       '--if-missing' or '--no-if-missing'. Possible actions are `doNothing`
+       '--if-missing' or '--no-if-missing'. Upon encountering '--no-if-missing,
+       clear the effect of any previous use of '--if-missing, such that the relevant
+       configuration variables are no longer overridden. Possible actions are `doNothing`
        or `add`.
 
 --only-trailers::
@@ -117,16 +164,19 @@ OPTIONS
 
 --only-input::
        Output only trailers that exist in the input; do not add any
-       from the command-line or by following configured `trailer.*`
-       rules.
+       from the command-line or by applying `trailer.*` configuration
+       variables.
 
 --unfold::
-       Remove any whitespace-continuation in trailers, so that each
-       trailer appears on a line by itself with its full content.
+       If a trailer has a value that runs over multiple lines (aka "folded"),
+       reformat the value into a single line.
 
 --parse::
        A convenience alias for `--only-trailers --only-input
-       --unfold`.
+       --unfold`. This makes it easier to only see the trailers coming from the
+       input without influencing them with any command line options or
+       configuration variables, while also making the output machine-friendly with
+       --unfold.
 
 --no-divider::
        Do not treat `---` as the end of the commit message. Use this
@@ -147,11 +197,11 @@ used when another separator is not specified in the config for this
 trailer.
 +
 For example, if the value for this option is "%=$", then only lines
-using the format '<token><sep><value>' with <sep> containing '%', '='
+using the format '<key><sep><value>' with <sep> containing '%', '='
 or '$' and then spaces will be considered trailers. And '%' will be
 the default separator used, so by default trailers will appear like:
-'<token>% <value>' (one percent sign and one space will appear between
-the token and the value).
+'<key>% <value>' (one percent sign and one space will appear between
+the key and the value).
 
 trailer.where::
        This option tells where a new trailer will be added.
@@ -165,41 +215,41 @@ If it is `start`, then each new trailer will appear at the start,
 instead of the end, of the existing trailers.
 +
 If it is `after`, then each new trailer will appear just after the
-last trailer with the same <token>.
+last trailer with the same <key>.
 +
 If it is `before`, then each new trailer will appear just before the
-first trailer with the same <token>.
+first trailer with the same <key>.
 
 trailer.ifexists::
        This option makes it possible to choose what action will be
        performed when there is already at least one trailer with the
-       same <token> in the message.
+       same <key> in the input.
 +
 The valid values for this option are: `addIfDifferentNeighbor` (this
 is the default), `addIfDifferent`, `add`, `replace` or `doNothing`.
 +
 With `addIfDifferentNeighbor`, a new trailer will be added only if no
-trailer with the same (<token>, <value>) pair is above or below the line
+trailer with the same (<key>, <value>) pair is above or below the line
 where the new trailer will be added.
 +
 With `addIfDifferent`, a new trailer will be added only if no trailer
-with the same (<token>, <value>) pair is already in the message.
+with the same (<key>, <value>) pair is already in the input.
 +
 With `add`, a new trailer will be added, even if some trailers with
-the same (<token>, <value>) pair are already in the message.
+the same (<key>, <value>) pair are already in the input.
 +
-With `replace`, an existing trailer with the same <token> will be
+With `replace`, an existing trailer with the same <key> will be
 deleted and the new trailer will be added. The deleted trailer will be
-the closest one (with the same <token>) to the place where the new one
+the closest one (with the same <key>) to the place where the new one
 will be added.
 +
 With `doNothing`, nothing will be done; that is no new trailer will be
-added if there is already one with the same <token> in the message.
+added if there is already one with the same <key> in the input.
 
 trailer.ifmissing::
        This option makes it possible to choose what action will be
        performed when there is not yet any trailer with the same
-       <token> in the message.
+       <key> in the input.
 +
 The valid values for this option are: `add` (this is the default) and
 `doNothing`.
@@ -208,100 +258,106 @@ With `add`, a new trailer will be added.
 +
 With `doNothing`, nothing will be done.
 
-trailer.<token>.key::
-       This `key` will be used instead of <token> in the trailer. At
-       the end of this key, a separator can appear and then some
-       space characters. By default the only valid separator is ':',
-       but this can be changed using the `trailer.separators` config
-       variable.
+trailer.<keyAlias>.key::
+       Defines a <keyAlias> for the <key>. The <keyAlias> must be a
+       prefix (case does not matter) of the <key>. For example, in `git
+       config trailer.ack.key "Acked-by"` the "Acked-by" is the <key> and
+       the "ack" is the <keyAlias>. This configuration allows the shorter
+       `--trailer "ack:..."` invocation on the command line using the "ack"
+       <keyAlias> instead of the longer `--trailer "Acked-by:..."`.
++
+At the end of the <key>, a separator can appear and then some
+space characters. By default the only valid separator is ':',
+but this can be changed using the `trailer.separators` config
+variable.
 +
-If there is a separator, then the key will be used instead of both the
-<token> and the default separator when adding the trailer.
+If there is a separator in the key, then it overrides the default
+separator when adding the trailer.
 
-trailer.<token>.where::
+trailer.<keyAlias>.where::
        This option takes the same values as the 'trailer.where'
        configuration variable and it overrides what is specified by
-       that option for trailers with the specified <token>.
+       that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.ifexists::
+trailer.<keyAlias>.ifexists::
        This option takes the same values as the 'trailer.ifexists'
        configuration variable and it overrides what is specified by
-       that option for trailers with the specified <token>.
+       that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.ifmissing::
+trailer.<keyAlias>.ifmissing::
        This option takes the same values as the 'trailer.ifmissing'
        configuration variable and it overrides what is specified by
-       that option for trailers with the specified <token>.
+       that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.command::
-       This option behaves in the same way as 'trailer.<token>.cmd', except
+trailer.<keyAlias>.command::
+       Deprecated in favor of 'trailer.<keyAlias>.cmd'.
+       This option behaves in the same way as 'trailer.<keyAlias>.cmd', except
        that it doesn't pass anything as argument to the specified command.
        Instead the first occurrence of substring $ARG is replaced by the
-       value that would be passed as argument.
+       <value> that would be passed as argument.
 +
-The 'trailer.<token>.command' option has been deprecated in favor of
-'trailer.<token>.cmd' due to the fact that $ARG in the user's command is
+Note that $ARG in the user's command is
 only replaced once and that the original way of replacing $ARG is not safe.
 +
-When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given
-for the same <token>, 'trailer.<token>.cmd' is used and
-'trailer.<token>.command' is ignored.
-
-trailer.<token>.cmd::
-       This option can be used to specify a shell command that will be called:
-       once to automatically add a trailer with the specified <token>, and then
-       each time a '--trailer <token>=<value>' argument to modify the <value> of
-       the trailer that this option would produce.
+When both 'trailer.<keyAlias>.cmd' and 'trailer.<keyAlias>.command' are given
+for the same <keyAlias>, 'trailer.<keyAlias>.cmd' is used and
+'trailer.<keyAlias>.command' is ignored.
+
+trailer.<keyAlias>.cmd::
+       This option can be used to specify a shell command that will be called
+       once to automatically add a trailer with the specified <keyAlias>, and then
+       called each time a '--trailer <keyAlias>=<value>' argument is specified to
+       modify the <value> of the trailer that this option would produce.
 +
 When the specified command is first called to add a trailer
-with the specified <token>, the behavior is as if a special
-'--trailer <token>=<value>' argument was added at the beginning
+with the specified <keyAlias>, the behavior is as if a special
+'--trailer <keyAlias>=<value>' argument was added at the beginning
 of the "git interpret-trailers" command, where <value>
 is taken to be the standard output of the command with any
 leading and trailing whitespace trimmed off.
 +
-If some '--trailer <token>=<value>' arguments are also passed
+If some '--trailer <keyAlias>=<value>' arguments are also passed
 on the command line, the command is called again once for each
-of these arguments with the same <token>. And the <value> part
+of these arguments with the same <keyAlias>. And the <value> part
 of these arguments, if any, will be passed to the command as its
 first argument. This way the command can produce a <value> computed
-from the <value> passed in the '--trailer <token>=<value>' argument.
+from the <value> passed in the '--trailer <keyAlias>=<value>' argument.
 
 EXAMPLES
 --------
 
 * Configure a 'sign' trailer with a 'Signed-off-by' key, and then
-  add two of these trailers to a message:
+  add two of these trailers to a commit message file:
 +
 ------------
 $ git config trailer.sign.key "Signed-off-by"
 $ cat msg.txt
 subject
 
-message
-$ cat msg.txt | git interpret-trailers --trailer 'sign: Alice <alice@example.com>' --trailer 'sign: Bob <bob@example.com>'
+body text
+$ git interpret-trailers --trailer 'sign: Alice <alice@example.com>' --trailer 'sign: Bob <bob@example.com>' <msg.txt
 subject
 
-message
+body text
 
 Signed-off-by: Alice <alice@example.com>
 Signed-off-by: Bob <bob@example.com>
 ------------
 
-* Use the `--in-place` option to edit a message file in place:
+* Use the `--in-place` option to edit a commit message file in place:
 +
 ------------
 $ cat msg.txt
 subject
 
-message
+body text
 
 Signed-off-by: Bob <bob@example.com>
 $ git interpret-trailers --trailer 'Acked-by: Alice <alice@example.com>' --in-place msg.txt
 $ cat msg.txt
 subject
 
-message
+body text
 
 Signed-off-by: Bob <bob@example.com>
 Acked-by: Alice <alice@example.com>
@@ -321,17 +377,30 @@ $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Re
   'Signed-off-by: ' already, and show how it works:
 +
 ------------
+$ cat msg1.txt
+subject
+
+body text
 $ git config trailer.sign.key "Signed-off-by: "
 $ git config trailer.sign.ifmissing add
 $ git config trailer.sign.ifexists doNothing
-$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
-$ git interpret-trailers <<EOF
-> EOF
+$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
+$ git interpret-trailers --trailer sign <msg1.txt
+subject
+
+body text
 
 Signed-off-by: Bob <bob@example.com>
-$ git interpret-trailers <<EOF
-> Signed-off-by: Alice <alice@example.com>
-> EOF
+$ cat msg2.txt
+subject
+
+body text
+
+Signed-off-by: Alice <alice@example.com>
+$ git interpret-trailers --trailer sign <msg2.txt
+subject
+
+body text
 
 Signed-off-by: Alice <alice@example.com>
 ------------
@@ -356,18 +425,17 @@ Fix #42
 $ cat ~/bin/glog-find-author
 #!/bin/sh
 test -n "$1" && git log --author="$1" --pretty="%an <%ae>" -1 || true
+$ cat msg.txt
+subject
+
+body text
 $ git config trailer.help.key "Helped-by: "
 $ git config trailer.help.ifExists "addIfDifferentNeighbor"
 $ git config trailer.help.cmd "~/bin/glog-find-author"
-$ git interpret-trailers --trailer="help:Junio" --trailer="help:Couder" <<EOF
-> subject
->
-> message
->
-> EOF
+$ git interpret-trailers --trailer="help:Junio" --trailer="help:Couder" <msg.txt
 subject
 
-message
+body text
 
 Helped-by: Junio C Hamano <gitster@pobox.com>
 Helped-by: Christian Couder <christian.couder@gmail.com>
@@ -381,18 +449,17 @@ Helped-by: Christian Couder <christian.couder@gmail.com>
 $ cat ~/bin/glog-grep
 #!/bin/sh
 test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
+$ cat msg.txt
+subject
+
+body text
 $ git config trailer.ref.key "Reference-to: "
 $ git config trailer.ref.ifExists "replace"
 $ git config trailer.ref.cmd "~/bin/glog-grep"
-$ git interpret-trailers --trailer="ref:Add copyright notices." <<EOF
-> subject
->
-> message
->
-> EOF
+$ git interpret-trailers --trailer="ref:Add copyright notices." <msg.txt
 subject
 
-message
+body text
 
 Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
 ------------
@@ -401,20 +468,23 @@ Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
   commit that is related, and show how it works:
 +
 ------------
+$ cat msg.txt
+subject
+
+body text
+
+see: HEAD~2
+$ cat ~/bin/glog-ref
+#!/bin/sh
+git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14
 $ git config trailer.see.key "See-also: "
 $ git config trailer.see.ifExists "replace"
 $ git config trailer.see.ifMissing "doNothing"
-$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
-$ git interpret-trailers <<EOF
-> subject
-> 
-> message
-> 
-> see: HEAD~2
-> EOF
+$ git config trailer.see.cmd "glog-ref"
+$ git interpret-trailers --trailer=see <msg.txt
 subject
 
-message
+body text
 
 See-also: fe3187489d69c4 (subject of related commit)
 ------------
@@ -426,22 +496,21 @@ See-also: fe3187489d69c4 (subject of related commit)
   to add a 'git-version' trailer:
 +
 ------------
-$ sed -e 's/ Z$/ /' >commit_template.txt <<EOF
-***subject***
-> 
-***message***
-> 
-Fixes: Z
-Cc: Z
-Reviewed-by: Z
-Signed-off-by: Z
-> EOF
+$ cat temp.txt
+***subject***
+
+***message***
+
+Fixes: Z
+Cc: Z
+Reviewed-by: Z
+Signed-off-by: Z
+$ sed -e 's/ Z$/ /' temp.txt > commit_template.txt
 $ git config commit.template commit_template.txt
-$ cat >.git/hooks/commit-msg <<EOF
-> #!/bin/sh
-> git interpret-trailers --trim-empty --trailer "git-version: \$(git describe)" "\$1" > "\$1.new"
-> mv "\$1.new" "\$1"
-> EOF
+$ cat .git/hooks/commit-msg
+#!/bin/sh
+git interpret-trailers --trim-empty --trailer "git-version: \$(git describe)" "\$1" > "\$1.new"
+mv "\$1.new" "\$1"
 $ chmod +x .git/hooks/commit-msg
 ------------
 
index 2a66cf888074656ce775ad93551603c15fed4ddc..579682172fe45816403f4d5ceffc03949eac5b3a 100644 (file)
@@ -120,11 +120,11 @@ By default, `git log` does not generate any diff output. The options
 below can be used to show the changes made by each commit.
 
 Note that unless one of `--diff-merges` variants (including short
-`-m`, `-c`, and `--cc` options) is explicitly given, merge commits
+`-m`, `-c`, `--cc`, and `--dd` options) is explicitly given, merge commits
 will not show a diff, even if a diff format like `--patch` is
 selected, nor will they match search options like `-S`. The exception
 is when `--first-parent` is in use, in which case `first-parent` is
-the default format.
+the default format for merge commits.
 
 :git-log: 1
 :diff-merges-default: `off`
index d7986419c2507a7032961ba5b37ce6c73e5822f9..f65a8cd91d4bb40e986c3610b6f1c2b89c719ace 100644 (file)
@@ -10,8 +10,9 @@ SYNOPSIS
 --------
 [verse]
 'git ls-files' [-z] [-t] [-v] [-f]
-               [-c|--cached] [-d|--deleted] [-o|--others] [-i|--|ignored]
-               [-s|--stage] [-u|--unmerged] [-k|--|killed] [-m|--modified]
+               [-c|--cached] [-d|--deleted] [-o|--others] [-i|--ignored]
+               [-s|--stage] [-u|--unmerged] [-k|--killed] [-m|--modified]
+               [--resolve-undo]
                [--directory [--no-empty-directory]] [--eol]
                [--deduplicate]
                [-x <pattern>|--exclude=<pattern>]
@@ -24,25 +25,30 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-This merges the file listing in the index with the actual working
+This command merges the file listing in the index with the actual working
 directory list, and shows different combinations of the two.
 
-One or more of the options below may be used to determine the files
-shown:
+Several flags can be used to determine which files are
+shown, and each file may be printed multiple times if there are
+multiple entries in the index or if multiple statuses are applicable for
+the relevant file selection options.
 
 OPTIONS
 -------
 -c::
 --cached::
-       Show cached files in the output (default)
+       Show all files cached in Git's index, i.e. all tracked files.
+       (This is the default if no -c/-s/-d/-o/-u/-k/-m/--resolve-undo
+       options are specified.)
 
 -d::
 --deleted::
-       Show deleted files in the output
+       Show files with an unstaged deletion
 
 -m::
 --modified::
-       Show modified files in the output
+       Show files with an unstaged modification (note that an unstaged
+       deletion also counts as an unstaged modification)
 
 -o::
 --others::
@@ -50,11 +56,14 @@ OPTIONS
 
 -i::
 --ignored::
-       Show only ignored files in the output. When showing files in the
-       index, print only those matched by an exclude pattern. When
-       showing "other" files, show only those matched by an exclude
-       pattern. Standard ignore rules are not automatically activated,
-       therefore at least one of the `--exclude*` options is required.
+       Show only ignored files in the output.  Must be used with
+       either an explicit '-c' or '-o'.  When showing files in the
+       index (i.e. when used with '-c'), print only those files
+       matching an exclude pattern.  When showing "other" files
+       (i.e. when used with '-o'), show only those matched by an
+       exclude pattern.  Standard ignore rules are not automatically
+       activated; therefore, at least one of the `--exclude*` options
+       is required.
 
 -s::
 --stage::
@@ -63,19 +72,29 @@ OPTIONS
 --directory::
        If a whole directory is classified as "other", show just its
        name (with a trailing slash) and not its whole contents.
+       Has no effect without -o/--others.
 
 --no-empty-directory::
        Do not list empty directories. Has no effect without --directory.
 
 -u::
 --unmerged::
-       Show unmerged files in the output (forces --stage)
+       Show information about unmerged files in the output, but do
+       not show any other tracked files (forces --stage, overrides
+       --cached).
 
 -k::
 --killed::
-       Show files on the filesystem that need to be removed due
-       to file/directory conflicts for checkout-index to
-       succeed.
+       Show untracked files on the filesystem that need to be removed
+       due to file/directory conflicts for tracked files to be able to
+       be written to the filesystem.
+
+--resolve-undo::
+       Show files having resolve-undo information in the index
+       together with their resolve-undo information.  (resolve-undo
+       information is what is used to implement "git checkout -m
+       $PATH", i.e. to recreate merge conflicts that were
+       accidentally resolved)
 
 -z::
        \0 line termination on output and do not quote filenames.
@@ -100,7 +119,8 @@ OPTIONS
 
 --exclude-per-directory=<file>::
        Read additional exclude patterns that apply only to the
-       directory and its subdirectories in <file>.
+       directory and its subdirectories in <file>.  Deprecated; use
+       --exclude-standard instead.
 
 --exclude-standard::
        Add the standard Git exclusions: .git/info/exclude, .gitignore
@@ -118,24 +138,27 @@ OPTIONS
        with `-s` or `-u` options does not make any sense.
 
 -t::
-       This feature is semi-deprecated. For scripting purpose,
-       linkgit:git-status[1] `--porcelain` and
+       Show status tags together with filenames.  Note that for
+       scripting purposes, linkgit:git-status[1] `--porcelain` and
        linkgit:git-diff-files[1] `--name-status` are almost always
-       superior alternatives, and users should look at
+       superior alternatives; users should look at
        linkgit:git-status[1] `--short` or linkgit:git-diff[1]
        `--name-status` for more user-friendly alternatives.
 +
 --
-This option identifies the file status with the following tags (followed by
-a space) at the start of each line:
-
-       H::     cached
-       S::     skip-worktree
-       M::     unmerged
-       R::     removed/deleted
-       C::     modified/changed
-       K::     to be killed
-       ?::     other
+This option provides a reason for showing each filename, in the form
+of a status tag (which is followed by a space and then the filename).
+The status tags are all single characters from the following list:
+
+       H::     tracked file that is not either unmerged or skip-worktree
+       S::     tracked file that is skip-worktree
+       M::     tracked file that is unmerged
+       R::     tracked file with unstaged removal/deletion
+       C::     tracked file with unstaged modification/change
+       K::     untracked paths which are part of file/directory conflicts
+               which prevent checking out tracked files
+       ?::     untracked file
+       U::     file with resolve-undo information
 --
 
 -v::
@@ -247,8 +270,14 @@ interpolated.  The following "fieldname" are understood:
 
 objectmode::
        The mode of the file which is recorded in the index.
+objecttype::
+       The object type of the file which is recorded in the index.
 objectname::
        The name of the file which is recorded in the index.
+objectsize[:padded]::
+       The object size of the file which is recorded in the index
+       ("-" if the object is a `commit` or `tree`).
+       It also supports a padded format of size with "%(objectsize:padded)".
 stage::
        The stage of the file which is recorded in the index.
 eolinfo:index::
@@ -269,7 +298,9 @@ traversing the directory tree and finding files to show when the
 flags --others or --ignored are specified.  linkgit:gitignore[5]
 specifies the format of exclude patterns.
 
-These exclude patterns come from these places, in order:
+Generally, you should just use --exclude-standard, but for historical
+reasons the exclude patterns can be specified from the following
+places, in order:
 
   1. The command-line flag --exclude=<pattern> specifies a
      single pattern.  Patterns are ordered in the same order
index 492e573856f24f6444fefb842e8ee733fa95be25..1c4f696ab57e2a241bee0ebab07d0bb199e77b3e 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git ls-remote' [--heads] [--tags] [--refs] [--upload-pack=<exec>]
              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]
-             [--symref] [<repository> [<refs>...]]
+             [--symref] [<repository> [<patterns>...]]
 
 DESCRIPTION
 -----------
@@ -85,31 +85,62 @@ OPTIONS
        either a URL or the name of a remote (see the GIT URLS and
        REMOTES sections of linkgit:git-fetch[1]).
 
-<refs>...::
+<patterns>...::
        When unspecified, all references, after filtering done
-       with --heads and --tags, are shown.  When <refs>... are
-       specified, only references matching the given patterns
-       are displayed.
+       with --heads and --tags, are shown.  When <patterns>... are
+       specified, only references matching one or more of the given
+       patterns are displayed. Each pattern is interpreted as a glob
+       (see `glob` in linkgit:gitglossary[7]) which is matched against
+       the "tail" of a ref, starting either from the start of the ref
+       (so a full name like `refs/heads/foo` matches) or from a slash
+       separator (so `bar` matches `refs/heads/bar` but not
+       `refs/heads/foobar`).
+
+OUTPUT
+------
+
+The output is in the format:
+
+------------
+<oid> TAB <ref> LF
+------------
+
+When showing an annotated tag, unless `--refs` is given, two such
+lines are shown: one with the refname for the tag itself as `<ref>`,
+and another with `<ref>` followed by `^{}`. The `<oid>` on the latter
+line shows the name of the object the tag points at.
 
 EXAMPLES
 --------
 
+* List all references (including symbolics and pseudorefs), peeling
+  tags:
++
+----
+$ git ls-remote
+27d43aaaf50ef0ae014b88bba294f93658016a2e       HEAD
+950264636c68591989456e3ba0a5442f93152c1a       refs/heads/main
+d9ab777d41f92a8c1684c91cfb02053d7dd1046b       refs/heads/next
+d4ca2e3147b409459955613c152220f4db848ee1       refs/tags/v2.40.0
+73876f4861cd3d187a4682290ab75c9dccadbc56       refs/tags/v2.40.0^{}
+----
+
+* List all references matching given patterns:
++
 ----
-$ git ls-remote --tags ./.
-d6602ec5194c87b0fc87103ca4d67251c76f233a       refs/tags/v0.99
-f25a265a342aed6041ab0cc484224d9ca54b6f41       refs/tags/v0.99.1
-7ceca275d047c90c0c7d5afb13ab97efdf51bd6e       refs/tags/v0.99.3
-c5db5456ae3b0873fc659c19fafdde22313cc441       refs/tags/v0.99.2
-0918385dbd9656cab0d1d81ba7453d49bbc16250       refs/tags/junio-gpg-pub
 $ git ls-remote http://www.kernel.org/pub/scm/git/git.git master seen rc
 5fe978a5381f1fbad26a80e682ddd2a401966740       refs/heads/master
 c781a84b5204fb294c9ccc79f8b3baceeb32c061       refs/heads/seen
-$ git remote add korg http://www.kernel.org/pub/scm/git/git.git
-$ git ls-remote --tags korg v\*
-d6602ec5194c87b0fc87103ca4d67251c76f233a       refs/tags/v0.99
-f25a265a342aed6041ab0cc484224d9ca54b6f41       refs/tags/v0.99.1
-c5db5456ae3b0873fc659c19fafdde22313cc441       refs/tags/v0.99.2
-7ceca275d047c90c0c7d5afb13ab97efdf51bd6e       refs/tags/v0.99.3
+----
+
+* List only tags matching a given wildcard pattern:
++
+----
+$ git ls-remote --tags http://www.kernel.org/pub/scm/git/git.git v\*
+485a869c64a68cc5795dd99689797c5900f4716d       refs/tags/v2.39.2
+cbf04937d5b9fcf0a76c28f69e6294e9e3ecd7e6       refs/tags/v2.39.2^{}
+d4ca2e3147b409459955613c152220f4db848ee1       refs/tags/v2.40.0
+73876f4861cd3d187a4682290ab75c9dccadbc56       refs/tags/v2.40.0^{}
 ----
 
 SEE ALSO
index 0240adb8eec96600572be6f3f8b988a5aa2eab45..6572095d8d69dca5ff044282b7e299060d83b7f4 100644 (file)
@@ -86,9 +86,9 @@ OPTIONS
 --format=<format>::
        A string that interpolates `%(fieldname)` from the result
        being shown. It also interpolates `%%` to `%`, and
-       `%xx` where `xx` are hex digits interpolates to character
-       with hex code `xx`; for example `%00` interpolates to
-       `\0` (NUL), `%09` to `\t` (TAB) and `%0a` to `\n` (LF).
+       `%xNN` where `NN` are hex digits interpolates to character
+       with hex code `NN`; for example `%x00` interpolates to
+       `\0` (NUL), `%x09` to `\t` (TAB) and `%x0a` to `\n` (LF).
        When specified, `--format` cannot be combined with other
        format-altering options, including `--long`, `--name-only`
        and `--object-only`.
@@ -145,7 +145,7 @@ FIELD NAMES
 -----------
 
 Various values from structured fields can be used to interpolate
-into the resulting output. For each outputing line, the following
+into the resulting output. For each outputting line, the following
 names can be used:
 
 objectmode::
index e3b2a88c4b75f1f6e23feb8a09030469468d9ca8..3f0a6662c81efb6fee6589aa41c2b8e945c36644 100644 (file)
@@ -34,7 +34,7 @@ OPTIONS
 
 -b::
        If any file doesn't begin with a From line, assume it is a
-       single mail message instead of signaling error.
+       single mail message instead of signaling an error.
 
 -d<prec>::
        Instead of the default 4 digits with leading zeros,
index 9c630efe19c68ec4b25fee1cf716c5a2073e2f51..51d0f7e94b6a0182089d5d7f48ab9f9d1cb9f572 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git maintenance' run [<options>]
 'git maintenance' start [--scheduler=<scheduler>]
-'git maintenance' (stop|register|unregister)
+'git maintenance' (stop|register|unregister) [<options>]
 
 
 DESCRIPTION
@@ -50,13 +50,13 @@ stop::
        the background maintenance is restarted later.
 
 register::
-       Initialize Git config values so any scheduled maintenance will
-       start running on this repository. This adds the repository to the
-       `maintenance.repo` config variable in the current user's global
-       config and enables some recommended configuration values for
-       `maintenance.<task>.schedule`. The tasks that are enabled are safe
-       for running in the background without disrupting foreground
-       processes.
+       Initialize Git config values so any scheduled maintenance will start
+       running on this repository. This adds the repository to the
+       `maintenance.repo` config variable in the current user's global config,
+       or the config specified by --config-file option, and enables some
+       recommended configuration values for `maintenance.<task>.schedule`. The
+       tasks that are enabled are safe for running in the background without
+       disrupting foreground processes.
 +
 The `register` subcommand will also set the `maintenance.strategy` config
 value to `incremental`, if this value is not previously set. The
@@ -79,6 +79,10 @@ unregister::
        Remove the current repository from background maintenance. This
        only removes the repository from the configured list. It does not
        stop the background maintenance processes from running.
++
+The `unregister` subcommand will report an error if the current repository
+is not already registered. Use the `--force` option to return success even
+when the current repository is not registered.
 
 TASKS
 -----
@@ -98,9 +102,9 @@ prefetch::
        requested refs within `refs/prefetch/`. Also, tags are not updated.
 +
 This is done to avoid disrupting the remote-tracking branches. The end users
-expect these refs to stay unmoved unless they initiate a fetch.  With prefetch
-task, however, the objects necessary to complete a later real fetch would
-already be obtained, so the real fetch would go faster.  In the ideal case,
+expect these refs to stay unmoved unless they initiate a fetch.  However,
+with the prefetch task, the objects necessary to complete a later real fetch
+would already be obtained, making the real fetch faster.  In the ideal case,
 it will just become an update to a bunch of remote-tracking branches without
 any object transfer.
 
index 2d944e0851f6a8e295bf8d8e0aac14725e1b4326..5ab957cfbc1420d5dca07a21dbf6708777ee754e 100644 (file)
@@ -9,8 +9,8 @@ git-merge-base - Find as good common ancestors as possible for a merge
 SYNOPSIS
 --------
 [verse]
-'git merge-base' [-a|--all] <commit> <commit>...
-'git merge-base' [-a|--all] --octopus <commit>...
+'git merge-base' [-a | --all] <commit> <commit>...
+'git merge-base' [-a | --all] --octopus <commit>...
 'git merge-base' --is-ancestor <commit> <commit>
 'git merge-base' --independent <commit>...
 'git merge-base' --fork-point <ref> [<commit>]
@@ -18,7 +18,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-'git merge-base' finds best common ancestor(s) between two commits to use
+'git merge-base' finds the best common ancestor(s) between two commits to use
 in a three-way merge.  One common ancestor is 'better' than another common
 ancestor if the latter is an ancestor of the former.  A common ancestor
 that does not have any better common ancestor is a 'best common
@@ -28,7 +28,7 @@ merge base for a pair of commits.
 OPERATION MODES
 ---------------
 
-As the most common special case, specifying only two commits on the
+In the most common special case, specifying only two commits on the
 command line means computing the merge base between the given two commits.
 
 More generally, among the two commits to compute the merge base from,
@@ -64,7 +64,7 @@ from linkgit:git-show-branch[1] when used with the `--merge-base` option.
        the two commits, but also takes into account the reflog of
        <ref> to see if the history leading to <commit> forked from
        an earlier incarnation of the branch <ref> (see discussion
-       on this mode below).
+       of this mode below).
 
 OPTIONS
 -------
@@ -88,7 +88,7 @@ For example, with this topology:
 
 the merge base between 'A' and 'B' is '1'.
 
-Given three commits 'A', 'B' and 'C', `git merge-base A B C` will compute the
+Given three commits 'A', 'B', and 'C', `git merge-base A B C` will compute the
 merge base between 'A' and a hypothetical commit 'M', which is a merge
 between 'B' and 'C'.  For example, with this topology:
 
@@ -130,7 +130,7 @@ When the history involves criss-cross merges, there can be more than one
 ---2---o---o---B
 ....
 
-both '1' and '2' are merge-bases of A and B.  Neither one is better than
+both '1' and '2' are merge bases of A and B.  Neither one is better than
 the other (both are 'best' merge bases).  When the `--all` option is not given,
 it is unspecified which best one is output.
 
@@ -204,7 +204,7 @@ will find B0, and
 
     $ git rebase --onto origin/master $fork_point topic
 
-will replay D0, D1 and D on top of B to create a new history of this
+will replay D0, D1, and D on top of B to create a new history of this
 shape:
 
 ....
index 7e9093fab60d267fa795e3d3fddd26cdfacfb557..71915a00fa472e73f5fadd2205f964919d3262db 100644 (file)
@@ -11,19 +11,20 @@ SYNOPSIS
 [verse]
 'git merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
        [--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>]
-       [--[no-]diff3] <current-file> <base-file> <other-file>
+       [--[no-]diff3] [--object-id] <current> <base> <other>
 
 
 DESCRIPTION
 -----------
-'git merge-file' incorporates all changes that lead from the `<base-file>`
-to `<other-file>` into `<current-file>`. The result ordinarily goes into
-`<current-file>`. 'git merge-file' is useful for combining separate changes
-to an original. Suppose `<base-file>` is the original, and both
-`<current-file>` and `<other-file>` are modifications of `<base-file>`,
+Given three files `<current>`, `<base>` and `<other>`,
+'git merge-file' incorporates all changes that lead from `<base>`
+to `<other>` into `<current>`. The result ordinarily goes into
+`<current>`. 'git merge-file' is useful for combining separate changes
+to an original. Suppose `<base>` is the original, and both
+`<current>` and `<other>` are modifications of `<base>`,
 then 'git merge-file' combines both changes.
 
-A conflict occurs if both `<current-file>` and `<other-file>` have changes
+A conflict occurs if both `<current>` and `<other>` have changes
 in a common segment of lines. If a conflict is found, 'git merge-file'
 normally outputs a warning and brackets the conflict with lines containing
 <<<<<<< and >>>>>>> markers. A typical conflict will look like this:
@@ -36,10 +37,14 @@ normally outputs a warning and brackets the conflict with lines containing
 
 If there are conflicts, the user should edit the result and delete one of
 the alternatives.  When `--ours`, `--theirs`, or `--union` option is in effect,
-however, these conflicts are resolved favouring lines from `<current-file>`,
-lines from `<other-file>`, or lines from both respectively.  The length of the
+however, these conflicts are resolved favouring lines from `<current>`,
+lines from `<other>`, or lines from both respectively.  The length of the
 conflict markers can be given with the `--marker-size` option.
 
+If `--object-id` is specified, exactly the same behavior occurs, except that
+instead of specifying what to merge as files, it is specified as a list of
+object IDs referring to blobs.
+
 The exit value of this program is negative on error, and the number of
 conflicts otherwise (truncated to 127 if there are more than that many
 conflicts). If the merge was clean, the exit value is 0.
@@ -52,6 +57,14 @@ linkgit:git[1].
 OPTIONS
 -------
 
+--object-id::
+       Specify the contents to merge as blobs in the current repository instead of
+       files.  In this case, the operation must take place within a valid repository.
++
+If the `-p` option is specified, the merged file (including conflicts, if any)
+goes to standard output as normal; otherwise, the merged file is written to the
+object store and the object ID of its blob is written to standard output.
+
 -L <label>::
        This option may be given up to three times, and
        specifies labels to be used in place of the
@@ -62,7 +75,7 @@ OPTIONS
 
 -p::
        Send results to standard output instead of overwriting
-       `<current-file>`.
+       `<current>`.
 
 -q::
        Quiet; do not warn about conflicts.
@@ -79,6 +92,12 @@ OPTIONS
        Instead of leaving conflicts in the file, resolve conflicts
        favouring our (or their or both) side of the lines.
 
+--diff-algorithm={patience|minimal|histogram|myers}::
+       Use a different diff algorithm while merging. The current default is "myers",
+       but selecting more recent algorithm such as "histogram" can help
+       avoid mismerges that occur due to unimportant matching lines
+       (such as braces from distinct functions). See also
+       linkgit:git-diff[1] `--diff-algorithm`.
 
 EXAMPLES
 --------
@@ -93,6 +112,11 @@ EXAMPLES
        merges tmp/a123 and tmp/c345 with the base tmp/b234, but uses labels
        `a` and `c` instead of `tmp/a123` and `tmp/c345`.
 
+`git merge-file -p --object-id abc1234 def567 890abcd`::
+
+       combines the changes of the blob abc1234 and 890abcd since def567,
+       tries to merge them and writes the result to standard output
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index d6c356740efcaa2ddb7c668f123b4024e3c0f270..b50acace3bc367ad7b73d2da10252e6b5f2e0d07 100644 (file)
@@ -19,12 +19,12 @@ DESCRIPTION
 This command has a modern `--write-tree` mode and a deprecated
 `--trivial-merge` mode.  With the exception of the
 <<DEPMERGE,DEPRECATED DESCRIPTION>> section at the end, the rest of
-this documentation describes modern `--write-tree` mode.
+this documentation describes the modern `--write-tree` mode.
 
 Performs a merge, but does not make any new commits and does not read
 from or write to either the working tree or index.
 
-The performed merge will use the same feature as the "real"
+The performed merge will use the same features as the "real"
 linkgit:git-merge[1], including:
 
   * three way content merges of individual files
@@ -64,6 +64,11 @@ OPTIONS
        share no common history.  This flag can be given to override that
        check and make the merge proceed anyway.
 
+--merge-base=<commit>::
+       Instead of finding the merge-bases for <branch1> and <branch2>,
+       specify a merge-base for the merge, and specifying multiple bases is
+       currently not supported. This option is incompatible with `--stdin`.
+
 [[OUTPUT]]
 OUTPUT
 ------
@@ -81,6 +86,31 @@ Whereas for a conflicted merge, the output is by default of the form:
 
 These are discussed individually below.
 
+However, there is an exception.  If `--stdin` is passed, then there is
+an extra section at the beginning, a NUL character at the end, and then
+all the sections repeat for each line of input.  Thus, if the first merge
+is conflicted and the second is clean, the output would be of the form:
+
+       <Merge status>
+       <OID of toplevel tree>
+       <Conflicted file info>
+       <Informational messages>
+       NUL
+       <Merge status>
+       <OID of toplevel tree>
+       NUL
+
+[[MS]]
+Merge status
+~~~~~~~~~~~~
+
+This is an integer status followed by a NUL character.  The integer status is:
+
+     0: merge had conflicts
+     1: merge was clean
+     <0: something prevented the merge from running (e.g. access to repository
+        objects denied by filesystem)
+
 [[OIDTLT]]
 OID of toplevel tree
 ~~~~~~~~~~~~~~~~~~~~
@@ -108,18 +138,50 @@ character instead of a newline character.
 Informational messages
 ~~~~~~~~~~~~~~~~~~~~~~
 
-This always starts with a blank line (or NUL if `-z` is passed) to
-separate it from the previous sections, and then has free-form
-messages about the merge, such as:
+This section provides informational messages, typically about
+conflicts.  The format of the section varies significantly depending
+on whether `-z` is passed.
+
+If `-z` is passed:
+
+The output format is zero or more conflict informational records, each
+of the form:
+
+       <list-of-paths><conflict-type>NUL<conflict-message>NUL
+
+where <list-of-paths> is of the form
+
+       <number-of-paths>NUL<path1>NUL<path2>NUL...<pathN>NUL
+
+and includes paths (or branch names) affected by the conflict or
+informational message in <conflict-message>.  Also, <conflict-type> is a
+stable string explaining the type of conflict, such as
+
+  * "Auto-merging"
+  * "CONFLICT (rename/delete)"
+  * "CONFLICT (submodule lacks merge base)"
+  * "CONFLICT (binary)"
+
+and <conflict-message> is a more detailed message about the conflict which often
+(but not always) embeds the <stable-short-type-description> within it.  These
+strings may change in future Git versions.  Some examples:
 
   * "Auto-merging <file>"
   * "CONFLICT (rename/delete): <oldfile> renamed...but deleted in..."
-  * "Failed to merge submodule <submodule> (<reason>)"
+  * "Failed to merge submodule <submodule> (no merge base)"
   * "Warning: cannot merge binary files: <filename>"
 
-Note that these free-form messages will never have a NUL character
-in or between them, even if -z is passed.  It is simply a large block
-of text taking up the remainder of the output.
+If `-z` is NOT passed:
+
+This section starts with a blank line to separate it from the previous
+sections, and then only contains the <conflict-message> information
+from the previous section (separated by newlines).  These are
+non-stable strings that should not be parsed by scripts, and are just
+meant for human consumption.  Also, note that while <conflict-message>
+strings usually do not contain embedded newlines, they sometimes do.
+(However, the free-form messages will never have an embedded NUL
+character).  So, the entire block of information is meant for human
+readers as an agglomeration of all conflict messages.
 
 EXIT STATUS
 -----------
@@ -127,7 +189,10 @@ EXIT STATUS
 For a successful, non-conflicted merge, the exit status is 0.  When the
 merge has conflicts, the exit status is 1.  If the merge is not able to
 complete (or start) due to some kind of error, the exit status is
-something other than 0 or 1 (and the output is unspecified).
+something other than 0 or 1 (and the output is unspecified).  When
+--stdin is passed, the return status is 0 for both successful and
+conflicted merges, and something other than 0 or 1 if it cannot complete
+all the requested merges.
 
 USAGE NOTES
 -----------
@@ -156,6 +221,17 @@ with linkgit:git-merge[1]:
   * any messages that would have been printed to stdout (the
     <<IM,Informational messages>>)
 
+INPUT FORMAT
+------------
+'git merge-tree --stdin' input format is fully text based. Each line
+has this format:
+
+       [<base-commit> -- ]<branch1> <branch2>
+
+If one line is separated by `--`, the string before the separator is
+used for specifying a merge-base for the merge and the string after
+the separator describes the branches to be merged.
+
 MISTAKES TO AVOID
 -----------------
 
@@ -177,7 +253,7 @@ Do NOT attempt to guess or make the user guess the conflict types from
 the <<CFI,Conflicted file info>> list.  The information there is
 insufficient to do so.  For example: Rename/rename(1to2) conflicts (both
 sides renamed the same file differently) will result in three different
-file having higher order stages (but each only has one higher order
+files having higher order stages (but each only has one higher order
 stage), with no way (short of the <<IM,Informational messages>> section)
 to determine which three files are related.  File/directory conflicts
 also result in a file with exactly one higher order stage.
@@ -187,7 +263,7 @@ a file with exactly one higher order stage.  In all cases, the
 <<IM,Informational messages>> section has the necessary info, though it
 is not designed to be machine parseable.
 
-Do NOT assume that each paths from <<CFI,Conflicted file info>>, and
+Do NOT assume that each path from <<CFI,Conflicted file info>>, and
 the logical conflicts in the <<IM,Informational messages>> have a
 one-to-one mapping, nor that there is a one-to-many mapping, nor a
 many-to-one mapping.  Many-to-many mappings exist, meaning that each
index 2d6a1391c89412e66aa1227201bdd89ba2f0f27a..1ab69f61f5749a21def2b08d7cd9466f59c3cd5c 100644 (file)
@@ -20,12 +20,12 @@ DESCRIPTION
 -----------
 Incorporates changes from the named commits (since the time their
 histories diverged from the current branch) into the current
-branch.  This command is used by 'git pull' to incorporate changes
+branch.  This command is used by `git pull` to incorporate changes
 from another repository and can be used by hand to merge changes
 from one branch into another.
 
 Assume the following history exists and the current branch is
-"`master`":
+`master`:
 
 ------------
          A---B---C topic
@@ -33,11 +33,12 @@ Assume the following history exists and the current branch is
     D---E---F---G master
 ------------
 
-Then "`git merge topic`" will replay the changes made on the
+Then `git merge topic` will replay the changes made on the
 `topic` branch since it diverged from `master` (i.e., `E`) until
 its current commit (`C`) on top of `master`, and record the result
 in a new commit along with the names of the two parent commits and
-a log message from the user describing the changes.
+a log message from the user describing the changes. Before the operation,
+`ORIG_HEAD` is set to the tip of the current branch (`C`).
 
 ------------
          A---B---C topic
@@ -45,21 +46,21 @@ a log message from the user describing the changes.
     D---E---F---G---H master
 ------------
 
-The second syntax ("`git merge --abort`") can only be run after the
-merge has resulted in conflicts. 'git merge --abort' will abort the
-merge process and try to reconstruct the pre-merge state. However,
-if there were uncommitted changes when the merge started (and
-especially if those changes were further modified after the merge
-was started), 'git merge --abort' will in some cases be unable to
-reconstruct the original (pre-merge) changes. Therefore:
+A merge stops if there's a conflict that cannot be resolved
+automatically or if `--no-commit` was provided when initiating the
+merge. At that point you can run `git merge --abort` or `git merge
+--continue`.
 
-*Warning*: Running 'git merge' with non-trivial uncommitted changes is
+`git merge --abort` will abort the merge process and try to reconstruct
+the pre-merge state. However, if there were uncommitted changes when the
+merge started (and especially if those changes were further modified
+after the merge was started), `git merge --abort` will in some cases be
+unable to reconstruct the original (pre-merge) changes. Therefore:
+
+*Warning*: Running `git merge` with non-trivial uncommitted changes is
 discouraged: while possible, it may leave you in a state that is hard to
 back out of in the case of a conflict.
 
-The third syntax ("`git merge --continue`") can only be run after the
-merge has resulted in conflicts.
-
 OPTIONS
 -------
 :git-merge: 1
@@ -73,8 +74,8 @@ include::merge-options.txt[]
 If `--log` is specified, a shortlog of the commits being merged
 will be appended to the specified message.
 +
-The 'git fmt-merge-msg' command can be
-used to give a good default for automated 'git merge'
+The `git fmt-merge-msg` command can be
+used to give a good default for automated `git merge`
 invocations. The automated message can include the branch description.
 
 --into-name <branch>::
@@ -103,14 +104,14 @@ include::rerere-options.txt[]
        present, apply it to the worktree.
 +
 If there were uncommitted worktree changes present when the merge
-started, 'git merge --abort' will in some cases be unable to
+started, `git merge --abort` will in some cases be unable to
 reconstruct these changes. It is therefore recommended to always
-commit or stash your changes before running 'git merge'.
+commit or stash your changes before running `git merge`.
 +
-'git merge --abort' is equivalent to 'git reset --merge' when
+`git merge --abort` is equivalent to `git reset --merge` when
 `MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in
-which case 'git merge --abort' applies the stash entry to the worktree
-whereas 'git reset --merge' will save the stashed changes in the stash
+which case `git merge --abort` applies the stash entry to the worktree
+whereas `git reset --merge` will save the stashed changes in the stash
 list.
 
 --quit::
@@ -119,8 +120,8 @@ list.
        stash entry will be saved to the stash list.
 
 --continue::
-       After a 'git merge' stops due to conflicts you can conclude the
-       merge by running 'git merge --continue' (see "HOW TO RESOLVE
+       After a `git merge` stops due to conflicts you can conclude the
+       merge by running `git merge --continue` (see "HOW TO RESOLVE
        CONFLICTS" section below).
 
 <commit>...::
@@ -143,25 +144,25 @@ PRE-MERGE CHECKS
 Before applying outside changes, you should get your own work in
 good shape and committed locally, so it will not be clobbered if
 there are conflicts.  See also linkgit:git-stash[1].
-'git pull' and 'git merge' will stop without doing anything when
-local uncommitted changes overlap with files that 'git pull'/'git
-merge' may need to update.
+`git pull` and `git merge` will stop without doing anything when
+local uncommitted changes overlap with files that `git pull`/`git
+merge` may need to update.
 
 To avoid recording unrelated changes in the merge commit,
-'git pull' and 'git merge' will also abort if there are any changes
+`git pull` and `git merge` will also abort if there are any changes
 registered in the index relative to the `HEAD` commit.  (Special
 narrow exceptions to this rule may exist depending on which merge
 strategy is in use, but generally, the index must match HEAD.)
 
-If all named commits are already ancestors of `HEAD`, 'git merge'
+If all named commits are already ancestors of `HEAD`, `git merge`
 will exit early with the message "Already up to date."
 
 FAST-FORWARD MERGE
 ------------------
 
 Often the current branch head is an ancestor of the named commit.
-This is the most common case especially when invoked from 'git
-pull': you are tracking an upstream repository, you have committed
+This is the most common case especially when invoked from `git
+pull`: you are tracking an upstream repository, you have committed
 no local changes, and now you want to update to a newer upstream
 revision.  In this case, a new commit is not needed to store the
 combined history; instead, the `HEAD` (along with the index) is
@@ -193,9 +194,13 @@ happens:
    versions: stage 1 stores the version from the common ancestor,
    stage 2 from `HEAD`, and stage 3 from `MERGE_HEAD` (you
    can inspect the stages with `git ls-files -u`).  The working
-   tree files contain the result of the "merge" program; i.e. 3-way
+   tree files contain the result of the merge operation; i.e. 3-way
    merge results with familiar conflict markers `<<<` `===` `>>>`.
-5. No other changes are made.  In particular, the local
+5. A ref named `AUTO_MERGE` is written, pointing to a tree
+   corresponding to the current content of the working tree (including
+   conflict markers for textual conflicts).  Note that this ref is only
+   written when the 'ort' merge strategy is used (the default).
+6. No other changes are made.  In particular, the local
    modifications you had before you started merge will stay the
    same and the index entries for them stay as they were,
    i.e. matching `HEAD`.
@@ -264,7 +269,7 @@ Barbie's remark on your side.  The only thing you can tell is that your
 side wants to say it is hard and you'd prefer to go shopping, while the
 other side wants to claim it is easy.
 
-An alternative style can be used by setting the "merge.conflictStyle"
+An alternative style can be used by setting the `merge.conflictStyle`
 configuration variable to either "diff3" or "zdiff3".  In "diff3"
 style, the above conflict may look like this:
 
@@ -323,19 +328,20 @@ After seeing a conflict, you can do two things:
 
  * Resolve the conflicts.  Git will mark the conflicts in
    the working tree.  Edit the files into shape and
-   'git add' them to the index.  Use 'git commit' or
-   'git merge --continue' to seal the deal. The latter command
+   `git add` them to the index.  Use `git commit` or
+   `git merge --continue` to seal the deal. The latter command
    checks whether there is a (interrupted) merge in progress
-   before calling 'git commit'.
+   before calling `git commit`.
 
 You can work through the conflict with a number of tools:
 
  * Use a mergetool.  `git mergetool` to launch a graphical
-   mergetool which will work you through the merge.
+   mergetool which will work through the merge with you.
 
  * Look at the diffs.  `git diff` will show a three-way diff,
    highlighting changes from both the `HEAD` and `MERGE_HEAD`
-   versions.
+   versions. `git diff AUTO_MERGE` will show what changes you've
+   made so far to resolve textual conflicts.
 
  * Look at the diffs from each branch. `git log --merge -p <path>`
    will show diffs first for the `HEAD` version and then the
@@ -386,7 +392,7 @@ CONFIGURATION
 
 branch.<name>.mergeOptions::
        Sets default options for merging into branch <name>. The syntax and
-       supported options are the same as those of 'git merge', but option
+       supported options are the same as those of `git merge`, but option
        values containing whitespace characters are currently not supported.
 
 include::includes/cmd-config-section-rest.txt[]
index 3e8f59ac0e46abc35b88723492fa2d411b2756ca..0726b560d432884eabc8834a51c2fbd68d74ad92 100644 (file)
@@ -28,22 +28,22 @@ to define the operation mode for the functions listed below.
 FUNCTIONS
 ---------
 get_merge_tool::
-       returns a merge tool. the return code is 1 if we returned a guessed
+       Returns a merge tool. The return code is 1 if we returned a guessed
        merge tool, else 0. '$GIT_MERGETOOL_GUI' may be set to 'true' to
        search for the appropriate guitool.
 
 get_merge_tool_cmd::
-       returns the custom command for a merge tool.
+       Returns the custom command for a merge tool.
 
 get_merge_tool_path::
-       returns the custom path for a merge tool.
+       Returns the custom path for a merge tool.
 
 initialize_merge_tool::
-       bring merge tool specific functions into scope so they can be used or
+       Brings merge tool specific functions into scope so they can be used or
        overridden.
 
 run_merge_tool::
-       launches a merge tool given the tool name and a true/false
+       Launches a merge tool given the tool name and a true/false
        flag to indicate whether a merge base is present.
        '$MERGED', '$LOCAL', '$REMOTE', and '$BASE' must be defined
        for use by the merge tool.
index c44e205629bf521fbb97c2b49493fcc9b9450932..b9e20c5dcd8c52df8e0782bc5d92e3a5ac5361b2 100644 (file)
@@ -17,7 +17,7 @@ Use `git mergetool` to run one of several merge utilities to resolve
 merge conflicts.  It is typically run after 'git merge'.
 
 If one or more <file> parameters are given, the merge tool program will
-be run to resolve differences on each file (skipping those without
+be run to resolve differences in each file (skipping those without
 conflicts).  Specifying a directory will include all unresolved files in
 that path.  If no <file> names are specified, 'git mergetool' will run
 the merge tool program on every file with merge conflicts.
@@ -49,7 +49,7 @@ variable `mergetool.<tool>.cmd`.
 +
 When 'git mergetool' is invoked with this tool (either through the
 `-t` or `--tool` option or the `merge.tool` configuration
-variable) the configured command line will be invoked with `$BASE`
+variable), the configured command line will be invoked with `$BASE`
 set to the name of a temporary file containing the common base for
 the merge, if available; `$LOCAL` set to the name of a temporary
 file containing the contents of the file on the current branch;
@@ -81,16 +81,17 @@ success of the resolution after the custom tool has exited.
 
 -g::
 --gui::
-       When 'git-mergetool' is invoked with the `-g` or `--gui` option
+       When 'git-mergetool' is invoked with the `-g` or `--gui` option,
        the default merge tool will be read from the configured
        `merge.guitool` variable instead of `merge.tool`. If
        `merge.guitool` is not set, we will fallback to the tool
-       configured under `merge.tool`.
+       configured under `merge.tool`. This may be autoselected using
+       the configuration variable `mergetool.guiDefault`.
 
 --no-gui::
-       This overrides a previous `-g` or `--gui` setting and reads the
-       default merge tool will be read from the configured `merge.tool`
-       variable.
+       This overrides a previous `-g` or `--gui` setting or
+       `mergetool.guiDefault` configuration and reads the default merge
+       tool from the configured `merge.tool` variable.
 
 -O<orderfile>::
        Process files in the order specified in the
@@ -114,7 +115,7 @@ These are safe to remove once a file has been merged and its
 `git mergetool` session has completed.
 
 Setting the `mergetool.keepBackup` configuration variable to `false`
-causes `git mergetool` to automatically remove the backup as files
+causes `git mergetool` to automatically remove the backup files as files
 are successfully merged.
 
 BACKEND SPECIFIC HINTS
index 466a69751910e4d7d031725f8d52d41d1e7f98a3..006d759962ac61a927d2a08bfa831d1692cc63c4 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Reads a tag contents on standard input and creates a tag object. The
+Reads a tag's contents on standard input and creates a tag object. The
 output is the new tag's <object> identifier.
 
 This command is mostly equivalent to linkgit:git-hash-object[1]
@@ -27,13 +27,13 @@ write a tag found in `my-tag`:
 The difference is that mktag will die before writing the tag if the
 tag doesn't pass a linkgit:git-fsck[1] check.
 
-The "fsck" check done mktag is stricter than what linkgit:git-fsck[1]
+The "fsck" check done by mktag is stricter than what linkgit:git-fsck[1]
 would run by default in that all `fsck.<msg-id>` messages are promoted
 from warnings to errors (so e.g. a missing "tagger" line is an error).
 
 Extra headers in the object are also an error under mktag, but ignored
 by linkgit:git-fsck[1]. This extra check can be turned off by setting
-the appropriate `fsck.<msg-id>` varible:
+the appropriate `fsck.<msg-id>` variable:
 
     git -c fsck.extraHeaderEntry=ignore mktag <my-tag-with-headers
 
@@ -56,7 +56,7 @@ has a very simple fixed format: four lines of
   tagger <tagger>
 
 followed by some 'optional' free-form message (some tags created
-by older Git may not have `tagger` line).  The message, when it
+by older Git may not have `tagger` line).  The message, when it
 exists, is separated by a blank line from the header.  The
 message part may contain a signature that Git itself doesn't
 care about, but that can be verified with gpg.
index 76b44f4da103872d9b39c893b62286d37b159693..383f09dd333f86d96288a776a1395696473b0fdb 100644 (file)
@@ -25,13 +25,13 @@ OPTIONS
 
 --missing::
        Allow missing objects.  The default behaviour (without this option)
-       is to verify that each tree entry's sha1 identifies an existing
+       is to verify that each tree entry's hash identifies an existing
        object.  This option has no effect on the treatment of gitlink entries
        (aka "submodules") which are always allowed to be missing.
 
 --batch::
        Allow building of more than one tree object before exiting.  Each
-       tree is separated by a single blank line. The final new-line is
+       tree is separated by a single blank line. The final newline is
        optional.  Note - if the `-z` option is used, lines are terminated
        with NUL.
 
index 79449bf98fe56b10f80a2d106dcfb7a3289da555..7f991a3380201fe4c9f66e9c99604dc16b467f73 100644 (file)
@@ -9,11 +9,11 @@ git-mv - Move or rename a file, a directory, or a symlink
 SYNOPSIS
 --------
 [verse]
-'git mv' <options>... <args>...
+'git mv' [<options>] <source>... <destination>
 
 DESCRIPTION
 -----------
-Move or rename a file, directory or symlink.
+Move or rename a file, directory, or symlink.
 
  git mv [-v] [-f] [-n] [-k] <source> <destination>
  git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>
@@ -30,7 +30,7 @@ OPTIONS
 -------
 -f::
 --force::
-       Force renaming or moving of a file even if the target exists
+       Force renaming or moving of a file even if the <destination> exists.
 -k::
        Skip move or rename actions which would lead to an error
        condition. An error happens when a source is neither existing nor
index ec8a27ce8bf8d689bb5f64554dd5426ce38262c0..d4f1c4d5945e8ed38c243ecee0920bd6e38219a5 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git name-rev' [--tags] [--refs=<pattern>]
-              ( --all | --stdin | <commit-ish>... )
+              ( --all | --annotate-stdin | <commit-ish>... )
 
 DESCRIPTION
 -----------
@@ -26,7 +26,7 @@ OPTIONS
 
 --refs=<pattern>::
        Only use refs whose names match a given shell pattern.  The pattern
-       can be one of branch name, tag name or fully qualified ref name. If
+       can be a branch name, a tag name, or a fully qualified ref name. If
        given multiple times, use refs whose names match any of the given shell
        patterns. Use `--no-refs` to clear any previous ref patterns given.
 
@@ -46,7 +46,8 @@ OPTIONS
        Transform stdin by substituting all the 40-character SHA-1
        hexes (say $hex) with "$hex ($rev_name)".  When used with
        --name-only, substitute with "$rev_name", omitting $hex
-       altogether.
+       altogether. This option was called `--stdin` in older versions
+       of Git.
 +
 For example:
 +
@@ -70,10 +71,6 @@ The full name after substitution is master,
 while its tree object is 70d105cc79e63b81cfdcb08a15297c23e60b07ad
 -----------
 
---stdin::
-       This option is deprecated in favor of 'git name-rev --annotate-stdin'.
-       They are functionally equivalent.
-
 --name-only::
        Instead of printing both the SHA-1 and the name, print only
        the name.  If given with --tags the usual tag prefix of
@@ -107,7 +104,7 @@ Now you are wiser, because you know that it happened 940 revisions before v0.99.
 Another nice thing you can do is:
 
 ------------
-% git log | git name-rev --stdin
+% git log | git name-rev --annotate-stdin
 ------------
 
 GIT
index efbc10f0f598eccd247f13c3e5d1ecf9473a6f78..f8310e56a85ab0f6f211d68b1886ec68ae126b9f 100644 (file)
@@ -9,10 +9,10 @@ SYNOPSIS
 --------
 [verse]
 'git notes' [list [<object>]]
-'git notes' add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' add [-f] [--allow-empty] [--[no-]separator | --separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
 'git notes' copy [-f] ( --stdin | <from-object> [<to-object>] )
-'git notes' append [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
-'git notes' edit [--allow-empty] [<object>]
+'git notes' append [--allow-empty] [--[no-]separator | --separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' edit [--allow-empty] [<object>] [--[no-]stripspace]
 'git notes' show [<object>]
 'git notes' merge [-v | -q] [-s <strategy> ] <notes-ref>
 'git notes' merge --commit [-v | -q]
@@ -65,7 +65,9 @@ add::
        However, if you're using `add` interactively (using an editor
        to supply the notes contents), then - instead of aborting -
        the existing notes will be opened in the editor (like the `edit`
-       subcommand).
+       subcommand). If you specify multiple `-m` and `-F`, a blank
+       line will be inserted between the messages. Use the `--separator`
+       option to insert other delimiters.
 
 copy::
        Copy the notes for the first object onto the second object (defaults to
@@ -85,8 +87,12 @@ corresponding <to-object>.  (The optional `<rest>` is ignored so that
 the command can read the input given to the `post-rewrite` hook.)
 
 append::
-       Append to the notes of an existing object (defaults to HEAD).
-       Creates a new notes object if needed.
+       Append new message(s) given by `-m` or `-F` options to an
+       existing note, or add them as a new note if one does not
+       exist, for the object (defaults to HEAD).  When appending to
+       an existing note, a blank line is added before each new
+       message as an inter-paragraph separator.  The separator can
+       be customized with the `--separator` option.
 
 edit::
        Edit the notes for a given object (defaults to HEAD).
@@ -136,6 +142,7 @@ OPTIONS
        are concatenated as separate paragraphs.
        Lines starting with `#` and empty lines other than a
        single line between paragraphs will be stripped out.
+       If you wish to keep them verbatim, use `--no-stripspace`.
 
 -F <file>::
 --file=<file>::
@@ -143,12 +150,16 @@ OPTIONS
        read the note message from the standard input.
        Lines starting with `#` and empty lines other than a
        single line between paragraphs will be stripped out.
+       If you wish to keep them verbatim, use `--no-stripspace`.
 
 -C <object>::
 --reuse-message=<object>::
        Take the given blob object (for example, another note) as the
        note message. (Use `git notes copy <object>` instead to
-       copy notes between objects.)
+       copy notes between objects.).  By default, message will be
+       copied verbatim, but if you wish to strip out the lines
+       starting with `#` and empty lines other than a single line
+       between paragraphs, use with`--stripspace` option.
 
 -c <object>::
 --reedit-message=<object>::
@@ -159,6 +170,19 @@ OPTIONS
        Allow an empty note object to be stored. The default behavior is
        to automatically remove empty notes.
 
+--[no-]separator, --separator=<paragraph-break>::
+       Specify a string used as a custom inter-paragraph separator
+       (a newline is added at the end as needed). If `--no-separator`, no
+       separators will be added between paragraphs.  Defaults to a blank
+       line.
+
+--[no-]stripspace::
+       Strip leading and trailing whitespace from the note message.
+       Also strip out empty lines other than a single line between
+       paragraphs. Lines starting with `#` will be stripped out
+       in non-editor cases like `-m`, `-F` and `-C`, but not in
+       editor case like `git notes edit`, `-c`, etc.
+
 --ref <ref>::
        Manipulate the notes tree in <ref>.  This overrides
        `GIT_NOTES_REF` and the "core.notesRef" configuration.  The ref
index a9995a932ca2d246db2bf756792c92b2431970eb..e32404c6aaee30f39f3f128839171601999bccb3 100644 (file)
@@ -116,9 +116,7 @@ unreachable object whose mtime is newer than the `--cruft-expiration`).
 +
 Incompatible with `--unpack-unreachable`, `--keep-unreachable`,
 `--pack-loose-unreachable`, `--stdin-packs`, as well as any other
-options which imply `--revs`. Also incompatible with `--max-pack-size`;
-when this option is set, the maximum pack size is not inferred from
-`pack.packSizeLimit`.
+options which imply `--revs`.
 
 --cruft-expiration=<approxidate>::
        If specified, objects are eliminated from the cruft pack if they
@@ -298,8 +296,8 @@ So does `git bundle` (see linkgit:git-bundle[1]) when it creates a bundle.
        nevertheless.
 
 --filter=<filter-spec>::
-       Requires `--stdout`.  Omits certain objects (usually blobs) from
-       the resulting packfile.  See linkgit:git-rev-list[1] for valid
+       Omits certain objects (usually blobs) from the resulting
+       packfile.  See linkgit:git-rev-list[1] for valid
        `<filter-spec>` forms.
 
 --no-filter::
index ee7034b5e52d2e9711196f4197ac3520cfdad0e0..13c3eb5ec96c94970171c96f0c503cba418ab1d8 100644 (file)
@@ -9,7 +9,21 @@ git-pack-redundant - Find redundant pack files
 SYNOPSIS
 --------
 [verse]
-'git pack-redundant' [ --verbose ] [ --alt-odb ] ( --all | <pack-filename>... )
+'git pack-redundant' [--verbose] [--alt-odb] (--all | <pack-filename>...)
+
+WARNING
+-------
+`git pack-redundant` has been deprecated and is scheduled for removal in
+a future version of Git. Because it can only remove entire duplicate
+packs and not individual duplicate objects, it is generally not a useful
+tool for reducing repository size. You are better off using `git gc` to
+do so, which will put objects into a new pack, removing duplicates.
+
+Running `pack-redundant` without the `--i-still-use-this` flag will fail
+in this release. If you believe you have a use case for which
+`pack-redundant` is better suited and oppose this removal, please
+contact the Git mailing list at git@vger.kernel.org. More information
+about the list is available at https://git-scm.com/community.
 
 DESCRIPTION
 -----------
@@ -34,7 +48,7 @@ OPTIONS
 
 --alt-odb::
        Don't require objects present in packs from alternate object
-       directories to be present in local packs.
+       database (odb) directories to be present in local packs.
 
 --verbose::
        Outputs some statistics to stderr. Has a small performance penalty.
index 154081f2de269975a09c1f5899dc7d5948c54ca7..284956acb3c5e8bb168626c9729a17a799c0afd7 100644 (file)
@@ -8,7 +8,7 @@ git-pack-refs - Pack heads and tags for efficient repository access
 SYNOPSIS
 --------
 [verse]
-'git pack-refs' [--all] [--no-prune]
+'git pack-refs' [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]
 
 DESCRIPTION
 -----------
@@ -51,14 +51,37 @@ The command by default packs all tags and refs that are already
 packed, and leaves other refs
 alone.  This is because branches are expected to be actively
 developed and packing their tips does not help performance.
-This option causes branch tips to be packed as well.  Useful for
-a repository with many branches of historical interests.
+This option causes all refs to be packed as well, with the exception
+of hidden refs, broken refs, and symbolic refs. Useful for a repository
+with many branches of historical interests.
 
 --no-prune::
 
 The command usually removes loose refs under `$GIT_DIR/refs`
 hierarchy after packing them.  This option tells it not to.
 
+--include <pattern>::
+
+Pack refs based on a `glob(7)` pattern. Repetitions of this option
+accumulate inclusion patterns. If a ref is both included in `--include` and
+`--exclude`, `--exclude` takes precedence. Using `--include` will preclude all
+tags from being included by default. Symbolic refs and broken refs will never
+be packed. When used with `--all`, it will be a noop. Use `--no-include` to clear
+and reset the list of patterns.
+
+--exclude <pattern>::
+
+Do not pack refs matching the given `glob(7)` pattern. Repetitions of this option
+accumulate exclusion patterns. Use `--no-exclude` to clear and reset the list of
+patterns. If a ref is already packed, including it with `--exclude` will not
+unpack it.
+
+When used with `--all`, pack only loose refs which do not match any of
+the provided `--exclude` patterns.
+
+When used with `--include`, refs provided to `--include`, minus refs that are
+provided to `--exclude` will be packed.
+
 
 BUGS
 ----
index 442caff8a9c394168839e63f4612c9083386175a..1d15fa45d5126f236de4018268725c627c993fe2 100644 (file)
@@ -8,18 +8,18 @@ git-patch-id - Compute unique ID for a patch
 SYNOPSIS
 --------
 [verse]
-'git patch-id' [--stable | --unstable]
+'git patch-id' [--stable | --unstable | --verbatim]
 
 DESCRIPTION
 -----------
 Read a patch from the standard input and compute the patch ID for it.
 
 A "patch ID" is nothing but a sum of SHA-1 of the file diffs associated with a
-patch, with whitespace and line numbers ignored.  As such, it's "reasonably
-stable", but at the same time also reasonably unique, i.e., two patches that
-have the same "patch ID" are almost guaranteed to be the same thing.
+patch, with line numbers ignored.  As such, it's "reasonably stable", but at
+the same time also reasonably unique, i.e., two patches that have the same
+"patch ID" are almost guaranteed to be the same thing.
 
-IOW, you can use this thing to look for likely duplicate commits.
+The main usecase for this command is to look for likely duplicate commits.
 
 When dealing with 'git diff-tree' output, it takes advantage of
 the fact that the patch is prefixed with the object name of the
@@ -30,6 +30,12 @@ This can be used to make a mapping from patch ID to commit ID.
 OPTIONS
 -------
 
+--verbatim::
+       Calculate the patch-id of the input as it is given, do not strip
+       any whitespace.
+
+       This is the default if patchid.verbatim is true.
+
 --stable::
        Use a "stable" sum of hashes as the patch ID. With this option:
         - Reordering file diffs that make up a patch does not affect the ID.
@@ -45,14 +51,16 @@ OPTIONS
           of "-O<orderfile>", thereby making existing databases storing such
           "unstable" or historical patch-ids unusable.
 
+        - All whitespace within the patch is ignored and does not affect the id.
+
        This is the default if patchid.stable is set to true.
 
 --unstable::
        Use an "unstable" hash as the patch ID. With this option,
        the result produced is compatible with the patch-id value produced
-       by git 1.9 and older.  Users with pre-existing databases storing
-       patch-ids produced by git 1.9 and older (who do not deal with reordered
-       patches) may want to use this option.
+       by git 1.9 and older and whitespace is ignored.  Users with pre-existing
+       databases storing patch-ids produced by git 1.9 and older (who do not deal
+       with reordered patches) may want to use this option.
 
        This is the default.
 
index 9fed59a31724c4dccd7364c03c32c72d9c8d4664..db742dcfeea84f0afe1eef8a4fdeb939b4e0dadf 100644 (file)
@@ -9,13 +9,13 @@ git-prune-packed - Remove extra objects that are already in pack files
 SYNOPSIS
 --------
 [verse]
-'git prune-packed' [-n|--dry-run] [-q|--quiet]
+'git prune-packed' [-n | --dry-run] [-q | --quiet]
 
 
 DESCRIPTION
 -----------
 This program searches the `$GIT_OBJECT_DIRECTORY` for all objects that currently
-exist in a pack file as well as the independent object directories.
+exist in a pack file as well as in the independent object directories.
 
 All such extra objects are removed.
 
index 03552dd86fc412b622aff2bcf8feda8e71711b3e..9a45571b901b1579e6c377a73359d7e4002438db 100644 (file)
@@ -18,7 +18,7 @@ NOTE: In most cases, users should run 'git gc', which calls
 'git prune'. See the section "NOTES", below.
 
 This runs 'git fsck --unreachable' using all the refs
-available in `refs/`, optionally with additional set of
+available in `refs/`, optionally with an additional set of
 objects specified on the command line, and prunes all unpacked
 objects unreachable from any of these head objects from the object database.
 In addition, it
index def7657ef9cb71b5da3eb36b2b8f439f172651a3..9b7cfbc5c1d8496c2649b4d6375e699b546fa6c5 100644 (file)
@@ -9,8 +9,8 @@ git-push - Update remote refs along with associated objects
 SYNOPSIS
 --------
 [verse]
-'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
-          [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
+'git push' [--all | --branches | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
+          [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-q | --quiet] [-v | --verbose]
           [-u | --set-upstream] [-o <string> | --push-option=<string>]
           [--[no-]signed|--signed=(true|false|if-asked)]
           [--force-with-lease[=<refname>[:<expect>]] [--force-if-includes]]
@@ -37,7 +37,7 @@ the default `<refspec>` by consulting `remote.*.push` configuration,
 and if it is not found, honors `push.default` configuration to decide
 what to push (See linkgit:git-config[1] for the meaning of `push.default`).
 
-When neither the command-line nor the configuration specify what to
+When neither the command-line nor the configuration specifies what to
 push, the default behavior is used, which corresponds to the `simple`
 value for `push.default`: the current branch is pushed to the
 corresponding upstream branch, but as a safety measure, the push is
@@ -48,7 +48,7 @@ local one.
 OPTIONS[[OPTIONS]]
 ------------------
 <repository>::
-       The "remote" repository that is destination of a push
+       The "remote" repository that is the destination of a push
        operation.  This parameter can be either a URL
        (see the section <<URLS,GIT URLS>> below) or the name
        of a remote (see the section <<REMOTES,REMOTES>> below).
@@ -147,6 +147,7 @@ already exists on the remote side.
 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
 
 --all::
+--branches::
        Push all branches (i.e. refs under `refs/heads/`); cannot be
        used with other <refspec>.
 
@@ -409,10 +410,14 @@ Specifying `--no-force-if-includes` disables this behavior.
        all submodules that changed in the revisions to be pushed will be
        pushed. If on-demand was not able to push all necessary revisions it will
        also be aborted and exit with non-zero status. If 'only' is used all
-       submodules will be recursively pushed while the superproject is left
+       submodules will be pushed while the superproject is left
        unpushed. A value of 'no' or using `--no-recurse-submodules` can be used
        to override the push.recurseSubmodules configuration variable when no
        submodule recursion is required.
++
+When using 'on-demand' or 'only', if a submodule has a
+"push.recurseSubmodules={on-demand,only}" or "submodule.recurse" configuration,
+further recursion will occur. In this case, "only" is treated as "on-demand".
 
 --[no-]verify::
        Toggle the pre-push hook (see linkgit:githooks[5]).  The
index 70562dc4c0235d53501bab56ff98af6169b8f968..40e02d92eb2419c32be66a69afb353c6d384c717 100644 (file)
@@ -38,14 +38,14 @@ OPTIONS
        a patch.  At the time of this writing only missing author
        information is warned about.
 
---author Author Name <Author Email>::
+--author 'Author Name <Author Email>'::
        The author name and email address to use when no author
        information can be found in the patch description.
 
 --patches <dir>::
        The directory to find the quilt patches.
 +
-The default for the patch directory is patches
+The default for the patch directory is 'patches'
 or the value of the `$QUILT_PATCHES` environment
 variable.
 
index 0b393715d707015a245eb1afe26dfbe044e8fcd6..fbdbe0befebab63b0316a29aaf420f9538cce43c 100644 (file)
@@ -70,7 +70,7 @@ to revert to color all lines according to the outer diff markers
        Defaults to 60. Try a larger value if `git range-diff` erroneously
        considers a large change a total rewrite (deletion of one commit
        and addition of another), and a smaller one in the reverse case.
-       See the ``Algorithm`` section below for an explanation why this is
+       See the ``Algorithm`` section below for an explanation of why this is
        needed.
 
 --left-only::
@@ -166,7 +166,7 @@ A typical output of `git range-diff` would look like this:
 
 In this example, there are 3 old and 3 new commits, where the developer
 removed the 3rd, added a new one before the first two, and modified the
-commit message of the 2nd commit as well its diff.
+commit message of the 2nd commit as well as its diff.
 
 When the output goes to a terminal, it is color-coded by default, just
 like regular `git diff`'s output. In addition, the first line (adding a
index b9bfdc0a319a996c2f1ad9e256dab0f8eac8a0fb..1c48c289963063c2e0db605bbb27c35dd803270c 100644 (file)
@@ -9,7 +9,7 @@ git-read-tree - Reads tree information into the index
 SYNOPSIS
 --------
 [verse]
-'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
+'git read-tree' [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>)
                [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]
                (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])
 
@@ -25,15 +25,15 @@ fast-forward (i.e. 2-way) merge, or a 3-way merge, with the `-m`
 flag.  When used with `-m`, the `-u` flag causes it to also update
 the files in the work tree with the result of the merge.
 
-Trivial merges are done by 'git read-tree' itself.  Only conflicting paths
-will be in unmerged state when 'git read-tree' returns.
+Only trivial merges are done by 'git read-tree' itself.  Only conflicting paths
+will be in an unmerged state when 'git read-tree' returns.
 
 OPTIONS
 -------
 -m::
        Perform a merge, not just a read.  The command will
        refuse to run if your index file has unmerged entries,
-       indicating that you have not finished previous merge you
+       indicating that you have not finished previous merge you
        started.
 
 --reset::
@@ -219,7 +219,7 @@ see which of the "local changes" that you made were carried forward by running
 `git diff-index --cached $M`.  Note that this does not
 necessarily match what `git diff-index --cached $H` would have
 produced before such a two tree merge.  This is because of cases
-18 and 19 --- if you already had the changes in $M (e.g. maybe
+18 and 19 -- if you already had the changes in $M (e.g. maybe
 you picked it up via e-mail in a patch form), `git diff-index
 --cached $H` would have told you about the change before this
 merge, but it would not show in `git diff-index --cached $M`
index 9cb8931c7ac8e89e1db036446044633cac00bb39..8a8d32161b4455feead4bd43e251e43041a24dd0 100644 (file)
@@ -38,6 +38,13 @@ The current branch is reset to `<upstream>` or `<newbase>` if the
 `git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set
 to point at the tip of the branch before the reset.
 
+[NOTE]
+`ORIG_HEAD` is not guaranteed to still point to the previous branch tip
+at the end of the rebase if other commands that write that pseudo-ref
+(e.g. `git reset`) are used during the rebase. The previous branch tip,
+however, is accessible using the reflog of the current branch
+(i.e. `@{1}`, see linkgit:gitrevisions[7]).
+
 The commits that were previously saved into the temporary area are
 then reapplied to the current branch, one by one, in order. Note that
 any commits in `HEAD` which introduce the same textual changes as a commit
@@ -201,6 +208,39 @@ Alternatively, you can undo the 'git rebase' with
 
     git rebase --abort
 
+MODE OPTIONS
+------------
+
+The options in this section cannot be used with any other option,
+including not with each other:
+
+--continue::
+       Restart the rebasing process after having resolved a merge conflict.
+
+--skip::
+       Restart the rebasing process by skipping the current patch.
+
+--abort::
+       Abort the rebase operation and reset HEAD to the original
+       branch. If `<branch>` was provided when the rebase operation was
+       started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD`
+       will be reset to where it was when the rebase operation was
+       started.
+
+--quit::
+       Abort the rebase operation but `HEAD` is not reset back to the
+       original branch. The index and working tree are also left
+       unchanged as a result. If a temporary stash entry was created
+       using `--autostash`, it will be saved to the stash list.
+
+--edit-todo::
+       Edit the todo list during an interactive rebase.
+
+--show-current-patch::
+       Show the current patch in an interactive rebase or when rebase
+       is stopped because of conflicts. This is the equivalent of
+       `git show REBASE_HEAD`.
+
 OPTIONS
 -------
 --onto <newbase>::
@@ -218,12 +258,14 @@ leave out at most one of A and B, in which case it defaults to HEAD.
        merge base of `<upstream>` and `<branch>`. Running
        `git rebase --keep-base <upstream> <branch>` is equivalent to
        running
-       `git rebase --onto <upstream>...<branch> <upstream> <branch>`.
+       `git rebase --reapply-cherry-picks --no-fork-point --onto <upstream>...<branch> <upstream> <branch>`.
 +
 This option is useful in the case where one is developing a feature on
 top of an upstream branch. While the feature is being worked on, the
 upstream branch may advance and it may not be the best idea to keep
-rebasing on top of the upstream but to keep the base commit as-is.
+rebasing on top of the upstream but to keep the base commit as-is. As
+the base commit is unchanged this option implies `--reapply-cherry-picks`
+to avoid losing commits.
 +
 Although both this option and `--fork-point` find the merge base between
 `<upstream>` and `<branch>`, this option uses the merge base as the _starting
@@ -240,22 +282,6 @@ See also INCOMPATIBLE OPTIONS below.
 <branch>::
        Working branch; defaults to `HEAD`.
 
---continue::
-       Restart the rebasing process after having resolved a merge conflict.
-
---abort::
-       Abort the rebase operation and reset HEAD to the original
-       branch. If `<branch>` was provided when the rebase operation was
-       started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD`
-       will be reset to where it was when the rebase operation was
-       started.
-
---quit::
-       Abort the rebase operation but `HEAD` is not reset back to the
-       original branch. The index and working tree are also left
-       unchanged as a result. If a temporary stash entry was created
-       using `--autostash`, it will be saved to the stash list.
-
 --apply::
        Use applying strategies to rebase (calling `git-am`
        internally).  This option may become a no-op in the future
@@ -263,7 +289,7 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty={drop,keep,ask}::
+--empty=(drop|keep|ask)::
        How to handle commits that are not empty to start and are not
        clean cherry-picks of any upstream commit, but which become
        empty after rebasing (because they contain a subset of already
@@ -278,7 +304,8 @@ See also INCOMPATIBLE OPTIONS below.
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
 by `git log --cherry-mark ...`) are detected and dropped as a
-preliminary step (unless `--reapply-cherry-picks` is passed).
+preliminary step (unless `--reapply-cherry-picks` or `--keep-base` is
+passed).
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -311,13 +338,14 @@ See also INCOMPATIBLE OPTIONS below.
        upstream changes, the behavior towards them is controlled by
        the `--empty` flag.)
 +
-By default (or if `--no-reapply-cherry-picks` is given), these commits
-will be automatically dropped.  Because this necessitates reading all
-upstream commits, this can be expensive in repos with a large number
-of upstream commits that need to be read.  When using the 'merge'
-backend, warnings will be issued for each dropped commit (unless
-`--quiet` is given). Advice will also be issued unless
-`advice.skippedCherryPicks` is set to false (see linkgit:git-config[1]).
+In the absence of `--keep-base` (or if `--no-reapply-cherry-picks` is
+given), these commits will be automatically dropped.  Because this
+necessitates reading all upstream commits, this can be expensive in
+repositories with a large number of upstream commits that need to be
+read. When using the 'merge' backend, warnings will be issued for each
+dropped commit (unless `--quiet` is given). Advice will also be issued
+unless `advice.skippedCherryPicks` is set to false (see
+linkgit:git-config[1]).
 +
 `--reapply-cherry-picks` allows rebase to forgo reading all upstream
 commits, potentially improving performance.
@@ -332,17 +360,6 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---skip::
-       Restart the rebasing process by skipping the current patch.
-
---edit-todo::
-       Edit the todo list during an interactive rebase.
-
---show-current-patch::
-       Show the current patch in an interactive rebase or when rebase
-       is stopped because of conflicts. This is the equivalent of
-       `git show REBASE_HEAD`.
-
 -m::
 --merge::
        Using merging strategies to rebase (default).
@@ -443,9 +460,9 @@ When `--fork-point` is active, 'fork_point' will be used instead of
 <branch>` command (see linkgit:git-merge-base[1]).  If 'fork_point'
 ends up being empty, the `<upstream>` will be used as a fallback.
 +
-If `<upstream>` is given on the command line, then the default is
-`--no-fork-point`, otherwise the default is `--fork-point`. See also
-`rebase.forkpoint` in linkgit:git-config[1].
+If `<upstream>` or `--keep-base` is given on the command line, then
+the default is `--no-fork-point`, otherwise the default is
+`--fork-point`. See also `rebase.forkpoint` in linkgit:git-config[1].
 +
 If your branch was based on `<upstream>` but `<upstream>` was rewound and
 your branch contains commits which were dropped, this option can be used
@@ -506,26 +523,31 @@ See also INCOMPATIBLE OPTIONS below.
 +
 The commit list format can be changed by setting the configuration option
 rebase.instructionFormat.  A customized instruction format will automatically
-have the long commit hash prepended to the format.
+have the commit hash prepended to the format.
 +
 See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
        By default, a rebase will simply drop merge commits from the todo
        list, and put the rebased commits into a single, linear branch.
        With `--rebase-merges`, the rebase will instead try to preserve
        the branching structure within the commits that are to be rebased,
        by recreating the merge commits. Any resolved merge conflicts or
        manual amendments in these merge commits will have to be
-       resolved/re-applied manually.
+       resolved/re-applied manually. `--no-rebase-merges` can be used to
+       countermand both the `rebase.rebaseMerges` config option and a previous
+       `--rebase-merges`.
 +
-By default, or when `no-rebase-cousins` was specified, commits which do not
-have `<upstream>` as direct ancestor will keep their original branch point,
-i.e. commits that would be excluded by linkgit:git-log[1]'s
-`--ancestry-path` option will keep their original ancestry by default. If
-the `rebase-cousins` mode is turned on, such commits are instead rebased
-onto `<upstream>` (or `<onto>`, if specified).
+When rebasing merges, there are two modes: `rebase-cousins` and
+`no-rebase-cousins`. If the mode is not specified, it defaults to
+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+`<upstream>` as direct ancestor will keep their original branch point, i.e.
+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+option will keep their original ancestry by default. In `rebase-cousins` mode,
+such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+specified).
 +
 It is currently only possible to recreate the merge commits using the
 `ort` merge strategy; different merge strategies can be used only via
@@ -561,30 +583,33 @@ See also INCOMPATIBLE OPTIONS below.
 --root::
        Rebase all commits reachable from `<branch>`, instead of
        limiting them with an `<upstream>`.  This allows you to rebase
-       the root commit(s) on a branch.  When used with `--onto`, it
-       will skip changes already contained in `<newbase>` (instead of
-       `<upstream>`) whereas without `--onto` it will operate on every
-       change.
+       the root commit(s) on a branch.
 +
 See also INCOMPATIBLE OPTIONS below.
 
 --autosquash::
 --no-autosquash::
-       When the commit log message begins with "squash! ..." or "fixup! ..."
-       or "amend! ...", and there is already a commit in the todo list that
-       matches the same `...`, automatically modify the todo list of
-       `rebase -i`, so that the commit marked for squashing comes right after
-       the commit to be modified, and change the action of the moved commit
-       from `pick` to `squash` or `fixup` or `fixup -C` respectively. A commit
-       matches the `...` if the commit subject matches, or if the `...` refers
-       to the commit's hash. As a fall-back, partial matches of the commit
-       subject work, too. The recommended way to create fixup/amend/squash
-       commits is by using the `--fixup`, `--fixup=amend:` or `--fixup=reword:`
-       and `--squash` options respectively of linkgit:git-commit[1].
+       Automatically squash commits with specially formatted messages into
+       previous commits being rebased.  If a commit message starts with
+       "squash! ", "fixup! " or "amend! ", the remainder of the subject line
+       is taken as a commit specifier, which matches a previous commit if it
+       matches the subject line or the hash of that commit.  If no commit
+       matches fully, matches of the specifier with the start of commit
+       subjects are considered.
++
+In the rebase todo list, the actions of squash, fixup and amend commits are
+changed from `pick` to `squash`, `fixup` or `fixup -C`, respectively, and they
+are moved right after the commit they modify.  The `--interactive` option can
+be used to review and edit the todo list before proceeding.
++
+The recommended way to create commits with squash markers is by using the
+`--squash`, `--fixup`, `--fixup=amend:` or `--fixup=reword:` options of
+linkgit:git-commit[1], which take the target commit as an argument and
+automatically fill in the subject line of the new commit from that.
 +
-If the `--autosquash` option is enabled by default using the
-configuration variable `rebase.autoSquash`, this option can be
-used to override and disable this setting.
+Settting configuration variable `rebase.autoSquash` to true enables
+auto-squashing by default for interactive rebase.  The `--no-autosquash`
+option can be used to override that setting.
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -601,13 +626,16 @@ See also INCOMPATIBLE OPTIONS below.
        Automatically reschedule `exec` commands that failed. This only makes
        sense in interactive mode (or when an `--exec` option was provided).
 +
-Even though this option applies once a rebase is started, it's set for
-the whole rebase at the start based on either the
-`rebase.rescheduleFailedExec` configuration (see linkgit:git-config[1]
-or "CONFIGURATION" below) or whether this option is
-provided. Otherwise an explicit `--no-reschedule-failed-exec` at the
-start would be overridden by the presence of
-`rebase.rescheduleFailedExec=true` configuration.
+This option applies once a rebase is started. It is preserved for the whole
+rebase based on, in order, the command line option provided to the initial `git
+rebase`, the `rebase.rescheduleFailedExec` configuration (see
+linkgit:git-config[1] or "CONFIGURATION" below), or it defaults to false.
++
+Recording this option for the whole rebase is a convenience feature. Otherwise
+an explicit `--no-reschedule-failed-exec` at the start would be overridden by
+the presence of a `rebase.rescheduleFailedExec=true` configuration when `git
+rebase --continue` is invoked. Currently, you cannot pass
+`--[no-]reschedule-failed-exec` to `git rebase --continue`.
 
 --update-refs::
 --no-update-refs::
@@ -617,6 +645,8 @@ start would be overridden by the presence of
 +
 If the configuration variable `rebase.updateRefs` is set, then this option
 can be used to override and disable this setting.
++
+See also INCOMPATIBLE OPTIONS below.
 
 INCOMPATIBLE OPTIONS
 --------------------
@@ -632,17 +662,15 @@ are incompatible with the following options:
  * --merge
  * --strategy
  * --strategy-option
- * --allow-empty-message
- * --[no-]autosquash
+ * --autosquash
  * --rebase-merges
  * --interactive
  * --exec
  * --no-keep-empty
  * --empty=
- * --reapply-cherry-picks
- * --edit-todo
+ * --[no-]reapply-cherry-picks when used without --keep-base
  * --update-refs
- * --root when used in combination with --onto
+ * --root when used without --onto
 
 In addition, the following pairs of options are incompatible:
 
@@ -676,7 +704,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty={drop,keep,ask}` option for changing the behavior
+also has an `--empty=(drop|keep|ask)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
index 014a78409b9473c10f62f85b21f9d9c6642ff844..20aca92073d8c9d65fc78ce5f3f48a02239333f5 100644 (file)
@@ -9,7 +9,7 @@ git-receive-pack - Receive what is pushed into the repository
 SYNOPSIS
 --------
 [verse]
-'git-receive-pack' <directory>
+'git receive-pack' <git-dir>
 
 DESCRIPTION
 -----------
@@ -18,10 +18,10 @@ information fed from the remote end.
 
 This command is usually not invoked directly by the end user.
 The UI for the protocol is on the 'git send-pack' side, and the
-program pair is meant to be used to push updates to remote
+program pair is meant to be used to push updates to remote
 repository.  For pull operations, see linkgit:git-fetch-pack[1].
 
-The command allows for creation and fast-forwarding of sha1 refs
+The command allows for the creation and fast-forwarding of sha1 refs
 (heads/tags) on the remote end (strictly speaking, it is the
 local end 'git-receive-pack' runs, but to the user who is sitting at
 the send-pack end, it is updating the remote.  Confused?)
@@ -38,7 +38,7 @@ its behavior, see linkgit:git-config[1].
 
 OPTIONS
 -------
-<directory>::
+<git-dir>::
        The repository to sync into.
 
 --http-backend-info-refs::
index db9d46edfa950e8107f54b4f61dda5115d5c07e4..ec64cbff4c6529d23fcf885ed5fa6f018404952f 100644 (file)
@@ -9,15 +9,7 @@ git-reflog - Manage reflog information
 SYNOPSIS
 --------
 [verse]
-'git reflog' <subcommand> <options>
-
-DESCRIPTION
------------
-The command takes various subcommands, and different options
-depending on the subcommand:
-
-[verse]
-'git reflog' ['show'] [<log-options>] [<ref>]
+'git reflog' [show] [<log-options>] [<ref>]
 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
        [--rewrite] [--updateref] [--stale-fix]
        [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
@@ -25,6 +17,10 @@ depending on the subcommand:
        [--dry-run | -n] [--verbose] <ref>@{<specifier>}...
 'git reflog exists' <ref>
 
+DESCRIPTION
+-----------
+This command manages the information recorded in the reflogs.
+
 Reference logs, or "reflogs", record when the tips of branches and
 other references were updated in the local repository. Reflogs are
 useful in various Git commands, to specify the old value of a
@@ -33,7 +29,8 @@ moves ago", `master@{one.week.ago}` means "where master used to point
 to one week ago in this local repository", and so on. See
 linkgit:gitrevisions[7] for more details.
 
-This command manages the information recorded in the reflogs.
+The command takes various subcommands, and different options
+depending on the subcommand:
 
 The "show" subcommand (which is also the default, in the absence of
 any subcommands) shows the log of the reference provided in the
index 88ea7e1cc01201ccf68ec1e2b8ccd2cda633903a..b33ee3c9e863b6bb3e5924ee021aeaa9b408466b 100644 (file)
@@ -44,15 +44,15 @@ The following sequences have a special meaning:
        This argument will not be passed to '<command>'. Instead, it
        will cause the helper to start by sending git:// service requests to
        the remote side with the service field set to an appropriate value and
-       the repository field set to rest of the argument. Default is not to send
+       the repository field set to the rest of the argument. Default is not to send
        such a request.
 +
-This is useful if remote side is git:// server accessed over
+This is useful if the remote side is git:// server accessed over
 some tunnel.
 
 '%V' (must be first characters in argument)::
        This argument will not be passed to '<command>'. Instead it sets
-       the vhost field in the git:// service request (to rest of the argument).
+       the vhost field in the git:// service request (to the rest of the argument).
        Default is not to send vhost in such request (if sent).
 
 ENVIRONMENT VARIABLES
@@ -82,12 +82,12 @@ begins with `ext::`.  Examples:
 
 "ext::ssh -i /home/foo/.ssh/somekey user&#64;host.example %S 'foo/repo'"::
        Like host.example:foo/repo, but use /home/foo/.ssh/somekey as
-       keypair and user as user on remote side. This avoids needing to
+       keypair and user as the user on the remote side. This avoids the need to
        edit .ssh/config.
 
 "ext::socat -t3600 - ABSTRACT-CONNECT:/git-server %G/somerepo"::
        Represents repository with path /somerepo accessible over
-       git protocol at abstract namespace address /git-server.
+       git protocol at the abstract namespace address /git-server.
 
 "ext::git-server-alias foo %G/repo"::
        Represents a repository with path /repo accessed using the
index 0451ceb8a26dfc0b4ef06dced69a2005bc4e5448..1dd2648a7904bb73c626019e9ab66e0db009ecf5 100644 (file)
@@ -13,19 +13,19 @@ DESCRIPTION
 -----------
 This helper uses specified file descriptors to connect to a remote Git server.
 This is not meant for end users but for programs and scripts calling git
-fetch, push or archive.
+fetch, push, or archive.
 
 If only <infd> is given, it is assumed to be a bidirectional socket connected
-to remote Git server (git-upload-pack, git-receive-pack or
+to a remote Git server (git-upload-pack, git-receive-pack, or
 git-upload-archive). If both <infd> and <outfd> are given, they are assumed
 to be pipes connected to a remote Git server (<infd> being the inbound pipe
-and <outfd> being the outbound pipe.
+and <outfd> being the outbound pipe).
 
 It is assumed that any handshaking procedures have already been completed
 (such as sending service request for git://) before this helper is started.
 
 <anything> can be any string. It is ignored. It is meant for providing
-information to user in the URL in case that URL is displayed in some
+information to the user in the URL in case that URL is displayed in some
 context.
 
 ENVIRONMENT VARIABLES
@@ -45,7 +45,7 @@ EXAMPLES
 `git push fd::7,8 master (as URL)`::
        Push master, using file descriptor #7 to read data from
        git-receive-pack and file descriptor #8 to write data to
-       same service.
+       the same service.
 
 `git push fd::7,8/bar master`::
        Same as above.
index 0bf13893d8147d872dbedeed9def497b2a5a0fea..c902512a9e89b07446a606b95cdff52ecc385c44 100644 (file)
@@ -74,6 +74,23 @@ to the new separate pack will be written.
        immediately instead of waiting for the next `git gc` invocation.
        Only useful with `--cruft -d`.
 
+--max-cruft-size=<n>::
+       Repack cruft objects into packs as large as `<n>` bytes before
+       creating new packs. As long as there are enough cruft packs
+       smaller than `<n>`, repacking will cause a new cruft pack to
+       be created containing objects from any combined cruft packs,
+       along with any new unreachable objects. Cruft packs larger than
+       `<n>` will not be modified. When the new cruft pack is larger
+       than `<n>` bytes, it will be split into multiple packs, all of
+       which are guaranteed to be at most `<n>` bytes in size. Only
+       useful with `--cruft -d`.
+
+--expire-to=<dir>::
+       Write a cruft pack containing pruned objects (if any) to the
+       directory `<dir>`. This option is useful for keeping a copy of
+       any pruned objects in a separate directory as a backup. Only
+       useful with `--cruft -d`.
+
 -l::
        Pass the `--local` option to 'git pack-objects'. See
        linkgit:git-pack-objects[1].
@@ -137,6 +154,29 @@ depth is 4095.
        a larger and slower repository; see the discussion in
        `pack.packSizeLimit`.
 
+--filter=<filter-spec>::
+       Remove objects matching the filter specification from the
+       resulting packfile and put them into a separate packfile. Note
+       that objects used in the working directory are not filtered
+       out. So for the split to fully work, it's best to perform it
+       in a bare repo and to use the `-a` and `-d` options along with
+       this option.  Also `--no-write-bitmap-index` (or the
+       `repack.writebitmaps` config option set to `false`) should be
+       used otherwise writing bitmap index will fail, as it supposes
+       a single packfile containing all the objects. See
+       linkgit:git-rev-list[1] for valid `<filter-spec>` forms.
+
+--filter-to=<dir>::
+       Write the pack containing filtered out objects to the
+       directory `<dir>`. Only useful with `--filter`. This can be
+       used for putting the pack on a separate object directory that
+       is accessed through the Git alternates mechanism. **WARNING:**
+       If the packfile containing the filtered out objects is not
+       accessible, the repo can become corrupt as it might not be
+       possible to access the objects in that packfile. See the
+       `objects` and `objects/info/alternates` sections of
+       linkgit:gitrepository-layout[5].
+
 -b::
 --write-bitmap-index::
        Write a reachability bitmap index as part of the repack. This
@@ -159,7 +199,7 @@ depth is 4095.
        Exclude the given pack from repacking. This is the equivalent
        of having `.keep` file on the pack. `<pack-name>` is the
        pack file name without leading directory (e.g. `pack-123.pack`).
-       The option could be specified multiple times to keep multiple
+       The option can be specified multiple times to keep multiple
        packs.
 
 --unpack-unreachable=<when>::
@@ -180,7 +220,7 @@ depth is 4095.
        Pass the `--delta-islands` option to `git-pack-objects`, see
        linkgit:git-pack-objects[1].
 
--g=<factor>::
+-g<factor>::
 --geometric=<factor>::
        Arrange resulting pack structure so that each successive pack
        contains at least `<factor>` times the number of objects as the
@@ -197,11 +237,8 @@ uniquely by the set of packs being "rolled-up"; in other words, the
 packs determined to need to be combined in order to restore a geometric
 progression.
 +
-When `--unpacked` is specified, loose objects are implicitly included in
-this "roll-up", without respect to their reachability. This is subject
-to change in the future. This option (implying a drastically different
-repack mode) is not guaranteed to work with all other combinations of
-option to `git repack`.
+Loose objects are implicitly included in this "roll-up", without respect to
+their reachability. This is subject to change in the future.
 +
 When writing a multi-pack bitmap, `git repack` selects the largest resulting
 pack as the preferred pack for object selection by the MIDX (see
index f271d758c38230410165238de266db7de2d73f71..4f257126e33cc779d1f407ce61cdfbde3cf9ffad 100644 (file)
@@ -35,7 +35,7 @@ Replacement references will be used by default by all Git commands
 except those doing reachability traversal (prune, pack transfer and
 fsck).
 
-It is possible to disable use of replacement references for any
+It is possible to disable the use of replacement references for any
 command using the `--no-replace-objects` option just after 'git'.
 
 For example if commit 'foo' has been replaced by commit 'bar':
@@ -111,7 +111,7 @@ OPTIONS
 FORMATS
 -------
 
-The following format are available:
+The following formats are available:
 
 * 'short':
        <replaced sha1>
diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
new file mode 100644 (file)
index 0000000..f6c269c
--- /dev/null
@@ -0,0 +1,127 @@
+git-replay(1)
+=============
+
+NAME
+----
+git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos too
+
+
+SYNOPSIS
+--------
+[verse]
+(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) <revision-range>...
+
+DESCRIPTION
+-----------
+
+Takes ranges of commits and replays them onto a new location. Leaves
+the working tree and the index untouched, and updates no references.
+The output of this command is meant to be used as input to
+`git update-ref --stdin`, which would update the relevant branches
+(see the OUTPUT section below).
+
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
+OPTIONS
+-------
+
+--onto <newbase>::
+       Starting point at which to create the new commits.  May be any
+       valid commit, and not just an existing branch name.
++
+When `--onto` is specified, the update-ref command(s) in the output will
+update the branch(es) in the revision range to point at the new
+commits, similar to the way how `git rebase --update-refs` updates
+multiple branches in the affected range.
+
+--advance <branch>::
+       Starting point at which to create the new commits; must be a
+       branch name.
++
+When `--advance` is specified, the update-ref command(s) in the output
+will update the branch passed as an argument to `--advance` to point at
+the new commits (in other words, this mimics a cherry-pick operation).
+
+<revision-range>::
+       Range of commits to replay. More than one <revision-range> can
+       be passed, but in `--advance <branch>` mode, they should have
+       a single tip, so that it's clear where <branch> should point
+       to. See "Specifying Ranges" in linkgit:git-rev-parse and the
+       "Commit Limiting" options below.
+
+include::rev-list-options.txt[]
+
+OUTPUT
+------
+
+When there are no conflicts, the output of this command is usable as
+input to `git update-ref --stdin`.  It is of the form:
+
+       update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+       update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+       update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
+
+where the number of refs updated depends on the arguments passed and
+the shape of the history being replayed.  When using `--advance`, the
+number of refs updated is always one, but for `--onto`, it can be one
+or more (rebasing multiple branches simultaneously is supported).
+
+EXIT STATUS
+-----------
+
+For a successful, non-conflicted replay, the exit status is 0.  When
+the replay has conflicts, the exit status is 1.  If the replay is not
+able to complete (or start) due to some kind of error, the exit status
+is something other than 0 or 1.
+
+EXAMPLES
+--------
+
+To simply rebase `mybranch` onto `target`:
+
+------------
+$ git replay --onto target origin/main..mybranch
+update refs/heads/mybranch ${NEW_mybranch_HASH} ${OLD_mybranch_HASH}
+------------
+
+To cherry-pick the commits from mybranch onto target:
+
+------------
+$ git replay --advance target origin/main..mybranch
+update refs/heads/target ${NEW_target_HASH} ${OLD_target_HASH}
+------------
+
+Note that the first two examples replay the exact same commits and on
+top of the exact same new base, they only differ in that the first
+provides instructions to make mybranch point at the new commits and
+the second provides instructions to make target point at them.
+
+What if you have a stack of branches, one depending upon another, and
+you'd really like to rebase the whole set?
+
+------------
+$ git replay --contained --onto origin/main origin/main..tipbranch
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/tipbranch ${NEW_tipbranch_HASH} ${OLD_tipbranch_HASH}
+------------
+
+When calling `git replay`, one does not need to specify a range of
+commits to replay using the syntax `A..B`; any range expression will
+do:
+
+------------
+$ git replay --onto origin/main ^base branch1 branch2 branch3
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
+------------
+
+This will simultaneously rebase `branch1`, `branch2`, and `branch3`,
+all commits they have since `base`, playing them on top of
+`origin/main`. These three branches may have commits on top of `base`
+that they have in common, but that does not need to be the case.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index fa5a42670929a9994b7d3580e1e3c76a69b0ab6a..15dcbb6d91c89eac48623f119e417824b5154e26 100644 (file)
@@ -16,7 +16,7 @@ DESCRIPTION
 Generate a request asking your upstream project to pull changes into
 their tree.  The request, printed to the standard output,
 begins with the branch description, summarizes
-the changes and indicates from where they can be pulled.
+the changes, and indicates from where they can be pulled.
 
 The upstream project is expected to have the commit named by
 `<start>` and the output asks it to integrate the changes you made
@@ -50,7 +50,7 @@ EXAMPLES
 --------
 
 Imagine that you built your work on your `master` branch on top of
-the `v1.0` release, and want it to be integrated to the project.
+the `v1.0` release, and want it to be integrated into the project.
 First you push that change to your public repository for others to
 see:
 
index 4cfc883378082673e57c4d14fc27764e6223c0c4..992b469270c004f4ce7c4223dbc3b84239a14a5f 100644 (file)
@@ -8,7 +8,7 @@ git-rerere - Reuse recorded resolution of conflicted merges
 SYNOPSIS
 --------
 [verse]
-'git rerere' ['clear'|'forget' <pathspec>|'diff'|'remaining'|'status'|'gc']
+'git rerere' [clear | forget <pathspec>... | diff | status | remaining | gc]
 
 DESCRIPTION
 -----------
index 01cb4c9b9c56a7685f3a8d73fd91cadf5ce7a9e6..79ad5643eedb82ce0d884c0695e1bba8440f8985 100644 (file)
@@ -49,7 +49,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 'git reset' [<mode>] [<commit>]::
        This form resets the current branch head to `<commit>` and
        possibly updates the index (resetting it to the tree of `<commit>`) and
-       the working tree depending on `<mode>`. If `<mode>` is omitted,
+       the working tree depending on `<mode>`. Before the operation, `ORIG_HEAD`
+       is set to the tip of the current branch. If `<mode>` is omitted,
        defaults to `--mixed`. The `<mode>` must be one of the following:
 +
 --
index 5964810caa4153a6628ca312669a77a90b41943a..975825b44aa4d01309c46c1923228c372a50e886 100644 (file)
@@ -78,6 +78,8 @@ all modified paths.
 --theirs::
        When restoring files in the working tree from the index, use
        stage #2 ('ours') or #3 ('theirs') for unmerged paths.
+       This option cannot be used when checking out paths from a
+       tree-ish (i.e. with the `--source` option).
 +
 Note that during `git rebase` and `git pull --rebase`, 'ours' and
 'theirs' may appear swapped. See the explanation of the same options
@@ -87,6 +89,8 @@ in linkgit:git-checkout[1] for details.
 --merge::
        When restoring files on the working tree from the index,
        recreate the conflicted merge in the unmerged paths.
+       This option cannot be used when checking out paths from a
+       tree-ish (i.e. with the `--source` option).
 
 --conflict=<style>::
        The same as `--merge` option above, but changes the way the
@@ -101,7 +105,7 @@ in linkgit:git-checkout[1] for details.
        specified. Unmerged paths on the working tree are left alone.
 
 --ignore-skip-worktree-bits::
-       In sparse checkout mode, by default is to only update entries
+       In sparse checkout mode, the default is to only update entries
        matched by `<pathspec>` and sparse patterns in
        $GIT_DIR/info/sparse-checkout. This option ignores the sparse
        patterns and unconditionally restores any files in
@@ -195,7 +199,7 @@ the same as using linkgit:git-reset[1])
 $ git restore --staged hello.c
 ------------
 
-or you can restore both the index and the working tree (this the same
+or you can restore both the index and the working tree (this is the same
 as using linkgit:git-checkout[1])
 
 ------------
index 20bb8e82176b89cbbe68403301a43848b2bef912..2e05c4b510927a66560ade549d9b7a05987e9d33 100644 (file)
@@ -9,7 +9,7 @@ git-rev-list - Lists commit objects in reverse chronological order
 SYNOPSIS
 --------
 [verse]
-'git rev-list' [<options>] <commit>... [[--] <path>...]
+'git rev-list' [<options>] <commit>... [--] [<path>...]
 
 DESCRIPTION
 -----------
@@ -17,9 +17,9 @@ DESCRIPTION
 :git-rev-list: 1
 include::rev-list-description.txt[]
 
-'rev-list' is a very essential Git command, since it
+'rev-list' is an essential Git command, since it
 provides the ability to build and traverse commit ancestry graphs. For
-this reason, it has a lot of different options that enables it to be
+this reason, it has a lot of different options that enable it to be
 used by commands as different as 'git bisect' and
 'git repack'.
 
index 6b8ca085aa6da56fa561ede46395e2be5a6d26be..546faf9017723581ea8e13bea6f5f90b00372403 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Many Git porcelainish commands take mixture of flags
+Many Git porcelainish commands take mixture of flags
 (i.e. parameters that begin with a dash '-') and parameters
 meant for the underlying 'git rev-list' command they use internally
 and flags and parameters for the other commands they use
@@ -36,7 +36,7 @@ Each of these options must appear first on the command line.
 --sq-quote::
        Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
        section below). In contrast to the `--sq` option below, this
-       mode does only quoting. Nothing else is done to command input.
+       mode only does quoting. Nothing else is done to command input.
 
 Options for --parseopt
 ~~~~~~~~~~~~~~~~~~~~~~
@@ -156,7 +156,7 @@ for another option.
        are not refs (i.e. branch or tag names; or more
        explicitly disambiguating "heads/master" form, when you
        want to name the "master" branch when there is an
-       unfortunately named tag "master"), and show them as full
+       unfortunately named tag "master"), and shows them as full
        refnames (e.g. "refs/heads/master").
 
 Options for Objects
@@ -197,6 +197,14 @@ respectively, and they must begin with `refs/` when applied to `--glob`
 or `--all`. If a trailing '/{asterisk}' is intended, it must be given
 explicitly.
 
+--exclude-hidden=[fetch|receive|uploadpack]::
+       Do not include refs that would be hidden by `git-fetch`,
+       `git-receive-pack` or `git-upload-pack` by consulting the appropriate
+       `fetch.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs`
+       configuration along with `transfer.hideRefs` (see
+       linkgit:git-config[1]). This option affects the next pseudo-ref option
+       `--all` or `--glob` and is cleared after processing them.
+
 --disambiguate=<prefix>::
        Show every object whose name begins with the given prefix.
        The <prefix> must be at least 4 hexadecimal digits long to
@@ -299,6 +307,9 @@ The following options are unaffected by `--path-format`:
        input, multiple algorithms may be printed, space-separated.
        If not specified, the default is "storage".
 
+--show-ref-format::
+       Show the reference storage format used for the repository.
+
 
 Other Options
 ~~~~~~~~~~~~~
@@ -375,7 +386,7 @@ Each line of options has this format:
        dash to separate words in a multi-word argument hint.
 
 The remainder of the line, after stripping the spaces, is used
-as the help associated to the option.
+as the help associated with the option.
 
 Blank lines are ignored, and lines that don't match this specification are used
 as option group headers (start the line with a space to create such
@@ -390,7 +401,7 @@ some-command [<options>] <args>...
 
 some-command does foo and bar!
 --
-h,help    show the help
+h,help!   show the help
 
 foo       some nifty option --foo
 bar=      some cool option --bar with an argument
@@ -416,10 +427,10 @@ usage: some-command [<options>] <args>...
     some-command does foo and bar!
 
     -h, --help            show the help
-    --foo                 some nifty option --foo
-    --bar ...             some cool option --bar with an argument
-    --baz <arg>           another cool option --baz with a named argument
-    --qux[=<path>]        qux may take a path argument but has meaning by itself
+    --[no-]foo            some nifty option --foo
+    --[no-]bar ...        some cool option --bar with an argument
+    --[no-]baz <arg>      another cool option --baz with a named argument
+    --[no-]qux[=<path>]   qux may take a path argument but has meaning by itself
 
 An option group Header
     -C[...]               option C with an optional argument
index 5016755efb61ded8351884a978b3febfc35040e6..cbe0208834d51f89cf9020bdf55ce96be4221ae5 100644 (file)
@@ -8,7 +8,7 @@ git-revert - Revert some existing commits
 SYNOPSIS
 --------
 [verse]
-'git revert' [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>...
+'git revert' [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] <commit>...
 'git revert' (--continue | --skip | --abort | --quit)
 
 DESCRIPTION
@@ -142,6 +142,16 @@ EXAMPLES
        changes. The revert only modifies the working tree and the
        index.
 
+DISCUSSION
+----------
+
+While git creates a basic commit message automatically, it is
+_strongly_ recommended to explain why the original commit is being
+reverted.
+In addition, repeatedly reverting reverts will result in increasingly
+unwieldy subject lines, for example 'Reapply "Reapply "<original subject>""'.
+Please consider rewording these to be shorter and more unique.
+
 CONFIGURATION
 -------------
 
index 81bc23f3cdbb56fbb8e0bd79c2bae40d35772cac..363a26934f54e06a4870cd1b7657082591f80f21 100644 (file)
@@ -163,7 +163,7 @@ will be staged (unless --cached or -n are used).
 
 A submodule is considered up to date when the HEAD is the same as
 recorded in the index, no tracked files are modified and no untracked
-files that aren't ignored are present in the submodules work tree.
+files that aren't ignored are present in the submodule's work tree.
 Ignored files are deemed expendable and won't stop a submodule's work
 tree from being removed.
 
index 3290043053aa6d16f6a0d63f1c9a04e54e349e36..30deb7fe2a4f5506745a938b76d8bbcfb504e34b 100644 (file)
@@ -68,11 +68,12 @@ This option may be specified multiple times.
        Invoke a text editor (see GIT_EDITOR in linkgit:git-var[1])
        to edit an introductory message for the patch series.
 +
-When `--compose` is used, git send-email will use the From, Subject, and
-In-Reply-To headers specified in the message. If the body of the message
-(what you type after the headers and a blank line) only contains blank
-(or Git: prefixed) lines, the summary won't be sent, but From, Subject,
-and In-Reply-To headers will be used unless they are removed.
+When `--compose` is used, git send-email will use the From, To, Cc, Bcc,
+Subject, Reply-To, and In-Reply-To headers specified in the message. If
+the body of the message (what you type after the headers and a blank
+line) only contains blank (or Git: prefixed) lines, the summary won't be
+sent, but the headers mentioned above will be used unless they are
+removed.
 +
 Missing From or In-Reply-To headers will be prompted for.
 +
@@ -93,7 +94,7 @@ See the CONFIGURATION section for `sendemail.multiEdit`.
 
 --in-reply-to=<identifier>::
        Make the first mail (or all the mails with `--no-thread`) appear as a
-       reply to the given Message-Id, which avoids breaking threads to
+       reply to the given Message-ID, which avoids breaking threads to
        provide a new patch series.
        The second and subsequent emails will be sent as replies according to
        the `--[no-]chain-reply-to` setting.
@@ -178,9 +179,18 @@ Sending
        for `sendmail` in `/usr/sbin`, `/usr/lib` and $PATH.
 
 --smtp-encryption=<encryption>::
-       Specify the encryption to use, either 'ssl' or 'tls'.  Any other
-       value reverts to plain SMTP.  Default is the value of
-       `sendemail.smtpEncryption`.
+       Specify in what way encrypting begins for the SMTP connection.
+       Valid values are 'ssl' and 'tls'. Any other value reverts to plain
+       (unencrypted) SMTP, which defaults to port 25.
+       Despite the names, both values will use the same newer version of TLS,
+       but for historic reasons have these names. 'ssl' refers to "implicit"
+       encryption (sometimes called SMTPS), that uses port 465 by default.
+       'tls' refers to "explicit" encryption (often known as STARTTLS),
+       that uses port 25 by default. Other ports might be used by the SMTP
+       server, which are not the default. Commonly found alternative port for
+       'tls' and unencrypted is 587. You need to check your provider's
+       documentation or your server configuration to make sure
+       for your own case. Default is the value of `sendemail.smtpEncryption`.
 
 --smtp-domain=<FQDN>::
        Specifies the Fully Qualified Domain Name (FQDN) used in the
@@ -311,6 +321,17 @@ Automating
        Output of this command must be single email address per line.
        Default is the value of `sendemail.ccCmd` configuration value.
 
+--header-cmd=<command>::
+       Specify a command that is executed once per outgoing message
+       and output RFC 2822 style header lines to be inserted into
+       them. When the `sendemail.headerCmd` configuration variable is
+       set, its value is always used. When --header-cmd is provided
+       at the command line, its value takes precedence over the
+       `sendemail.headerCmd` configuration variable.
+
+--no-header-cmd::
+       Disable any header command in use.
+
 --[no-]chain-reply-to::
        If this is set, each email will be sent as a reply to the previous
        email sent.  If disabled with "--no-chain-reply-to", all emails after
@@ -433,7 +454,7 @@ have been specified, in which case default to 'compose'.
                        998 characters unless a suitable transfer encoding
                        ('auto', 'base64', or 'quoted-printable') is used;
                        this is due to SMTP limits as described by
-                       http://www.ietf.org/rfc/rfc5322.txt.
+                       https://www.ietf.org/rfc/rfc5322.txt.
 --
 +
 Default is the value of `sendemail.validate`; if this is not set,
@@ -448,8 +469,8 @@ Information
 
 --dump-aliases::
        Instead of the normal operation, dump the shorthand alias names from
-       the configured alias file(s), one per line in alphabetical order. Note,
-       this only includes the alias name and not its expanded email addresses.
+       the configured alias file(s), one per line in alphabetical order. Note
+       that this only includes the alias name and not its expanded email addresses.
        See 'sendemail.aliasesfile' for more information about aliases.
 
 
@@ -475,14 +496,10 @@ edit ~/.gitconfig to specify your account settings:
        smtpServerPort = 587
 ----
 
-If you have multi-factor authentication set up on your Gmail account, you will
-need to generate an app-specific password for use with 'git send-email'. Visit
+If you have multi-factor authentication set up on your Gmail account, you can
+generate an app-specific password for use with 'git send-email'. Visit
 https://security.google.com/settings/security/apppasswords to create it.
 
-If you do not have multi-factor authentication set up on your Gmail account,
-you will need to allow less secure app access. Visit
-https://myaccount.google.com/lesssecureapps to enable it.
-
 Once your commits are ready to be sent to the mailing list, run the
 following commands:
 
index be41f119740ea7f5e2f53d3211e608bf4255544f..b9e73f2e77b1ccd35ac76ce075a59b362fa7f1dd 100644 (file)
@@ -9,9 +9,10 @@ git-send-pack - Push objects over Git protocol to another repository
 SYNOPSIS
 --------
 [verse]
-'git send-pack' [--dry-run] [--force] [--receive-pack=<git-receive-pack>]
+'git send-pack' [--mirror] [--dry-run] [--force]
+               [--receive-pack=<git-receive-pack>]
                [--verbose] [--thin] [--atomic]
-               [--[no-]signed|--signed=(true|false|if-asked)]
+               [--[no-]signed | --signed=(true|false|if-asked)]
                [<host>:]<directory> (--all | <ref>...)
 
 DESCRIPTION
@@ -54,7 +55,7 @@ be in a separate packet, and the list must end with a flush packet.
 --force::
        Usually, the command refuses to update a remote ref that
        is not an ancestor of the local ref used to overwrite it.
-       This flag disables the check.  What this means is that
+       This flag disables the check.  This means that
        the remote repository can lose commits; use it with
        care.
 
@@ -105,7 +106,7 @@ SPECIFYING THE REFS
 There are three ways to specify which refs to update on the
 remote end.
 
-With `--all` flag, all refs that exist locally are transferred to
+With the `--all` flag, all refs that exist locally are transferred to
 the remote side.  You cannot specify any '<ref>' if you use
 this flag.
 
@@ -114,9 +115,9 @@ both on the local side and on the remote side are updated.
 
 When one or more '<ref>' are specified explicitly (whether on the
 command line or via `--stdin`), it can be either a
-single pattern, or a pair of such pattern separated by a colon
+single pattern, or a pair of such patterns separated by a colon
 ":" (this means that a ref name cannot have a colon in it).  A
-single pattern '<name>' is just shorthand for '<name>:<name>'.
+single pattern '<name>' is just shorthand for '<name>:<name>'.
 
 Each pattern pair consists of the source side (before the colon)
 and the destination side (after the colon).  The ref to be
@@ -129,7 +130,7 @@ name. See linkgit:git-rev-parse[1].
  - It is an error if <src> does not match exactly one of the
    local refs.
 
- - It is an error if <dst> matches more than one remote refs.
+ - It is an error if <dst> matches more than one remote ref.
 
  - If <dst> does not match any remote ref, either
 
@@ -142,9 +143,9 @@ name. See linkgit:git-rev-parse[1].
 
 Without `--force`, the <src> ref is stored at the remote only if
 <dst> does not exist, or <dst> is a proper subset (i.e. an
-ancestor) of <src>.  This check, known as "fast-forward check",
-is performed in order to avoid accidentally overwriting the
-remote ref and lose other peoples' commits from there.
+ancestor) of <src>.  This check, known as the "fast-forward check",
+is performed to avoid accidentally overwriting the
+remote ref and losing other people's commits from there.
 
 With `--force`, the fast-forward check is disabled for all refs.
 
index 8632612c31d07818659ff4f68ee567c789e5cd48..bdaf6e5fc4fa79f055e7eb1717464716ea420cc8 100644 (file)
@@ -22,7 +22,7 @@ The 'git sh-setup' scriptlet is designed to be sourced (using
 the normal Git directories and a few helper shell functions.
 
 Before sourcing it, your script should set up a few variables;
-`USAGE` (and `LONG_USAGE`, if any) is used to define message
+`USAGE` (and `LONG_USAGE`, if any) is used to define the message
 given by `usage()` shell function.  `SUBDIRECTORY_OK` can be set
 if the script can run from a subdirectory of the working tree
 (some commands do not).
index f64e77047b2f86f1645671fc46e9487f788c117a..7d0277d033d60227cee0b24a4c8747dfa892f4e7 100644 (file)
@@ -47,6 +47,11 @@ OPTIONS
 
        Each pretty-printed commit will be rewrapped before it is shown.
 
+--date=<format>::
+       Show dates formatted according to the given date string. (See
+       the `--date` option in the "Commit Formatting" section of
+       linkgit:git-log[1]). Useful with `--group=format:<format>`.
+
 --group=<type>::
        Group commits based on `<type>`. If no `--group` option is
        specified, the default is `author`. `<type>` is one of:
@@ -59,6 +64,9 @@ OPTIONS
    example, if your project uses `Reviewed-by` trailers, you might want
    to see who has been reviewing with
    `git shortlog -ns --group=trailer:reviewed-by`.
+ - `format:<format>`, any string accepted by the `--format` option of
+   'git log'. (See the "PRETTY FORMATS" section of
+   linkgit:git-log[1].)
 +
 Note that commits that do not include the trailer will not be counted.
 Likewise, commits with multiple trailers (e.g., multiple signoffs) may
index e5ec6b467f9f3f765d31949725ddc0c7c634391f..c771c89770787356193bd4e241b1fae8069f8d48 100644 (file)
@@ -8,12 +8,12 @@ git-show-branch - Show branches and their commits
 SYNOPSIS
 --------
 [verse]
-'git show-branch' [-a|--all] [-r|--remotes] [--topo-order | --date-order]
+'git show-branch' [-a | --all] [-r | --remotes] [--topo-order | --date-order]
                [--current] [--color[=<when>] | --no-color] [--sparse]
                [--more=<n> | --list | --independent | --merge-base]
                [--no-name | --sha1-name] [--topics]
                [(<rev> | <glob>)...]
-'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]
+'git show-branch' (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]
 
 DESCRIPTION
 -----------
@@ -50,7 +50,7 @@ OPTIONS
 
 --current::
        With this option, the command includes the current
-       branch to the list of revs to be shown when it is not
+       branch in the list of revs to be shown when it is not
        given on the command line.
 
 --topo-order::
@@ -74,8 +74,7 @@ OPTIONS
        that is the common ancestor of all the branches.  This
        flag tells the command to go <n> more common commits
        beyond that.  When <n> is negative, display only the
-       <reference>s given, without showing the commit ancestry
-       tree.
+       <ref>s given, without showing the commit ancestry tree.
 
 --list::
        Synonym to `--more=-1`
@@ -88,8 +87,8 @@ OPTIONS
        the case of three or more commits.
 
 --independent::
-       Among the <reference>s given, display only the ones that
-       cannot be reached from any other <reference>.
+       Among the <ref>s given, display only the ones that cannot be
+       reached from any other <ref>.
 
 --no-name::
        Do not show naming strings for each commit.
@@ -126,25 +125,26 @@ OPTIONS
        default to color output.
        Same as `--color=never`.
 
-Note that --more, --list, --independent and --merge-base options
+Note that --more, --list, --independent, and --merge-base options
 are mutually exclusive.
 
 
 OUTPUT
 ------
-Given N <references>, the first N lines are the one-line
-description from their commit message.  The branch head that is
-pointed at by $GIT_DIR/HEAD is prefixed with an asterisk `*`
-character while other heads are prefixed with a `!` character.
 
-Following these N lines, one-line log for each commit is
+Given N <ref>s, the first N lines are the one-line description from
+their commit message. The branch head that is pointed at by
+$GIT_DIR/HEAD is prefixed with an asterisk `*` character while other
+heads are prefixed with a `!` character.
+
+Following these N lines, a one-line log for each commit is
 displayed, indented N places.  If a commit is on the I-th
 branch, the I-th indentation character shows a `+` sign;
 otherwise it shows a space.  Merge commits are denoted by
 a `-` sign.  Each commit shows a short name that
 can be used as an extended SHA-1 to name that commit.
 
-The following example shows three branches, "master", "fixes"
+The following example shows three branches, "master", "fixes",
 and "mhf":
 
 ------------------------------------------------
@@ -154,7 +154,7 @@ $ git show-branch master fixes mhf
   ! [mhf] Allow "+remote:local" refspec to cause --force when fetching.
 ---
   + [mhf] Allow "+remote:local" refspec to cause --force when fetching.
-  + [mhf~1] Use git-octopus when pulling more than one heads.
+  + [mhf~1] Use git-octopus when pulling more than one head.
  +  [fixes] Introduce "reset type" flag to "git reset"
   + [mhf~2] "git fetch --force".
   + [mhf~3] Use .git/remote/origin, not .git/branches/origin.
@@ -197,7 +197,7 @@ $ git show-branch --reflog="10,1 hour ago" --list master
 
 shows 10 reflog entries going back from the tip as of 1 hour ago.
 Without `--list`, the output also shows how these tips are
-topologically related with each other.
+topologically related to each other.
 
 CONFIGURATION
 -------------
index ab4d271925da7983b16867d89e898bbed17ae469..ba7574700593e85ea65ac2e0e8516f286d11dcc8 100644 (file)
@@ -8,10 +8,14 @@ git-show-ref - List references in a local repository
 SYNOPSIS
 --------
 [verse]
-'git show-ref' [-q|--quiet] [--verify] [--head] [-d|--dereference]
-            [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags]
+'git show-ref' [--head] [-d | --dereference]
+            [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]
             [--heads] [--] [<pattern>...]
+'git show-ref' --verify [-q | --quiet] [-d | --dereference]
+            [-s | --hash[=<n>]] [--abbrev[=<n>]]
+            [--] [<ref>...]
 'git show-ref' --exclude-existing[=<pattern>]
+'git show-ref' --exists <ref>
 
 DESCRIPTION
 -----------
@@ -23,10 +27,14 @@ particular ref exists.
 
 By default, shows the tags, heads, and remote refs.
 
-The --exclude-existing form is a filter that does the inverse. It reads
+The `--exclude-existing` form is a filter that does the inverse. It reads
 refs from stdin, one ref per line, and shows those that don't exist in
 the local repository.
 
+The `--exists` form can be used to check for the existence of a single
+references. This form does not verify whether the reference resolves to an
+actual object.
+
 Use of this utility is encouraged in favor of directly accessing files under
 the `.git` directory.
 
@@ -47,14 +55,14 @@ OPTIONS
 -d::
 --dereference::
 
-       Dereference tags into object IDs as well. They will be shown with "{caret}{}"
+       Dereference tags into object IDs as well. They will be shown with `^{}`
        appended.
 
 -s::
 --hash[=<n>]::
 
-       Only show the SHA-1 hash, not the reference name. When combined with
-       --dereference the dereferenced tag will still be shown after the SHA-1.
+       Only show the OID, not the reference name. When combined with
+       `--dereference`, the dereferenced tag will still be shown after the OID.
 
 --verify::
 
@@ -62,6 +70,12 @@ OPTIONS
        Aside from returning an error code of 1, it will also print an error
        message if `--quiet` was not specified.
 
+--exists::
+
+       Check whether the given reference exists. Returns an exit code of 0 if
+       it does, 2 if it is missing, and 1 in case looking up the reference
+       failed with an error other than the reference being missing.
+
 --abbrev[=<n>]::
 
        Abbreviate the object name.  When using `--hash`, you do
@@ -70,15 +84,15 @@ OPTIONS
 -q::
 --quiet::
 
-       Do not print any results to stdout. When combined with `--verify` this
-       can be used to silently check if a reference exists.
+       Do not print any results to stdout. Can be used with `--verify` to
+       silently check if a reference exists.
 
 --exclude-existing[=<pattern>]::
 
-       Make 'git show-ref' act as a filter that reads refs from stdin of the
-       form "`^(?:<anything>\s)?<refname>(?:\^{})?$`"
+       Make `git show-ref` act as a filter that reads refs from stdin of the
+       form `^(?:<anything>\s)?<refname>(?:\^{})?$`
        and performs the following actions on each:
-       (1) strip "{caret}{}" at the end of line if any;
+       (1) strip `^{}` at the end of line if any;
        (2) ignore if pattern is provided and does not head-match refname;
        (3) warn if refname is not a well-formed refname and skip;
        (4) ignore if refname is a ref that exists in the local repository;
@@ -96,7 +110,13 @@ OPTIONS
 OUTPUT
 ------
 
-The output is in the format: '<SHA-1 ID>' '<space>' '<reference name>'.
+The output is in the format:
+
+------------
+<oid> SP <ref> LF
+------------
+
+For example,
 
 -----------------------------------------------------------------------------
 $ git show-ref --head --dereference
@@ -110,7 +130,13 @@ $ git show-ref --head --dereference
 ...
 -----------------------------------------------------------------------------
 
-When using --hash (and not --dereference) the output format is: '<SHA-1 ID>'
+When using `--hash` (and not `--dereference`), the output is in the format:
+
+------------
+<oid> LF
+------------
+
+For example,
 
 -----------------------------------------------------------------------------
 $ git show-ref --heads --hash
@@ -132,7 +158,7 @@ use:
 -----------------------------------------------------------------------------
 
 This will show "refs/heads/master" but also "refs/remote/other-repo/master",
-if such references exists.
+if such references exist.
 
 When using the `--verify` flag, the command requires an exact path:
 
@@ -142,10 +168,10 @@ When using the `--verify` flag, the command requires an exact path:
 
 will only match the exact branch called "master".
 
-If nothing matches, 'git show-ref' will return an error code of 1,
+If nothing matches, `git show-ref` will return an error code of 1,
 and in the case of verification, it will show an error message.
 
-For scripting, you can ask it to be quiet with the "--quiet" flag, which
+For scripting, you can ask it to be quiet with the `--quiet` flag, which
 allows you to do things like
 
 -----------------------------------------------------------------------------
@@ -157,11 +183,11 @@ to check whether a particular branch exists or not (notice how we don't
 actually want to show any results, and we want to use the full refname for it
 in order to not trigger the problem with ambiguous partial matches).
 
-To show only tags, or only proper branch heads, use "--tags" and/or "--heads"
+To show only tags, or only proper branch heads, use `--tags` and/or `--heads`
 respectively (using both means that it shows tags and heads, but not other
 random references under the refs/ subdirectory).
 
-To do automatic tag object dereferencing, use the "-d" or "--dereference"
+To do automatic tag object dereferencing, use the `-d` or `--dereference`
 flag, so you can do
 
 -----------------------------------------------------------------------------
index 2b1bc7288d5f6fd27b92ed8119ef5dfbc89b9d1d..5eb67439affbef1a7fccb6a745220977b6a0a45d 100644 (file)
@@ -26,7 +26,7 @@ with --name-only).
 
 For plain blobs, it shows the plain contents.
 
-The command takes options applicable to the 'git diff-tree' command to
+Some options that 'git log' command understands can be used to
 control how the changes the commit introduces are shown.
 
 This manual page describes only the most frequently used options.
@@ -61,7 +61,7 @@ EXAMPLES
 --------
 
 `git show v1.0.0`::
-       Shows the tag `v1.0.0`, along with the object the tags
+       Shows the tag `v1.0.0`, along with the object the tag
        points at.
 
 `git show v1.0.0^{tree}`::
index 3776705bf5359ecc912e056dbf245afa0e775821..529a8edd9c1ed88c8b0ab9c39ab95dccd97671b3 100644 (file)
@@ -9,7 +9,7 @@ git-sparse-checkout - Reduce your working tree to a subset of tracked files
 SYNOPSIS
 --------
 [verse]
-'git sparse-checkout <subcommand> [<options>]'
+'git sparse-checkout' (init | list | set | add | reapply | disable | check-rules) [<options>]
 
 
 DESCRIPTION
@@ -135,6 +135,29 @@ paths to pass to a subsequent 'set' or 'add' command.  However,
 the disable command, so the easy restore of calling a plain `init`
 decreased in utility.
 
+'check-rules'::
+       Check whether sparsity rules match one or more paths.
++
+By default `check-rules` reads a list of paths from stdin and outputs only
+the ones that match the current sparsity rules. The input is expected to consist
+of one path per line, matching the output of `git ls-tree --name-only` including
+that pathnames that begin with a double quote (") are interpreted as C-style
+quoted strings.
++
+When called with the `--rules-file <file>` flag the input files are matched
+against the sparse checkout rules found in `<file>` instead of the current ones.
+The rules in the files are expected to be in the same form as accepted by `git
+sparse-checkout set --stdin` (in particular, they must be newline-delimited).
++
+By default, the rules passed to the `--rules-file` option are interpreted as
+cone mode directories. To pass non-cone mode patterns with `--rules-file`,
+combine the option with the `--no-cone` option.
++
+When called with the `-z` flag, the format of the paths input on stdin as well
+as the output paths are \0 terminated and not quoted. Note that this does not
+apply to the format of the rules passed with the `--rules-file` option.
+
+
 EXAMPLES
 --------
 `git sparse-checkout set MY/DIR1 SUB/DIR2`::
@@ -263,7 +286,7 @@ patterns in non-cone mode has a number of shortcomings:
     problem above?  Also, if it suggests paths, what if the user has a
     file or directory that begins with either a '!' or '#' or has a '*',
     '\', '?', '[', or ']' in its name?  And if it suggests paths, will
-    it complete "/pro" to "/proc" (in the root filesytem) rather than to
+    it complete "/pro" to "/proc" (in the root filesystem) rather than to
     "/progress.txt" in the current directory?  (Note that users are
     likely to want to start paths with a leading '/' in non-cone mode,
     for the same reason that .gitignore files often have one.)
index c5d70918283836d9a57c1af4710ce69fba78e3f1..06fb7f1d18c6bec047b9cbdb2487f72614fa6073 100644 (file)
@@ -9,17 +9,20 @@ SYNOPSIS
 --------
 [verse]
 'git stash' list [<log-options>]
-'git stash' show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]
-'git stash' drop [-q|--quiet] [<stash>]
-'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
+'git stash' show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]
+'git stash' drop [-q | --quiet] [<stash>]
+'git stash' pop [--index] [-q | --quiet] [<stash>]
+'git stash' apply [--index] [-q | --quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]
-            [-u|--include-untracked] [-a|--all] [-m|--message <message>]
+'git stash' [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
+            [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]
             [--pathspec-from-file=<file> [--pathspec-file-nul]]
             [--] [<pathspec>...]]
+'git stash' save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
+            [-u | --include-untracked] [-a | --all] [<message>]
 'git stash' clear
 'git stash' create [<message>]
-'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
+'git stash' store [(-m | --message) <message>] [-q | --quiet] <commit>
 
 DESCRIPTION
 -----------
@@ -47,7 +50,7 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
 COMMANDS
 --------
 
-push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
+push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [(-m|--message) <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
 
        Save your local modifications to a new 'stash entry' and roll them
        back to HEAD (in the working tree and in the index).
@@ -363,7 +366,7 @@ only the commit ends-up being in the stash and not on the current branch.
 # ... hack hack hack ...
 $ git add --patch foo           # add unrelated changes to the index
 $ git stash push --staged       # save these changes to the stash
-# ... hack hack hack, finish curent changes ...
+# ... hack hack hack, finish current changes ...
 $ git commit -m 'Massive'       # commit fully tested changes
 $ git switch fixup-branch       # switch to another branch
 $ git stash pop                 # to finish work on the saved changes
index 54a4b29b473cc4e928b484286b01b337c9b25bdd..10fecc51a75d4783c68565d25302c50480d626ce 100644 (file)
@@ -9,7 +9,7 @@ git-status - Show the working tree status
 SYNOPSIS
 --------
 [verse]
-'git status' [<options>...] [--] [<pathspec>...]
+'git status' [<options>] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -245,11 +245,12 @@ U           U    unmerged, both modified
 ....
 
 Submodules have more state and instead report
-               M    the submodule has a different HEAD than
-                    recorded in the index
-               m    the submodule has modified content
-               ?    the submodule has untracked files
-since modified content or untracked files in a submodule cannot be added
+
+* 'M' = the submodule has a different HEAD than recorded in the index
+* 'm' = the submodule has modified content
+* '?' = the submodule has untracked files
+
+This is since modified content or untracked files in a submodule cannot be added
 via `git add` in the superproject to prepare a commit.
 
 'm' and '?' are applied recursively. For example if a nested submodule
@@ -457,6 +458,66 @@ during the write may conflict with other simultaneous processes, causing
 them to fail. Scripts running `status` in the background should consider
 using `git --no-optional-locks status` (see linkgit:git[1] for details).
 
+UNTRACKED FILES AND PERFORMANCE
+-------------------------------
+
+`git status` can be very slow in large worktrees if/when it
+needs to search for untracked files and directories. There are
+many configuration options available to speed this up by either
+avoiding the work or making use of cached results from previous
+Git commands. There is no single optimum set of settings right
+for everyone. We'll list a summary of the relevant options to help
+you, but before going into the list, you may want to run `git status`
+again, because your configuration may already be caching `git status`
+results, so it could be faster on subsequent runs.
+
+* The `--untracked-files=no` flag or the
+       `status.showUntrackedfiles=false` config (see above for both):
+       indicate that `git status` should not report untracked
+       files. This is the fastest option. `git status` will not list
+       the untracked files, so you need to be careful to remember if
+       you create any new files and manually `git add` them.
+
+* `advice.statusUoption=false` (see linkgit:git-config[1]):
+       setting this variable to `false` disables the warning message
+       given when enumerating untracked files takes more than 2
+       seconds.  In a large project, it may take longer and the user
+       may have already accepted the trade off (e.g. using "-uno" may
+       not be an acceptable option for the user), in which case, there
+       is no point issuing the warning message, and in such a case,
+       disabling the warning may be the best.
+
+* `core.untrackedCache=true` (see linkgit:git-update-index[1]):
+       enable the untracked cache feature and only search directories
+       that have been modified since the previous `git status` command.
+       Git remembers the set of untracked files within each directory
+       and assumes that if a directory has not been modified, then
+       the set of untracked files within has not changed.  This is much
+       faster than enumerating the contents of every directory, but still
+       not without cost, because Git still has to search for the set of
+       modified directories. The untracked cache is stored in the
+       `.git/index` file. The reduced cost of searching for untracked
+       files is offset slightly by the increased size of the index and
+       the cost of keeping it up-to-date. That reduced search time is
+       usually worth the additional size.
+
+* `core.untrackedCache=true` and `core.fsmonitor=true` or
+       `core.fsmonitor=<hook_command_pathname>` (see
+       linkgit:git-update-index[1]): enable both the untracked cache
+       and FSMonitor features and only search directories that have
+       been modified since the previous `git status` command.  This
+       is faster than using just the untracked cache alone because
+       Git can also avoid searching for modified directories.  Git
+       only has to enumerate the exact set of directories that have
+       changed recently. While the FSMonitor feature can be enabled
+       without the untracked cache, the benefits are greatly reduced
+       in that case.
+
+Note that after you turn on the untracked cache and/or FSMonitor
+features it may take a few `git status` commands for the various
+caches to warm up before you see improved command times.  This is
+normal.
+
 SEE ALSO
 --------
 linkgit:gitignore[5]
index 2438f76da05ebf013103a08962d634c4baa9ea15..a293327581aa54e120d1edc3b72467e42c250af4 100644 (file)
@@ -29,7 +29,7 @@ With no arguments, this will:
 In the case where the input consists entirely of whitespace characters, no
 output will be produced.
 
-*NOTE*: This is intended for cleaning metadata, prefer the `--whitespace=fix`
+*NOTE*: This is intended for cleaning metadata. Prefer the `--whitespace=fix`
 mode of linkgit:git-apply[1] for correcting whitespace of patches or files in
 the repository.
 
@@ -37,11 +37,11 @@ OPTIONS
 -------
 -s::
 --strip-comments::
-       Skip and remove all lines starting with comment character (default '#').
+       Skip and remove all lines starting with comment character (default '#').
 
 -c::
 --comment-lines::
-       Prepend comment character and blank to each line. Lines will automatically
+       Prepend the comment character and a blank space to each line. Lines will automatically
        be terminated with a newline. On empty lines, only the comment character
        will be prepended.
 
index 4d3ab6b9f925cde1ef8835d790fa1fd66108a36d..695730609aa3ab5a18d45cbff6aba5d1499dfdce 100644 (file)
@@ -95,7 +95,7 @@ too (and can also report changes to a submodule's work tree).
 init [--] [<path>...]::
        Initialize the submodules recorded in the index (which were
        added and committed elsewhere) by setting `submodule.$name.url`
-       in .git/config. It uses the same setting from `.gitmodules` as
+       in `.git/config`, using the same setting from `.gitmodules` as
        a template. If the URL is relative, it will be resolved using
        the default remote. If there is no default remote, the current
        repository will be assumed to be upstream.
@@ -105,9 +105,12 @@ If no path is specified and submodule.active has been configured, submodules
 configured to be active will be initialized, otherwise all submodules are
 initialized.
 +
-When present, it will also copy the value of `submodule.$name.update`.
-This command does not alter existing information in .git/config.
-You can then customize the submodule clone URLs in .git/config
+It will also copy the value of `submodule.$name.update`, if present in
+the `.gitmodules` file, to `.git/config`, but (1) this command does not
+alter existing information in `.git/config`, and (2) `submodule.$name.update`
+that is set to a custom command is *not* copied for security reasons.
++
+You can then customize the submodule clone URLs in `.git/config`
 for your local setup and proceed to `git submodule update`;
 you can also just use `git submodule update --init` without
 the explicit 'init' step if you do not intend to customize
@@ -143,6 +146,8 @@ the submodules. The "updating" can be done in several ways depending
 on command line options and the value of `submodule.<name>.update`
 configuration variable. The command line option takes precedence over
 the configuration variable. If neither is given, a 'checkout' is performed.
+(note: what is in `.gitmodules` file is irrelevant at this point;
+see `git submodule init` above for how `.gitmodules` is used).
 The 'update' procedures supported both from the command line as well as
 through the `submodule.<name>.update` configuration are:
 
@@ -160,16 +165,18 @@ checked out in the submodule.
        merge;; the commit recorded in the superproject will be merged
            into the current branch in the submodule.
 
-The following 'update' procedures are only available via the
-`submodule.<name>.update` configuration variable:
+The following update procedures have additional limitations:
 
-       custom command;; arbitrary shell command that takes a single
-           argument (the sha1 of the commit recorded in the
-           superproject) is executed. When `submodule.<name>.update`
-           is set to '!command', the remainder after the exclamation mark
-           is the custom command.
+       custom command;; mechanism for running arbitrary commands with the
+           commit ID as an argument. Specifically, if the
+           `submodule.<name>.update` configuration variable is set to
+           `!custom command`, the object name of the commit recorded in the
+           superproject for the submodule is appended to the `custom command`
+           string and executed. Note that this mechanism is not supported in
+           the `.gitmodules` file or on the command line.
 
-       none;; the submodule is not updated.
+       none;; the submodule is not updated. This update procedure is not
+           allowed on the command line.
 
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in `.gitmodules`, you can automatically initialize the
index c60fc9c138b5981ce5727efa4482350e699dbac5..f38e4c8afa1bcbcfba7bd8b3e1da0bbad9703f77 100644 (file)
@@ -59,13 +59,18 @@ out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 -c <new-branch>::
 --create <new-branch>::
        Create a new branch named `<new-branch>` starting at
-       `<start-point>` before switching to the branch. This is a
-       convenient shortcut for:
+       `<start-point>` before switching to the branch. This is the
+       transactional equivalent of
 +
 ------------
 $ git branch <new-branch>
 $ git switch <new-branch>
 ------------
++
+that is to say, the branch is not reset/created unless "git switch" is
+successful (e.g., when the branch is in use in another worktree, not
+just the current branch stays the same, but the branch is not reset to
+the start-point, either).
 
 -C <new-branch>::
 --force-create <new-branch>::
@@ -171,7 +176,7 @@ name, the guessing is aborted.  You can explicitly give a name with
        `branch.autoSetupMerge` configuration variable is true.
 
 --orphan <new-branch>::
-       Create a new 'orphan' branch, named `<new-branch>`. All
+       Create a new unborn branch, named `<new-branch>`. All
        tracked files are removed.
 
 --ignore-other-worktrees::
index ef68ad2b7112d5749fd7ee090befe248ceb27204..761b154bcbb58b7c27245fb77e10c4f2e6a8a063 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git symbolic-ref' [-m <reason>] <name> <ref>
-'git symbolic-ref' [-q] [--short] <name>
+'git symbolic-ref' [-q] [--short] [--no-recurse] <name>
 'git symbolic-ref' --delete [-q] <name>
 
 DESCRIPTION
@@ -27,7 +27,7 @@ symbolic ref.
 
 A symbolic ref is a regular file that stores a string that
 begins with `ref: refs/`.  For example, your `.git/HEAD` is
-a regular file whose contents is `ref: refs/heads/master`.
+a regular file whose content is `ref: refs/heads/master`.
 
 OPTIONS
 -------
@@ -46,6 +46,15 @@ OPTIONS
        When showing the value of <name> as a symbolic ref, try to shorten the
        value, e.g. from `refs/heads/master` to `master`.
 
+--recurse::
+--no-recurse::
+       When showing the value of <name> as a symbolic ref, if
+       <name> refers to another symbolic ref, follow such a chain
+       of symbolic refs until the result no longer points at a
+       symbolic ref (`--recurse`, which is the default).
+       `--no-recurse` stops after dereferencing only a single level
+       of symbolic ref.
+
 -m::
        Update the reflog for <name> with <reason>.  This is valid only
        when creating or updating a symbolic ref.
index 31a97a1b6c5b22ab5c8b959a8a550126f26146d2..d42efb3112787f943559723b9b19915df8181d5e 100644 (file)
@@ -9,7 +9,7 @@ git-tag - Create, list, delete or verify a tag object signed with GPG
 SYNOPSIS
 --------
 [verse]
-'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>] [-e]
+'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]
        <tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
@@ -26,19 +26,19 @@ to delete, list or verify tags.
 
 Unless `-f` is given, the named tag must not yet exist.
 
-If one of `-a`, `-s`, or `-u <keyid>` is passed, the command
+If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
 creates a 'tag' object, and requires a tag message.  Unless
 `-m <msg>` or `-F <file>` is given, an editor is started for the user to type
 in the tag message.
 
-If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <keyid>`
+If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
 are absent, `-a` is implied.
 
 Otherwise, a tag reference that points directly at the given object
 (i.e., a lightweight tag) is created.
 
 A GnuPG signed tag object will be created when `-s` or `-u
-<keyid>` is used.  When `-u <keyid>` is not used, the
+<key-id>` is used.  When `-u <key-id>` is not used, the
 committer identity for the current user is used to find the
 GnuPG key for signing.         The configuration variable `gpg.program`
 is used to specify custom GnuPG binary.
@@ -72,8 +72,8 @@ OPTIONS
        Override `tag.gpgSign` configuration variable that is
        set to force each and every tag to be signed.
 
--u <keyid>::
---local-user=<keyid>::
+-u <key-id>::
+--local-user=<key-id>::
        Make a GPG-signed tag, using the given key.
 
 -f::
@@ -131,6 +131,10 @@ options for details.
 --ignore-case::
        Sorting and filtering tags are case insensitive.
 
+--omit-empty::
+       Do not print a newline after formatted refs where the format expands
+       to the empty string.
+
 --column[=<options>]::
 --no-column::
        Display tag listing in columns. See configuration variable
@@ -164,14 +168,14 @@ This option is only applicable when listing tags without annotation lines.
        Use the given tag message (instead of prompting).
        If multiple `-m` options are given, their values are
        concatenated as separate paragraphs.
-       Implies `-a` if none of `-a`, `-s`, or `-u <keyid>`
+       Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
        is given.
 
 -F <file>::
 --file=<file>::
        Take the tag message from the given file.  Use '-' to
        read the message from the standard input.
-       Implies `-a` if none of `-a`, `-s`, or `-u <keyid>`
+       Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
        is given.
 
 -e::
@@ -220,7 +224,7 @@ it in the repository configuration as follows:
 
 -------------------------------------
 [user]
-    signingKey = <gpg-keyid>
+    signingKey = <gpg-key_id>
 -------------------------------------
 
 `pager.tag` is only respected when listing tags, i.e., when `-l` is
@@ -377,6 +381,16 @@ $ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
 
 include::date-formats.txt[]
 
+FILES
+-----
+
+`$GIT_DIR/TAG_EDITMSG`::
+       This file contains the message of an in-progress annotated
+       tag. If `git tag` exits due to an error before creating an
+       annotated tag then the tag message that has been provided by the
+       user in an editor session will be available in this file, but
+       may be overwritten by the next invocation of `git tag`.
+
 NOTES
 -----
 
index f4bb9c5daf95c6669210429d7a560565dadc91d5..8c47890a6a89bd7c0dbb2e3d259961262e4db3f7 100644 (file)
@@ -49,7 +49,7 @@ OPTIONS
 --remove::
        If a specified file is in the index but is missing then it's
        removed.
-       Default behavior is to ignore removed file.
+       Default behavior is to ignore removed files.
 
 --refresh::
        Looks at the current index and checks to see if merges or
@@ -95,7 +95,7 @@ OPTIONS
        the index.  If you want to change the working tree file,
        you need to unset the bit to tell Git.  This is
        sometimes helpful when working with a big project on a
-       filesystem that has very slow lstat(2) system call
+       filesystem that has very slow lstat(2) system call
        (e.g. cifs).
 +
 Git will fail (gracefully) in case it needs to modify this file
@@ -108,7 +108,7 @@ you will need to handle the situation manually.
        without regard to the "assume unchanged" setting.
 
 --[no-]skip-worktree::
-       When one of these flags is specified, the object name recorded
+       When one of these flags is specified, the object names recorded
        for the paths are not updated. Instead, these options
        set and unset the "skip-worktree" bit for the paths. See
        section "Skip-worktree bit" below for more information.
@@ -119,7 +119,7 @@ you will need to handle the situation manually.
        the `--remove` option was specified.
 
 --[no-]fsmonitor-valid::
-       When one of these flags is specified, the object name recorded
+       When one of these flags is specified, the object names recorded
        for the paths are not updated. Instead, these options
        set and unset the "fsmonitor valid" bit for the paths. See
        section "File System Monitor" below for more information.
@@ -127,7 +127,7 @@ you will need to handle the situation manually.
 -g::
 --again::
        Runs 'git update-index' itself on the paths whose index
-       entries are different from those from the `HEAD` commit.
+       entries are different from those of the `HEAD` commit.
 
 --unresolve::
        Restores the 'unmerged' or 'needs updating' state of a
@@ -151,24 +151,30 @@ you will need to handle the situation manually.
        automatically removed with warning messages.
 
 --stdin::
-       Instead of taking list of paths from the command line,
-       read list of paths from the standard input.  Paths are
+       Instead of taking list of paths from the command line,
+       read list of paths from the standard input.  Paths are
        separated by LF (i.e. one path per line) by default.
 
 --verbose::
-        Report what is being added and removed from index.
+       Report what is being added and removed from the index.
 
 --index-version <n>::
        Write the resulting index out in the named on-disk format version.
-       Supported versions are 2, 3 and 4. The current default version is 2
+       Supported versions are 2, 3, and 4. The current default version is 2
        or 3, depending on whether extra features are used, such as
-       `git add -N`.
+       `git add -N`.  With `--verbose`, also report the version the index
+       file uses before and after this command.
 +
 Version 4 performs a simple pathname compression that reduces index
 size by 30%-50% on large repositories, which results in faster load
-time. Version 4 is relatively young (first released in 1.8.0 in
-October 2012). Other Git implementations such as JGit and libgit2
-may not support it yet.
+time.  Git supports it since version 1.8.0, released in October 2012,
+and support for it was added to libgit2 in 2016 and to JGit in 2020.
+Older versions of this manual page called it "relatively young", but
+it should be considered mature technology these days.
+
+--show-index-version::
+       Report the index format version used by the on-disk index file.
+       See `--index-version` above.
 
 -z::
        Only meaningful with `--stdin` or `--index-info`; paths are
index 48b6683071e65be1abf29ece20dc90c5f0579ec3..0561808cca04a6873909ad18a6fd5f6e9af8b8de 100644 (file)
@@ -118,7 +118,7 @@ verify::
        <oldvalue> is zero or missing, the ref must not exist.
 
 option::
-       Modify behavior of the next command naming a <ref>.
+       Modify the behavior of the next command naming a <ref>.
        The only valid option is `no-deref` to avoid dereferencing
        a symbolic ref.
 
index 969bb2e15f1070ddc116b408b1eba9c61d496606..6bc9b50d89f7aac6921e6b9fd9b2659d1d81d3af 100644 (file)
@@ -9,7 +9,7 @@ git-update-server-info - Update auxiliary info file to help dumb servers
 SYNOPSIS
 --------
 [verse]
-'git update-server-info'
+'git update-server-info' [-f | --force]
 
 DESCRIPTION
 -----------
@@ -19,11 +19,17 @@ $GIT_OBJECT_DIRECTORY/info directories to help clients discover
 what references and packs the server has.  This command
 generates such auxiliary files.
 
+OPTIONS
+-------
+-f::
+--force::
+       Update the info files from scratch.
+
 OUTPUT
 ------
 
 Currently the command updates the following files.  Please see
-linkgit:gitrepository-layout[5] for description of
+linkgit:gitrepository-layout[5] for description of
 what they are for:
 
 * objects/info/packs
index fba0f1c1b27c629b31c86f78fbe22fdfd3ce360d..e8eb10baad79dd8648efbaab9e302114a65d90f9 100644 (file)
@@ -9,7 +9,7 @@ git-upload-archive - Send archive back to git-archive
 SYNOPSIS
 --------
 [verse]
-'git upload-archive' <directory>
+'git upload-archive' <repository>
 
 DESCRIPTION
 -----------
@@ -54,7 +54,7 @@ access via non-smart-http.
 
 OPTIONS
 -------
-<directory>::
+<repository>::
        The repository to get a tar archive from.
 
 GIT
index b656b4756752f6d91098b85d2766a80e86ca092a..7ad60bc3485bc80606fbf38bb13a4ba0e8f0902e 100644 (file)
@@ -26,7 +26,7 @@ OPTIONS
 -------
 
 --[no-]strict::
-       Do not try <directory>/.git/ if <directory> is no Git directory.
+       Do not try <directory>/.git/ if <directory> is not a Git directory.
 
 --timeout=<n>::
        Interrupt transfer after <n> seconds of inactivity.
index 387cc1b91420f78da6d16016af8cdc757db6dd44..0680568dfda732e9786011cd7b8088a91fca1662 100644 (file)
@@ -9,16 +9,17 @@ git-var - Show a Git logical variable
 SYNOPSIS
 --------
 [verse]
-'git var' ( -l | <variable> )
+'git var' (-l | <variable>)
 
 DESCRIPTION
 -----------
-Prints a Git logical variable.
+Prints a Git logical variable. Exits with code 1 if the variable has
+no value.
 
 OPTIONS
 -------
 -l::
-       Cause the logical variables to be listed. In addition, all the
+       Display the logical variables. In addition, all the
        variables of the Git configuration file .git/config are listed
        as well. (However, the configuration variables listing functionality
        is deprecated in favor of `git config -l`.)
@@ -49,6 +50,14 @@ ifdef::git-default-editor[]
     The build you are using chose '{git-default-editor}' as the default.
 endif::git-default-editor[]
 
+GIT_SEQUENCE_EDITOR::
+    Text editor used to edit the 'todo' file while running `git rebase
+    -i`. Like `GIT_EDITOR`, the value is meant to be interpreted by
+    the shell when it is used. The order of preference is the
+    `$GIT_SEQUENCE_EDITOR` environment variable, then
+    `sequence.editor` configuration, and then the value of `git var
+    GIT_EDITOR`.
+
 GIT_PAGER::
     Text viewer for use by Git commands (e.g., 'less').  The value
     is meant to be interpreted by the shell.  The order of preference
@@ -62,6 +71,29 @@ endif::git-default-pager[]
 GIT_DEFAULT_BRANCH::
     The name of the first branch created in newly initialized repositories.
 
+GIT_SHELL_PATH::
+    The path of the binary providing the POSIX shell for commands which use the shell.
+
+GIT_ATTR_SYSTEM::
+    The path to the system linkgit:gitattributes[5] file, if one is enabled.
+
+GIT_ATTR_GLOBAL::
+    The path to the global (per-user) linkgit:gitattributes[5] file.
+
+GIT_CONFIG_SYSTEM::
+    The path to the system configuration file, if one is enabled.
+
+GIT_CONFIG_GLOBAL::
+    The path to the global (per-user) configuration files, if any.
+
+Most path values contain only one value. However, some can contain multiple
+values, which are separated by newlines, and are listed in order from highest to
+lowest priority.  Callers should be prepared for any such path value to contain
+multiple items.
+
+Note that paths are printed even if they do not exist, but not if they are
+disabled by other environment variables.
+
 SEE ALSO
 --------
 linkgit:git-commit-tree[1]
index 92097f6673d8e72229b42c33227353ad5226f17e..aee4c40eac4666ebacb4cc2fd67e99b8d7e2fbfb 100644 (file)
@@ -8,7 +8,7 @@ git-verify-commit - Check the GPG signature of commits
 SYNOPSIS
 --------
 [verse]
-'git verify-commit' <commit>...
+'git verify-commit' [-v | --verbose] [--raw] <commit>...
 
 DESCRIPTION
 -----------
index 61ca6d04c206dc5667ffe83b8dd3c9cc4d1ab31c..d7e886918aa7af8accc73737e51cacc38118f747 100644 (file)
@@ -9,13 +9,13 @@ git-verify-pack - Validate packed Git archive files
 SYNOPSIS
 --------
 [verse]
-'git verify-pack' [-v|--verbose] [-s|--stat-only] [--] <pack>.idx ...
+'git verify-pack' [-v | --verbose] [-s | --stat-only] [--] <pack>.idx...
 
 
 DESCRIPTION
 -----------
 Reads given idx file for packed Git archive created with the
-'git pack-objects' command and verifies idx file and the
+'git pack-objects' command and verifies the idx file and the
 corresponding pack file.
 
 OPTIONS
@@ -25,13 +25,13 @@ OPTIONS
 
 -v::
 --verbose::
-       After verifying the pack, show list of objects contained
+       After verifying the pack, show the list of objects contained
        in the pack and a histogram of delta chain length.
 
 -s::
 --stat-only::
        Do not verify the pack contents; only show the histogram of delta
-       chain length.  With `--verbose`, list of objects is also shown.
+       chain length.  With `--verbose`, the list of objects is also shown.
 
 \--::
        Do not interpret any more arguments as options.
index 0b8075dad965f58b159bbe45f18d94bcc9f5ae60..81d50ecc4c6879ac486ab110038a41acd422717e 100644 (file)
@@ -8,7 +8,7 @@ git-verify-tag - Check the GPG signature of tags
 SYNOPSIS
 --------
 [verse]
-'git verify-tag' [--format=<format>] <tag>...
+'git verify-tag' [-v | --verbose] [--format=<format>] [--raw] <tag>...
 
 DESCRIPTION
 -----------
index 8b63ceb00e71a5e2f09cf7686b0978dcf71d1e03..8e55e0bb1ec8a6aaeaef5f6b615929850f63c358 100644 (file)
@@ -3,7 +3,7 @@ git-whatchanged(1)
 
 NAME
 ----
-git-whatchanged - Show logs with difference each commit introduces
+git-whatchanged - Show logs with differences each commit introduces
 
 
 SYNOPSIS
@@ -18,11 +18,11 @@ Shows commit logs and diff output each commit introduces.
 
 New users are encouraged to use linkgit:git-log[1] instead.  The
 `whatchanged` command is essentially the same as linkgit:git-log[1]
-but defaults to show the raw format diff output and to skip merges.
+but defaults to showing the raw format diff output and skipping merges.
 
-The command is kept primarily for historical reasons; fingers of
+The command is primarily kept for historical reasons; fingers of
 many people who learned Git long before `git log` was invented by
-reading Linux kernel mailing list are trained to type it.
+reading the Linux kernel mailing list are trained to type it.
 
 
 Examples
index ada30c86a7c2139cccd1315370dc2f63c4472e26..2a240f53ba7f987b367a7fef689fe916543dedfd 100644 (file)
@@ -9,7 +9,8 @@ git-worktree - Manage multiple working trees
 SYNOPSIS
 --------
 [verse]
-'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]
+'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]]
+                  [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]
 'git worktree list' [-v | --porcelain [-z]]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree move' <worktree> <new-path>
@@ -94,6 +95,16 @@ exist, a new branch based on `HEAD` is automatically created as if
 `-b <branch>` was given.  If `<branch>` does exist, it will be checked out
 in the new worktree, if it's not checked out anywhere else, otherwise the
 command will refuse to create the worktree (unless `--force` is used).
++
+If `<commit-ish>` is omitted, neither `--detach`, or `--orphan` is
+used, and there are no valid local branches (or remote branches if
+`--guess-remote` is specified) then, as a convenience, the new worktree is
+associated with a new unborn branch named `<branch>` (after
+`$(basename <path>)` if neither `-b` or `-B` is used) as if `--orphan` was
+passed to the command. In the event the repository has a remote and
+`--guess-remote` is used, but no remote or local branches exist, then the
+command fails with a warning reminding the user to fetch from their remote
+first (or override by using `-f/--force`).
 
 list::
 
@@ -221,6 +232,10 @@ This can also be set up as the default behaviour by using the
        With `prune`, do not remove anything; just report what it would
        remove.
 
+--orphan::
+       With `add`, make the new worktree and index empty, associating
+       the worktree with a new unborn branch named `<new-branch>`.
+
 --porcelain::
        With `list`, output in an easy-to-parse format for scripts.
        This format will remain stable across Git versions and regardless of user
@@ -271,7 +286,8 @@ rules and how to access refs of one worktree from another.
 In general, all pseudo refs are per-worktree and all refs starting with
 `refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
 under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
-however: refs inside `refs/bisect` and `refs/worktree` are not shared.
+however: refs inside `refs/bisect`, `refs/worktree` and `refs/rewritten` are
+not shared.
 
 Refs that are per-worktree can still be accessed from another worktree via
 two special paths, `main-worktree` and `worktrees`. The former gives
@@ -348,8 +364,8 @@ linked worktree `git rev-parse --git-path HEAD` returns
 `/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git
 rev-parse --git-path refs/heads/master` uses
 `$GIT_COMMON_DIR` and returns `/path/main/.git/refs/heads/master`,
-since refs are shared across all worktrees, except `refs/bisect` and
-`refs/worktree`.
+since refs are shared across all worktrees, except `refs/bisect`,
+`refs/worktree` and `refs/rewritten`.
 
 See linkgit:gitrepository-layout[5] for more information. The rule of
 thumb is do not make any assumption about whether a path belongs to
index 1d33e083ab8ba104641001cd6e85b49505c3c374..da4e8d1303a07c85964acfbd564ed4c521140cbb 100644 (file)
@@ -13,8 +13,7 @@ SYNOPSIS
     [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
     [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
     [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
-    [--super-prefix=<path>] [--config-env=<name>=<envvar>]
-    <command> [<args>]
+    [--config-env=<name>=<envvar>] <command> [<args>]
 
 DESCRIPTION
 -----------
@@ -97,9 +96,9 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config
        to avoid ambiguity with `<name>` containing one.
 +
 This is useful for cases where you want to pass transitory
-configuration options to git, but are doing so on OS's where
-other processes might be able to read your cmdline
-(e.g. `/proc/self/cmdline`), but not your environ
+configuration options to git, but are doing so on operating systems
+where other processes might be able to read your command line
+(e.g. `/proc/self/cmdline`), but not your environment
 (e.g. `/proc/self/environ`). That behavior is the default on
 Linux, but may not be on your system.
 +
@@ -169,11 +168,6 @@ If you just want to run git as if it was started in `<path>` then use
        details.  Equivalent to setting the `GIT_NAMESPACE` environment
        variable.
 
---super-prefix=<path>::
-       Currently for internal use only.  Set a prefix which gives a path from
-       above a repository down to its root.  One use is to give submodules
-       context about the superproject that invoked it.
-
 --bare::
        Treat the repository as a bare repository.  If GIT_DIR
        environment is not set, it is set to the current working
@@ -218,6 +212,11 @@ If you just want to run git as if it was started in `<path>` then use
        nohelpers (exclude helper commands), alias and config
        (retrieve command list from config variable completion.commands)
 
+--attr-source=<tree-ish>::
+       Read gitattributes from <tree-ish> instead of the worktree. See
+       linkgit:gitattributes[5]. This is equivalent to setting the
+       `GIT_ATTR_SOURCE` environment variable.
+
 GIT COMMANDS
 ------------
 
@@ -552,10 +551,15 @@ double-quotes and respecting backslash escapes. E.g., the value
 
 `GIT_DEFAULT_HASH`::
        If this variable is set, the default hash algorithm for new
-       repositories will be set to this value. This value is currently
-       ignored when cloning; the setting of the remote repository
-       is used instead. The default is "sha1". THIS VARIABLE IS
-       EXPERIMENTAL! See `--object-format` in linkgit:git-init[1].
+       repositories will be set to this value. This value is
+       ignored when cloning and the setting of the remote repository
+       is always used. The default is "sha1".
+       See `--object-format` in linkgit:git-init[1].
+
+`GIT_DEFAULT_REF_FORMAT`::
+       If this variable is set, the default reference backend format for new
+       repositories will be set to this value. The default is "files".
+       See `--ref-format` in linkgit:git-init[1].
 
 Git Commits
 ~~~~~~~~~~~
@@ -619,7 +623,7 @@ The file parameters can point at the user's working file
 (e.g. `new-file` in "git-diff-files"), `/dev/null` (e.g. `old-file`
 when a new file is added), or a temporary file (e.g. `old-file` in the
 index).  `GIT_EXTERNAL_DIFF` should not worry about unlinking the
-temporary file --- it is removed when `GIT_EXTERNAL_DIFF` exits.
+temporary file -- it is removed when `GIT_EXTERNAL_DIFF` exits.
 +
 For a path that is unmerged, `GIT_EXTERNAL_DIFF` is called with 1
 parameter, <path>.
@@ -692,6 +696,9 @@ for further details.
        tells Git not to verify the SSL certificate when fetching or
        pushing over HTTPS.
 
+`GIT_ATTR_SOURCE`::
+       Sets the treeish that gitattributes will be read from.
+
 `GIT_ASKPASS`::
        If this environment variable is set, then Git commands which need to
        acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
@@ -722,13 +729,12 @@ for further details.
        waiting for someone with sufficient permissions to fix it.
 
 `GIT_FLUSH`::
-// NEEDSWORK: make it into a usual Boolean environment variable
-       If this environment variable is set to "1", then commands such
+       If this Boolean environment variable is set to true, then commands such
        as 'git blame' (in incremental mode), 'git rev-list', 'git log',
        'git check-attr' and 'git check-ignore' will
        force a flush of the output stream after each record have been
        flushed. If this
-       variable is set to "0", the output of these commands will be done
+       variable is set to false, the output of these commands will be done
        using completely buffered I/O.   If this environment variable is
        not set, Git will choose buffered or record-oriented flushing
        based on whether stdout appears to be redirected to a file or not.
@@ -909,6 +915,16 @@ for full details.
        should not normally need to set this to `0`, but it may be
        useful when trying to salvage data from a corrupted repository.
 
+`GIT_COMMIT_GRAPH_PARANOIA`::
+       When loading a commit object from the commit-graph, Git performs an
+       existence check on the object in the object database. This is done to
+       avoid issues with stale commit-graphs that contain references to
+       already-deleted commits, but comes with a performance penalty.
++
+The default is "false", which disables the aforementioned behavior.
+Setting this to "true" enables the existence check so that stale commits
+will never be returned from the commit-graph at the cost of performance.
+
 `GIT_ALLOW_PROTOCOL`::
        If set to a colon-separated list of protocols, behave as if
        `protocol.allow` is set to `never`, and each of the listed
@@ -1013,10 +1029,11 @@ When first created, objects are stored in individual files, but for
 efficiency may later be compressed together into "pack files".
 
 Named pointers called refs mark interesting points in history.  A ref
-may contain the SHA-1 name of an object or the name of another ref.  Refs
-with names beginning `ref/head/` contain the SHA-1 name of the most
+may contain the SHA-1 name of an object or the name of another ref (the
+latter is called a "symbolic ref").
+Refs with names beginning `refs/head/` contain the SHA-1 name of the most
 recent commit (or "head") of a branch under development.  SHA-1 names of
-tags of interest are stored under `ref/tags/`.  A special ref named
+tags of interest are stored under `refs/tags/`.  A symbolic ref named
 `HEAD` contains the name of the currently checked-out branch.
 
 The index file is initialized with a list of all paths and, for each
@@ -1059,7 +1076,7 @@ Authors
 -------
 Git was started by Linus Torvalds, and is currently maintained by Junio
 C Hamano. Numerous contributions have come from the Git mailing list
-<git@vger.kernel.org>.  http://www.openhub.net/p/git/contributors/summary
+<git@vger.kernel.org>.  https://openhub.net/p/git/contributors/summary
 gives you a more complete list of contributors.
 
 If you have a clone of git.git itself, the
index 4b36d51beb66f08bff4576ed3afcbc9a6d78d63a..201bdf5edbd1badb08e73d32062b1c61ce38a4c3 100644 (file)
@@ -100,6 +100,21 @@ for a path to `Unspecified` state.  This can be done by listing
 the name of the attribute prefixed with an exclamation point `!`.
 
 
+RESERVED BUILTIN_* ATTRIBUTES
+-----------------------------
+
+builtin_* is a reserved namespace for builtin attribute values. Any
+user defined attributes under this namespace will be ignored and
+trigger a warning.
+
+`builtin_objectmode`
+~~~~~~~~~~~~~~~~~~~~
+This attribute is for filtering files by their file bit modes (40000,
+120000, 160000, 100755, 100644). e.g. ':(attr:builtin_objectmode=160000)'.
+You may also check these values with `git check-attr builtin_objectmode -- <file>`.
+If the object is not in the index `git check-attr --cached` will return unspecified.
+
+
 EFFECTS
 -------
 
@@ -120,20 +135,19 @@ repository upon 'git add' and 'git commit'.
 `text`
 ^^^^^^
 
-This attribute enables and controls end-of-line normalization.  When a
-text file is normalized, its line endings are converted to LF in the
-repository.  To control what line ending style is used in the working
-directory, use the `eol` attribute for a single file and the
-`core.eol` configuration variable for all text files.
-Note that setting `core.autocrlf` to `true` or `input` overrides
-`core.eol` (see the definitions of those options in
-linkgit:git-config[1]).
+This attribute marks the path as a text file, which enables end-of-line
+conversion: When a matching file is added to the index, the file's line
+endings are normalized to LF in the index.  Conversely, when the file is
+copied from the index to the working directory, its line endings may be
+converted from LF to CRLF depending on the `eol` attribute, the Git
+config, and the platform (see explanation of `eol` below).
 
 Set::
 
        Setting the `text` attribute on a path enables end-of-line
-       normalization and marks the path as a text file.  End-of-line
-       conversion takes place without guessing the content type.
+       conversion on checkin and checkout as described above.  Line endings
+       are normalized to LF in the index every time the file is checked in,
+       even if the file was previously added to Git with CRLF line endings.
 
 Unset::
 
@@ -142,10 +156,11 @@ Unset::
 
 Set to string value "auto"::
 
-       When `text` is set to "auto", the path is marked for automatic
-       end-of-line conversion.  If Git decides that the content is
-       text, its line endings are converted to LF on checkin.
-       When the file has been committed with CRLF, no conversion is done.
+       When `text` is set to "auto", Git decides by itself whether the file
+       is text or binary.  If it is text and the file was not already in
+       Git with CRLF endings, line endings are converted on checkin and
+       checkout as described above.  Otherwise, no conversion is done on
+       checkin or checkout.
 
 Unspecified::
 
@@ -159,26 +174,29 @@ unspecified.
 `eol`
 ^^^^^
 
-This attribute sets a specific line-ending style to be used in the
-working directory.  This attribute has effect only if the `text`
-attribute is set or unspecified, or if it is set to `auto`, the file is
-detected as text, and it is stored with LF endings in the index.  Note
-that setting this attribute on paths which are in the index with CRLF
-line endings may make the paths to be considered dirty unless
-`text=auto` is set. Adding the path to the index again will normalize
-the line endings in the index.
+This attribute marks a path to use a specific line-ending style in the
+working tree when it is checked out.  It has effect only if `text` or
+`text=auto` is set (see above), but specifying `eol` automatically sets
+`text` if `text` was left unspecified.
 
 Set to string value "crlf"::
 
-       This setting forces Git to normalize line endings for this
-       file on checkin and convert them to CRLF when the file is
-       checked out.
+       This setting converts the file's line endings in the working
+       directory to CRLF when the file is checked out.
 
 Set to string value "lf"::
 
-       This setting forces Git to normalize line endings to LF on
-       checkin and prevents conversion to CRLF when the file is
-       checked out.
+       This setting uses the same line endings in the working directory as
+       in the index when the file is checked out.
+
+Unspecified::
+
+       If the `eol` attribute is unspecified for a file, its line endings
+       in the working directory are determined by the `core.autocrlf` or
+       `core.eol` configuration variable (see the definitions of those
+       options in linkgit:git-config[1]).  If `text` is set but neither of
+       those variables is, the default is `eol=crlf` on Windows and
+       `eol=lf` on all other platforms.
 
 Backwards compatibility with `crlf` attribute
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -758,6 +776,37 @@ with the above configuration, i.e. `j-c-diff`, with 7
 parameters, just like `GIT_EXTERNAL_DIFF` program is called.
 See linkgit:git[1] for details.
 
+Setting the internal diff algorithm
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The diff algorithm can be set through the `diff.algorithm` config key, but
+sometimes it may be helpful to set the diff algorithm per path. For example,
+one may want to use the `minimal` diff algorithm for .json files, and the
+`histogram` for .c files, and so on without having to pass in the algorithm
+through the command line each time.
+
+First, in `.gitattributes`, assign the `diff` attribute for paths.
+
+------------------------
+*.json diff=<name>
+------------------------
+
+Then, define a "diff.<name>.algorithm" configuration to specify the diff
+algorithm, choosing from `myers`, `patience`, `minimal`, or `histogram`.
+
+----------------------------------------------------------------
+[diff "<name>"]
+  algorithm = histogram
+----------------------------------------------------------------
+
+This diff algorithm applies to user facing diff output like git-diff(1),
+git-show(1) and is used for the `--stat` output as well. The merge machinery
+will not use the diff algorithm set through this method.
+
+NOTE: If `diff.<name>.command` is defined for path with the
+`diff=<name>` attribute, it is executed as an external diff driver
+(see above), and adding `diff.<name>.algorithm` has no effect, as the
+algorithm is not passed to the external diff driver.
 
 Defining a custom hunk-header
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1098,7 +1147,10 @@ size (see below).
 The merge driver is expected to leave the result of the merge in
 the file named with `%A` by overwriting it, and exit with zero
 status if it managed to merge them cleanly, or non-zero if there
-were conflicts.
+were conflicts.  When the driver crashes (e.g. killed by SEGV),
+it is expected to exit with non-zero status that are higher than
+128, and in such a case, the merge results in a failure (which is
+different from producing a conflict).
 
 The `merge.*.recursive` variable specifies what other merge
 driver to use when the merge driver is called for an internal
@@ -1114,8 +1166,8 @@ will be stored via placeholder `%P`.
 ^^^^^^^^^^^^^^^^^^^^^^
 
 This attribute controls the length of conflict markers left in
-the work tree file during a conflicted merge.  Only setting to
-the value to a positive integer has any meaningful effect.
+the work tree file during a conflicted merge.  Only a positive
+integer has a meaningful effect.
 
 For example, this line in `.gitattributes` can be used to tell the merge
 machinery to leave much longer (instead of the usual 7-character-long)
@@ -1155,7 +1207,7 @@ Unspecified::
 
 String::
 
-       Specify a comma separate list of common whitespace problems to
+       Specify a comma separated list of common whitespace problems to
        notice in the same format as the `core.whitespace` configuration
        variable.
 
index 1819a5a1859c5479064823bd786e98267f7037b5..e5fac943227a23a5bf9214cddb08d48b0ce3c5ca 100644 (file)
@@ -23,10 +23,10 @@ arguments.  Here are the rules:
     A subcommand may take dashed options (which may take their own
     arguments, e.g. "--max-parents 2") and arguments.  You SHOULD
     give dashed options first and then arguments.  Some commands may
-    accept dashed options after you have already gave non-option
+    accept dashed options after you have already given non-option
     arguments (which may make the command ambiguous), but you should
     not rely on it (because eventually we may find a way to fix
-    these ambiguity by enforcing the "options then args" rule).
+    these ambiguities by enforcing the "options then args" rule).
 
  * Revisions come first and then paths.
    E.g. in `git diff v1.0 v2.0 arch/x86 include/asm-x86`,
@@ -37,12 +37,12 @@ arguments.  Here are the rules:
    they can be disambiguated by placing `--` between them.
    E.g. `git diff -- HEAD` is, "I have a file called HEAD in my work
    tree.  Please show changes between the version I staged in the index
-   and what I have in the work tree for that file", not "show difference
+   and what I have in the work tree for that file", not "show the difference
    between the HEAD commit and the work tree as a whole".  You can say
    `git diff HEAD --` to ask for the latter.
 
  * Without disambiguating `--`, Git makes a reasonable guess, but errors
-   out and asking you to disambiguate when ambiguous.  E.g. if you have a
+   out and asks you to disambiguate when ambiguous.  E.g. if you have a
    file called HEAD in your work tree, `git diff HEAD` is ambiguous, and
    you have to say either `git diff HEAD --` or `git diff -- HEAD` to
    disambiguate.
index c0b95256cc8c8d640d284f77b23d63c8785601d1..2122aeb976915750b843b89d49210e7c934cf2c8 100644 (file)
@@ -1089,7 +1089,7 @@ the remote repository URL in the local repository's config file
 like this:
 
 ------------------------------------------------
-$ git config remote.linus.url http://www.kernel.org/pub/scm/git/git.git/
+$ git config remote.linus.url https://git.kernel.org/pub/scm/git/git.git/
 ------------------------------------------------
 
 and use the "linus" keyword with 'git pull' instead of the full URL.
index 80517b4eb2cb259d7b99528902e3192db2f4166d..71dd19731af0dfb187ac55b15826e6b4b3e887b6 100644 (file)
@@ -17,9 +17,10 @@ DESCRIPTION
 
 Git will sometimes need credentials from the user in order to perform
 operations; for example, it may need to ask for a username and password
-in order to access a remote repository over HTTP. This manual describes
-the mechanisms Git uses to request these credentials, as well as some
-features to avoid inputting these credentials repeatedly.
+in order to access a remote repository over HTTP. Some remotes accept
+a personal access token or OAuth access token as a password. This
+manual describes the mechanisms Git uses to request these credentials,
+as well as some features to avoid inputting these credentials repeatedly.
 
 REQUESTING CREDENTIALS
 ----------------------
@@ -61,7 +62,9 @@ for a password. It is generally configured by adding this to your config:
 
 Credential helpers, on the other hand, are external programs from which Git can
 request both usernames and passwords; they typically interface with secure
-storage provided by the OS or other programs.
+storage provided by the OS or other programs. Alternatively, a
+credential-generating helper might generate credentials for certain servers via
+some API.
 
 To use a helper, you must first select one to use. Git currently
 includes the following helpers:
@@ -101,6 +104,17 @@ $ git help credential-foo
 $ git config --global credential.helper foo
 -------------------------------------------
 
+=== Available helpers
+
+The community maintains a comprehensive list of Git credential helpers at
+https://git-scm.com/doc/credential-helpers.
+
+=== OAuth
+
+An alternative to inputting passwords or personal access tokens is to use an
+OAuth credential helper. Initial authentication opens a browser window to the
+host. Subsequent authentication happens in the background. Many popular Git
+hosts support OAuth.
 
 CREDENTIAL CONTEXTS
 -------------------
@@ -164,7 +178,7 @@ helper::
 If there are multiple instances of the `credential.helper` configuration
 variable, each helper will be tried in turn, and may provide a username,
 password, or nothing. Once Git has acquired both a username and a
-password, no more helpers will be tried.
+non-expired password, no more helpers will be tried.
 +
 If `credential.helper` is configured to the empty string, this resets
 the helper list to empty (so you may override a helper set by a
@@ -257,7 +271,7 @@ appended to its command line, which is one of:
 
 `erase`::
 
-       Remove a matching credential, if any, from the helper's storage.
+       Remove matching credentials, if any, from the helper's storage.
 
 The details of the credential will be provided on the helper's stdin
 stream. The exact format is the same as the input/output format of the
@@ -269,6 +283,7 @@ stdout in the same format (see linkgit:git-credential[1] for common
 attributes). A helper is free to produce a subset, or even no values at
 all if it has nothing useful to provide. Any provided attributes will
 overwrite those already known about by Git's credential subsystem.
+Unrecognised attributes are silently discarded.
 
 While it is possible to override all attributes, well behaving helpers
 should refrain from doing so for any attribute other than username and
@@ -286,8 +301,8 @@ For a `store` or `erase` operation, the helper's output is ignored.
 If a helper fails to perform the requested operation or needs to notify
 the user of a potential issue, it may write to stderr.
 
-If it does not support the requested operation (e.g., a read-only store),
-it should silently ignore the request.
+If it does not support the requested operation (e.g., a read-only store
+or generator), it should silently ignore the request.
 
 If a helper receives any other operation, it should silently ignore the
 request. This leaves room for future operations to be added (older
index 0d57f86abc4a51c9665f88ff34103bbe94f09009..3cda2e07c24a96af8ff1c08c46780b5fdb490f4f 100644 (file)
@@ -173,7 +173,7 @@ Note that when rename detection is on but both copy and break
 detection are off, rename detection adds a preliminary step that first
 checks if files are moved across directories while keeping their
 filename the same.  If there is a file added to a directory whose
-contents is sufficiently similar to a file with the same name that got
+contents are sufficiently similar to a file with the same name that got
 deleted from a different directory, it will mark them as renames and
 exclude them from the later quadratic step (the one that pairwise
 compares all unmatched files to find the "best" matches, determined by
@@ -213,7 +213,7 @@ from the original, and does not count insertion.  If you removed
 only 10 lines from a 100-line document, even if you added 910
 new lines to make a new 1000-line document, you did not do a
 complete rewrite.  diffcore-break breaks such a case in order to
-help diffcore-rename to consider such filepairs as candidate of
+help diffcore-rename to consider such filepairs as candidate of
 rename/copy detection, but if filepairs broken that way were not
 matched with other filepairs to create rename/copy, then this
 transformation merges them back into the original
@@ -230,13 +230,13 @@ like these:
 
 * -B/60 (the same as above, since diffcore-break defaults to 50%).
 
-Note that earlier implementation left a broken pair as separate
-creation and deletion patches.  This was an unnecessary hack and
+Note that earlier implementation left a broken pair as separate
+creation and deletion patches.  This was an unnecessary hack, and
 the latest implementation always merges all the broken pairs
 back into modifications, but the resulting patch output is
 formatted differently for easier review in case of such
-a complete rewrite by showing the entire contents of old version
-prefixed with '-', followed by the entire contents of new
+a complete rewrite by showing the entire contents of the old version
+prefixed with '-', followed by the entire contents of the new
 version prefixed with '+'.
 
 
@@ -263,7 +263,7 @@ textual diff has an added or a deleted line that matches the given
 regular expression.  This means that it will detect in-file (or what
 rename-detection considers the same file) moves, which is noise.  The
 implementation runs diff twice and greps, and this can be quite
-expensive.  To speed things up binary files without textconv filters
+expensive.  To speed things up, binary files without textconv filters
 will be ignored.
 
 When `-S` or `-G` are used without `--pickaxe-all`, only filepairs
index faba2ef0881c52e8c6c2e5ec0f5702c0b1ba5028..6cfdd0e07b46f269c059e38409dffe45d44a59f9 100644 (file)
@@ -14,7 +14,7 @@ DESCRIPTION
 -----------
 
 Git users can broadly be grouped into four categories for the purposes of
-describing here a small set of useful command for everyday Git.
+describing here a small set of useful commands for everyday Git.
 
 *      <<STANDALONE,Individual Developer (Standalone)>> commands are essential
        for anybody who makes a commit, even for somebody who works alone.
@@ -229,7 +229,7 @@ without a formal "merging". Or longhand +
   git am -3 -k`
 
 An alternate participant submission mechanism is using the
-`git request-pull` or pull-request mechanisms (e.g as used on
+`git request-pull` or pull-request mechanisms (e.g. as used on
 GitHub (www.github.com) to notify your upstream of your
 contribution.
 
index 00e0a20e6571969ebeabca8da5d9815be65d682b..1b75cf71cec103f8a5e36056532f888d1ac58e71 100644 (file)
@@ -67,7 +67,7 @@ A Git bundle consists of several parts.
 * "Capabilities", which are only in the v3 format, indicate functionality that
        the bundle requires to be read properly.
 
-* "Prerequisites" lists the objects that are NOT included in the bundle and the
+* "Prerequisites" list the objects that are NOT included in the bundle and the
   reader of the bundle MUST already have, in order to use the data in the
   bundle. The objects stored in the bundle may refer to prerequisite objects and
   anything reachable from them (e.g. a tree object in the bundle can reference
@@ -86,10 +86,10 @@ In the bundle format, there can be a comment following a prerequisite obj-id.
 This is a comment and it has no specific meaning. The writer of the bundle MAY
 put any string here. The reader of the bundle MUST ignore the comment.
 
-Note on the shallow clone and a Git bundle
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Note on shallow clones and Git bundles
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Note that the prerequisites does not represent a shallow-clone boundary. The
+Note that the prerequisites do not represent a shallow-clone boundary. The
 semantics of the prerequisites and the shallow-clone boundaries are different,
 and the Git bundle v2 format cannot represent a shallow clone repository.
 
index 57202ede273ad266be910045294fd74853dd193c..3315df6201dc964886652c494b2123260de9f434 100644 (file)
@@ -42,7 +42,7 @@ Each row consists of a 4-byte chunk identifier (ID) and an 8-byte offset.
 Each integer is stored in network-byte order.
 
 The chunk identifier `ID[i]` is a label for the data stored within this
-fill from `OFFSET[i]` (inclusive) to `OFFSET[i+1]` (exclusive). Thus, the
+file from `OFFSET[i]` (inclusive) to `OFFSET[i+1]` (exclusive). Thus, the
 size of the `i`th chunk is equal to the difference between `OFFSET[i+1]`
 and `OFFSET[i]`. This requires that the chunk data appears contiguously
 in the same order as the table of contents.
@@ -67,7 +67,7 @@ caller is responsible for opening the `hashfile` and writing header
 information so the file format is identifiable before the chunk-based
 format begins.
 
-Then, call `add_chunk()` for each chunk that is intended for write. This
+Then, call `add_chunk()` for each chunk that is intended for writing. This
 populates the `chunkfile` with information about the order and size of
 each chunk to write. Provide a `chunk_write_fn` function pointer to
 perform the write of the chunk data upon request.
index 7324665716d7db7f352354f80c61406629264e95..31cad585e233031d205e5338bed74fcaf125de1f 100644 (file)
@@ -3,7 +3,7 @@ gitformat-commit-graph(5)
 
 NAME
 ----
-gitformat-commit-graph - Git commit graph format
+gitformat-commit-graph - Git commit-graph format
 
 SYNOPSIS
 --------
@@ -14,7 +14,7 @@ $GIT_DIR/objects/info/commit-graphs/*
 DESCRIPTION
 -----------
 
-The Git commit graph stores a list of commit OIDs and some associated
+The Git commit-graph stores a list of commit OIDs and some associated
 metadata, including:
 
 - The generation number of the commit.
@@ -34,7 +34,7 @@ corresponding to the array position within the list of commit OIDs. Due
 to some special constants we use to track parents, we can store at most
 (1 << 30) + (1 << 29) + (1 << 28) - 1 (around 1.8 billion) commits.
 
-== Commit graph files have the following format:
+== Commit-graph files have the following format:
 
 In order to allow extensions that add extra data to the graph, we organize
 the body into "chunks" and provide a binary lookup table at the beginning
index 015cb21bdc089a30e5403877ca1c61bea2b04e24..0773e5c3800392b8d4a548519b70379501207054 100644 (file)
@@ -83,11 +83,13 @@ Git index format
 
   32-bit mode, split into (high to low bits)
 
+    16-bit unused, must be zero
+
     4-bit object type
       valid values in binary are 1000 (regular file), 1010 (symbolic link)
       and 1110 (gitlink)
 
-    3-bit unused
+    3-bit unused, must be zero
 
     9-bit unix permission. Only 0755 and 0644 are valid for regular files.
     Symbolic links and gitlinks have value 0 in this field.
index e06af02f211e7a7a8488d1470d7f76dd58e5989c..d6ae229be5685950da9a8cff4bbe215c62e0c17c 100644 (file)
@@ -17,8 +17,8 @@ $GIT_DIR/objects/pack/multi-pack-index
 DESCRIPTION
 -----------
 
-The Git pack format is now Git stores most of its primary repository
-data. Over the lietime af a repository loose objects (if any) and
+The Git pack format is how Git stores most of its primary repository
+data. Over the lifetime of a repository, loose objects (if any) and
 smaller packs are consolidated into larger pack(s). See
 linkgit:git-gc[1] and linkgit:git-pack-objects[1].
 
@@ -48,7 +48,7 @@ Similarly, in SHA-256 repositories, these values are computed using SHA-256.
      Observation: we cannot have more than 4G versions ;-) and
      more than 4G objects in a pack.
 
-   - The header is followed by number of object entries, each of
+   - The header is followed by number of object entries, each of
      which looks like this:
 
      (undeltified representation)
@@ -62,7 +62,7 @@ Similarly, in SHA-256 repositories, these values are computed using SHA-256.
         is an OBJ_OFS_DELTA object
      compressed delta data
 
-     Observation: length of each object is encoded in a variable
+     Observation: the length of each object is encoded in a variable
      length format and is not constrained to 32-bit or anything.
 
   - The trailer records a pack checksum of all of the above.
@@ -117,7 +117,7 @@ the delta data is a sequence of instructions to reconstruct the object
 from the base object. If the base object is deltified, it must be
 converted to canonical form first. Each instruction appends more and
 more data to the target object until it's complete. There are two
-supported instructions so far: one for copy a byte range from the
+supported instructions so far: one for copying a byte range from the
 source object and one for inserting new data embedded in the
 instruction itself.
 
@@ -137,7 +137,7 @@ copy. Offset and size are in little-endian order.
 
 All offset and size bytes are optional. This is to reduce the
 instruction size when encoding small offsets or sizes. The first seven
-bits in the first octet determines which of the next seven octets is
+bits in the first octet determine which of the next seven octets is
 present. If bit zero is set, offset1 is present. If bit one is set
 offset2 is present and so on.
 
@@ -161,9 +161,9 @@ converted to 0x10000.
   | 0xxxxxxx |    data    |
   +----------+============+
 
-This is the instruction to construct target object without the base
+This is the instruction to construct the target object without the base
 object. The following data is appended to the target object. The first
-seven bits of the first octet determines the size of data in
+seven bits of the first octet determine the size of data in
 bytes. The size must be non-zero.
 
 ==== Reserved instruction
@@ -294,7 +294,7 @@ Pack file entry: <+
 
   - The same trailer as a v1 pack file:
 
-    A copy of the pack checksum at the end of
+    A copy of the pack checksum at the end of the
     corresponding packfile.
 
     Index checksum of all of the above.
@@ -390,10 +390,20 @@ CHUNK LOOKUP:
 CHUNK DATA:
 
        Packfile Names (ID: {'P', 'N', 'A', 'M'})
-           Stores the packfile names as concatenated, null-terminated strings.
-           Packfiles must be listed in lexicographic order for fast lookups by
-           name. This is the only chunk not guaranteed to be a multiple of four
-           bytes in length, so should be the last chunk for alignment reasons.
+           Store the names of packfiles as a sequence of NUL-terminated
+           strings. There is no extra padding between the filenames,
+           and they are listed in lexicographic order. The chunk itself
+           is padded at the end with between 0 and 3 NUL bytes to make the
+           chunk size a multiple of 4 bytes.
+
+       Bitmapped Packfiles (ID: {'B', 'T', 'M', 'P'})
+           Stores a table of two 4-byte unsigned integers in network order.
+           Each table entry corresponds to a single pack (in the order that
+           they appear above in the `PNAM` chunk). The values for each table
+           entry are as follows:
+           - The first bit position (in pseudo-pack order, see below) to
+             contain an object from that pack.
+           - The number of bits whose objects are selected from that pack.
 
        OID Fanout (ID: {'O', 'I', 'D', 'F'})
            The ith entry, F[i], stores the number of OIDs with first
@@ -508,6 +518,73 @@ packs arranged in MIDX order (with the preferred pack coming first).
 The MIDX's reverse index is stored in the optional 'RIDX' chunk within
 the MIDX itself.
 
+=== `BTMP` chunk
+
+The Bitmapped Packfiles (`BTMP`) chunk encodes additional information
+about the objects in the multi-pack index's reachability bitmap. Recall
+that objects from the MIDX are arranged in "pseudo-pack" order (see
+above) for reachability bitmaps.
+
+From the example above, suppose we have packs "a", "b", and "c", with
+10, 15, and 20 objects, respectively. In pseudo-pack order, those would
+be arranged as follows:
+
+    |a,0|a,1|...|a,9|b,0|b,1|...|b,14|c,0|c,1|...|c,19|
+
+When working with single-pack bitmaps (or, equivalently, multi-pack
+reachability bitmaps with a preferred pack), linkgit:git-pack-objects[1]
+performs ``verbatim'' reuse, attempting to reuse chunks of the bitmapped
+or preferred packfile instead of adding objects to the packing list.
+
+When a chunk of bytes is reused from an existing pack, any objects
+contained therein do not need to be added to the packing list, saving
+memory and CPU time. But a chunk from an existing packfile can only be
+reused when the following conditions are met:
+
+  - The chunk contains only objects which were requested by the caller
+    (i.e. does not contain any objects which the caller didn't ask for
+    explicitly or implicitly).
+
+  - All objects stored in non-thin packs as offset- or reference-deltas
+    also include their base object in the resulting pack.
+
+The `BTMP` chunk encodes the necessary information in order to implement
+multi-pack reuse over a set of packfiles as described above.
+Specifically, the `BTMP` chunk encodes three pieces of information (all
+32-bit unsigned integers in network byte-order) for each packfile `p`
+that is stored in the MIDX, as follows:
+
+`bitmap_pos`:: The first bit position (in pseudo-pack order) in the
+  multi-pack index's reachability bitmap occupied by an object from `p`.
+
+`bitmap_nr`:: The number of bit positions (including the one at
+  `bitmap_pos`) that encode objects from that pack `p`.
+
+For example, the `BTMP` chunk corresponding to the above example (with
+packs ``a'', ``b'', and ``c'') would look like:
+
+[cols="1,2,2"]
+|===
+| |`bitmap_pos` |`bitmap_nr`
+
+|packfile ``a''
+|`0`
+|`10`
+
+|packfile ``b''
+|`10`
+|`15`
+
+|packfile ``c''
+|`25`
+|`20`
+|===
+
+With this information in place, we can treat each packfile as
+individually reusable in the same fashion as verbatim pack reuse is
+performed on individual packs prior to the implementation of the `BTMP`
+chunk.
+
 == cruft packs
 
 The cruft packs feature offer an alternative to Git's traditional mechanism of
@@ -588,51 +665,17 @@ later on.
 It is linkgit:git-gc[1] that is typically responsible for removing expired
 unreachable objects.
 
-=== Caution for mixed-version environments
-
-Repositories that have cruft packs in them will continue to work with any older
-version of Git. Note, however, that previous versions of Git which do not
-understand the `.mtimes` file will use the cruft pack's mtime as the mtime for
-all of the objects in it. In other words, do not expect older (pre-cruft pack)
-versions of Git to interpret or even read the contents of the `.mtimes` file.
-
-Note that having mixed versions of Git GC-ing the same repository can lead to
-unreachable objects never being completely pruned. This can happen under the
-following circumstances:
-
-  - An older version of Git running GC explodes the contents of an existing
-    cruft pack loose, using the cruft pack's mtime.
-  - A newer version running GC collects those loose objects into a cruft pack,
-    where the .mtime file reflects the loose object's actual mtimes, but the
-    cruft pack mtime is "now".
-
-Repeating this process will lead to unreachable objects not getting pruned as a
-result of repeatedly resetting the objects' mtimes to the present time.
-
-If you are GC-ing repositories in a mixed version environment, consider omitting
-the `--cruft` option when using linkgit:git-repack[1] and linkgit:git-gc[1], and
-leaving the `gc.cruftPacks` configuration unset until all writers understand
-cruft packs.
-
 === Alternatives
 
 Notable alternatives to this design include:
 
-  - The location of the per-object mtime data, and
-  - Storing unreachable objects in multiple cruft packs.
+  - The location of the per-object mtime data.
 
 On the location of mtime data, a new auxiliary file tied to the pack was chosen
 to avoid complicating the `.idx` format. If the `.idx` format were ever to gain
 support for optional chunks of data, it may make sense to consolidate the
 `.mtimes` format into the `.idx` itself.
 
-Storing unreachable objects among multiple cruft packs (e.g., creating a new
-cruft pack during each repacking operation including only unreachable objects
-which aren't already stored in an earlier cruft pack) is significantly more
-complicated to construct, and so aren't pursued here. The obvious drawback to
-the current implementation is that the entire cruft pack must be re-written from
-scratch.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
index a249869fafaa6b10e52c55c7187efb8d2e212e8e..d4d3a31f03541199f7e65feb80b2c713f217573d 100644 (file)
@@ -17,12 +17,24 @@ DESCRIPTION
 Git uses cryptographic signatures in various places, currently objects (tags,
 commits, mergetags) and transactions (pushes). In every case, the command which
 is about to create an object or transaction determines a payload from that,
-calls gpg to obtain a detached signature for the payload (`gpg -bsa`) and
-embeds the signature into the object or transaction.
+calls an external program to obtain a detached signature for the payload
+(`gpg -bsa` in the case of PGP signatures), and embeds the signature into the
+object or transaction.
 
-Signatures always begin with `-----BEGIN PGP SIGNATURE-----`
-and end with `-----END PGP SIGNATURE-----`, unless gpg is told to
-produce RFC1991 signatures which use `MESSAGE` instead of `SIGNATURE`.
+Signatures begin with an "ASCII Armor" header line and end with a tail line,
+which differ depending on signature type (as selected by `gpg.format`, see
+linkgit:git-config[1]). These are, for `gpg.format` values:
+
+`gpg` (PGP)::
+       `-----BEGIN PGP SIGNATURE-----` and `-----END PGP SIGNATURE-----`.
+       Or, if gpg is told to produce RFC1991 signatures,
+       `-----BEGIN PGP MESSAGE-----` and `-----END PGP MESSAGE-----`
+
+`ssh` (SSH)::
+       `-----BEGIN SSH SIGNATURE-----` and `-----END SSH SIGNATURE-----`
+
+`x509` (X.509)::
+       `-----BEGIN SIGNED MESSAGE-----` and `-----END SIGNED MESSAGE-----`
 
 Signatures sometimes appear as a part of the normal payload
 (e.g. a signed tag has the signature block appended after the payload
@@ -37,7 +49,7 @@ line.
 This is even true for an originally empty line.  In the following
 examples, the end of line that ends with a whitespace letter is
 highlighted with a `$` sign; if you are trying to recreate these
-example by hand, do not cut and paste them---they are there
+example by hand, do not cut and paste them--they are there
 primarily to highlight extra whitespace at the end of some lines.
 
 The signed payload and the way the signature is embedded depends
index a16e62bc8c8ea7fd8bcb419515258b76ef682f61..883982e7a0516204ddd2f2f9e14775fa17e2a810 100644 (file)
@@ -27,6 +27,18 @@ repository. An exception are hooks triggered during a push ('pre-receive',
 'update', 'post-receive', 'post-update', 'push-to-checkout') which are always
 executed in $GIT_DIR.
 
+Environment variables, such as `GIT_DIR`, `GIT_WORK_TREE`, etc., are exported
+so that Git commands run by the hook can correctly locate the repository.  If
+your hook needs to invoke Git commands in a foreign repository or in a
+different working tree of the same repository, then it should clear these
+environment variables so they do not interfere with Git operations at the
+foreign location.  For example:
+
+------------
+local_desc=$(git describe)
+foreign_desc=$(unset $(git rev-parse --local-env-vars); git -C ../foreign-repo describe)
+------------
+
 Hooks can get their arguments via the environment, command-line
 arguments, and stdin. See the documentation for each hook below for
 details.
@@ -68,7 +80,7 @@ If it exits with non-zero status, then the working tree will not be
 committed after applying the patch.
 
 It can be used to inspect the current working tree and refuse to
-make a commit if it does not pass certain test.
+make a commit if it does not pass certain tests.
 
 The default 'pre-applypatch' hook, when enabled, runs the
 'pre-commit' hook, if the latter is enabled.
@@ -145,7 +157,7 @@ If the exit status is non-zero, `git commit` will abort.
 The purpose of the hook is to edit the message file in place, and
 it is not suppressed by the `--no-verify` option.  A non-zero exit
 means a failure of the hook and aborts the commit.  It should not
-be used as replacement for pre-commit hook.
+be used as a replacement for the pre-commit hook.
 
 The sample `prepare-commit-msg` hook that comes with Git removes the
 help message found in the commented portion of the commit template.
@@ -333,7 +345,7 @@ for the user.
 
 The default 'update' hook, when enabled--and with
 `hooks.allowunannotated` config option unset or set to false--prevents
-unannotated tags to be pushed.
+unannotated tags from being pushed.
 
 [[proc-receive]]
 proc-receive
@@ -367,12 +379,12 @@ following example for the protocol, the letter 'S' stands for
     S: ... ...
     S: flush-pkt
 
-    # Receive result from the hook.
+    # Receive results from the hook.
     # OK, run this command successfully.
     H: PKT-LINE(ok <ref>)
     # NO, I reject it.
     H: PKT-LINE(ng <ref> <reason>)
-    # Fall through, let 'receive-pack' to execute it.
+    # Fall through, let 'receive-pack' execute it.
     H: PKT-LINE(ok <ref>)
     H: PKT-LINE(option fall-through)
     # OK, but has an alternate reference.  The alternate reference name
@@ -583,10 +595,51 @@ processed by rebase.
 sendemail-validate
 ~~~~~~~~~~~~~~~~~~
 
-This hook is invoked by linkgit:git-send-email[1].  It takes a single parameter,
-the name of the file that holds the e-mail to be sent.  Exiting with a
-non-zero status causes `git send-email` to abort before sending any
-e-mails.
+This hook is invoked by linkgit:git-send-email[1].
+
+It takes these command line arguments. They are,
+1. the name of the file which holds the contents of the email to be sent.
+2. The name of the file which holds the SMTP headers of the email.
+
+The SMTP headers are passed in the exact same way as they are passed to the
+user's Mail Transport Agent (MTA). In effect, the email given to the user's
+MTA, is the contents of $2 followed by the contents of $1.
+
+An example of a few common headers is shown below. Take notice of the
+capitalization and multi-line tab structure.
+
+  From: Example <from@example.com>
+  To: to@example.com
+  Cc: cc@example.com,
+         A <author@example.com>,
+         One <one@example.com>,
+         two@example.com
+  Subject: PATCH-STRING
+
+Exiting with a non-zero status causes `git send-email` to abort
+before sending any e-mails.
+
+The following environment variables are set when executing the hook.
+
+`GIT_SENDEMAIL_FILE_COUNTER`::
+       A 1-based counter incremented by one for every file holding an e-mail
+       to be sent (excluding any FIFOs). This counter does not follow the
+       patch series counter scheme. It will always start at 1 and will end at
+       GIT_SENDEMAIL_FILE_TOTAL.
+
+`GIT_SENDEMAIL_FILE_TOTAL`::
+       The total number of files that will be sent (excluding any FIFOs). This
+       counter does not follow the patch series counter scheme. It will always
+       be equal to the number of files being sent, whether there is a cover
+       letter or not.
+
+These variables may for instance be used to validate patch series.
+
+The sample `sendemail-validate` hook that comes with Git checks that all sent
+patches (excluding the cover letter) can be applied on top of the upstream
+repository default branch without conflicts. Some placeholders are left for
+additional validation steps to be performed after all patches of a given series
+have been applied.
 
 fsmonitor-watchman
 ~~~~~~~~~~~~~~~~~~
index f2738b10db6b2be5e7babb74fbe013faecd425d1..5e0964ef4191d95acc0965b82fdfa1de295a6bc3 100644 (file)
@@ -88,7 +88,7 @@ PATTERN FORMAT
    Put a backslash ("`\`") in front of the first "`!`" for patterns
    that begin with a literal "`!`", for example, "`\!important!.txt`".
 
- - The slash '/' is used as the directory separator. Separators may
+ - The slash "`/`" is used as the directory separator. Separators may
    occur at the beginning, middle or end of the `.gitignore` search pattern.
 
  - If there is a separator at the beginning or middle (or both) of the
@@ -146,7 +146,9 @@ The purpose of gitignore files is to ensure that certain files
 not tracked by Git remain untracked.
 
 To stop tracking a file that is currently tracked, use
-'git rm --cached'.
+'git rm --cached' to remove the file from the index. The filename
+can then be added to the `.gitignore` file to stop the file from
+being reintroduced in later commits.
 
 Git does not follow symbolic links when accessing a `.gitignore` file in
 the working tree. This keeps behavior consistent when the file is
@@ -172,10 +174,10 @@ EXAMPLES
    is not relevant  if there is already a middle slash in
    the pattern.
 
- - The pattern "foo/*", matches "foo/test.json"
-   (a regular file), "foo/bar" (a directory), but it does not match
-   "foo/bar/hello.c" (a regular file), as the asterisk in the
-   pattern does not match "bar/hello.c" which has a slash in it.
+ - The pattern `foo/*`, matches `foo/test.json`
+   (a regular file), `foo/bar` (a directory), but it does not match
+   `foo/bar/hello.c` (a regular file), as the asterisk in the
+   pattern does not match `bar/hello.c` which has a slash in it.
 
 --------------------------------------------------------------
     $ git status
index d50e9ed10e04c688b6ee898b20e24a1cd796cc78..c2213bb77b380a213c1d4a8f6230790b9d8ecb57 100644 (file)
@@ -26,7 +26,7 @@ changes each commit introduces are shown.  Finally, it supports some
 gitk-specific options.
 
 gitk generally only understands options with arguments in the
-'sticked' form (see linkgit:gitcli[7]) due to limitations in the
+'stuck' form (see linkgit:gitcli[7]) due to limitations in the
 command-line parser.
 
 rev-list options and arguments
index dcee09b5001dc8cfb95753c0c02a5b49ba036752..d9bec8b1875502b22934877311d3124a3e5a22df 100644 (file)
@@ -43,9 +43,9 @@ submodule.<name>.update::
        command in the superproject. This is only used by `git
        submodule init` to initialize the configuration variable of
        the same name. Allowed values here are 'checkout', 'rebase',
-       'merge' or 'none'. See description of 'update' command in
-       linkgit:git-submodule[1] for their meaning. For security
-       reasons, the '!command' form is not accepted here.
+       'merge' or 'none', but not '!command' (for security reasons).
+       See the description of the 'update' command in
+       linkgit:git-submodule[1] for more details.
 
 submodule.<name>.branch::
        A remote branch name for tracking updates in the upstream submodule.
index 0fb5ea0c1ca7547679d060f8fe2be7d2edf58c86..d6c6effc2151ffa136f0f2e36a07b659e25c43e4 100644 (file)
@@ -30,7 +30,7 @@ to be in effect. The client MUST NOT ask for capabilities the server
 did not say it supports.
 
 Server MUST diagnose and abort if capabilities it does not understand
-was sent.  Server MUST NOT ignore capabilities that client requested
+were sent.  Server MUST NOT ignore capabilities that client requested
 and server advertised.  As a consequence of these rules, server MUST
 NOT advertise capabilities it does not understand.
 
@@ -61,8 +61,8 @@ complete cut across the DAG, or the client has said "done".
 Without multi_ack, a client sends have lines in --date-order until
 the server has found a common base.  That means the client will send
 have lines that are already known by the server to be common, because
-they overlap in time with another branch that the server hasn't found
-a common base on yet.
+they overlap in time with another branch on which the server hasn't found
+a common base yet.
 
 For example suppose the client has commits in caps that the server
 doesn't and the server has commits in lower case that the client
@@ -88,7 +88,7 @@ interleaved with S-R-Q.
 
 multi_ack_detailed
 ------------------
-This is an extension of multi_ack that permits client to better
+This is an extension of multi_ack that permits the client to better
 understand the server's in-memory state. See linkgit:gitprotocol-pack[5],
 section "Packfile Negotiation" for more information.
 
@@ -135,7 +135,7 @@ to disable the feature in a backwards-compatible manner.
 side-band, side-band-64k
 ------------------------
 
-This capability means that server can send, and client understand multiplexed
+This capability means that the server can send, and the client can understand, multiplexed
 progress reports and error info interleaved with the packfile itself.
 
 These two options are mutually exclusive. A modern client always
@@ -163,14 +163,14 @@ Further, with side-band and its up to 1000-byte messages, it's actually
 same deal, you have up to 65519 bytes of data and 1 byte for the stream
 code.
 
-The client MUST send only maximum of one of "side-band" and "side-
-band-64k".  Server MUST diagnose it as an error if client requests
+The client MUST send only one of "side-band" and "side-
+band-64k".  The server MUST diagnose it as an error if client requests
 both.
 
 ofs-delta
 ---------
 
-Server can send, and client understand PACKv2 with delta referring to
+The server can send, and the client can understand, PACKv2 with delta referring to
 its base by position in pack rather than by an obj-id.  That is, they can
 send/read OBJ_OFS_DELTA (aka type 6) in a packfile.
 
@@ -252,7 +252,7 @@ the current shallow boundary, instead of the depth from remote refs.
 no-progress
 -----------
 
-The client was started with "git clone -q" or something, and doesn't
+The client was started with "git clone -q" or something similar, and doesn't
 want that side band 2.  Basically the client just says "I do not
 wish to receive stream 2 on sideband, so do not send it to me, and if
 you did, I will drop it on the floor anyway".  However, the sideband
@@ -273,7 +273,7 @@ request include-tag only has to do with the client's desires for tag
 data, whether or not a server had advertised objects in the
 refs/tags/* namespace.
 
-Servers MUST pack the tags if their referrant is packed and the client
+Servers MUST pack the tags if their referent is packed and the client
 has requested include-tags.
 
 Clients MUST be prepared for the case where a server has ignored
index 1486651bd1002f3c121c703caae154caa822fedc..cdc9d6e707586cc1f7c23de318957d2e42ef5d88 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-This document sets defines things common to various over-the-wire
+This document defines things common to various over-the-wire
 protocols and file formats used in Git.
 
 ABNF Notation
index ccc13f0a40758ac5f8268472354998d63331bf92..836b3490ccda97f6fb8356b25244365198a8f681 100644 (file)
@@ -42,7 +42,7 @@ both the "smart" and "dumb" HTTP protocols used by Git operate
 by appending additional path components onto the end of the user
 supplied `$GIT_URL` string.
 
-An example of a dumb client requesting for a loose object:
+An example of a dumb client requesting a loose object:
 
   $GIT_URL:     http://example.com:8080/git/repo.git
   URL request:  http://example.com:8080/git/repo.git/objects/d0/49f6c27a2244e12041955e262a404c7faba355
@@ -379,7 +379,7 @@ C: Place any object seen into set `advertised`.
 C: Build an empty set, `common`, to hold the objects that are later
    determined to be on both ends.
 
-C: Build a set, `want`, of the objects from `advertised` the client
+C: Build a set, `want`, of the objects from `advertised` that the client
    wants to fetch, based on what it saw during ref discovery.
 
 C: Start a queue, `c_pending`, ordered by commit time (popping newest
@@ -423,7 +423,7 @@ multiple commands. Object names MUST be given using the object format
 negotiated through the `object-format` capability (default SHA-1).
 
 The `have` list is created by popping the first 32 commits
-from `c_pending`.  Less can be supplied if `c_pending` empties.
+from `c_pending`.  Fewer can be supplied if `c_pending` empties.
 
 If the client has sent 256 "have" commits and has not yet
 received one of those back from `s_common`, or the client has
@@ -529,8 +529,8 @@ TODO: Document this further.
 REFERENCES
 ----------
 
-http://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)]
-http://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1]
+https://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)]
+https://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1]
 
 SEE ALSO
 --------
index dd4108b7a3b95456e0887fc41aed4e381261bd41..837b691c892b31e204948c29b986337809683ce1 100644 (file)
@@ -30,7 +30,7 @@ pkt-line Format
 ---------------
 
 The descriptions below build on the pkt-line format described in
-linkgit:gitprotocol-common[5]. When the grammar indicate `PKT-LINE(...)`, unless
+linkgit:gitprotocol-common[5]. When the grammar indicates `PKT-LINE(...)`, unless
 otherwise noted the usual pkt-line LF rules apply: the sender SHOULD
 include a LF, but the receiver MUST NOT complain if it is not present.
 
@@ -137,7 +137,7 @@ an absolute path in the remote filesystem.
                    v
     ssh user@example.com "git-upload-pack '/project.git'"
 
-In a "user@host:path" format URI, its relative to the user's home
+In a "user@host:path" format URI, it's relative to the user's home
 directory, because the Git client will run:
 
      git clone user@example.com:project.git
@@ -325,7 +325,7 @@ a positive depth, this step is skipped.
 
 If the client has requested a positive depth, the server will compute
 the set of commits which are no deeper than the desired depth. The set
-of commits start at the client's wants.
+of commits starts at the client's wants.
 
 The server writes 'shallow' lines for each
 commit whose parents will not be sent as a result. The server writes
index 59bf41cefb9b958f57e6e95201784ec8cec56fb0..8c1e7c61eac751d352b9268ff2d57b357ce62ed4 100644 (file)
@@ -29,7 +29,7 @@ protocol.  Protocol v2 will improve upon v1 in the following ways:
     semantics the http remote helper can simply act as a proxy
 
 In protocol v2 communication is command oriented.  When first contacting a
-server a list of capabilities will advertised.  Some of these capabilities
+server a list of capabilities will be advertised.  Some of these capabilities
 will be commands which a client can request be executed.  Once a command
 has completed, a client can reuse the connection and request that other
 commands be executed.
@@ -578,6 +578,207 @@ and associated requested information, each separated by a single space.
 
        obj-info = obj-id SP obj-size
 
+bundle-uri
+~~~~~~~~~~
+
+If the 'bundle-uri' capability is advertised, the server supports the
+`bundle-uri' command.
+
+The capability is currently advertised with no value (i.e. not
+"bundle-uri=somevalue"), a value may be added in the future for
+supporting command-wide extensions. Clients MUST ignore any unknown
+capability values and proceed with the 'bundle-uri` dialog they
+support.
+
+The 'bundle-uri' command is intended to be issued before `fetch` to
+get URIs to bundle files (see linkgit:git-bundle[1]) to "seed" and
+inform the subsequent `fetch` command.
+
+The client CAN issue `bundle-uri` before or after any other valid
+command. To be useful to clients it's expected that it'll be issued
+after an `ls-refs` and before `fetch`, but CAN be issued at any time
+in the dialog.
+
+DISCUSSION of bundle-uri
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The intent of the feature is optimize for server resource consumption
+in the common case by changing the common case of fetching a very
+large PACK during linkgit:git-clone[1] into a smaller incremental
+fetch.
+
+It also allows servers to achieve better caching in combination with
+an `uploadpack.packObjectsHook` (see linkgit:git-config[1]).
+
+By having new clones or fetches be a more predictable and common
+negotiation against the tips of recently produces *.bundle file(s).
+Servers might even pre-generate the results of such negotiations for
+the `uploadpack.packObjectsHook` as new pushes come in.
+
+One way that servers could take advantage of these bundles is that the
+server would anticipate that fresh clones will download a known bundle,
+followed by catching up to the current state of the repository using ref
+tips found in that bundle (or bundles).
+
+PROTOCOL for bundle-uri
+^^^^^^^^^^^^^^^^^^^^^^^
+
+A `bundle-uri` request takes no arguments, and as noted above does not
+currently advertise a capability value. Both may be added in the
+future.
+
+When the client issues a `command=bundle-uri` request, the response is a
+list of key-value pairs provided as packet lines with value
+`<key>=<value>`. Each `<key>` should be interpreted as a config key from
+the `bundle.*` namespace to construct a list of bundles. These keys are
+grouped by a `bundle.<id>.` subsection, where each key corresponding to a
+given `<id>` contributes attributes to the bundle defined by that `<id>`.
+See linkgit:git-config[1] for the specific details of these keys and how
+the Git client will interpret their values.
+
+Clients MUST parse the line according to the above format, lines that do
+not conform to the format SHOULD be discarded. The user MAY be warned in
+such a case.
+
+bundle-uri CLIENT AND SERVER EXPECTATIONS
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+URI CONTENTS::
+The content at the advertised URIs MUST be one of two types.
++
+The advertised URI may contain a bundle file that `git bundle verify`
+would accept. I.e. they MUST contain one or more reference tips for
+use by the client, MUST indicate prerequisites (in any) with standard
+"-" prefixes, and MUST indicate their "object-format", if
+applicable.
++
+The advertised URI may alternatively contain a plaintext file that `git
+config --list` would accept (with the `--file` option). The key-value
+pairs in this list are in the `bundle.*` namespace (see
+linkgit:git-config[1]).
+
+bundle-uri CLIENT ERROR RECOVERY::
+A client MUST above all gracefully degrade on errors, whether that
+error is because of bad missing/data in the bundle URI(s), because
+that client is too dumb to e.g. understand and fully parse out bundle
+headers and their prerequisite relationships, or something else.
++
+Server operators should feel confident in turning on "bundle-uri" and
+not worry if e.g. their CDN goes down that clones or fetches will run
+into hard failures. Even if the server bundle(s) are
+incomplete, or bad in some way the client should still end up with a
+functioning repository, just as if it had chosen not to use this
+protocol extension.
++
+All subsequent discussion on client and server interaction MUST keep
+this in mind.
+
+bundle-uri SERVER TO CLIENT::
+The ordering of the returned bundle uris is not significant. Clients
+MUST parse their headers to discover their contained OIDS and
+prerequisites. A client MUST consider the content of the bundle(s)
+themselves and their header as the ultimate source of truth.
++
+A server MAY even return bundle(s) that don't have any direct
+relationship to the repository being cloned (either through accident,
+or intentional "clever" configuration), and expect a client to sort
+out what data they'd like from the bundle(s), if any.
+
+bundle-uri CLIENT TO SERVER::
+The client SHOULD provide reference tips found in the bundle header(s)
+as 'have' lines in any subsequent `fetch` request. A client MAY also
+ignore the bundle(s) entirely if doing so is deemed worse for some
+reason, e.g. if the bundles can't be downloaded, it doesn't like the
+tips it finds etc.
+
+WHEN ADVERTISED BUNDLE(S) REQUIRE NO FURTHER NEGOTIATION::
+If after issuing `bundle-uri` and `ls-refs`, and getting the header(s)
+of the bundle(s) the client finds that the ref tips it wants can be
+retrieved entirely from advertised bundle(s), the client MAY disconnect
+from the Git server. The results of such a 'clone' or 'fetch' should be
+indistinguishable from the state attained without using bundle-uri.
+
+EARLY CLIENT DISCONNECTIONS AND ERROR RECOVERY::
+A client MAY perform an early disconnect while still downloading the
+bundle(s) (having streamed and parsed their headers). In such a case
+the client MUST gracefully recover from any errors related to
+finishing the download and validation of the bundle(s).
++
+I.e. a client might need to re-connect and issue a 'fetch' command,
+and possibly fall back to not making use of 'bundle-uri' at all.
++
+This "MAY" behavior is specified as such (and not a "SHOULD") on the
+assumption that a server advertising bundle uris is more likely than
+not to be serving up a relatively large repository, and to be pointing
+to URIs that have a good chance of being in working order. A client
+MAY e.g. look at the payload size of the bundles as a heuristic to see
+if an early disconnect is worth it, should falling back on a full
+"fetch" dialog be necessary.
+
+WHEN ADVERTISED BUNDLE(S) REQUIRE FURTHER NEGOTIATION::
+A client SHOULD commence a negotiation of a PACK from the server via
+the "fetch" command using the OID tips found in advertised bundles,
+even if's still in the process of downloading those bundle(s).
++
+This allows for aggressive early disconnects from any interactive
+server dialog. The client blindly trusts that the advertised OID tips
+are relevant, and issues them as 'have' lines, it then requests any
+tips it would like (usually from the "ls-refs" advertisement) via
+'want' lines. The server will then compute a (hopefully small) PACK
+with the expected difference between the tips from the bundle(s) and
+the data requested.
++
+The only connection the client then needs to keep active is to the
+concurrently downloading static bundle(s), when those and the
+incremental PACK are retrieved they should be inflated and
+validated. Any errors at this point should be gracefully recovered
+from, see above.
+
+bundle-uri PROTOCOL FEATURES
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The client constructs a bundle list from the `<key>=<value>` pairs
+provided by the server. These pairs are part of the `bundle.*` namespace
+as documented in linkgit:git-config[1]. In this section, we discuss some
+of these keys and describe the actions the client will do in response to
+this information.
+
+In particular, the `bundle.version` key specifies an integer value. The
+only accepted value at the moment is `1`, but if the client sees an
+unexpected value here then the client MUST ignore the bundle list.
+
+As long as `bundle.version` is understood, all other unknown keys MAY be
+ignored by the client. The server will guarantee compatibility with older
+clients, though newer clients may be better able to use the extra keys to
+minimize downloads.
+
+Any backwards-incompatible addition of pre-URI key-value will be
+guarded by a new `bundle.version` value or values in 'bundle-uri'
+capability advertisement itself, and/or by new future `bundle-uri`
+request arguments.
+
+Some example key-value pairs that are not currently implemented but could
+be implemented in the future include:
+
+ * Add a "hash=<val>" or "size=<bytes>" advertise the expected hash or
+   size of the bundle file.
+
+ * Advertise that one or more bundle files are the same (to e.g. have
+   clients round-robin or otherwise choose one of N possible files).
+
+ * A "oid=<OID>" shortcut and "prerequisite=<OID>" shortcut. For
+   expressing the common case of a bundle with one tip and no
+   prerequisites, or one tip and one prerequisite.
++
+This would allow for optimizing the common case of servers who'd like
+to provide one "big bundle" containing only their "main" branch,
+and/or incremental updates thereof.
++
+A client receiving such a a response MAY assume that they can skip
+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.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 1a2ef4c15055a20eaa65cbbf0f40525ba7e2ba48..949cd8a31e9a9e896ccec63d5c7e2f23f740973a 100644 (file)
@@ -23,7 +23,9 @@ A Git repository comes in two different flavours:
 
 *Note*: Also you can have a plain text file `.git` at the root of
 your working tree, containing `gitdir: <path>` to point at the real
-directory that has the repository.  This mechanism is often used for
+directory that has the repository.
+This mechanism is called a 'gitfile' and is usually managed via the
+`git submodule` and `git worktree` commands. It is often used for
 a working tree of a submodule checkout, to allow you in the
 containing superproject to `git checkout` a branch that does not
 have the submodule.  The `checkout` has to remove the entire
index 941858a6ecce88440975f02c2462f6aa8692b0bc..8400d591da0e8a93035d72a947b07728055eeebd 100644 (file)
@@ -78,7 +78,7 @@ Submodule operations can be configured using the following mechanisms
 
  * The command line for those commands that support taking submodules
    as part of their pathspecs. Most commands have a boolean flag
-   `--recurse-submodules` which specify whether to recurse into submodules.
+   `--recurse-submodules` which specifies whether to recurse into submodules.
    Examples are `grep` and `checkout`.
    Some commands take enums, such as `fetch` and `push`, where you can
    specify how submodules are affected.
@@ -192,7 +192,7 @@ For example:
   [submodule "baz"]
     url = https://example.org/baz
 
-In the above config only the submodule 'bar' and 'baz' are active,
+In the above config only the submodules 'bar' and 'baz' are active,
 'bar' due to (1) and 'baz' due to (3). 'foo' is inactive because
 (1) takes precedence over (3)
 
@@ -274,7 +274,7 @@ will not be checked out by default; you can instruct `clone` to recurse
 into submodules. The `init` and `update` subcommands of `git submodule`
 will maintain submodules checked out and at an appropriate revision in
 your working tree. Alternatively you can set `submodule.recurse` to have
-`checkout` recursing into submodules (note that `submodule.recurse` also
+`checkout` recurse into submodules (note that `submodule.recurse` also
 affects other Git commands, see linkgit:git-config[1] for a complete list).
 
 
index 0e0b863105b104b706d3cc75b4d3998a4abe580f..4759408788070baba26b94e91dde74a9e6c6dda5 100644 (file)
@@ -49,7 +49,7 @@ $ git config --global user.email you@yourdomain.example.com
 Importing a new project
 -----------------------
 
-Assume you have a tarball project.tar.gz with your initial work.  You
+Assume you have a tarball `project.tar.gz` with your initial work.  You
 can place it under Git revision control as follows.
 
 ------------------------------------------------
@@ -65,10 +65,10 @@ Initialized empty Git repository in .git/
 ------------------------------------------------
 
 You've now initialized the working directory--you may notice a new
-directory created, named ".git".
+directory created, named `.git`.
 
 Next, tell Git to take a snapshot of the contents of all files under the
-current directory (note the '.'), with 'git add':
+current directory (note the `.`), with `git add`:
 
 ------------------------------------------------
 $ git add .
@@ -76,7 +76,7 @@ $ git add .
 
 This snapshot is now stored in a temporary staging area which Git calls
 the "index".  You can permanently store the contents of the index in the
-repository with 'git commit':
+repository with `git commit`:
 
 ------------------------------------------------
 $ git commit
@@ -95,21 +95,20 @@ $ git add file1 file2 file3
 ------------------------------------------------
 
 You are now ready to commit.  You can see what is about to be committed
-using 'git diff' with the --cached option:
+using `git diff` with the `--cached` option:
 
 ------------------------------------------------
 $ git diff --cached
 ------------------------------------------------
 
-(Without --cached, 'git diff' will show you any changes that
+(Without `--cached`, `git diff` will show you any changes that
 you've made but not yet added to the index.)  You can also get a brief
-summary of the situation with 'git status':
+summary of the situation with `git status`:
 
 ------------------------------------------------
 $ git status
 On branch master
 Changes to be committed:
-Your branch is up to date with 'origin/master'.
   (use "git restore --staged <file>..." to unstage)
 
        modified:   file1
@@ -128,7 +127,7 @@ $ git commit
 This will again prompt you for a message describing the change, and then
 record a new version of the project.
 
-Alternatively, instead of running 'git add' beforehand, you can use
+Alternatively, instead of running `git add` beforehand, you can use
 
 ------------------------------------------------
 $ git commit -a
@@ -138,10 +137,10 @@ which will automatically notice any modified (but not new) files, add
 them to the index, and commit, all in one step.
 
 A note on commit messages: Though not required, it's a good idea to
-begin the commit message with a single short (less than 50 character)
-line summarizing the change, followed by a blank line and then a more
-thorough description. The text up to the first blank line in a commit
-message is treated as the commit title, and that title is used
+begin the commit message with a single short (no more than 50
+characters) line summarizing the change, followed by a blank line and
+then a more thorough description. The text up to the first blank line in
+a commit message is treated as the commit title, and that title is used
 throughout Git.  For example, linkgit:git-format-patch[1] turns a
 commit into email, and it uses the title on the Subject line and the
 rest of the commit in the body.
@@ -151,7 +150,7 @@ Git tracks content not files
 
 Many revision control systems provide an `add` command that tells the
 system to start tracking changes to a new file.  Git's `add` command
-does something simpler and more powerful: 'git add' is used both for new
+does something simpler and more powerful: `git add` is used both for new
 and newly modified files, and in both cases it takes a snapshot of the
 given files and stages that content in the index, ready for inclusion in
 the next commit.
@@ -182,7 +181,7 @@ Managing branches
 -----------------
 
 A single Git repository can maintain multiple branches of
-development.  To create a new branch named "experimental", use
+development.  To create a new branch named `experimental`, use
 
 ------------------------------------------------
 $ git branch experimental
@@ -201,8 +200,8 @@ you'll get a list of all existing branches:
 * master
 ------------------------------------------------
 
-The "experimental" branch is the one you just created, and the
-"master" branch is a default branch that was created for you
+The `experimental` branch is the one you just created, and the
+`master` branch is a default branch that was created for you
 automatically.  The asterisk marks the branch you are currently on;
 type
 
@@ -210,8 +209,8 @@ type
 $ git switch experimental
 ------------------------------------------------
 
-to switch to the experimental branch.  Now edit a file, commit the
-change, and switch back to the master branch:
+to switch to the `experimental` branch.  Now edit a file, commit the
+change, and switch back to the `master` branch:
 
 ------------------------------------------------
 (edit file)
@@ -220,9 +219,9 @@ $ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
-made on the experimental branch and you're back on the master branch.
+made on the `experimental` branch and you're back on the `master` branch.
 
-You can make a different change on the master branch:
+You can make a different change on the `master` branch:
 
 ------------------------------------------------
 (edit file)
@@ -230,7 +229,7 @@ $ git commit -a
 ------------------------------------------------
 
 at this point the two branches have diverged, with different changes
-made in each.  To merge the changes made in experimental into master, run
+made in each.  To merge the changes made in `experimental` into `master`, run
 
 ------------------------------------------------
 $ git merge experimental
@@ -258,16 +257,16 @@ $ gitk
 
 will show a nice graphical representation of the resulting history.
 
-At this point you could delete the experimental branch with
+At this point you could delete the `experimental` branch with
 
 ------------------------------------------------
 $ git branch -d experimental
 ------------------------------------------------
 
-This command ensures that the changes in the experimental branch are
+This command ensures that the changes in the `experimental` branch are
 already in the current branch.
 
-If you develop on a branch crazy-idea, then regret it, you can always
+If you develop on a branch `crazy-idea`, then regret it, you can always
 delete the branch with
 
 -------------------------------------
@@ -281,7 +280,7 @@ Using Git for collaboration
 ---------------------------
 
 Suppose that Alice has started a new project with a Git repository in
-/home/alice/project, and that Bob, who has a home directory on the
+`/home/alice/project`, and that Bob, who has a home directory on the
 same machine, wants to contribute.
 
 Bob begins with:
@@ -290,7 +289,7 @@ Bob begins with:
 bob$ git clone /home/alice/project myrepo
 ------------------------------------------------
 
-This creates a new directory "myrepo" containing a clone of Alice's
+This creates a new directory `myrepo` containing a clone of Alice's
 repository.  The clone is on an equal footing with the original
 project, possessing its own copy of the original project's history.
 
@@ -303,31 +302,31 @@ bob$ git commit -a
 ------------------------------------------------
 
 When he's ready, he tells Alice to pull changes from the repository
-at /home/bob/myrepo.  She does this with:
+at `/home/bob/myrepo`.  She does this with:
 
 ------------------------------------------------
 alice$ cd /home/alice/project
 alice$ git pull /home/bob/myrepo master
 ------------------------------------------------
 
-This merges the changes from Bob's "master" branch into Alice's
+This merges the changes from Bob's `master` branch into Alice's
 current branch.  If Alice has made her own changes in the meantime,
 then she may need to manually fix any conflicts.
 
-The "pull" command thus performs two operations: it fetches changes
+The `pull` command thus performs two operations: it fetches changes
 from a remote branch, then merges them into the current branch.
 
 Note that in general, Alice would want her local changes committed before
-initiating this "pull".  If Bob's work conflicts with what Alice did since
+initiating this `pull`.  If Bob's work conflicts with what Alice did since
 their histories forked, Alice will use her working tree and the index to
 resolve conflicts, and existing local changes will interfere with the
 conflict resolution process (Git will still perform the fetch but will
 refuse to merge -- Alice will have to get rid of her local changes in
 some way and pull again when this happens).
 
-Alice can peek at what Bob did without merging first, using the "fetch"
+Alice can peek at what Bob did without merging first, using the `fetch`
 command; this allows Alice to inspect what Bob did, using a special
-symbol "FETCH_HEAD", in order to determine if he has anything worth
+symbol `FETCH_HEAD`, in order to determine if he has anything worth
 pulling, like this:
 
 ------------------------------------------------
@@ -336,10 +335,10 @@ alice$ git log -p HEAD..FETCH_HEAD
 ------------------------------------------------
 
 This operation is safe even if Alice has uncommitted local changes.
-The range notation "HEAD..FETCH_HEAD" means "show everything that is reachable
-from the FETCH_HEAD but exclude anything that is reachable from HEAD".
-Alice already knows everything that leads to her current state (HEAD),
-and reviews what Bob has in his state (FETCH_HEAD) that she has not
+The range notation `HEAD..FETCH_HEAD` means "show everything that is reachable
+from the `FETCH_HEAD` but exclude anything that is reachable from `HEAD`".
+Alice already knows everything that leads to her current state (`HEAD`),
+and reviews what Bob has in his state (`FETCH_HEAD`) that she has not
 seen with this command.
 
 If Alice wants to visualize what Bob did since their histories forked
@@ -349,7 +348,7 @@ she can issue the following command:
 $ gitk HEAD..FETCH_HEAD
 ------------------------------------------------
 
-This uses the same two-dot range notation we saw earlier with 'git log'.
+This uses the same two-dot range notation we saw earlier with `git log`.
 
 Alice may want to view what both of them did since they forked.
 She can use three-dot form instead of the two-dot form:
@@ -361,13 +360,13 @@ $ gitk HEAD...FETCH_HEAD
 This means "show everything that is reachable from either one, but
 exclude anything that is reachable from both of them".
 
-Please note that these range notation can be used with both gitk
-and "git log".
+Please note that these range notation can be used with both `gitk`
+and `git log`.
 
 After inspecting what Bob did, if there is nothing urgent, Alice may
 decide to continue working without pulling from Bob.  If Bob's history
 does have something Alice would immediately need, Alice may choose to
-stash her work-in-progress first, do a "pull", and then finally unstash
+stash her work-in-progress first, do a `pull`, and then finally unstash
 her work-in-progress on top of the resulting history.
 
 When you are working in a small closely knit group, it is not
@@ -379,8 +378,8 @@ it easier:
 alice$ git remote add bob /home/bob/myrepo
 ------------------------------------------------
 
-With this, Alice can perform the first part of the "pull" operation
-alone using the 'git fetch' command without merging them with her own
+With this, Alice can perform the first part of the `pull` operation
+alone using the `git fetch` command without merging them with her own
 branch, using:
 
 -------------------------------------
@@ -388,7 +387,7 @@ alice$ git fetch bob
 -------------------------------------
 
 Unlike the longhand form, when Alice fetches from Bob using a
-remote repository shorthand set up with 'git remote', what was
+remote repository shorthand set up with `git remote`, what was
 fetched is stored in a remote-tracking branch, in this case
 `bob/master`.  So after this:
 
@@ -397,10 +396,10 @@ alice$ git log -p master..bob/master
 -------------------------------------
 
 shows a list of all the changes that Bob made since he branched from
-Alice's master branch.
+Alice's `master` branch.
 
 After examining those changes, Alice
-could merge the changes into her master branch:
+could merge the changes into her `master` branch:
 
 -------------------------------------
 alice$ git merge bob/master
@@ -432,12 +431,12 @@ bob$ git config --get remote.origin.url
 /home/alice/project
 -------------------------------------
 
-(The complete configuration created by 'git clone' is visible using
+(The complete configuration created by `git clone` is visible using
 `git config -l`, and the linkgit:git-config[1] man page
 explains the meaning of each option.)
 
-Git also keeps a pristine copy of Alice's master branch under the
-name "origin/master":
+Git also keeps a pristine copy of Alice's `master` branch under the
+name `origin/master`:
 
 -------------------------------------
 bob$ git branch -r
@@ -462,8 +461,8 @@ Exploring history
 -----------------
 
 Git history is represented as a series of interrelated commits.  We
-have already seen that the 'git log' command can list those commits.
-Note that first line of each git log entry also gives a name for the
+have already seen that the `git log` command can list those commits.
+Note that first line of each `git log` entry also gives a name for the
 commit:
 
 -------------------------------------
@@ -475,7 +474,7 @@ Date:   Tue May 16 17:18:22 2006 -0700
     merge-base: Clarify the comments on post processing.
 -------------------------------------
 
-We can give this name to 'git show' to see the details about this
+We can give this name to `git show` to see the details about this
 commit.
 
 -------------------------------------
@@ -514,7 +513,7 @@ You can also give commits names of your own; after running
 $ git tag v2.5 1b2e1d63ff
 -------------------------------------
 
-you can refer to 1b2e1d63ff by the name "v2.5".  If you intend to
+you can refer to `1b2e1d63ff` by the name `v2.5`.  If you intend to
 share this name with other people (for example, to identify a release
 version), you should create a "tag" object, and perhaps sign it; see
 linkgit:git-tag[1] for details.
@@ -533,22 +532,22 @@ $ git reset --hard HEAD^ # reset your current branch and working
 Be careful with that last command: in addition to losing any changes
 in the working directory, it will also remove all later commits from
 this branch.  If this branch is the only branch containing those
-commits, they will be lost.  Also, don't use 'git reset' on a
+commits, they will be lost.  Also, don't use `git reset` on a
 publicly-visible branch that other developers pull from, as it will
 force needless merges on other developers to clean up the history.
-If you need to undo changes that you have pushed, use 'git revert'
+If you need to undo changes that you have pushed, use `git revert`
 instead.
 
-The 'git grep' command can search for strings in any version of your
+The `git grep` command can search for strings in any version of your
 project, so
 
 -------------------------------------
 $ git grep "hello" v2.5
 -------------------------------------
 
-searches for all occurrences of "hello" in v2.5.
+searches for all occurrences of "hello" in `v2.5`.
 
-If you leave out the commit name, 'git grep' will search any of the
+If you leave out the commit name, `git grep` will search any of the
 files it manages in your current directory.  So
 
 -------------------------------------
@@ -558,7 +557,7 @@ $ git grep "hello"
 is a quick way to search just the files that are tracked by Git.
 
 Many Git commands also take sets of commits, which can be specified
-in a number of ways.  Here are some examples with 'git log':
+in a number of ways.  Here are some examples with `git log`:
 
 -------------------------------------
 $ git log v2.5..v2.6            # commits between v2.5 and v2.6
@@ -568,16 +567,16 @@ $ git log v2.5.. Makefile       # commits since v2.5 which modify
                                # Makefile
 -------------------------------------
 
-You can also give 'git log' a "range" of commits where the first is not
+You can also give `git log` a "range" of commits where the first is not
 necessarily an ancestor of the second; for example, if the tips of
-the branches "stable" and "master" diverged from a common
+the branches `stable` and `master` diverged from a common
 commit some time ago, then
 
 -------------------------------------
 $ git log stable..master
 -------------------------------------
 
-will list commits made in the master branch but not in the
+will list commits made in the `master` branch but not in the
 stable branch, while
 
 -------------------------------------
@@ -585,15 +584,15 @@ $ git log master..stable
 -------------------------------------
 
 will show the list of commits made on the stable branch but not
-the master branch.
+the `master` branch.
 
-The 'git log' command has a weakness: it must present commits in a
+The `git log` command has a weakness: it must present commits in a
 list.  When the history has lines of development that diverged and
-then merged back together, the order in which 'git log' presents
+then merged back together, the order in which `git log` presents
 those commits is meaningless.
 
 Most projects with multiple contributors (such as the Linux kernel,
-or Git itself) have frequent merges, and 'gitk' does a better job of
+or Git itself) have frequent merges, and `gitk` does a better job of
 visualizing their history.  For example,
 
 -------------------------------------
@@ -601,7 +600,7 @@ $ gitk --since="2 weeks ago" drivers/
 -------------------------------------
 
 allows you to browse any commits from the last 2 weeks of commits
-that modified files under the "drivers" directory.  (Note: you can
+that modified files under the `drivers` directory.  (Note: you can
 adjust gitk's fonts by holding down the control key while pressing
 "-" or "+".)
 
@@ -613,7 +612,7 @@ of the file:
 $ git diff v2.5:Makefile HEAD:Makefile.in
 -------------------------------------
 
-You can also use 'git show' to see any such file:
+You can also use `git show` to see any such file:
 
 -------------------------------------
 $ git show v2.5:Makefile
@@ -649,7 +648,7 @@ digressions that may be interesting at this point are:
 
   * linkgit:git-bisect[1]: When there is a regression in your
     project, one way to track down the bug is by searching through
-    the history to find the exact commit that's to blame.  Git bisect
+    the history to find the exact commit that's to blame.  `git bisect`
     can help you perform a binary search for that commit.  It is
     smart enough to perform a close-to-optimal search even in the
     case of complex non-linear history with lots of merged branches.
index 34b1d6e224356cd3e9a637d0d5bdbef2f6ee269d..59fc1d27419f534edd42968c41ec83f00ff3647e 100644 (file)
@@ -53,7 +53,7 @@ following order:
    `/etc/gitweb-common.conf`),
 
  * either per-instance configuration file (defaults to 'gitweb_config.perl'
-   in the same directory as the installed gitweb), or if it does not exists
+   in the same directory as the installed gitweb), or if it does not exist
    then fallback system-wide configuration file (defaults to `/etc/gitweb.conf`).
 
 Values obtained in later configuration files override values obtained earlier
@@ -242,7 +242,7 @@ $mimetypes_file::
 
 $highlight_bin::
        Path to the highlight executable to use (it must be the one from
-       http://www.andre-simon.de[] due to assumptions about parameters and output).
+       http://andre-simon.de/zip/download.php[] due to assumptions about parameters and output).
        By default set to 'highlight'; set it to full path to highlight
        executable if it is not installed on your web server's PATH.
        Note that 'highlight' feature must be set for gitweb to actually
@@ -820,7 +820,7 @@ filesystem (i.e. "$projectroot/$project"), `%h` to the current hash
 (\'h' gitweb parameter) and `%b` to the current hash base
 (\'hb' gitweb parameter); `%%` expands to \'%'.
 +
-For example, at the time this page was written, the http://repo.or.cz[]
+For example, at the time this page was written, the https://repo.or.cz[]
 Git hosting site set it to the following to enable graphical log
 (using the third party tool *git-browser*):
 +
index 7cee9d36899101b1ea48cebcb875388164b002b8..ddd4a0fc70571b83ab6c00bbfc4fd94cabfd4464 100644 (file)
@@ -8,7 +8,7 @@ gitweb - Git web interface (web frontend to Git repositories)
 SYNOPSIS
 --------
 To get started with gitweb, run linkgit:git-instaweb[1] from a Git repository.
-This would configure and start your web server, and run web browser pointing to
+This will configure and start your web server, and run a web browser pointing to
 gitweb.
 
 
@@ -20,15 +20,15 @@ Gitweb provides a web interface to Git repositories.  Its features include:
 * Browsing every revision of the repository.
 * Viewing the contents of files in the repository at any revision.
 * Viewing the revision log of branches, history of files and directories,
-  see what was changed when, by who.
+  seeing what was changed, when, and by whom.
 * Viewing the blame/annotation details of any file (if enabled).
 * Generating RSS and Atom feeds of commits, for any branch.
   The feeds are auto-discoverable in modern web browsers.
-* Viewing everything that was changed in a revision, and step through
+* Viewing everything that was changed in a revision, and stepping through
   revisions one at a time, viewing the history of the repository.
-* Finding commits which commit messages matches given search term.
+* Finding commits whose commit messages match a given search term.
 
-See http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
+See https://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
 browsed using gitweb itself.
 
 
@@ -41,9 +41,9 @@ for details.
 Repositories
 ~~~~~~~~~~~~
 Gitweb can show information from one or more Git repositories.  These
-repositories have to be all on local filesystem, and have to share common
+repositories have to be all on local filesystem, and have to share common
 repository root, i.e. be all under a single parent repository (but see also
-"Advanced web server setup" section, "Webserver configuration with multiple
+the "Advanced web server setup" section, "Webserver configuration with multiple
 projects' root" subsection).
 
 -----------------------------------------------------------------------
@@ -51,7 +51,7 @@ our $projectroot = '/path/to/parent/directory';
 -----------------------------------------------------------------------
 
 The default value for `$projectroot` is `/pub/git`.  You can change it during
-building gitweb via `GITWEB_PROJECTROOT` build configuration variable.
+building gitweb via the `GITWEB_PROJECTROOT` build configuration variable.
 
 By default all Git repositories under `$projectroot` are visible and available
 to gitweb.  The list of projects is generated by default by scanning the
@@ -66,7 +66,7 @@ found at "$projectroot/$repo".
 
 Projects list file format
 ~~~~~~~~~~~~~~~~~~~~~~~~~
-Instead of having gitweb find repositories by scanning filesystem
+Instead of having gitweb find repositories by scanning the filesystem
 starting from $projectroot, you can provide a pre-generated list of
 visible projects by setting `$projects_list` to point to a plain text
 file with a list of projects (with some additional info).
@@ -503,7 +503,7 @@ repositories, you can configure Apache like this:
 
 The above configuration expects your public repositories to live under
 `/pub/git` and will serve them as `http://git.domain.org/dir-under-pub-git`,
-both as clonable Git URL and as browseable gitweb interface.  If you then
+both as clonable Git URL and as browsable gitweb interface.  If you then
 start your linkgit:git-daemon[1] with `--base-path=/pub/git --export-all`
 then you can even use the `git://` URL with exactly the same path.
 
index aa2f41f5e700774ef49eb7079c5239448fc61276..f7d98c11e3dd0821094ae82cb488606a0de14732 100644 (file)
@@ -20,7 +20,7 @@
 [[def_branch]]branch::
        A "branch" is a line of development.  The most recent
        <<def_commit,commit>> on a branch is referred to as the tip of
-       that branch.  The tip of the branch is referenced by a branch
+       that branch.  The tip of the branch is <<def_ref,referenced>> by a branch
        <<def_head,head>>, which moves forward as additional development
        is done on the branch.  A single Git
        <<def_repository,repository>> can track an arbitrary number of
@@ -75,6 +75,21 @@ state in the Git history, by creating a new commit representing the current
 state of the <<def_index,index>> and advancing <<def_HEAD,HEAD>>
 to point at the new commit.
 
+[[def_commit_graph_general]]commit graph concept, representations and usage::
+       A synonym for the <<def_DAG,DAG>> structure formed by the commits
+       in the object database, <<def_ref,referenced>> by branch tips,
+       using their <<def_chain,chain>> of linked commits.
+       This structure is the definitive commit graph. The
+       graph can be represented in other ways, e.g. the
+       <<def_commit_graph_file,"commit-graph" file>>.
+
+[[def_commit_graph_file]]commit-graph file::
+       The "commit-graph" (normally hyphenated) file is a supplemental
+       representation of the <<def_commit_graph_general,commit graph>>
+       which accelerates commit graph walks. The "commit-graph" file is
+       stored either in the .git/objects/info directory or in the info
+       directory of an alternate object database.
+
 [[def_commit_object]]commit object::
        An <<def_object,object>> which contains the information about a
        particular <<def_revision,revision>>, such as <<def_parent,parents>>, committer,
@@ -83,9 +98,8 @@ to point at the new commit.
        revision.
 
 [[def_commit-ish]]commit-ish (also committish)::
-       A <<def_commit_object,commit object>> or an
-       <<def_object,object>> that can be recursively dereferenced to
-       a commit object.
+       A <<def_commit_object,commit object>> or an <<def_object,object>> that
+       can be recursively <<def_dereference,dereferenced>> to a commit object.
        The following are all commit-ishes:
        a commit object,
        a <<def_tag_object,tag object>> that points to a commit
@@ -110,6 +124,25 @@ to point at the new commit.
        dangling object has no references to it from any
        reference or <<def_object,object>> in the <<def_repository,repository>>.
 
+[[def_dereference]]dereference::
+       Referring to a <<def_symref,symbolic ref>>: the action of accessing the
+       <<def_ref,reference>> pointed at by a symbolic ref. Recursive
+       dereferencing involves repeating the aforementioned process on the
+       resulting ref until a non-symbolic reference is found.
++
+Referring to a <<def_tag_object,tag object>>: the action of accessing the
+<<def_object,object>> a tag points at. Tags are recursively dereferenced by
+repeating the operation on the result object until the result has either a
+specified <<def_object_type,object type>> (where applicable) or any non-"tag"
+object type. A synonym for "recursive dereference" in the context of tags is
+"<<def_peel,peel>>".
++
+Referring to a <<def_commit_object,commit object>>: the action of accessing
+the commit's tree object. Commits cannot be dereferenced recursively.
++
+Unless otherwise specified, "dereferencing" as it used in the context of Git
+commands or protocols is implicitly recursive.
+
 [[def_detached_HEAD]]detached HEAD::
        Normally the <<def_HEAD,HEAD>> stores the name of a
        <<def_branch,branch>>, and commands that operate on the
@@ -169,9 +202,11 @@ current branch integrates with) obviously do not work, as there is no
 [[def_gitfile]]gitfile::
        A plain file `.git` at the root of a working tree that
        points at the directory that is the real repository.
+       For proper use see linkgit:git-worktree[1] or linkgit:git-submodule[1].
+       For syntax see linkgit:gitrepository-layout[5].
 
 [[def_grafts]]grafts::
-       Grafts enables two otherwise different lines of development to be joined
+       Grafts enable two otherwise different lines of development to be joined
        together by recording fake ancestry information for commits. This way
        you can make Git pretend the set of <<def_parent,parents>> a <<def_commit,commit>> has
        is different from what was recorded when the commit was
@@ -262,7 +297,7 @@ This commit is referred to as a "merge commit", or sometimes just a
        identified by its <<def_object_name,object name>>. The objects usually
        live in `$GIT_DIR/objects/`.
 
-[[def_object_identifier]]object identifier::
+[[def_object_identifier]]object identifier (oid)::
        Synonym for <<def_object_name,object name>>.
 
 [[def_object_name]]object name::
@@ -279,6 +314,12 @@ This commit is referred to as a "merge commit", or sometimes just a
 [[def_octopus]]octopus::
        To <<def_merge,merge>> more than two <<def_branch,branches>>.
 
+[[def_orphan]]orphan::
+       The act of getting on a <<def_branch,branch>> that does not
+       exist yet (i.e., an <<def_unborn,unborn>> branch).  After
+       such an operation, the commit first created becomes a commit
+       without a parent, starting a new history.
+
 [[def_origin]]origin::
        The default upstream <<def_repository,repository>>. Most projects have
        at least one upstream project which they track. By default
@@ -429,6 +470,10 @@ exclude;;
        of the logical predecessor(s) in the line of development, i.e. its
        parents.
 
+[[def_peel]]peel::
+       The action of recursively <<def_dereference,dereferencing>> a
+       <<def_tag_object,tag object>>.
+
 [[def_pickaxe]]pickaxe::
        The term <<def_pickaxe,pickaxe>> refers to an option to the diffcore
        routines that help select changes that add or delete a given text
@@ -493,6 +538,14 @@ exclude;;
        <<def_tree_object,trees>> to the trees or <<def_blob_object,blobs>>
        that they contain.
 
+[[def_reachability_bitmap]]reachability bitmaps::
+       Reachability bitmaps store information about the
+       <<def_reachable,reachability>> of a selected set of commits in
+       a packfile, or a multi-pack index (MIDX), to speed up object search.
+       The bitmaps are stored in a ".bitmap" file. A repository may have at
+       most one bitmap file in use. The bitmap file may belong to either one
+       pack, or the repository's multi-pack index (if it exists).
+
 [[def_rebase]]rebase::
        To reapply a series of changes from a <<def_branch,branch>> to a
        different base, and reset the <<def_head,head>> of that branch
@@ -597,12 +650,11 @@ The most notable example is `HEAD`.
        copies of) commit objects of the contained submodules.
 
 [[def_symref]]symref::
-       Symbolic reference: instead of containing the <<def_SHA1,SHA-1>>
-       id itself, it is of the format 'ref: refs/some/thing' and when
-       referenced, it recursively dereferences to this reference.
-       '<<def_HEAD,HEAD>>' is a prime example of a symref. Symbolic
-       references are manipulated with the linkgit:git-symbolic-ref[1]
-       command.
+       Symbolic reference: instead of containing the <<def_SHA1,SHA-1>> id
+       itself, it is of the format 'ref: refs/some/thing' and when referenced,
+       it recursively <<def_dereference,dereferences>> to this reference.
+       '<<def_HEAD,HEAD>>' is a prime example of a symref. Symbolic references
+       are manipulated with the linkgit:git-symbolic-ref[1] command.
 
 [[def_tag]]tag::
        A <<def_ref,ref>> under `refs/tags/` namespace that points to an
@@ -638,11 +690,11 @@ The most notable example is `HEAD`.
        <<def_tree,tree>> is equivalent to a <<def_directory,directory>>.
 
 [[def_tree-ish]]tree-ish (also treeish)::
-       A <<def_tree_object,tree object>> or an <<def_object,object>>
-       that can be recursively dereferenced to a tree object.
-       Dereferencing a <<def_commit_object,commit object>> yields the
-       tree object corresponding to the <<def_revision,revision>>'s
-       top <<def_directory,directory>>.
+       A <<def_tree_object,tree object>> or an <<def_object,object>> that can
+       be recursively <<def_dereference,dereferenced>> to a tree object.
+       Dereferencing a <<def_commit_object,commit object>> yields the tree
+       object corresponding to the <<def_revision,revision>>'s top
+       <<def_directory,directory>>.
        The following are all tree-ishes:
        a <<def_commit-ish,commit-ish>>,
        a tree object,
@@ -651,6 +703,18 @@ The most notable example is `HEAD`.
        object,
        etc.
 
+[[def_unborn]]unborn::
+       The <<def_HEAD,HEAD>> can point at a <<def_branch,branch>>
+       that does not yet exist and that does not have any commit on
+       it yet, and such a branch is called an unborn branch.  The
+       most typical way users encounter an unborn branch is by
+       creating a repository anew without cloning from elsewhere.
+       The HEAD would point at the 'main' (or 'master', depending
+       on your configuration) branch that is yet to be born.  Also
+       some operations can get you on an unborn branch with their
+       <<def_orphan,orphan>> option.
+
+
 [[def_unmerged_index]]unmerged index::
        An <<def_index,index>> which contains unmerged
        <<def_index_entry,index entries>>.
index 601aae88e9a300e122414a3bc9b69ee14caf6733..b9cb95e82f0eca994c7a0b9149e028feef83bdec 100644 (file)
@@ -1,9 +1,10 @@
 Content-type: text/asciidoc
-Abstract: When a critical vulnerability is discovered and fixed, we follow this
- script to coordinate a public release.
+Abstract: When a vulnerability is reported, we follow these guidelines to
+ assess the vulnerability, create and review a fix, and coordinate embargoed
+ security releases.
 
 How we coordinate embargoed releases
-====================================
+------------------------------------
 
 To protect Git users from critical vulnerabilities, we do not just release
 fixed versions like regular maintenance releases. Instead, we coordinate
@@ -11,33 +12,147 @@ releases with packagers, keeping the fixes under an embargo until the release
 date. That way, users will have a chance to upgrade on that date, no matter
 what Operating System or distribution they run.
 
-Open a Security Advisory draft
-------------------------------
+The `git-security` mailing list
+-------------------------------
+
+Responsible disclosures of vulnerabilities, analysis, proposed fixes as
+well as the orchestration of coordinated embargoed releases all happen on the
+`git-security` mailing list at <git-security@googlegroups.com>.
+
+In this context, the term "embargo" refers to the time period that information
+about a vulnerability is kept under wraps and only shared on a need-to-know
+basis. This is necessary to protect Git's users from bad actors who would
+otherwise be made aware of attack vectors that could be exploited. "Lifting the
+embargo" refers to publishing the version that fixes the vulnerabilities.
+
+Audience of the `git-security` mailing list
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Anybody may contact the `git-security` mailing list by sending an email
+to <git-security@googlegroups.com>, though the archive is closed to the
+public and only accessible to subscribed members.
+
+There are a few dozen subscribed members: core Git developers who are trusted
+with addressing vulnerabilities, and stakeholders (i.e. owners of products
+affected by security vulnerabilities in Git).
+
+Most of the discussions revolve around assessing the severity of the reported
+issue (including the decision whether the report is security-relevant or can be
+redirected to the public mailing list), how to remediate the issue, determining
+the timeline of the disclosure as well as aligning priorities and
+requirements.
 
-The first step is to https://github.com/git/git/security/advisories/new[open an
-advisory]. Technically, it is not necessary, but it is convenient and saves a
-bit of hassle. This advisory can also be used to obtain the CVE number and it
-will give us a private fork associated with it that can be used to collaborate
-on a fix.
+Communications
+~~~~~~~~~~~~~~
 
-Release date of the embargoed version
--------------------------------------
+If you are a stakeholder, it is a good idea to pay close attention to the
+discussions, as pertinent information may be buried in the middle of a lively
+conversation that might not look relevant to your interests. For example, the
+tentative timeline might be agreed upon in the middle of discussing code
+comment formatting in one of the patches and whether or not to combine fixes
+for multiple, separate vulnerabilities into the same embargoed release. Most
+mail threads are not usually structured specifically to communicate
+agreements, assessments or timelines.
 
-If the vulnerability affects Windows users, we want to have our friends over at
-Visual Studio on board. This means we need to target a "Patch Tuesday" (i.e. a
-second Tuesday of the month), at the minimum three weeks from heads-up to
-coordinated release.
+Typical timeline
+----------------
 
-If the vulnerability affects the server side, or can benefit from scans on the
-server side (i.e. if `git fsck` can detect an attack), it is important to give
-all involved Git repository hosting sites enough time to scan all of those
-repositories.
+- A potential vulnerability is reported to the `git-security` mailing list.
+
+- The members of the git-security list start a discussion to give an initial
+  assessment of the severity of the reported potential vulnerability.
+  We aspire to do so within a few days.
+
+- After discussion, if consensus is reached that it is not critical enough
+  to warrant any embargo, the reporter is redirected to the public Git mailing
+  list. This ends the reporter's interaction with the `git-security` list.
+
+- If it is deemed critical enough for an embargo, ideas are presented on how to
+  address the vulnerability.
+
+- Usually around that time, the Git maintainer or their delegate(s) open a draft
+  security advisory in the `git/git` repository on GitHub (see below for more
+  details).
+
+- Code review can take place in a variety of different locations,
+  depending on context. These are: patches sent inline on the git-security list,
+  a private fork on GitHub associated with the draft security advisory, or the
+  git/cabal repository.
+
+- Contributors working on a fix should consider beginning by sending
+  patches to the git-security list (inline with the original thread), since they
+  are accessible to all subscribers, along with the original reporter.
+
+- Once the review has settled and everyone involved in the review agrees that
+  the patches are nearing the finish line, the Git maintainer, and others
+  determine a release date as well as the release trains that are serviced. The
+  decision regarding which versions need a backported fix is based on input from
+  the reporter, the contributor who worked on the patches, and from
+  stakeholders. Operators of hosting sites who may want to analyze whether the
+  given issue is exploited via any of the repositories they host, and binary
+  packagers who want to make sure their product gets patched adequately against
+  the vulnerability, for example, may want to give their input at this stage.
+
+- While the Git community does its best to accommodate the specific timeline
+  requests of the various binary packagers, the nature of the issue may preclude
+  a prolonged release schedule. For fixes deemed urgent, it may be in the best
+  interest of the Git users community to shorten the disclosure and release
+  timeline, and packagers may need to adapt accordingly.
+
+- Subsequently, branches with the fixes are pushed to the git/cabal repository.
+
+- The tags are created by the Git maintainer and pushed to the same repository.
+
+- The Git for Windows, Git for macOS, BSD, Debian, etc. maintainers prepare the
+  corresponding release artifacts, based on the tags created that have been
+  prepared by the Git maintainer.
+
+- The release artifacts prepared by various binary packagers can be
+  made available to stakeholders under embargo via a mail to the
+  `git-security` list.
+
+- Less than a week before the release, a mail with the relevant information is
+  sent to <distros@vs.openwall.org> (see below), a list used to pre-announce
+  embargoed releases of open source projects to the stakeholders of all major
+  distributions of Linux as well as other OSes.
+
+- Public communication is then prepared in advance of the release date. This
+  includes blog posts and mails to the Git and Git for Windows mailing lists.
+
+- On the day of the release, at around 10am Pacific Time, the Git maintainer
+  pushes the tag and the `master` branch to the public repository, then sends
+  out an announcement mail.
+
+- Once the tag is pushed, the Git for Windows maintainer publishes the
+  corresponding tag and creates a GitHub Release with the associated release
+  artifacts (Git for Windows installer, Portable Git, MinGit, etc).
+
+- Git for Windows release is then announced via a mail to the public Git and
+  Git for Windows mailing lists as well as via a tweet.
+
+- Ditto for distribution packagers for Linux and other platforms:
+  their releases are announced via their preferred channels.
+
+- A mail to <oss-security@lists.openwall.org> (see below for details) is sent
+  as a follow-up to the <distros@vs.openwall.org> one, describing the
+  vulnerability in detail, often including a proof of concept of an exploit.
+
+Note: The Git project makes no guarantees about timelines, but aims to keep
+embargoes reasonably short in the interest of keeping Git's users safe.
+
+Opening a Security Advisory draft
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first step is to https://github.com/git/git/security/advisories/new[open
+an advisory]. Technically, this is not necessary. However, it is the most
+convenient way to obtain the CVE number and it gives us a private repository
+associated with it that can be used to collaborate on a fix.
 
 Notifying the Linux distributions
----------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 At most two weeks before release date, we need to send a notification to
-distros@vs.openwall.org, preferably less than 7 days before the release date.
+<distros@vs.openwall.org>, preferably less than 7 days before the release date.
 This will reach most (all?) Linux distributions. See an example below, and the
 guidelines for this mailing list at
 https://oss-security.openwall.org/wiki/mailing-lists/distros#how-to-use-the-lists[here].
@@ -65,7 +180,7 @@ created using a command like this:
        tar cJvf cve-xxx.bundle.tar.xz cve-xxx.bundle
 
 Example mail to distros@vs.openwall.org
----------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 ....
 To: distros@vs.openwall.org
@@ -101,7 +216,7 @@ Thanks,
 ....
 
 Example mail to oss-security@lists.openwall.com
------------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 ....
 To: oss-security@lists.openwall.com
@@ -128,4 +243,4 @@ it goes to <developer>.
 
 Thanks,
 <name>
-....
+....
\ No newline at end of file
index 35d48ef714e9b2cddbbff68b63a14901844ded90..5f800fd85a3dbd4218bb0aaa2bd4de08a3c71434 100644 (file)
@@ -213,4 +213,4 @@ The procedure will result in a history that looks like this:
                 B0--B1---------B2
 ------------
 
-See also http://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html
+See also https://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html
index a67130debb63dc99cbd860fd7b31fb187b1f9571..013014bbef67ac149ea8ebdb3093ed8648b5a469 100644 (file)
@@ -104,7 +104,7 @@ by doing the following:
    files in mbox format).
 
  - Write his own patches to address issues raised on the list but
-   nobody has stepped up solving.  Send it out just like other
+   nobody has stepped up to solve.  Send it out just like other
    contributors do, and pick them up just like patches from other
    contributors (see above).
 
@@ -231,7 +231,7 @@ by doing the following:
  - Prepare 'jch' branch, which is used to represent somewhere
    between 'master' and 'seen' and often is slightly ahead of 'next'.
 
-     $ Meta/Reintegrate master..seen >Meta/redo-jch.sh
+     $ Meta/Reintegrate master..jch >Meta/redo-jch.sh
 
    The result is a script that lists topics to be merged in order to
    rebuild 'seen' as the input to Meta/Reintegrate script.  Remove
@@ -256,7 +256,7 @@ by doing the following:
    merged to 'next', add it at the end of the list.  Then:
 
      $ git checkout -B jch master
-     $ Meta/redo-jch.sh -c1
+     $ sh Meta/redo-jch.sh -c1
 
    to rebuild the 'jch' branch from scratch.  "-c1" tells the script
    to stop merging at the first line that begins with '###'
@@ -283,6 +283,11 @@ by doing the following:
 
      $ git diff jch next
 
+   Then build the rest of 'jch':
+
+     $ git checkout jch
+     $ sh Meta/redo-jch.sh
+
    When all is well, clean up the redo-jch.sh script with
 
      $ sh Meta/redo-jch.sh -u
@@ -293,7 +298,7 @@ by doing the following:
 
  - Rebuild 'seen'.
 
-     $ Meta/Reintegrate master..seen >Meta/redo-seen.sh
+     $ Meta/Reintegrate jch..seen >Meta/redo-seen.sh
 
    Edit the result by adding new topics that are not still in 'seen'
    in the script.  Then
@@ -406,13 +411,13 @@ Preparing a "merge-fix"
 
 A merge of two topics may not textually conflict but still have
 conflict at the semantic level. A classic example is for one topic
-to rename an variable and all its uses, while another topic adds a
+to rename a variable and all its uses, while another topic adds a
 new use of the variable under its old name. When these two topics
 are merged together, the reference to the variable newly added by
 the latter topic will still use the old name in the result.
 
 The Meta/Reintegrate script that is used by redo-jch and redo-seen
-scripts implements a crude but usable way to work this issue around.
+scripts implements a crude but usable way to work around this issue.
 When the script merges branch $X, it checks if "refs/merge-fix/$X"
 exists, and if so, the effect of it is squashed into the result of
 the mechanical merge.  In other words,
index 15a4c8031f1f3bee61ca77d4d37b7ae443d98d9c..880c51112ba4789bd8b903976c25e7c2cb689599 100644 (file)
@@ -1,13 +1,13 @@
 From: Eric S. Raymond <esr@thyrsus.com>
 Abstract: This is how-to documentation for people who want to add extension
- commands to Git.  It should be read alongside api-builtin.txt.
+ commands to Git.  It should be read alongside builtin.h.
 Content-type: text/asciidoc
 
 How to integrate new subcommands
 ================================
 
 This is how-to documentation for people who want to add extension
-commands to Git.  It should be read alongside api-builtin.txt.
+commands to Git.  It should be read alongside builtin.h.
 
 Runtime environment
 -------------------
index 7af2e52cf312c474d5888855fb86b7456a2d82db..2cad9b3ca5366fd115569e1e8d3b7484fed75bfd 100644 (file)
@@ -4,7 +4,7 @@ How to use git-daemon
 =====================
 
 Git can be run in inetd mode and in stand alone mode. But all you want is
-let a coworker pull from you, and therefore need to set up a Git server
+to let a coworker pull from you, and therefore need to set up a Git server
 real quick, right?
 
 Note that git-daemon is not really chatty at the moment, especially when
index a499a94ac2289ac6664035d339e37914748757d2..3bd581ac3591b6c3bf49a77c60e705539ab8319d 100644 (file)
@@ -11,7 +11,7 @@ Message-ID: <BAYC1-PASMTP12374B54BA370A1E1C6E78AE4E0@CEZ.ICE>
 How to use the subtree merge strategy
 =====================================
 
-There are situations where you want to include contents in your project
+There are situations where you want to include content in your project
 from an independently developed project. You can just pull from the
 other project as long as there are no conflicting paths.
 
index 6c6baeeeb75bd8ff15e7b2ec1f3c394e0c9ef845..3a866af4a4205d0cd101d862c45e7d96bd5718fa 100644 (file)
@@ -34,7 +34,7 @@ project find it more convenient to use legacy encodings, Git
 does not forbid it.  However, there are a few things to keep in
 mind.
 
-. 'git commit' and 'git commit-tree' issues
+. 'git commit' and 'git commit-tree' issue
   a warning if the commit log message given to it does not look
   like a valid UTF-8 string, unless you explicitly say your
   project uses a legacy encoding.  The way to say this is to
@@ -46,7 +46,7 @@ mind.
 ------------
 +
 Commit objects created with the above setting record the value
-of `i18n.commitEncoding` in its `encoding` header.  This is to
+of `i18n.commitEncoding` in their `encoding` header.  This is to
 help other people who look at them later.  Lack of this header
 implies that the commit log message is encoded in UTF-8.
 
diff --git a/Documentation/lint-fsck-msgids.perl b/Documentation/lint-fsck-msgids.perl
new file mode 100755 (executable)
index 0000000..1233ffe
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/perl
+
+my ($fsck_h, $fsck_msgids_txt, $okfile) = @ARGV;
+
+my (%in_fsck_h, $fh, $bad);
+
+open($fh, "<", "$fsck_h") or die;
+while (<$fh>) {
+       if (/^\s+FUNC\(([0-9A-Z_]+), ([A-Z]+)\)/) {
+               my ($name, $severity) = ($1, $2);
+               my ($first) = 1;
+               $name = join('',
+                            map {
+                                    y/A-Z/a-z/;
+                                    if (!$first) {
+                                            s/^(.)/uc($1)/e;
+                                    } else {
+                                            $first = 0;
+                                    }
+                                    $_;
+                            }
+                            split(/_/, $name));
+               $in_fsck_h{$name} = $severity;
+       }
+}
+close($fh);
+
+open($fh, "<", "$fsck_msgids_txt") or die;
+my ($previous, $current);
+while (<$fh>) {
+       if (!defined $current) {
+               if (/^\`([a-zA-Z0-9]*)\`::/) {
+                       $current = $1;
+                       if ((defined $previous) &&
+                           ($current le $previous)) {
+                               print STDERR "$previous >= $current in doc\n";
+                               $bad = 1;
+                       }
+               }
+       } elsif (/^\s+\(([A-Z]+)\) /) {
+               my ($level) = $1;
+               if (!exists $in_fsck_h{$current}) {
+                       print STDERR "$current does not exist in fsck.h\n";
+                       $bad = 1;
+               } elsif ($in_fsck_h{$current} eq "") {
+                       print STDERR "$current defined twice\n";
+                       $bad = 1;
+               } elsif ($in_fsck_h{$current} ne $level) {
+                       print STDERR "$current severity $level != $in_fsck_h{$current}\n";
+                       $bad = 1;
+               }
+               $previous = $current;
+               $in_fsck_h{$current} = ""; # mark as seen.
+               undef $current;
+       }
+}
+close($fh);
+
+for my $key (keys %in_fsck_h) {
+       if ($in_fsck_h{$key} ne "") {
+               print STDERR "$key not explained in doc.\n";
+               $bad = 1;
+       }
+}
+
+die if ($bad);
+
+open($fh, ">", "$okfile");
+print $fh "good\n";
+close($fh);
diff --git a/Documentation/manpage-base-url.xsl.in b/Documentation/manpage-base-url.xsl.in
deleted file mode 100644 (file)
index e800904..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<!-- manpage-base-url.xsl:
-     special settings for manpages rendered from newer docbook -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-               version="1.0">
-
-<!-- set a base URL for relative links -->
-<xsl:param name="man.base.url.for.relative.links"
-       >@@MAN_BASE_URL@@</xsl:param>
-
-</xsl:stylesheet>
index a9c7ec69f46d8c631e86449aafdf327d8e51d435..beb5ff8ec2bbc271cb61f52929d3286c027a864a 100644 (file)
@@ -8,19 +8,7 @@
 <xsl:param name="man.output.quietly" select="1"/>
 <xsl:param name="refentry.meta.get.quietly" select="1"/>
 
-<!-- convert asciidoc callouts to man page format -->
-<xsl:template match="co">
-       <xsl:value-of select="concat('\fB(',substring-after(@id,'-'),')\fR')"/>
-</xsl:template>
-<xsl:template match="calloutlist">
-       <xsl:text>.sp&#10;</xsl:text>
-       <xsl:apply-templates/>
-       <xsl:text>&#10;</xsl:text>
-</xsl:template>
-<xsl:template match="callout">
-       <xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
-       <xsl:apply-templates/>
-       <xsl:text>.br&#10;</xsl:text>
-</xsl:template>
+<!-- unset maximum length of title -->
+<xsl:param name="man.th.title.max.length"/>
 
 </xsl:stylesheet>
diff --git a/Documentation/manpage-quote-apos.xsl b/Documentation/manpage-quote-apos.xsl
deleted file mode 100644 (file)
index aeb8839..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-               version="1.0">
-
-<!-- work around newer groff/man setups using a prettier apostrophe
-     that unfortunately does not quote anything when cut&pasting
-     examples to the shell -->
-<xsl:template name="escape.apostrophe">
-  <xsl:param name="content"/>
-  <xsl:call-template name="string.subst">
-    <xsl:with-param name="string" select="$content"/>
-    <xsl:with-param name="target">'</xsl:with-param>
-    <xsl:with-param name="replacement">\(aq</xsl:with-param>
-  </xsl:call-template>
-</xsl:template>
-
-</xsl:stylesheet>
index d8f7cd7ca017f4a257129e19ea7ce407aad5c3e8..3eaefc4e940518713e7d140badcd3734ce9abbf0 100644 (file)
@@ -191,7 +191,7 @@ endif::git-pull[]
 --autostash::
 --no-autostash::
        Automatically create a temporary stash entry before the operation
-       begins, record it in the special ref `MERGE_AUTOSTASH`
+       begins, record it in the ref `MERGE_AUTOSTASH`
        and apply it after the operation ends.  This means
        that you can run the operation on a dirty worktree.  However, use
        with care: the final stash application after a successful
index 2d631e9b1f242f334d9751823b5771d8e1d84112..d1a4c468e6354e47f52579d6e44f2387b51ba811 100644 (file)
@@ -32,10 +32,10 @@ have special meaning:
   - `+` is used to "open a new tab"
   - `,` is used to "open a new vertical split"
   - `/` is used to "open a new horizontal split"
-  - `@` is used to indicate which is the file containing the final version after
+  - `@` is used to indicate the file containing the final version after
     solving the conflicts. If not present, `MERGED` will be used by default.
 
-The precedence of the operators is this one (you can use parentheses to change
+The precedence of the operators is as follows (you can use parentheses to change
 it):
 
     `@` > `+` > `/` > `,`
@@ -162,7 +162,7 @@ information as the first tab, with a different layout.
 |       REMOTE        |                     |
 ---------------------------------------------
 ....
-Note how in the third tab definition we need to use parenthesis to make `,`
+Note how in the third tab definition we need to use parentheses to make `,`
 have precedence over `/`.
 --
 
index 4cb106f0d146e754e626e5d22a512ad9ab5524a3..e561e6668c9e59daa705e563068cd8b20aabd038 100644 (file)
@@ -1,6 +1,9 @@
-THIS OPTION IS EXPERIMENTAL! SHA-256 support is experimental and still
-in an early stage.  A SHA-256 repository will in general not be able to
-share work with "regular" SHA-1 repositories.  It should be assumed
-that, e.g., Git internal file formats in relation to SHA-256
-repositories may change in backwards-incompatible ways.  Only use
-`--object-format=sha256` for testing purposes.
+Note: At present, there is no interoperability between SHA-256
+repositories and SHA-1 repositories.
+
+Historically, we warned that SHA-256 repositories may later need
+backward incompatible changes when we introduce such interoperability
+features. Today, we only expect compatible changes. Furthermore, if such
+changes prove to be necessary, it can be expected that SHA-256 repositories
+created with today's Git will be usable by future versions of Git
+without data loss.
index 0b4c1c8d98a4acf01c19722941095da2c2c6525d..d38b4ab5666c35e0ae83680abca0e067c7b14792 100644 (file)
@@ -122,7 +122,9 @@ The placeholders are:
 - Placeholders that expand to a single literal character:
 '%n':: newline
 '%%':: a raw '%'
-'%x00':: print a byte from a hex code
+'%x00':: '%x' followed by two hexadecimal digits is replaced with a
+        byte with the hexadecimal digits' value (we will call this
+        "literal formatting code" in the rest of this document).
 
 - Placeholders that affect formatting of later placeholders:
 '%Cred':: switch color to red
@@ -146,24 +148,34 @@ The placeholders are:
 '%m':: left (`<`), right (`>`) or boundary (`-`) mark
 '%w([<w>[,<i1>[,<i2>]]])':: switch line wrapping, like the -w option of
                            linkgit:git-shortlog[1].
-'%<(<N>[,trunc|ltrunc|mtrunc])':: make the next placeholder take at
-                                 least N columns, padding spaces on
+'%<( <N> [,trunc|ltrunc|mtrunc])':: make the next placeholder take at
+                                 least N column widths, padding spaces on
                                  the right if necessary.  Optionally
-                                 truncate at the beginning (ltrunc),
-                                 the middle (mtrunc) or the end
-                                 (trunc) if the output is longer than
-                                 N columns.  Note that truncating
+                                 truncate (with ellipsis '..') at the left (ltrunc) `..ft`,
+                                 the middle (mtrunc) `mi..le`, or the end
+                                 (trunc) `rig..`, if the output is longer than
+                                 N columns.
+                                 Note 1: that truncating
                                  only works correctly with N >= 2.
-'%<|(<N>)':: make the next placeholder take at least until Nth
-            columns, padding spaces on the right if necessary
-'%>(<N>)', '%>|(<N>)':: similar to '%<(<N>)', '%<|(<N>)' respectively,
+                                 Note 2: spaces around the N and M (see below)
+                                 values are optional.
+                                 Note 3: Emojis and other wide characters
+                                 will take two display columns, which may
+                                 over-run column boundaries.
+                                 Note 4: decomposed character combining marks
+                                 may be misplaced at padding boundaries.
+'%<|( <M> )':: make the next placeholder take at least until Mth
+            display column, padding spaces on the right if necessary.
+            Use negative M values for column positions measured
+            from the right hand edge of the terminal window.
+'%>( <N> )', '%>|( <M> )':: similar to '%<( <N> )', '%<|( <M> )' respectively,
                        but padding spaces on the left
-'%>>(<N>)', '%>>|(<N>)':: similar to '%>(<N>)', '%>|(<N>)'
+'%>>( <N> )', '%>>|( <M> )':: similar to '%>( <N> )', '%>|( <M> )'
                          respectively, except that if the next
                          placeholder takes more spaces than given and
                          there are spaces on its left, use those
                          spaces
-'%><(<N>)', '%><|(<N>)':: similar to '%<(<N>)', '%<|(<N>)'
+'%><( <N> )', '%><|( <M> )':: similar to '%<( <N> )', '%<|( <M> )'
                          respectively, but padding both sides
                          (i.e. the text is centered)
 
@@ -212,13 +224,30 @@ The placeholders are:
        linkgit:git-rev-list[1])
 '%d':: ref names, like the --decorate option of linkgit:git-log[1]
 '%D':: ref names without the " (", ")" wrapping.
-'%(describe[:options])':: human-readable name, like
-                         linkgit:git-describe[1]; empty string for
-                         undescribable commits.  The `describe` string
-                         may be followed by a colon and zero or more
-                         comma-separated options.  Descriptions can be
-                         inconsistent when tags are added or removed at
-                         the same time.
+'%(decorate[:<options>])'::
+ref names with custom decorations. The `decorate` string may be followed by a
+colon and zero or more comma-separated options. Option values may contain
+literal formatting codes. These must be used for commas (`%x2C`) and closing
+parentheses (`%x29`), due to their role in the option syntax.
++
+** 'prefix=<value>': Shown before the list of ref names.  Defaults to "{nbsp}`(`".
+** 'suffix=<value>': Shown after the list of ref names.  Defaults to "`)`".
+** 'separator=<value>': Shown between ref names.  Defaults to "`,`{nbsp}".
+** 'pointer=<value>': Shown between HEAD and the branch it points to, if any.
+                     Defaults to "{nbsp}`->`{nbsp}".
+** 'tag=<value>': Shown before tag names. Defaults to "`tag:`{nbsp}".
+
++
+For example, to produce decorations with no wrapping
+or tag annotations, and spaces as separators:
++
+`%(decorate:prefix=,suffix=,tag=,separator= )`
+
+'%(describe[:<options>])'::
+human-readable name, like linkgit:git-describe[1]; empty string for
+undescribable commits.  The `describe` string may be followed by a colon and
+zero or more comma-separated options.  Descriptions can be inconsistent when
+tags are added or removed at the same time.
 +
 ** 'tags[=<bool-value>]': Instead of only considering annotated tags,
    consider lightweight tags as well.
@@ -271,13 +300,11 @@ endif::git-rev-list[]
 '%gE':: reflog identity email (respecting .mailmap, see
        linkgit:git-shortlog[1] or linkgit:git-blame[1])
 '%gs':: reflog subject
-'%(trailers[:options])':: display the trailers of the body as
-                         interpreted by
-                         linkgit:git-interpret-trailers[1]. The
-                         `trailers` string may be followed by a colon
-                         and zero or more comma-separated options.
-                         If any option is provided multiple times the
-                         last occurrence wins.
+'%(trailers[:<options>])'::
+display the trailers of the body as interpreted by
+linkgit:git-interpret-trailers[1]. The `trailers` string may be followed by
+a colon and zero or more comma-separated options. If any option is provided
+multiple times, the last occurrence wins.
 +
 ** 'key=<key>': only show trailers with specified <key>. Matching is done
    case-insensitively and trailing colon is optional. If option is
index dc685be363a674e754dc62f961558d97d5e65a7d..23888cd612c9fb0c18ac1e24ac56a1a6f2a0a149 100644 (file)
@@ -48,7 +48,7 @@ people using 80-column terminals.
 --expand-tabs::
 --no-expand-tabs::
        Perform a tab expansion (replace each tab with enough spaces
-       to fill to the next display column that is multiple of '<n>')
+       to fill to the next display column that is multiple of '<n>')
        in the log message before showing it in the output.
        `--expand-tabs` is a short-hand for `--expand-tabs=8`, and
        `--no-expand-tabs` is a short-hand for `--expand-tabs=0`,
@@ -73,7 +73,7 @@ environment overrides). See linkgit:git-config[1] for more details.
 With an optional '<ref>' argument, use the ref to find the notes
 to display.  The ref can specify the full refname when it begins
 with `refs/notes/`; when it begins with `notes/`, `refs/` and otherwise
-`refs/notes/` is prefixed to form a full name of the ref.
+`refs/notes/` is prefixed to form the full name of the ref.
 +
 Multiple --notes options can be combined to control which notes are
 being displayed. Examples: "--notes=foo" will show only notes from
@@ -87,6 +87,10 @@ being displayed. Examples: "--notes=foo" will show only notes from
        "--notes --notes=foo --no-notes --notes=bar" will only show notes
        from "refs/notes/bar".
 
+--show-notes-by-default::
+       Show the default notes unless options for displaying specific
+       notes are given.
+
 --show-notes[=<ref>]::
 --[no-]standard-notes::
        These options are deprecated. Use the above --notes/--no-notes
index 95a7390b2c78bddfc2c094fa6f0ba82e2b6de417..c718f7946f065d425c818c09bfd11fd641b73b4f 100644 (file)
@@ -71,7 +71,7 @@ refspec (or `--force`).
 Unlike when pushing with linkgit:git-push[1], any updates outside of
 `refs/{tags,heads}/*` will be accepted without `+` in the refspec (or
 `--force`), whether that's swapping e.g. a tree object for a blob, or
-a commit for another commit that's doesn't have the previous commit as
+a commit for another commit that doesn't have the previous commit as
 an ancestor etc.
 +
 Unlike when pushing with linkgit:git-push[1], there is no
@@ -80,7 +80,7 @@ configuration which'll amend these rules, and nothing like a
 +
 As with pushing with linkgit:git-push[1], all of the rules described
 above about what's not allowed as an update can be overridden by
-adding an the optional leading `+` to a refspec (or using `--force`
+adding an optional leading `+` to a refspec (or using the `--force`
 command line option). The only exception to this is that no amount of
 forcing will make the `refs/heads/*` namespace accept a non-commit
 object.
@@ -88,7 +88,7 @@ object.
 [NOTE]
 When the remote branch you want to fetch is known to
 be rewound and rebased regularly, it is expected that
-its new tip will not be descendant of its previous tip
+its new tip will not be descendant of its previous tip
 (as stored in your remote-tracking branch the last time
 you fetched).  You would want
 to use the `+` sign to indicate non-fast-forward updates
diff --git a/Documentation/ref-storage-format.txt b/Documentation/ref-storage-format.txt
new file mode 100644 (file)
index 0000000..1a65cac
--- /dev/null
@@ -0,0 +1 @@
+* `files` for loose files with packed-refs. This is the default.
index 1837509566a79a36a5f607837bc13af082d81b86..2bf239ff0309aa0e45436c06788f49c094bfac8f 100644 (file)
@@ -56,7 +56,7 @@ endif::git-rev-list[]
        error to use this option unless `--walk-reflogs` is in use.
 
 --grep=<pattern>::
-       Limit the commits output to ones with log message that
+       Limit the commits output to ones with log message that
        matches the specified pattern (regular expression).  With
        more than one `--grep=<pattern>`, commits whose message
        matches any of the given patterns are chosen (but see
@@ -72,7 +72,7 @@ endif::git-rev-list[]
        instead of ones that match at least one.
 
 --invert-grep::
-       Limit the commits output to ones with log message that do not
+       Limit the commits output to ones with log message that do not
        match the pattern specified with `--grep=<pattern>`.
 
 -i::
@@ -151,6 +151,10 @@ endif::git-log[]
 --not::
        Reverses the meaning of the '{caret}' prefix (or lack thereof)
        for all following revision specifiers, up to the next `--not`.
+       When used on the command line before --stdin, the revisions passed
+       through stdin will not be affected by it. Conversely, when passed
+       via standard input, the revisions passed on the command line will
+       not be affected by it.
 
 --all::
        Pretend as if all the refs in `refs/`, along with `HEAD`, are
@@ -195,6 +199,14 @@ respectively, and they must begin with `refs/` when applied to `--glob`
 or `--all`. If a trailing '/{asterisk}' is intended, it must be given
 explicitly.
 
+--exclude-hidden=[fetch|receive|uploadpack]::
+       Do not include refs that would be hidden by `git-fetch`,
+       `git-receive-pack` or `git-upload-pack` by consulting the appropriate
+       `fetch.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs`
+       configuration along with `transfer.hideRefs` (see
+       linkgit:git-config[1]). This option affects the next pseudo-ref option
+       `--all` or `--glob` and is cleared after processing them.
+
 --reflog::
        Pretend as if all objects mentioned by reflogs are listed on the
        command line as `<commit>`.
@@ -228,10 +240,13 @@ ifndef::git-rev-list[]
 endif::git-rev-list[]
 
 --stdin::
-       In addition to the '<commit>' listed on the command
-       line, read them from the standard input. If a `--` separator is
-       seen, stop reading commits and start reading paths to limit the
-       result.
+       In addition to getting arguments from the command line, read
+       them from standard input as well. This accepts commits and
+       pseudo-options like `--all` and `--glob=`. When a `--` separator
+       is seen, the following input is treated as paths and used to
+       limit the result. Flags like `--not` which are read via standard input
+       are only respected for arguments passed in the same way and will not
+       influence any subsequent command line arguments.
 
 ifdef::git-rev-list[]
 --quiet::
@@ -882,7 +897,7 @@ ifdef::git-rev-list[]
        Print the object IDs of any object referenced by the listed
        commits.  `--objects foo ^bar` thus means ``send me
        all object IDs which I need to download if I have the commit
-       object _bar_ but not _foo_''.
+       object _bar_ but not _foo_''. See also `--object-names` below.
 
 --in-commit-order::
        Print tree and blob ids in order of the commits. The tree
@@ -912,7 +927,12 @@ ifdef::git-rev-list[]
 
 --object-names::
        Only useful with `--objects`; print the names of the object IDs
-       that are found. This is the default behavior.
+       that are found. This is the default behavior. Note that the
+       "name" of each object is ambiguous, and mostly intended as a
+       hint for packing objects. In particular: no distinction is made between
+       the names of tags, trees, and blobs; path names may be modified
+       to remove newlines; and if an object would appear multiple times
+       with different names, only one name is shown.
 
 --no-object-names::
        Only useful with `--objects`; does not print the names of the object
@@ -1093,12 +1113,12 @@ preferred format.  See the `strftime` manual for a complete list of
 format placeholders. When using `-local`, the correct syntax is
 `--date=format-local:...`.
 
-`--date=default` is the default format, and is similar to
-`--date=rfc2822`, with a few exceptions:
+`--date=default` is the default format, and is based on ctime(3)
+output.  It shows a single line with three-letter day of the week,
+three-letter month, day-of-month, hour-minute-seconds in "HH:MM:SS"
+format, followed by 4-digit year, plus timezone information, unless
+the local time zone is used, e.g. `Thu Jan 1 00:00:00 1970 +0000`.
 --
-       - there is no comma after the day-of-week
-
-       - the time zone is omitted when the local time zone is used
 
 ifdef::git-rev-list[]
 --header::
index e3e350126df8bec3022570a295c632b840a8a4ab..6ea6c7cead16476a710629d1aa56c345d07b8538 100644 (file)
@@ -30,10 +30,11 @@ characters and to avoid word splitting.
   explicitly say 'heads/master' to tell Git which one you mean.
   When ambiguous, a '<refname>' is disambiguated by taking the
   first match in the following rules:
-
++
   . If '$GIT_DIR/<refname>' exists, that is what you mean (this is usually
-    useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`
-    and `CHERRY_PICK_HEAD`);
+    useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`,
+    `REBASE_HEAD`, `REVERT_HEAD`, `CHERRY_PICK_HEAD`, `BISECT_HEAD`
+    and `AUTO_MERGE`);
 
   . otherwise, 'refs/<refname>' if it exists;
 
@@ -44,18 +45,38 @@ characters and to avoid word splitting.
   . otherwise, 'refs/remotes/<refname>' if it exists;
 
   . otherwise, 'refs/remotes/<refname>/HEAD' if it exists.
+
 +
-`HEAD` names the commit on which you based the changes in the working tree.
-`FETCH_HEAD` records the branch which you fetched from a remote repository
-with your last `git fetch` invocation.
-`ORIG_HEAD` is created by commands that move your `HEAD` in a drastic
-way, to record the position of the `HEAD` before their operation, so that
-you can easily change the tip of the branch back to the state before you ran
-them.
-`MERGE_HEAD` records the commit(s) which you are merging into your branch
-when you run `git merge`.
-`CHERRY_PICK_HEAD` records the commit which you are cherry-picking
-when you run `git cherry-pick`.
+  `HEAD`:::
+    names the commit on which you based the changes in the working tree.
+  `FETCH_HEAD`:::
+    records the branch which you fetched from a remote repository with
+    your last `git fetch` invocation.
+  `ORIG_HEAD`:::
+    is created by commands that move your `HEAD` in a drastic way (`git
+    am`, `git merge`, `git rebase`, `git reset`), to record the position
+    of the `HEAD` before their operation, so that you can easily change
+    the tip of the branch back to the state before you ran them.
+  `MERGE_HEAD`:::
+    records the commit(s) which you are merging into your branch when you
+    run `git merge`.
+  `REBASE_HEAD`:::
+    during a rebase, records the commit at which the operation is
+    currently stopped, either because of conflicts or an `edit` command in
+    an interactive rebase.
+  `REVERT_HEAD`:::
+    records the commit which you are reverting when you run `git revert`.
+  `CHERRY_PICK_HEAD`:::
+    records the commit which you are cherry-picking when you run `git
+    cherry-pick`.
+  `BISECT_HEAD`:::
+    records the current commit to be tested when you run `git bisect
+    --no-checkout`.
+  `AUTO_MERGE`:::
+    records a tree object corresponding to the state the
+    'ort' merge strategy wrote to the working tree when a merge operation
+    resulted in conflicts.
+
 +
 Note that any of the 'refs/*' cases above may come either from
 the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
@@ -363,7 +384,7 @@ Revision Range Summary
 
 '<rev>{caret}!', e.g. 'HEAD{caret}!'::
   A suffix '{caret}' followed by an exclamation mark is the same
-  as giving commit '<rev>' and then all its parents prefixed with
+  as giving commit '<rev>' and all its parents prefixed with
   '{caret}' to exclude them (and their ancestors).
 
 '<rev>{caret}-<n>', e.g. 'HEAD{caret}-, HEAD{caret}-2'::
index f33436c7f65ff9fd46f7ee4fa10975c833a61096..361f51a64736fab34faa8efaa90071c957d74c3b 100644 (file)
@@ -8,7 +8,8 @@ scalar - A tool for managing large Git repositories
 SYNOPSIS
 --------
 [verse]
-scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]
+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]
+       [--[no-]src] <url> [<enlistment>]
 scalar list
 scalar register [<enlistment>]
 scalar unregister [<enlistment>]
@@ -80,6 +81,11 @@ remote-tracking branch for the branch this option was used for the initial
 cloning. If the HEAD at the remote did not point at any branch when
 `--single-branch` clone was made, no remote-tracking branch is created.
 
+--[no-]src::
+       By default, `scalar clone` places the cloned repository within a
+       `<entlistment>/src` directory. Use `--no-src` to place the cloned
+       repository directly in the `<enlistment>` directory.
+
 --[no-]full-clone::
        A sparse-checkout is initialized by default. This behavior can be
        turned off via `--full-clone`.
index 12aa2333e46b02aaf86106705bac722ceeec09f8..d98758f3cb70d7298cb5bd5a18c8fa23bfe2cd6e 100644 (file)
@@ -9,7 +9,7 @@ endif::git-commit[]
        the committer has the rights to submit the work under the
        project's license or agrees to some contributor representation,
        such as a Developer Certificate of Origin.
-       (See http://developercertificate.org for the one used by the
+       (See https://developercertificate.org for the one used by the
        Linux kernel and Git projects.)  Consult the documentation or
        leadership of the project to which you're contributing to
        understand how the signoffs are used in that project.
index eda8c195c19629dfe836b36d9baaa21ec06199d6..7780a76b080e4b7ad2c82dc8eb6616e92fd21c4e 100644 (file)
@@ -1,7 +1,7 @@
 Git API Documents
 =================
 
-Git has grown a set of internal API over time.  This collection
+Git has grown a set of internal APIs over time.  This collection
 documents them.
 
 ////////////////////////////////////////////////////////////////
index 487d4d83fff2a873e762a9fc57a6f392a68caad4..c2ba01828c6b01b258d4d77d46714db3fafb6e0a 100644 (file)
@@ -28,9 +28,9 @@ and `diff.c` for examples.
 
 * `struct ll_merge_options`
 
-Check ll-merge.h for details.
+Check merge-ll.h for details.
 
 Low-level (single file) merge
 -----------------------------
 
-Check ll-merge.h for details.
+Check merge-ll.h for details.
index d44ada98e7db9ccdd7dd622221ee298af5de5000..c4fb152b23291c3978db623b5ebe5eaea1385442 100644 (file)
@@ -2,7 +2,7 @@ Simple-IPC API
 ==============
 
 The Simple-IPC API is a collection of `ipc_` prefixed library routines
-and a basic communication protocol that allow an IPC-client process to
+and a basic communication protocol that allows an IPC-client process to
 send an application-specific IPC-request message to an IPC-server
 process and receive an application-specific IPC-response message.
 
@@ -20,12 +20,12 @@ IPC-client.
 
 The IPC-client routines within a client application process connect
 to the IPC-server and send a request message and wait for a response.
-When received, the response is returned back the caller.
+When received, the response is returned back to the caller.
 
 For example, the `fsmonitor--daemon` feature will be built as a server
 application on top of the IPC-server library routines.  It will have
 threads watching for file system events and a thread pool waiting for
-client connections.  Clients, such as `git status` will request a list
+client connections.  Clients, such as `git status`, will request a list
 of file system events since a point in time and the server will
 respond with a list of changed files and directories.  The formats of
 the request and response are application-specific; the IPC-client and
@@ -37,7 +37,7 @@ 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
-used by applications like Git-LFS.  In the LFS-style sub-process model
+used by applications like Git-LFS.  In the LFS-style sub-process model,
 the helper is started by the foreground process, communication happens
 via a pair of file descriptors bound to the stdin/stdout of the
 sub-process, the sub-process only serves the current foreground
@@ -102,4 +102,4 @@ stateless request, receive an application-specific
 response, and disconnect.  It is a one round trip facility for
 querying the server.  The Simple-IPC routines hide the socket,
 named pipe, and thread pool details and allow the application
-layer to focus on the application at hand.
+layer to focus on the task at hand.
index 2afa28bb5aa121489a95800765ae8bf844a3355f..de5fc250595b7a216e32623e6959b29673c2d192 100644 (file)
@@ -148,20 +148,18 @@ filename collisions).
 
 == Trace2 API
 
-All public Trace2 functions and macros are defined in `trace2.h` and
-`trace2.c`.  All public symbols are prefixed with `trace2_`.
+The Trace2 public API is defined and documented in `trace2.h`; refer to it for
+more information.  All public functions and macros are prefixed
+with `trace2_` and are implemented in `trace2.c`.
 
 There are no public Trace2 data structures.
 
 The Trace2 code also defines a set of private functions and data types
 in the `trace2/` directory.  These symbols are prefixed with `tr2_`
-and should only be used by functions in `trace2.c`.
+and should only be used by functions in `trace2.c` (or other private
+source files in `trace2/`).
 
-== Conventions for Public Functions and Macros
-
-The functions defined by the Trace2 API are declared and documented
-in `trace2.h`.  It defines the API functions and wrapper macros for
-Trace2.
+=== Conventions for Public Functions and Macros
 
 Some functions have a `_fl()` suffix to indicate that they take `file`
 and `line-number` arguments.
@@ -172,52 +170,7 @@ take a `va_list` argument.
 Some functions have a `_printf_fl()` suffix to indicate that they also
 take a `printf()` style format with a variable number of arguments.
 
-There are CPP wrapper macros and `#ifdef`s to hide most of these details.
-See `trace2.h` for more details.  The following discussion will only
-describe the simplified forms.
-
-== Public API
-
-All Trace2 API functions send a message to all of the active
-Trace2 Targets.  This section describes the set of available
-messages.
-
-It helps to divide these functions into groups for discussion
-purposes.
-
-=== Basic Command Messages
-
-These are concerned with the lifetime of the overall git process.
-e.g: `void trace2_initialize_clock()`, `void trace2_initialize()`,
-`int trace2_is_enabled()`, `void trace2_cmd_start(int argc, const char **argv)`.
-
-=== Command Detail Messages
-
-These are concerned with describing the specific Git command
-after the command line, config, and environment are inspected.
-e.g: `void trace2_cmd_name(const char *name)`,
-`void trace2_cmd_mode(const char *mode)`.
-
-=== Child Process Messages
-
-These are concerned with the various spawned child processes,
-including shell scripts, git commands, editors, pagers, and hooks.
-
-e.g: `void trace2_child_start(struct child_process *cmd)`.
-
-=== Git Thread Messages
-
-These messages are concerned with Git thread usage.
-
-e.g: `void trace2_thread_start(const char *thread_name)`.
-
-=== Region and Data Messages
-
-These are concerned with recording performance data
-over regions or spans of code. e.g:
-`void trace2_region_enter(const char *category, const char *label, const struct repository *repo)`.
-
-Refer to trace2.h for details about all trace2 functions.
+CPP wrapper macros are defined to hide most of these details.
 
 == Trace2 Target Formats
 
@@ -685,8 +638,8 @@ The "exec_id" field is a command-unique id and is only useful if the
 
 `"thread_start"`::
        This event is generated when a thread is started.  It is
-       generated from *within* the new thread's thread-proc (for TLS
-       reasons).
+       generated from *within* the new thread's thread-proc (because
+       it needs to access data in the thread's thread-local storage).
 +
 ------------
 {
@@ -698,7 +651,7 @@ The "exec_id" field is a command-unique id and is only useful if the
 
 `"thread_exit"`::
        This event is generated when a thread exits.  It is generated
-       from *within* the thread's thread-proc (for TLS reasons).
+       from *within* the thread's thread-proc.
 +
 ------------
 {
@@ -816,6 +769,73 @@ The "value" field may be an integer or a string.
 }
 ------------
 
+`"th_timer"`::
+       This event logs the amount of time that a stopwatch timer was
+       running in the thread.  This event is generated when a thread
+       exits for timers that requested per-thread events.
++
+------------
+{
+       "event":"th_timer",
+       ...
+       "category":"my_category",
+       "name":"my_timer",
+       "intervals":5,         # number of time it was started/stopped
+       "t_total":0.052741,    # total time in seconds it was running
+       "t_min":0.010061,      # shortest interval
+       "t_max":0.011648       # longest interval
+}
+------------
+
+`"timer"`::
+       This event logs the amount of time that a stopwatch timer was
+       running aggregated across all threads.  This event is generated
+       when the process exits.
++
+------------
+{
+       "event":"timer",
+       ...
+       "category":"my_category",
+       "name":"my_timer",
+       "intervals":5,         # number of time it was started/stopped
+       "t_total":0.052741,    # total time in seconds it was running
+       "t_min":0.010061,      # shortest interval
+       "t_max":0.011648       # longest interval
+}
+------------
+
+`"th_counter"`::
+       This event logs the value of a counter variable in a thread.
+       This event is generated when a thread exits for counters that
+       requested per-thread events.
++
+------------
+{
+       "event":"th_counter",
+       ...
+       "category":"my_category",
+       "name":"my_counter",
+       "count":23
+}
+------------
+
+`"counter"`::
+       This event logs the value of a counter variable across all threads.
+       This event is generated when the process exits.  The total value
+       reported here is the sum across all threads.
++
+------------
+{
+       "event":"counter",
+       ...
+       "category":"my_category",
+       "name":"my_counter",
+       "count":23
+}
+------------
+
+
 == Example Trace2 API Usage
 
 Here is a hypothetical usage of the Trace2 API showing the intended
@@ -1206,7 +1226,7 @@ worked on 508 items at offset 2032.  Thread "th04" worked on 508 items
 at offset 508.
 +
 This example also shows that thread names are assigned in a racy manner
-as each thread starts and allocates TLS storage.
+as each thread starts.
 
 Config (def param) Events::
 
@@ -1247,6 +1267,60 @@ d0 | main                     | data         | r0  |  0.002126 |  0.002126 | fsy
 d0 | main                     | exit         |     |  0.000470 |           |              | code:0
 d0 | main                     | atexit       |     |  0.000477 |           |              | code:0
 ----------------
+
+Stopwatch Timer Events::
+
+       Measure the time spent in a function call or span of code
+       that might be called from many places within the code
+       throughout the life of the process.
++
+----------------
+static void expensive_function(void)
+{
+       trace2_timer_start(TRACE2_TIMER_ID_TEST1);
+       ...
+       sleep_millisec(1000); // Do something expensive
+       ...
+       trace2_timer_stop(TRACE2_TIMER_ID_TEST1);
+}
+
+static int ut_100timer(int argc, const char **argv)
+{
+       ...
+
+       expensive_function();
+
+       // Do something else 1...
+
+       expensive_function();
+
+       // Do something else 2...
+
+       expensive_function();
+
+       return 0;
+}
+----------------
++
+In this example, we measure the total time spent in
+`expensive_function()` regardless of when it is called
+in the overall flow of the program.
++
+----------------
+$ export GIT_TRACE2_PERF_BRIEF=1
+$ export GIT_TRACE2_PERF=~/log.perf
+$ t/helper/test-tool trace2 100timer 3 1000
+...
+$ cat ~/log.perf
+d0 | main                     | version      |     |           |           |              | ...
+d0 | main                     | start        |     |  0.001453 |           |              | t/helper/test-tool trace2 100timer 3 1000
+d0 | main                     | cmd_name     |     |           |           |              | trace2 (trace2)
+d0 | main                     | exit         |     |  3.003667 |           |              | code:0
+d0 | main                     | timer        |     |           |           | test         | name:test1 intervals:3 total:3.001686 min:1.000254 max:1.000929
+d0 | main                     | atexit       |     |  3.003796 |           |              | code:0
+----------------
+
+
 == Future Work
 
 === Relationship to the Existing Trace Api (api-trace.txt)
index c2e652b71a7f698a6843bbf327535b4692538c0f..f5d200939b056076368d4e8a735772fde114599d 100644 (file)
@@ -114,7 +114,7 @@ result in an empty bitmap (no bits set).
 
     * N entries with compressed bitmaps, one for each indexed commit
 +
-Where `N` is the total amount of entries in this bitmap index.
+Where `N` is the total number of entries in this bitmap index.
 Each entry contains the following:
 
        ** {empty}
@@ -126,7 +126,7 @@ Each entry contains the following:
        ** {empty}
        1-byte XOR-offset: ::
            The xor offset used to compress this bitmap. For an entry
-           in position `x`, a XOR offset of `y` means that the actual
+           in position `x`, an XOR offset of `y` means that the actual
            bitmap representing this commit is composed by XORing the
            bitmap for this entry with the bitmap in entry `x-y` (i.e.
            the bitmap `y` entries before this one).
@@ -239,7 +239,7 @@ bitmaps.
 
 For a `.bitmap` containing `nr_entries` reachability bitmaps, the table
 contains a list of `nr_entries` <commit_pos, offset, xor_row> triplets
-(sorted in the ascending order of `commit_pos`). The content of i'th
+(sorted in the ascending order of `commit_pos`). The content of the i'th
 triplet is -
 
        * {empty}
index b78d01d9adfc72ff7598cda0e836d8c9f3c99e0b..91d3a13e3276fcc609abe7268c462e2da61b97af 100644 (file)
@@ -479,14 +479,14 @@ outline for submitting these features:
    (This choice is an opt-in via a config option and a command-line
    option.)
 
-4. Allow the client to understand the `bundle.flag=forFetch` configuration
+4. Allow the client to understand the `bundle.heuristic` configuration key
    and the `bundle.<id>.creationToken` heuristic. When `git clone`
-   discovers a bundle URI with `bundle.flag=forFetch`, it configures the
-   client repository to check that bundle URI during later `git fetch <remote>`
+   discovers a bundle URI with `bundle.heuristic`, it configures the client
+   repository to check that bundle URI during later `git fetch <remote>`
    commands.
 
 5. Allow clients to discover bundle URIs during `git fetch` and configure
-   a bundle URI for later fetches if `bundle.flag=forFetch`.
+   a bundle URI for later fetches if `bundle.heuristic` is set.
 
 6. Implement the "inspect headers" heuristic to reduce data downloads when
    the `bundle.<id>.creationToken` heuristic is not available.
index 90c9760c230555053db461a8cb3d1a3676729cd7..2c26e95e51ab9aacb147fad022c669a2a17e928b 100644 (file)
@@ -1,4 +1,4 @@
-Git Commit Graph Design Notes
+Git Commit-Graph Design Notes
 =============================
 
 Git walks the commit graph for many reasons, including:
@@ -17,7 +17,7 @@ There are two main costs here:
 
 The commit-graph file is a supplemental data structure that accelerates
 commit graph walks. If a user downgrades or disables the 'core.commitGraph'
-config setting, then the existing ODB is sufficient. The file is stored
+config setting, then the existing object database is sufficient. The file is stored
 as "commit-graph" either in the .git/objects/info directory or in the info
 directory of an alternate.
 
@@ -95,7 +95,7 @@ with default order), but is not used when the topological order is
 required (such as merge base calculations, "git log --graph").
 
 In practice, we expect some commits to be created recently and not stored
-in the commit graph. We can treat these commits as having "infinite"
+in the commit-graph. We can treat these commits as having "infinite"
 generation number and walk until reaching commits with known generation
 number.
 
@@ -136,7 +136,7 @@ Design Details
 
 - Commit grafts and replace objects can change the shape of the commit
   history. The latter can also be enabled/disabled on the fly using
-  `--no-replace-objects`. This leads to difficultly storing both possible
+  `--no-replace-objects`. This leads to difficulty storing both possible
   interpretations of a commit id, especially when computing generation
   numbers. The commit-graph will not be read or written when
   replace-objects or grafts are present.
@@ -149,7 +149,7 @@ Design Details
   helpful for these clones, anyway. The commit-graph will not be read or
   written when shallow commits are present.
 
-Commit Graphs Chains
+Commit-Graphs Chains
 --------------------
 
 Typically, repos grow with near-constant velocity (commits per day). Over time,
index e2ac36dd210bef993e1cca06f37d788c3d4fe262..ed574810891cad1024658920e0fa8ac550231534 100644 (file)
@@ -562,7 +562,7 @@ hash re-encode during clone and to encourage peers to modernize.
 The design described here allows fetches by SHA-1 clients of a
 personal SHA-256 repository because it's not much more difficult than
 allowing pushes from that repository. This support needs to be guarded
-by a configuration option --- servers like git.kernel.org that serve a
+by a configuration option -- servers like git.kernel.org that serve a
 large number of clients would not be expected to bear that cost.
 
 Meaning of signatures
index e790258a1adcdb9d683074a63fd6c941743bb757..b4a144e5f4758d30e523406596b85009336141b0 100644 (file)
@@ -56,14 +56,14 @@ Rejected Multi-Threaded Solution
 
 The most "straightforward" implementation would be to spread the set of
 to-be-updated cache entries across multiple threads. But due to the
-thread-unsafe functions in the ODB code, we would have to use locks to
+thread-unsafe functions in the object database code, we would have to use locks to
 coordinate the parallel operation. An early prototype of this solution
 showed that the multi-threaded checkout would bring performance
 improvements over the sequential code, but there was still too much lock
 contention. A `perf` profiling indicated that around 20% of the runtime
 during a local Linux clone (on an SSD) was spent in locking functions.
 For this reason this approach was rejected in favor of using multiple
-child processes, which led to better performance.
+child processes, which led to better performance.
 
 Multi-Process Solution
 ----------------------
@@ -126,7 +126,7 @@ Then, for each assigned item, each worker:
 
 * W5: Writes the result to the file descriptor opened at W2.
 
-* W6: Calls `fstat()` or lstat()` on the just-written path, and sends
+* W6: Calls `fstat()` or `lstat()` on the just-written path, and sends
   the result back to the main process, together with the end status of
   the operation and the item's identification number.
 
@@ -148,7 +148,7 @@ information, the main process handles the results in two steps:
 
 - First, it updates the in-memory index with the `lstat()` information
   sent by the workers. (This must be done first as this information
-  might me required in the following step.)
+  might be required in the following step.)
 
 - Then it writes the items which collided on disk (i.e. items marked
   with `PC_ITEM_COLLIDED`). More on this below.
@@ -185,7 +185,7 @@ quite straightforward: for each parallel-eligible entry, the main
 process must remove all files that prevent this entry from being written
 (before enqueueing it). This includes any non-directory file in the
 leading path of the entry. Later, when a worker gets assigned the entry,
-it looks again for the non-directories files and for an already existing
+it looks again for the non-directory files and for an already existing
 file at the entry's path. If any of these checks finds something, the
 worker knows that there was a path collision.
 
@@ -232,7 +232,7 @@ conversion and re-encoding, are eligible for parallel checkout.
 Ineligible entries are checked out by the classic sequential codepath
 *before* spawning workers.
 
-Note: submodules's files are also eligible for parallel checkout (as
+Note: submodules' files are also eligible for parallel checkout (as
 long as they don't fall into any of the excluding categories mentioned
 above). But since each submodule is checked out in its own child
 process, we don't mix the superproject's and the submodules' files in
index 92fcee2bfffff8c42a07f0dd4b87421abd94f2bb..cd948b00722cba5ae9f01b31f6a226f8d4497ea8 100644 (file)
@@ -3,7 +3,7 @@ Partial Clone Design Notes
 
 The "Partial Clone" feature is a performance optimization for Git that
 allows Git to function without having a complete copy of the repository.
-The goal of this work is to allow Git better handle extremely large
+The goal of this work is to allow Git to better handle extremely large
 repositories.
 
 During clone and fetch operations, Git downloads the complete contents
@@ -256,7 +256,7 @@ remote in a specific order.
 - Dynamic object fetching currently uses the existing pack protocol V0
   which means that each object is requested via fetch-pack.  The server
   will send a full set of info/refs when the connection is established.
-  If there are large number of refs, this may incur significant overhead.
+  If there are large number of refs, this may incur significant overhead.
 
 
 Future Work
@@ -265,7 +265,7 @@ Future Work
 - Improve the way to specify the order in which promisor remotes are
   tried.
 +
-For example this could allow to specify explicitly something like:
+For example this could allow specifying explicitly something like:
 "When fetching from this remote, I want to use these promisor remotes
 in this order, though, when pushing or fetching to that remote, I want
 to use those promisor remotes in that order."
@@ -322,7 +322,7 @@ Footnotes
 
 [a] expensive-to-modify list of missing objects:  Earlier in the design of
     partial clone we discussed the need for a single list of missing objects.
-    This would essentially be a sorted linear list of OIDs that the were
+    This would essentially be a sorted linear list of OIDs that were
     omitted by the server during a clone or subsequent fetches.
 
 This file would need to be loaded into memory on every object lookup.
index ceda4bbfda4d27c8138c79f207f0d2adadf8bf50..59bea66c0fc6ef704e0ba9e81809a8ad1c2e9058 100644 (file)
@@ -11,7 +11,7 @@ write out the next tree object to be committed.  The state is
 "virtual" in the sense that it does not necessarily have to, and
 often does not, match the files in the working tree.
 
-There are cases Git needs to examine the differences between the
+There are cases where Git needs to examine the differences between the
 virtual working tree state in the index and the files in the
 working tree.  The most obvious case is when the user asks `git
 diff` (or its low level implementation, `git diff-files`) or
@@ -165,9 +165,9 @@ Avoiding runtime penalty
 
 In order to avoid the above runtime penalty, post 1.4.2 Git used
 to have a code that made sure the index file
-got timestamp newer than the youngest files in the index when
-there are many young files with the same timestamp as the
-resulting index file would otherwise would have by waiting
+got timestamp newer than the youngest files in the index when
+there were many young files with the same timestamp as the
+resulting index file otherwise would have by waiting
 before finishing writing the index file out.
 
 I suspected that in practice the situation where many paths in the
@@ -190,7 +190,7 @@ In a large project where raciness avoidance cost really matters,
 however, the initial computation of all object names in the
 index takes more than one second, and the index file is written
 out after all that happens.  Therefore the timestamp of the
-index file will be more than one seconds later than the
+index file will be more than one second later than the
 youngest file in the working tree.  This means that in these
 cases there actually will not be any racily clean entry in
 the resulting index.
index 6a67cc4174f820931a25bbd40c83959237b2983c..dd0b37c4e34738038b0171038dba6b180739d433 100644 (file)
@@ -46,7 +46,7 @@ search lookup, and range scans.
 
 Storage in the file is organized into variable sized blocks. Prefix
 compression is used within a single block to reduce disk space. Block
-size and alignment is tunable by the writer.
+size and alignment are tunable by the writer.
 
 Performance
 ^^^^^^^^^^^
@@ -115,7 +115,7 @@ Varint encoding
 Varint encoding is identical to the ofs-delta encoding method used
 within pack files.
 
-Decoder works such as:
+Decoder works as follows:
 
 ....
 val = buf[ptr] & 0x7f
@@ -175,7 +175,7 @@ log_index*
 footer
 ....
 
-in a log-only file the first log block immediately follows the file
+In a log-only file, the first log block immediately follows the file
 header, without padding to block alignment.
 
 Block size
@@ -247,7 +247,7 @@ uint32( hash_id )
 ....
 
 The header is identical to `version_number=1`, with the 4-byte hash ID
-("sha1" for SHA1 and "s256" for SHA-256) append to the header.
+("sha1" for SHA1 and "s256" for SHA-256) appended to the header.
 
 For maximum backward compatibility, it is recommended to use version 1 when
 writing SHA1 reftables.
@@ -288,7 +288,7 @@ The 2-byte `restart_count` stores the number of entries in the
 `restart_count` to binary search between restarts before starting a
 linear scan.
 
-Exactly `restart_count` 3-byte `restart_offset` values precedes the
+Exactly `restart_count` 3-byte `restart_offset` values precede the
 `restart_count`. Offsets are relative to the start of the block and
 refer to the first byte of any `ref_record` whose name has not been
 prefix compressed. Entries in the `restart_offset` list must be sorted,
index 1e34d913901e5922089da9451042ad2dc496b8f0..73f41761e2090a467dc0cade4888825d4606a7a7 100644 (file)
@@ -664,7 +664,7 @@ skip-irrelevant-renames optimization means we sometimes don't detect
 renames for any files within a directory that was renamed, in which
 case we will not have been able to detect any rename for the directory
 itself.  In such a case, we do not know whether the directory was
-renamed; we want to be careful to avoid cacheing some kind of "this
+renamed; we want to be careful to avoid caching some kind of "this
 directory was not renamed" statement.  If we did, then a subsequent
 commit being rebased could add a file to the old directory, and the
 user would expect it to end up in the correct directory -- something
index 7844ef30ffdefd9158ae49ddd5d03d6a5c4edd4c..27be3741e6040ed9f9915f504793b4ad978d40ad 100644 (file)
@@ -82,9 +82,9 @@ When the config key `extensions.preciousObjects` is set to `true`,
 objects in the repository MUST NOT be deleted (e.g., by `git-prune` or
 `git repack -d`).
 
-==== `partialclone`
+==== `partialClone`
 
-When the config key `extensions.partialclone` is set, it indicates
+When the config key `extensions.partialClone` is set, it indicates
 that the repo was created with a partial clone (or later performed
 a partial fetch) and that the remote may have omitted sending
 certain unwanted objects.  Such a remote is called a "promisor remote"
@@ -96,7 +96,12 @@ The value of this key is the name of the promisor remote.
 ==== `worktreeConfig`
 
 If set, by default "git config" reads from both "config" and
-"config.worktree" file from GIT_DIR in that order. In
+"config.worktree" files from GIT_DIR in that order. In
 multiple working directory mode, "config" file is shared while
 "config.worktree" is per-working directory (i.e., it's in
 GIT_COMMON_DIR/worktrees/<id>/config.worktree)
+
+==== `refStorage`
+
+Specifies the file format for the ref database. The only valid value
+is `files` (loose references with a packed-refs file).
index 35d454143399e0593af41029d75e9ff4fd3d432e..580f23360a27bf71581ce6d570a041eba62d075a 100644 (file)
@@ -60,7 +60,7 @@ By resolving this conflict, to leave line D, the user declares:
     what AB and AC wanted to do.
 
 As branch AC2 refers to the same commit as AC, the above implies that
-this is also compatible what AB and AC2 wanted to do.
+this is also compatible with what AB and AC2 wanted to do.
 
 By extension, this means that rerere should recognize that the above
 conflicts are the same.  To do this, the labels on the conflict
@@ -76,7 +76,7 @@ examples would both result in the following normalized conflict:
 Sorting hunks
 ~~~~~~~~~~~~~
 
-As before, lets imagine that a common ancestor had a file with line A
+As before, let's imagine that a common ancestor had a file with line A
 its early part, and line X in its late part.  And then four branches
 are forked that do these things:
 
@@ -99,7 +99,7 @@ conflict to leave line D means that the user declares:
     compatible with what AB and AC wanted to do.
 
 So the conflict we would see when merging AB into ACAB should be
-resolved the same way---it is the resolution that is in line with that
+resolved the same way--it is the resolution that is in line with that
 declaration.
 
 Imagine that similarly previously a branch XYXZ was forked from XY,
@@ -145,7 +145,7 @@ Nested conflicts
 Nested conflicts are handled very similarly to "simple" conflicts.
 Similar to simple conflicts, the conflict is first normalized by
 stripping the labels from conflict markers, stripping the common ancestor
-version, and the sorting the conflict hunks, both for the outer and the
+version, and sorting the conflict hunks, both for the outer and the
 inner conflict.  This is done recursively, so any number of nested
 conflicts can be handled.
 
diff --git a/Documentation/technical/sparse-checkout.txt b/Documentation/technical/sparse-checkout.txt
new file mode 100644 (file)
index 0000000..fa0d01c
--- /dev/null
@@ -0,0 +1,1103 @@
+Table of contents:
+
+  * Terminology
+  * Purpose of sparse-checkouts
+  * Usecases of primary concern
+  * Oversimplified mental models ("Cliff Notes" for this document!)
+  * Desired behavior
+  * Behavior classes
+  * Subcommand-dependent defaults
+  * Sparse specification vs. sparsity patterns
+  * Implementation Questions
+  * Implementation Goals/Plans
+  * Known bugs
+  * Reference Emails
+
+
+=== Terminology ===
+
+cone mode: one of two modes for specifying the desired subset of files
+       in a sparse-checkout.  In cone-mode, the user specifies
+       directories (getting both everything under that directory as
+       well as everything in leading directories), while in non-cone
+       mode, the user specifies gitignore-style patterns.  Controlled
+       by the --[no-]cone option to sparse-checkout init|set.
+
+SKIP_WORKTREE: When tracked files do not match the sparse specification and
+       are removed from the working tree, the file in the index is marked
+       with a SKIP_WORKTREE bit.  Note that if a tracked file has the
+       SKIP_WORKTREE bit set but the file is later written by the user to
+       the working tree anyway, the SKIP_WORKTREE bit will be cleared at
+       the beginning of any subsequent Git operation.
+
+       Most sparse checkout users are unaware of this implementation
+       detail, and the term should generally be avoided in user-facing
+       descriptions and command flags.  Unfortunately, prior to the
+       `sparse-checkout` subcommand this low-level detail was exposed,
+       and as of time of writing, is still exposed in various places.
+
+sparse-checkout: a subcommand in git used to reduce the files present in
+       the working tree to a subset of all tracked files.  Also, the
+       name of the file in the $GIT_DIR/info directory used to track
+       the sparsity patterns corresponding to the user's desired
+       subset.
+
+sparse cone: see cone mode
+
+sparse directory: An entry in the index corresponding to a directory, which
+       appears in the index instead of all the files under that directory
+       that would normally appear.  See also sparse-index.  Something that
+       can cause confusion is that the "sparse directory" does NOT match
+       the sparse specification, i.e. the directory is NOT present in the
+       working tree.  May be renamed in the future (e.g. to "skipped
+       directory").
+
+sparse index: A special mode for sparse-checkout that also makes the
+       index sparse by recording a directory entry in lieu of all the
+       files underneath that directory (thus making that a "skipped
+       directory" which unfortunately has also been called a "sparse
+       directory"), and does this for potentially multiple
+       directories.  Controlled by the --[no-]sparse-index option to
+       init|set|reapply.
+
+sparsity patterns: patterns from $GIT_DIR/info/sparse-checkout used to
+       define the set of files of interest.  A warning: It is easy to
+       over-use this term (or the shortened "patterns" term), for two
+       reasons: (1) users in cone mode specify directories rather than
+       patterns (their directories are transformed into patterns, but
+       users may think you are talking about non-cone mode if you use the
+       word "patterns"), and (b) the sparse specification might
+       transiently differ in the working tree or index from the sparsity
+       patterns (see "Sparse specification vs. sparsity patterns").
+
+sparse specification: The set of paths in the user's area of focus.  This
+       is typically just the tracked files that match the sparsity
+       patterns, but the sparse specification can temporarily differ and
+       include additional files.  (See also "Sparse specification
+       vs. sparsity patterns")
+
+       * When working with history, the sparse specification is exactly
+         the set of files matching the sparsity patterns.
+       * When interacting with the working tree, the sparse specification
+         is the set of tracked files with a clear SKIP_WORKTREE bit or
+         tracked files present in the working copy.
+       * When modifying or showing results from the index, the sparse
+         specification is the set of files with a clear SKIP_WORKTREE bit
+         or that differ in the index from HEAD.
+       * If working with the index and the working copy, the sparse
+         specification is the union of the paths from above.
+
+vivifying: When a command restores a tracked file to the working tree (and
+       hopefully also clears the SKIP_WORKTREE bit in the index for that
+       file), this is referred to as "vivifying" the file.
+
+
+=== Purpose of sparse-checkouts ===
+
+sparse-checkouts exist to allow users to work with a subset of their
+files.
+
+You can think of sparse-checkouts as subdividing "tracked" files into two
+categories -- a sparse subset, and all the rest.  Implementationally, we
+mark "all the rest" in the index with a SKIP_WORKTREE bit and leave them
+out of the working tree.  The SKIP_WORKTREE files are still tracked, just
+not present in the working tree.
+
+In the past, sparse-checkouts were defined by "SKIP_WORKTREE means the file
+is missing from the working tree but pretend the file contents match HEAD".
+That was not only bogus (it actually meant the file missing from the
+working tree matched the index rather than HEAD), but it was also a
+low-level detail which only provided decent behavior for a few commands.
+There were a surprising number of ways in which that guiding principle gave
+command results that violated user expectations, and as such was a bad
+mental model.  However, it persisted for many years and may still be found
+in some corners of the code base.
+
+Anyway, the idea of "working with a subset of files" is simple enough, but
+there are multiple different high-level usecases which affect how some Git
+subcommands should behave.  Further, even if we only considered one of
+those usecases, sparse-checkouts can modify different subcommands in over a
+half dozen different ways.  Let's start by considering the high level
+usecases:
+
+  A) Users are _only_ interested in the sparse portion of the repo
+
+  A*) Users are _only_ interested in the sparse portion of the repo
+      that they have downloaded so far
+
+  B) Users want a sparse working tree, but are working in a larger whole
+
+  C) sparse-checkout is a behind-the-scenes implementation detail allowing
+     Git to work with a specially crafted in-house virtual file system;
+     users are actually working with a "full" working tree that is
+     lazily populated, and sparse-checkout helps with the lazy population
+     piece.
+
+It may be worth explaining each of these in a bit more detail:
+
+
+  (Behavior A) Users are _only_ interested in the sparse portion of the repo
+
+These folks might know there are other things in the repository, but
+don't care.  They are uninterested in other parts of the repository, and
+only want to know about changes within their area of interest.  Showing
+them other files from history (e.g. from diff/log/grep/etc.)  is a
+usability annoyance, potentially a huge one since other changes in
+history may dwarf the changes they are interested in.
+
+Some of these users also arrive at this usecase from wanting to use partial
+clones together with sparse checkouts (in a way where they have downloaded
+blobs within the sparse specification) and do disconnected development.
+Not only do these users generally not care about other parts of the
+repository, but consider it a blocker for Git commands to try to operate on
+those.  If commands attempt to access paths in history outside the sparsity
+specification, then the partial clone will attempt to download additional
+blobs on demand, fail, and then fail the user's command.  (This may be
+unavoidable in some cases, e.g. when `git merge` has non-trivial changes to
+reconcile outside the sparse specification, but we should limit how often
+users are forced to connect to the network.)
+
+Also, even for users using partial clones that do not mind being
+always connected to the network, the need to download blobs as
+side-effects of various other commands (such as the printed diffstat
+after a merge or pull) can lead to worries about local repository size
+growing unnecessarily[10].
+
+  (Behavior A*) Users are _only_ interested in the sparse portion of the repo
+      that they have downloaded so far (a variant on the first usecase)
+
+This variant is driven by folks who using partial clones together with
+sparse checkouts and do disconnected development (so far sounding like a
+subset of behavior A users) and doing so on very large repositories.  The
+reason for yet another variant is that downloading even just the blobs
+through history within their sparse specification may be too much, so they
+only download some.  They would still like operations to succeed without
+network connectivity, though, so things like `git log -S${SEARCH_TERM} -p`
+or `git grep ${SEARCH_TERM} OLDREV ` would need to be prepared to provide
+partial results that depend on what happens to have been downloaded.
+
+This variant could be viewed as Behavior A with the sparse specification
+for history querying operations modified from "sparsity patterns" to
+"sparsity patterns limited to the blobs we have already downloaded".
+
+  (Behavior B) Users want a sparse working tree, but are working in a
+      larger whole
+
+Stolee described this usecase this way[11]:
+
+"I'm also focused on users that know that they are a part of a larger
+whole. They know they are operating on a large repository but focus on
+what they need to contribute their part. I expect multiple "roles" to
+use very different, almost disjoint parts of the codebase. Some other
+"architect" users operate across the entire tree or hop between different
+sections of the codebase as necessary. In this situation, I'm wary of
+scoping too many features to the sparse-checkout definition, especially
+"git log," as it can be too confusing to have their view of the codebase
+depend on your "point of view."
+
+People might also end up wanting behavior B due to complex inter-project
+dependencies.  The initial attempts to use sparse-checkouts usually involve
+the directories you are directly interested in plus what those directories
+depend upon within your repository.  But there's a monkey wrench here: if
+you have integration tests, they invert the hierarchy: to run integration
+tests, you need not only what you are interested in and its in-tree
+dependencies, you also need everything that depends upon what you are
+interested in or that depends upon one of your dependencies...AND you need
+all the in-tree dependencies of that expanded group.  That can easily
+change your sparse-checkout into a nearly dense one.
+
+Naturally, that tends to kill the benefits of sparse-checkouts.  There are
+a couple solutions to this conundrum: either avoid grabbing in-repo
+dependencies (maybe have built versions of your in-repo dependencies pulled
+from a CI cache somewhere), or say that users shouldn't run integration
+tests directly and instead do it on the CI server when they submit a code
+review.  Or do both.  Regardless of whether you stub out your in-repo
+dependencies or stub out the things that depend upon you, there is
+certainly a reason to want to query and be aware of those other stubbed-out
+parts of the repository, particularly when the dependencies are complex or
+change relatively frequently.  Thus, for such uses, sparse-checkouts can be
+used to limit what you directly build and modify, but these users do not
+necessarily want their sparse checkout paths to limit their queries of
+versions in history.
+
+Some people may also be interested in behavior B over behavior A simply as
+a performance workaround: if they are using non-cone mode, then they have
+to deal with its inherent quadratic performance problems.  In that mode,
+every operation that checks whether paths match the sparsity specification
+can be expensive.  As such, these users may only be willing to pay for
+those expensive checks when interacting with the working copy, and may
+prefer getting "unrelated" results from their history queries over having
+slow commands.
+
+  (Behavior C) sparse-checkout is an implementational detail supporting a
+              special VFS.
+
+This usecase goes slightly against the traditional definition of
+sparse-checkout in that it actually tries to present a full or dense
+checkout to the user.  However, this usecase utilizes the same underlying
+technical underpinnings in a new way which does provide some performance
+advantages to users.  The basic idea is that a company can have an in-house
+Git-aware Virtual File System which pretends all files are present in the
+working tree, by intercepting all file system accesses and using those to
+fetch and write accessed files on demand via partial clones.  The VFS uses
+sparse-checkout to prevent Git from writing or paying attention to many
+files, and manually updates the sparse checkout patterns itself based on
+user access and modification of files in the working tree.  See commit
+ecc7c8841d ("repo_read_index: add config to expect files outside sparse
+patterns", 2022-02-25) and the link at [17] for a more detailed description
+of such a VFS.
+
+The biggest difference here is that users are completely unaware that the
+sparse-checkout machinery is even in use.  The sparse patterns are not
+specified by the user but rather are under the complete control of the VFS
+(and the patterns are updated frequently and dynamically by it).  The user
+will perceive the checkout as dense, and commands should thus behave as if
+all files are present.
+
+
+=== Usecases of primary concern ===
+
+Most of the rest of this document will focus on Behavior A and Behavior
+B.  Some notes about the other two cases and why we are not focusing on
+them:
+
+  (Behavior A*)
+
+Supporting this usecase is estimated to be difficult and a lot of work.
+There are no plans to implement it currently, but it may be a potential
+future alternative.  Knowing about the existence of additional alternatives
+may affect our choice of command line flags (e.g. if we need tri-state or
+quad-state flags rather than just binary flags), so it was still important
+to at least note.
+
+Further, I believe the descriptions below for Behavior A are probably still
+valid for this usecase, with the only exception being that it redefines the
+sparse specification to restrict it to already-downloaded blobs.  The hard
+part is in making commands capable of respecting that modified definition.
+
+  (Behavior C)
+
+This usecase violates some of the early sparse-checkout documented
+assumptions (since files marked as SKIP_WORKTREE will be displayed to users
+as present in the working tree).  That violation may mean various
+sparse-checkout related behaviors are not well suited to this usecase and
+we may need tweaks -- to both documentation and code -- to handle it.
+However, this usecase is also perhaps the simplest model to support in that
+everything behaves like a dense checkout with a few exceptions (e.g. branch
+checkouts and switches write fewer things, knowing the VFS will lazily
+write the rest on an as-needed basis).
+
+Since there is no publically available VFS-related code for folks to try,
+the number of folks who can test such a usecase is limited.
+
+The primary reason to note the Behavior C usecase is that as we fix things
+to better support Behaviors A and B, there may be additional places where
+we need to make tweaks allowing folks in this usecase to get the original
+non-sparse treatment.  For an example, see ecc7c8841d ("repo_read_index:
+add config to expect files outside sparse patterns", 2022-02-25).  The
+secondary reason to note Behavior C, is so that folks taking advantage of
+Behavior C do not assume they are part of the Behavior B camp and propose
+patches that break things for the real Behavior B folks.
+
+
+=== Oversimplified mental models ===
+
+An oversimplification of the differences in the above behaviors is:
+
+  Behavior A: Restrict worktree and history operations to sparse specification
+  Behavior B: Restrict worktree operations to sparse specification; have any
+             history operations work across all files
+  Behavior C: Do not restrict either worktree or history operations to the
+             sparse specification...with the exception of branch checkouts or
+             switches which avoid writing files that will match the index so
+             they can later lazily be populated instead.
+
+
+=== Desired behavior ===
+
+As noted previously, despite the simple idea of just working with a subset
+of files, there are a range of different behavioral changes that need to be
+made to different subcommands to work well with such a feature.  See
+[1,2,3,4,5,6,7,8,9,10] for various examples.  In particular, at [2], we saw
+that mere composition of other commands that individually worked correctly
+in a sparse-checkout context did not imply that the higher level command
+would work correctly; it sometimes requires further tweaks.  So,
+understanding these differences can be beneficial.
+
+* Commands behaving the same regardless of high-level use-case
+
+  * commands that only look at files within the sparsity specification
+
+      * diff (without --cached or REVISION arguments)
+      * grep (without --cached or REVISION arguments)
+      * diff-files
+
+  * commands that restore files to the working tree that match sparsity
+    patterns, and remove unmodified files that don't match those
+    patterns:
+
+      * switch
+      * checkout (the switch-like half)
+      * read-tree
+      * reset --hard
+
+  * commands that write conflicted files to the working tree, but otherwise
+    will omit writing files to the working tree that do not match the
+    sparsity patterns:
+
+      * merge
+      * rebase
+      * cherry-pick
+      * revert
+
+      * `am` and `apply --cached` should probably be in this section but
+       are buggy (see the "Known bugs" section below)
+
+    The behavior for these commands somewhat depends upon the merge
+    strategy being used:
+      * `ort` behaves as described above
+      * `recursive` tries to not vivify files unnecessarily, but does sometimes
+       vivify files without conflicts.
+      * `octopus` and `resolve` will always vivify any file changed in the merge
+       relative to the first parent, which is rather suboptimal.
+
+    It is also important to note that these commands WILL update the index
+    outside the sparse specification relative to when the operation began,
+    BUT these commands often make a commit just before or after such that
+    by the end of the operation there is no change to the index outside the
+    sparse specification.  Of course, if the operation hits conflicts or
+    does not make a commit, then these operations clearly can modify the
+    index outside the sparse specification.
+
+    Finally, it is important to note that at least the first four of these
+    commands also try to remove differences between the sparse
+    specification and the sparsity patterns (much like the commands in the
+    previous section).
+
+  * commands that always ignore sparsity since commits must be full-tree
+
+      * archive
+      * bundle
+      * commit
+      * format-patch
+      * fast-export
+      * fast-import
+      * commit-tree
+
+  * commands that write any modified file to the working tree (conflicted
+    or not, and whether those paths match sparsity patterns or not):
+
+      * stash
+      * apply (without `--index` or `--cached`)
+
+* Commands that may slightly differ for behavior A vs. behavior B:
+
+  Commands in this category behave mostly the same between the two
+  behaviors, but may differ in verbosity and types of warning and error
+  messages.
+
+  * commands that make modifications to which files are tracked:
+      * add
+      * rm
+      * mv
+      * update-index
+
+    The fact that files can move between the 'tracked' and 'untracked'
+    categories means some commands will have to treat untracked files
+    differently.  But if we have to treat untracked files differently,
+    then additional commands may also need changes:
+
+      * status
+      * clean
+
+    In particular, `status` may need to report any untracked files outside
+    the sparsity specification as an erroneous condition (especially to
+    avoid the user trying to `git add` them, forcing `git add` to display
+    an error).
+
+    It's not clear to me exactly how (or even if) `clean` would change,
+    but it's the other command that also affects untracked files.
+
+    `update-index` may be slightly special.  Its --[no-]skip-worktree flag
+    may need to ignore the sparse specification by its nature.  Also, its
+    current --[no-]ignore-skip-worktree-entries default is totally bogus.
+
+  * commands for manually tweaking paths in both the index and the working tree
+      * `restore`
+      * the restore-like half of `checkout`
+
+    These commands should be similar to add/rm/mv in that they should
+    only operate on the sparse specification by default, and require a
+    special flag to operate on all files.
+
+    Also, note that these commands currently have a number of issues (see
+    the "Known bugs" section below)
+
+* Commands that significantly differ for behavior A vs. behavior B:
+
+  * commands that query history
+      * diff (with --cached or REVISION arguments)
+      * grep (with --cached or REVISION arguments)
+      * show (when given commit arguments)
+      * blame (only matters when one or more -C flags are passed)
+       * and annotate
+      * log
+      * whatchanged
+      * ls-files
+      * diff-index
+      * diff-tree
+      * ls-tree
+
+    Note: for log and whatchanged, revision walking logic is unaffected
+    but displaying of patches is affected by scoping the command to the
+    sparse-checkout.  (The fact that revision walking is unaffected is
+    why rev-list, shortlog, show-branch, and bisect are not in this
+    list.)
+
+    ls-files may be slightly special in that e.g. `git ls-files -t` is
+    often used to see what is sparse and what is not.  Perhaps -t should
+    always work on the full tree?
+
+* Commands I don't know how to classify
+
+  * range-diff
+
+    Is this like `log` or `format-patch`?
+
+  * cherry
+
+    See range-diff
+
+* Commands unaffected by sparse-checkouts
+
+  * shortlog
+  * show-branch
+  * rev-list
+  * bisect
+
+  * branch
+  * describe
+  * fetch
+  * gc
+  * init
+  * maintenance
+  * notes
+  * pull (merge & rebase have the necessary changes)
+  * push
+  * submodule
+  * tag
+
+  * config
+  * filter-branch (works in separate checkout without sparse-checkout setup)
+  * pack-refs
+  * prune
+  * remote
+  * repack
+  * replace
+
+  * bugreport
+  * count-objects
+  * fsck
+  * gitweb
+  * help
+  * instaweb
+  * merge-tree (doesn't touch worktree or index, and merges always compute full-tree)
+  * rerere
+  * verify-commit
+  * verify-tag
+
+  * commit-graph
+  * hash-object
+  * index-pack
+  * mktag
+  * mktree
+  * multi-pack-index
+  * pack-objects
+  * prune-packed
+  * symbolic-ref
+  * unpack-objects
+  * update-ref
+  * write-tree (operates on index, possibly optimized to use sparse dir entries)
+
+  * for-each-ref
+  * get-tar-commit-id
+  * ls-remote
+  * merge-base (merges are computed full tree, so merge base should be too)
+  * name-rev
+  * pack-redundant
+  * rev-parse
+  * show-index
+  * show-ref
+  * unpack-file
+  * var
+  * verify-pack
+
+  * <Everything under 'Interacting with Others' in 'git help --all'>
+  * <Everything under 'Low-level...Syncing' in 'git help --all'>
+  * <Everything under 'Low-level...Internal Helpers' in 'git help --all'>
+  * <Everything under 'External commands' in 'git help --all'>
+
+* Commands that might be affected, but who cares?
+
+  * merge-file
+  * merge-index
+  * gitk?
+
+
+=== Behavior classes ===
+
+From the above there are a few classes of behavior:
+
+  * "restrict"
+
+    Commands in this class only read or write files in the working tree
+    within the sparse specification.
+
+    When moving to a new commit (e.g. switch, reset --hard), these commands
+    may update index files outside the sparse specification as of the start
+    of the operation, but by the end of the operation those index files
+    will match HEAD again and thus those files will again be outside the
+    sparse specification.
+
+    When paths are explicitly specified, these paths are intersected with
+    the sparse specification and will only operate on such paths.
+    (e.g. `git restore [--staged] -- '*.png'`, `git reset -p -- '*.md'`)
+
+    Some of these commands may also attempt, at the end of their operation,
+    to cull transient differences between the sparse specification and the
+    sparsity patterns (see "Sparse specification vs. sparsity patterns" for
+    details, but this basically means either removing unmodified files not
+    matching the sparsity patterns and marking those files as
+    SKIP_WORKTREE, or vivifying files that match the sparsity patterns and
+    marking those files as !SKIP_WORKTREE).
+
+  * "restrict modulo conflicts"
+
+    Commands in this class generally behave like the "restrict" class,
+    except that:
+      (1) they will ignore the sparse specification and write files with
+         conflicts to the working tree (thus temporarily expanding the
+         sparse specification to include such files.)
+      (2) they are grouped with commands which move to a new commit, since
+         they often create a commit and then move to it, even though we
+         know there are many exceptions to moving to the new commit.  (For
+         example, the user may rebase a commit that becomes empty, or have
+         a cherry-pick which conflicts, or a user could run `merge
+         --no-commit`, and we also view `apply --index` kind of like `am
+         --no-commit`.)  As such, these commands can make changes to index
+         files outside the sparse specification, though they'll mark such
+         files with SKIP_WORKTREE.
+
+  * "restrict also specially applied to untracked files"
+
+    Commands in this class generally behave like the "restrict" class,
+    except that they have to handle untracked files differently too, often
+    because these commands are dealing with files changing state between
+    'tracked' and 'untracked'.  Often, this may mean printing an error
+    message if the command had nothing to do, but the arguments may have
+    referred to files whose tracked-ness state could have changed were it
+    not for the sparsity patterns excluding them.
+
+  * "no restrict"
+
+    Commands in this class ignore the sparse specification entirely.
+
+  * "restrict or no restrict dependent upon behavior A vs. behavior B"
+
+    Commands in this class behave like "no restrict" for folks in the
+    behavior B camp, and like "restrict" for folks in the behavior A camp.
+    However, when behaving like "restrict" a warning of some sort might be
+    provided that history queries have been limited by the sparse-checkout
+    specification.
+
+
+=== Subcommand-dependent defaults ===
+
+Note that we have different defaults depending on the command for the
+desired behavior :
+
+  * Commands defaulting to "restrict":
+    * diff-files
+    * diff (without --cached or REVISION arguments)
+    * grep (without --cached or REVISION arguments)
+    * switch
+    * checkout (the switch-like half)
+    * reset (<commit>)
+
+    * restore
+    * checkout (the restore-like half)
+    * checkout-index
+    * reset (with pathspec)
+
+    This behavior makes sense; these interact with the working tree.
+
+  * Commands defaulting to "restrict modulo conflicts":
+    * merge
+    * rebase
+    * cherry-pick
+    * revert
+
+    * am
+    * apply --index (which is kind of like an `am --no-commit`)
+
+    * read-tree (especially with -m or -u; is kind of like a --no-commit merge)
+    * reset (<tree-ish>, due to similarity to read-tree)
+
+    These also interact with the working tree, but require slightly
+    different behavior either so that (a) conflicts can be resolved or (b)
+    because they are kind of like a merge-without-commit operation.
+
+    (See also the "Known bugs" section below regarding `am` and `apply`)
+
+  * Commands defaulting to "no restrict":
+    * archive
+    * bundle
+    * commit
+    * format-patch
+    * fast-export
+    * fast-import
+    * commit-tree
+
+    * stash
+    * apply (without `--index`)
+
+    These have completely different defaults and perhaps deserve the most
+    detailed explanation:
+
+    In the case of commands in the first group (format-patch,
+    fast-export, bundle, archive, etc.), these are commands for
+    communicating history, which will be broken if they restrict to a
+    subset of the repository.  As such, they operate on full paths and
+    have no `--restrict` option for overriding.  Some of these commands may
+    take paths for manually restricting what is exported, but it needs to
+    be very explicit.
+
+    In the case of stash, it needs to vivify files to avoid losing the
+    user's changes.
+
+    In the case of apply without `--index`, that command needs to update
+    the working tree without the index (or the index without the working
+    tree if `--cached` is passed), and if we restrict those updates to the
+    sparse specification then we'll lose changes from the user.
+
+  * Commands defaulting to "restrict also specially applied to untracked files":
+    * add
+    * rm
+    * mv
+    * update-index
+    * status
+    * clean (?)
+
+    Our original implementation for the first three of these commands was
+    "no restrict", but it had some severe usability issues:
+      * `git add <somefile>` if honored and outside the sparse
+       specification, can result in the file randomly disappearing later
+       when some subsequent command is run (since various commands
+       automatically clean up unmodified files outside the sparse
+       specification).
+      * `git rm '*.jpg'` could very negatively surprise users if it deletes
+       files outside the range of the user's interest.
+      * `git mv` has similar surprises when moving into or out of the cone,
+       so best to restrict by default
+
+    So, we switched `add` and `rm` to default to "restrict", which made
+    usability problems much less severe and less frequent, but we still got
+    complaints because commands like:
+       git add <file-outside-sparse-specification>
+       git rm <file-outside-sparse-specification>
+    would silently do nothing.  We should instead print an error in those
+    cases to get usability right.
+
+    update-index needs to be updated to match, and status and maybe clean
+    also need to be updated to specially handle untracked paths.
+
+    There may be a difference in here between behavior A and behavior B in
+    terms of verboseness of errors or additional warnings.
+
+  * Commands falling under "restrict or no restrict dependent upon behavior
+    A vs. behavior B"
+
+    * diff (with --cached or REVISION arguments)
+    * grep (with --cached or REVISION arguments)
+    * show (when given commit arguments)
+    * blame (only matters when one or more -C flags passed)
+      * and annotate
+    * log
+      * and variants: shortlog, gitk, show-branch, whatchanged, rev-list
+    * ls-files
+    * diff-index
+    * diff-tree
+    * ls-tree
+
+    For now, we default to behavior B for these, which want a default of
+    "no restrict".
+
+    Note that two of these commands -- diff and grep -- also appeared in a
+    different list with a default of "restrict", but only when limited to
+    searching the working tree.  The working tree vs. history distinction
+    is fundamental in how behavior B operates, so this is expected.  Note,
+    though, that for diff and grep with --cached, when doing "restrict"
+    behavior, the difference between sparse specification and sparsity
+    patterns is important to handle.
+
+    "restrict" may make more sense as the long term default for these[12].
+    Also, supporting "restrict" for these commands might be a fair amount
+    of work to implement, meaning it might be implemented over multiple
+    releases.  If that behavior were the default in the commands that
+    supported it, that would force behavior B users to need to learn to
+    slowly add additional flags to their commands, depending on git
+    version, to get the behavior they want.  That gradual switchover would
+    be painful, so we should avoid it at least until it's fully
+    implemented.
+
+
+=== Sparse specification vs. sparsity patterns ===
+
+In a well-behaved situation, the sparse specification is given directly
+by the $GIT_DIR/info/sparse-checkout file.  However, it can transiently
+diverge for a few reasons:
+
+    * needing to resolve conflicts (merging will vivify conflicted files)
+    * running Git commands that implicitly vivify files (e.g. "git stash apply")
+    * running Git commands that explicitly vivify files (e.g. "git checkout
+      --ignore-skip-worktree-bits FILENAME")
+    * other commands that write to these files (perhaps a user copies it
+      from elsewhere)
+
+For the last item, note that we do automatically clear the SKIP_WORKTREE
+bit for files that are present in the working tree.  This has been true
+since 82386b4496 ("Merge branch 'en/present-despite-skipped'",
+2022-03-09)
+
+However, such a situation is transient because:
+
+   * Such transient differences can and will be automatically removed as
+     a side-effect of commands which call unpack_trees() (checkout,
+     merge, reset, etc.).
+   * Users can also request such transient differences be corrected via
+     running `git sparse-checkout reapply`.  Various places recommend
+     running that command.
+   * Additional commands are also welcome to implicitly fix these
+     differences; we may add more in the future.
+
+While we avoid dropping unstaged changes or files which have conflicts,
+we otherwise aggressively try to fix these transient differences.  If
+users want these differences to persist, they should run the `set` or
+`add` subcommands of `git sparse-checkout` to reflect their intended
+sparse specification.
+
+However, when we need to do a query on history restricted to the
+"relevant subset of files" such a transiently expanded sparse
+specification is ignored.  There are a couple reasons for this:
+
+   * The behavior wanted when doing something like
+        git grep expression REVISION
+     is roughly what the users would expect from
+        git checkout REVISION && git grep expression
+     (modulo a "REVISION:" prefix), which has a couple ramifications:
+
+   * REVISION may have paths not in the current index, so there is no
+     path we can consult for a SKIP_WORKTREE setting for those paths.
+
+   * Since `checkout` is one of those commands that tries to remove
+     transient differences in the sparse specification, it makes sense
+     to use the corrected sparse specification
+     (i.e. $GIT_DIR/info/sparse-checkout) rather than attempting to
+     consult SKIP_WORKTREE anyway.
+
+So, a transiently expanded (or restricted) sparse specification applies to
+the working tree, but not to history queries where we always use the
+sparsity patterns.  (See [16] for an early discussion of this.)
+
+Similar to a transiently expanded sparse specification of the working tree
+based on additional files being present in the working tree, we also need
+to consider additional files being modified in the index.  In particular,
+if the user has staged changes to files (relative to HEAD) that do not
+match the sparsity patterns, and the file is not present in the working
+tree, we still want to consider the file part of the sparse specification
+if we are specifically performing a query related to the index (e.g. git
+diff --cached [REVISION], git diff-index [REVISION], git restore --staged
+--source=REVISION -- PATHS, etc.)  Note that a transiently expanded sparse
+specification for the index usually only matters under behavior A, since
+under behavior B index operations are lumped with history and tend to
+operate full-tree.
+
+
+=== Implementation Questions ===
+
+  * Do the options --scope={sparse,all} sound good to others?  Are there better
+    options?
+    * Names in use, or appearing in patches, or previously suggested:
+      * --sparse/--dense
+      * --ignore-skip-worktree-bits
+      * --ignore-skip-worktree-entries
+      * --ignore-sparsity
+      * --[no-]restrict-to-sparse-paths
+      * --full-tree/--sparse-tree
+      * --[no-]restrict
+      * --scope={sparse,all}
+      * --focus/--unfocus
+      * --limit/--unlimited
+    * Rationale making me lean slightly towards --scope={sparse,all}:
+      * We want a name that works for many commands, so we need a name that
+       does not conflict
+      * We know that we have more than two possible usecases, so it is best
+       to avoid a flag that appears to be binary.
+      * --scope={sparse,all} isn't overly long and seems relatively
+       explanatory
+      * `--sparse`, as used in add/rm/mv, is totally backwards for
+       grep/log/etc.  Changing the meaning of `--sparse` for these
+       commands would fix the backwardness, but possibly break existing
+       scripts.  Using a new name pairing would allow us to treat
+       `--sparse` in these commands as a deprecated alias.
+      * There is a different `--sparse`/`--dense` pair for commands using
+       revision machinery, so using that naming might cause confusion
+      * There is also a `--sparse` in both pack-objects and show-branch, which
+       don't conflict but do suggest that `--sparse` is overloaded
+      * The name --ignore-skip-worktree-bits is a double negative, is
+       quite a mouthful, refers to an implementation detail that many
+       users may not be familiar with, and we'd need a negation for it
+       which would probably be even more ridiculously long.  (But we
+       can make --ignore-skip-worktree-bits a deprecated alias for
+       --no-restrict.)
+
+  * If a config option is added (sparse.scope?) what should the values and
+    description be?  "sparse" (behavior A), "worktree-sparse-history-dense"
+    (behavior B), "dense" (behavior C)?  There's a risk of confusion,
+    because even for Behaviors A and B we want some commands to be
+    full-tree and others to operate sparsely, so the wording may need to be
+    more tied to the usecases and somehow explain that.  Also, right now,
+    the primary difference we are focusing is just the history-querying
+    commands (log/diff/grep).  Previous config suggestion here: [13]
+
+  * Is `--no-expand` a good alias for ls-files's `--sparse` option?
+    (`--sparse` does not map to either `--scope=sparse` or `--scope=all`,
+    because in non-cone mode it does nothing and in cone-mode it shows the
+    sparse directory entries which are technically outside the sparse
+    specification)
+
+  * Under Behavior A:
+    * Does ls-files' `--no-expand` override the default `--scope=all`, or
+      does it need an extra flag?
+    * Does ls-files' `-t` option imply `--scope=all`?
+    * Does update-index's `--[no-]skip-worktree` option imply `--scope=all`?
+
+  * sparse-checkout: once behavior A is fully implemented, should we take
+    an interim measure to ease people into switching the default?  Namely,
+    if folks are not already in a sparse checkout, then require
+    `sparse-checkout init/set` to take a
+    `--set-scope=(sparse|worktree-sparse-history-dense|dense)` flag (which
+    would set sparse.scope according to the setting given), and throw an
+    error if the flag is not provided?  That error would be a great place
+    to warn folks that the default may change in the future, and get them
+    used to specifying what they want so that the eventual default switch
+    is seamless for them.
+
+
+=== Implementation Goals/Plans ===
+
+ * Get buy-in on this document in general.
+
+ * Figure out answers to the 'Implementation Questions' sections (above)
+
+ * Fix bugs in the 'Known bugs' section (below)
+
+ * Provide some kind of method for backfilling the blobs within the sparse
+   specification in a partial clone
+
+ [Below here is kind of spitballing since the first two haven't been resolved]
+
+ * update-index: flip the default to --no-ignore-skip-worktree-entries,
+   nuke this stupid "Oh, there's a bug?  Let me add a flag to let users
+   request that they not trigger this bug." flag
+
+ * Flags & Config
+   * Make `--sparse` in add/rm/mv a deprecated alias for `--scope=all`
+   * Make `--ignore-skip-worktree-bits` in checkout-index/checkout/restore
+     a deprecated aliases for `--scope=all`
+   * Create config option (sparse.scope?), tie it to the "Cliff notes"
+     overview
+
+   * Add --scope=sparse (and --scope=all) flag to each of the history querying
+     commands.  IMPORTANT: make sure diff machinery changes don't mess with
+     format-patch, fast-export, etc.
+
+=== Known bugs ===
+
+This list used to be a lot longer (see e.g. [1,2,3,4,5,6,7,8,9]), but we've
+been working on it.
+
+0. Behavior A is not well supported in Git.  (Behavior B didn't used to
+   be either, but was the easier of the two to implement.)
+
+1. am and apply:
+
+   apply, without `--index` or `--cached`, relies on files being present
+   in the working copy, and also writes to them unconditionally.  As
+   such, it should first check for the files' presence, and if found to
+   be SKIP_WORKTREE, then clear the bit and vivify the paths, then do
+   its work.  Currently, it just throws an error.
+
+   apply, with either `--cached` or `--index`, will not preserve the
+   SKIP_WORKTREE bit.  This is fine if the file has conflicts, but
+   otherwise SKIP_WORKTREE bits should be preserved for --cached and
+   probably also for --index.
+
+   am, if there are no conflicts, will vivify files and fail to preserve
+   the SKIP_WORKTREE bit.  If there are conflicts and `-3` is not
+   specified, it will vivify files and then complain the patch doesn't
+   apply.  If there are conflicts and `-3` is specified, it will vivify
+   files and then complain that those vivified files would be
+   overwritten by merge.
+
+2. reset --hard:
+
+   reset --hard provides confusing error message (works correctly, but
+   misleads the user into believing it didn't):
+
+    $ touch addme
+    $ git add addme
+    $ git ls-files -t
+    H addme
+    H tracked
+    S tracked-but-maybe-skipped
+    $ git reset --hard                           # usually works great
+    error: Path 'addme' not uptodate; will not remove from working tree.
+    HEAD is now at bdbbb6f third
+    $ git ls-files -t
+    H tracked
+    S tracked-but-maybe-skipped
+    $ ls -1
+    tracked
+
+    `git reset --hard` DID remove addme from the index and the working tree, contrary
+    to the error message, but in line with how reset --hard should behave.
+
+3. read-tree
+
+   `read-tree` doesn't apply the 'SKIP_WORKTREE' bit to *any* of the
+   entries it reads into the index, resulting in all your files suddenly
+   appearing to be "deleted".
+
+4. Checkout, restore:
+
+   These command do not handle path & revision arguments appropriately:
+
+    $ ls
+    tracked
+    $ git ls-files -t
+    H tracked
+    S tracked-but-maybe-skipped
+    $ git status --porcelain
+    $ git checkout -- '*skipped'
+    error: pathspec '*skipped' did not match any file(s) known to git
+    $ git ls-files -- '*skipped'
+    tracked-but-maybe-skipped
+    $ git checkout HEAD -- '*skipped'
+    error: pathspec '*skipped' did not match any file(s) known to git
+    $ git ls-tree HEAD | grep skipped
+    100644 blob 276f5a64354b791b13840f02047738c77ad0584f       tracked-but-maybe-skipped
+    $ git status --porcelain
+    $ git checkout HEAD~1 -- '*skipped'
+    $ git ls-files -t
+    H tracked
+    H tracked-but-maybe-skipped
+    $ git status --porcelain
+    M  tracked-but-maybe-skipped
+    $ git checkout HEAD -- '*skipped'
+    $ git status --porcelain
+    $
+
+    Note that checkout without a revision (or restore --staged) fails to
+    find a file to restore from the index, even though ls-files shows
+    such a file certainly exists.
+
+    Similar issues occur with HEAD (--source=HEAD in restore's case),
+    but suddenly works when HEAD~1 is specified.  And then after that it
+    will work with HEAD specified, even though it didn't before.
+
+    Directories are also an issue:
+
+    $ git sparse-checkout set nomatches
+    $ git status
+    On branch main
+    You are in a sparse checkout with 0% of tracked files present.
+
+    nothing to commit, working tree clean
+    $ git checkout .
+    error: pathspec '.' did not match any file(s) known to git
+    $ git checkout HEAD~1 .
+    Updated 1 path from 58916d9
+    $ git ls-files -t
+    S tracked
+    H tracked-but-maybe-skipped
+
+5. checkout and restore --staged, continued:
+
+   These commands do not correctly scope operations to the sparse
+   specification, and make it worse by not setting important SKIP_WORKTREE
+   bits:
+
+   $ git restore --source OLDREV --staged outside-sparse-cone/
+   $ git status --porcelain
+   MD outside-sparse-cone/file1
+   MD outside-sparse-cone/file2
+   MD outside-sparse-cone/file3
+
+   We can add a --scope=all mode to `git restore` to let it operate outside
+   the sparse specification, but then it will be important to set the
+   SKIP_WORKTREE bits appropriately.
+
+6. Performance issues; see:
+    https://lore.kernel.org/git/CABPp-BEkJQoKZsQGCYioyga_uoDQ6iBeW+FKr8JhyuuTMK1RDw@mail.gmail.com/
+
+
+=== Reference Emails ===
+
+Emails that detail various bugs we've had in sparse-checkout:
+
+[1] (Original descriptions of behavior A & behavior B)
+    https://lore.kernel.org/git/CABPp-BGJ_Nvi5TmgriD9Bh6eNXE2EDq2f8e8QKXAeYG3BxZafA@mail.gmail.com/
+[2] (Fix stash applications in sparse checkouts; bugs from behavioral differences)
+    https://lore.kernel.org/git/ccfedc7140dbf63ba26a15f93bd3885180b26517.1606861519.git.gitgitgadget@gmail.com/
+[3] (Present-despite-skipped entries)
+    https://lore.kernel.org/git/11d46a399d26c913787b704d2b7169cafc28d639.1642175983.git.gitgitgadget@gmail.com/
+[4] (Clone --no-checkout interaction)
+    https://lore.kernel.org/git/pull.801.v2.git.git.1591324899170.gitgitgadget@gmail.com/ (clone --no-checkout)
+[5] (The need for update_sparsity() and avoiding `read-tree -mu HEAD`)
+    https://lore.kernel.org/git/3a1f084641eb47515b5a41ed4409a36128913309.1585270142.git.gitgitgadget@gmail.com/
+[6] (SKIP_WORKTREE is advisory, not mandatory)
+    https://lore.kernel.org/git/844306c3e86ef67591cc086decb2b760e7d710a3.1585270142.git.gitgitgadget@gmail.com/
+[7] (`worktree add` should copy sparsity settings from current worktree)
+    https://lore.kernel.org/git/c51cb3714e7b1d2f8c9370fe87eca9984ff4859f.1644269584.git.gitgitgadget@gmail.com/
+[8] (Avoid negative surprises in add, rm, and mv)
+    https://lore.kernel.org/git/cover.1617914011.git.matheus.bernardino@usp.br/
+    https://lore.kernel.org/git/pull.1018.v4.git.1632497954.gitgitgadget@gmail.com/
+[9] (Move from out-of-cone to in-cone)
+    https://lore.kernel.org/git/20220630023737.473690-6-shaoxuan.yuan02@gmail.com/
+    https://lore.kernel.org/git/20220630023737.473690-4-shaoxuan.yuan02@gmail.com/
+[10] (Unnecessarily downloading objects outside sparse specification)
+     https://lore.kernel.org/git/CAOLTT8QfwOi9yx_qZZgyGa8iL8kHWutEED7ok_jxwTcYT_hf9Q@mail.gmail.com/
+
+[11] (Stolee's comments on high-level usecases)
+     https://lore.kernel.org/git/1a1e33f6-3514-9afc-0a28-5a6b85bd8014@gmail.com/
+
+[12] Others commenting on eventually switching default to behavior A:
+  * https://lore.kernel.org/git/xmqqh719pcoo.fsf@gitster.g/
+  * https://lore.kernel.org/git/xmqqzgeqw0sy.fsf@gitster.g/
+  * https://lore.kernel.org/git/a86af661-cf58-a4e5-0214-a67d3a794d7e@github.com/
+
+[13] Previous config name suggestion and description
+  * https://lore.kernel.org/git/CABPp-BE6zW0nJSStcVU=_DoDBnPgLqOR8pkTXK3dW11=T01OhA@mail.gmail.com/
+
+[14] Tangential issue: switch to cone mode as default sparse specification mechanism:
+  https://lore.kernel.org/git/a1b68fd6126eb341ef3637bb93fedad4309b36d0.1650594746.git.gitgitgadget@gmail.com/
+
+[15] Lengthy email on grep behavior, covering what should be searched:
+  * https://lore.kernel.org/git/CABPp-BGVO3QdbfE84uF_3QDF0-y2iHHh6G5FAFzNRfeRitkuHw@mail.gmail.com/
+
+[16] Email explaining sparsity patterns vs. SKIP_WORKTREE and history operations,
+     search for the parenthetical comment starting "We do not check".
+    https://lore.kernel.org/git/CABPp-BFsCPPNOZ92JQRJeGyNd0e-TCW-LcLyr0i_+VSQJP+GCg@mail.gmail.com/
+
+[17] https://lore.kernel.org/git/20220207190320.2960362-1-jonathantanmy@google.com/
diff --git a/Documentation/technical/unit-tests.txt b/Documentation/technical/unit-tests.txt
new file mode 100644 (file)
index 0000000..206037f
--- /dev/null
@@ -0,0 +1,240 @@
+= Unit Testing
+
+In our current testing environment, we spend a significant amount of effort
+crafting end-to-end tests for error conditions that could easily be captured by
+unit tests (or we simply forgo some hard-to-setup and rare error conditions).
+Unit tests additionally provide stability to the codebase and can simplify
+debugging through isolation. Writing unit tests in pure C, rather than with our
+current shell/test-tool helper setup, simplifies test setup, simplifies passing
+data around (no shell-isms required), and reduces testing runtime by not
+spawning a separate process for every test invocation.
+
+We believe that a large body of unit tests, living alongside the existing test
+suite, will improve code quality for the Git project.
+
+== Definitions
+
+For the purposes of this document, we'll use *test framework* to refer to
+projects that support writing test cases and running tests within the context
+of a single executable. *Test harness* will refer to projects that manage
+running multiple executables (each of which may contain multiple test cases) and
+aggregating their results.
+
+In reality, these terms are not strictly defined, and many of the projects
+discussed below contain features from both categories.
+
+For now, we will evaluate projects solely on their framework features. Since we
+are relying on having TAP output (see below), we can assume that any framework
+can be made to work with a harness that we can choose later.
+
+
+== Summary
+
+We believe the best way forward is to implement a custom TAP framework for the
+Git project. We use a version of the framework originally proposed in
+https://lore.kernel.org/git/c902a166-98ce-afba-93f2-ea6027557176@gmail.com/[1].
+
+See the <<framework-selection,Framework Selection>> section below for the
+rationale behind this decision.
+
+
+== Choosing a test harness
+
+During upstream discussion, it was occasionally noted that `prove` provides many
+convenient features, such as scheduling slower tests first, or re-running
+previously failed tests.
+
+While we already support the use of `prove` as a test harness for the shell
+tests, it is not strictly required. The t/Makefile allows running shell tests
+directly (though with interleaved output if parallelism is enabled). Git
+developers who wish to use `prove` as a more advanced harness can do so by
+setting DEFAULT_TEST_TARGET=prove in their config.mak.
+
+We will follow a similar approach for unit tests: by default the test
+executables will be run directly from the t/Makefile, but `prove` can be
+configured with DEFAULT_UNIT_TEST_TARGET=prove.
+
+
+[[framework-selection]]
+== Framework selection
+
+There are a variety of features we can use to rank the candidate frameworks, and
+those features have different priorities:
+
+* Critical features: we probably won't consider a framework without these
+** Can we legally / easily use the project?
+*** <<license,License>>
+*** <<vendorable-or-ubiquitous,Vendorable or ubiquitous>>
+*** <<maintainable-extensible,Maintainable / extensible>>
+*** <<major-platform-support,Major platform support>>
+** Does the project support our bare-minimum needs?
+*** <<tap-support,TAP support>>
+*** <<diagnostic-output,Diagnostic output>>
+*** <<runtime-skippable-tests,Runtime-skippable tests>>
+* Nice-to-have features:
+** <<parallel-execution,Parallel execution>>
+** <<mock-support,Mock support>>
+** <<signal-error-handling,Signal & error-handling>>
+* Tie-breaker stats
+** <<project-kloc,Project KLOC>>
+** <<adoption,Adoption>>
+
+[[license]]
+=== License
+
+We must be able to legally use the framework in connection with Git. As Git is
+licensed only under GPLv2, we must eliminate any LGPLv3, GPLv3, or Apache 2.0
+projects.
+
+[[vendorable-or-ubiquitous]]
+=== Vendorable or ubiquitous
+
+We want to avoid forcing Git developers to install new tools just to run unit
+tests. Any prospective frameworks and harnesses must either be vendorable
+(meaning, we can copy their source directly into Git's repository), or so
+ubiquitous that it is reasonable to expect that most developers will have the
+tools installed already.
+
+[[maintainable-extensible]]
+=== Maintainable / extensible
+
+It is unlikely that any pre-existing project perfectly fits our needs, so any
+project we select will need to be actively maintained and open to accepting
+changes. Alternatively, assuming we are vendoring the source into our repo, it
+must be simple enough that Git developers can feel comfortable making changes as
+needed to our version.
+
+In the comparison table below, "True" means that the framework seems to have
+active developers, that it is simple enough that Git developers can make changes
+to it, and that the project seems open to accepting external contributions (or
+that it is vendorable). "Partial" means that at least one of the above
+conditions holds.
+
+[[major-platform-support]]
+=== Major platform support
+
+At a bare minimum, unit-testing must work on Linux, MacOS, and Windows.
+
+In the comparison table below, "True" means that it works on all three major
+platforms with no issues. "Partial" means that there may be annoyances on one or
+more platforms, but it is still usable in principle.
+
+[[tap-support]]
+=== TAP support
+
+The https://testanything.org/[Test Anything Protocol] is a text-based interface
+that allows tests to communicate with a test harness. It is already used by
+Git's integration test suite. Supporting TAP output is a mandatory feature for
+any prospective test framework.
+
+In the comparison table below, "True" means this is natively supported.
+"Partial" means TAP output must be generated by post-processing the native
+output.
+
+Frameworks that do not have at least Partial support will not be evaluated
+further.
+
+[[diagnostic-output]]
+=== Diagnostic output
+
+When a test case fails, the framework must generate enough diagnostic output to
+help developers find the appropriate test case in source code in order to debug
+the failure.
+
+[[runtime-skippable-tests]]
+=== Runtime-skippable tests
+
+Test authors may wish to skip certain test cases based on runtime circumstances,
+so the framework should support this.
+
+[[parallel-execution]]
+=== Parallel execution
+
+Ideally, we will build up a significant collection of unit test cases, most
+likely split across multiple executables. It will be necessary to run these
+tests in parallel to enable fast develop-test-debug cycles.
+
+In the comparison table below, "True" means that individual test cases within a
+single test executable can be run in parallel. We assume that executable-level
+parallelism can be handled by the test harness.
+
+[[mock-support]]
+=== Mock support
+
+Unit test authors may wish to test code that interacts with objects that may be
+inconvenient to handle in a test (e.g. interacting with a network service).
+Mocking allows test authors to provide a fake implementation of these objects
+for more convenient tests.
+
+[[signal-error-handling]]
+=== Signal & error handling
+
+The test framework should fail gracefully when test cases are themselves buggy
+or when they are interrupted by signals during runtime.
+
+[[project-kloc]]
+=== Project KLOC
+
+The size of the project, in thousands of lines of code as measured by
+https://dwheeler.com/sloccount/[sloccount] (rounded up to the next multiple of
+1,000). As a tie-breaker, we probably prefer a project with fewer LOC.
+
+[[adoption]]
+=== Adoption
+
+As a tie-breaker, we prefer a more widely-used project. We use the number of
+GitHub / GitLab stars to estimate this.
+
+
+=== Comparison
+
+:true: [lime-background]#True#
+:false: [red-background]#False#
+:partial: [yellow-background]#Partial#
+
+:gpl: [lime-background]#GPL v2#
+:isc: [lime-background]#ISC#
+:mit: [lime-background]#MIT#
+:expat: [lime-background]#Expat#
+:lgpl: [lime-background]#LGPL v2.1#
+
+:custom-impl: https://lore.kernel.org/git/c902a166-98ce-afba-93f2-ea6027557176@gmail.com/[Custom Git impl.]
+:greatest: https://github.com/silentbicycle/greatest[Greatest]
+:criterion: https://github.com/Snaipe/Criterion[Criterion]
+:c-tap: https://github.com/rra/c-tap-harness/[C TAP]
+:check: https://libcheck.github.io/check/[Check]
+
+[format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"]
+|=====
+Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiquitous>>","<<maintainable-extensible,Maintainable / extensible>>","<<major-platform-support,Major platform support>>","<<tap-support,TAP support>>","<<diagnostic-output,Diagnostic output>>","<<runtime--skippable-tests,Runtime- skippable tests>>","<<parallel-execution,Parallel execution>>","<<mock-support,Mock support>>","<<signal-error-handling,Signal & error handling>>","<<project-kloc,Project KLOC>>","<<adoption,Adoption>>"
+{custom-impl},{gpl},{true},{true},{true},{true},{true},{true},{false},{false},{false},1,0
+{greatest},{isc},{true},{partial},{true},{partial},{true},{true},{false},{false},{false},3,1400
+{criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800
+{c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33
+{check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973
+|=====
+
+=== Additional framework candidates
+
+Several suggested frameworks have been eliminated from consideration:
+
+* Incompatible licenses:
+** https://github.com/zorgnax/libtap[libtap] (LGPL v3)
+** https://cmocka.org/[cmocka] (Apache 2.0)
+* Missing source: https://www.kindahl.net/mytap/doc/index.html[MyTap]
+* No TAP support:
+** https://nemequ.github.io/munit/[µnit]
+** https://github.com/google/cmockery[cmockery]
+** https://github.com/lpabon/cmockery2[cmockery2]
+** https://github.com/ThrowTheSwitch/Unity[Unity]
+** https://github.com/siu/minunit[minunit]
+** https://cunit.sourceforge.net/[CUnit]
+
+
+== Milestones
+
+* Add useful tests of library-like code
+* Integrate with
+  https://lore.kernel.org/git/20230502211454.1673000-1-calvinwan@google.com/[stdlib
+  work]
+* Run alongside regular `make test` target
index 86d0008f94d878278c1318fa6b96dcc36356344a..bf17012241536ceb1c407fea04b7b1a84441923f 100644 (file)
@@ -33,7 +33,9 @@ config file would appear like this:
 ------------
 
 The `<pushurl>` is used for pushes only. It is optional and defaults
-to `<URL>`.
+to `<URL>`. Pushing to a remote affects all defined pushurls or all
+defined urls if no pushurls are defined. Fetch, however, will only
+fetch from the first defined url if multiple urls are defined.
 
 Named file in `$GIT_DIR/remotes`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -46,7 +48,7 @@ provide a refspec on the command line.  This file should have the
 following format:
 
 ------------
-       URL: one of the above URL format
+       URL: one of the above URL formats
        Push: <refspec>
        Pull: <refspec>
 
index 1c229d758152b6377e23125967e2041a5fd5be5a..4e79c1589ece05ff21a144e5b48829a5dc39b50f 100644 (file)
@@ -6,9 +6,9 @@ address of the remote server, and the path to the repository.
 Depending on the transport protocol, some of this information may be
 absent.
 
-Git supports ssh, git, http, and https protocols (in addition, ftp,
+Git supports ssh, git, http, and https protocols (in addition, ftp
 and ftps can be used for fetching, but this is inefficient and
-deprecated; do not use it).
+deprecated; do not use them).
 
 The native transport (i.e. git:// URL) does no authentication and
 should be used with caution on unsecured networks.
index dc9c6a663a97e644d865e7d01957bfa2ac77bb5d..5d32ff23844108f5a388ecd35f7a38d5286310eb 100644 (file)
@@ -1122,7 +1122,7 @@ choosing "Stage Hunk For Commit").
 === Creating good commit messages
 
 Though not required, it's a good idea to begin the commit message
-with a single short (less than 50 character) line summarizing the
+with a single short (no more than 50 characters) line summarizing the
 change, followed by a blank line and then a more thorough
 description.  The text up to the first blank line in a commit
 message is treated as the commit title, and that title is used
@@ -1343,6 +1343,33 @@ $ git diff -3 file.txt           # diff against stage 3
 $ git diff --theirs file.txt   # same as the above.
 -------------------------------------------------
 
+When using the 'ort' merge strategy (the default), before updating the working
+tree with the result of the merge, Git writes a ref named AUTO_MERGE
+reflecting the state of the tree it is about to write. Conflicted paths with
+textual conflicts that could not be automatically merged are written to this
+tree with conflict markers, just as in the working tree. AUTO_MERGE can thus be
+used with linkgit:git-diff[1] to show the changes you've made so far to resolve
+conflicts. Using the same example as above, after resolving the conflict we
+get:
+
+-------------------------------------------------
+$ git diff AUTO_MERGE
+diff --git a/file.txt b/file.txt
+index cd10406..8bf5ae7 100644
+--- a/file.txt
++++ b/file.txt
+@@ -1,5 +1 @@
+-<<<<<<< HEAD:file.txt
+-Hello world
+-=======
+-Goodbye
+->>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
++Goodbye world
+-------------------------------------------------
+
+Notice that the diff shows we deleted the conflict markers and both versions of
+the content line, and wrote "Goodbye world" instead.
+
 The linkgit:git-log[1] and linkgit:gitk[1] commands also provide special help
 for merges:
 
@@ -4102,13 +4129,11 @@ Note that terminology has changed since that revision.  For example, the
 README in that revision uses the word "changeset" to describe what we
 now call a <<def_commit_object,commit>>.
 
-Also, we do not call it "cache" any more, but rather "index"; however, the
-file is still called `cache.h`.  Remark: Not much reason to change it now,
-especially since there is no good single name for it anyway, because it is
-basically _the_ header file which is included by _all_ of Git's C sources.
+Also, we do not call it "cache" any more, but rather "index"; however,
+the file is still called `read-cache.h`.
 
 If you grasp the ideas in that initial commit, you should check out a
-more recent version and skim `cache.h`, `object.h` and `commit.h`.
+more recent version and skim `read-cache-ll.h`, `object.h` and `commit.h`.
 
 In the early days, Git (in the tradition of UNIX) was a bunch of programs
 which were extremely simple, and which you used in scripts, piping the
@@ -4119,11 +4144,11 @@ many of these parts have become builtins, and some of the core has been
 and to avoid code duplication.
 
 By now, you know what the index is (and find the corresponding data
-structures in `cache.h`), and that there are just a couple of object types
-(blobs, trees, commits and tags) which inherit their common structure from
-`struct object`, which is their first member (and thus, you can cast e.g.
-`(struct object *)commit` to achieve the _same_ as `&commit->object`, i.e.
-get at the object name and flags).
+structures in `read-cache-ll.h`), and that there are just a couple of
+object types (blobs, trees, commits and tags) which inherit their
+common structure from `struct object`, which is their first member
+(and thus, you can cast e.g.  `(struct object *)commit` to achieve the
+_same_ as `&commit->object`, i.e.  get at the object name and flags).
 
 Now is a good point to take a break to let this information sink in.
 
index d78ceb857a0873b19c3c3af02c243ea11e107657..0901e1bb235c0419a628819c4f6ec8158d121899 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.38.5
+DEF_VER=v2.43.GIT
 
 LF='
 '
@@ -11,7 +11,7 @@ LF='
 if test -f version
 then
        VN=$(cat version) || VN="$DEF_VER"
-elif test -d ${GIT_DIR:-.git} -o -f .git &&
+elif { test -d "${GIT_DIR:-.git}" || test -f .git; } &&
        VN=$(git describe --match "v[0-9]*" HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
diff --git a/INSTALL b/INSTALL
index 43b39d99c43ebccb111fad97b13233d2df49cade..c6fb240c91eb9044f1baea43ae29c2991447bbc6 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -119,12 +119,12 @@ Issues of note:
        - A POSIX-compliant shell is required to run some scripts needed
          for everyday use (e.g. "bisect", "request-pull").
 
-       - "Perl" version 5.8 or later is needed to use some of the
-         features (e.g. preparing a partial commit using "git add -i/-p",
+       - "Perl" version 5.8.1 or later is needed to use some of the
+         features (e.g. sending patches using "git send-email",
          interacting with svn repositories with "git svn").  If you can
          live without these, use NO_PERL.  Note that recent releases of
          Redhat/Fedora are reported to ship Perl binary package with some
-         core modules stripped away (see http://lwn.net/Articles/477234/),
+         core modules stripped away (see https://lwn.net/Articles/477234/),
          so you might need to install additional packages other than Perl
          itself, e.g. Digest::MD5, File::Spec, File::Temp, Net::Domain,
          Net::SMTP, and Time::HiRes.
@@ -133,10 +133,6 @@ Issues of note:
          you are using libcurl older than 7.34.0.  Otherwise you can use
          NO_OPENSSL without losing git-imap-send.
 
-         By default, git uses OpenSSL for SHA1 but it will use its own
-         library (inspired by Mozilla's) with either NO_OPENSSL or
-         BLK_SHA1.
-
        - "libcurl" library is used for fetching and pushing
          repositories over http:// or https://, as well as by
          git-imap-send if the curl version is >= 7.34.0. If you do
index 865b2c7ae6f98118b7d036d3757c08aca4d338dd..15990ff3122eb61676e4bc39477804e77d04367b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,8 +4,20 @@ all::
 # Import tree-wide shared Makefile behavior and libraries
 include shared.mak
 
+# == Makefile defines ==
+#
+# These defines change the behavior of the Makefile itself, but have
+# no impact on what it builds:
+#
 # Define V=1 to have a more verbose compile.
 #
+# == Portability and optional library defines ==
+#
+# These defines indicate what Git can expect from the OS, what
+# libraries are available etc. Much of this is auto-detected in
+# config.mak.uname, or in configure.ac when using the optional "make
+# configure && ./configure" (see INSTALL).
+#
 # Define SHELL_PATH to a POSIX shell if your /bin/sh is broken.
 #
 # Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
@@ -30,68 +42,8 @@ include shared.mak
 #
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 #
-# Define USE_LIBPCRE if you have and want to use libpcre. Various
-# commands such as log and grep offer runtime options to use
-# Perl-compatible regular expressions instead of standard or extended
-# POSIX regular expressions.
-#
-# Only libpcre version 2 is supported. USE_LIBPCRE2 is a synonym for
-# USE_LIBPCRE, support for the old USE_LIBPCRE1 has been removed.
-#
-# Define LIBPCREDIR=/foo/bar if your PCRE header and library files are
-# in /foo/bar/include and /foo/bar/lib directories.
-#
 # Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header.
 #
-# Define NO_CURL if you do not have libcurl installed.  git-http-fetch and
-# git-http-push are not built, and you cannot use http:// and https://
-# transports (neither smart nor dumb).
-#
-# Define CURLDIR=/foo/bar if your curl header and library files are in
-# /foo/bar/include and /foo/bar/lib directories.
-#
-# Define CURL_CONFIG to curl's configuration program that prints information
-# about the library (e.g., its version number).  The default is 'curl-config'.
-#
-# Define CURL_LDFLAGS to specify flags that you need to link when using libcurl,
-# if you do not want to rely on the libraries provided by CURL_CONFIG.  The
-# default value is a result of `curl-config --libs`.  An example value for
-# CURL_LDFLAGS is as follows:
-#
-#     CURL_LDFLAGS=-lcurl
-#
-# Define NO_EXPAT if you do not have expat installed.  git-http-push is
-# not built, and you cannot push using http:// and https:// transports (dumb).
-#
-# Define EXPATDIR=/foo/bar if your expat header and library files are in
-# /foo/bar/include and /foo/bar/lib directories.
-#
-# Define EXPAT_NEEDS_XMLPARSE_H if you have an old version of expat (e.g.,
-# 1.1 or 1.2) that provides xmlparse.h instead of expat.h.
-#
-# Define NO_GETTEXT if you don't want Git output to be translated.
-# A translated Git requires GNU libintl or another gettext implementation,
-# plus libintl-perl at runtime.
-#
-# Define USE_GETTEXT_SCHEME and set it to 'fallthrough', if you don't trust
-# the installed gettext translation of the shell scripts output.
-#
-# Define HAVE_LIBCHARSET_H if you haven't set NO_GETTEXT and you can't
-# trust the langinfo.h's nl_langinfo(CODESET) function to return the
-# current character set. GNU and Solaris have a nl_langinfo(CODESET),
-# FreeBSD can use either, but MinGW and some others need to use
-# libcharset.h's locale_charset() instead.
-#
-# Define CHARSET_LIB to the library you need to link with in order to
-# use locale_charset() function.  On some platforms this needs to set to
-# -lcharset, on others to -liconv .
-#
-# Define LIBC_CONTAINS_LIBINTL if your gettext implementation doesn't
-# need -lintl when linking.
-#
-# Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt
-# doesn't support GNU extensions like --check and --statistics
-#
 # Define HAVE_PATHS_H if you have paths.h and want to use the default PATH
 # it specifies.
 #
@@ -152,39 +104,6 @@ include shared.mak
 # and do not want to use Apple's CommonCrypto library.  This allows you
 # to provide your own OpenSSL library, for example from MacPorts.
 #
-# Define BLK_SHA1 environment variable to make use of the bundled
-# optimized C SHA1 routine.
-#
-# Define DC_SHA1 to unconditionally enable the collision-detecting sha1
-# algorithm. This is slower, but may detect attempted collision attacks.
-# Takes priority over other *_SHA1 knobs.
-#
-# Define DC_SHA1_EXTERNAL in addition to DC_SHA1 if you want to build / link
-# git with the external SHA1 collision-detect library.
-# Without this option, i.e. the default behavior is to build git with its
-# own built-in code (or submodule).
-#
-# Define DC_SHA1_SUBMODULE in addition to DC_SHA1 to use the
-# sha1collisiondetection shipped as a submodule instead of the
-# non-submodule copy in sha1dc/. This is an experimental option used
-# by the git project to migrate to using sha1collisiondetection as a
-# submodule.
-#
-# Define OPENSSL_SHA1 environment variable when running make to link
-# with the SHA1 routine from openssl library.
-#
-# Define SHA1_MAX_BLOCK_SIZE to limit the amount of data that will be hashed
-# in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO
-# wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined.
-#
-# Define BLK_SHA256 to use the built-in SHA-256 routines.
-#
-# Define NETTLE_SHA256 to use the SHA-256 routines in libnettle.
-#
-# Define GCRYPT_SHA256 to use the SHA-256 routines in libgcrypt.
-#
-# Define OPENSSL_SHA256 to use the SHA-256 routines in OpenSSL.
-#
 # Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
 #
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
@@ -267,7 +186,7 @@ include shared.mak
 # Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
 #
 # Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
-# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
+# as the compiler can crash (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 #
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On
@@ -288,10 +207,6 @@ include shared.mak
 # Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
 # field that counts the on-disk footprint in 512-byte blocks.
 #
-# Define GNU_ROFF if your target system uses GNU groff.  This forces
-# apostrophes to be ASCII so that cut&pasting examples to the shell
-# will work.
-#
 # Define USE_ASCIIDOCTOR to use Asciidoctor instead of AsciiDoc to build the
 # documentation.
 #
@@ -370,6 +285,10 @@ include shared.mak
 # Define NO_REGEX if your C library lacks regex support with REG_STARTEND
 # feature.
 #
+# Define USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS if your C library provides
+# the flag REG_ENHANCED and you'd like to use it to enable enhanced basic
+# regular expressions.
+#
 # Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
 # user.
 #
@@ -490,6 +409,149 @@ include shared.mak
 # to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c`
 # that implements the `fsm_os_settings__*()` routines.
 #
+# === Optional library: libintl ===
+#
+# Define NO_GETTEXT if you don't want Git output to be translated.
+# A translated Git requires GNU libintl or another gettext implementation,
+# plus libintl-perl at runtime.
+#
+# Define USE_GETTEXT_SCHEME and set it to 'fallthrough', if you don't trust
+# the installed gettext translation of the shell scripts output.
+#
+# Define HAVE_LIBCHARSET_H if you haven't set NO_GETTEXT and you can't
+# trust the langinfo.h's nl_langinfo(CODESET) function to return the
+# current character set. GNU and Solaris have a nl_langinfo(CODESET),
+# FreeBSD can use either, but MinGW and some others need to use
+# libcharset.h's locale_charset() instead.
+#
+# Define CHARSET_LIB to the library you need to link with in order to
+# use locale_charset() function.  On some platforms this needs to set to
+# -lcharset, on others to -liconv .
+#
+# Define LIBC_CONTAINS_LIBINTL if your gettext implementation doesn't
+# need -lintl when linking.
+#
+# Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt
+# doesn't support GNU extensions like --check and --statistics
+#
+# === Optional library: libexpat ===
+#
+# Define NO_EXPAT if you do not have expat installed.  git-http-push is
+# not built, and you cannot push using http:// and https:// transports (dumb).
+#
+# Define EXPATDIR=/foo/bar if your expat header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define EXPAT_NEEDS_XMLPARSE_H if you have an old version of expat (e.g.,
+# 1.1 or 1.2) that provides xmlparse.h instead of expat.h.
+
+# === Optional library: libcurl ===
+#
+# Define NO_CURL if you do not have libcurl installed.  git-http-fetch and
+# git-http-push are not built, and you cannot use http:// and https://
+# transports (neither smart nor dumb).
+#
+# Define CURLDIR=/foo/bar if your curl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define CURL_CONFIG to curl's configuration program that prints information
+# about the library (e.g., its version number).  The default is 'curl-config'.
+#
+# Define CURL_LDFLAGS to specify flags that you need to link when using libcurl,
+# if you do not want to rely on the libraries provided by CURL_CONFIG.  The
+# default value is a result of `curl-config --libs`.  An example value for
+# CURL_LDFLAGS is as follows:
+#
+#     CURL_LDFLAGS=-lcurl
+#
+# === Optional library: libpcre2 ===
+#
+# Define USE_LIBPCRE if you have and want to use libpcre. Various
+# commands such as log and grep offer runtime options to use
+# Perl-compatible regular expressions instead of standard or extended
+# POSIX regular expressions.
+#
+# Only libpcre version 2 is supported. USE_LIBPCRE2 is a synonym for
+# USE_LIBPCRE, support for the old USE_LIBPCRE1 has been removed.
+#
+# Define LIBPCREDIR=/foo/bar if your PCRE header and library files are
+# in /foo/bar/include and /foo/bar/lib directories.
+#
+# == SHA-1 and SHA-256 defines ==
+#
+# === SHA-1 backend ===
+#
+# ==== Security ====
+#
+# Due to the SHAttered (https://shattered.io) attack vector on SHA-1
+# it's strongly recommended to use the sha1collisiondetection
+# counter-cryptanalysis library for SHA-1 hashing.
+#
+# If you know that you can trust the repository contents, or where
+# potential SHA-1 attacks are otherwise mitigated the other backends
+# listed in "SHA-1 implementations" are faster than
+# sha1collisiondetection.
+#
+# ==== Default SHA-1 backend ====
+#
+# If no *_SHA1 backend is picked, the first supported one listed in
+# "SHA-1 implementations" will be picked.
+#
+# ==== Options common to all SHA-1 implementations ====
+#
+# Define SHA1_MAX_BLOCK_SIZE to limit the amount of data that will be hashed
+# in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO
+# wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined.
+#
+# ==== SHA-1 implementations ====
+#
+# Define OPENSSL_SHA1 to link to the SHA-1 routines from the OpenSSL
+# library.
+#
+# Define BLK_SHA1 to make use of optimized C SHA-1 routines bundled
+# with git (in the block-sha1/ directory).
+#
+# Define APPLE_COMMON_CRYPTO_SHA1 to use Apple's CommonCrypto for
+# SHA-1.
+#
+# If don't enable any of the *_SHA1 settings in this section, Git will
+# default to its built-in sha1collisiondetection library, which is a
+# collision-detecting sha1 This is slower, but may detect attempted
+# collision attacks.
+#
+# ==== Options for the sha1collisiondetection library ====
+#
+# Define DC_SHA1_EXTERNAL if you want to build / link
+# git with the external SHA1 collision-detect library.
+# Without this option, i.e. the default behavior is to build git with its
+# own built-in code (or submodule).
+#
+# Define DC_SHA1_SUBMODULE to use the
+# sha1collisiondetection shipped as a submodule instead of the
+# non-submodule copy in sha1dc/. This is an experimental option used
+# by the git project to migrate to using sha1collisiondetection as a
+# submodule.
+#
+# === SHA-256 backend ===
+#
+# ==== Security ====
+#
+# Unlike SHA-1 the SHA-256 algorithm does not suffer from any known
+# vulnerabilities, so any implementation will do.
+#
+# ==== SHA-256 implementations ====
+#
+# Define OPENSSL_SHA256 to use the SHA-256 routines in OpenSSL.
+#
+# Define NETTLE_SHA256 to use the SHA-256 routines in libnettle.
+#
+# Define GCRYPT_SHA256 to use the SHA-256 routines in libgcrypt.
+#
+# If don't enable any of the *_SHA256 settings in this section, Git
+# will default to its built-in sha256 implementation.
+#
+# == DEVELOPER defines ==
+#
 # Define DEVELOPER to enable more compiler warnings. Compiler version
 # and family are auto detected, but could be overridden by defining
 # COMPILER_FEATURES (see config.mak.dev). You can still set
@@ -620,6 +682,9 @@ TEST_BUILTINS_OBJS =
 TEST_OBJS =
 TEST_PROGRAMS_NEED_X =
 THIRD_PARTY_SOURCES =
+UNIT_TEST_PROGRAMS =
+UNIT_TEST_DIR = t/unit-tests
+UNIT_TEST_BIN = $(UNIT_TEST_DIR)/bin
 
 # Having this variable in your environment would break pipelines because
 # you cause "cd" to echo its destination to stdout.  It can also take
@@ -627,7 +692,6 @@ THIRD_PARTY_SOURCES =
 # interactive shell sessions without exporting it.
 unexport CDPATH
 
-SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
 SCRIPT_SH += git-merge-octopus.sh
@@ -643,7 +707,6 @@ SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-sh-i18n
 SCRIPT_LIB += git-sh-setup
 
-SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-archimport.perl
 SCRIPT_PERL += git-cvsexportcommit.perl
 SCRIPT_PERL += git-cvsimport.perl
@@ -689,9 +752,10 @@ SCRIPTS = $(SCRIPT_SH_GEN) \
 
 ETAGS_TARGET = TAGS
 
-FUZZ_OBJS += fuzz-commit-graph.o
-FUZZ_OBJS += fuzz-pack-headers.o
-FUZZ_OBJS += fuzz-pack-idx.o
+FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-date.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
 .PHONY: fuzz-objs
 fuzz-objs: $(FUZZ_OBJS)
 
@@ -722,6 +786,8 @@ PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
 TEST_BUILTINS_OBJS += test-advise.o
 TEST_BUILTINS_OBJS += test-bitmap.o
 TEST_BUILTINS_OBJS += test-bloom.o
+TEST_BUILTINS_OBJS += test-bundle-uri.o
+TEST_BUILTINS_OBJS += test-cache-tree.o
 TEST_BUILTINS_OBJS += test-chmtime.o
 TEST_BUILTINS_OBJS += test-config.o
 TEST_BUILTINS_OBJS += test-crontab.o
@@ -735,8 +801,9 @@ TEST_BUILTINS_OBJS += test-dump-cache-tree.o
 TEST_BUILTINS_OBJS += test-dump-fsmonitor.o
 TEST_BUILTINS_OBJS += test-dump-split-index.o
 TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
+TEST_BUILTINS_OBJS += test-env-helper.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
-TEST_BUILTINS_OBJS += test-fast-rebase.o
+TEST_BUILTINS_OBJS += test-find-pack.o
 TEST_BUILTINS_OBJS += test-fsmonitor-client.o
 TEST_BUILTINS_OBJS += test-genrandom.o
 TEST_BUILTINS_OBJS += test-genzeros.o
@@ -745,7 +812,6 @@ TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
 TEST_BUILTINS_OBJS += test-hexdump.o
-TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
 TEST_BUILTINS_OBJS += test-match-trees.o
@@ -789,6 +855,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
 TEST_BUILTINS_OBJS += test-submodule.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-trace2.o
+TEST_BUILTINS_OBJS += test-truncate.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
 TEST_BUILTINS_OBJS += test-userdiff.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
@@ -969,6 +1036,7 @@ LIB_OBJS += fsmonitor.o
 LIB_OBJS += fsmonitor-ipc.o
 LIB_OBJS += fsmonitor-settings.o
 LIB_OBJS += gettext.o
+LIB_OBJS += git-zlib.o
 LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
@@ -976,6 +1044,7 @@ LIB_OBJS += hash-lookup.o
 LIB_OBJS += hashmap.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
+LIB_OBJS += hex-ll.o
 LIB_OBJS += hook.o
 LIB_OBJS += ident.o
 LIB_OBJS += json-writer.o
@@ -987,7 +1056,6 @@ LIB_OBJS += linear-assignment.o
 LIB_OBJS += list-objects-filter-options.o
 LIB_OBJS += list-objects-filter.o
 LIB_OBJS += list-objects.o
-LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
 LIB_OBJS += ls-refs.o
@@ -996,6 +1064,7 @@ LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += mem-pool.o
 LIB_OBJS += merge-blobs.o
+LIB_OBJS += merge-ll.o
 LIB_OBJS += merge-ort.o
 LIB_OBJS += merge-ort-wrappers.o
 LIB_OBJS += merge-recursive.o
@@ -1026,6 +1095,7 @@ LIB_OBJS += pack-write.o
 LIB_OBJS += packfile.o
 LIB_OBJS += pager.o
 LIB_OBJS += parallel-checkout.o
+LIB_OBJS += parse.o
 LIB_OBJS += parse-options-cb.o
 LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
@@ -1078,6 +1148,7 @@ LIB_OBJS += sigchain.o
 LIB_OBJS += sparse-index.o
 LIB_OBJS += split-index.o
 LIB_OBJS += stable-qsort.o
+LIB_OBJS += statinfo.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
@@ -1095,6 +1166,7 @@ LIB_OBJS += trace.o
 LIB_OBJS += trace2.o
 LIB_OBJS += trace2/tr2_cfg.o
 LIB_OBJS += trace2/tr2_cmd_name.o
+LIB_OBJS += trace2/tr2_ctr.o
 LIB_OBJS += trace2/tr2_dst.o
 LIB_OBJS += trace2/tr2_sid.o
 LIB_OBJS += trace2/tr2_sysenv.o
@@ -1103,6 +1175,7 @@ LIB_OBJS += trace2/tr2_tgt_event.o
 LIB_OBJS += trace2/tr2_tgt_normal.o
 LIB_OBJS += trace2/tr2_tgt_perf.o
 LIB_OBJS += trace2/tr2_tls.o
+LIB_OBJS += trace2/tr2_tmr.o
 LIB_OBJS += trailer.o
 LIB_OBJS += transport-helper.o
 LIB_OBJS += transport.o
@@ -1127,14 +1200,13 @@ LIB_OBJS += write-or-die.o
 LIB_OBJS += ws.o
 LIB_OBJS += wt-status.o
 LIB_OBJS += xdiff-interface.o
-LIB_OBJS += zlib.o
 
 BUILTIN_OBJS += builtin/add.o
 BUILTIN_OBJS += builtin/am.o
 BUILTIN_OBJS += builtin/annotate.o
 BUILTIN_OBJS += builtin/apply.o
 BUILTIN_OBJS += builtin/archive.o
-BUILTIN_OBJS += builtin/bisect--helper.o
+BUILTIN_OBJS += builtin/bisect.o
 BUILTIN_OBJS += builtin/blame.o
 BUILTIN_OBJS += builtin/branch.o
 BUILTIN_OBJS += builtin/bugreport.o
@@ -1166,7 +1238,6 @@ BUILTIN_OBJS += builtin/diff-index.o
 BUILTIN_OBJS += builtin/diff-tree.o
 BUILTIN_OBJS += builtin/diff.o
 BUILTIN_OBJS += builtin/difftool.o
-BUILTIN_OBJS += builtin/env--helper.o
 BUILTIN_OBJS += builtin/fast-export.o
 BUILTIN_OBJS += builtin/fast-import.o
 BUILTIN_OBJS += builtin/fetch-pack.o
@@ -1222,6 +1293,7 @@ BUILTIN_OBJS += builtin/remote-fd.o
 BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/repack.o
 BUILTIN_OBJS += builtin/replace.o
+BUILTIN_OBJS += builtin/replay.o
 BUILTIN_OBJS += builtin/rerere.o
 BUILTIN_OBJS += builtin/reset.o
 BUILTIN_OBJS += builtin/rev-list.o
@@ -1267,6 +1339,13 @@ THIRD_PARTY_SOURCES += compat/regex/%
 THIRD_PARTY_SOURCES += sha1collisiondetection/%
 THIRD_PARTY_SOURCES += sha1dc/%
 
+UNIT_TEST_PROGRAMS += t-basic
+UNIT_TEST_PROGRAMS += t-mem-pool
+UNIT_TEST_PROGRAMS += t-strbuf
+UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
+UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
+UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
+
 # xdiff and reftable libs may in turn depend on what is in libgit.a
 GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
 EXTLIBS =
@@ -1299,11 +1378,53 @@ SP_EXTRA_FLAGS = -Wno-universal-initializer
 SANITIZE_LEAK =
 SANITIZE_ADDRESS =
 
-# For the 'coccicheck' target; setting SPATCH_BATCH_SIZE higher will
-# usually result in less CPU usage at the cost of higher peak memory.
-# Setting it to 0 will feed all files in a single spatch invocation.
-SPATCH_FLAGS = --all-includes
-SPATCH_BATCH_SIZE = 1
+# For the 'coccicheck' target
+SPATCH_INCLUDE_FLAGS = --all-includes
+SPATCH_FLAGS =
+SPATCH_TEST_FLAGS =
+
+# If *.o files are present, have "coccicheck" depend on them, with
+# COMPUTE_HEADER_DEPENDENCIES this will speed up the common-case of
+# only needing to re-generate coccicheck results for the users of a
+# given API if it's changed, and not all files in the project. If
+# COMPUTE_HEADER_DEPENDENCIES=no this will be unset too.
+SPATCH_USE_O_DEPENDENCIES = YesPlease
+
+# Set SPATCH_CONCAT_COCCI to concatenate the contrib/cocci/*.cocci
+# files into a single contrib/cocci/ALL.cocci before running
+# "coccicheck".
+#
+# Pros:
+#
+# - Speeds up a one-shot run of "make coccicheck", as we won't have to
+#   parse *.[ch] files N times for the N *.cocci rules
+#
+# Cons:
+#
+# - Will make incremental development of *.cocci slower, as
+#   e.g. changing strbuf.cocci will re-run all *.cocci.
+#
+# - Makes error and performance analysis harder, as rules will be
+#   applied from a monolithic ALL.cocci, rather than
+#   e.g. strbuf.cocci. To work around this either undefine this, or
+#   generate a specific patch, e.g. this will always use strbuf.cocci,
+#   not ALL.cocci:
+#
+#      make contrib/coccinelle/strbuf.cocci.patch
+SPATCH_CONCAT_COCCI = YesPlease
+
+# Rebuild 'coccicheck' if $(SPATCH), its flags etc. change
+TRACK_SPATCH_DEFINES =
+TRACK_SPATCH_DEFINES += $(SPATCH)
+TRACK_SPATCH_DEFINES += $(SPATCH_INCLUDE_FLAGS)
+TRACK_SPATCH_DEFINES += $(SPATCH_FLAGS)
+TRACK_SPATCH_DEFINES += $(SPATCH_TEST_FLAGS)
+GIT-SPATCH-DEFINES: FORCE
+       @FLAGS='$(TRACK_SPATCH_DEFINES)'; \
+           if test x"$$FLAGS" != x"`cat GIT-SPATCH-DEFINES 2>/dev/null`" ; then \
+               echo >&2 "    * new spatch flags"; \
+               echo "$$FLAGS" >GIT-SPATCH-DEFINES; \
+            fi
 
 include config.mak.uname
 -include config.mak.autogen
@@ -1444,7 +1565,6 @@ ifeq ($(uname_S),Darwin)
                APPLE_COMMON_CRYPTO = YesPlease
                COMPAT_CFLAGS += -DAPPLE_COMMON_CRYPTO
        endif
-       NO_REGEX = YesPlease
        PTHREAD_LIBS =
 endif
 
@@ -1803,7 +1923,7 @@ ifdef NO_POSIX_GOODIES
        BASIC_CFLAGS += -DNO_POSIX_GOODIES
 endif
 
-ifdef APPLE_COMMON_CRYPTO
+ifdef APPLE_COMMON_CRYPTO_SHA1
        # Apple CommonCrypto requires chunking
        SHA1_MAX_BLOCK_SIZE = 1024L*1024L*1024L
 endif
@@ -1820,11 +1940,10 @@ ifdef BLK_SHA1
        LIB_OBJS += block-sha1/sha1.o
        BASIC_CFLAGS += -DSHA1_BLK
 else
-ifdef APPLE_COMMON_CRYPTO
+ifdef APPLE_COMMON_CRYPTO_SHA1
        COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
        BASIC_CFLAGS += -DSHA1_APPLE
 else
-       DC_SHA1 := YesPlease
        BASIC_CFLAGS += -DSHA1_DC
        LIB_OBJS += sha1dc_git.o
 ifdef DC_SHA1_EXTERNAL
@@ -1847,7 +1966,7 @@ endif
        BASIC_CFLAGS += \
                -DSHA1DC_NO_STANDARD_INCLUDES \
                -DSHA1DC_INIT_SAFE_HASH_DEFAULT=0 \
-               -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"cache.h\"" \
+               -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"git-compat-util.h\"" \
                -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="\"git-compat-util.h\""
 endif
 endif
@@ -1932,6 +2051,11 @@ endif
 ifdef NO_REGEX
        COMPAT_CFLAGS += -Icompat/regex
        COMPAT_OBJS += compat/regex/regex.o
+else
+ifdef USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+       COMPAT_CFLAGS += -DUSE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+       COMPAT_OBJS += compat/regcomp_enhanced.o
+endif
 endif
 ifdef NATIVE_CRLF
        BASIC_CFLAGS += -DNATIVE_CRLF
@@ -2041,11 +2165,13 @@ ifdef FSMONITOR_DAEMON_BACKEND
        COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND
        COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o
        COMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o
+       COMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o
 endif
 
 ifdef FSMONITOR_OS_SETTINGS
        COMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS
        COMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o
+       COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o
 endif
 
 ifeq ($(TCLTK_PATH),)
@@ -2227,7 +2353,7 @@ profile-fast: profile-clean
 
 all:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
-       $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_COMMANDS_TO_INSTALL) $(OTHER_PROGRAMS))), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
+       $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_COMMANDS_TO_INSTALL) $(OTHER_PROGRAMS))), if test ! -d '$p' && test ! '$p' -ef '$p$X'; then $(RM) '$p'; fi;)
 endif
 
 all::
@@ -2561,6 +2687,7 @@ OBJECTS += $(TEST_OBJS)
 OBJECTS += $(XDIFF_OBJS)
 OBJECTS += $(FUZZ_OBJS)
 OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
+OBJECTS += $(UNIT_TEST_OBJS)
 
 ifndef NO_CURL
        OBJECTS += http.o http-walker.o remote-curl.o
@@ -2608,7 +2735,7 @@ $(OBJECTS): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
 
 ifdef USE_COMPUTED_HEADER_DEPENDENCIES
 # Take advantage of gcc's on-the-fly dependency generation
-# See <http://gcc.gnu.org/gcc-3.0/features.html>.
+# See <https://gcc.gnu.org/gcc-3.0/features.html>.
 dep_files_present := $(wildcard $(dep_files))
 ifneq ($(dep_files_present),)
 include $(dep_files_present)
@@ -2631,8 +2758,8 @@ exec-cmd.sp exec-cmd.s exec-cmd.o: EXTRA_CPPFLAGS = \
        '-DBINDIR="$(bindir_relative_SQ)"' \
        '-DFALLBACK_RUNTIME_PREFIX="$(prefix_SQ)"'
 
-builtin/init-db.sp builtin/init-db.s builtin/init-db.o: GIT-PREFIX
-builtin/init-db.sp builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
+setup.sp setup.s setup.o: GIT-PREFIX
+setup.sp setup.s setup.o: EXTRA_CPPFLAGS = \
        -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
 
 config.sp config.s config.o: GIT-PREFIX
@@ -2667,6 +2794,13 @@ compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
 compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null
 endif
 
+headless-git.o: compat/win32/headless.c GIT-CFLAGS
+       $(QUIET_CC)$(CC) $(ALL_CFLAGS) $(COMPAT_CFLAGS) \
+               -fno-stack-protector -o $@ -c -Wall -Wwrite-strings $<
+
+headless-git$X: headless-git.o git.res GIT-LDFLAGS
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -mwindows -o $@ $< git.res
+
 git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
@@ -2982,9 +3116,9 @@ GIT-BUILD-OPTIONS: FORCE
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
        @echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
+       @echo NO_REGEX=\''$(subst ','\'',$(subst ','\'',$(NO_REGEX)))'\' >>$@+
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
        @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
-       @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+
        @echo SANITIZE_LEAK=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_LEAK)))'\' >>$@+
        @echo SANITIZE_ADDRESS=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_ADDRESS)))'\' >>$@+
        @echo X=\'$(X)\' >>$@+
@@ -3040,6 +3174,7 @@ else
        @echo RUNTIME_PREFIX=\'false\' >>$@+
 endif
        @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
+       @if test -f GIT-BUILD-DIR; then rm GIT-BUILD-DIR; fi
 
 ### Detect Python interpreter path changes
 ifndef NO_PYTHON
@@ -3055,7 +3190,7 @@ endif
 
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
-all:: $(TEST_PROGRAMS) $(test_bindir_programs)
+all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS)
 
 bin-wrappers/%: wrap-for-bin.sh
        $(call mkdir_p_parent_template)
@@ -3103,6 +3238,12 @@ $(SP_OBJ): %.sp: %.c %.o
 sparse: $(SP_OBJ)
 
 EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/%
+ifndef OPENSSL_SHA1
+       EXCEPT_HDRS += sha1/openssl.h
+endif
+ifndef OPENSSL_SHA256
+       EXCEPT_HDRS += sha256/openssl.h
+endif
 ifndef NETTLE_SHA256
        EXCEPT_HDRS += sha256/nettle.h
 endif
@@ -3138,35 +3279,113 @@ check: $(GENERATED_H)
                exit 1; \
        fi
 
+COCCI_GEN_ALL = .build/contrib/coccinelle/ALL.cocci
+COCCI_GLOB = $(wildcard contrib/coccinelle/*.cocci)
+COCCI_RULES_TRACKED = $(COCCI_GLOB:%=.build/%)
+COCCI_RULES_TRACKED_NO_PENDING = $(filter-out %.pending.cocci,$(COCCI_RULES_TRACKED))
+COCCI_RULES =
+COCCI_RULES += $(COCCI_GEN_ALL)
+COCCI_RULES += $(COCCI_RULES_TRACKED)
+COCCI_NAMES =
+COCCI_NAMES += $(COCCI_RULES:.build/contrib/coccinelle/%.cocci=%)
+
+COCCICHECK_PENDING = $(filter %.pending.cocci,$(COCCI_RULES))
+COCCICHECK = $(filter-out $(COCCICHECK_PENDING),$(COCCI_RULES))
+
+COCCICHECK_PATCHES = $(COCCICHECK:%=%.patch)
+COCCICHECK_PATCHES_PENDING = $(COCCICHECK_PENDING:%=%.patch)
+
+COCCICHECK_PATCHES_INTREE = $(COCCICHECK_PATCHES:.build/%=%)
+COCCICHECK_PATCHES_PENDING_INTREE = $(COCCICHECK_PATCHES_PENDING:.build/%=%)
+
+# It's expensive to compute the many=many rules below, only eval them
+# on $(MAKECMDGOALS) that match these $(COCCI_RULES)
+COCCI_RULES_GLOB =
+COCCI_RULES_GLOB += cocci%
+COCCI_RULES_GLOB += .build/contrib/coccinelle/%
+COCCI_RULES_GLOB += $(COCCICHECK_PATCHES)
+COCCI_RULES_GLOB += $(COCCICHEC_PATCHES_PENDING)
+COCCI_RULES_GLOB += $(COCCICHECK_PATCHES_INTREE)
+COCCI_RULES_GLOB += $(COCCICHECK_PATCHES_PENDING_INTREE)
+COCCI_GOALS = $(filter $(COCCI_RULES_GLOB),$(MAKECMDGOALS))
+
 COCCI_TEST_RES = $(wildcard contrib/coccinelle/tests/*.res)
 
-%.cocci.patch: %.cocci $(COCCI_SOURCES)
-       $(QUIET_SPATCH) \
-       if test $(SPATCH_BATCH_SIZE) = 0; then \
-               limit=; \
-       else \
-               limit='-n $(SPATCH_BATCH_SIZE)'; \
-       fi; \
-       if ! echo $(COCCI_SOURCES) | xargs $$limit \
-               $(SPATCH) $(SPATCH_FLAGS) \
-               --sp-file $< --patch . \
-               >$@+ 2>$@.log; \
+$(COCCI_RULES_TRACKED): .build/% : %
+       $(call mkdir_p_parent_template)
+       $(QUIET_CP)cp $< $@
+
+.build/contrib/coccinelle/FOUND_H_SOURCES: $(FOUND_H_SOURCES)
+       $(call mkdir_p_parent_template)
+       $(QUIET_GEN) >$@
+
+$(COCCI_GEN_ALL): $(COCCI_RULES_TRACKED_NO_PENDING)
+       $(call mkdir_p_parent_template)
+       $(QUIET_SPATCH_CAT)cat $^ >$@
+
+ifeq ($(COMPUTE_HEADER_DEPENDENCIES),no)
+SPATCH_USE_O_DEPENDENCIES =
+endif
+define cocci-rule
+
+## Rule for .build/$(1).patch/$(2); Params:
+# $(1) = e.g. ".build/contrib/coccinelle/free.cocci"
+# $(2) = e.g. "grep.c"
+# $(3) = e.g. "grep.o"
+COCCI_$(1:.build/contrib/coccinelle/%.cocci=%) += $(1).d/$(2).patch
+$(1).d/$(2).patch: GIT-SPATCH-DEFINES
+$(1).d/$(2).patch: $(if $(and $(SPATCH_USE_O_DEPENDENCIES),$(wildcard $(3))),$(3),.build/contrib/coccinelle/FOUND_H_SOURCES)
+$(1).d/$(2).patch: $(1)
+$(1).d/$(2).patch: $(1).d/%.patch : %
+       $$(call mkdir_p_parent_template)
+       $$(QUIET_SPATCH)if ! $$(SPATCH) $$(SPATCH_FLAGS) \
+               $$(SPATCH_INCLUDE_FLAGS) \
+               --sp-file $(1) --patch . $$< \
+               >$$@ 2>$$@.log; \
        then \
-               cat $@.log; \
+               echo "ERROR when applying '$(1)' to '$$<'; '$$@.log' follows:"; \
+               cat $$@.log; \
                exit 1; \
-       fi; \
-       mv $@+ $@; \
-       if test -s $@; \
+       fi
+endef
+
+define cocci-matrix
+
+$(foreach s,$(COCCI_SOURCES),$(call cocci-rule,$(c),$(s),$(s:%.c=%.o)))
+endef
+
+ifdef COCCI_GOALS
+$(eval $(foreach c,$(COCCI_RULES),$(call cocci-matrix,$(c))))
+endif
+
+define spatch-rule
+
+.build/contrib/coccinelle/$(1).cocci.patch: $$(COCCI_$(1))
+       $$(QUIET_SPATCH_CAT)cat $$^ >$$@ && \
+       if test -s $$@; \
        then \
-               echo '    ' SPATCH result: $@; \
+               echo '    ' SPATCH result: $$@; \
        fi
+contrib/coccinelle/$(1).cocci.patch: .build/contrib/coccinelle/$(1).cocci.patch
+       $$(QUIET_CP)cp $$< $$@
+
+endef
+
+ifdef COCCI_GOALS
+$(eval $(foreach n,$(COCCI_NAMES),$(call spatch-rule,$(n))))
+endif
 
 COCCI_TEST_RES_GEN = $(addprefix .build/,$(COCCI_TEST_RES))
+$(COCCI_TEST_RES_GEN): GIT-SPATCH-DEFINES
 $(COCCI_TEST_RES_GEN): .build/%.res : %.c
 $(COCCI_TEST_RES_GEN): .build/%.res : %.res
+ifdef SPATCH_CONCAT_COCCI
+$(COCCI_TEST_RES_GEN): .build/contrib/coccinelle/tests/%.res : $(COCCI_GEN_ALL)
+else
 $(COCCI_TEST_RES_GEN): .build/contrib/coccinelle/tests/%.res : contrib/coccinelle/%.cocci
+endif
        $(call mkdir_p_parent_template)
-       $(QUIET_SPATCH_T)$(SPATCH) $(SPATCH_FLAGS) \
+       $(QUIET_SPATCH_TEST)$(SPATCH) $(SPATCH_TEST_FLAGS) \
                --very-quiet --no-show-diff \
                --sp-file $< -o $@ \
                $(@:.build/%.res=%.c) && \
@@ -3177,11 +3396,15 @@ $(COCCI_TEST_RES_GEN): .build/contrib/coccinelle/tests/%.res : contrib/coccinell
 coccicheck-test: $(COCCI_TEST_RES_GEN)
 
 coccicheck: coccicheck-test
-coccicheck: $(addsuffix .patch,$(filter-out %.pending.cocci,$(wildcard contrib/coccinelle/*.cocci)))
+ifdef SPATCH_CONCAT_COCCI
+coccicheck: contrib/coccinelle/ALL.cocci.patch
+else
+coccicheck: $(COCCICHECK_PATCHES_INTREE)
+endif
 
 # See contrib/coccinelle/README
 coccicheck-pending: coccicheck-test
-coccicheck-pending: $(addsuffix .patch,$(wildcard contrib/coccinelle/*.pending.cocci))
+coccicheck-pending: $(COCCICHECK_PATCHES_PENDING_INTREE)
 
 .PHONY: coccicheck coccicheck-pending
 
@@ -3393,12 +3616,12 @@ rpm::
 .PHONY: rpm
 
 ifneq ($(INCLUDE_DLLS_IN_ARTIFACTS),)
-OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll)
+OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll t/unit-tests/bin/*.dll)
 endif
 
 artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
                GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
-               $(MOFILES)
+               $(UNIT_TEST_PROGS) $(MOFILES)
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
                SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
        test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3448,17 +3671,19 @@ profile-clean:
        $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
 
 cocciclean:
+       $(RM) GIT-SPATCH-DEFINES
        $(RM) -r .build/contrib/coccinelle
-       $(RM) contrib/coccinelle/*.cocci.patch*
+       $(RM) contrib/coccinelle/*.cocci.patch
 
 clean: profile-clean coverage-clean cocciclean
        $(RM) -r .build
        $(RM) po/git.pot po/git-core.pot
        $(RM) git.res
        $(RM) $(OBJECTS)
+       $(RM) headless-git.o
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
-       $(RM) $(TEST_PROGRAMS)
+       $(RM) $(TEST_PROGRAMS) $(UNIT_TEST_PROGS)
        $(RM) $(FUZZ_PROGRAMS)
        $(RM) $(SP_OBJ)
        $(RM) $(HCC)
@@ -3484,6 +3709,7 @@ endif
        $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS
 ifdef MSVC
        $(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS))
+       $(RM) headless-git.o.pdb
        $(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS))
        $(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS))
        $(RM) $(patsubst %.exe,%.ipdb,$(OTHER_PROGRAMS))
@@ -3636,3 +3862,15 @@ $(FUZZ_PROGRAMS): all
                $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
 
 fuzz-all: $(FUZZ_PROGRAMS)
+
+$(UNIT_TEST_BIN):
+       @mkdir -p $(UNIT_TEST_BIN)
+
+$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS $(UNIT_TEST_BIN)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
+               $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
+
+.PHONY: build-unit-tests unit-tests
+build-unit-tests: $(UNIT_TEST_PROGS)
+unit-tests: $(UNIT_TEST_PROGS)
+       $(MAKE) -C t/ unit-tests
index 7ce4f05bae8120d9fa258e854a8669f6ea9cb7b1..2c3de2f9c803798e9eb3bc31184b3d86ba69d60b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ string translations (localization l10) should see [po/README.md][]
 To subscribe to the list, send an email with just "subscribe git" in
 the body to majordomo@vger.kernel.org (not the Git list). The mailing
 list archives are available at <https://lore.kernel.org/git/>,
-<http://marc.info/?l=git> and other archival sites.
+<https://marc.info/?l=git> and other archival sites.
 
 Issues which are security relevant should be disclosed privately to
 the Git Security mailing list <git-security@googlegroups.com>.
index a960781cc9188a435355580ba5b67e297dcb3772..a55478f9ad4b2cd26617696780296a7b4afc204a 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.38.5.txt
\ No newline at end of file
+Documentation/RelNotes/2.44.0.txt
\ No newline at end of file
index 39e06b58486e3e94e640929c27460e786533a2f8..1202cde23dbc9b27e3bee5d3198a9cec3236a288 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "strbuf.h"
 
 /*
  * Do not use this for inspecting *tracked* content.  When path is a
@@ -280,3 +282,46 @@ char *prefix_filename(const char *pfx, const char *arg)
 #endif
        return strbuf_detach(&path, NULL);
 }
+
+char *prefix_filename_except_for_dash(const char *pfx, const char *arg)
+{
+       if (!strcmp(arg, "-"))
+               return xstrdup(arg);
+       return prefix_filename(pfx, arg);
+}
+
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
+{
+       if (!*path)
+               die("The empty string is not a valid path");
+       if (!is_absolute_path(path)) {
+               struct stat cwd_stat, pwd_stat;
+               size_t orig_len = sb->len;
+               char *cwd = xgetcwd();
+               char *pwd = getenv("PWD");
+               if (pwd && strcmp(pwd, cwd) &&
+                   !stat(cwd, &cwd_stat) &&
+                   (cwd_stat.st_dev || cwd_stat.st_ino) &&
+                   !stat(pwd, &pwd_stat) &&
+                   pwd_stat.st_dev == cwd_stat.st_dev &&
+                   pwd_stat.st_ino == cwd_stat.st_ino)
+                       strbuf_addstr(sb, pwd);
+               else
+                       strbuf_addstr(sb, cwd);
+               if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
+                       strbuf_addch(sb, '/');
+               free(cwd);
+       }
+       strbuf_addstr(sb, path);
+}
+
+void strbuf_add_real_path(struct strbuf *sb, const char *path)
+{
+       if (sb->len) {
+               struct strbuf resolved = STRBUF_INIT;
+               strbuf_realpath(&resolved, path, 1);
+               strbuf_addbuf(sb, &resolved);
+               strbuf_release(&resolved);
+       } else
+               strbuf_realpath(sb, path, 1);
+}
diff --git a/abspath.h b/abspath.h
new file mode 100644 (file)
index 0000000..4653080
--- /dev/null
+++ b/abspath.h
@@ -0,0 +1,54 @@
+#ifndef ABSPATH_H
+#define ABSPATH_H
+
+int is_directory(const char *);
+char *strbuf_realpath(struct strbuf *resolved, const char *path,
+                     int die_on_error);
+char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
+                               int die_on_error);
+char *real_pathdup(const char *path, int die_on_error);
+const char *absolute_path(const char *path);
+char *absolute_pathdup(const char *path);
+
+/*
+ * Concatenate "prefix" (if len is non-zero) and "path", with no
+ * connecting characters (so "prefix" should end with a "/").
+ * Unlike prefix_path, this should be used if the named file does
+ * not have to interact with index entry; i.e. name of a random file
+ * on the filesystem.
+ *
+ * The return value is always a newly allocated string (even if the
+ * prefix was empty).
+ */
+char *prefix_filename(const char *prefix, const char *path);
+
+/* Likewise, but path=="-" always yields "-" */
+char *prefix_filename_except_for_dash(const char *prefix, const char *path);
+
+static inline int is_absolute_path(const char *path)
+{
+       return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
+}
+
+/**
+ * Add a path to a buffer, converting a relative path to an
+ * absolute one in the process.  Symbolic links are not
+ * resolved.
+ */
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
+
+/**
+ * Canonize `path` (make it absolute, resolve symlinks, remove extra
+ * slashes) and append it to `sb`.  Die with an informative error
+ * message if there is a problem.
+ *
+ * The directory part of `path` (i.e., everything up to the last
+ * dir_sep) must denote a valid, existing directory, but the last
+ * component need not exist.
+ *
+ * Callers that don't mind links should use the more lightweight
+ * strbuf_add_absolute_path() instead.
+ */
+void strbuf_add_real_path(struct strbuf *sb, const char *path);
+
+#endif /* ABSPATH_H */
index f071b2a1b4f2ee8bf1959706d1778c3697d81ba7..6bf87e7ae71b06ea59d42d238c6242a66fdec0b5 100644 (file)
@@ -1,8 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "add-interactive.h"
 #include "color.h"
 #include "config.h"
 #include "diffcore.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "preload-index.h"
+#include "read-cache-ll.h"
+#include "repository.h"
 #include "revision.h"
 #include "refs.h"
 #include "string-list.h"
@@ -10,6 +16,7 @@
 #include "dir.h"
 #include "run-command.h"
 #include "prompt.h"
+#include "tree.h"
 
 static void init_color(struct repository *r, struct add_i_state *s,
                       const char *section_and_slot, char *dst,
@@ -530,8 +537,8 @@ static int get_modified_files(struct repository *r,
        struct collection_status s = { 0 };
        int i;
 
-       if (discard_index(r->index) < 0 ||
-           repo_read_index_preload(r, ps, 0) < 0)
+       discard_index(r->index);
+       if (repo_read_index_preload(r, ps, 0) < 0)
                return error(_("could not read index"));
 
        prefix_item_list_clear(files);
@@ -551,7 +558,7 @@ static int get_modified_files(struct repository *r,
                opt.def = is_initial ?
                        empty_tree_oid_hex() : oid_to_hex(&head_oid);
 
-               init_revisions(&rev, NULL);
+               repo_init_revisions(r, &rev, NULL);
                setup_revisions(0, NULL, &rev, &opt);
 
                rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
@@ -562,7 +569,7 @@ static int get_modified_files(struct repository *r,
                        copy_pathspec(&rev.prune_data, ps);
 
                if (s.mode == FROM_INDEX)
-                       run_diff_index(&rev, 1);
+                       run_diff_index(&rev, DIFF_INDEX_CACHED);
                else {
                        rev.diffopt.flags.ignore_dirty_submodules = 1;
                        run_diff_files(&rev, 0);
@@ -724,7 +731,7 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
 }
 
 static void revert_from_diff(struct diff_queue_struct *q,
-                            struct diff_options *opt, void *data)
+                            struct diff_options *opt, void *data UNUSED)
 {
        int i, add_flags = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
 
@@ -997,27 +1004,26 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
        count = list_and_choose(s, files, opts);
        opts->flags = 0;
        if (count > 0) {
-               struct strvec args = STRVEC_INIT;
+               struct child_process cmd = CHILD_PROCESS_INIT;
 
-               strvec_pushl(&args, "git", "diff", "-p", "--cached",
+               strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached",
                             oid_to_hex(!is_initial ? &oid :
                                        s->r->hash_algo->empty_tree),
                             "--", NULL);
                for (i = 0; i < files->items.nr; i++)
                        if (files->selected[i])
-                               strvec_push(&args,
+                               strvec_push(&cmd.args,
                                            files->items.items[i].string);
-               res = run_command_v_opt(args.v, 0);
-               strvec_clear(&args);
+               res = run_command(&cmd);
        }
 
        putchar('\n');
        return res;
 }
 
-static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
-                   struct prefix_item_list *unused_files,
-                   struct list_and_choose_options *unused_opts)
+static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED,
+                   struct prefix_item_list *files UNUSED,
+                   struct list_and_choose_options *opts UNUSED)
 {
        color_fprintf_ln(stdout, s->help_color, "status        - %s",
                         _("show paths with changes"));
@@ -1068,7 +1074,7 @@ struct print_command_item_data {
        const char *color, *reset;
 };
 
-static void print_command_item(int i, int selected,
+static void print_command_item(int i, int selected UNUSED,
                               struct string_list_item *item,
                               void *print_command_item_data)
 {
@@ -1157,8 +1163,8 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
                    _("staged"), _("unstaged"), _("path"));
        opts.list_opts.header = header.buf;
 
-       if (discard_index(r->index) < 0 ||
-           repo_read_index(r) < 0 ||
+       discard_index(r->index);
+       if (repo_read_index(r) < 0 ||
            repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
                                         NULL, NULL, NULL) < 0)
                warning(_("could not refresh index"));
index 33ecd8398a12707836cd89975226179413fa9574..79eda168ebb7cdb739720c2c0d16a44484522822 100644 (file)
@@ -1,11 +1,17 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "add-interactive.h"
+#include "advice.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-name.h"
+#include "read-cache-ll.h"
+#include "repository.h"
 #include "strbuf.h"
 #include "run-command.h"
 #include "strvec.h"
 #include "pathspec.h"
 #include "color.h"
-#include "diff.h"
 #include "compat/terminal.h"
 #include "prompt.h"
 
@@ -414,7 +420,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                strvec_push(&args,
                            /* could be on an unborn branch */
                            !strcmp("HEAD", s->revision) &&
-                           get_oid("HEAD", &oid) ?
+                           repo_get_oid(the_repository, "HEAD", &oid) ?
                            empty_tree_oid_hex() : s->revision);
        }
        color_arg_index = args.nr;
@@ -483,7 +489,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                if (!eol)
                        eol = pend;
 
-               if (starts_with(p, "diff ")) {
+               if (starts_with(p, "diff ") ||
+                   starts_with(p, "* Unmerged path ")) {
                        complete_file(marker, hunk);
                        ALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,
                                   file_diff_alloc);
@@ -1098,10 +1105,11 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
        size_t i;
 
        strbuf_reset(&s->buf);
-       strbuf_commented_addf(&s->buf, _("Manual hunk edit mode -- see bottom for "
-                                     "a quick guide.\n"));
+       strbuf_commented_addf(&s->buf, comment_line_char,
+                             _("Manual hunk edit mode -- see bottom for "
+                               "a quick guide.\n"));
        render_hunk(s, hunk, 0, 0, &s->buf);
-       strbuf_commented_addf(&s->buf,
+       strbuf_commented_addf(&s->buf, comment_line_char,
                              _("---\n"
                                "To remove '%c' lines, make them ' ' lines "
                                "(context).\n"
@@ -1110,12 +1118,13 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
                              s->mode->is_reverse ? '+' : '-',
                              s->mode->is_reverse ? '-' : '+',
                              comment_line_char);
-       strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
+       strbuf_commented_addf(&s->buf, comment_line_char, "%s",
+                             _(s->mode->edit_hunk_hint));
        /*
         * TRANSLATORS: 'it' refers to the patch mentioned in the previous
         * messages.
         */
-       strbuf_commented_addf(&s->buf,
+       strbuf_commented_addf(&s->buf, comment_line_char,
                              _("If it does not apply cleanly, you will be "
                                "given an opportunity to\n"
                                "edit again.  If all lines of the hunk are "
@@ -1750,7 +1759,8 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
                s.mode = &patch_mode_add;
        s.revision = revision;
 
-       if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
+       discard_index(r->index);
+       if (repo_read_index(r) < 0 ||
            (!s.mode->index_only &&
             repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
                                          NULL, NULL, NULL) < 0) ||
index fd189689437c7512e549482fe696f7a2d2400cd2..50c79443ba749fc56437806c21a20755c1988714 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "config.h"
 #include "color.h"
+#include "gettext.h"
 #include "help.h"
 #include "string-list.h"
 
@@ -44,6 +46,7 @@ static struct {
        [ADVICE_COMMIT_BEFORE_MERGE]                    = { "commitBeforeMerge", 1 },
        [ADVICE_DETACHED_HEAD]                          = { "detachedHead", 1 },
        [ADVICE_SUGGEST_DETACHING_HEAD]                 = { "suggestDetachingHead", 1 },
+       [ADVICE_DIVERGING]                              = { "diverging", 1 },
        [ADVICE_FETCH_SHOW_FORCED_UPDATES]              = { "fetchShowForcedUpdates", 1 },
        [ADVICE_GRAFT_FILE_DEPRECATED]                  = { "graftFileDeprecated", 1 },
        [ADVICE_IGNORED_HOOK]                           = { "ignoredHook", 1 },
@@ -75,6 +78,7 @@ static struct {
        [ADVICE_SUBMODULES_NOT_UPDATED]                 = { "submodulesNotUpdated", 1 },
        [ADVICE_UPDATE_SPARSE_PATH]                     = { "updateSparsePath", 1 },
        [ADVICE_WAITING_FOR_EDITOR]                     = { "waitingForEditor", 1 },
+       [ADVICE_WORKTREE_ADD_ORPHAN]                    = { "worktreeAddOrphan", 1 },
 };
 
 static const char turn_off_instructions[] =
@@ -187,9 +191,10 @@ int error_resolve_conflict(const char *me)
                error(_("Pulling is not possible because you have unmerged files."));
        else if (!strcmp(me, "revert"))
                error(_("Reverting is not possible because you have unmerged files."));
+       else if (!strcmp(me, "rebase"))
+               error(_("Rebasing is not possible because you have unmerged files."));
        else
-               error(_("It is not possible to %s because you have unmerged files."),
-                       me);
+               BUG("Unhandled conflict reason '%s'", me);
 
        if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
                /*
@@ -217,6 +222,14 @@ void NORETURN die_conclude_merge(void)
 
 void NORETURN die_ff_impossible(void)
 {
+       advise_if_enabled(ADVICE_DIVERGING,
+               _("Diverging branches can't be fast-forwarded, you need to either:\n"
+               "\n"
+               "\tgit merge --no-ff\n"
+               "\n"
+               "or:\n"
+               "\n"
+               "\tgit rebase\n"));
        die(_("Not possible to fast-forward, aborting."));
 }
 
index 07e0f76833e78070a26ec288db929ec966024d7e..2affbe142616de0d329c9aaaaee9ca355fb73adf 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -1,8 +1,6 @@
 #ifndef ADVICE_H
 #define ADVICE_H
 
-#include "git-compat-util.h"
-
 struct string_list;
 
 /*
@@ -21,6 +19,7 @@ struct string_list;
        ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
        ADVICE_COMMIT_BEFORE_MERGE,
        ADVICE_DETACHED_HEAD,
+       ADVICE_DIVERGING,
        ADVICE_SUGGEST_DETACHING_HEAD,
        ADVICE_FETCH_SHOW_FORCED_UPDATES,
        ADVICE_GRAFT_FILE_DEPRECATED,
@@ -50,6 +49,7 @@ struct string_list;
        ADVICE_UPDATE_SPARSE_PATH,
        ADVICE_WAITING_FOR_EDITOR,
        ADVICE_SKIPPED_CHERRY_PICKS,
+       ADVICE_WORKTREE_ADD_ORPHAN,
 };
 
 int git_default_advice_config(const char *var, const char *value);
diff --git a/alias.c b/alias.c
index 00abde081739436236aa077412c3b5b686144f42..5a238f2e3012d2fb12443b27ed02831e9edee476 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "alias.h"
 #include "config.h"
+#include "gettext.h"
+#include "strbuf.h"
 #include "string-list.h"
 
 struct config_alias_data {
@@ -9,7 +11,8 @@ struct config_alias_data {
        struct string_list *list;
 };
 
-static int config_alias_cb(const char *key, const char *value, void *d)
+static int config_alias_cb(const char *key, const char *value,
+                          const struct config_context *ctx UNUSED, void *d)
 {
        struct config_alias_data *data = d;
        const char *p;
@@ -44,6 +47,23 @@ void list_aliases(struct string_list *list)
        read_early_config(config_alias_cb, &data);
 }
 
+void quote_cmdline(struct strbuf *buf, const char **argv)
+{
+       for (const char **argp = argv; *argp; argp++) {
+               if (argp != argv)
+                       strbuf_addch(buf, ' ');
+               strbuf_addch(buf, '"');
+               for (const char *p = *argp; *p; p++) {
+                       const char c = *p;
+
+                       if (c == '"' || c =='\\')
+                               strbuf_addch(buf, '\\');
+                       strbuf_addch(buf, c);
+               }
+               strbuf_addch(buf, '"');
+       }
+}
+
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
 #define SPLIT_CMDLINE_ARGC_OVERFLOW 3
diff --git a/alias.h b/alias.h
index aef4843bb7821e87dcbe017ddfff64b8d6681fca..43db736484d1059aab35758e4162e6ade6c15539 100644 (file)
--- a/alias.h
+++ b/alias.h
@@ -1,9 +1,12 @@
 #ifndef ALIAS_H
 #define ALIAS_H
 
+struct strbuf;
 struct string_list;
 
 char *alias_lookup(const char *alias);
+/* Quote argv so buf can be parsed by split_cmdline() */
+void quote_cmdline(struct strbuf *buf, const char **argv);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
diff --git a/alloc.c b/alloc.c
index 27f697e4c87a05ef7cc847a17e83e14e9cfd2a4d..377e80f5dda2f80e6222f4eb2fa8c607d708cc9d 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -8,11 +8,12 @@
  * up with maximal alignment because it doesn't know what the object alignment
  * for the new allocation is.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "object.h"
 #include "blob.h"
 #include "tree.h"
 #include "commit.h"
+#include "repository.h"
 #include "tag.h"
 #include "alloc.h"
 
diff --git a/apply.c b/apply.c
index 1ac3a028a0aa9b63ab499abafcafc0d4b59dfc5a..7608e3301ca0727dcfec0a579ed46390afa41ab0 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -7,21 +7,34 @@
  *
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "base85.h"
 #include "config.h"
-#include "object-store.h"
-#include "blob.h"
+#include "object-store-ll.h"
 #include "delta.h"
 #include "diff.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "lockfile.h"
+#include "name-hash.h"
+#include "object-name.h"
+#include "object-file.h"
 #include "parse-options.h"
+#include "path.h"
 #include "quote.h"
+#include "read-cache.h"
 #include "rerere.h"
 #include "apply.h"
 #include "entry.h"
+#include "setup.h"
+#include "symlinks.h"
+#include "wildmatch.h"
+#include "ws.h"
 
 struct gitdiff_data {
        struct strbuf *root;
@@ -125,7 +138,7 @@ void clear_apply_state(struct apply_state *state)
        /* &state->fn_table is cleared at the end of apply_patch() */
 }
 
-static void mute_routine(const char *msg, va_list params)
+static void mute_routine(const char *msg UNUSED, va_list params UNUSED)
 {
        /* do nothing */
 }
@@ -386,11 +399,22 @@ static void say_patch_name(FILE *output, const char *fmt, struct patch *patch)
 
 #define SLOP (16)
 
+/*
+ * apply.c isn't equipped to handle arbitrarily large patches, because
+ * it intermingles `unsigned long` with `int` for the type used to store
+ * buffer lengths.
+ *
+ * Only process patches that are just shy of 1 GiB large in order to
+ * avoid any truncation or overflow issues.
+ */
+#define MAX_APPLY_SIZE (1024UL * 1024 * 1023)
+
 static int read_patch_file(struct strbuf *sb, int fd)
 {
        if (strbuf_read(sb, fd, 0) < 0)
-               return error_errno("git apply: failed to read");
-
+               return error_errno(_("failed to read patch"));
+       else if (sb->len >= MAX_APPLY_SIZE)
+               return error(_("patch too large"));
        /*
         * Make sure that we have some slop in the buffer
         * so that we can do speculative "memcmp" etc, and
@@ -892,9 +916,9 @@ static int parse_traditional_patch(struct apply_state *state,
        return 0;
 }
 
-static int gitdiff_hdrend(struct gitdiff_data *state,
-                         const char *line,
-                         struct patch *patch)
+static int gitdiff_hdrend(struct gitdiff_data *state UNUSED,
+                         const char *line UNUSED,
+                         struct patch *patch UNUSED)
 {
        return 1;
 }
@@ -1044,7 +1068,7 @@ static int gitdiff_renamedst(struct gitdiff_data *state,
        return 0;
 }
 
-static int gitdiff_similarity(struct gitdiff_data *state,
+static int gitdiff_similarity(struct gitdiff_data *state UNUSED,
                              const char *line,
                              struct patch *patch)
 {
@@ -1054,7 +1078,7 @@ static int gitdiff_similarity(struct gitdiff_data *state,
        return 0;
 }
 
-static int gitdiff_dissimilarity(struct gitdiff_data *state,
+static int gitdiff_dissimilarity(struct gitdiff_data *state UNUSED,
                                 const char *line,
                                 struct patch *patch)
 {
@@ -1104,9 +1128,9 @@ static int gitdiff_index(struct gitdiff_data *state,
  * This is normal for a diff that doesn't change anything: we'll fall through
  * into the next diff. Tell the parser to break out.
  */
-static int gitdiff_unrecognized(struct gitdiff_data *state,
-                               const char *line,
-                               struct patch *patch)
+static int gitdiff_unrecognized(struct gitdiff_data *state UNUSED,
+                               const char *line UNUSED,
+                               struct patch *patch UNUSED)
 {
        return 1;
 }
@@ -2903,7 +2927,7 @@ static int apply_one_fragment(struct apply_state *state,
                        break;
                case ' ':
                        if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
-                           ws_blank_line(patch + 1, plen, ws_rule))
+                           ws_blank_line(patch + 1, plen))
                                is_blank_context = 1;
                        /* fallthrough */
                case '-':
@@ -2932,7 +2956,7 @@ static int apply_one_fragment(struct apply_state *state,
                                      (first == '+' ? 0 : LINE_COMMON));
                        if (first == '+' &&
                            (ws_rule & WS_BLANK_AT_EOF) &&
-                           ws_blank_line(patch + 1, plen, ws_rule))
+                           ws_blank_line(patch + 1, plen))
                                added_blank_line = 1;
                        break;
                case '@': case '\\':
@@ -3191,7 +3215,8 @@ static int apply_binary(struct apply_state *state,
                unsigned long size;
                char *result;
 
-               result = read_object_file(&oid, &type, &size);
+               result = repo_read_object_file(the_repository, &oid, &type,
+                                              &size);
                if (!result)
                        return error(_("the necessary postimage %s for "
                                       "'%s' cannot be read"),
@@ -3254,7 +3279,8 @@ static int read_blob_object(struct strbuf *buf, const struct object_id *oid, uns
                unsigned long sz;
                char *result;
 
-               result = read_object_file(oid, &type, &sz);
+               result = repo_read_object_file(the_repository, oid, &type,
+                                              &sz);
                if (!result)
                        return -1;
                /* XXX read_sha1_file NUL-terminates */
@@ -3482,7 +3508,8 @@ static int resolve_to(struct image *image, const struct object_id *result_id)
 
        clear_image(image);
 
-       image->buf = read_object_file(result_id, &type, &size);
+       image->buf = repo_read_object_file(the_repository, result_id, &type,
+                                          &size);
        if (!image->buf || type != OBJ_BLOB)
                die("unable to read blob object %s", oid_to_hex(result_id));
        image->len = size;
@@ -3600,7 +3627,7 @@ static int try_threeway(struct apply_state *state,
        /* Preimage the patch was prepared for */
        if (patch->is_new)
                write_object_file("", 0, OBJ_BLOB, &pre_oid);
-       else if (get_oid(patch->old_oid_prefix, &pre_oid) ||
+       else if (repo_get_oid(the_repository, patch->old_oid_prefix, &pre_oid) ||
                 read_blob_object(&buf, &pre_oid, patch->old_mode))
                return error(_("repository lacks the necessary blob to perform 3-way merge."));
 
@@ -4095,7 +4122,7 @@ static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid)
 static int build_fake_ancestor(struct apply_state *state, struct patch *list)
 {
        struct patch *patch;
-       struct index_state result = { NULL };
+       struct index_state result = INDEX_STATE_INIT(state->repo);
        struct lock_file lock = LOCK_INIT;
        int res;
 
@@ -4117,7 +4144,7 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list)
                        else
                                return error(_("sha1 information is lacking or "
                                               "useless for submodule %s"), name);
-               } else if (!get_oid_blob(patch->old_oid_prefix, &oid)) {
+               } else if (!repo_get_oid_blob(the_repository, patch->old_oid_prefix, &oid)) {
                        ; /* ok */
                } else if (!patch->lines_added && !patch->lines_deleted) {
                        /* mode-only change: update the current */
diff --git a/apply.h b/apply.h
index b9f18ce87d1e0374a04aaaf785592c752dba6058..7cd38b1443c67e448bdc9f004f94a841a12eb747 100644 (file)
--- a/apply.h
+++ b/apply.h
@@ -1,7 +1,7 @@
 #ifndef APPLY_H
 #define APPLY_H
 
-#include "hash.h"
+#include "hash-ll.h"
 #include "lockfile.h"
 #include "string-list.h"
 #include "strmap.h"
index 3e4822b68409b85d36436bd42da862991274cd81..f2a0ed77523b457fa17497498d3ffb4499773a1f 100644 (file)
@@ -1,13 +1,18 @@
 /*
  * Copyright (c) 2005, 2006 Rene Scharfe
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
+#include "git-zlib.h"
+#include "hex.h"
 #include "tar.h"
 #include "archive.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "strbuf.h"
 #include "streaming.h"
 #include "run-command.h"
+#include "write-or-die.h"
 
 #define RECORDSIZE     (512)
 #define BLOCKSIZE      (RECORDSIZE * 20)
@@ -406,14 +411,15 @@ static int tar_filter_config(const char *var, const char *value,
        return 0;
 }
 
-static int git_tar_config(const char *var, const char *value, void *cb)
+static int git_tar_config(const char *var, const char *value,
+                         const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "tar.umask")) {
                if (value && !strcmp(value, "user")) {
                        tar_umask = umask(0);
                        umask(tar_umask);
                } else {
-                       tar_umask = git_config_int(var, value);
+                       tar_umask = git_config_int(var, value, ctx->kvi);
                }
                return 0;
        }
@@ -498,6 +504,7 @@ static int write_tar_filter_archive(const struct archiver *ar,
        strvec_push(&filter.args, cmd.buf);
        filter.use_shell = 1;
        filter.in = -1;
+       filter.silent_exec_failure = 1;
 
        if (start_command(&filter) < 0)
                die_errno(_("unable to start '%s' filter"), cmd.buf);
index 0456f1ebf15c839639f4759c329cb957b0a64feb..fd1d3f816d30d696456cf1915bb40f2d325f3010 100644 (file)
@@ -1,13 +1,18 @@
 /*
  * Copyright (c) 2006 Rene Scharfe
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "archive.h"
+#include "gettext.h"
+#include "git-zlib.h"
+#include "hex.h"
 #include "streaming.h"
 #include "utf8.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "strbuf.h"
 #include "userdiff.h"
+#include "write-or-die.h"
 #include "xdiff-interface.h"
 #include "date.h"
 
@@ -613,6 +618,7 @@ static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
 }
 
 static int archive_zip_config(const char *var, const char *value,
+                             const struct config_context *ctx UNUSED,
                              void *data UNUSED)
 {
        return userdiff_config(var, value);
index cc1087262f0bc00893184b2ebbf5dffddf89bbb7..a6730bebfa07d65d003b5594c63081e6ef7d3e88 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -1,14 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "convert.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "path.h"
+#include "pretty.h"
+#include "setup.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "commit.h"
+#include "tree.h"
 #include "tree-walk.h"
 #include "attr.h"
 #include "archive.h"
 #include "parse-options.h"
 #include "unpack-trees.h"
-#include "dir.h"
 #include "quote.h"
 
 static char const * const archive_usage[] = {
@@ -59,7 +68,8 @@ static void format_subst(const struct commit *commit,
                strbuf_add(&fmt, b + 8, c - b - 8);
 
                strbuf_add(buf, src, b - src);
-               format_commit_message(commit, fmt.buf, buf, ctx);
+               repo_format_commit_message(the_repository, commit, fmt.buf,
+                                          buf, ctx);
                len -= c + 1 - src;
                src  = c + 1;
        }
@@ -84,7 +94,7 @@ static void *object_file_to_archive(const struct archiver_args *args,
                               (args->tree ? &args->tree->object.oid : NULL), oid);
 
        path += args->baselen;
-       buffer = read_object_file(oid, type, sizep);
+       buffer = repo_read_object_file(the_repository, oid, type, sizep);
        if (buffer && S_ISREG(mode)) {
                struct strbuf buf = STRBUF_INIT;
                size_t size = 0;
@@ -166,6 +176,29 @@ static int write_archive_entry(const struct object_id *oid, const char *base,
                args->convert = check_attr_export_subst(check);
        }
 
+       if (args->prefix) {
+               static struct strbuf new_path = STRBUF_INIT;
+               static struct strbuf buf = STRBUF_INIT;
+               const char *rel;
+
+               rel = relative_path(path_without_prefix, args->prefix, &buf);
+
+               /*
+                * We don't add an entry for the current working
+                * directory when we are at the root; skip it also when
+                * we're in a subdirectory or submodule.  Skip entries
+                * higher up as well.
+                */
+               if (!strcmp(rel, "./") || starts_with(rel, "../"))
+                       return S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0;
+
+               /* rel can refer to path, so don't edit it in place */
+               strbuf_reset(&new_path);
+               strbuf_add(&new_path, args->base, args->baselen);
+               strbuf_addstr(&new_path, rel);
+               strbuf_swap(&path, &new_path);
+       }
+
        if (args->verbose)
                fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
 
@@ -401,6 +434,27 @@ static int reject_entry(const struct object_id *oid UNUSED,
        return ret;
 }
 
+static int reject_outside(const struct object_id *oid UNUSED,
+                         struct strbuf *base, const char *filename,
+                         unsigned mode, void *context)
+{
+       struct archiver_args *args = context;
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf path = STRBUF_INIT;
+       int ret = 0;
+
+       if (S_ISDIR(mode))
+               return READ_TREE_RECURSIVE;
+
+       strbuf_addbuf(&path, base);
+       strbuf_addstr(&path, filename);
+       if (starts_with(relative_path(path.buf, args->prefix, &buf), "../"))
+               ret = -1;
+       strbuf_release(&buf);
+       strbuf_release(&path);
+       return ret;
+}
+
 static int path_exists(struct archiver_args *args, const char *path)
 {
        const char *paths[] = { path, NULL };
@@ -408,8 +462,13 @@ static int path_exists(struct archiver_args *args, const char *path)
        int ret;
 
        ctx.args = args;
-       parse_pathspec(&ctx.pathspec, 0, 0, "", paths);
+       parse_pathspec(&ctx.pathspec, 0, PATHSPEC_PREFER_CWD,
+                      args->prefix, paths);
        ctx.pathspec.recursive = 1;
+       if (args->prefix && read_tree(args->repo, args->tree, &ctx.pathspec,
+                                     reject_outside, args))
+               die(_("pathspec '%s' matches files outside the "
+                     "current directory"), path);
        ret = read_tree(args->repo, args->tree,
                        &ctx.pathspec,
                        reject_entry, &ctx);
@@ -425,9 +484,8 @@ static void parse_pathspec_arg(const char **pathspec,
         * Also if pathspec patterns are dependent, we're in big
         * trouble as we test each one separately
         */
-       parse_pathspec(&ar_args->pathspec, 0,
-                      PATHSPEC_PREFER_FULL,
-                      "", pathspec);
+       parse_pathspec(&ar_args->pathspec, 0, PATHSPEC_PREFER_CWD,
+                      ar_args->prefix, pathspec);
        ar_args->pathspec.recursive = 1;
        if (pathspec) {
                while (*pathspec) {
@@ -439,8 +497,7 @@ static void parse_pathspec_arg(const char **pathspec,
 }
 
 static void parse_treeish_arg(const char **argv,
-               struct archiver_args *ar_args, const char *prefix,
-               int remote)
+                             struct archiver_args *ar_args, int remote)
 {
        const char *name = argv[0];
        const struct object_id *commit_oid;
@@ -455,13 +512,14 @@ static void parse_treeish_arg(const char **argv,
                const char *colon = strchrnul(name, ':');
                int refnamelen = colon - name;
 
-               if (!dwim_ref(name, refnamelen, &oid, &ref, 0))
+               if (!repo_dwim_ref(the_repository, name, refnamelen, &oid, &ref, 0))
                        die(_("no such ref: %.*s"), refnamelen, name);
        } else {
-               dwim_ref(name, strlen(name), &oid, &ref, 0);
+               repo_dwim_ref(the_repository, name, strlen(name), &oid, &ref,
+                             0);
        }
 
-       if (get_oid(name, &oid))
+       if (repo_get_oid(the_repository, name, &oid))
                die(_("not a valid object name: %s"), name);
 
        commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1);
@@ -472,25 +530,13 @@ static void parse_treeish_arg(const char **argv,
                commit_oid = NULL;
                archive_time = time(NULL);
        }
+       if (ar_args->mtime_option)
+               archive_time = approxidate(ar_args->mtime_option);
 
        tree = parse_tree_indirect(&oid);
        if (!tree)
                die(_("not a tree object: %s"), oid_to_hex(&oid));
 
-       if (prefix) {
-               struct object_id tree_oid;
-               unsigned short mode;
-               int err;
-
-               err = get_tree_entry(ar_args->repo,
-                                    &tree->object.oid,
-                                    prefix, &tree_oid,
-                                    &mode);
-               if (err || !S_ISDIR(mode))
-                       die(_("current working directory is untracked"));
-
-               tree = parse_tree_indirect(&tree_oid);
-       }
        ar_args->refname = ref;
        ar_args->tree = tree;
        ar_args->commit_oid = commit_oid;
@@ -498,7 +544,7 @@ static void parse_treeish_arg(const char **argv,
        ar_args->time = archive_time;
 }
 
-static void extra_file_info_clear(void *util, const char *str)
+static void extra_file_info_clear(void *util, const char *str UNUSED)
 {
        struct extra_file_info *info = util;
        free(info->base);
@@ -586,6 +632,7 @@ static int parse_archive_args(int argc, const char **argv,
        const char *remote = NULL;
        const char *exec = NULL;
        const char *output = NULL;
+       const char *mtime_option = NULL;
        int compression_level = -1;
        int verbose = 0;
        int i;
@@ -607,6 +654,9 @@ static int parse_archive_args(int argc, const char **argv,
                OPT_BOOL(0, "worktree-attributes", &worktree_attributes,
                        N_("read .gitattributes in working directory")),
                OPT__VERBOSE(&verbose, N_("report archived files on stderr")),
+               { OPTION_STRING, 0, "mtime", &mtime_option, N_("time"),
+                 N_("set modification time of archive entries"),
+                 PARSE_OPT_NONEG },
                OPT_NUMBER_CALLBACK(&compression_level,
                        N_("set compression level"), number_callback),
                OPT_GROUP(""),
@@ -635,6 +685,8 @@ static int parse_archive_args(int argc, const char **argv,
                base = "";
 
        if (list) {
+               if (argc)
+                       die(_("extra command line parameter '%s'"), *argv);
                for (i = 0; i < nr_archivers; i++)
                        if (!is_remote || archivers[i]->flags & ARCHIVER_REMOTE)
                                printf("%s\n", archivers[i]->name);
@@ -668,6 +720,7 @@ static int parse_archive_args(int argc, const char **argv,
        args->base = base;
        args->baselen = strlen(base);
        args->worktree_attributes = worktree_attributes;
+       args->mtime_option = mtime_option;
 
        return argc;
 }
@@ -703,13 +756,14 @@ int write_archive(int argc, const char **argv, const char *prefix,
                setup_git_directory();
        }
 
-       parse_treeish_arg(argv, &args, prefix, remote);
+       parse_treeish_arg(argv, &args, remote);
        parse_pathspec_arg(argv + 1, &args);
 
        rc = ar->write_archive(ar, &args);
 
        string_list_clear_func(&args.extra_files, extra_file_info_clear);
        free(args.refname);
+       clear_pathspec(&args.pathspec);
 
        return rc;
 }
index 08bed3ed3af6b062f3ff2791613c64b86d42ec00..bbe65ba0f900df1367d1d7607300d732a86c5c0c 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -1,8 +1,8 @@
 #ifndef ARCHIVE_H
 #define ARCHIVE_H
 
-#include "cache.h"
 #include "pathspec.h"
+#include "string-list.h"
 
 struct repository;
 struct pretty_print_context;
@@ -16,6 +16,7 @@ struct archiver_args {
        struct tree *tree;
        const struct object_id *commit_oid;
        const struct commit *commit;
+       const char *mtime_option;
        timestamp_t time;
        struct pathspec pathspec;
        unsigned int verbose : 1;
diff --git a/attr.c b/attr.c
index 9922529b58c381dbc2cdf778f7e59b17fdf64657..679e42258c2b205bfceb9d065295c47c375442d4 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -6,14 +6,26 @@
  * an insanely large number of attributes.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
 #include "exec-cmd.h"
 #include "attr.h"
 #include "dir.h"
+#include "gettext.h"
+#include "path.h"
 #include "utf8.h"
 #include "quote.h"
+#include "read-cache-ll.h"
+#include "refs.h"
+#include "revision.h"
+#include "object-store-ll.h"
+#include "setup.h"
 #include "thread-utils.h"
+#include "tree-walk.h"
+#include "object-name.h"
+
+const char *git_attr_tree;
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -172,6 +184,15 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
        }
 }
 
+/*
+ * Atribute name cannot begin with "builtin_" which
+ * is a reserved namespace for built in attributes values.
+ */
+static int attr_name_reserved(const char *name)
+{
+       return starts_with(name, "builtin_");
+}
+
 static int attr_name_valid(const char *name, size_t namelen)
 {
        /*
@@ -304,7 +325,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
                        cp++;
                        len--;
                }
-               if (!attr_name_valid(cp, len)) {
+               if (!attr_name_valid(cp, len) || attr_name_reserved(cp)) {
                        report_invalid_attr(cp, len, src, lineno);
                        return NULL;
                }
@@ -368,7 +389,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
                name += strlen(ATTRIBUTE_MACRO_PREFIX);
                name += strspn(name, blank);
                namelen = strcspn(name, blank);
-               if (!attr_name_valid(name, namelen)) {
+               if (!attr_name_valid(name, namelen) || attr_name_reserved(name)) {
                        report_invalid_attr(name, namelen, src, lineno);
                        goto fail_return;
                }
@@ -603,8 +624,7 @@ struct attr_check *attr_check_dup(const struct attr_check *check)
 
        ret->nr = check->nr;
        ret->alloc = check->alloc;
-       ALLOC_ARRAY(ret->items, ret->nr);
-       COPY_ARRAY(ret->items, check->items, ret->nr);
+       DUP_ARRAY(ret->items, check->items, ret->nr);
 
        return ret;
 }
@@ -745,42 +765,18 @@ static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
        return res;
 }
 
-static struct attr_stack *read_attr_from_index(struct index_state *istate,
-                                              const char *path,
-                                              unsigned flags)
+static struct attr_stack *read_attr_from_buf(char *buf, const char *path,
+                                            unsigned flags)
 {
        struct attr_stack *res;
-       char *buf, *sp;
+       char *sp;
        int lineno = 0;
-       unsigned long size;
-
-       if (!istate)
-               return NULL;
 
-       /*
-        * The .gitattributes file only applies to files within its
-        * parent directory. In the case of cone-mode sparse-checkout,
-        * the .gitattributes file is sparse if and only if all paths
-        * within that directory are also sparse. Thus, don't load the
-        * .gitattributes file since it will not matter.
-        *
-        * In the case of a sparse index, it is critical that we don't go
-        * looking for a .gitattributes file, as doing so would cause the
-        * index to expand.
-        */
-       if (!path_in_cone_mode_sparse_checkout(path, istate))
-               return NULL;
-
-       buf = read_blob_data_from_index(istate, path, &size);
        if (!buf)
                return NULL;
-       if (size >= ATTR_MAX_FILE_SIZE) {
-               warning(_("ignoring overly large gitattributes blob '%s'"), path);
-               return NULL;
-       }
 
        CALLOC_ARRAY(res, 1);
-       for (sp = buf; *sp; ) {
+       for (sp = buf; *sp;) {
                char *ep;
                int more;
 
@@ -791,16 +787,100 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
                sp = ep + more;
        }
        free(buf);
+
        return res;
 }
 
+static struct attr_stack *read_attr_from_blob(struct index_state *istate,
+                                             const struct object_id *tree_oid,
+                                             const char *path, unsigned flags)
+{
+       struct object_id oid;
+       unsigned long sz;
+       enum object_type type;
+       void *buf;
+       unsigned short mode;
+
+       if (!tree_oid)
+               return NULL;
+
+       if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode))
+               return NULL;
+
+       buf = repo_read_object_file(istate->repo, &oid, &type, &sz);
+       if (!buf || type != OBJ_BLOB) {
+               free(buf);
+               return NULL;
+       }
+
+       return read_attr_from_buf(buf, path, flags);
+}
+
+static struct attr_stack *read_attr_from_index(struct index_state *istate,
+                                              const char *path, unsigned flags)
+{
+       struct attr_stack *stack = NULL;
+       char *buf;
+       unsigned long size;
+       int sparse_dir_pos = -1;
+
+       if (!istate)
+               return NULL;
+
+       /*
+        * When handling sparse-checkouts, .gitattributes files
+        * may reside within a sparse directory. We distinguish
+        * whether a path exists directly in the index or not by
+        * evaluating if 'pos' is negative.
+        * If 'pos' is negative, the path is not directly present
+        * in the index and is likely within a sparse directory.
+        * For paths not in the index, The absolute value of 'pos'
+        * minus 1 gives us the position where the path would be
+        * inserted in lexicographic order within the index.
+        * We then subtract another 1 from this value
+        * (sparse_dir_pos = -pos - 2) to find the position of the
+        * last index entry which is lexicographically smaller than
+        * the path. This would be the sparse directory containing
+        * the path. By identifying the sparse directory containing
+        * the path, we can correctly read the attributes specified
+        * in the .gitattributes file from the tree object of the
+        * sparse directory.
+        */
+       if (!path_in_cone_mode_sparse_checkout(path, istate)) {
+               int pos = index_name_pos_sparse(istate, path, strlen(path));
+
+               if (pos < 0)
+                       sparse_dir_pos = -pos - 2;
+       }
+
+       if (sparse_dir_pos >= 0 &&
+           S_ISSPARSEDIR(istate->cache[sparse_dir_pos]->ce_mode) &&
+           !strncmp(istate->cache[sparse_dir_pos]->name, path, ce_namelen(istate->cache[sparse_dir_pos]))) {
+               const char *relative_path = path + ce_namelen(istate->cache[sparse_dir_pos]);
+               stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags);
+       } else {
+               buf = read_blob_data_from_index(istate, path, &size);
+               if (!buf)
+                       return NULL;
+               if (size >= ATTR_MAX_FILE_SIZE) {
+                       warning(_("ignoring overly large gitattributes blob '%s'"), path);
+                       return NULL;
+               }
+               stack = read_attr_from_buf(buf, path, flags);
+       }
+       return stack;
+}
+
 static struct attr_stack *read_attr(struct index_state *istate,
+                                   const struct object_id *tree_oid,
                                    const char *path, unsigned flags)
 {
        struct attr_stack *res = NULL;
 
        if (direction == GIT_ATTR_INDEX) {
                res = read_attr_from_index(istate, path, flags);
+       } else if (tree_oid) {
+               res = read_attr_from_blob(istate, tree_oid, path, flags);
        } else if (!is_bare_repository()) {
                if (direction == GIT_ATTR_CHECKOUT) {
                        res = read_attr_from_index(istate, path, flags);
@@ -824,7 +904,7 @@ static struct attr_stack *read_attr(struct index_state *istate,
        return res;
 }
 
-static const char *git_etc_gitattributes(void)
+const char *git_attr_system_file(void)
 {
        static const char *system_wide;
        if (!system_wide)
@@ -832,7 +912,7 @@ static const char *git_etc_gitattributes(void)
        return system_wide;
 }
 
-static const char *get_home_gitattributes(void)
+const char *git_attr_global_file(void)
 {
        if (!git_attributes_file)
                git_attributes_file = xdg_config_home("attributes");
@@ -840,7 +920,7 @@ static const char *get_home_gitattributes(void)
        return git_attributes_file;
 }
 
-static int git_attr_system(void)
+int git_attr_system_is_enabled(void)
 {
        return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
 }
@@ -860,6 +940,7 @@ static void push_stack(struct attr_stack **attr_stack_p,
 }
 
 static void bootstrap_attr_stack(struct index_state *istate,
+                                const struct object_id *tree_oid,
                                 struct attr_stack **stack)
 {
        struct attr_stack *e;
@@ -873,19 +954,19 @@ static void bootstrap_attr_stack(struct index_state *istate,
        push_stack(stack, e, NULL, 0);
 
        /* system-wide frame */
-       if (git_attr_system()) {
-               e = read_attr_from_file(git_etc_gitattributes(), flags);
+       if (git_attr_system_is_enabled()) {
+               e = read_attr_from_file(git_attr_system_file(), flags);
                push_stack(stack, e, NULL, 0);
        }
 
        /* home directory */
-       if (get_home_gitattributes()) {
-               e = read_attr_from_file(get_home_gitattributes(), flags);
+       if (git_attr_global_file()) {
+               e = read_attr_from_file(git_attr_global_file(), flags);
                push_stack(stack, e, NULL, 0);
        }
 
        /* root directory */
-       e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
+       e = read_attr(istate, tree_oid, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
        push_stack(stack, e, xstrdup(""), 0);
 
        /* info frame */
@@ -899,6 +980,7 @@ static void bootstrap_attr_stack(struct index_state *istate,
 }
 
 static void prepare_attr_stack(struct index_state *istate,
+                              const struct object_id *tree_oid,
                               const char *path, int dirlen,
                               struct attr_stack **stack)
 {
@@ -920,7 +1002,7 @@ static void prepare_attr_stack(struct index_state *istate,
         * .gitattributes in deeper directories to shallower ones,
         * and finally use the built-in set as the default.
         */
-       bootstrap_attr_stack(istate, stack);
+       bootstrap_attr_stack(istate, tree_oid, stack);
 
        /*
         * Pop the "info" one that is always at the top of the stack.
@@ -975,7 +1057,7 @@ static void prepare_attr_stack(struct index_state *istate,
                strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
                strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
 
-               next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW);
+               next = read_attr(istate, tree_oid, pathbuf.buf, READ_ATTR_NOFOLLOW);
 
                /* reset the pathbuf to not include "/.gitattributes" */
                strbuf_setlen(&pathbuf, len);
@@ -1095,8 +1177,8 @@ static void determine_macros(struct all_attrs_item *all_attrs,
  * Otherwise all attributes are collected.
  */
 static void collect_some_attrs(struct index_state *istate,
-                              const char *path,
-                              struct attr_check *check)
+                              const struct object_id *tree_oid,
+                              const char *path, struct attr_check *check)
 {
        int pathlen, rem, dirlen;
        const char *cp, *last_slash = NULL;
@@ -1115,7 +1197,7 @@ static void collect_some_attrs(struct index_state *istate,
                dirlen = 0;
        }
 
-       prepare_attr_stack(istate, path, dirlen, &check->stack);
+       prepare_attr_stack(istate, tree_oid, path, dirlen, &check->stack);
        all_attrs_init(&g_attr_hashmap, check);
        determine_macros(check->all_attrs, check->stack);
 
@@ -1123,19 +1205,144 @@ static void collect_some_attrs(struct index_state *istate,
        fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
 }
 
+static const char *default_attr_source_tree_object_name;
+static int ignore_bad_attr_tree;
+
+void set_git_attr_source(const char *tree_object_name)
+{
+       default_attr_source_tree_object_name = xstrdup(tree_object_name);
+}
+
+static void compute_default_attr_source(struct object_id *attr_source)
+{
+       if (!default_attr_source_tree_object_name)
+               default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT);
+
+       if (!default_attr_source_tree_object_name && git_attr_tree) {
+               default_attr_source_tree_object_name = git_attr_tree;
+               ignore_bad_attr_tree = 1;
+       }
+
+       if (!default_attr_source_tree_object_name &&
+           startup_info->have_repository &&
+           is_bare_repository()) {
+               default_attr_source_tree_object_name = "HEAD";
+               ignore_bad_attr_tree = 1;
+       }
+
+       if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
+               return;
+
+       if (repo_get_oid_treeish(the_repository,
+                                default_attr_source_tree_object_name,
+                                attr_source) && !ignore_bad_attr_tree)
+               die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+}
+
+static struct object_id *default_attr_source(void)
+{
+       static struct object_id attr_source;
+
+       if (is_null_oid(&attr_source))
+               compute_default_attr_source(&attr_source);
+       if (is_null_oid(&attr_source))
+               return NULL;
+       return &attr_source;
+}
+
+static const char *interned_mode_string(unsigned int mode)
+{
+       static struct {
+               unsigned int val;
+               char str[7];
+       } mode_string[] = {
+               { .val = 0040000 },
+               { .val = 0100644 },
+               { .val = 0100755 },
+               { .val = 0120000 },
+               { .val = 0160000 },
+       };
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mode_string); i++) {
+               if (mode_string[i].val != mode)
+                       continue;
+               if (!*mode_string[i].str)
+                       snprintf(mode_string[i].str, sizeof(mode_string[i].str),
+                                "%06o", mode);
+               return mode_string[i].str;
+       }
+       BUG("Unsupported mode 0%o", mode);
+}
+
+static const char *builtin_object_mode_attr(struct index_state *istate, const char *path)
+{
+       unsigned int mode;
+
+       if (direction == GIT_ATTR_CHECKIN) {
+               struct object_id oid;
+               struct stat st;
+               if (lstat(path, &st))
+                       die_errno(_("unable to stat '%s'"), path);
+               mode = canon_mode(st.st_mode);
+               if (S_ISDIR(mode)) {
+                       /*
+                        *`path` is either a directory or it is a submodule,
+                        * in which case it is already indexed as submodule
+                        * or it does not exist in the index yet and we need to
+                        * check if we can resolve to a ref.
+                       */
+                       int pos = index_name_pos(istate, path, strlen(path));
+                       if (pos >= 0) {
+                                if (S_ISGITLINK(istate->cache[pos]->ce_mode))
+                                        mode = istate->cache[pos]->ce_mode;
+                       } else if (resolve_gitlink_ref(path, "HEAD", &oid) == 0) {
+                               mode = S_IFGITLINK;
+                       }
+               }
+       } else {
+               /*
+                * For GIT_ATTR_CHECKOUT and GIT_ATTR_INDEX we only check
+                * for mode in the index.
+                */
+               int pos = index_name_pos(istate, path, strlen(path));
+               if (pos >= 0)
+                       mode = istate->cache[pos]->ce_mode;
+               else
+                       return ATTR__UNSET;
+       }
+
+       return interned_mode_string(mode);
+}
+
+
+static const char *compute_builtin_attr(struct index_state *istate,
+                                         const char *path,
+                                         const struct git_attr *attr) {
+       static const struct git_attr *object_mode_attr;
+
+       if (!object_mode_attr)
+               object_mode_attr = git_attr("builtin_objectmode");
+
+       if (attr == object_mode_attr)
+               return builtin_object_mode_attr(istate, path);
+       return ATTR__UNSET;
+}
+
 void git_check_attr(struct index_state *istate,
                    const char *path,
                    struct attr_check *check)
 {
        int i;
+       const struct object_id *tree_oid = default_attr_source();
 
-       collect_some_attrs(istate, path, check);
+       collect_some_attrs(istate, tree_oid, path, check);
 
        for (i = 0; i < check->nr; i++) {
                unsigned int n = check->items[i].attr->attr_nr;
                const char *value = check->all_attrs[n].value;
                if (value == ATTR__UNKNOWN)
-                       value = ATTR__UNSET;
+                       value = compute_builtin_attr(istate, path, check->all_attrs[n].attr);
                check->items[i].value = value;
        }
 }
@@ -1144,9 +1351,10 @@ void git_all_attrs(struct index_state *istate,
                   const char *path, struct attr_check *check)
 {
        int i;
+       const struct object_id *tree_oid = default_attr_source();
 
        attr_check_reset(check);
-       collect_some_attrs(istate, path, check);
+       collect_some_attrs(istate, tree_oid, path, check);
 
        for (i = 0; i < check->all_attrs_nr; i++) {
                const char *name = check->all_attrs[i].attr->name;
diff --git a/attr.h b/attr.h
index 2f22dffadb32ad6f0b2b9a325e098b9bb0f199f7..127998ae013c9de221b700144ad2082a78bfcaba 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -45,7 +45,7 @@
  * const char *path;
  *
  * setup_check();
- * git_check_attr(path, check);
+ * git_check_attr(&the_index, path, check);
  * ------------
  *
  * - Act on `.value` member of the result, left in `check->items[]`:
@@ -134,6 +134,12 @@ struct git_attr;
 struct all_attrs_item;
 struct attr_stack;
 
+/*
+ * The textual object name for the tree-ish used by git_check_attr()
+ * to read attributes from (instead of from the working tree).
+ */
+void set_git_attr_source(const char *);
+
 /*
  * Given a string, return the gitattribute object that
  * corresponds to it.
@@ -202,7 +208,8 @@ void attr_check_free(struct attr_check *check);
 const char *git_attr_name(const struct git_attr *);
 
 void git_check_attr(struct index_state *istate,
-                   const char *path, struct attr_check *check);
+                   const char *path,
+                   struct attr_check *check);
 
 /*
  * Retrieve all attributes that apply to the specified path.
@@ -220,4 +227,15 @@ void git_attr_set_direction(enum git_attr_direction new_direction);
 
 void attr_start(void);
 
+/* Return the system gitattributes file. */
+const char *git_attr_system_file(void);
+
+/* Return the global gitattributes file, if any. */
+const char *git_attr_global_file(void);
+
+/* Return whether the system gitattributes file is enabled and should be used. */
+int git_attr_system_is_enabled(void);
+
+extern const char *git_attr_tree;
+
 #endif /* ATTR_H */
index 6ccf46bc197e8cce014421f803e07e1f8f9d4963..44e76bd90af76938f04f7e7b0df8818d4bb81e61 100644 (file)
--- a/banned.h
+++ b/banned.h
 #define strncpy(x,y,n) BANNED(strncpy)
 #undef strncat
 #define strncat(x,y,n) BANNED(strncat)
+#undef strtok
+#define strtok(x,y) BANNED(strtok)
+#undef strtok_r
+#define strtok_r(x,y,z) BANNED(strtok_r)
 
 #undef sprintf
 #undef vsprintf
index 5ca601ee14fd2ab3b78577aa22a5db778bc7fbe0..bbacdca31b3bc3c1020d49e8e0c4c539a1232cbc 100644 (file)
--- a/base85.c
+++ b/base85.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "base85.h"
 
 #undef DEBUG_85
 
diff --git a/base85.h b/base85.h
new file mode 100644 (file)
index 0000000..c835086
--- /dev/null
+++ b/base85.h
@@ -0,0 +1,7 @@
+#ifndef BASE85_H
+#define BASE85_H
+
+int decode_85(char *dst, const char *line, int linelen);
+void encode_85(char *buf, const unsigned char *data, int bytes);
+
+#endif /* BASE85_H */
index fd581b85a72cc6d1f9b447893f9a3b345a8ce9da..f1273c787d9667d81215ba0dcd32ede3319138ea 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -1,12 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "revision.h"
 #include "refs.h"
 #include "list-objects.h"
 #include "quote.h"
-#include "hash-lookup.h"
 #include "run-command.h"
 #include "log-tree.h"
 #include "bisect.h"
@@ -14,7 +16,9 @@
 #include "strvec.h"
 #include "commit-slab.h"
 #include "commit-reach.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "dir.h"
 
 static struct oid_array good_revs;
@@ -22,8 +26,6 @@ static struct oid_array skipped_revs;
 
 static struct object_id *current_bad_oid;
 
-static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
-
 static const char *term_bad;
 static const char *term_good;
 
@@ -150,8 +152,9 @@ static void show_list(const char *debug, int counted, int nr,
                unsigned commit_flags = commit->object.flags;
                enum object_type type;
                unsigned long size;
-               char *buf = read_object_file(&commit->object.oid, &type,
-                                            &size);
+               char *buf = repo_read_object_file(the_repository,
+                                                 &commit->object.oid, &type,
+                                                 &size);
                const char *subject_start;
                int subject_len;
 
@@ -467,14 +470,12 @@ static int read_bisect_refs(void)
 }
 
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
-static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
 static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
-static GIT_PATH_FUNC(git_path_head_name, "head-name")
 
 static void read_bisect_paths(struct strvec *array)
 {
@@ -704,45 +705,31 @@ static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
 
 static int is_expected_rev(const struct object_id *oid)
 {
-       const char *filename = git_path_bisect_expected_rev();
-       struct stat st;
-       struct strbuf str = STRBUF_INIT;
-       FILE *fp;
-       int res = 0;
-
-       if (stat(filename, &st) || !S_ISREG(st.st_mode))
+       struct object_id expected_oid;
+       if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
                return 0;
-
-       fp = fopen_or_warn(filename, "r");
-       if (!fp)
-               return 0;
-
-       if (strbuf_getline_lf(&str, fp) != EOF)
-               res = !strcmp(str.buf, oid_to_hex(oid));
-
-       strbuf_release(&str);
-       fclose(fp);
-
-       return res;
+       return oideq(oid, &expected_oid);
 }
 
 enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
                                  int no_checkout)
 {
-       char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
        struct commit *commit;
        struct pretty_print_context pp = {0};
        struct strbuf commit_msg = STRBUF_INIT;
 
-       oid_to_hex_r(bisect_rev_hex, bisect_rev);
        update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 
-       argv_checkout[2] = bisect_rev_hex;
        if (no_checkout) {
                update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
        } else {
-               if (run_command_v_opt(argv_checkout, RUN_GIT_CMD))
+               struct child_process cmd = CHILD_PROCESS_INIT;
+
+               cmd.git_cmd = 1;
+               strvec_pushl(&cmd.args, "checkout", "-q",
+                            oid_to_hex(bisect_rev), "--", NULL);
+               if (run_command(&cmd))
                        /*
                         * Errors in `run_command()` itself, signaled by res < 0,
                         * and errors in the child process, signaled by res > 0
@@ -752,7 +739,8 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
        }
 
        commit = lookup_commit_reference(the_repository, bisect_rev);
-       format_commit_message(commit, "[%H] %s%n", &commit_msg, &pp);
+       repo_format_commit_message(the_repository, commit, "[%H] %s%n",
+                                  &commit_msg, &pp);
        fputs(commit_msg.buf, stdout);
        strbuf_release(&commit_msg);
 
@@ -847,7 +835,8 @@ static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int
        enum bisect_error res = BISECT_OK;
        struct commit_list *result;
 
-       result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
+       result = repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1,
+                                          rev + 1);
 
        for (; result; result = result->next) {
                const struct object_id *mb = &result->item->object.oid;
@@ -1178,18 +1167,16 @@ int bisect_clean_state(void)
        struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
        for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
        string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
+       string_list_append(&refs_for_removal, xstrdup("BISECT_EXPECTED_REV"));
        result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
        refs_for_removal.strdup_strings = 1;
        string_list_clear(&refs_for_removal, 0);
-       unlink_or_warn(git_path_bisect_expected_rev());
        unlink_or_warn(git_path_bisect_ancestors_ok());
        unlink_or_warn(git_path_bisect_log());
        unlink_or_warn(git_path_bisect_names());
        unlink_or_warn(git_path_bisect_run());
        unlink_or_warn(git_path_bisect_terms());
        unlink_or_warn(git_path_bisect_first_parent());
-       /* Cleanup head-name if it got left by an old version of git-bisect */
-       unlink_or_warn(git_path_head_name());
        /*
         * Cleanup BISECT_START last to support the --no-checkout option
         * introduced in the commit 4796e823a.
diff --git a/blame.c b/blame.c
index 8bfeaa1c63aedc151b1125e98f52229842d48b19..1a16d4eb6a59148325190f8152e13410398177ba 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -1,11 +1,20 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "cache-tree.h"
 #include "mergesort.h"
+#include "commit.h"
+#include "convert.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "gettext.h"
+#include "hex.h"
+#include "path.h"
+#include "read-cache.h"
+#include "revision.h"
+#include "setup.h"
 #include "tag.h"
+#include "trace2.h"
 #include "blame.h"
 #include "alloc.h"
 #include "commit-slab.h"
@@ -176,12 +185,12 @@ static void set_commit_buffer_from_strbuf(struct repository *r,
 static struct commit *fake_working_tree_commit(struct repository *r,
                                               struct diff_options *opt,
                                               const char *path,
-                                              const char *contents_from)
+                                              const char *contents_from,
+                                              struct object_id *oid)
 {
        struct commit *commit;
        struct blame_origin *origin;
        struct commit_list **parent_tail, *parent;
-       struct object_id head_oid;
        struct strbuf buf = STRBUF_INIT;
        const char *ident;
        time_t now;
@@ -197,17 +206,18 @@ static struct commit *fake_working_tree_commit(struct repository *r,
        commit->date = now;
        parent_tail = &commit->parents;
 
-       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-               die("no such ref: HEAD");
-
-       parent_tail = append_parent(r, parent_tail, &head_oid);
+       parent_tail = append_parent(r, parent_tail, oid);
        append_merge_parents(r, parent_tail);
        verify_working_tree_path(r, commit, path);
 
        origin = make_origin(commit, path);
 
-       ident = fmt_ident("Not Committed Yet", "not.committed.yet",
-                       WANT_BLANK_IDENT, NULL, 0);
+       if (contents_from)
+               ident = fmt_ident("External file (--contents)", "external.file",
+                                 WANT_BLANK_IDENT, NULL, 0);
+       else
+               ident = fmt_ident("Not Committed Yet", "not.committed.yet",
+                                 WANT_BLANK_IDENT, NULL, 0);
        strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
        for (parent = commit->parents; parent; parent = parent->next)
                strbuf_addf(&msg, "parent %s\n",
@@ -1028,8 +1038,9 @@ static void fill_origin_blob(struct diff_options *opt,
                                    &o->blob_oid, 1, &file->ptr, &file_size))
                        ;
                else
-                       file->ptr = read_object_file(&o->blob_oid, &type,
-                                                    &file_size);
+                       file->ptr = repo_read_object_file(the_repository,
+                                                         &o->blob_oid, &type,
+                                                         &file_size);
                file->size = file_size;
 
                if (!file->ptr)
@@ -2429,7 +2440,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,
 
                        if (sg_origin[i])
                                continue;
-                       if (parse_commit(p))
+                       if (repo_parse_commit(the_repository, p))
                                continue;
                        porigin = find(sb->repo, p, origin, sb->bloom_data);
                        if (!porigin)
@@ -2592,7 +2603,7 @@ void assign_blame(struct blame_scoreboard *sb, int opt)
                 * so hold onto it in the meantime.
                 */
                blame_origin_incref(suspect);
-               parse_commit(commit);
+               repo_parse_commit(the_repository, commit);
                if (sb->reverse ||
                    (!(commit->object.flags & UNINTERESTING) &&
                     !(revs->max_age != -1 && commit->date < revs->max_age)))
@@ -2771,22 +2782,39 @@ void setup_scoreboard(struct blame_scoreboard *sb,
                sb->commits.compare = compare_commits_by_reverse_commit_date;
        }
 
-       if (sb->final && sb->contents_from)
-               die(_("cannot use --contents with final commit object name"));
-
        if (sb->reverse && sb->revs->first_parent_only)
                sb->revs->children.name = NULL;
 
-       if (!sb->final) {
+       if (sb->contents_from || !sb->final) {
+               struct object_id head_oid, *parent_oid;
+
                /*
-                * "--not A B -- path" without anything positive;
-                * do not default to HEAD, but use the working tree
-                * or "--contents".
+                * Build a fake commit at the top of the history, when
+                * (1) "git blame [^A] --path", i.e. with no positive end
+                *     of the history range, in which case we build such
+                *     a fake commit on top of the HEAD to blame in-tree
+                *     modifications.
+                * (2) "git blame --contents=file [A] -- path", with or
+                *     without positive end of the history range but with
+                *     --contents, in which case we pretend that there is
+                *     a fake commit on top of the positive end (defaulting to
+                *     HEAD) that has the given contents in the path.
                 */
-               setup_work_tree();
+               if (sb->final) {
+                       parent_oid = &sb->final->object.oid;
+               } else {
+                       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+                               die("no such ref: HEAD");
+                       parent_oid = &head_oid;
+               }
+
+               if (!sb->contents_from)
+                       setup_work_tree();
+
                sb->final = fake_working_tree_commit(sb->repo,
                                                     &sb->revs->diffopt,
-                                                    sb->path, sb->contents_from);
+                                                    sb->path, sb->contents_from,
+                                                    parent_oid);
                add_pending_object(sb->revs, &(sb->final->object), ":");
        }
 
@@ -2838,8 +2866,10 @@ void setup_scoreboard(struct blame_scoreboard *sb,
                                    &sb->final_buf_size))
                        ;
                else
-                       sb->final_buf = read_object_file(&o->blob_oid, &type,
-                                                        &sb->final_buf_size);
+                       sb->final_buf = repo_read_object_file(the_repository,
+                                                             &o->blob_oid,
+                                                             &type,
+                                                             &sb->final_buf_size);
 
                if (!sb->final_buf)
                        die(_("cannot read blob %s for path %s"),
diff --git a/blame.h b/blame.h
index 38bde535b3d46461d0619bc68c852210693de571..5b4e47d44c613e31fc4788c12ee9d8dc2a49d571 100644 (file)
--- a/blame.h
+++ b/blame.h
@@ -1,12 +1,9 @@
 #ifndef BLAME_H
 #define BLAME_H
 
-#include "cache.h"
-#include "commit.h"
+#include "oidset.h"
 #include "xdiff-interface.h"
-#include "revision.h"
 #include "prio-queue.h"
-#include "diff.h"
 
 #define PICKAXE_BLAME_MOVE             01
 #define PICKAXE_BLAME_COPY             02
diff --git a/blob.c b/blob.c
index 182718aba9fe320f1064a12fb5baf1969f563b29..3fb2922b1ae65a552101d625a806e914b99eb339 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -1,6 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "blob.h"
-#include "repository.h"
 #include "alloc.h"
 
 const char *blob_type = "blob";
@@ -13,8 +12,7 @@ struct blob *lookup_blob(struct repository *r, const struct object_id *oid)
        return object_as_type(obj, OBJ_BLOB, 0);
 }
 
-int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
+void parse_blob_buffer(struct blob *item)
 {
        item->object.parsed = 1;
-       return 0;
 }
diff --git a/blob.h b/blob.h
index 1664872055783557e1836dcc64bc2f51c20c5b4f..74555c90c449ffa89226d264f46c433580707f61 100644 (file)
--- a/blob.h
+++ b/blob.h
@@ -11,8 +11,6 @@ struct blob {
 
 struct blob *lookup_blob(struct repository *r, const struct object_id *oid);
 
-int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
-
 /**
  * Blobs do not contain references to other objects and do not have
  * structured data that needs parsing. However, code may use the
@@ -21,5 +19,6 @@ int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
  * parse_blob_buffer() is used (by object.c) to flag that the object
  * has been read successfully from the database.
  **/
+void parse_blob_buffer(struct blob *item);
 
 #endif /* BLOB_H */
diff --git a/bloom.c b/bloom.c
index d0730525da51f783e0f72f502a62db4407bd7ad9..e529f7605ca14d3fcb779412fc22e1e20d45ac20 100644 (file)
--- a/bloom.c
+++ b/bloom.c
@@ -2,10 +2,10 @@
 #include "bloom.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "revision.h"
 #include "hashmap.h"
 #include "commit-graph.h"
 #include "commit.h"
+#include "commit-slab.h"
 
 define_commit_slab(bloom_filter_slab, struct bloom_filter);
 
@@ -28,6 +28,26 @@ static inline unsigned char get_bitmask(uint32_t pos)
        return ((unsigned char)1) << (pos & (BITS_PER_WORD - 1));
 }
 
+static int check_bloom_offset(struct commit_graph *g, uint32_t pos,
+                             uint32_t offset)
+{
+       /*
+        * Note that we allow offsets equal to the data size, which would set
+        * our pointers at one past the end of the chunk memory. This is
+        * necessary because the on-disk index points to the end of the
+        * entries (so we can compute size by comparing adjacent ones). And
+        * naturally the final entry's end is one-past-the-end of the chunk.
+        */
+       if (offset <= g->chunk_bloom_data_size - BLOOMDATA_CHUNK_HEADER_SIZE)
+               return 0;
+
+       warning("ignoring out-of-range offset (%"PRIuMAX") for changed-path"
+               " filter at pos %"PRIuMAX" of %s (chunk size: %"PRIuMAX")",
+               (uintmax_t)offset, (uintmax_t)pos,
+               g->filename, (uintmax_t)g->chunk_bloom_data_size);
+       return -1;
+}
+
 static int load_bloom_filter_from_graph(struct commit_graph *g,
                                        struct bloom_filter *filter,
                                        uint32_t graph_pos)
@@ -50,6 +70,20 @@ static int load_bloom_filter_from_graph(struct commit_graph *g,
        else
                start_index = 0;
 
+       if (check_bloom_offset(g, lex_pos, end_index) < 0 ||
+           check_bloom_offset(g, lex_pos - 1, start_index) < 0)
+               return 0;
+
+       if (end_index < start_index) {
+               warning("ignoring decreasing changed-path index offsets"
+                       " (%"PRIuMAX" > %"PRIuMAX") for positions"
+                       " %"PRIuMAX" and %"PRIuMAX" of %s",
+                       (uintmax_t)start_index, (uintmax_t)end_index,
+                       (uintmax_t)(lex_pos-1), (uintmax_t)lex_pos,
+                       g->filename);
+               return 0;
+       }
+
        filter->len = end_index - start_index;
        filter->data = (unsigned char *)(g->chunk_bloom_data +
                                        sizeof(unsigned char) * start_index +
index d182756827fe5128292798b707a52aed25e7aa48..534594f7f8006d11ebb9b8d10712b6fc6191a043 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -1,10 +1,16 @@
 #include "git-compat-util.h"
-#include "cache.h"
+#include "advice.h"
 #include "config.h"
 #include "branch.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "path.h"
 #include "refs.h"
 #include "refspec.h"
 #include "remote.h"
+#include "repository.h"
 #include "sequencer.h"
 #include "commit.h"
 #include "worktree.h"
@@ -32,7 +38,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
        if (!remote_find_tracking(remote, &tracking->spec)) {
                switch (++tracking->matches) {
                case 1:
-                       string_list_append(tracking->srcs, tracking->spec.src);
+                       string_list_append_nodup(tracking->srcs, tracking->spec.src);
                        tracking->remote = remote->name;
                        break;
                case 2:
@@ -228,7 +234,7 @@ static int inherit_tracking(struct tracking *tracking, const char *orig_ref)
                return -1;
        }
 
-       tracking->remote = xstrdup(branch->remote_name);
+       tracking->remote = branch->remote_name;
        for (i = 0; i < branch->merge_nr; i++)
                string_list_append(tracking->srcs, branch->merge_name[i]);
        return 0;
@@ -328,7 +334,7 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
                if (!skip_prefix(tracking.srcs->items[0].string,
                                 "refs/heads/", &tracked_branch) ||
                    strcmp(tracked_branch, new_ref))
-                       return;
+                       goto cleanup;
        }
 
        if (tracking.srcs->nr < 1)
@@ -414,9 +420,9 @@ static void prepare_checked_out_branches(void)
                wt_status_state_free_buffers(&state);
 
                if (wt_status_check_bisect(wt, &state) &&
-                   state.branch) {
+                   state.bisecting_from) {
                        struct strbuf ref = STRBUF_INIT;
-                       strbuf_addf(&ref, "refs/heads/%s", state.branch);
+                       strbuf_addf(&ref, "refs/heads/%s", state.bisecting_from);
                        old = strmap_put(&current_checked_out_branches,
                                         ref.buf,
                                         xstrdup(wt->path));
@@ -465,7 +471,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force)
 
        if ((path = branch_checked_out(ref->buf)))
                die(_("cannot force update the branch '%s' "
-                     "checked out at '%s'"),
+                     "used by worktree at '%s'"),
                    ref->buf + strlen("refs/heads/"), path);
 
        return 1;
@@ -475,9 +481,12 @@ static int check_tracking_branch(struct remote *remote, void *cb_data)
 {
        char *tracking_branch = cb_data;
        struct refspec_item query;
+       int res;
        memset(&query, 0, sizeof(struct refspec_item));
        query.dst = tracking_branch;
-       return !remote_find_tracking(remote, &query);
+       res = !remote_find_tracking(remote, &query);
+       free(query.src);
+       return res;
 }
 
 static int validate_remote_tracking_branch(char *ref)
@@ -531,7 +540,7 @@ static void dwim_branch_start(struct repository *r, const char *start_name,
                explicit_tracking = 1;
 
        real_ref = NULL;
-       if (get_oid_mb(start_name, &oid)) {
+       if (repo_get_oid_mb(r, start_name, &oid)) {
                if (explicit_tracking) {
                        int code = die_message(_(upstream_missing), start_name);
                        advise_if_enabled(ADVICE_SET_UPSTREAM_FAILURE,
@@ -541,7 +550,8 @@ static void dwim_branch_start(struct repository *r, const char *start_name,
                die(_("not a valid object name: '%s'"), start_name);
        }
 
-       switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) {
+       switch (repo_dwim_ref(r, start_name, strlen(start_name), &oid,
+                             &real_ref, 0)) {
        case 0:
                /* Not branching from any existing branch */
                if (explicit_tracking)
@@ -632,9 +642,10 @@ void dwim_and_setup_tracking(struct repository *r, const char *new_ref,
                             const char *orig_ref, enum branch_track track,
                             int quiet)
 {
-       char *real_orig_ref;
+       char *real_orig_ref = NULL;
        dwim_branch_start(r, orig_ref, track, &real_orig_ref, NULL);
        setup_tracking(new_ref, real_orig_ref, track, quiet);
+       free(real_orig_ref);
 }
 
 /**
@@ -756,7 +767,7 @@ void create_branches_recursively(struct repository *r, const char *name,
                                _("submodule '%s': unable to find submodule"),
                                submodule_entry_list.entries[i].submodule->name);
                        if (advice_enabled(ADVICE_SUBMODULES_NOT_UPDATED))
-                               advise(_("You may try updating the submodules using 'git checkout %s && git submodule update --init'"),
+                               advise(_("You may try updating the submodules using 'git checkout --no-recurse-submodules %s && git submodule update --init'"),
                                       start_commitish);
                        exit(code);
                }
@@ -772,7 +783,7 @@ void create_branches_recursively(struct repository *r, const char *name,
                            name);
        }
 
-       create_branch(the_repository, name, start_commitish, force, 0, reflog, quiet,
+       create_branch(r, name, start_commitish, force, 0, reflog, quiet,
                      BRANCH_TRACK_NEVER, dry_run);
        if (dry_run)
                return;
@@ -820,40 +831,17 @@ void remove_branch_state(struct repository *r, int verbose)
 void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
        struct worktree **worktrees = get_worktrees();
-       const struct worktree *wt;
-
-       wt = find_shared_symref(worktrees, "HEAD", branch);
-       if (wt && (!ignore_current_worktree || !wt->is_current)) {
-               skip_prefix(branch, "refs/heads/", &branch);
-               die(_("'%s' is already checked out at '%s'"), branch, wt->path);
-       }
-
-       free_worktrees(worktrees);
-}
-
-int replace_each_worktree_head_symref(const char *oldref, const char *newref,
-                                     const char *logmsg)
-{
-       int ret = 0;
-       struct worktree **worktrees = get_worktrees();
-       int i;
-
-       for (i = 0; worktrees[i]; i++) {
-               struct ref_store *refs;
 
-               if (worktrees[i]->is_detached)
-                       continue;
-               if (!worktrees[i]->head_ref)
-                       continue;
-               if (strcmp(oldref, worktrees[i]->head_ref))
+       for (int i = 0; worktrees[i]; i++) {
+               if (worktrees[i]->is_current && ignore_current_worktree)
                        continue;
 
-               refs = get_worktree_ref_store(worktrees[i]);
-               if (refs_create_symref(refs, "HEAD", newref, logmsg))
-                       ret = error(_("HEAD of working tree %s is not updated"),
-                                   worktrees[i]->path);
+               if (is_shared_symref(worktrees[i], "HEAD", branch)) {
+                       skip_prefix(branch, "refs/heads/", &branch);
+                       die(_("'%s' is already used by worktree at '%s'"),
+                               branch, worktrees[i]->path);
+               }
        }
 
        free_worktrees(worktrees);
-       return ret;
 }
index ef56103c050fa09d6087e2bade7f24240d79ae04..30c01aed73d7b7751b87cfb4c5cd55387c8b8bb9 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -155,12 +155,4 @@ int read_branch_desc(struct strbuf *, const char *branch_name);
  */
 void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
-/*
- * Update all per-worktree HEADs pointing at the old ref to point the new ref.
- * This will be used when renaming a branch. Returns 0 if successful, non-zero
- * otherwise.
- */
-int replace_each_worktree_head_symref(const char *oldref, const char *newref,
-                                     const char *logmsg);
-
 #endif
index 8901a34d6bf424680b9d13a1bdf332bedb4d8e20..28280636da8f52deabea4472d52f574ecb3a4945 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -2,9 +2,6 @@
 #define BUILTIN_H
 
 #include "git-compat-util.h"
-#include "strbuf.h"
-#include "cache.h"
-#include "commit.h"
 
 /*
  * builtin API
  *     on bare repositories.
  *     This only makes sense when `RUN_SETUP` is also set.
  *
- * `SUPPORT_SUPER_PREFIX`:
- *
- *     The built-in supports `--super-prefix`.
- *
  * `DELAY_PAGER_CONFIG`:
  *
  *     If RUN_SETUP or RUN_SETUP_GENTLY is set, git.c normally handles
@@ -111,12 +104,22 @@ void setup_auto_pager(const char *cmd, int def);
 
 int is_builtin(const char *s);
 
+/*
+ * Builtins which do not use RUN_SETUP should never see
+ * a prefix that is not empty; use this to protect downstream
+ * code which is not prepared to call prefix_filename(), etc.
+ */
+#define BUG_ON_NON_EMPTY_PREFIX(prefix) do { \
+       if ((prefix)) \
+               BUG("unexpected prefix in builtin: %s", (prefix)); \
+} while (0)
+
 int cmd_add(int argc, const char **argv, const char *prefix);
 int cmd_am(int argc, const char **argv, const char *prefix);
 int cmd_annotate(int argc, const char **argv, const char *prefix);
 int cmd_apply(int argc, const char **argv, const char *prefix);
 int cmd_archive(int argc, const char **argv, const char *prefix);
-int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
+int cmd_bisect(int argc, const char **argv, const char *prefix);
 int cmd_blame(int argc, const char **argv, const char *prefix);
 int cmd_branch(int argc, const char **argv, const char *prefix);
 int cmd_bugreport(int argc, const char **argv, const char *prefix);
@@ -208,6 +211,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix);
 int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 int cmd_repack(int argc, const char **argv, const char *prefix);
+int cmd_replay(int argc, const char **argv, const char *prefix);
 int cmd_rerere(int argc, const char **argv, const char *prefix);
 int cmd_reset(int argc, const char **argv, const char *prefix);
 int cmd_restore(int argc, const char **argv, const char *prefix);
index f84372964c8c486601941c927d6e39bb8da42084..ada7719561f0ec324238e613a9788a5f5a2981eb 100644 (file)
@@ -3,19 +3,22 @@
  *
  * Copyright (C) 2006 Linus Torvalds
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "advice.h"
+#include "config.h"
 #include "lockfile.h"
+#include "editor.h"
 #include "dir.h"
+#include "gettext.h"
 #include "pathspec.h"
-#include "exec-cmd.h"
-#include "cache-tree.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
 #include "diff.h"
-#include "diffcore.h"
+#include "read-cache.h"
+#include "repository.h"
 #include "revision.h"
 #include "bulk-checkin.h"
 #include "strvec.h"
@@ -33,17 +36,12 @@ static int pathspec_file_nul;
 static int include_sparse;
 static const char *pathspec_from_file;
 
-struct update_callback_data {
-       int flags;
-       int add_errors;
-};
-
 static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
 {
        int i, ret = 0;
 
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < the_index.cache_nr; i++) {
+               struct cache_entry *ce = the_index.cache[i];
                int err;
 
                if (!include_sparse &&
@@ -55,7 +53,7 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
                        continue;
 
                if (!show_only)
-                       err = chmod_cache_entry(ce, flip);
+                       err = chmod_index_entry(&the_index, ce, flip);
                else
                        err = S_ISREG(ce->ce_mode) ? 0 : -1;
 
@@ -66,101 +64,12 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
        return ret;
 }
 
-static int fix_unmerged_status(struct diff_filepair *p,
-                              struct update_callback_data *data)
-{
-       if (p->status != DIFF_STATUS_UNMERGED)
-               return p->status;
-       if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
-               /*
-                * This is not an explicit add request, and the
-                * path is missing from the working tree (deleted)
-                */
-               return DIFF_STATUS_DELETED;
-       else
-               /*
-                * Either an explicit add request, or path exists
-                * in the working tree.  An attempt to explicitly
-                * add a path that does not exist in the working tree
-                * will be caught as an error by the caller immediately.
-                */
-               return DIFF_STATUS_MODIFIED;
-}
-
-static void update_callback(struct diff_queue_struct *q,
-                           struct diff_options *opt, void *cbdata)
-{
-       int i;
-       struct update_callback_data *data = cbdata;
-
-       for (i = 0; i < q->nr; i++) {
-               struct diff_filepair *p = q->queue[i];
-               const char *path = p->one->path;
-
-               if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
-                       continue;
-
-               switch (fix_unmerged_status(p, data)) {
-               default:
-                       die(_("unexpected diff status %c"), p->status);
-               case DIFF_STATUS_MODIFIED:
-               case DIFF_STATUS_TYPE_CHANGED:
-                       if (add_file_to_index(&the_index, path, data->flags)) {
-                               if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
-                                       die(_("updating files failed"));
-                               data->add_errors++;
-                       }
-                       break;
-               case DIFF_STATUS_DELETED:
-                       if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
-                               break;
-                       if (!(data->flags & ADD_CACHE_PRETEND))
-                               remove_file_from_index(&the_index, path);
-                       if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
-                               printf(_("remove '%s'\n"), path);
-                       break;
-               }
-       }
-}
-
-int add_files_to_cache(const char *prefix,
-                      const struct pathspec *pathspec, int flags)
-{
-       struct update_callback_data data;
-       struct rev_info rev;
-
-       memset(&data, 0, sizeof(data));
-       data.flags = flags;
-
-       repo_init_revisions(the_repository, &rev, prefix);
-       setup_revisions(0, NULL, &rev, NULL);
-       if (pathspec)
-               copy_pathspec(&rev.prune_data, pathspec);
-       rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
-       rev.diffopt.format_callback = update_callback;
-       rev.diffopt.format_callback_data = &data;
-       rev.diffopt.flags.override_submodule_config = 1;
-       rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
-
-       /*
-        * Use an ODB transaction to optimize adding multiple objects.
-        * This function is invoked from commands other than 'add', which
-        * may not have their own transaction active.
-        */
-       begin_odb_transaction();
-       run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-       end_odb_transaction();
-
-       release_revisions(&rev);
-       return !!data.add_errors;
-}
-
 static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
 {
        int i, retval = 0;
 
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < the_index.cache_nr; i++) {
+               struct cache_entry *ce = the_index.cache[i];
 
                if (!include_sparse &&
                    (ce_skip_worktree(ce) ||
@@ -172,7 +81,8 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
                        continue; /* do not touch non blobs */
                if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
                        continue;
-               retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE);
+               retval |= add_file_to_index(&the_index, ce->name,
+                                           flags | ADD_CACHE_RENORMALIZE);
        }
 
        return retval;
@@ -237,59 +147,14 @@ static int refresh(int verbose, const struct pathspec *pathspec)
        return ret;
 }
 
-int run_add_interactive(const char *revision, const char *patch_mode,
-                       const struct pathspec *pathspec)
-{
-       int status, i;
-       struct strvec argv = STRVEC_INIT;
-       int use_builtin_add_i =
-               git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
-
-       if (use_builtin_add_i < 0 &&
-           git_config_get_bool("add.interactive.usebuiltin",
-                               &use_builtin_add_i))
-               use_builtin_add_i = 1;
-
-       if (use_builtin_add_i != 0) {
-               enum add_p_mode mode;
-
-               if (!patch_mode)
-                       return !!run_add_i(the_repository, pathspec);
-
-               if (!strcmp(patch_mode, "--patch"))
-                       mode = ADD_P_ADD;
-               else if (!strcmp(patch_mode, "--patch=stash"))
-                       mode = ADD_P_STASH;
-               else if (!strcmp(patch_mode, "--patch=reset"))
-                       mode = ADD_P_RESET;
-               else if (!strcmp(patch_mode, "--patch=checkout"))
-                       mode = ADD_P_CHECKOUT;
-               else if (!strcmp(patch_mode, "--patch=worktree"))
-                       mode = ADD_P_WORKTREE;
-               else
-                       die("'%s' not supported", patch_mode);
-
-               return !!run_add_p(the_repository, mode, revision, pathspec);
-       }
-
-       strvec_push(&argv, "add--interactive");
-       if (patch_mode)
-               strvec_push(&argv, patch_mode);
-       if (revision)
-               strvec_push(&argv, revision);
-       strvec_push(&argv, "--");
-       for (i = 0; i < pathspec->nr; i++)
-               /* pass original pathspec, to be re-parsed */
-               strvec_push(&argv, pathspec->items[i].original);
-
-       status = run_command_v_opt(argv.v, RUN_GIT_CMD);
-       strvec_clear(&argv);
-       return status;
-}
-
 int interactive_add(const char **argv, const char *prefix, int patch)
 {
        struct pathspec pathspec;
+       int unused;
+
+       if (!git_config_get_bool("add.interactive.usebuiltin", &unused))
+               warning(_("the add.interactive.useBuiltin setting has been removed!\n"
+                         "See its entry in 'git help config' for details."));
 
        parse_pathspec(&pathspec, 0,
                       PATHSPEC_PREFER_FULL |
@@ -297,9 +162,10 @@ int interactive_add(const char **argv, const char *prefix, int patch)
                       PATHSPEC_PREFIX_ORIGIN,
                       prefix, argv);
 
-       return run_add_interactive(NULL,
-                                  patch ? "--patch" : NULL,
-                                  &pathspec);
+       if (patch)
+               return !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec);
+       else
+               return !!run_add_i(the_repository, &pathspec);
 }
 
 static int edit_patch(int argc, const char **argv, const char *prefix)
@@ -312,8 +178,8 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 
-       if (read_cache() < 0)
-               die(_("Could not read the index"));
+       if (repo_read_index(the_repository) < 0)
+               die(_("could not read the index"));
 
        repo_init_revisions(the_repository, &rev, prefix);
        rev.diffopt.context = 7;
@@ -325,22 +191,21 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
        out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
        rev.diffopt.file = xfdopen(out, "w");
        rev.diffopt.close_file = 1;
-       if (run_diff_files(&rev, 0))
-               die(_("Could not write patch"));
+       run_diff_files(&rev, 0);
 
        if (launch_editor(file, NULL, NULL))
                die(_("editing patch failed"));
 
        if (stat(file, &st))
-               die_errno(_("Could not stat '%s'"), file);
+               die_errno(_("could not stat '%s'"), file);
        if (!st.st_size)
-               die(_("Empty patch. Aborted."));
+               die(_("empty patch. aborted"));
 
        child.git_cmd = 1;
        strvec_pushl(&child.args, "apply", "--recount", "--cached", file,
                     NULL);
        if (run_command(&child))
-               die(_("Could not apply '%s'"), file);
+               die(_("could not apply '%s'"), file);
 
        unlink(file);
        free(file);
@@ -363,6 +228,8 @@ static char *chmod_arg;
 
 static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
 {
+       BUG_ON_OPT_ARG(arg);
+
        /* if we are told to ignore, we are not adding removals */
        *(int *)opt->value = !unset ? 0 : 1;
        return 0;
@@ -397,7 +264,8 @@ static struct option builtin_add_options[] = {
        OPT_END(),
 };
 
-static int add_config(const char *var, const char *value, void *cb)
+static int add_config(const char *var, const char *value,
+                     const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "add.ignoreerrors") ||
            !strcmp(var, "add.ignore-errors")) {
@@ -405,7 +273,10 @@ static int add_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       if (git_color_config(var, value, cb) < 0)
+               return -1;
+
+       return git_default_config(var, value, ctx, cb);
 }
 
 static const char embedded_advice[] = N_(
@@ -544,13 +415,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
        /*
         * Check the "pathspec '%s' did not match any files" block
         * below before enabling new magic.
         */
-       parse_pathspec(&pathspec, PATHSPEC_ATTR,
+       parse_pathspec(&pathspec, 0,
                       PATHSPEC_PREFER_FULL |
                       PATHSPEC_SYMLINK_LEADING_PATH,
                       prefix, argv);
@@ -559,7 +430,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                if (pathspec.nr)
                        die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
-               parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
+               parse_pathspec_file(&pathspec, 0,
                                    PATHSPEC_PREFER_FULL |
                                    PATHSPEC_SYMLINK_LEADING_PATH,
                                    prefix, pathspec_from_file, pathspec_file_nul);
@@ -587,7 +458,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                 (!(addremove || take_worktree_changes)
                  ? ADD_CACHE_IGNORE_REMOVAL : 0));
 
-       if (read_cache_preload(&pathspec) < 0)
+       if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
                die(_("index file corrupt"));
 
        die_in_unpopulated_submodule(&the_index, prefix);
@@ -630,7 +501,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                               PATHSPEC_LITERAL |
                               PATHSPEC_GLOB |
                               PATHSPEC_ICASE |
-                              PATHSPEC_EXCLUDE);
+                              PATHSPEC_EXCLUDE |
+                              PATHSPEC_ATTR);
 
                for (i = 0; i < pathspec.nr; i++) {
                        const char *path = pathspec.items[i].match;
@@ -680,7 +552,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        if (add_renormalize)
                exit_status |= renormalize_tracked_files(&pathspec, flags);
        else
-               exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+               exit_status |= add_files_to_cache(the_repository, prefix,
+                                                 &pathspec, include_sparse,
+                                                 flags);
 
        if (add_new_files)
                exit_status |= add_files(&dir, flags);
@@ -692,9 +566,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 finish:
        if (write_locked_index(&the_index, &lock_file,
                               COMMIT_LOCK | SKIP_IF_UNCHANGED))
-               die(_("Unable to write new index file"));
+               die(_("unable to write new index file"));
 
        dir_clear(&dir);
-       UNLEAK(pathspec);
+       clear_pathspec(&pathspec);
        return exit_status;
 }
index 39fea24833078be76d76d974e5b601715ed51aea..d1990d7edcbe37467c59a420a9b03a5894e6da93 100644 (file)
@@ -3,11 +3,15 @@
  *
  * Based on git-am.sh by Junio C Hamano.
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "exec-cmd.h"
+#include "abspath.h"
+#include "advice.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
 #include "dir.h"
 #include "run-command.h"
 #include "refs.h"
 #include "commit.h"
 #include "diff.h"
-#include "diffcore.h"
 #include "unpack-trees.h"
 #include "branch.h"
+#include "object-name.h"
+#include "preload-index.h"
 #include "sequencer.h"
 #include "revision.h"
 #include "merge-recursive.h"
 #include "log-tree.h"
 #include "notes-utils.h"
 #include "rerere.h"
-#include "prompt.h"
 #include "mailinfo.h"
 #include "apply.h"
 #include "string-list.h"
-#include "packfile.h"
+#include "pager.h"
+#include "path.h"
 #include "repository.h"
 #include "pretty.h"
 
@@ -83,9 +88,16 @@ enum signoff_type {
        SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
 };
 
-enum show_patch_type {
-       SHOW_PATCH_RAW = 0,
-       SHOW_PATCH_DIFF = 1,
+enum resume_type {
+       RESUME_FALSE = 0,
+       RESUME_APPLY,
+       RESUME_RESOLVED,
+       RESUME_SKIP,
+       RESUME_ABORT,
+       RESUME_QUIT,
+       RESUME_SHOW_PATCH_RAW,
+       RESUME_SHOW_PATCH_DIFF,
+       RESUME_ALLOW_EMPTY,
 };
 
 enum empty_action {
@@ -117,6 +129,7 @@ struct am_state {
 
        /* various operating modes and command line options */
        int interactive;
+       int no_verify;
        int threeway;
        int quiet;
        int signoff; /* enum signoff_type */
@@ -472,10 +485,12 @@ static void am_destroy(const struct am_state *state)
  */
 static int run_applypatch_msg_hook(struct am_state *state)
 {
-       int ret;
+       int ret = 0;
 
        assert(state->msg);
-       ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
+
+       if (!state->no_verify)
+               ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
 
        if (!ret) {
                FREE_AND_NULL(state->msg);
@@ -492,24 +507,12 @@ static int run_applypatch_msg_hook(struct am_state *state)
  */
 static int run_post_rewrite_hook(const struct am_state *state)
 {
-       struct child_process cp = CHILD_PROCESS_INIT;
-       const char *hook = find_hook("post-rewrite");
-       int ret;
-
-       if (!hook)
-               return 0;
-
-       strvec_push(&cp.args, hook);
-       strvec_push(&cp.args, "rebase");
+       struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
 
-       cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
-       cp.stdout_to_stderr = 1;
-       cp.trace2_hook_name = "post-rewrite";
+       strvec_push(&opt.args, "rebase");
+       opt.path_to_stdin = am_path(state, "rewritten");
 
-       ret = run_command(&cp);
-
-       close(cp.in);
-       return ret;
+       return run_hooks_opt("post-rewrite", &opt);
 }
 
 /**
@@ -786,7 +789,7 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
  * A split_mail_conv() callback that converts an StGit patch to an RFC2822
  * message suitable for parsing with git-mailinfo.
  */
-static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr UNUSED)
 {
        struct strbuf sb = STRBUF_INIT;
        int subject_printed = 0;
@@ -869,7 +872,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
  * A split_patches_conv() callback that converts a mercurial patch to a RFC2822
  * message suitable for parsing with git-mailinfo.
  */
-static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr UNUSED)
 {
        struct strbuf sb = STRBUF_INIT;
        int rc = 0;
@@ -1075,7 +1078,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
        else
                write_state_text(state, "applying", "");
 
-       if (!get_oid("HEAD", &curr_head)) {
+       if (!repo_get_oid(the_repository, "HEAD", &curr_head)) {
                write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
                if (!state->rebasing)
                        update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
@@ -1118,7 +1121,7 @@ static void am_next(struct am_state *state)
        unlink(am_path(state, "original-commit"));
        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 
-       if (!get_oid("HEAD", &head))
+       if (!repo_get_oid(the_repository, "HEAD", &head))
                write_state_text(state, "abort-safety", oid_to_hex(&head));
        else
                write_state_text(state, "abort-safety", "");
@@ -1283,7 +1286,7 @@ static int parse_mail(struct am_state *state, const char *mail)
 
        strbuf_addstr(&msg, "\n\n");
        strbuf_addbuf(&msg, &mi.log_message);
-       strbuf_stripspace(&msg, 0);
+       strbuf_stripspace(&msg, '\0');
 
        assert(!state->author_name);
        state->author_name = strbuf_detach(&author_name, NULL);
@@ -1338,7 +1341,8 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
        size_t ident_len;
        struct ident_split id;
 
-       buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
+       buffer = repo_logmsg_reencode(the_repository, commit, NULL,
+                                     get_commit_output_encoding());
 
        ident_line = find_commit_header(buffer, "author", &ident_len);
        if (!ident_line)
@@ -1370,7 +1374,7 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
                die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
        state->msg = xstrdup(msg + 2);
        state->msg_len = strlen(state->msg);
-       unuse_commit_buffer(commit, buffer);
+       repo_unuse_commit_buffer(the_repository, commit, buffer);
 }
 
 /**
@@ -1411,9 +1415,9 @@ static void write_index_patch(const struct am_state *state)
        struct rev_info rev_info;
        FILE *fp;
 
-       if (!get_oid("HEAD", &head)) {
+       if (!repo_get_oid(the_repository, "HEAD", &head)) {
                struct commit *commit = lookup_commit_or_die(&head, "HEAD");
-               tree = get_commit_tree(commit);
+               tree = repo_get_commit_tree(the_repository, commit);
        } else
                tree = lookup_tree(the_repository,
                                   the_repository->hash_algo->empty_tree);
@@ -1429,7 +1433,7 @@ static void write_index_patch(const struct am_state *state)
        rev_info.diffopt.close_file = 1;
        add_pending_object(&rev_info, &tree->object, "");
        diff_setup_done(&rev_info.diffopt);
-       run_diff_index(&rev_info, 1);
+       run_diff_index(&rev_info, DIFF_INDEX_CACHED);
        release_revisions(&rev_info);
 }
 
@@ -1476,6 +1480,7 @@ static int run_apply(const struct am_state *state, const char *index_file)
        int res, opts_left;
        int force_apply = 0;
        int options = 0;
+       const char **apply_argv;
 
        if (init_apply_state(&apply_state, the_repository, NULL))
                BUG("init_apply_state() failed");
@@ -1483,7 +1488,14 @@ static int run_apply(const struct am_state *state, const char *index_file)
        strvec_push(&apply_opts, "apply");
        strvec_pushv(&apply_opts, state->git_apply_opts.v);
 
-       opts_left = apply_parse_options(apply_opts.nr, apply_opts.v,
+       /*
+        * Build a copy that apply_parse_options() can rearrange.
+        * apply_opts.v keeps referencing the allocated strings for
+        * strvec_clear() to release.
+        */
+       DUP_ARRAY(apply_argv, apply_opts.v, apply_opts.nr);
+
+       opts_left = apply_parse_options(apply_opts.nr, apply_argv,
                                        &apply_state, &force_apply, &options,
                                        NULL);
 
@@ -1513,14 +1525,15 @@ static int run_apply(const struct am_state *state, const char *index_file)
        strvec_clear(&apply_paths);
        strvec_clear(&apply_opts);
        clear_apply_state(&apply_state);
+       free(apply_argv);
 
        if (res)
                return res;
 
        if (index_file) {
                /* Reload index as apply_all_patches() will have modified it. */
-               discard_cache();
-               read_cache_from(index_file);
+               discard_index(&the_index);
+               read_index_from(&the_index, index_file, get_git_dir());
        }
 
        return 0;
@@ -1556,14 +1569,14 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
        struct commit *result;
        char *their_tree_name;
 
-       if (get_oid("HEAD", &our_tree) < 0)
+       if (repo_get_oid(the_repository, "HEAD", &our_tree) < 0)
                oidcpy(&our_tree, the_hash_algo->empty_tree);
 
        if (build_fake_ancestor(state, index_path))
                return error("could not build fake ancestor");
 
-       discard_cache();
-       read_cache_from(index_path);
+       discard_index(&the_index);
+       read_index_from(&the_index, index_path, get_git_dir());
 
        if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
                return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
@@ -1583,7 +1596,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
                rev_info.diffopt.filter |= diff_filter_bit('M');
                add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
                diff_setup_done(&rev_info.diffopt);
-               run_diff_index(&rev_info, 1);
+               run_diff_index(&rev_info, DIFF_INDEX_CACHED);
                release_revisions(&rev_info);
        }
 
@@ -1596,8 +1609,8 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 
        say(state, stdout, _("Falling back to patching base and 3-way merge..."));
 
-       discard_cache();
-       read_cache();
+       discard_index(&the_index);
+       repo_read_index(the_repository);
 
        /*
         * This is not so wrong. Depending on which base we picked, orig_tree
@@ -1640,13 +1653,13 @@ static void do_commit(const struct am_state *state)
        const char *reflog_msg, *author, *committer = NULL;
        struct strbuf sb = STRBUF_INIT;
 
-       if (run_hooks("pre-applypatch"))
+       if (!state->no_verify && run_hooks("pre-applypatch"))
                exit(1);
 
-       if (write_cache_as_tree(&tree, 0, NULL))
+       if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
                die(_("git write-tree failed to write a tree"));
 
-       if (!get_oid_commit("HEAD", &parent)) {
+       if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) {
                old_oid = &parent;
                commit_list_insert(lookup_commit(the_repository, &parent),
                                   &parents);
@@ -1781,7 +1794,8 @@ static void am_run(struct am_state *state, int resume)
 
        unlink(am_path(state, "dirtyindex"));
 
-       if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0)
+       if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
+                                        NULL, NULL, NULL) < 0)
                die(_("unable to write index file"));
 
        if (repo_index_has_changes(the_repository, NULL, &sb)) {
@@ -1930,7 +1944,7 @@ static void am_resolve(struct am_state *state, int allow_empty)
                }
        }
 
-       if (unmerged_cache()) {
+       if (unmerged_index(&the_index)) {
                printf_ln(_("You still have unmerged paths in your index.\n"
                        "You should 'git add' each file with resolved conflicts to mark them as such.\n"
                        "You might run `git rm` on a file to accept \"deleted by them\" for it."));
@@ -1967,9 +1981,9 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
        if (parse_tree(head) || parse_tree(remote))
                return -1;
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
-       refresh_cache(REFRESH_QUIET);
+       refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
 
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
@@ -2007,7 +2021,7 @@ static int merge_tree(struct tree *tree)
        if (parse_tree(tree))
                return -1;
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
@@ -2045,12 +2059,12 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
        if (!remote_tree)
                return error(_("Could not parse object '%s'."), oid_to_hex(remote));
 
-       read_cache_unmerged();
+       repo_read_index_unmerged(the_repository);
 
        if (fast_forward_to(head_tree, head_tree, 1))
                return -1;
 
-       if (write_cache_as_tree(&index, 0, NULL))
+       if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
                return -1;
 
        index_tree = parse_tree_indirect(&index);
@@ -2087,7 +2101,7 @@ static void am_skip(struct am_state *state)
 
        am_rerere_clear();
 
-       if (get_oid("HEAD", &head))
+       if (repo_get_oid(the_repository, "HEAD", &head))
                oidcpy(&head, the_hash_algo->empty_tree);
 
        if (clean_index(&head, &head))
@@ -2129,7 +2143,7 @@ static int safe_to_abort(const struct am_state *state)
                oidclr(&abort_safety);
        strbuf_release(&sb);
 
-       if (get_oid("HEAD", &head))
+       if (repo_get_oid(the_repository, "HEAD", &head))
                oidclr(&head);
 
        if (oideq(&head, &abort_safety))
@@ -2162,7 +2176,7 @@ static void am_abort(struct am_state *state)
        if (!has_curr_head)
                oidcpy(&curr_head, the_hash_algo->empty_tree);
 
-       has_orig_head = !get_oid("ORIG_HEAD", &orig_head);
+       has_orig_head = !repo_get_oid(the_repository, "ORIG_HEAD", &orig_head);
        if (!has_orig_head)
                oidcpy(&orig_head, the_hash_algo->empty_tree);
 
@@ -2180,28 +2194,26 @@ static void am_abort(struct am_state *state)
        am_destroy(state);
 }
 
-static int show_patch(struct am_state *state, enum show_patch_type sub_mode)
+static int show_patch(struct am_state *state, enum resume_type resume_mode)
 {
        struct strbuf sb = STRBUF_INIT;
        const char *patch_path;
        int len;
 
        if (!is_null_oid(&state->orig_commit)) {
-               const char *av[4] = { "show", NULL, "--", NULL };
-               char *new_oid_str;
-               int ret;
+               struct child_process cmd = CHILD_PROCESS_INIT;
 
-               av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));
-               ret = run_command_v_opt(av, RUN_GIT_CMD);
-               free(new_oid_str);
-               return ret;
+               strvec_pushl(&cmd.args, "show", oid_to_hex(&state->orig_commit),
+                            "--", NULL);
+               cmd.git_cmd = 1;
+               return run_command(&cmd);
        }
 
-       switch (sub_mode) {
-       case SHOW_PATCH_RAW:
+       switch (resume_mode) {
+       case RESUME_SHOW_PATCH_RAW:
                patch_path = am_path(state, msgnum(state));
                break;
-       case SHOW_PATCH_DIFF:
+       case RESUME_SHOW_PATCH_DIFF:
                patch_path = am_path(state, "patch");
                break;
        default:
@@ -2248,77 +2260,35 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int
        return 0;
 }
 
-enum resume_type {
-       RESUME_FALSE = 0,
-       RESUME_APPLY,
-       RESUME_RESOLVED,
-       RESUME_SKIP,
-       RESUME_ABORT,
-       RESUME_QUIT,
-       RESUME_SHOW_PATCH,
-       RESUME_ALLOW_EMPTY,
-};
-
-struct resume_mode {
-       enum resume_type mode;
-       enum show_patch_type sub_mode;
-};
-
 static int parse_opt_show_current_patch(const struct option *opt, const char *arg, int unset)
 {
        int *opt_value = opt->value;
-       struct resume_mode *resume = container_of(opt_value, struct resume_mode, mode);
 
+       BUG_ON_OPT_NEG(unset);
+
+       if (!arg)
+               *opt_value = opt->defval;
+       else if (!strcmp(arg, "raw"))
+               *opt_value = RESUME_SHOW_PATCH_RAW;
+       else if (!strcmp(arg, "diff"))
+               *opt_value = RESUME_SHOW_PATCH_DIFF;
        /*
         * Please update $__git_showcurrentpatch in git-completion.bash
         * when you add new options
         */
-       const char *valid_modes[] = {
-               [SHOW_PATCH_DIFF] = "diff",
-               [SHOW_PATCH_RAW] = "raw"
-       };
-       int new_value = SHOW_PATCH_RAW;
-
-       BUG_ON_OPT_NEG(unset);
-
-       if (arg) {
-               for (new_value = 0; new_value < ARRAY_SIZE(valid_modes); new_value++) {
-                       if (!strcmp(arg, valid_modes[new_value]))
-                               break;
-               }
-               if (new_value >= ARRAY_SIZE(valid_modes))
-                       return error(_("invalid value for '%s': '%s'"),
-                                    "--show-current-patch", arg);
-       }
-
-       if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)
-               return error(_("options '%s=%s' and '%s=%s' "
-                                          "cannot be used together"),
-                                        "--show-current-patch", "--show-current-patch", arg, valid_modes[resume->sub_mode]);
-
-       resume->mode = RESUME_SHOW_PATCH;
-       resume->sub_mode = new_value;
+       else
+               return error(_("invalid value for '%s': '%s'"),
+                            "--show-current-patch", arg);
        return 0;
 }
 
-static int git_am_config(const char *k, const char *v, void *cb UNUSED)
-{
-       int status;
-
-       status = git_gpg_config(k, v, NULL);
-       if (status)
-               return status;
-
-       return git_default_config(k, v, NULL);
-}
-
 int cmd_am(int argc, const char **argv, const char *prefix)
 {
        struct am_state state;
        int binary = -1;
        int keep_cr = -1;
        int patch_format = PATCH_FORMAT_UNKNOWN;
-       struct resume_mode resume = { .mode = RESUME_FALSE };
+       enum resume_type resume_mode = RESUME_FALSE;
        int in_progress;
        int ret = 0;
 
@@ -2331,6 +2301,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_BOOL('i', "interactive", &state.interactive,
                        N_("run interactively")),
+               OPT_BOOL('n', "no-verify", &state.no_verify,
+                       N_("bypass pre-applypatch and applypatch-msg hooks")),
                OPT_HIDDEN_BOOL('b', "binary", &binary,
                        N_("historical option -- no-op")),
                OPT_BOOL('3', "3way", &state.threeway,
@@ -2347,12 +2319,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                        N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),
                OPT_BOOL('m', "message-id", &state.message_id,
                        N_("pass -m flag to git-mailinfo")),
-               OPT_SET_INT_F(0, "keep-cr", &keep_cr,
-                       N_("pass --keep-cr flag to git-mailsplit for mbox format"),
-                       1, PARSE_OPT_NONEG),
-               OPT_SET_INT_F(0, "no-keep-cr", &keep_cr,
-                       N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
-                       0, PARSE_OPT_NONEG),
+               OPT_SET_INT(0, "keep-cr", &keep_cr,
+                           N_("pass --keep-cr flag to git-mailsplit for mbox format"),
+                           1),
                OPT_BOOL('c', "scissors", &state.scissors,
                        N_("strip everything before a scissors line")),
                OPT_CALLBACK_F(0, "quoted-cr", &state.quoted_cr, N_("action"),
@@ -2390,27 +2359,27 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                        PARSE_OPT_NOARG),
                OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
                        N_("override error message when patch failure occurs")),
-               OPT_CMDMODE(0, "continue", &resume.mode,
+               OPT_CMDMODE(0, "continue", &resume_mode,
                        N_("continue applying patches after resolving a conflict"),
                        RESUME_RESOLVED),
-               OPT_CMDMODE('r', "resolved", &resume.mode,
+               OPT_CMDMODE('r', "resolved", &resume_mode,
                        N_("synonyms for --continue"),
                        RESUME_RESOLVED),
-               OPT_CMDMODE(0, "skip", &resume.mode,
+               OPT_CMDMODE(0, "skip", &resume_mode,
                        N_("skip the current patch"),
                        RESUME_SKIP),
-               OPT_CMDMODE(0, "abort", &resume.mode,
+               OPT_CMDMODE(0, "abort", &resume_mode,
                        N_("restore the original branch and abort the patching operation"),
                        RESUME_ABORT),
-               OPT_CMDMODE(0, "quit", &resume.mode,
+               OPT_CMDMODE(0, "quit", &resume_mode,
                        N_("abort the patching operation but keep HEAD where it is"),
                        RESUME_QUIT),
-               { OPTION_CALLBACK, 0, "show-current-patch", &resume.mode,
+               { OPTION_CALLBACK, 0, "show-current-patch", &resume_mode,
                  "(diff|raw)",
                  N_("show the patch being applied"),
                  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
-                 parse_opt_show_current_patch, RESUME_SHOW_PATCH },
-               OPT_CMDMODE(0, "allow-empty", &resume.mode,
+                 parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW },
+               OPT_CMDMODE(0, "allow-empty", &resume_mode,
                        N_("record the empty patch as an empty commit"),
                        RESUME_ALLOW_EMPTY),
                OPT_BOOL(0, "committer-date-is-author-date",
@@ -2422,7 +2391,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
                  N_("GPG-sign commits"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-               OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+               OPT_CALLBACK_F(0, "empty", &state.empty_type, "(stop|drop|keep)",
                  N_("how to handle empty patches"),
                  PARSE_OPT_NONEG, am_option_parse_empty),
                OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
@@ -2433,7 +2402,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(usage, options);
 
-       git_config(git_am_config, NULL);
+       git_config(git_default_config, NULL);
 
        am_state_init(&state);
 
@@ -2465,12 +2434,12 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                 *    intend to feed us a patch but wanted to continue
                 *    unattended.
                 */
-               if (argc || (resume.mode == RESUME_FALSE && !isatty(0)))
+               if (argc || (resume_mode == RESUME_FALSE && !isatty(0)))
                        die(_("previous rebase directory %s still exists but mbox given."),
                                state.dir);
 
-               if (resume.mode == RESUME_FALSE)
-                       resume.mode = RESUME_APPLY;
+               if (resume_mode == RESUME_FALSE)
+                       resume_mode = RESUME_APPLY;
 
                if (state.signoff == SIGNOFF_EXPLICIT)
                        am_append_signoff(&state);
@@ -2484,7 +2453,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                 * stray directories.
                 */
                if (file_exists(state.dir) && !state.rebasing) {
-                       if (resume.mode == RESUME_ABORT || resume.mode == RESUME_QUIT) {
+                       if (resume_mode == RESUME_ABORT || resume_mode == RESUME_QUIT) {
                                am_destroy(&state);
                                am_state_release(&state);
                                return 0;
@@ -2495,7 +2464,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                                state.dir);
                }
 
-               if (resume.mode)
+               if (resume_mode)
                        die(_("Resolve operation not in progress, we are not resuming."));
 
                for (i = 0; i < argc; i++) {
@@ -2513,7 +2482,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                strvec_clear(&paths);
        }
 
-       switch (resume.mode) {
+       switch (resume_mode) {
        case RESUME_FALSE:
                am_run(&state, 0);
                break;
@@ -2522,7 +2491,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                break;
        case RESUME_RESOLVED:
        case RESUME_ALLOW_EMPTY:
-               am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
+               am_resolve(&state, resume_mode == RESUME_ALLOW_EMPTY ? 1 : 0);
                break;
        case RESUME_SKIP:
                am_skip(&state);
@@ -2534,8 +2503,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                am_rerere_clear();
                am_destroy(&state);
                break;
-       case RESUME_SHOW_PATCH:
-               ret = show_patch(&state, resume.sub_mode);
+       case RESUME_SHOW_PATCH_RAW:
+       case RESUME_SHOW_PATCH_DIFF:
+               ret = show_patch(&state, resume_mode);
                break;
        default:
                BUG("invalid resume value");
index 555219de40fa7e3097612a60eb953f81580a8de9..861a01910ca20bcb9bd18279ebd10f9d94032fe1 100644 (file)
@@ -1,6 +1,6 @@
-#include "cache.h"
 #include "builtin.h"
-#include "parse-options.h"
+#include "gettext.h"
+#include "repository.h"
 #include "apply.h"
 
 static const char * const apply_usage[] = {
index f094390ee01f810e7035f2efc2be75deedc3befa..15ee1ec7bb765fe6307dc3f17cde238242169cc4 100644 (file)
@@ -2,13 +2,13 @@
  * Copyright (c) 2006 Franck Bui-Huu
  * Copyright (c) 2006 Rene Scharfe
  */
-#include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "gettext.h"
 #include "transport.h"
 #include "parse-options.h"
 #include "pkt-line.h"
-#include "sideband.h"
+#include "repository.h"
 
 static void create_output_file(const char *output_file)
 {
@@ -81,7 +81,7 @@ static int run_remote_archiver(int argc, const char **argv,
 int cmd_archive(int argc, const char **argv, const char *prefix)
 {
        const char *exec = "git-upload-archive";
-       const char *output = NULL;
+       char *output = NULL;
        const char *remote = NULL;
        struct option local_opts[] = {
                OPT_FILENAME('o', "output", &output,
@@ -106,5 +106,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
 
        setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
 
+       UNLEAK(output);
        return write_archive(argc, argv, prefix, the_repository, output, 0);
 }
similarity index 78%
rename from builtin/bisect--helper.c
rename to builtin/bisect.c
index 28ef7ec2a48856b1c13f1cdd31546985148752f0..f69c3f7e43df4623be83509b7c9070d69072dc2a 100644 (file)
@@ -1,37 +1,62 @@
 #include "builtin.h"
-#include "cache.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "bisect.h"
 #include "refs.h"
-#include "dir.h"
 #include "strvec.h"
 #include "run-command.h"
+#include "oid-array.h"
+#include "path.h"
 #include "prompt.h"
 #include "quote.h"
 #include "revision.h"
 
 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
-static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
-static GIT_PATH_FUNC(git_path_head_name, "head-name")
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
 static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
 static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
 
-static const char * const git_bisect_helper_usage[] = {
-       N_("git bisect--helper --bisect-reset [<commit>]"),
-       "git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]",
-       N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
-                                           " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
-       "git bisect--helper --bisect-next",
-       N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
-       N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
-       N_("git bisect--helper --bisect-replay <filename>"),
-       N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
-       "git bisect--helper --bisect-visualize",
-       N_("git bisect--helper --bisect-run <cmd>..."),
+#define BUILTIN_GIT_BISECT_START_USAGE \
+       N_("git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]" \
+          "    [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \
+          "    [<pathspec>...]")
+#define BUILTIN_GIT_BISECT_STATE_USAGE \
+       N_("git bisect (good|bad) [<rev>...]")
+#define BUILTIN_GIT_BISECT_TERMS_USAGE \
+       "git bisect terms [--term-good | --term-bad]"
+#define BUILTIN_GIT_BISECT_SKIP_USAGE \
+       N_("git bisect skip [(<rev>|<range>)...]")
+#define BUILTIN_GIT_BISECT_NEXT_USAGE \
+       "git bisect next"
+#define BUILTIN_GIT_BISECT_RESET_USAGE \
+       N_("git bisect reset [<commit>]")
+#define BUILTIN_GIT_BISECT_VISUALIZE_USAGE \
+       "git bisect visualize"
+#define BUILTIN_GIT_BISECT_REPLAY_USAGE \
+       N_("git bisect replay <logfile>")
+#define BUILTIN_GIT_BISECT_LOG_USAGE \
+       "git bisect log"
+#define BUILTIN_GIT_BISECT_RUN_USAGE \
+       N_("git bisect run <cmd> [<arg>...]")
+
+static const char * const git_bisect_usage[] = {
+       BUILTIN_GIT_BISECT_START_USAGE,
+       BUILTIN_GIT_BISECT_STATE_USAGE,
+       BUILTIN_GIT_BISECT_TERMS_USAGE,
+       BUILTIN_GIT_BISECT_SKIP_USAGE,
+       BUILTIN_GIT_BISECT_NEXT_USAGE,
+       BUILTIN_GIT_BISECT_RESET_USAGE,
+       BUILTIN_GIT_BISECT_VISUALIZE_USAGE,
+       BUILTIN_GIT_BISECT_REPLAY_USAGE,
+       BUILTIN_GIT_BISECT_LOG_USAGE,
+       BUILTIN_GIT_BISECT_RUN_USAGE,
        NULL
 };
 
@@ -206,32 +231,31 @@ static int bisect_reset(const char *commit)
        struct strbuf branch = STRBUF_INIT;
 
        if (!commit) {
-               if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) {
+               if (!strbuf_read_file(&branch, git_path_bisect_start(), 0))
                        printf(_("We are not bisecting.\n"));
-                       return 0;
-               }
-               strbuf_rtrim(&branch);
+               else
+                       strbuf_rtrim(&branch);
        } else {
                struct object_id oid;
 
-               if (get_oid_commit(commit, &oid))
+               if (repo_get_oid_commit(the_repository, commit, &oid))
                        return error(_("'%s' is not a valid commit"), commit);
                strbuf_addstr(&branch, commit);
        }
 
-       if (!ref_exists("BISECT_HEAD")) {
-               struct strvec argv = STRVEC_INIT;
+       if (branch.len && !ref_exists("BISECT_HEAD")) {
+               struct child_process cmd = CHILD_PROCESS_INIT;
 
-               strvec_pushl(&argv, "checkout", branch.buf, "--", NULL);
-               if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
+               cmd.git_cmd = 1;
+               strvec_pushl(&cmd.args, "checkout", "--ignore-other-worktrees",
+                               branch.buf, "--", NULL);
+               if (run_command(&cmd)) {
                        error(_("could not check out original"
                                " HEAD '%s'. Try 'git bisect"
                                " reset <commit>'."), branch.buf);
                        strbuf_release(&branch);
-                       strvec_clear(&argv);
                        return -1;
                }
-               strvec_clear(&argv);
        }
 
        strbuf_release(&branch);
@@ -245,7 +269,8 @@ static void log_commit(FILE *fp, char *fmt, const char *state,
        struct strbuf commit_msg = STRBUF_INIT;
        char *label = xstrfmt(fmt, state);
 
-       format_commit_message(commit, "%s", &commit_msg, &pp);
+       repo_format_commit_message(the_repository, commit, "%s", &commit_msg,
+                                  &pp);
 
        fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid),
                commit_msg.buf);
@@ -272,7 +297,7 @@ static int bisect_write(const char *state, const char *rev,
                goto finish;
        }
 
-       if (get_oid(rev, &oid)) {
+       if (repo_get_oid(the_repository, rev, &oid)) {
                res = error(_("couldn't get the oid of the rev '%s'"), rev);
                goto finish;
        }
@@ -547,7 +572,7 @@ static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
         * sets up a revision walk.
         */
        reset_revision_walk();
-       init_revisions(revs, NULL);
+       repo_init_revisions(the_repository, revs, NULL);
        setup_revisions(0, NULL, revs, NULL);
        for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
        cb.object_flags = UNINTERESTING;
@@ -583,8 +608,8 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
 
        while ((commit = get_revision(&revs)) != NULL) {
                strbuf_reset(&commit_name);
-               format_commit_message(commit, "%s",
-                                     &commit_name, &pp);
+               repo_format_commit_message(the_repository, commit, "%s",
+                                          &commit_name, &pp);
                fprintf(fp, "# possible first %s commit: [%s] %s\n",
                        terms->term_bad, oid_to_hex(&commit->object.oid),
                        commit_name.buf);
@@ -613,7 +638,8 @@ static int bisect_successful(struct bisect_terms *terms)
 
        read_ref(bad_ref, &oid);
        commit = lookup_commit_reference_by_name(bad_ref);
-       format_commit_message(commit, "%s", &commit_name, &pp);
+       repo_format_commit_message(the_repository, commit, "%s", &commit_name,
+                                  &pp);
 
        res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n",
                            terms->term_bad, oid_to_hex(&commit->object.oid),
@@ -657,7 +683,8 @@ static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char
        return bisect_next(terms, prefix);
 }
 
-static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
+                                     const char **argv)
 {
        int no_checkout = 0;
        int first_parent_only = 0;
@@ -754,7 +781,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
         */
        head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
        if (!head)
-               if (get_oid("HEAD", &head_oid))
+               if (repo_get_oid(the_repository, "HEAD", &head_oid))
                        return error(_("bad HEAD - I need a HEAD"));
 
        /*
@@ -765,10 +792,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
                strbuf_read_file(&start_head, git_path_bisect_start(), 0);
                strbuf_trim(&start_head);
                if (!no_checkout) {
-                       const char *argv[] = { "checkout", start_head.buf,
-                                              "--", NULL };
+                       struct child_process cmd = CHILD_PROCESS_INIT;
 
-                       if (run_command_v_opt(argv, RUN_GIT_CMD)) {
+                       cmd.git_cmd = 1;
+                       strvec_pushl(&cmd.args, "checkout", start_head.buf,
+                                    "--", NULL);
+                       if (run_command(&cmd)) {
                                res = error(_("checking out '%s' failed."
                                                 " Try 'git bisect start "
                                                 "<valid-branch>'."),
@@ -778,19 +807,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
                }
        } else {
                /* Get the rev from where we start. */
-               if (!get_oid(head, &head_oid) &&
+               if (!repo_get_oid(the_repository, head, &head_oid) &&
                    !starts_with(head, "refs/heads/")) {
                        strbuf_reset(&start_head);
                        strbuf_addstr(&start_head, oid_to_hex(&head_oid));
-               } else if (!get_oid(head, &head_oid) &&
+               } else if (!repo_get_oid(the_repository, head, &head_oid) &&
                           skip_prefix(head, "refs/heads/", &head)) {
-                       /*
-                        * This error message should only be triggered by
-                        * cogito usage, and cogito users should understand
-                        * it relates to cg-seek.
-                        */
-                       if (!is_empty_or_missing_file(git_path_head_name()))
-                               return error(_("won't bisect on cg-seek'ed tree"));
                        strbuf_addstr(&start_head, head);
                } else {
                        return error(_("bad HEAD - strange symbolic ref"));
@@ -812,7 +834,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
                write_file(git_path_bisect_first_parent(), "\n");
 
        if (no_checkout) {
-               if (get_oid(start_head.buf, &oid) < 0) {
+               if (repo_get_oid(the_repository, start_head.buf, &oid) < 0) {
                        res = error(_("invalid ref: '%s'"), start_head.buf);
                        goto finish;
                }
@@ -885,18 +907,17 @@ static int bisect_autostart(struct bisect_terms *terms)
        yesno = git_prompt(_("Do you want me to do it for you "
                             "[Y/n]? "), PROMPT_ECHO);
        res = tolower(*yesno) == 'n' ?
-               -1 : bisect_start(terms, empty_strvec, 0);
+               -1 : bisect_start(terms, 0, empty_strvec);
 
        return res;
 }
 
-static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,
-                                     int argc)
+static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
+                                     const char **argv)
 {
        const char *state;
        int i, verify_expected = 1;
        struct object_id oid, expected;
-       struct strbuf buf = STRBUF_INIT;
        struct oid_array revs = OID_ARRAY_INIT;
 
        if (!argc)
@@ -917,11 +938,12 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a
 
        if (argc == 0) {
                const char *head = "BISECT_HEAD";
-               enum get_oid_result res_head = get_oid(head, &oid);
+               enum get_oid_result res_head = repo_get_oid(the_repository,
+                                                           head, &oid);
 
                if (res_head == MISSING_OBJECT) {
                        head = "HEAD";
-                       res_head = get_oid(head, &oid);
+                       res_head = repo_get_oid(the_repository, head, &oid);
                }
 
                if (res_head)
@@ -937,7 +959,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a
        for (; argc; argc--, argv++) {
                struct commit *commit;
 
-               if (get_oid(*argv, &oid)){
+               if (repo_get_oid(the_repository, *argv, &oid)){
                        error(_("Bad rev input: %s"), *argv);
                        oid_array_clear(&revs);
                        return BISECT_FAILED;
@@ -950,10 +972,8 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a
                oid_array_append(&revs, &commit->object.oid);
        }
 
-       if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz ||
-           get_oid_hex(buf.buf, &expected) < 0)
+       if (read_ref("BISECT_EXPECTED_REV", &expected))
                verify_expected = 0; /* Ignore invalid file contents */
-       strbuf_release(&buf);
 
        for (i = 0; i < revs.nr; i++) {
                if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) {
@@ -962,7 +982,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a
                }
                if (verify_expected && !oideq(&revs.oid[i], &expected)) {
                        unlink_or_warn(git_path_bisect_ancestors_ok());
-                       unlink_or_warn(git_path_bisect_expected_rev());
+                       delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF);
                        verify_expected = 0;
                }
        }
@@ -1010,7 +1030,7 @@ static int process_replay_line(struct bisect_terms *terms, struct strbuf *line)
                struct strvec argv = STRVEC_INIT;
                int res;
                sq_dequote_to_strvec(rev, &argv);
-               res = bisect_start(terms, argv.v, argv.nr);
+               res = bisect_start(terms, argv.nr, argv.v);
                strvec_clear(&argv);
                return res;
        }
@@ -1060,7 +1080,8 @@ static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *f
        return bisect_auto_next(terms, NULL);
 }
 
-static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_skip(struct bisect_terms *terms, int argc,
+                                    const char **argv)
 {
        int i;
        enum bisect_error res;
@@ -1075,7 +1096,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
                        struct rev_info revs;
                        struct commit *commit;
 
-                       init_revisions(&revs, NULL);
+                       repo_init_revisions(the_repository, &revs, NULL);
                        setup_revisions(2, argv + i - 1, &revs, NULL);
 
                        if (prepare_revision_walk(&revs))
@@ -1090,48 +1111,47 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
                        strvec_push(&argv_state, argv[i]);
                }
        }
-       res = bisect_state(terms, argv_state.v, argv_state.nr);
+       res = bisect_state(terms, argv_state.nr, argv_state.v);
 
        strvec_clear(&argv_state);
        return res;
 }
 
-static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)
+static int bisect_visualize(struct bisect_terms *terms, int argc,
+                           const char **argv)
 {
-       struct strvec args = STRVEC_INIT;
-       int flags = RUN_COMMAND_NO_STDIN, res = 0;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        struct strbuf sb = STRBUF_INIT;
 
        if (bisect_next_check(terms, NULL) != 0)
                return BISECT_FAILED;
 
+       cmd.no_stdin = 1;
        if (!argc) {
                if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") ||
                     getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) {
-                       strvec_push(&args, "gitk");
+                       strvec_push(&cmd.args, "gitk");
                } else {
-                       strvec_push(&args, "log");
-                       flags |= RUN_GIT_CMD;
+                       strvec_push(&cmd.args, "log");
+                       cmd.git_cmd = 1;
                }
        } else {
                if (argv[0][0] == '-') {
-                       strvec_push(&args, "log");
-                       flags |= RUN_GIT_CMD;
+                       strvec_push(&cmd.args, "log");
+                       cmd.git_cmd = 1;
                } else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git"))
-                       flags |= RUN_GIT_CMD;
+                       cmd.git_cmd = 1;
 
-               strvec_pushv(&args, argv);
+               strvec_pushv(&cmd.args, argv);
        }
 
-       strvec_pushl(&args, "--bisect", "--", NULL);
+       strvec_pushl(&cmd.args, "--bisect", "--", NULL);
 
        strbuf_read_file(&sb, git_path_bisect_names(), 0);
-       sq_dequote_to_strvec(sb.buf, &args);
+       sq_dequote_to_strvec(sb.buf, &cmd.args);
        strbuf_release(&sb);
 
-       res = run_command_v_opt(args.v, flags);
-       strvec_clear(&args);
-       return res;
+       return run_command(&cmd);
 }
 
 static int get_first_good(const char *refname UNUSED,
@@ -1142,8 +1162,17 @@ static int get_first_good(const char *refname UNUSED,
        return 1;
 }
 
-static int verify_good(const struct bisect_terms *terms,
-                      const char **quoted_argv)
+static int do_bisect_run(const char *command)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+
+       printf(_("running %s\n"), command);
+       cmd.use_shell = 1;
+       strvec_push(&cmd.args, command);
+       return run_command(&cmd);
+}
+
+static int verify_good(const struct bisect_terms *terms, const char *command)
 {
        int rc;
        enum bisect_error res;
@@ -1163,8 +1192,7 @@ static int verify_good(const struct bisect_terms *terms,
        if (res != BISECT_OK)
                return -1;
 
-       printf(_("running %s\n"), quoted_argv[0]);
-       rc = run_command_v_opt(quoted_argv, RUN_USING_SHELL);
+       rc = do_bisect_run(command);
 
        res = bisect_checkout(&current_rev, no_checkout);
        if (res != BISECT_OK)
@@ -1173,11 +1201,10 @@ static int verify_good(const struct bisect_terms *terms,
        return rc;
 }
 
-static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
+static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
 {
        int res = BISECT_OK;
        struct strbuf command = STRBUF_INIT;
-       struct strvec run_args = STRVEC_INIT;
        const char *new_state;
        int temporary_stdout_fd, saved_stdout;
        int is_first_run = 1;
@@ -1185,18 +1212,15 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
        if (bisect_next_check(terms, NULL))
                return BISECT_FAILED;
 
-       if (argc)
-               sq_quote_argv(&command, argv);
-       else {
+       if (!argc) {
                error(_("bisect run failed: no command provided."));
                return BISECT_FAILED;
        }
 
-       strvec_push(&run_args, command.buf);
-
+       sq_quote_argv(&command, argv);
+       strbuf_ltrim(&command);
        while (1) {
-               printf(_("running %s\n"), command.buf);
-               res = run_command_v_opt(run_args.v, RUN_USING_SHELL);
+               res = do_bisect_run(command.buf);
 
                /*
                 * Exit code 126 and 127 can either come from the shell
@@ -1206,10 +1230,10 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
                 * missing or non-executable script.
                 */
                if (is_first_run && (res == 126 || res == 127)) {
-                       int rc = verify_good(terms, run_args.v);
+                       int rc = verify_good(terms, command.buf);
                        is_first_run = 0;
-                       if (rc < 0) {
-                               error(_("unable to verify '%s' on good"
+                       if (rc < 0 || 128 <= rc) {
+                               error(_("unable to verify %s on good"
                                        " revision"), command.buf);
                                res = BISECT_FAILED;
                                break;
@@ -1224,7 +1248,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
 
                if (res < 0 || 128 <= res) {
                        error(_("bisect run failed: exit code %d from"
-                               " '%s' is < 0 or >= 128"), res, command.buf);
+                               " %s is < 0 or >= 128"), res, command.buf);
                        break;
                }
 
@@ -1246,7 +1270,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
                saved_stdout = dup(1);
                dup2(temporary_stdout_fd, 1);
 
-               res = bisect_state(terms, &new_state, 1);
+               res = bisect_state(terms, 1, &new_state);
 
                fflush(stdout);
                dup2(saved_stdout, 1);
@@ -1258,14 +1282,14 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
                if (res == BISECT_ONLY_SKIPPED_LEFT)
                        error(_("bisect run cannot continue any more"));
                else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {
-                       printf(_("bisect run success"));
+                       puts(_("bisect run success"));
                        res = BISECT_OK;
                } else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
-                       printf(_("bisect found first bad commit"));
+                       puts(_("bisect found first bad commit"));
                        res = BISECT_OK;
                } else if (res) {
-                       error(_("bisect run failed: 'git bisect--helper --bisect-state"
-                       " %s' exited with error code %d"), new_state, res);
+                       error(_("bisect run failed: 'git bisect %s'"
+                               " exited with error code %d"), new_state, res);
                } else {
                        continue;
                }
@@ -1273,126 +1297,147 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
        }
 
        strbuf_release(&command);
-       strvec_clear(&run_args);
        return res;
 }
 
-int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
+static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED)
+{
+       if (argc > 1)
+               return error(_("'%s' requires either no argument or a commit"),
+                            "git bisect reset");
+       return bisect_reset(argc ? argv[0] : NULL);
+}
+
+static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNUSED)
+{
+       int res;
+       struct bisect_terms terms = { 0 };
+
+       if (argc > 1)
+               return error(_("'%s' requires 0 or 1 argument"),
+                            "git bisect terms");
+       res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL);
+       free_terms(&terms);
+       return res;
+}
+
+static int cmd_bisect__start(int argc, const char **argv, const char *prefix UNUSED)
 {
-       enum {
-               BISECT_RESET = 1,
-               BISECT_NEXT_CHECK,
-               BISECT_TERMS,
-               BISECT_START,
-               BISECT_AUTOSTART,
-               BISECT_NEXT,
-               BISECT_STATE,
-               BISECT_LOG,
-               BISECT_REPLAY,
-               BISECT_SKIP,
-               BISECT_VISUALIZE,
-               BISECT_RUN,
-       } cmdmode = 0;
-       int res = 0, nolog = 0;
+       int res;
+       struct bisect_terms terms = { 0 };
+
+       set_terms(&terms, "bad", "good");
+       res = bisect_start(&terms, argc, argv);
+       free_terms(&terms);
+       return res;
+}
+
+static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *prefix)
+{
+       int res;
+       struct bisect_terms terms = { 0 };
+
+       if (argc)
+               return error(_("'%s' requires 0 arguments"),
+                            "git bisect next");
+       get_terms(&terms);
+       res = bisect_next(&terms, prefix);
+       free_terms(&terms);
+       return res;
+}
+
+static int cmd_bisect__log(int argc UNUSED, const char **argv UNUSED, const char *prefix UNUSED)
+{
+       return bisect_log();
+}
+
+static int cmd_bisect__replay(int argc, const char **argv, const char *prefix UNUSED)
+{
+       int res;
+       struct bisect_terms terms = { 0 };
+
+       if (argc != 1)
+               return error(_("no logfile given"));
+       set_terms(&terms, "bad", "good");
+       res = bisect_replay(&terms, argv[0]);
+       free_terms(&terms);
+       return res;
+}
+
+static int cmd_bisect__skip(int argc, const char **argv, const char *prefix UNUSED)
+{
+       int res;
+       struct bisect_terms terms = { 0 };
+
+       set_terms(&terms, "bad", "good");
+       get_terms(&terms);
+       res = bisect_skip(&terms, argc, argv);
+       free_terms(&terms);
+       return res;
+}
+
+static int cmd_bisect__visualize(int argc, const char **argv, const char *prefix UNUSED)
+{
+       int res;
+       struct bisect_terms terms = { 0 };
+
+       get_terms(&terms);
+       res = bisect_visualize(&terms, argc, argv);
+       free_terms(&terms);
+       return res;
+}
+
+static int cmd_bisect__run(int argc, const char **argv, const char *prefix UNUSED)
+{
+       int res;
+       struct bisect_terms terms = { 0 };
+
+       if (!argc)
+               return error(_("'%s' failed: no command provided."), "git bisect run");
+       get_terms(&terms);
+       res = bisect_run(&terms, argc, argv);
+       free_terms(&terms);
+       return res;
+}
+
+int cmd_bisect(int argc, const char **argv, const char *prefix)
+{
+       int res = 0;
+       parse_opt_subcommand_fn *fn = NULL;
        struct option options[] = {
-               OPT_CMDMODE(0, "bisect-reset", &cmdmode,
-                        N_("reset the bisection state"), BISECT_RESET),
-               OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
-                        N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
-               OPT_CMDMODE(0, "bisect-terms", &cmdmode,
-                        N_("print out the bisect terms"), BISECT_TERMS),
-               OPT_CMDMODE(0, "bisect-start", &cmdmode,
-                        N_("start the bisect session"), BISECT_START),
-               OPT_CMDMODE(0, "bisect-next", &cmdmode,
-                        N_("find the next bisection commit"), BISECT_NEXT),
-               OPT_CMDMODE(0, "bisect-state", &cmdmode,
-                        N_("mark the state of ref (or refs)"), BISECT_STATE),
-               OPT_CMDMODE(0, "bisect-log", &cmdmode,
-                        N_("list the bisection steps so far"), BISECT_LOG),
-               OPT_CMDMODE(0, "bisect-replay", &cmdmode,
-                        N_("replay the bisection process from the given file"), BISECT_REPLAY),
-               OPT_CMDMODE(0, "bisect-skip", &cmdmode,
-                        N_("skip some commits for checkout"), BISECT_SKIP),
-               OPT_CMDMODE(0, "bisect-visualize", &cmdmode,
-                        N_("visualize the bisection"), BISECT_VISUALIZE),
-               OPT_CMDMODE(0, "bisect-run", &cmdmode,
-                        N_("use <cmd>... to automatically bisect"), BISECT_RUN),
-               OPT_BOOL(0, "no-log", &nolog,
-                        N_("no log for BISECT_WRITE")),
+               OPT_SUBCOMMAND("reset", &fn, cmd_bisect__reset),
+               OPT_SUBCOMMAND("terms", &fn, cmd_bisect__terms),
+               OPT_SUBCOMMAND("start", &fn, cmd_bisect__start),
+               OPT_SUBCOMMAND("next", &fn, cmd_bisect__next),
+               OPT_SUBCOMMAND("log", &fn, cmd_bisect__log),
+               OPT_SUBCOMMAND("replay", &fn, cmd_bisect__replay),
+               OPT_SUBCOMMAND("skip", &fn, cmd_bisect__skip),
+               OPT_SUBCOMMAND("visualize", &fn, cmd_bisect__visualize),
+               OPT_SUBCOMMAND("view", &fn, cmd_bisect__visualize),
+               OPT_SUBCOMMAND("run", &fn, cmd_bisect__run),
                OPT_END()
        };
-       struct bisect_terms terms = { .term_good = NULL, .term_bad = NULL };
+       argc = parse_options(argc, argv, prefix, options, git_bisect_usage,
+                            PARSE_OPT_SUBCOMMAND_OPTIONAL);
 
-       argc = parse_options(argc, argv, prefix, options,
-                            git_bisect_helper_usage,
-                            PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN_OPT);
+       if (!fn) {
+               struct bisect_terms terms = { 0 };
 
-       if (!cmdmode)
-               usage_with_options(git_bisect_helper_usage, options);
+               if (!argc)
+                       usage_msg_opt(_("need a command"), git_bisect_usage, options);
 
-       switch (cmdmode) {
-       case BISECT_RESET:
-               if (argc > 1)
-                       return error(_("--bisect-reset requires either no argument or a commit"));
-               res = bisect_reset(argc ? argv[0] : NULL);
-               break;
-       case BISECT_TERMS:
-               if (argc > 1)
-                       return error(_("--bisect-terms requires 0 or 1 argument"));
-               res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL);
-               break;
-       case BISECT_START:
-               set_terms(&terms, "bad", "good");
-               res = bisect_start(&terms, argv, argc);
-               break;
-       case BISECT_NEXT:
-               if (argc)
-                       return error(_("--bisect-next requires 0 arguments"));
-               get_terms(&terms);
-               res = bisect_next(&terms, prefix);
-               break;
-       case BISECT_STATE:
-               set_terms(&terms, "bad", "good");
-               get_terms(&terms);
-               res = bisect_state(&terms, argv, argc);
-               break;
-       case BISECT_LOG:
-               if (argc)
-                       return error(_("--bisect-log requires 0 arguments"));
-               res = bisect_log();
-               break;
-       case BISECT_REPLAY:
-               if (argc != 1)
-                       return error(_("no logfile given"));
-               set_terms(&terms, "bad", "good");
-               res = bisect_replay(&terms, argv[0]);
-               break;
-       case BISECT_SKIP:
                set_terms(&terms, "bad", "good");
                get_terms(&terms);
-               res = bisect_skip(&terms, argv, argc);
-               break;
-       case BISECT_VISUALIZE:
-               get_terms(&terms);
-               res = bisect_visualize(&terms, argv, argc);
-               break;
-       case BISECT_RUN:
-               if (!argc)
-                       return error(_("bisect run failed: no command provided."));
-               get_terms(&terms);
-               res = bisect_run(&terms, argv, argc);
-               break;
-       default:
-               BUG("unknown subcommand %d", cmdmode);
+               if (check_and_set_terms(&terms, argv[0]))
+                       usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage,
+                                      options, argv[0]);
+               res = bisect_state(&terms, argc, argv);
+               free_terms(&terms);
+       } else {
+               argc--;
+               argv++;
+               res = fn(argc, argv, prefix);
        }
-       free_terms(&terms);
-
-       /*
-        * Handle early success
-        * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
-        */
-       if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND))
-               res = BISECT_OK;
 
-       return -res;
+       return is_bisect_success(res) ? 0 : -res;
 }
index a9fe8cf7a68bf31547b7482a40eb269f6ea0c30f..db1f56de61a434c3028869a2f4105c319ac6fd33 100644 (file)
@@ -5,10 +5,13 @@
  * See COPYING for licensing conditions
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "color.h"
 #include "builtin.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "commit.h"
 #include "diff.h"
 #include "userdiff.h"
 #include "line-range.h"
 #include "line-log.h"
-#include "dir.h"
 #include "progress.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
 #include "blame.h"
 #include "refs.h"
+#include "setup.h"
 #include "tag.h"
+#include "write-or-die.h"
 
 static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
+static char annotate_usage[] = N_("git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>");
 
 static const char *blame_opt_usage[] = {
        blame_usage,
@@ -38,6 +45,13 @@ static const char *blame_opt_usage[] = {
        NULL
 };
 
+static const char *annotate_opt_usage[] = {
+       annotate_usage,
+       "",
+       N_("<rev-opts> are documented in git-rev-list(1)"),
+       NULL
+};
+
 static int longest_file;
 static int longest_author;
 static int max_orig_digits;
@@ -191,13 +205,13 @@ static void get_commit_info(struct commit *commit,
        const char *message;
 
        encoding = get_log_output_encoding();
-       message = logmsg_reencode(commit, NULL, encoding);
+       message = repo_logmsg_reencode(the_repository, commit, NULL, encoding);
        get_ac_line(message, "\nauthor ",
                    &ret->author, &ret->author_mail,
                    &ret->author_time, &ret->author_tz);
 
        if (!detailed) {
-               unuse_commit_buffer(commit, message);
+               repo_unuse_commit_buffer(the_repository, commit, message);
                return;
        }
 
@@ -211,7 +225,7 @@ static void get_commit_info(struct commit *commit,
        else
                strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid));
 
-       unuse_commit_buffer(commit, message);
+       repo_unuse_commit_buffer(the_repository, commit, message);
 }
 
 /*
@@ -593,8 +607,9 @@ static int read_ancestry(const char *graft_file)
 
 static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)
 {
-       const char *uniq = find_unique_abbrev(&suspect->commit->object.oid,
-                                             auto_abbrev);
+       const char *uniq = repo_find_unique_abbrev(the_repository,
+                                                  &suspect->commit->object.oid,
+                                                  auto_abbrev);
        int len = strlen(uniq);
        if (auto_abbrev < len)
                return len;
@@ -677,7 +692,8 @@ static const char *add_prefix(const char *prefix, const char *path)
        return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
 }
 
-static int git_blame_config(const char *var, const char *value, void *cb)
+static int git_blame_config(const char *var, const char *value,
+                           const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "blame.showroot")) {
                show_root = git_config_bool(var, value);
@@ -731,6 +747,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, "blame.coloring")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (!strcmp(value, "repeatedLines")) {
                        coloring_mode |= OUTPUT_COLOR_LINE;
                } else if (!strcmp(value, "highlightRecent")) {
@@ -750,7 +768,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)
        if (userdiff_config(var, value) < 0)
                return -1;
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static int blame_copy_callback(const struct option *option, const char *arg, int unset)
@@ -794,7 +812,7 @@ static int is_a_rev(const char *name)
 {
        struct object_id oid;
 
-       if (get_oid(name, &oid))
+       if (repo_get_oid(the_repository, name, &oid))
                return 0;
        return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
 }
@@ -837,7 +855,7 @@ static void build_ignorelist(struct blame_scoreboard *sb,
                                                    peel_to_commit_oid, sb);
        }
        for_each_string_list_item(i, ignore_rev_list) {
-               if (get_oid_committish(i->string, &oid) ||
+               if (repo_get_oid_committish(the_repository, i->string, &oid) ||
                    peel_to_commit_oid(&oid, sb))
                        die(_("cannot find revision %s to ignore"), i->string);
                oidset_insert(&sb->ignore_list, &oid);
@@ -899,6 +917,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        long anchor;
        const int hexsz = the_hash_algo->hexsz;
        long num_lines = 0;
+       const char *str_usage = cmd_is_annotate ? annotate_usage : blame_usage;
+       const char **opt_usage = cmd_is_annotate ? annotate_opt_usage : blame_opt_usage;
 
        setup_default_color_by_age();
        git_config(git_blame_config, &output_option);
@@ -914,7 +934,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        parse_options_start(&ctx, argc, argv, prefix, options,
                            PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
        for (;;) {
-               switch (parse_options_step(&ctx, options, blame_opt_usage)) {
+               switch (parse_options_step(&ctx, options, opt_usage)) {
                case PARSE_OPT_NON_OPTION:
                case PARSE_OPT_UNKNOWN:
                        break;
@@ -934,7 +954,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                        ctx.argv[0] = "--children";
                        reverse = 1;
                }
-               parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
+               parse_revision_opt(&revs, &ctx, options, opt_usage);
        }
 parse_done:
        revision_opts_finish(&revs);
@@ -1040,7 +1060,7 @@ parse_done:
                switch (argc - dashdash_pos - 1) {
                case 2: /* (1b) */
                        if (argc != 4)
-                               usage_with_options(blame_opt_usage, options);
+                               usage_with_options(opt_usage, options);
                        /* reorder for the new way: <rev> -- <path> */
                        argv[1] = argv[3];
                        argv[3] = argv[2];
@@ -1051,11 +1071,11 @@ parse_done:
                        argv[argc] = NULL;
                        break;
                default:
-                       usage_with_options(blame_opt_usage, options);
+                       usage_with_options(opt_usage, options);
                }
        } else {
                if (argc < 2)
-                       usage_with_options(blame_opt_usage, options);
+                       usage_with_options(opt_usage, options);
                if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */
                        path = add_prefix(prefix, argv[1]);
                        argv[1] = argv[2];
@@ -1113,7 +1133,7 @@ parse_done:
                                    nth_line_cb, &sb, lno, anchor,
                                    &bottom, &top, sb.path,
                                    the_repository->index))
-                       usage(blame_usage);
+                       usage(str_usage);
                if ((!lno && (top || bottom)) || lno < bottom)
                        die(Q_("file %s has only %lu line",
                               "file %s has only %lu lines",
index e0e0af432024bb6db1802dec92fe48bab9da3157..0a32d1b6c851d4cf07bb02be9376d61a8d38e5d3 100644 (file)
@@ -5,21 +5,22 @@
  * Based on git-branch.sh by Junio C Hamano.
  */
 
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "color.h"
+#include "editor.h"
+#include "environment.h"
 #include "refs.h"
 #include "commit.h"
-#include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "remote.h"
 #include "parse-options.h"
 #include "branch.h"
-#include "diff.h"
-#include "revision.h"
+#include "path.h"
 #include "string-list.h"
 #include "column.h"
 #include "utf8.h"
-#include "wt-status.h"
 #include "ref-filter.h"
 #include "worktree.h"
 #include "help.h"
@@ -77,7 +78,8 @@ static unsigned int colopts;
 
 define_list_config_array(color_branch_slots);
 
-static int git_branch_config(const char *var, const char *value, void *cb)
+static int git_branch_config(const char *var, const char *value,
+                            const struct config_context *ctx, void *cb)
 {
        const char *slot_name;
 
@@ -111,7 +113,10 @@ static int git_branch_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       return git_color_default_config(var, value, cb);
+       if (git_color_config(var, value, cb) < 0)
+               return -1;
+
+       return git_default_config(var, value, ctx, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
@@ -150,24 +155,25 @@ static int branch_merged(int kind, const char *name,
        if (!reference_rev)
                reference_rev = head_rev;
 
-       merged = in_merge_bases(rev, reference_rev);
+       merged = reference_rev ? repo_in_merge_bases(the_repository, rev,
+                                                    reference_rev) : 0;
 
        /*
         * After the safety valve is fully redefined to "check with
         * upstream, if any, otherwise with HEAD", we should just
-        * return the result of the in_merge_bases() above without
+        * return the result of the repo_in_merge_bases() above without
         * any of the following code, but during the transition period,
         * a gentle reminder is in order.
         */
        if ((head_rev != reference_rev) &&
-           in_merge_bases(rev, head_rev) != merged) {
+           (head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) {
                if (merged)
                        warning(_("deleting branch '%s' that has been merged to\n"
-                               "         '%s', but not yet merged to HEAD."),
+                               "         '%s', but not yet merged to HEAD"),
                                name, reference_name);
                else
                        warning(_("not deleting branch '%s' that is not yet merged to\n"
-                               "         '%s', even though it is merged to HEAD."),
+                               "         '%s', even though it is merged to HEAD"),
                                name, reference_name);
        }
        free(reference_name_to_free);
@@ -180,13 +186,13 @@ static int check_branch_commit(const char *branchname, const char *refname,
 {
        struct commit *rev = lookup_commit_reference(the_repository, oid);
        if (!force && !rev) {
-               error(_("Couldn't look up commit object for '%s'"), refname);
+               error(_("couldn't look up commit object for '%s'"), refname);
                return -1;
        }
        if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
-               error(_("The branch '%s' is not fully merged.\n"
+               error(_("the branch '%s' is not fully merged.\n"
                      "If you are sure you want to delete it, "
-                     "run 'git branch -D %s'."), branchname, branchname);
+                     "run 'git branch -D %s'"), branchname, branchname);
                return -1;
        }
        return 0;
@@ -197,7 +203,7 @@ static void delete_branch_config(const char *branchname)
        struct strbuf buf = STRBUF_INIT;
        strbuf_addf(&buf, "branch.%s", branchname);
        if (git_config_rename_section(buf.buf, NULL) < 0)
-               warning(_("Update of config-file failed"));
+               warning(_("update of config-file failed"));
        strbuf_release(&buf);
 }
 
@@ -216,10 +222,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
        struct string_list_item *item;
        int branch_name_pos;
+       const char *fmt_remotes = "refs/remotes/%s";
 
        switch (kinds) {
        case FILTER_REFS_REMOTES:
-               fmt = "refs/remotes/%s";
+               fmt = fmt_remotes;
                /* For subsequent UI messages */
                remote_branch = 1;
                allowed_interpret = INTERPRET_BRANCH_REMOTE;
@@ -235,11 +242,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
        }
        branch_name_pos = strcspn(fmt, "%");
 
-       if (!force) {
+       if (!force)
                head_rev = lookup_commit_reference(the_repository, &head_oid);
-               if (!head_rev)
-                       die(_("Couldn't look up commit object for HEAD"));
-       }
 
        for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
                char *target = NULL;
@@ -252,8 +256,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                if (kinds == FILTER_REFS_BRANCHES) {
                        const char *path;
                        if ((path = branch_checked_out(name))) {
-                               error(_("Cannot delete branch '%s' "
-                                       "checked out at '%s'"),
+                               error(_("cannot delete branch '%s' "
+                                       "used by worktree at '%s'"),
                                      bname.buf, path);
                                ret = 1;
                                continue;
@@ -266,9 +270,25 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                                        | RESOLVE_REF_ALLOW_BAD_NAME,
                                        &oid, &flags);
                if (!target) {
-                       error(remote_branch
-                             ? _("remote-tracking branch '%s' not found.")
-                             : _("branch '%s' not found."), bname.buf);
+                       if (remote_branch) {
+                               error(_("remote-tracking branch '%s' not found"), bname.buf);
+                       } else {
+                               char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
+                               char *virtual_target = resolve_refdup(virtual_name,
+                                                       RESOLVE_REF_READING
+                                                       | RESOLVE_REF_NO_RECURSE
+                                                       | RESOLVE_REF_ALLOW_BAD_NAME,
+                                                       &oid, &flags);
+                               FREE_AND_NULL(virtual_name);
+
+                               if (virtual_target)
+                                       error(_("branch '%s' not found.\n"
+                                               "Did you forget --remote?"),
+                                               bname.buf);
+                               else
+                                       error(_("branch '%s' not found"), bname.buf);
+                               FREE_AND_NULL(virtual_target);
+                       }
                        ret = 1;
                        continue;
                }
@@ -283,7 +303,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                item = string_list_append(&refs_to_delete, name);
                item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
                                    : (flags & REF_ISSYMREF) ? target
-                                   : find_unique_abbrev(&oid, DEFAULT_ABBREV));
+                                   : repo_find_unique_abbrev(the_repository, &oid, DEFAULT_ABBREV));
 
        next:
                free(target);
@@ -345,17 +365,8 @@ static const char *quote_literal_for_format(const char *s)
        static struct strbuf buf = STRBUF_INIT;
 
        strbuf_reset(&buf);
-       while (*s) {
-               const char *ep = strchrnul(s, '%');
-               if (s < ep)
-                       strbuf_add(&buf, s, ep - s);
-               if (*ep == '%') {
-                       strbuf_addstr(&buf, "%%");
-                       s = ep + 1;
-               } else {
-                       s = ep;
-               }
-       }
+       while (strbuf_expand_step(&buf, &s))
+               strbuf_addstr(&buf, "%%");
        return buf.buf;
 }
 
@@ -423,8 +434,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 {
        int i;
        struct ref_array array;
-       struct strbuf out = STRBUF_INIT;
-       struct strbuf err = STRBUF_INIT;
        int maxwidth = 0;
        const char *remote_prefix = "";
        char *to_free = NULL;
@@ -451,25 +460,30 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
        if (verify_ref_format(format))
                die(_("unable to parse format string"));
 
+       filter_ahead_behind(the_repository, format, &array);
        ref_array_sort(sorting, &array);
 
-       for (i = 0; i < array.nr; i++) {
-               strbuf_reset(&err);
-               strbuf_reset(&out);
-               if (format_ref_array_item(array.items[i], format, &out, &err))
-                       die("%s", err.buf);
-               if (column_active(colopts)) {
-                       assert(!filter->verbose && "--column and --verbose are incompatible");
-                        /* format to a string_list to let print_columns() do its job */
+       if (column_active(colopts)) {
+               struct strbuf out = STRBUF_INIT, err = STRBUF_INIT;
+
+               assert(!filter->verbose && "--column and --verbose are incompatible");
+
+               for (i = 0; i < array.nr; i++) {
+                       strbuf_reset(&err);
+                       strbuf_reset(&out);
+                       if (format_ref_array_item(array.items[i], format, &out, &err))
+                               die("%s", err.buf);
+
+                       /* format to a string_list to let print_columns() do its job */
                        string_list_append(output, out.buf);
-               } else {
-                       fwrite(out.buf, 1, out.len, stdout);
-                       putchar('\n');
                }
+
+               strbuf_release(&err);
+               strbuf_release(&out);
+       } else {
+               print_formatted_ref_array(&array, format);
        }
 
-       strbuf_release(&err);
-       strbuf_release(&out);
        ref_array_clear(&array);
        free(to_free);
 }
@@ -489,9 +503,9 @@ static void print_current_branch_name(void)
                die(_("HEAD (%s) points outside of refs/heads/"), refname);
 }
 
-static void reject_rebase_or_bisect_branch(const char *target)
+static void reject_rebase_or_bisect_branch(struct worktree **worktrees,
+                                          const char *target)
 {
-       struct worktree **worktrees = get_worktrees();
        int i;
 
        for (i = 0; worktrees[i]; i++) {
@@ -501,31 +515,57 @@ static void reject_rebase_or_bisect_branch(const char *target)
                        continue;
 
                if (is_worktree_being_rebased(wt, target))
-                       die(_("Branch %s is being rebased at %s"),
+                       die(_("branch %s is being rebased at %s"),
                            target, wt->path);
 
                if (is_worktree_being_bisected(wt, target))
-                       die(_("Branch %s is being bisected at %s"),
+                       die(_("branch %s is being bisected at %s"),
                            target, wt->path);
        }
+}
 
-       free_worktrees(worktrees);
+/*
+ * Update all per-worktree HEADs pointing at the old ref to point the new ref.
+ * This will be used when renaming a branch. Returns 0 if successful, non-zero
+ * otherwise.
+ */
+static int replace_each_worktree_head_symref(struct worktree **worktrees,
+                                            const char *oldref, const char *newref,
+                                            const char *logmsg)
+{
+       int ret = 0;
+       int i;
+
+       for (i = 0; worktrees[i]; i++) {
+               struct ref_store *refs;
+
+               if (worktrees[i]->is_detached)
+                       continue;
+               if (!worktrees[i]->head_ref)
+                       continue;
+               if (strcmp(oldref, worktrees[i]->head_ref))
+                       continue;
+
+               refs = get_worktree_ref_store(worktrees[i]);
+               if (refs_create_symref(refs, "HEAD", newref, logmsg))
+                       ret = error(_("HEAD of working tree %s is not updated"),
+                                   worktrees[i]->path);
+       }
+
+       return ret;
 }
 
+#define IS_HEAD 1
+#define IS_ORPHAN 2
+
 static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
 {
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
        struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
        const char *interpreted_oldname = NULL;
        const char *interpreted_newname = NULL;
-       int recovery = 0;
-
-       if (!oldname) {
-               if (copy)
-                       die(_("cannot copy the current branch while not on any."));
-               else
-                       die(_("cannot rename the current branch while not on any."));
-       }
+       int recovery = 0, oldref_usage = 0;
+       struct worktree **worktrees = get_worktrees();
 
        if (strbuf_check_branch_ref(&oldref, oldname)) {
                /*
@@ -535,14 +575,25 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
                if (ref_exists(oldref.buf))
                        recovery = 1;
                else
-                       die(_("Invalid branch name: '%s'"), oldname);
+                       die(_("invalid branch name: '%s'"), oldname);
        }
 
-       if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) {
-               if (copy && !strcmp(head, oldname))
-                       die(_("No commit on branch '%s' yet."), oldname);
+       for (int i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+
+               if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) {
+                       oldref_usage |= IS_HEAD;
+                       if (is_null_oid(&wt->head_oid))
+                               oldref_usage |= IS_ORPHAN;
+                       break;
+               }
+       }
+
+       if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
+               if (oldref_usage & IS_HEAD)
+                       die(_("no commit on branch '%s' yet"), oldname);
                else
-                       die(_("No branch named '%s'."), oldname);
+                       die(_("no branch named '%s'"), oldname);
        }
 
        /*
@@ -554,7 +605,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
        else
                validate_new_branchname(newname, &newref, force);
 
-       reject_rebase_or_bisect_branch(oldref.buf);
+       reject_rebase_or_bisect_branch(worktrees, oldref.buf);
 
        if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
            !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
@@ -568,38 +619,39 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
                strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                            oldref.buf, newref.buf);
 
-       if (!copy &&
-           (!head || strcmp(oldname, head) || !is_null_oid(&head_oid)) &&
+       if (!copy && !(oldref_usage & IS_ORPHAN) &&
            rename_ref(oldref.buf, newref.buf, logmsg.buf))
-               die(_("Branch rename failed"));
+               die(_("branch rename failed"));
        if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
-               die(_("Branch copy failed"));
+               die(_("branch copy failed"));
 
        if (recovery) {
                if (copy)
-                       warning(_("Created a copy of a misnamed branch '%s'"),
+                       warning(_("created a copy of a misnamed branch '%s'"),
                                interpreted_oldname);
                else
-                       warning(_("Renamed a misnamed branch '%s' away"),
+                       warning(_("renamed a misnamed branch '%s' away"),
                                interpreted_oldname);
        }
 
-       if (!copy &&
-           replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
-               die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
+       if (!copy && (oldref_usage & IS_HEAD) &&
+           replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf,
+                                             logmsg.buf))
+               die(_("branch renamed to %s, but HEAD is not updated"), newname);
 
        strbuf_release(&logmsg);
 
        strbuf_addf(&oldsection, "branch.%s", interpreted_oldname);
-       strbuf_release(&oldref);
        strbuf_addf(&newsection, "branch.%s", interpreted_newname);
-       strbuf_release(&newref);
        if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
-               die(_("Branch is renamed, but update of config-file failed"));
-       if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
-               die(_("Branch is copied, but update of config-file failed"));
+               die(_("branch is renamed, but update of config-file failed"));
+       if (copy && strcmp(interpreted_oldname, interpreted_newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
+               die(_("branch is copied, but update of config-file failed"));
+       strbuf_release(&oldref);
+       strbuf_release(&newref);
        strbuf_release(&oldsection);
        strbuf_release(&newsection);
+       free_worktrees(worktrees);
 }
 
 static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
@@ -613,7 +665,7 @@ static int edit_branch_description(const char *branch_name)
        exists = !read_branch_desc(&buf, branch_name);
        if (!buf.len || buf.buf[buf.len-1] != '\n')
                strbuf_addch(&buf, '\n');
-       strbuf_commented_addf(&buf,
+       strbuf_commented_addf(&buf, comment_line_char,
                    _("Please edit the description for the branch\n"
                      "  %s\n"
                      "Lines starting with '%c' will be stripped.\n"),
@@ -624,7 +676,7 @@ static int edit_branch_description(const char *branch_name)
                strbuf_release(&buf);
                return -1;
        }
-       strbuf_stripspace(&buf, 1);
+       strbuf_stripspace(&buf, comment_line_char);
 
        strbuf_addf(&name, "branch.%s.description", branch_name);
        if (buf.len || exists)
@@ -646,7 +698,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        int reflog = 0, quiet = 0, icase = 0, force = 0,
            recurse_submodules_explicit = 0;
        enum branch_track track;
-       struct ref_filter filter;
+       struct ref_filter filter = REF_FILTER_INIT;
        static struct ref_sorting *sorting;
        struct string_list sorting_options = STRING_LIST_INIT_DUP;
        struct ref_format format = REF_FORMAT_INIT;
@@ -665,8 +717,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
                OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")),
                OPT__COLOR(&branch_use_color, N_("use colored output")),
-               OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
-                       FILTER_REFS_REMOTES),
+               OPT_SET_INT_F('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
+                             FILTER_REFS_REMOTES,
+                             PARSE_OPT_NONEG),
                OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
                OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
                OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
@@ -674,12 +727,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT__ABBREV(&filter.abbrev),
 
                OPT_GROUP(N_("Specific git-branch actions:")),
-               OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
-                       FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES),
+               OPT_SET_INT_F('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
+                             FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES,
+                             PARSE_OPT_NONEG),
                OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1),
                OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
                OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
                OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+               OPT_BOOL(0, "omit-empty",  &format.array_opts.omit_empty,
+                       N_("do not output a newline after empty formatted refs")),
                OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
                OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
                OPT_BOOL('l', "list", &list, N_("list branch names")),
@@ -702,20 +758,25 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        setup_ref_filter_porcelain_msg();
 
-       memset(&filter, 0, sizeof(filter));
        filter.kind = FILTER_REFS_BRANCHES;
        filter.abbrev = -1;
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_branch_usage, options);
 
+       /*
+        * Try to set sort keys from config. If config does not set any,
+        * fall back on default (refname) sorting.
+        */
        git_config(git_branch_config, &sorting_options);
+       if (!sorting_options.nr)
+               string_list_append(&sorting_options, "refname");
 
        track = git_branch_track;
 
        head = resolve_refdup("HEAD", 0, &head_oid, NULL);
        if (!head)
-               die(_("Failed to resolve HEAD as a valid ref."));
+               die(_("failed to resolve HEAD as a valid ref"));
        if (!strcmp(head, "HEAD"))
                filter.detached = 1;
        else if (!skip_prefix(head, "refs/heads/", &head))
@@ -769,6 +830,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (list)
                setup_auto_pager("branch", 1);
 
+       UNLEAK(sorting_options);
+
        if (delete) {
                if (!argc)
                        die(_("branch name required"));
@@ -796,93 +859,104 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
                ref_sorting_release(sorting);
+               ref_filter_clear(&filter);
                return 0;
        } else if (edit_description) {
                const char *branch_name;
                struct strbuf branch_ref = STRBUF_INIT;
+               struct strbuf buf = STRBUF_INIT;
+               int ret = 1; /* assume failure */
 
                if (!argc) {
                        if (filter.detached)
-                               die(_("Cannot give description to detached HEAD"));
+                               die(_("cannot give description to detached HEAD"));
                        branch_name = head;
-               } else if (argc == 1)
-                       branch_name = argv[0];
-               else
+               } else if (argc == 1) {
+                       strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+                       branch_name = buf.buf;
+               } else {
                        die(_("cannot edit description of more than one branch"));
+               }
 
                strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
-               if (!ref_exists(branch_ref.buf)) {
-                       strbuf_release(&branch_ref);
-
-                       if (!argc || !strcmp(head, branch_name))
-                               return error(_("No commit on branch '%s' yet."),
-                                            branch_name);
-                       else
-                               return error(_("No branch named '%s'."),
-                                            branch_name);
-               }
+               if (!ref_exists(branch_ref.buf))
+                       error((!argc || branch_checked_out(branch_ref.buf))
+                             ? _("no commit on branch '%s' yet")
+                             : _("no branch named '%s'"),
+                             branch_name);
+               else if (!edit_branch_description(branch_name))
+                       ret = 0; /* happy */
+
                strbuf_release(&branch_ref);
+               strbuf_release(&buf);
 
-               if (edit_branch_description(branch_name))
-                       return 1;
-       } else if (copy) {
+               return ret;
+       } else if (copy || rename) {
                if (!argc)
                        die(_("branch name required"));
+               else if ((argc == 1) && filter.detached)
+                       die(copy? _("cannot copy the current branch while not on any")
+                               : _("cannot rename the current branch while not on any"));
                else if (argc == 1)
-                       copy_or_rename_branch(head, argv[0], 1, copy > 1);
+                       copy_or_rename_branch(head, argv[0], copy, copy + rename > 1);
                else if (argc == 2)
-                       copy_or_rename_branch(argv[0], argv[1], 1, copy > 1);
+                       copy_or_rename_branch(argv[0], argv[1], copy, copy + rename > 1);
                else
-                       die(_("too many branches for a copy operation"));
-       } else if (rename) {
-               if (!argc)
-                       die(_("branch name required"));
-               else if (argc == 1)
-                       copy_or_rename_branch(head, argv[0], 0, rename > 1);
-               else if (argc == 2)
-                       copy_or_rename_branch(argv[0], argv[1], 0, rename > 1);
-               else
-                       die(_("too many arguments for a rename operation"));
+                       die(copy? _("too many branches for a copy operation")
+                               : _("too many arguments for a rename operation"));
        } else if (new_upstream) {
-               struct branch *branch = branch_get(argv[0]);
+               struct branch *branch;
+               struct strbuf buf = STRBUF_INIT;
 
-               if (argc > 1)
+               if (!argc)
+                       branch = branch_get(NULL);
+               else if (argc == 1) {
+                       strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+                       branch = branch_get(buf.buf);
+               } else
                        die(_("too many arguments to set new upstream"));
 
                if (!branch) {
                        if (!argc || !strcmp(argv[0], "HEAD"))
                                die(_("could not set upstream of HEAD to %s when "
-                                     "it does not point to any branch."),
+                                     "it does not point to any branch"),
                                    new_upstream);
                        die(_("no such branch '%s'"), argv[0]);
                }
 
                if (!ref_exists(branch->refname)) {
-                       if (!argc || !strcmp(head, branch->name))
-                               die(_("No commit on branch '%s' yet."), branch->name);
+                       if (!argc || branch_checked_out(branch->refname))
+                               die(_("no commit on branch '%s' yet"), branch->name);
                        die(_("branch '%s' does not exist"), branch->name);
                }
 
                dwim_and_setup_tracking(the_repository, branch->name,
                                        new_upstream, BRANCH_TRACK_OVERRIDE,
                                        quiet);
+               strbuf_release(&buf);
        } else if (unset_upstream) {
-               struct branch *branch = branch_get(argv[0]);
+               struct branch *branch;
                struct strbuf buf = STRBUF_INIT;
 
-               if (argc > 1)
+               if (!argc)
+                       branch = branch_get(NULL);
+               else if (argc == 1) {
+                       strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+                       branch = branch_get(buf.buf);
+               } else
                        die(_("too many arguments to unset upstream"));
 
                if (!branch) {
                        if (!argc || !strcmp(argv[0], "HEAD"))
                                die(_("could not unset upstream of HEAD when "
-                                     "it does not point to any branch."));
+                                     "it does not point to any branch"));
                        die(_("no such branch '%s'"), argv[0]);
                }
 
                if (!branch_has_merge_config(branch))
-                       die(_("Branch '%s' has no upstream information"), branch->name);
+                       die(_("branch '%s' has no upstream information"), branch->name);
 
+               strbuf_reset(&buf);
                strbuf_addf(&buf, "branch.%s.remote", branch->name);
                git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
                strbuf_reset(&buf);
@@ -894,11 +968,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                const char *start_name = argc == 2 ? argv[1] : head;
 
                if (filter.kind != FILTER_REFS_BRANCHES)
-                       die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
+                       die(_("the -a, and -r, options to 'git branch' do not take a branch name.\n"
                                  "Did you mean to use: -a|-r --list <pattern>?"));
 
                if (track == BRANCH_TRACK_OVERRIDE)
-                       die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
+                       die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead"));
 
                if (recurse_submodules) {
                        create_branches_recursively(the_repository, branch_name,
index 530895be55fe6d9627c5d954f4c235d22769051e..3106e56a130c58994fced2afd8dbd337134861ad 100644 (file)
@@ -1,4 +1,7 @@
 #include "builtin.h"
+#include "abspath.h"
+#include "editor.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "strbuf.h"
 #include "help.h"
@@ -6,7 +9,8 @@
 #include "hook.h"
 #include "hook-list.h"
 #include "diagnose.h"
-
+#include "object-file.h"
+#include "setup.h"
 
 static void get_system_info(struct strbuf *sys_info)
 {
@@ -60,7 +64,8 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit)
 }
 
 static const char * const bugreport_usage[] = {
-       N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--diagnose[=<mode>]"),
+       N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+          "              [--diagnose[=<mode>]]"),
        NULL
 };
 
@@ -105,6 +110,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
        const char *user_relative_path = NULL;
        char *prefixed_filename;
        size_t output_path_len;
+       int ret;
 
        const struct option bugreport_options[] = {
                OPT_CALLBACK_F(0, "diagnose", &diagnose, N_("mode"),
@@ -120,6 +126,11 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, bugreport_options,
                             bugreport_usage, 0);
 
+       if (argc) {
+               error(_("unknown argument `%s'"), argv[0]);
+               usage(bugreport_usage[0]);
+       }
+
        /* Prepare the path to put the result */
        prefixed_filename = prefix_filename(prefix,
                                            option_output ? option_output : "");
@@ -181,7 +192,9 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
                user_relative_path);
 
        free(prefixed_filename);
-       UNLEAK(buffer);
-       UNLEAK(report_path);
-       return !!launch_editor(report_path.buf, NULL, NULL);
+       strbuf_release(&buffer);
+
+       ret = !!launch_editor(report_path.buf, NULL, NULL);
+       strbuf_release(&report_path);
+       return ret;
 }
index e80efce3a420a0371bd984102105b8a51af34df1..3ad11dc5d0548727da0f27e04563a8d947207f74 100644 (file)
@@ -1,7 +1,11 @@
 #include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "setup.h"
 #include "strvec.h"
 #include "parse-options.h"
-#include "cache.h"
+#include "pkt-line.h"
+#include "repository.h"
 #include "bundle.h"
 
 /*
  * bundle supporting "fetch", "pull", and "ls-remote".
  */
 
-static const char * const builtin_bundle_usage[] = {
-  N_("git bundle create [<options>] <file> <git-rev-list args>"),
-  N_("git bundle verify [<options>] <file>"),
-  N_("git bundle list-heads <file> [<refname>...]"),
-  N_("git bundle unbundle <file> [<refname>...]"),
-  NULL
+#define BUILTIN_BUNDLE_CREATE_USAGE \
+       N_("git bundle create [-q | --quiet | --progress]\n" \
+          "                  [--version=<version>] <file> <git-rev-list-args>")
+#define BUILTIN_BUNDLE_VERIFY_USAGE \
+       N_("git bundle verify [-q | --quiet] <file>")
+#define BUILTIN_BUNDLE_LIST_HEADS_USAGE \
+       N_("git bundle list-heads <file> [<refname>...]")
+#define BUILTIN_BUNDLE_UNBUNDLE_USAGE \
+       N_("git bundle unbundle [--progress] <file> [<refname>...]")
+
+static char const * const builtin_bundle_usage[] = {
+       BUILTIN_BUNDLE_CREATE_USAGE,
+       BUILTIN_BUNDLE_VERIFY_USAGE,
+       BUILTIN_BUNDLE_LIST_HEADS_USAGE,
+       BUILTIN_BUNDLE_UNBUNDLE_USAGE,
+       NULL,
 };
 
 static const char * const builtin_bundle_create_usage[] = {
-  N_("git bundle create [<options>] <file> <git-rev-list args>"),
-  NULL
+       BUILTIN_BUNDLE_CREATE_USAGE,
+       NULL
 };
 
 static const char * const builtin_bundle_verify_usage[] = {
-  N_("git bundle verify [<options>] <file>"),
-  NULL
+       BUILTIN_BUNDLE_VERIFY_USAGE,
+       NULL
 };
 
 static const char * const builtin_bundle_list_heads_usage[] = {
-  N_("git bundle list-heads <file> [<refname>...]"),
-  NULL
+       BUILTIN_BUNDLE_LIST_HEADS_USAGE,
+       NULL
 };
 
 static const char * const builtin_bundle_unbundle_usage[] = {
-  N_("git bundle unbundle <file> [<refname>...]"),
-  NULL
+       BUILTIN_BUNDLE_UNBUNDLE_USAGE,
+       NULL
 };
 
 static int parse_options_cmd_bundle(int argc,
@@ -45,51 +59,45 @@ static int parse_options_cmd_bundle(int argc,
                const char * const usagestr[],
                const struct option options[],
                char **bundle_file) {
-       int newargc;
-       newargc = parse_options(argc, argv, NULL, options, usagestr,
+       argc = parse_options(argc, argv, NULL, options, usagestr,
                             PARSE_OPT_STOP_AT_NON_OPTION);
-       if (argc < 1)
-               usage_with_options(usagestr, options);
-       *bundle_file = prefix_filename(prefix, argv[0]);
-       return newargc;
+       if (!argc)
+               usage_msg_opt(_("need a <file> argument"), usagestr, options);
+       *bundle_file = prefix_filename_except_for_dash(prefix, argv[0]);
+       return argc;
 }
 
 static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
-       int all_progress_implied = 0;
-       int progress = isatty(STDERR_FILENO);
-       struct strvec pack_opts;
+       struct strvec pack_opts = STRVEC_INIT;
        int version = -1;
        int ret;
        struct option options[] = {
-               OPT_SET_INT('q', "quiet", &progress,
-                           N_("do not show progress meter"), 0),
-               OPT_SET_INT(0, "progress", &progress,
-                           N_("show progress meter"), 1),
-               OPT_SET_INT(0, "all-progress", &progress,
-                           N_("show progress meter during object writing phase"), 2),
-               OPT_BOOL(0, "all-progress-implied",
-                        &all_progress_implied,
-                        N_("similar to --all-progress when progress meter is shown")),
+               OPT_PASSTHRU_ARGV('q', "quiet", &pack_opts, NULL,
+                                 N_("do not show progress meter"),
+                                 PARSE_OPT_NOARG),
+               OPT_PASSTHRU_ARGV(0, "progress", &pack_opts, NULL,
+                                 N_("show progress meter"),
+                                 PARSE_OPT_NOARG),
+               OPT_PASSTHRU_ARGV(0, "all-progress", &pack_opts, NULL,
+                                 N_("historical; same as --progress"),
+                                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
+               OPT_PASSTHRU_ARGV(0, "all-progress-implied", &pack_opts, NULL,
+                                 N_("historical; does nothing"),
+                                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
                OPT_INTEGER(0, "version", &version,
                            N_("specify bundle format version")),
                OPT_END()
        };
        char *bundle_file;
 
+       if (isatty(STDERR_FILENO))
+               strvec_push(&pack_opts, "--progress");
+       strvec_push(&pack_opts, "--all-progress-implied");
+
        argc = parse_options_cmd_bundle(argc, argv, prefix,
                        builtin_bundle_create_usage, options, &bundle_file);
        /* bundle internals use argv[1] as further parameters */
 
-       strvec_init(&pack_opts);
-       if (progress == 0)
-               strvec_push(&pack_opts, "--quiet");
-       else if (progress == 1)
-               strvec_push(&pack_opts, "--progress");
-       else if (progress == 2)
-               strvec_push(&pack_opts, "--all-progress");
-       if (progress && all_progress_implied)
-               strvec_push(&pack_opts, "--all-progress-implied");
-
        if (!startup_info->have_repository)
                die(_("Need a repository to create a bundle."));
        ret = !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version);
@@ -98,6 +106,23 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
        return ret;
 }
 
+/*
+ * Similar to read_bundle_header(), but handle "-" as stdin.
+ */
+static int open_bundle(const char *path, struct bundle_header *header,
+                      const char **name)
+{
+       if (!strcmp(path, "-")) {
+               if (name)
+                       *name = "<stdin>";
+               return read_bundle_header_fd(0, header, "<stdin>");
+       }
+
+       if (name)
+               *name = path;
+       return read_bundle_header(path, header);
+}
+
 static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
        struct bundle_header header = BUNDLE_HEADER_INIT;
        int bundle_fd = -1;
@@ -109,22 +134,24 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
                OPT_END()
        };
        char *bundle_file;
+       const char *name;
 
        argc = parse_options_cmd_bundle(argc, argv, prefix,
                        builtin_bundle_verify_usage, options, &bundle_file);
        /* bundle internals use argv[1] as further parameters */
 
-       if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+       if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) {
                ret = 1;
                goto cleanup;
        }
        close(bundle_fd);
-       if (verify_bundle(the_repository, &header, !quiet)) {
+       if (verify_bundle(the_repository, &header,
+                         quiet ? VERIFY_BUNDLE_QUIET : VERIFY_BUNDLE_VERBOSE)) {
                ret = 1;
                goto cleanup;
        }
 
-       fprintf(stderr, _("%s is okay\n"), bundle_file);
+       fprintf(stderr, _("%s is okay\n"), name);
        ret = 0;
 cleanup:
        free(bundle_file);
@@ -145,7 +172,7 @@ static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix
                        builtin_bundle_list_heads_usage, options, &bundle_file);
        /* bundle internals use argv[1] as further parameters */
 
-       if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+       if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
                ret = 1;
                goto cleanup;
        }
@@ -175,7 +202,7 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
                        builtin_bundle_unbundle_usage, options, &bundle_file);
        /* bundle internals use argv[1] as further parameters */
 
-       if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+       if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
                ret = 1;
                goto cleanup;
        }
@@ -185,7 +212,7 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
                strvec_pushl(&extra_index_pack_args, "-v", "--progress-title",
                             _("Unbundling objects"), NULL);
        ret = !!unbundle(the_repository, &header, bundle_fd,
-                        &extra_index_pack_args) ||
+                        &extra_index_pack_args, 0) ||
                list_bundle_refs(&header, argc, argv);
        bundle_header_release(&header);
 cleanup:
index 989eee0bb4c7d924b97b3aac6b43443f7c34ab67..7d4899348a387b1a07377c2d968d7574a74d219d 100644 (file)
@@ -3,20 +3,27 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "config.h"
+#include "convert.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "ident.h"
 #include "parse-options.h"
 #include "userdiff.h"
 #include "streaming.h"
-#include "tree-walk.h"
 #include "oid-array.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
 #include "promisor-remote.h"
 #include "mailmap.h"
+#include "write-or-die.h"
 
 enum batch_mode {
        BATCH_MODE_CONTENTS,
@@ -32,7 +39,8 @@ struct batch_options {
        int all_objects;
        int unordered;
        int transform_mode; /* may be 'w' or 'c' for --filters or --textconv */
-       int nul_terminated;
+       char input_delim;
+       char output_delim;
        const char *format;
 };
 
@@ -60,7 +68,7 @@ static int filter_object(const char *path, unsigned mode,
 {
        enum object_type type;
 
-       *buf = read_object_file(oid, &type, size);
+       *buf = repo_read_object_file(the_repository, oid, &type, size);
        if (!*buf)
                return error(_("cannot read object %s '%s'"),
                             oid_to_hex(oid), path);
@@ -132,14 +140,27 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
 
        case 's':
                oi.sizep = &size;
+
+               if (use_mailmap) {
+                       oi.typep = &type;
+                       oi.contentp = (void**)&buf;
+               }
+
                if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
                        die("git cat-file: could not get object info");
+
+               if (use_mailmap && (type == OBJ_COMMIT || type == OBJ_TAG)) {
+                       size_t s = size;
+                       buf = replace_idents_using_mailmap(buf, &s);
+                       size = cast_size_t_to_ulong(s);
+               }
+
                printf("%"PRIuMAX"\n", (uintmax_t)size);
                ret = 0;
                goto cleanup;
 
        case 'e':
-               return !has_object_file(&oid);
+               return !repo_has_object_file(the_repository, &oid);
 
        case 'w':
 
@@ -174,7 +195,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                        ret = stream_blob(&oid);
                        goto cleanup;
                }
-               buf = read_object_file(&oid, &type, &size);
+               buf = repo_read_object_file(the_repository, &oid, &type,
+                                           &size);
                if (!buf)
                        die("Cannot read object %s", obj_name);
 
@@ -194,8 +216,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                if (exp_type_id == OBJ_BLOB) {
                        struct object_id blob_oid;
                        if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) {
-                               char *buffer = read_object_file(&oid, &type,
-                                                               &size);
+                               char *buffer = repo_read_object_file(the_repository,
+                                                                    &oid,
+                                                                    &type,
+                                                                    &size);
                                const char *target;
                                if (!skip_prefix(buffer, "object ", &target) ||
                                    get_oid_hex(target, &blob_oid))
@@ -283,10 +307,8 @@ static int is_atom(const char *atom, const char *s, int slen)
 }
 
 static void expand_atom(struct strbuf *sb, const char *atom, int len,
-                       void *vdata)
+                       struct expand_data *data)
 {
-       struct expand_data *data = vdata;
-
        if (is_atom("objectname", atom, len)) {
                if (!data->mark_query)
                        strbuf_addstr(sb, oid_to_hex(&data->oid));
@@ -320,19 +342,21 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
                die("unknown format element: %.*s", len, atom);
 }
 
-static size_t expand_format(struct strbuf *sb, const char *start, void *data)
+static void expand_format(struct strbuf *sb, const char *start,
+                         struct expand_data *data)
 {
-       const char *end;
-
-       if (*start != '(')
-               return 0;
-       end = strchr(start + 1, ')');
-       if (!end)
-               die("format element '%s' does not end in ')'", start);
-
-       expand_atom(sb, start + 1, end - start - 1, data);
-
-       return end - start + 1;
+       while (strbuf_expand_step(sb, &start)) {
+               const char *end;
+
+               if (skip_prefix(start, "%", &start) || *start != '(')
+                       strbuf_addch(sb, '%');
+               else if (!(end = strchr(start + 1, ')')))
+                       die("format element '%s' does not end in ')'", start);
+               else {
+                       expand_atom(sb, start + 1, end - start - 1, data);
+                       start = end + 1;
+               }
+       }
 }
 
 static void batch_write(struct batch_options *opt, const void *data, int len)
@@ -370,9 +394,10 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
                                if (!textconv_object(the_repository,
                                                     data->rest, 0100644, oid,
                                                     1, &contents, &size))
-                                       contents = read_object_file(oid,
-                                                                   &type,
-                                                                   &size);
+                                       contents = repo_read_object_file(the_repository,
+                                                                        oid,
+                                                                        &type,
+                                                                        &size);
                                if (!contents)
                                        die("could not convert '%s' %s",
                                            oid_to_hex(oid), data->rest);
@@ -389,7 +414,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
                unsigned long size;
                void *contents;
 
-               contents = read_object_file(oid, &type, &size);
+               contents = repo_read_object_file(the_repository, oid, &type,
+                                                &size);
 
                if (use_mailmap) {
                        size_t s = size;
@@ -409,11 +435,12 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
        }
 }
 
-static void print_default_format(struct strbuf *scratch, struct expand_data *data)
+static void print_default_format(struct strbuf *scratch, struct expand_data *data,
+                                struct batch_options *opt)
 {
-       strbuf_addf(scratch, "%s %s %"PRIuMAX"\n", oid_to_hex(&data->oid),
+       strbuf_addf(scratch, "%s %s %"PRIuMAX"%c", oid_to_hex(&data->oid),
                    type_name(data->type),
-                   (uintmax_t)data->size);
+                   (uintmax_t)data->size, opt->output_delim);
 }
 
 /*
@@ -431,6 +458,9 @@ static void batch_object_write(const char *obj_name,
        if (!data->skip_object_info) {
                int ret;
 
+               if (use_mailmap)
+                       data->info.typep = &data->type;
+
                if (pack)
                        ret = packed_object_info(the_repository, pack, offset,
                                                 &data->info);
@@ -439,27 +469,39 @@ static void batch_object_write(const char *obj_name,
                                                       &data->oid, &data->info,
                                                       OBJECT_INFO_LOOKUP_REPLACE);
                if (ret < 0) {
-                       printf("%s missing\n",
-                              obj_name ? obj_name : oid_to_hex(&data->oid));
+                       printf("%s missing%c",
+                              obj_name ? obj_name : oid_to_hex(&data->oid), opt->output_delim);
                        fflush(stdout);
                        return;
                }
+
+               if (use_mailmap && (data->type == OBJ_COMMIT || data->type == OBJ_TAG)) {
+                       size_t s = data->size;
+                       char *buf = NULL;
+
+                       buf = repo_read_object_file(the_repository, &data->oid, &data->type,
+                                                   &data->size);
+                       buf = replace_idents_using_mailmap(buf, &s);
+                       data->size = cast_size_t_to_ulong(s);
+
+                       free(buf);
+               }
        }
 
        strbuf_reset(scratch);
 
        if (!opt->format) {
-               print_default_format(scratch, data);
+               print_default_format(scratch, data, opt);
        } else {
-               strbuf_expand(scratch, opt->format, expand_format, data);
-               strbuf_addch(scratch, '\n');
+               expand_format(scratch, opt->format, data);
+               strbuf_addch(scratch, opt->output_delim);
        }
 
        batch_write(opt, scratch->buf, scratch->len);
 
        if (opt->batch_mode == BATCH_MODE_CONTENTS) {
                print_object_or_die(opt, data);
-               batch_write(opt, "\n", 1);
+               batch_write(opt, &opt->output_delim, 1);
        }
 }
 
@@ -477,22 +519,25 @@ static void batch_one_object(const char *obj_name,
        if (result != FOUND) {
                switch (result) {
                case MISSING_OBJECT:
-                       printf("%s missing\n", obj_name);
+                       printf("%s missing%c", obj_name, opt->output_delim);
                        break;
                case SHORT_NAME_AMBIGUOUS:
-                       printf("%s ambiguous\n", obj_name);
+                       printf("%s ambiguous%c", obj_name, opt->output_delim);
                        break;
                case DANGLING_SYMLINK:
-                       printf("dangling %"PRIuMAX"\n%s\n",
-                              (uintmax_t)strlen(obj_name), obj_name);
+                       printf("dangling %"PRIuMAX"%c%s%c",
+                              (uintmax_t)strlen(obj_name),
+                              opt->output_delim, obj_name, opt->output_delim);
                        break;
                case SYMLINK_LOOP:
-                       printf("loop %"PRIuMAX"\n%s\n",
-                              (uintmax_t)strlen(obj_name), obj_name);
+                       printf("loop %"PRIuMAX"%c%s%c",
+                              (uintmax_t)strlen(obj_name),
+                              opt->output_delim, obj_name, opt->output_delim);
                        break;
                case NOT_DIR:
-                       printf("notdir %"PRIuMAX"\n%s\n",
-                              (uintmax_t)strlen(obj_name), obj_name);
+                       printf("notdir %"PRIuMAX"%c%s%c",
+                              (uintmax_t)strlen(obj_name),
+                              opt->output_delim, obj_name, opt->output_delim);
                        break;
                default:
                        BUG("unknown get_sha1_with_context result %d\n",
@@ -504,9 +549,9 @@ static void batch_one_object(const char *obj_name,
        }
 
        if (ctx.mode == 0) {
-               printf("symlink %"PRIuMAX"\n%s\n",
+               printf("symlink %"PRIuMAX"%c%s%c",
                       (uintmax_t)ctx.symlink_path.len,
-                      ctx.symlink_path.buf);
+                      opt->output_delim, ctx.symlink_path.buf, opt->output_delim);
                fflush(stdout);
                return;
        }
@@ -531,7 +576,7 @@ static int batch_object_cb(const struct object_id *oid, void *vdata)
 }
 
 static int collect_loose_object(const struct object_id *oid,
-                               const char *path,
+                               const char *path UNUSED,
                                void *data)
 {
        oid_array_append(data, oid);
@@ -539,8 +584,8 @@ static int collect_loose_object(const struct object_id *oid,
 }
 
 static int collect_packed_object(const struct object_id *oid,
-                                struct packed_git *pack,
-                                uint32_t pos,
+                                struct packed_git *pack UNUSED,
+                                uint32_t pos UNUSED,
                                 void *data)
 {
        oid_array_append(data, oid);
@@ -563,7 +608,7 @@ static int batch_unordered_object(const struct object_id *oid,
 }
 
 static int batch_unordered_loose(const struct object_id *oid,
-                                const char *path,
+                                const char *path UNUSED,
                                 void *data)
 {
        return batch_unordered_object(oid, NULL, 0, data);
@@ -651,20 +696,12 @@ static void batch_objects_command(struct batch_options *opt,
        struct queued_cmd *queued_cmd = NULL;
        size_t alloc = 0, nr = 0;
 
-       while (1) {
-               int i, ret;
+       while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) {
+               int i;
                const struct parse_cmd *cmd = NULL;
                const char *p = NULL, *cmd_end;
                struct queued_cmd call = {0};
 
-               if (opt->nul_terminated)
-                       ret = strbuf_getline_nul(&input, stdin);
-               else
-                       ret = strbuf_getline(&input, stdin);
-
-               if (ret)
-                       break;
-
                if (!input.len)
                        die(_("empty command in input"));
                if (isspace(*input.buf))
@@ -734,9 +771,8 @@ static int batch_objects(struct batch_options *opt)
         */
        memset(&data, 0, sizeof(data));
        data.mark_query = 1;
-       strbuf_expand(&output,
+       expand_format(&output,
                      opt->format ? opt->format : DEFAULT_FORMAT,
-                     expand_format,
                      &data);
        data.mark_query = 0;
        strbuf_release(&output);
@@ -759,10 +795,10 @@ static int batch_objects(struct batch_options *opt)
                if (!memcmp(&data.info, &empty, sizeof(empty)))
                        data.skip_object_info = 1;
 
-               if (has_promisor_remote())
+               if (repo_has_promisor_remote(the_repository))
                        warning("This repository uses promisor remotes. Some objects may not be loaded.");
 
-               read_replace_refs = 0;
+               disable_replace_refs();
 
                cb.opt = opt;
                cb.expand = &data;
@@ -808,16 +844,7 @@ static int batch_objects(struct batch_options *opt)
                goto cleanup;
        }
 
-       while (1) {
-               int ret;
-               if (opt->nul_terminated)
-                       ret = strbuf_getline_nul(&input, stdin);
-               else
-                       ret = strbuf_getline(&input, stdin);
-
-               if (ret == EOF)
-                       break;
-
+       while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) {
                if (data.split_on_whitespace) {
                        /*
                         * Split at first whitespace, tying off the beginning
@@ -842,12 +869,13 @@ static int batch_objects(struct batch_options *opt)
        return retval;
 }
 
-static int git_cat_file_config(const char *var, const char *value, void *cb)
+static int git_cat_file_config(const char *var, const char *value,
+                              const struct config_context *ctx, void *cb)
 {
        if (userdiff_config(var, value) < 0)
                return -1;
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static int batch_option_callback(const struct option *opt,
@@ -886,16 +914,18 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
        const char *exp_type = NULL, *obj_name = NULL;
        struct batch_options batch = {0};
        int unknown_type = 0;
+       int input_nul_terminated = 0;
+       int nul_terminated = 0;
 
        const char * const usage[] = {
                N_("git cat-file <type> <object>"),
                N_("git cat-file (-e | -p) <object>"),
                N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
-               N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
-                  "             [--buffer] [--follow-symlinks] [--unordered]\n"
-                  "             [--textconv | --filters]"),
                N_("git cat-file (--textconv | --filters)\n"
                   "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"),
+               N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
+                  "             [--buffer] [--follow-symlinks] [--unordered]\n"
+                  "             [--textconv | --filters] [-Z]"),
                NULL
        };
        const struct option options[] = {
@@ -922,7 +952,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                        N_("like --batch, but don't emit <contents>"),
                        PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
                        batch_option_callback),
-               OPT_BOOL('z', NULL, &batch.nul_terminated, N_("stdin is NUL-terminated")),
+               OPT_BOOL_F('z', NULL, &input_nul_terminated, N_("stdin is NUL-terminated"),
+                       PARSE_OPT_HIDDEN),
+               OPT_BOOL('Z', NULL, &nul_terminated, N_("stdin and stdout is NUL-terminated")),
                OPT_CALLBACK_F(0, "batch-command", &batch, N_("format"),
                        N_("read commands from stdin"),
                        PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
@@ -981,9 +1013,18 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
        else if (batch.all_objects)
                usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
                               "--batch-all-objects");
-       else if (batch.nul_terminated)
+       else if (input_nul_terminated)
                usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
                               "-z");
+       else if (nul_terminated)
+               usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
+                              "-Z");
+
+       batch.input_delim = batch.output_delim = '\n';
+       if (input_nul_terminated)
+               batch.input_delim = '\0';
+       if (nul_terminated)
+               batch.input_delim = batch.output_delim = '\0';
 
        /* Batch defaults */
        if (batch.buffer_output < 0)
index dd833977864d79a3abde9fc66042d7bda56b8e82..c1da1d184e9f6e24687f751bc67f88641f7083b0 100644 (file)
@@ -1,17 +1,23 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "attr.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "quote.h"
+#include "repository.h"
+#include "setup.h"
 #include "parse-options.h"
+#include "write-or-die.h"
 
 static int all_attrs;
 static int cached_attrs;
 static int stdin_paths;
+static char *source;
 static const char * const check_attr_usage[] = {
-N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
-N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
+N_("git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>..."),
+N_("git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"),
 NULL
 };
 
@@ -23,6 +29,7 @@ static const struct option check_attr_options[] = {
        OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
        OPT_BOOL('z', NULL, &nul_term_line,
                 N_("terminate input and output records by a NUL character")),
+       OPT_STRING(0, "source", &source, N_("<tree-ish>"), N_("which tree-ish to check attributes at")),
        OPT_END()
 };
 
@@ -55,10 +62,10 @@ static void output_attr(struct attr_check *check, const char *file)
        }
 }
 
-static void check_attr(const char *prefix,
-                      struct attr_check *check,
+static void check_attr(const char *prefix, struct attr_check *check,
                       int collect_all,
                       const char *file)
+
 {
        char *full_path =
                prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
@@ -73,8 +80,7 @@ static void check_attr(const char *prefix,
        free(full_path);
 }
 
-static void check_attr_stdin_paths(const char *prefix,
-                                  struct attr_check *check,
+static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
                                   int collect_all)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -105,6 +111,7 @@ static NORETURN void error_with_usage(const char *msg)
 int cmd_check_attr(int argc, const char **argv, const char *prefix)
 {
        struct attr_check *check;
+       struct object_id initialized_oid;
        int cnt, i, doubledash, filei;
 
        if (!is_bare_repository())
@@ -115,7 +122,10 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, check_attr_options,
                             check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
 
-       if (read_cache() < 0) {
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
+
+       if (repo_read_index(the_repository) < 0) {
                die("invalid cache");
        }
 
@@ -176,6 +186,12 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (source) {
+               if (repo_get_oid_tree(the_repository, source, &initialized_oid))
+                       die("%s: not a valid tree-ish source", source);
+               set_git_attr_source(source);
+       }
+
        if (stdin_paths)
                check_attr_stdin_paths(prefix, check, all_attrs);
        else {
index 21912569650d11c07297193a24e041bce035a284..906cd967536c6f3a90c0a57cb4f3649e7b0da804 100644 (file)
@@ -1,12 +1,14 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "dir.h"
+#include "gettext.h"
 #include "quote.h"
 #include "pathspec.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "submodule.h"
+#include "write-or-die.h"
 
 static int quiet, verbose, stdin_paths, show_non_matching, no_index;
 static const char * const check_ignore_usage[] = {
@@ -179,7 +181,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)
                die(_("--non-matching is only valid with --verbose"));
 
        /* read_cache() is only necessary so we can watch out for submodules. */
-       if (!no_index && read_cache() < 0)
+       if (!no_index && repo_read_index(the_repository) < 0)
                die(_("index file corrupt"));
 
        setup_standard_excludes(&dir);
index 7dc47e47932c7d2e76128e54f49fa2ea0c570d62..b8a05b8e07b523953dfe9e8d6e572346a27d7791 100644 (file)
@@ -1,8 +1,12 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
+#include "ident.h"
 #include "mailmap.h"
 #include "parse-options.h"
+#include "strbuf.h"
 #include "string-list.h"
+#include "write-or-die.h"
 
 static int use_stdin;
 static const char * const check_mailmap_usage[] = {
index fd0e5f86832a0ed4d9c08512291c836938f2bec2..5eb6bdc3f691e8a71e07fc2be80ee94e3599f522 100644 (file)
@@ -2,9 +2,9 @@
  * GIT - The information manager from hell
  */
 
-#include "cache.h"
-#include "refs.h"
 #include "builtin.h"
+#include "refs.h"
+#include "setup.h"
 #include "strbuf.h"
 
 static const char builtin_check_ref_format_usage[] =
@@ -60,6 +60,8 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
        char *to_free = NULL;
        int ret = 1;
 
+       BUG_ON_NON_EMPTY_PREFIX(prefix);
+
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(builtin_check_ref_format_usage);
 
index ede7dc32a43c01a78a0074e1bddd5ba18b487568..6b62b5375bd2bf0d998f74ba2c63c071a5e76e73 100644 (file)
@@ -1,9 +1,11 @@
 #include "builtin.h"
 #include "config.h"
 #include "entry.h"
+#include "gettext.h"
 #include "parallel-checkout.h"
 #include "parse-options.h"
 #include "pkt-line.h"
+#include "read-cache-ll.h"
 
 static void packet_to_pc_item(const char *buffer, int len,
                              struct parallel_checkout_item *pc_item)
index 97e06e8c52c012c918558cd73aa65d5750b8118b..2e086a204dc7b19271cdeb2733c62a64066ab304 100644 (file)
@@ -4,22 +4,26 @@
  * Copyright (C) 2005 Linus Torvalds
  *
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
-#include "dir.h"
+#include "gettext.h"
 #include "lockfile.h"
 #include "quote.h"
+#include "repository.h"
 #include "cache-tree.h"
 #include "parse-options.h"
 #include "entry.h"
 #include "parallel-checkout.h"
+#include "read-cache-ll.h"
+#include "setup.h"
+#include "sparse-index.h"
 
 #define CHECKOUT_ALL 4
 static int nul_term_line;
 static int checkout_stage; /* default to checkout stage0 */
 static int ignore_skip_worktree; /* default to 0 */
-static int to_tempfile;
+static int to_tempfile = -1;
 static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
 
 static struct checkout state = CHECKOUT_INIT;
@@ -65,7 +69,7 @@ static void write_tempfile_record(const char *name, const char *prefix)
 static int checkout_file(const char *name, const char *prefix)
 {
        int namelen = strlen(name);
-       int pos = cache_name_pos(name, namelen);
+       int pos = index_name_pos(&the_index, name, namelen);
        int has_same_name = 0;
        int is_file = 0;
        int is_skipped = 1;
@@ -75,8 +79,8 @@ static int checkout_file(const char *name, const char *prefix)
        if (pos < 0)
                pos = -pos - 1;
 
-       while (pos < active_nr) {
-               struct cache_entry *ce = active_cache[pos];
+       while (pos < the_index.cache_nr) {
+               struct cache_entry *ce = the_index.cache[pos];
                if (ce_namelen(ce) != namelen ||
                    memcmp(ce->name, name, namelen))
                        break;
@@ -136,8 +140,8 @@ static int checkout_all(const char *prefix, int prefix_length)
        int i, errs = 0;
        struct cache_entry *last_ce = NULL;
 
-       for (i = 0; i < active_nr ; i++) {
-               struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < the_index.cache_nr ; i++) {
+               struct cache_entry *ce = the_index.cache[i];
 
                if (S_ISSPARSEDIR(ce->ce_mode)) {
                        if (!ce_skip_worktree(ce))
@@ -151,7 +155,7 @@ static int checkout_all(const char *prefix, int prefix_length)
                         */
                        if (ignore_skip_worktree) {
                                ensure_full_index(&the_index);
-                               ce = active_cache[i];
+                               ce = the_index.cache[i];
                        }
                }
 
@@ -188,15 +192,16 @@ static const char * const builtin_checkout_index_usage[] = {
 static int option_parse_stage(const struct option *opt,
                              const char *arg, int unset)
 {
+       int *stage = opt->value;
+
        BUG_ON_OPT_NEG(unset);
 
        if (!strcmp(arg, "all")) {
-               to_tempfile = 1;
-               checkout_stage = CHECKOUT_ALL;
+               *stage = CHECKOUT_ALL;
        } else {
                int ch = arg[0];
                if ('1' <= ch && ch <= '3')
-                       checkout_stage = arg[0] - '0';
+                       *stage = arg[0] - '0';
                else
                        die(_("stage should be between 1 and 3 or all"));
        }
@@ -234,7 +239,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                        N_("write the content to temporary files")),
                OPT_STRING(0, "prefix", &state.base_dir, N_("string"),
                        N_("when creating files, prepend <string>")),
-               OPT_CALLBACK_F(0, "stage", NULL, "(1|2|3|all)",
+               OPT_CALLBACK_F(0, "stage", &checkout_stage, "(1|2|3|all)",
                        N_("copy out the files from named stage"),
                        PARSE_OPT_NONEG, option_parse_stage),
                OPT_END()
@@ -249,7 +254,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
-       if (read_cache() < 0) {
+       if (repo_read_index(the_repository) < 0) {
                die("invalid cache");
        }
 
@@ -264,13 +269,20 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                state.base_dir = "";
        state.base_dir_len = strlen(state.base_dir);
 
+       if (to_tempfile < 0)
+               to_tempfile = (checkout_stage == CHECKOUT_ALL);
+       if (!to_tempfile && checkout_stage == CHECKOUT_ALL)
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--stage=all", "--no-temp");
+
        /*
         * when --prefix is specified we do not want to update cache.
         */
        if (index_opt && !state.base_dir_len && !to_tempfile) {
                state.refresh_cache = 1;
                state.istate = &the_index;
-               hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+               repo_hold_locked_index(the_repository, &lock_file,
+                                      LOCK_DIE_ON_ERROR);
        }
 
        get_parallel_checkout_configs(&pc_workers, &pc_threshold);
index 2a132392fbe7478c808b01d06039112fc321a55e..a6e30931b5c809da06c751ba6ef43e47a5a3e0ea 100644 (file)
@@ -1,7 +1,6 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
-#include "blob.h"
 #include "branch.h"
 #include "cache-tree.h"
 #include "checkout.h"
@@ -9,19 +8,28 @@
 #include "config.h"
 #include "diff.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "hook.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "lockfile.h"
+#include "mem-pool.h"
 #include "merge-recursive.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
 #include "refs.h"
 #include "remote.h"
 #include "resolve-undo.h"
 #include "revision.h"
-#include "run-command.h"
+#include "setup.h"
 #include "submodule.h"
-#include "submodule-config.h"
+#include "symlinks.h"
+#include "trace2.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
@@ -29,6 +37,7 @@
 #include "xdiff-interface.h"
 #include "entry.h"
 #include "parallel-checkout.h"
+#include "add-interactive.h"
 
 static const char * const checkout_usage[] = {
        N_("git checkout [<options>] <branch>"),
@@ -74,7 +83,7 @@ struct checkout_opts {
        const char *ignore_unmerged_opt;
        int ignore_unmerged;
        int pathspec_file_nul;
-       const char *pathspec_from_file;
+       char *pathspec_from_file;
 
        const char *new_branch;
        const char *new_branch_force;
@@ -148,9 +157,9 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
         * entry in place. Whether it is UPTODATE or not, checkout_entry will
         * do the right thing.
         */
-       pos = cache_name_pos(ce->name, ce->ce_namelen);
+       pos = index_name_pos(&the_index, ce->name, ce->ce_namelen);
        if (pos >= 0) {
-               struct cache_entry *old = active_cache[pos];
+               struct cache_entry *old = the_index.cache[pos];
                if (ce->ce_mode == old->ce_mode &&
                    !ce_intent_to_add(old) &&
                    oideq(&ce->oid, &old->oid)) {
@@ -160,7 +169,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
                }
        }
 
-       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+       add_index_entry(&the_index, ce,
+                       ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
 }
 
@@ -178,8 +188,8 @@ static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
 
 static int skip_same_name(const struct cache_entry *ce, int pos)
 {
-       while (++pos < active_nr &&
-              !strcmp(active_cache[pos]->name, ce->name))
+       while (++pos < the_index.cache_nr &&
+              !strcmp(the_index.cache[pos]->name, ce->name))
                ; /* skip */
        return pos;
 }
@@ -187,9 +197,9 @@ static int skip_same_name(const struct cache_entry *ce, int pos)
 static int check_stage(int stage, const struct cache_entry *ce, int pos,
                       int overlay_mode)
 {
-       while (pos < active_nr &&
-              !strcmp(active_cache[pos]->name, ce->name)) {
-               if (ce_stage(active_cache[pos]) == stage)
+       while (pos < the_index.cache_nr &&
+              !strcmp(the_index.cache[pos]->name, ce->name)) {
+               if (ce_stage(the_index.cache[pos]) == stage)
                        return 0;
                pos++;
        }
@@ -206,8 +216,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
        unsigned seen = 0;
        const char *name = ce->name;
 
-       while (pos < active_nr) {
-               ce = active_cache[pos];
+       while (pos < the_index.cache_nr) {
+               ce = the_index.cache[pos];
                if (strcmp(name, ce->name))
                        break;
                seen |= (1 << ce_stage(ce));
@@ -223,15 +233,15 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
                          const struct checkout *state, int *nr_checkouts,
                          int overlay_mode)
 {
-       while (pos < active_nr &&
-              !strcmp(active_cache[pos]->name, ce->name)) {
-               if (ce_stage(active_cache[pos]) == stage)
-                       return checkout_entry(active_cache[pos], state,
+       while (pos < the_index.cache_nr &&
+              !strcmp(the_index.cache[pos]->name, ce->name)) {
+               if (ce_stage(the_index.cache[pos]) == stage)
+                       return checkout_entry(the_index.cache[pos], state,
                                              NULL, nr_checkouts);
                pos++;
        }
        if (!overlay_mode) {
-               unlink_entry(ce);
+               unlink_entry(ce, NULL);
                return 0;
        }
        if (stage == 2)
@@ -243,7 +253,7 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
 static int checkout_merged(int pos, const struct checkout *state,
                           int *nr_checkouts, struct mem_pool *ce_mem_pool)
 {
-       struct cache_entry *ce = active_cache[pos];
+       struct cache_entry *ce = the_index.cache[pos];
        const char *path = ce->name;
        mmfile_t ancestor, ours, theirs;
        enum ll_merge_result merge_status;
@@ -256,7 +266,7 @@ static int checkout_merged(int pos, const struct checkout *state,
        int renormalize = 0;
 
        memset(threeway, 0, sizeof(threeway));
-       while (pos < active_nr) {
+       while (pos < the_index.cache_nr) {
                int stage;
                stage = ce_stage(ce);
                if (!stage || strcmp(path, ce->name))
@@ -265,7 +275,7 @@ static int checkout_merged(int pos, const struct checkout *state,
                if (stage == 2)
                        mode = create_ce_mode(ce->ce_mode);
                pos++;
-               ce = active_cache[pos];
+               ce = the_index.cache[pos];
        }
        if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
                return error(_("path '%s' does not have necessary versions"), path);
@@ -391,8 +401,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
        if (pc_workers > 1)
                init_parallel_checkout();
 
-       for (pos = 0; pos < active_nr; pos++) {
-               struct cache_entry *ce = active_cache[pos];
+       for (pos = 0; pos < the_index.cache_nr; pos++) {
+               struct cache_entry *ce = the_index.cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce)) {
                                errs |= checkout_entry(ce, &state,
@@ -430,8 +440,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
                                              "Updated %d paths from %s",
                                              nr_checkouts),
                                   nr_checkouts,
-                                  find_unique_abbrev(&opts->source_tree->object.oid,
-                                                     DEFAULT_ABBREV));
+                                  repo_find_unique_abbrev(the_repository, &opts->source_tree->object.oid,
+                                                          DEFAULT_ABBREV));
                else if (!nr_unmerged || nr_checkouts)
                        fprintf_ln(stderr, Q_("Updated %d path from the index",
                                              "Updated %d paths from the index",
@@ -487,18 +497,40 @@ static int checkout_paths(const struct checkout_opts *opts,
                die(_("'%s' must be used when '%s' is not specified"),
                    "--worktree", "--source");
 
-       if (opts->checkout_index && !opts->checkout_worktree &&
-           opts->writeout_stage)
-               die(_("'%s' or '%s' cannot be used with %s"),
-                   "--ours", "--theirs", "--staged");
+       /*
+        * Reject --staged option to the restore command when combined with
+        * merge-related options. Use the accept_ref flag to distinguish it
+        * from the checkout command, which does not accept --staged anyway.
+        *
+        * `restore --ours|--theirs --worktree --staged` could mean resolving
+        * conflicted paths to one side in both the worktree and the index,
+        * but does not currently.
+        *
+        * `restore --merge|--conflict=<style>` already recreates conflicts
+        * in both the worktree and the index, so adding --staged would be
+        * meaningless.
+        */
+       if (!opts->accept_ref && opts->checkout_index) {
+               if (opts->writeout_stage)
+                       die(_("'%s' or '%s' cannot be used with %s"),
+                           "--ours", "--theirs", "--staged");
+
+               if (opts->merge)
+                       die(_("'%s' or '%s' cannot be used with %s"),
+                           "--merge", "--conflict", "--staged");
+       }
 
-       if (opts->checkout_index && !opts->checkout_worktree &&
-           opts->merge)
-               die(_("'%s' or '%s' cannot be used with %s"),
-                   "--merge", "--conflict", "--staged");
+       /*
+        * recreating unmerged index entries and writing out data from
+        * unmerged index entries would make no sense when checking out
+        * of a tree-ish.
+        */
+       if ((opts->merge || opts->writeout_stage) && opts->source_tree)
+               die(_("'%s', '%s', or '%s' cannot be used when checking out of a tree"),
+                   "--merge", "--ours", "--theirs");
 
        if (opts->patch_mode) {
-               const char *patch_mode;
+               enum add_p_mode patch_mode;
                const char *rev = new_branch_info->name;
                char rev_oid[GIT_MAX_HEXSZ + 1];
 
@@ -516,23 +548,26 @@ static int checkout_paths(const struct checkout_opts *opts,
                        rev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid);
 
                if (opts->checkout_index && opts->checkout_worktree)
-                       patch_mode = "--patch=checkout";
+                       patch_mode = ADD_P_CHECKOUT;
                else if (opts->checkout_index && !opts->checkout_worktree)
-                       patch_mode = "--patch=reset";
+                       patch_mode = ADD_P_RESET;
                else if (!opts->checkout_index && opts->checkout_worktree)
-                       patch_mode = "--patch=worktree";
+                       patch_mode = ADD_P_WORKTREE;
                else
                        BUG("either flag must have been set, worktree=%d, index=%d",
                            opts->checkout_worktree, opts->checkout_index);
-               return run_add_interactive(rev, patch_mode, &opts->pathspec);
+               return !!run_add_p(the_repository, patch_mode, rev,
+                                  &opts->pathspec);
        }
 
        repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
-       if (read_cache_preload(&opts->pathspec) < 0)
+       if (repo_read_index_preload(the_repository, &opts->pathspec, 0) < 0)
                return error(_("index file corrupt"));
 
        if (opts->source_tree)
                read_tree_some(opts->source_tree, &opts->pathspec);
+       if (opts->merge)
+               unmerge_index(&the_index, &opts->pathspec, CE_MATCHED);
 
        ps_matched = xcalloc(opts->pathspec.nr, 1);
 
@@ -540,13 +575,13 @@ static int checkout_paths(const struct checkout_opts *opts,
         * Make sure all pathspecs participated in locating the paths
         * to be checked out.
         */
-       for (pos = 0; pos < active_nr; pos++)
+       for (pos = 0; pos < the_index.cache_nr; pos++)
                if (opts->overlay_mode)
-                       mark_ce_for_checkout_overlay(active_cache[pos],
+                       mark_ce_for_checkout_overlay(the_index.cache[pos],
                                                     ps_matched,
                                                     opts);
                else
-                       mark_ce_for_checkout_no_overlay(active_cache[pos],
+                       mark_ce_for_checkout_no_overlay(the_index.cache[pos],
                                                        ps_matched,
                                                        opts);
 
@@ -556,13 +591,9 @@ static int checkout_paths(const struct checkout_opts *opts,
        }
        free(ps_matched);
 
-       /* "checkout -m path" to recreate conflicted state */
-       if (opts->merge)
-               unmerge_marked_index(&the_index);
-
        /* Any unmerged paths? */
-       for (pos = 0; pos < active_nr; pos++) {
-               const struct cache_entry *ce = active_cache[pos];
+       for (pos = 0; pos < the_index.cache_nr; pos++) {
+               const struct cache_entry *ce = the_index.cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce))
                                continue;
@@ -637,14 +668,16 @@ static void describe_detached_head(const char *msg, struct commit *commit)
 {
        struct strbuf sb = STRBUF_INIT;
 
-       if (!parse_commit(commit))
+       if (!repo_parse_commit(the_repository, commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
        if (print_sha1_ellipsis()) {
                fprintf(stderr, "%s %s... %s\n", msg,
-                       find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+                       repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
+                       sb.buf);
        } else {
                fprintf(stderr, "%s %s %s\n", msg,
-                       find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+                       repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
+                       sb.buf);
        }
        strbuf_release(&sb);
 }
@@ -698,7 +731,8 @@ static void setup_branch_path(struct branch_info *branch)
         * If this is a ref, resolve it; otherwise, look up the OID for our
         * expression.  Failure here is okay.
         */
-       if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname, 0))
+       if (!repo_dwim_ref(the_repository, branch->name, strlen(branch->name),
+                          &branch->oid, &branch->refname, 0))
                repo_get_oid_committish(the_repository, branch->name, &branch->oid);
 
        strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
@@ -722,7 +756,7 @@ static void init_topts(struct unpack_trees_options *topts, int merge,
 
        setup_unpack_trees_porcelain(topts, "checkout");
 
-       topts->initial_checkout = is_cache_unborn();
+       topts->initial_checkout = is_index_unborn(&the_index);
        topts->update = 1;
        topts->merge = 1;
        topts->quiet = merge && old_commit;
@@ -740,17 +774,18 @@ static int merge_working_tree(const struct checkout_opts *opts,
        struct lock_file lock_file = LOCK_INIT;
        struct tree *new_tree;
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
-       if (read_cache_preload(NULL) < 0)
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
+       if (repo_read_index_preload(the_repository, NULL, 0) < 0)
                return error(_("index file corrupt"));
 
-       resolve_undo_clear();
+       resolve_undo_clear_index(&the_index);
        if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
                if (new_branch_info->commit)
                        BUG("'switch --orphan' should never accept a commit as starting point");
                new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
        } else
-               new_tree = get_commit_tree(new_branch_info->commit);
+               new_tree = repo_get_commit_tree(the_repository,
+                                               new_branch_info->commit);
        if (opts->discard_changes) {
                ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
                if (ret)
@@ -761,9 +796,9 @@ static int merge_working_tree(const struct checkout_opts *opts,
                struct unpack_trees_options topts;
                const struct object_id *old_commit_oid;
 
-               refresh_cache(REFRESH_QUIET);
+               refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
 
-               if (unmerged_cache()) {
+               if (unmerged_index(&the_index)) {
                        error(_("you need to resolve your current index first"));
                        return 1;
                }
@@ -812,7 +847,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
                         */
                        if (!old_branch_info->commit)
                                return 1;
-                       old_tree = get_commit_tree(old_branch_info->commit);
+                       old_tree = repo_get_commit_tree(the_repository,
+                                                       old_branch_info->commit);
 
                        if (repo_index_has_changes(the_repository, old_tree, &sb))
                                die(_("cannot continue with staged changes in "
@@ -832,7 +868,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                         * entries in the index.
                         */
 
-                       add_files_to_cache(NULL, NULL, 0);
+                       add_files_to_cache(the_repository, NULL, NULL, 0, 0);
                        init_merge_options(&o, the_repository);
                        o.verbosity = 0;
                        work = write_in_core_index_as_tree(the_repository);
@@ -867,7 +903,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                }
        }
 
-       if (!cache_tree_fully_valid(active_cache_tree))
+       if (!cache_tree_fully_valid(the_index.cache_tree))
                cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
 
        if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
@@ -884,7 +920,7 @@ static void report_tracking(struct branch_info *new_branch_info)
        struct strbuf sb = STRBUF_INIT;
        struct branch *branch = branch_get(new_branch_info->name);
 
-       if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL))
+       if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL, 1))
                return;
        fputs(sb.buf, stdout);
        strbuf_release(&sb);
@@ -1001,7 +1037,7 @@ static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
        strbuf_addstr(sb, "  ");
        strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV);
        strbuf_addch(sb, ' ');
-       if (!parse_commit(commit))
+       if (!repo_parse_commit(the_repository, commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
        strbuf_addch(sb, '\n');
 }
@@ -1057,7 +1093,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
                        " git branch <new-branch-name> %s\n\n",
                        /* Give ngettext() the count */
                        lost),
-                       find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+                       repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
 }
 
 /*
@@ -1157,11 +1193,14 @@ static int switch_branches(const struct checkout_opts *opts,
        return ret || writeout_error;
 }
 
-static int git_checkout_config(const char *var, const char *value, void *cb)
+static int git_checkout_config(const char *var, const char *value,
+                              const struct config_context *ctx, void *cb)
 {
        struct checkout_opts *opts = cb;
 
        if (!strcmp(var, "diff.ignoresubmodules")) {
+               if (!value)
+                       return config_error_nonbool(var);
                handle_ignore_submodules_arg(&opts->diff_options, value);
                return 0;
        }
@@ -1173,7 +1212,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
        if (starts_with(var, "submodule."))
                return git_default_submodule_config(var, value, NULL);
 
-       return git_xmerge_config(var, value, NULL);
+       return git_xmerge_config(var, value, ctx, NULL);
 }
 
 static void setup_new_branch_info_and_source_tree(
@@ -1201,7 +1240,8 @@ static void setup_new_branch_info_and_source_tree(
                *source_tree = parse_tree_indirect(rev);
        } else {
                parse_commit_or_die(new_branch_info->commit);
-               *source_tree = get_commit_tree(new_branch_info->commit);
+               *source_tree = repo_get_commit_tree(the_repository,
+                                                   new_branch_info->commit);
        }
 }
 
@@ -1269,7 +1309,7 @@ static int parse_branchname_arg(int argc, const char **argv,
         *       between A and B, A...B names that merge base.
         *
         *   (b) If <something> is _not_ a commit, either "--" is present
-        *       or <something> is not a path, no -t or -b was given, and
+        *       or <something> is not a path, no -t or -b was given,
         *       and there is a tracking branch whose name is <something>
         *       in one and only one remote (or if the branch exists on the
         *       remote named in checkout.defaultRemote), then this is a
@@ -1319,7 +1359,7 @@ static int parse_branchname_arg(int argc, const char **argv,
        if (!strcmp(arg, "-"))
                arg = "@{-1}";
 
-       if (get_oid_mb(arg, rev)) {
+       if (repo_get_oid_mb(the_repository, arg, rev)) {
                /*
                 * Either case (3) or (4), with <something> not being
                 * a commit, or an attempt to use case (1) with an
@@ -1416,7 +1456,8 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)
        char *to_free;
        int code;
 
-       if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free, 0) == 1) {
+       if (repo_dwim_ref(the_repository, branch_info->name,
+                         strlen(branch_info->name), &oid, &to_free, 0) == 1) {
                const char *ref = to_free;
 
                if (skip_prefix(ref, "refs/tags/", &ref))
@@ -1470,6 +1511,28 @@ static void die_if_some_operation_in_progress(void)
                      "or \"git worktree add\"."));
        if (state.bisect_in_progress)
                warning(_("you are switching branch while bisecting"));
+
+       wt_status_state_free_buffers(&state);
+}
+
+/*
+ * die if attempting to checkout an existing branch that is in use
+ * in another worktree, unless ignore-other-wortrees option is given.
+ * The check is bypassed when the branch is already the current one,
+ * as it will not make things any worse.
+ */
+static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
+                                               const char *full_ref)
+{
+       int flags;
+       char *head_ref;
+
+       if (opts->ignore_other_worktrees)
+               return;
+       head_ref = resolve_refdup("HEAD", 0, NULL, &flags);
+       if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
+               die_if_checked_out(full_ref, 1);
+       free(head_ref);
 }
 
 static int checkout_branch(struct checkout_opts *opts,
@@ -1532,14 +1595,15 @@ static int checkout_branch(struct checkout_opts *opts,
        if (!opts->can_switch_when_in_progress)
                die_if_some_operation_in_progress();
 
-       if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
-           !opts->ignore_other_worktrees) {
-               int flag;
-               char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
-               if (head_ref &&
-                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path)))
-                       die_if_checked_out(new_branch_info->path, 1);
-               free(head_ref);
+       /* "git checkout <branch>" */
+       if (new_branch_info->path && !opts->force_detach && !opts->new_branch)
+               die_if_switching_to_a_branch_in_use(opts, new_branch_info->path);
+
+       /* "git checkout -B <branch>" */
+       if (opts->new_branch_force) {
+               char *full_ref = xstrfmt("refs/heads/%s", opts->new_branch);
+               die_if_switching_to_a_branch_in_use(opts, full_ref);
+               free(full_ref);
        }
 
        if (!new_branch_info->commit && opts->new_branch) {
@@ -1583,7 +1647,7 @@ static struct option *add_common_switch_branch_options(
                        parse_opt_tracking_mode),
                OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
                           PARSE_OPT_NOCOMPLETE),
-               OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+               OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unborn branch")),
                OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
                           N_("update ignored files (default)"),
                           PARSE_OPT_NOCOMPLETE),
@@ -1656,8 +1720,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
        }
 
        if (opts->conflict_style) {
+               struct key_value_info kvi = KVI_INIT;
+               struct config_context ctx = {
+                       .kvi = &kvi,
+               };
                opts->merge = 1; /* implied */
-               git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
+               git_xmerge_config("merge.conflictstyle", opts->conflict_style,
+                                 &ctx, NULL);
        }
        if (opts->force) {
                opts->discard_changes = 1;
@@ -1743,7 +1812,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
        } else if (!opts->accept_ref && opts->from_treeish) {
                struct object_id rev;
 
-               if (get_oid_mb(opts->from_treeish, &rev))
+               if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev))
                        die(_("could not resolve %s"), opts->from_treeish);
 
                setup_new_branch_info_and_source_tree(new_branch_info,
@@ -1871,6 +1940,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                            options, checkout_usage, &new_branch_info);
        branch_info_release(&new_branch_info);
        clear_pathspec(&opts.pathspec);
+       free(opts.pathspec_from_file);
        FREE_AND_NULL(options);
        return ret;
 }
index 5466636e66604ef5331a6f508d7d4a62e330d1e7..d90766cad3a0ba13c41dd0b90c03254ba5eb83ee 100644 (file)
@@ -6,12 +6,17 @@
  * Based on git-clean.sh by Pavel Roskin
  */
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
 #include "config.h"
 #include "dir.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "string-list.h"
 #include "quote.h"
 #include "column.h"
@@ -26,7 +31,7 @@ static struct string_list del_list = STRING_LIST_INIT_DUP;
 static unsigned int colopts;
 
 static const char *const builtin_clean_usage[] = {
-       N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
+       N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] [<pathspec>...]"),
        NULL
 };
 
@@ -99,7 +104,8 @@ struct menu_stuff {
 
 define_list_config_array(color_interactive_slots);
 
-static int git_clean_config(const char *var, const char *value, void *cb)
+static int git_clean_config(const char *var, const char *value,
+                           const struct config_context *ctx, void *cb)
 {
        const char *slot_name;
 
@@ -126,8 +132,10 @@ static int git_clean_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       /* inspect the color.ui config variable and others */
-       return git_color_default_config(var, value, cb);
+       if (git_color_config(var, value, cb) < 0)
+               return -1;
+
+       return git_default_config(var, value, ctx, cb);
 }
 
 static const char *clean_get_color(enum color_clean ix)
@@ -560,7 +568,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
 
 /*
  * Implement a git-add-interactive compatible UI, which is borrowed
- * from git-add--interactive.perl.
+ * from add-interactive.c.
  *
  * Return value:
  *
@@ -963,7 +971,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
        if (ignored && ignored_only)
-               die(_("-x and -X cannot be used together"));
+               die(_("options '%s' and '%s' cannot be used together"), "-x", "-X");
        if (!ignored)
                setup_standard_excludes(&dir);
        if (ignored_only)
@@ -1012,7 +1020,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                die(_("index file corrupt"));
 
        pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
@@ -1031,7 +1039,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                struct stat st;
                const char *rel;
 
-               if (!cache_name_is_other(ent->name, ent->len))
+               if (!index_name_is_other(&the_index, ent->name, ent->len))
                        continue;
 
                if (lstat(ent->name, &st))
@@ -1092,5 +1100,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        strbuf_release(&buf);
        string_list_clear(&del_list, 0);
        string_list_clear(&exclude_list, 0);
+       clear_pathspec(&pathspec);
        return (errors != 0);
 }
index d4a64e15820d005f99439fd99249515dd4703849..bad1b70ce8255156cf4745aca5e120371d39642d 100644 (file)
@@ -8,15 +8,21 @@
  * Clone a repository into a different directory that does not yet exist.
  */
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "parse-options.h"
-#include "fetch-pack.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
 #include "branch.h"
 #include "remote.h"
 #include "run-command.h"
+#include "setup.h"
 #include "connected.h"
 #include "packfile.h"
+#include "path.h"
+#include "pkt-line.h"
 #include "list-objects-filter-options.h"
 #include "hook.h"
 #include "bundle.h"
@@ -62,6 +71,7 @@ static char *remote_name = NULL;
 static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
+static const char *ref_format;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
@@ -147,14 +157,13 @@ static struct option builtin_clone_options[] = {
                    N_("any cloned submodules will be shallow")),
        OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
                   N_("separate git dir from working tree")),
+       OPT_STRING(0, "ref-format", &ref_format, N_("format"),
+                  N_("specify the reference format to use")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
                        N_("set config inside the new repository")),
        OPT_STRING_LIST(0, "server-option", &server_options,
                        N_("server-specific"), N_("option to transmit")),
-       OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
-                       TRANSPORT_FAMILY_IPV4),
-       OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
-                       TRANSPORT_FAMILY_IPV6),
+       OPT_IPVERSION(&family),
        OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
        OPT_BOOL(0, "also-filter-submodules", &option_filter_submodules,
                    N_("apply partial clone filters to submodules")),
@@ -326,8 +335,18 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
 
        iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC);
 
-       if (!iter)
+       if (!iter) {
+               if (errno == ENOTDIR) {
+                       int saved_errno = errno;
+                       struct stat st;
+
+                       if (!lstat(src->buf, &st) && S_ISLNK(st.st_mode))
+                               die(_("'%s' is a symlink, refusing to clone with --local"),
+                                   src->buf);
+                       errno = saved_errno;
+               }
                die_errno(_("failed to start iterator over '%s'"), src->buf);
+       }
 
        strbuf_addch(src, '/');
        src_len = src->len;
@@ -547,9 +566,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
                        continue;
                if (ends_with(ref->name, "^{}"))
                        continue;
-               if (!has_object_file_with_flags(&ref->old_oid,
-                                               OBJECT_INFO_QUICK |
-                                               OBJECT_INFO_SKIP_FETCH_OBJECT))
+               if (!repo_has_object_file_with_flags(the_repository, &ref->old_oid,
+                                                    OBJECT_INFO_QUICK |
+                                                    OBJECT_INFO_SKIP_FETCH_OBJECT))
                        continue;
                update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
@@ -653,9 +672,9 @@ static void update_head(const struct ref *our, const struct ref *remote,
 
 static int git_sparse_checkout_init(const char *repo)
 {
-       struct strvec argv = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        int result = 0;
-       strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL);
+       strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);
 
        /*
         * We must apply the setting in the current process
@@ -663,12 +682,12 @@ static int git_sparse_checkout_init(const char *repo)
         */
        core_apply_sparse_checkout = 1;
 
-       if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
+       cmd.git_cmd = 1;
+       if (run_command(&cmd)) {
                error(_("failed to initialize sparse-checkout"));
                result = 1;
        }
 
-       strvec_clear(&argv);
        return result;
 }
 
@@ -703,7 +722,7 @@ static int checkout(int submodule_progress, int filter_submodules)
        /* We need to be in the new work tree for the checkout */
        setup_work_tree();
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
@@ -733,45 +752,49 @@ static int checkout(int submodule_progress, int filter_submodules)
                           oid_to_hex(&oid), "1", NULL);
 
        if (!err && (option_recurse_submodules.nr > 0)) {
-               struct strvec args = STRVEC_INIT;
-               strvec_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
+               struct child_process cmd = CHILD_PROCESS_INIT;
+               strvec_pushl(&cmd.args, "submodule", "update", "--require-init",
+                            "--recursive", NULL);
 
                if (option_shallow_submodules == 1)
-                       strvec_push(&args, "--depth=1");
+                       strvec_push(&cmd.args, "--depth=1");
 
                if (max_jobs != -1)
-                       strvec_pushf(&args, "--jobs=%d", max_jobs);
+                       strvec_pushf(&cmd.args, "--jobs=%d", max_jobs);
 
                if (submodule_progress)
-                       strvec_push(&args, "--progress");
+                       strvec_push(&cmd.args, "--progress");
 
                if (option_verbosity < 0)
-                       strvec_push(&args, "--quiet");
+                       strvec_push(&cmd.args, "--quiet");
 
                if (option_remote_submodules) {
-                       strvec_push(&args, "--remote");
-                       strvec_push(&args, "--no-fetch");
+                       strvec_push(&cmd.args, "--remote");
+                       strvec_push(&cmd.args, "--no-fetch");
                }
 
                if (filter_submodules && filter_options.choice)
-                       strvec_pushf(&args, "--filter=%s",
+                       strvec_pushf(&cmd.args, "--filter=%s",
                                     expand_list_objects_filter_spec(&filter_options));
 
                if (option_single_branch >= 0)
-                       strvec_push(&args, option_single_branch ?
+                       strvec_push(&cmd.args, option_single_branch ?
                                               "--single-branch" :
                                               "--no-single-branch");
 
-               err = run_command_v_opt(args.v, RUN_GIT_CMD);
-               strvec_clear(&args);
+               cmd.git_cmd = 1;
+               err = run_command(&cmd);
        }
 
        return err;
 }
 
-static int git_clone_config(const char *k, const char *v, void *cb)
+static int git_clone_config(const char *k, const char *v,
+                           const struct config_context *ctx, void *cb)
 {
        if (!strcmp(k, "clone.defaultremotename")) {
+               if (!v)
+                       return config_error_nonbool(k);
                free(remote_name);
                remote_name = xstrdup(v);
        }
@@ -780,17 +803,19 @@ static int git_clone_config(const char *k, const char *v, void *cb)
        if (!strcmp(k, "clone.filtersubmodules"))
                config_filter_submodules = git_config_bool(k, v);
 
-       return git_default_config(k, v, cb);
+       return git_default_config(k, v, ctx, cb);
 }
 
-static int write_one_config(const char *key, const char *value, void *data)
+static int write_one_config(const char *key, const char *value,
+                           const struct config_context *ctx,
+                           void *data)
 {
        /*
         * give git_clone_config a chance to write config values back to the
         * environment, since git_config_set_multivar_gently only deals with
         * config-file writes
         */
-       int apply_failed = git_clone_config(key, value, data);
+       int apply_failed = git_clone_config(key, value, ctx, data);
        if (apply_failed)
                return apply_failed;
 
@@ -864,11 +889,15 @@ static void write_refspec_config(const char *src_ref_prefix,
 
 static void dissociate_from_references(void)
 {
-       static const char* argv[] = { "repack", "-a", "-d", NULL };
        char *alternates = git_pathdup("objects/info/alternates");
 
        if (!access(alternates, F_OK)) {
-               if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))
+               struct child_process cmd = CHILD_PROCESS_INIT;
+
+               cmd.git_cmd = 1;
+               cmd.no_stdin = 1;
+               strvec_pushl(&cmd.args, "repack", "-a", "-d", NULL);
+               if (run_command(&cmd))
                        die(_("cannot repack to clean up"));
                if (unlink(alternates) && errno != ENOENT)
                        die_errno(_("cannot unlink temporary alternates file"));
@@ -887,6 +916,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        int is_bundle = 0, is_local;
        int reject_shallow = 0;
        const char *repo_name, *repo, *work_tree, *git_dir;
+       char *repo_to_free = NULL;
        char *path = NULL, *dir, *display_repo = NULL;
        int dest_exists, real_dest_exists = 0;
        const struct ref *refs, *remote_head;
@@ -903,6 +933,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        int err = 0, complete_refs_before_fetch = 1;
        int submodule_progress;
        int filter_submodules = 0;
+       int hash_algo;
+       unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+       const int do_not_override_repo_unix_permissions = -1;
 
        struct transport_ls_refs_options transport_ls_refs_options =
                TRANSPORT_LS_REFS_OPTIONS_INIT;
@@ -927,6 +960,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_single_branch == -1)
                option_single_branch = deepen ? 1 : 0;
 
+       if (ref_format) {
+               ref_storage_format = ref_storage_format_by_name(ref_format);
+               if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), ref_format);
+       }
+
        if (option_mirror)
                option_bare = 1;
 
@@ -937,14 +976,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        }
 
        if (bundle_uri && deepen)
-               die(_("--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-exclude"));
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--bundle-uri",
+                   "--depth/--shallow-since/--shallow-exclude");
 
        repo_name = argv[0];
 
        path = get_repo_path(repo_name, &is_bundle);
        if (path) {
                FREE_AND_NULL(path);
-               repo = absolute_pathdup(repo_name);
+               repo = repo_to_free = absolute_pathdup(repo_name);
        } else if (strchr(repo_name, ':')) {
                repo = repo_name;
                display_repo = transport_anonymize_url(repo);
@@ -1069,8 +1110,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                }
        }
 
-       init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
-               INIT_DB_QUIET);
+       /*
+        * Initialize the repository, but skip initializing the reference
+        * database. We do not yet know about the object format of the
+        * repository, and reference backends may persist that information into
+        * their on-disk data structures.
+        */
+       init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
+               ref_storage_format, NULL,
+               do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
 
        if (real_git_dir) {
                free((char *)git_dir);
@@ -1157,10 +1205,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_required_reference.nr || option_optional_reference.nr)
                setup_reference();
 
-       if (option_sparse_checkout && git_sparse_checkout_init(dir))
-               return 1;
-
-       remote = remote_get(remote_name);
+       remote = remote_get_early(remote_name);
 
        refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
                        branch_top.buf);
@@ -1238,19 +1283,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (transport->smart_options && !deepen && !filter_options.choice)
                transport->smart_options->check_self_contained_and_connected = 1;
 
-       /*
-        * Before fetching from the remote, download and install bundle
-        * data from the --bundle-uri option.
-        */
-       if (bundle_uri) {
-               /* At this point, we need the_repository to match the cloned repo. */
-               if (repo_init(the_repository, git_dir, work_tree))
-                       warning(_("failed to initialize the repo, skipping bundle URI"));
-               else if (fetch_bundle_uri(the_repository, bundle_uri))
-                       warning(_("failed to fetch objects from bundle URI '%s'"),
-                               bundle_uri);
-       }
-
        strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
        refspec_ref_prefixes(&remote->fetch,
                             &transport_ls_refs_options.ref_prefixes);
@@ -1263,18 +1295,55 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
 
+       /*
+        * Now that we know what algorithm the remote side is using, let's set
+        * ours to the same thing.
+        */
+       hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+       initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1);
+       repo_set_hash_algo(the_repository, hash_algo);
+       create_reference_database(the_repository->ref_storage_format, NULL, 1);
+
+       /*
+        * Before fetching from the remote, download and install bundle
+        * data from the --bundle-uri option.
+        */
+       if (bundle_uri) {
+               int has_heuristic = 0;
+
+               /* At this point, we need the_repository to match the cloned repo. */
+               if (repo_init(the_repository, git_dir, work_tree))
+                       warning(_("failed to initialize the repo, skipping bundle URI"));
+               else if (fetch_bundle_uri(the_repository, bundle_uri, &has_heuristic))
+                       warning(_("failed to fetch objects from bundle URI '%s'"),
+                               bundle_uri);
+               else if (has_heuristic)
+                       git_config_set_gently("fetch.bundleuri", bundle_uri);
+       } else {
+               /*
+               * Populate transport->got_remote_bundle_uri and
+               * transport->bundle_uri. We might get nothing.
+               */
+               transport_get_remote_bundle_uri(transport);
+
+               if (transport->bundles &&
+                   hashmap_get_size(&transport->bundles->bundles)) {
+                       /* At this point, we need the_repository to match the cloned repo. */
+                       if (repo_init(the_repository, git_dir, work_tree))
+                               warning(_("failed to initialize the repo, skipping bundle URI"));
+                       else if (fetch_bundle_list(the_repository,
+                                                  transport->bundles))
+                               warning(_("failed to fetch advertised bundles"));
+               } else {
+                       clear_bundle_list(transport->bundles);
+                       FREE_AND_NULL(transport->bundles);
+               }
+       }
+
        if (refs)
                mapped_refs = wanted_peer_refs(refs, &remote->fetch);
 
        if (mapped_refs) {
-               int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
-
-               /*
-                * Now that we know what algorithm the remote side is using,
-                * let's set ours to the same thing.
-                */
-               initialize_repository_version(hash_algo, 1);
-               repo_set_hash_algo(the_repository, hash_algo);
                /*
                 * transport_get_remote_refs() may return refs with null sha-1
                 * in mapped_refs (see struct transport->get_refs_list
@@ -1375,6 +1444,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                dissociate_from_references();
        }
 
+       if (option_sparse_checkout && git_sparse_checkout_init(dir))
+               return 1;
+
        junk_mode = JUNK_LEAVE_REPO;
        err = checkout(submodule_progress, filter_submodules);
 
@@ -1387,7 +1459,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        free(unborn_head);
        free(dir);
        free(path);
-       UNLEAK(repo);
+       free(repo_to_free);
        junk_mode = JUNK_LEAVE_ALL;
 
        transport_ls_refs_options_release(&transport_ls_refs_options);
index 158fdf53d9fb9ce3694502fe6edbc609d7839282..e80218f81f94b5fc7a3e4605527556da336bb6ff 100644 (file)
@@ -1,6 +1,6 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "gettext.h"
 #include "strbuf.h"
 #include "parse-options.h"
 #include "string-list.h"
@@ -12,7 +12,8 @@ static const char * const builtin_column_usage[] = {
 };
 static unsigned int colopts;
 
-static int column_config(const char *var, const char *value, void *cb)
+static int column_config(const char *var, const char *value,
+                        const struct config_context *ctx UNUSED, void *cb)
 {
        return git_column_config(var, value, cb, &colopts);
 }
@@ -55,5 +56,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
                string_list_append(&list, sb.buf);
 
        print_columns(&list, colopts, &copts);
+       strbuf_release(&sb);
+       string_list_clear(&list, 0);
        return 0;
 }
index 51557fe786e6d1674ad5ffff87d55e7155a0c4b9..666ad574a46b1218def7201e581e969f90e57d48 100644 (file)
@@ -1,22 +1,27 @@
 #include "builtin.h"
+#include "commit.h"
 #include "config.h"
-#include "dir.h"
-#include "lockfile.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
 #include "repository.h"
 #include "commit-graph.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "progress.h"
+#include "replace-object.h"
+#include "strbuf.h"
 #include "tag.h"
+#include "trace2.h"
 
 #define BUILTIN_COMMIT_GRAPH_VERIFY_USAGE \
-       N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]")
+       N_("git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]")
 
 #define BUILTIN_COMMIT_GRAPH_WRITE_USAGE \
-       N_("git commit-graph write [--object-dir <objdir>] [--append] " \
-          "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " \
-          "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] " \
-          "<split options>")
+       N_("git commit-graph write [--object-dir <dir>] [--append]\n" \
+          "                       [--split[=<strategy>]] [--reachable | --stdin-packs | --stdin-commits]\n" \
+          "                       [--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress]\n" \
+          "                       <split options>")
 
 static const char * builtin_commit_graph_verify_usage[] = {
        BUILTIN_COMMIT_GRAPH_VERIFY_USAGE,
@@ -63,10 +68,13 @@ static int graph_verify(int argc, const char **argv, const char *prefix)
        struct commit_graph *graph = NULL;
        struct object_directory *odb = NULL;
        char *graph_name;
-       int open_ok;
+       char *chain_name;
+       enum { OPENED_NONE, OPENED_GRAPH, OPENED_CHAIN } opened = OPENED_NONE;
        int fd;
        struct stat st;
        int flags = 0;
+       int incomplete_chain = 0;
+       int ret;
 
        static struct option builtin_commit_graph_verify_options[] = {
                OPT_BOOL(0, "shallow", &opts.shallow,
@@ -95,24 +103,40 @@ static int graph_verify(int argc, const char **argv, const char *prefix)
 
        odb = find_odb(the_repository, opts.obj_dir);
        graph_name = get_commit_graph_filename(odb);
-       open_ok = open_commit_graph(graph_name, &fd, &st);
-       if (!open_ok && errno != ENOENT)
+       chain_name = get_commit_graph_chain_filename(odb);
+       if (open_commit_graph(graph_name, &fd, &st))
+               opened = OPENED_GRAPH;
+       else if (errno != ENOENT)
                die_errno(_("Could not open commit-graph '%s'"), graph_name);
+       else if (open_commit_graph_chain(chain_name, &fd, &st))
+               opened = OPENED_CHAIN;
+       else if (errno != ENOENT)
+               die_errno(_("could not open commit-graph chain '%s'"), chain_name);
 
        FREE_AND_NULL(graph_name);
+       FREE_AND_NULL(chain_name);
        FREE_AND_NULL(options);
 
-       if (open_ok)
+       if (opened == OPENED_NONE)
+               return 0;
+       else if (opened == OPENED_GRAPH)
                graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb);
        else
-               graph = read_commit_graph_one(the_repository, odb);
+               graph = load_commit_graph_chain_fd_st(the_repository, fd, &st,
+                                                     &incomplete_chain);
 
-       /* Return failure if open_ok predicted success */
        if (!graph)
-               return !!open_ok;
+               return 1;
+
+       ret = verify_commit_graph(the_repository, graph, flags);
+       free_commit_graph(graph);
 
-       UNLEAK(graph);
-       return verify_commit_graph(the_repository, graph, flags);
+       if (incomplete_chain) {
+               error("one or more commit-graph chain files could not be loaded");
+               ret |= 1;
+       }
+
+       return ret;
 }
 
 extern int read_replace_refs;
@@ -179,10 +203,11 @@ static int write_option_max_new_filters(const struct option *opt,
 }
 
 static int git_commit_graph_write_config(const char *var, const char *value,
+                                        const struct config_context *ctx,
                                         void *cb UNUSED)
 {
        if (!strcmp(var, "commitgraph.maxnewfilters"))
-               write_opts.max_new_filters = git_config_int(var, value);
+               write_opts.max_new_filters = git_config_int(var, value, ctx->kvi);
        /*
         * No need to fall-back to 'git_default_config', since this was already
         * called in 'cmd_commit_graph()'.
@@ -267,8 +292,8 @@ static int graph_write(int argc, const char **argv, const char *prefix)
 
        if (opts.reachable) {
                if (write_commit_graph_reachable(odb, flags, &write_opts))
-                       return 1;
-               return 0;
+                       result = 1;
+               goto cleanup;
        }
 
        if (opts.stdin_packs) {
@@ -302,6 +327,7 @@ cleanup:
        FREE_AND_NULL(options);
        string_list_clear(&pack_indexes, 0);
        strbuf_release(&buf);
+       oidset_clear(&commits);
        return result;
 }
 
@@ -317,7 +343,7 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       read_replace_refs = 0;
+       disable_replace_refs();
        save_commit_buffer = 0;
 
        argc = parse_options(argc, argv, prefix, options,
index 63ea3229333c8766d51c34be9aec3f4d1bab3cd9..1bb78198392e9e13db117e87a4d421e17d10b26a 100644 (file)
@@ -3,20 +3,20 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "repository.h"
 #include "commit.h"
-#include "tree.h"
-#include "builtin.h"
-#include "utf8.h"
-#include "gpg-interface.h"
 #include "parse-options.h"
 
 static const char * const commit_tree_usage[] = {
-       N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] "
-               "[(-F <file>)...] <tree>"),
+       N_("git commit-tree <tree> [(-p <parent>)...]"),
+       N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+          "                [(-F <file>)...] <tree>"),
        NULL
 };
 
@@ -36,14 +36,6 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
        commit_list_insert(parent, parents_p);
 }
 
-static int commit_tree_config(const char *var, const char *value, void *cb)
-{
-       int status = git_gpg_config(var, value, NULL);
-       if (status)
-               return status;
-       return git_default_config(var, value, cb);
-}
-
 static int parse_parent_arg_callback(const struct option *opt,
                const char *arg, int unset)
 {
@@ -52,7 +44,7 @@ static int parse_parent_arg_callback(const struct option *opt,
 
        BUG_ON_OPT_NEG_NOARG(unset, arg);
 
-       if (get_oid_commit(arg, &oid))
+       if (repo_get_oid_commit(the_repository, arg, &oid))
                die(_("not a valid object name %s"), arg);
 
        assert_oid_type(&oid, OBJ_COMMIT);
@@ -120,7 +112,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       git_config(commit_tree_config, NULL);
+       git_config(git_default_config, NULL);
 
        if (argc < 2 || !strcmp(argv[1], "-h"))
                usage_with_options(commit_tree_usage, options);
@@ -130,7 +122,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        if (argc != 1)
                die(_("must give exactly one tree"));
 
-       if (get_oid_tree(argv[0], &tree_oid))
+       if (repo_get_oid_tree(the_repository, argv[0], &tree_oid))
                die(_("not a valid object name %s"), argv[0]);
 
        if (!buffer.len) {
index d9de4ef008b5add9fed39008589167c508c67d64..65196a28278bd5afb1bbc1e68864376225868abe 100644 (file)
@@ -5,34 +5,34 @@
  * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
  */
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
+#include "advice.h"
 #include "config.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "color.h"
 #include "dir.h"
-#include "builtin.h"
+#include "editor.h"
+#include "environment.h"
 #include "diff.h"
-#include "diffcore.h"
 #include "commit.h"
+#include "gettext.h"
 #include "revision.h"
 #include "wt-status.h"
 #include "run-command.h"
-#include "hook.h"
-#include "refs.h"
-#include "log-tree.h"
 #include "strbuf.h"
-#include "utf8.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "unpack-trees.h"
-#include "quote.h"
-#include "submodule.h"
-#include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
+#include "sparse-index.h"
 #include "mailmap.h"
 #include "help.h"
 #include "commit-reach.h"
 #include "pretty.h"
 
 static const char * const builtin_commit_usage[] = {
-       N_("git commit [<options>] [--] <pathspec>..."),
+       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"
+          "           [--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>...]"),
        NULL
 };
 
 static const char * const builtin_status_usage[] = {
-       N_("git status [<options>] [--] <pathspec>..."),
+       N_("git status [<options>] [--] [<pathspec>...]"),
        NULL
 };
 
@@ -265,8 +272,8 @@ static int list_paths(struct string_list *list, const char *with_tree,
 
        /* TODO: audit for interaction with sparse-index. */
        ensure_full_index(&the_index);
-       for (i = 0; i < active_nr; i++) {
-               const struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < the_index.cache_nr; i++) {
+               const struct cache_entry *ce = the_index.cache[i];
                struct string_list_item *item;
 
                if (ce->ce_flags & CE_UPDATE)
@@ -295,10 +302,10 @@ static void add_remove_files(struct string_list *list)
                        continue;
 
                if (!lstat(p->string, &st)) {
-                       if (add_to_cache(p->string, &st, 0))
+                       if (add_to_index(&the_index, p->string, &st, 0))
                                die(_("updating files failed"));
                } else
-                       remove_file_from_cache(p->string);
+                       remove_file_from_index(&the_index, p->string);
        }
 }
 
@@ -309,7 +316,7 @@ static void create_base_index(const struct commit *current_head)
        struct tree_desc t;
 
        if (!current_head) {
-               discard_cache();
+               discard_index(&the_index);
                return;
        }
 
@@ -336,7 +343,7 @@ static void refresh_cache_or_die(int refresh_flags)
         * refresh_flags contains REFRESH_QUIET, so the only errors
         * are for unmerged entries.
         */
-       if (refresh_cache(refresh_flags | REFRESH_IN_PORCELAIN))
+       if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
                die_resolve_conflict("commit");
 }
 
@@ -375,12 +382,13 @@ static const char *prepare_index(const char **argv, const char *prefix,
            (!amend || (fixup_message && strcmp(fixup_prefix, "amend"))))))
                die(_("No paths with --include/--only does not make sense."));
 
-       if (read_cache_preload(&pathspec) < 0)
+       if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
                die(_("index file corrupt"));
 
        if (interactive) {
                char *old_index_env = NULL, *old_repo_index_file;
-               hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+               repo_hold_locked_index(the_repository, &index_lock,
+                                      LOCK_DIE_ON_ERROR);
 
                refresh_cache_or_die(refresh_flags);
 
@@ -403,9 +411,10 @@ static const char *prepare_index(const char **argv, const char *prefix,
                        unsetenv(INDEX_ENVIRONMENT);
                FREE_AND_NULL(old_index_env);
 
-               discard_cache();
-               read_cache_from(get_lock_file_path(&index_lock));
-               if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
+               discard_index(&the_index);
+               read_index_from(&the_index, get_lock_file_path(&index_lock),
+                               get_git_dir());
+               if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
                        if (reopen_lock_file(&index_lock) < 0)
                                die(_("unable to write index file"));
                        if (write_locked_index(&the_index, &index_lock, 0))
@@ -431,12 +440,14 @@ static const char *prepare_index(const char **argv, const char *prefix,
         * (B) on failure, rollback the real index.
         */
        if (all || (also && pathspec.nr)) {
-               hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
-               add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+               repo_hold_locked_index(the_repository, &index_lock,
+                                      LOCK_DIE_ON_ERROR);
+               add_files_to_cache(the_repository, also ? prefix : NULL,
+                                  &pathspec, 0, 0);
                refresh_cache_or_die(refresh_flags);
-               update_main_cache_tree(WRITE_TREE_SILENT);
+               cache_tree_update(&the_index, WRITE_TREE_SILENT);
                if (write_locked_index(&the_index, &index_lock, 0))
-                       die(_("unable to write new_index file"));
+                       die(_("unable to write new index file"));
                commit_style = COMMIT_NORMAL;
                ret = get_lock_file_path(&index_lock);
                goto out;
@@ -452,14 +463,15 @@ static const char *prepare_index(const char **argv, const char *prefix,
         * We still need to refresh the index here.
         */
        if (!only && !pathspec.nr) {
-               hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+               repo_hold_locked_index(the_repository, &index_lock,
+                                      LOCK_DIE_ON_ERROR);
                refresh_cache_or_die(refresh_flags);
-               if (active_cache_changed
-                   || !cache_tree_fully_valid(active_cache_tree))
-                       update_main_cache_tree(WRITE_TREE_SILENT);
+               if (the_index.cache_changed
+                   || !cache_tree_fully_valid(the_index.cache_tree))
+                       cache_tree_update(&the_index, WRITE_TREE_SILENT);
                if (write_locked_index(&the_index, &index_lock,
                                       COMMIT_LOCK | SKIP_IF_UNCHANGED))
-                       die(_("unable to write new_index file"));
+                       die(_("unable to write new index file"));
                commit_style = COMMIT_AS_IS;
                ret = get_index_file();
                goto out;
@@ -498,16 +510,16 @@ static const char *prepare_index(const char **argv, const char *prefix,
        if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
                exit(1);
 
-       discard_cache();
-       if (read_cache() < 0)
+       discard_index(&the_index);
+       if (repo_read_index(the_repository) < 0)
                die(_("cannot read the index"));
 
-       hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+       repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
        add_remove_files(&partial);
-       refresh_cache(REFRESH_QUIET);
-       update_main_cache_tree(WRITE_TREE_SILENT);
+       refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+       cache_tree_update(&the_index, WRITE_TREE_SILENT);
        if (write_locked_index(&the_index, &index_lock, 0))
-               die(_("unable to write new_index file"));
+               die(_("unable to write new index file"));
 
        hold_lock_file_for_update(&false_lock,
                                  git_path("next-index-%"PRIuMAX,
@@ -516,14 +528,14 @@ static const char *prepare_index(const char **argv, const char *prefix,
 
        create_base_index(current_head);
        add_remove_files(&partial);
-       refresh_cache(REFRESH_QUIET);
+       refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
 
        if (write_locked_index(&the_index, &false_lock, 0))
                die(_("unable to write temporary index file"));
 
-       discard_cache();
+       discard_index(&the_index);
        ret = get_lock_file_path(&false_lock);
-       read_cache_from(ret);
+       read_index_from(&the_index, ret, get_git_dir());
 out:
        string_list_clear(&partial, 0);
        clear_pathspec(&pathspec);
@@ -546,7 +558,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
        s->index_file = index_file;
        s->fp = fp;
        s->nowarn = nowarn;
-       s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
+       s->is_initial = repo_get_oid(the_repository, s->reference, &oid) ? 1 : 0;
        if (!s->is_initial)
                oidcpy(&s->oid_commit, &oid);
        s->status_format = status_format;
@@ -701,15 +713,15 @@ static void prepare_amend_commit(struct commit *commit, struct strbuf *sb,
 {
        const char *buffer, *subject, *fmt;
 
-       buffer = get_commit_buffer(commit, NULL);
+       buffer = repo_get_commit_buffer(the_repository, commit, NULL);
        find_commit_subject(buffer, &subject);
        /*
         * If we amend the 'amend!' commit then we don't want to
         * duplicate the subject line.
         */
        fmt = starts_with(subject, "amend!") ? "%b" : "%B";
-       format_commit_message(commit, fmt, sb, ctx);
-       unuse_commit_buffer(commit, buffer);
+       repo_format_commit_message(the_repository, commit, fmt, sb, ctx);
+       repo_unuse_commit_buffer(the_repository, commit, buffer);
 }
 
 static int prepare_to_commit(const char *index_file, const char *prefix,
@@ -747,10 +759,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        struct commit *c;
                        c = lookup_commit_reference_by_name(squash_message);
                        if (!c)
-                               die(_("could not lookup commit %s"), squash_message);
+                               die(_("could not lookup commit '%s'"), squash_message);
                        ctx.output_encoding = get_commit_output_encoding();
-                       format_commit_message(c, "squash! %s\n\n", &sb,
-                                             &ctx);
+                       repo_format_commit_message(the_repository, c,
+                                                  "squash! %s\n\n", &sb,
+                                                  &ctx);
                }
        }
 
@@ -781,10 +794,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                char *fmt;
                commit = lookup_commit_reference_by_name(fixup_commit);
                if (!commit)
-                       die(_("could not lookup commit %s"), fixup_commit);
+                       die(_("could not lookup commit '%s'"), fixup_commit);
                ctx.output_encoding = get_commit_output_encoding();
                fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
-               format_commit_message(commit, fmt, &sb, &ctx);
+               repo_format_commit_message(the_repository, commit, fmt, &sb,
+                                          &ctx);
                free(fmt);
                hook_arg1 = "message";
 
@@ -875,10 +889,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        s->hints = 0;
 
        if (clean_message_contents)
-               strbuf_stripspace(&sb, 0);
+               strbuf_stripspace(&sb, '\0');
 
        if (signoff)
-               append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
+               append_signoff(&sb, ignored_log_message_bytes(sb.buf, sb.len), 0);
 
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
                die_errno(_("could not write commit template"));
@@ -980,21 +994,21 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                struct object_id oid;
                const char *parent = "HEAD";
 
-               if (!active_nr && read_cache() < 0)
+               if (!the_index.initialized && repo_read_index(the_repository) < 0)
                        die(_("Cannot read index"));
 
                if (amend)
                        parent = "HEAD^1";
 
-               if (get_oid(parent, &oid)) {
+               if (repo_get_oid(the_repository, parent, &oid)) {
                        int i, ita_nr = 0;
 
                        /* TODO: audit for interaction with sparse-index. */
                        ensure_full_index(&the_index);
-                       for (i = 0; i < active_nr; i++)
-                               if (ce_intent_to_add(active_cache[i]))
+                       for (i = 0; i < the_index.cache_nr; i++)
+                               if (ce_intent_to_add(the_index.cache[i]))
                                        ita_nr++;
-                       committable = active_nr - ita_nr > 0;
+                       committable = the_index.cache_nr - ita_nr > 0;
                } else {
                        /*
                         * Unless the user did explicitly request a submodule
@@ -1022,7 +1036,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                struct child_process run_trailer = CHILD_PROCESS_INIT;
 
                strvec_pushl(&run_trailer.args, "interpret-trailers",
-                            "--in-place", git_path_commit_editmsg(), NULL);
+                            "--in-place", "--no-divider",
+                            git_path_commit_editmsg(), NULL);
                strvec_pushv(&run_trailer.args, trailer_args.v);
                run_trailer.git_cmd = 1;
                if (run_command(&run_trailer))
@@ -1061,11 +1076,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                 * and could have updated it. We must do this before we invoke
                 * the editor and after we invoke run_status above.
                 */
-               discard_cache();
+               discard_index(&the_index);
        }
-       read_cache_from(index_file);
+       read_index_from(&the_index, index_file, get_git_dir());
 
-       if (update_main_cache_tree(0)) {
+       if (cache_tree_update(&the_index, 0)) {
                error(_("Error building trees"));
                return 0;
        }
@@ -1121,7 +1136,8 @@ static const char *find_author_by_nickname(const char *name)
                struct pretty_print_context ctx = {0};
                ctx.date_mode.type = DATE_NORMAL;
                strbuf_release(&buf);
-               format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
+               repo_format_commit_message(the_repository, commit,
+                                          "%aN <%aE>", &buf, &ctx);
                release_revisions(&revs);
                return strbuf_detach(&buf, NULL);
        }
@@ -1167,9 +1183,9 @@ static const char *read_commit_message(const char *name)
 
        commit = lookup_commit_reference_by_name(name);
        if (!commit)
-               die(_("could not lookup commit %s"), name);
+               die(_("could not lookup commit '%s'"), name);
        out_enc = get_commit_output_encoding();
-       return logmsg_reencode(commit, NULL, out_enc);
+       return repo_logmsg_reencode(the_repository, commit, NULL, out_enc);
 }
 
 /*
@@ -1383,7 +1399,8 @@ static int parse_status_slot(const char *slot)
        return LOOKUP_CONFIG(color_status_slots, slot);
 }
 
-static int git_status_config(const char *k, const char *v, void *cb)
+static int git_status_config(const char *k, const char *v,
+                            const struct config_context *ctx, void *cb)
 {
        struct wt_status *s = cb;
        const char *slot_name;
@@ -1392,7 +1409,8 @@ static int git_status_config(const char *k, const char *v, void *cb)
                return git_column_config(k, v, "status", &s->colopts);
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
-               s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+               s->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi,
+                                                             &is_bool);
                if (is_bool && s->submodule_summary)
                        s->submodule_summary = -1;
                return 0;
@@ -1452,11 +1470,11 @@ static int git_status_config(const char *k, const char *v, void *cb)
        }
        if (!strcmp(k, "diff.renamelimit")) {
                if (s->rename_limit == -1)
-                       s->rename_limit = git_config_int(k, v);
+                       s->rename_limit = git_config_int(k, v, ctx->kvi);
                return 0;
        }
        if (!strcmp(k, "status.renamelimit")) {
-               s->rename_limit = git_config_int(k, v);
+               s->rename_limit = git_config_int(k, v, ctx->kvi);
                return 0;
        }
        if (!strcmp(k, "diff.renames")) {
@@ -1468,7 +1486,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
                s->detect_rename = git_config_rename(k, v);
                return 0;
        }
-       return git_diff_ui_config(k, v, NULL);
+       return git_diff_ui_config(k, v, ctx, NULL);
 }
 
 int cmd_status(int argc, const char **argv, const char *prefix)
@@ -1549,11 +1567,11 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                      &s.pathspec, NULL, NULL);
 
        if (use_optional_locks())
-               fd = hold_locked_index(&index_lock, 0);
+               fd = repo_hold_locked_index(the_repository, &index_lock, 0);
        else
                fd = -1;
 
-       s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
+       s.is_initial = repo_get_oid(the_repository, s.reference, &oid) ? 1 : 0;
        if (!s.is_initial)
                oidcpy(&s.oid_commit, &oid);
 
@@ -1583,10 +1601,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
-static int git_commit_config(const char *k, const char *v, void *cb)
+static int git_commit_config(const char *k, const char *v,
+                            const struct config_context *ctx, void *cb)
 {
        struct wt_status *s = cb;
-       int status;
 
        if (!strcmp(k, "commit.template"))
                return git_config_pathname(&template_file, k, v);
@@ -1602,14 +1620,12 @@ static int git_commit_config(const char *k, const char *v, void *cb)
        }
        if (!strcmp(k, "commit.verbose")) {
                int is_bool;
-               config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+               config_commit_verbose = git_config_bool_or_int(k, v, ctx->kvi,
+                                                              &is_bool);
                return 0;
        }
 
-       status = git_gpg_config(k, v, NULL);
-       if (status)
-               return status;
-       return git_status_config(k, v, s);
+       return git_status_config(k, v, ctx, s);
 }
 
 int cmd_commit(int argc, const char **argv, const char *prefix)
@@ -1700,11 +1716,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
        s.colopts = 0;
 
-       if (get_oid("HEAD", &oid))
+       if (repo_get_oid(the_repository, "HEAD", &oid))
                current_head = NULL;
        else {
                current_head = lookup_commit_or_die(&oid, "HEAD");
-               if (parse_commit(current_head))
+               if (repo_parse_commit(the_repository, current_head))
                        die(_("could not parse HEAD commit"));
        }
        verbose = -1; /* unspecified */
@@ -1816,7 +1832,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                append_merge_tag_headers(parents, &tail);
        }
 
-       if (commit_tree_extended(sb.buf, sb.len, &active_cache_tree->oid,
+       if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid,
                                 parents, &oid, author_ident.buf, NULL,
                                 sign_commit, extra)) {
                rollback_index_files();
@@ -1838,7 +1854,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        if (commit_index_files())
                die(_("repository has been updated, but unable to write\n"
-                     "new_index file. Check that disk is not full and quota is\n"
+                     "new index file. Check that disk is not full and quota is\n"
                      "not exceeded, and then \"git restore --staged :/\" to recover."));
 
        git_test_write_commit_graph_or_die();
@@ -1864,8 +1880,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        apply_autostash(git_path_merge_autostash(the_repository));
 
 cleanup:
-       UNLEAK(author_ident);
-       UNLEAK(err);
-       UNLEAK(sb);
+       strbuf_release(&author_ident);
+       strbuf_release(&err);
+       strbuf_release(&sb);
        return ret;
 }
index 753e5fac297e08763317adcab2fefe386ea68421..11a4d4ef1411222f3750c760b68efd10180148c0 100644 (file)
@@ -1,10 +1,18 @@
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
 #include "config.h"
 #include "color.h"
+#include "editor.h"
+#include "environment.h"
+#include "repository.h"
+#include "gettext.h"
+#include "ident.h"
 #include "parse-options.h"
 #include "urlmatch.h"
+#include "path.h"
 #include "quote.h"
+#include "setup.h"
+#include "strbuf.h"
 #include "worktree.h"
 
 static const char *const builtin_config_usage[] = {
@@ -185,37 +193,42 @@ static void check_argc(int argc, int min, int max)
        usage_builtin_config();
 }
 
-static void show_config_origin(struct strbuf *buf)
+static void show_config_origin(const struct key_value_info *kvi,
+                              struct strbuf *buf)
 {
        const char term = end_nul ? '\0' : '\t';
 
-       strbuf_addstr(buf, current_config_origin_type());
+       strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
        strbuf_addch(buf, ':');
        if (end_nul)
-               strbuf_addstr(buf, current_config_name());
+               strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
        else
-               quote_c_style(current_config_name(), buf, NULL, 0);
+               quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
        strbuf_addch(buf, term);
 }
 
-static void show_config_scope(struct strbuf *buf)
+static void show_config_scope(const struct key_value_info *kvi,
+                             struct strbuf *buf)
 {
        const char term = end_nul ? '\0' : '\t';
-       const char *scope = config_scope_name(current_config_scope());
+       const char *scope = config_scope_name(kvi->scope);
 
        strbuf_addstr(buf, N_(scope));
        strbuf_addch(buf, term);
 }
 
 static int show_all_config(const char *key_, const char *value_,
+                          const struct config_context *ctx,
                           void *cb UNUSED)
 {
+       const struct key_value_info *kvi = ctx->kvi;
+
        if (show_origin || show_scope) {
                struct strbuf buf = STRBUF_INIT;
                if (show_scope)
-                       show_config_scope(&buf);
+                       show_config_scope(kvi, &buf);
                if (show_origin)
-                       show_config_origin(&buf);
+                       show_config_origin(kvi, &buf);
                /* Use fwrite as "buf" can contain \0's if "end_null" is set. */
                fwrite(buf.buf, 1, buf.len, stdout);
                strbuf_release(&buf);
@@ -233,12 +246,13 @@ struct strbuf_list {
        int alloc;
 };
 
-static int format_config(struct strbuf *buf, const char *key_, const char *value_)
+static int format_config(struct strbuf *buf, const char *key_,
+                        const char *value_, const struct key_value_info *kvi)
 {
        if (show_scope)
-               show_config_scope(buf);
+               show_config_scope(kvi, buf);
        if (show_origin)
-               show_config_origin(buf);
+               show_config_origin(kvi, buf);
        if (show_keys)
                strbuf_addstr(buf, key_);
        if (!omit_values) {
@@ -247,13 +261,14 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
 
                if (type == TYPE_INT)
                        strbuf_addf(buf, "%"PRId64,
-                                   git_config_int64(key_, value_ ? value_ : ""));
+                                   git_config_int64(key_, value_ ? value_ : "", kvi));
                else if (type == TYPE_BOOL)
                        strbuf_addstr(buf, git_config_bool(key_, value_) ?
                                      "true" : "false");
                else if (type == TYPE_BOOL_OR_INT) {
                        int is_bool, v;
-                       v = git_config_bool_or_int(key_, value_, &is_bool);
+                       v = git_config_bool_or_int(key_, value_, kvi,
+                                                  &is_bool);
                        if (is_bool)
                                strbuf_addstr(buf, v ? "true" : "false");
                        else
@@ -292,9 +307,11 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
        return 0;
 }
 
-static int collect_config(const char *key_, const char *value_, void *cb)
+static int collect_config(const char *key_, const char *value_,
+                         const struct config_context *ctx, void *cb)
 {
        struct strbuf_list *values = cb;
+       const struct key_value_info *kvi = ctx->kvi;
 
        if (!use_key_regexp && strcmp(key_, key))
                return 0;
@@ -309,7 +326,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)
        ALLOC_GROW(values->items, values->nr + 1, values->alloc);
        strbuf_init(&values->items[values->nr], 0);
 
-       return format_config(&values->items[values->nr++], key_, value_);
+       return format_config(&values->items[values->nr++], key_, value_, kvi);
 }
 
 static int get_value(const char *key_, const char *regex_, unsigned flags)
@@ -367,14 +384,18 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
        }
 
        config_with_options(collect_config, &values,
-                           &given_config_source, &config_options);
+                           &given_config_source, the_repository,
+                           &config_options);
 
        if (!values.nr && default_value) {
+               struct key_value_info kvi = KVI_INIT;
                struct strbuf *item;
+
+               kvi_from_param(&kvi);
                ALLOC_GROW(values.items, values.nr + 1, values.alloc);
                item = &values.items[values.nr++];
                strbuf_init(item, 0);
-               if (format_config(item, key_, default_value) < 0)
+               if (format_config(item, key_, default_value, &kvi) < 0)
                        die(_("failed to format default config value: %s"),
                                default_value);
        }
@@ -403,7 +424,8 @@ free_strings:
        return ret;
 }
 
-static char *normalize_value(const char *key, const char *value)
+static char *normalize_value(const char *key, const char *value,
+                            struct key_value_info *kvi)
 {
        if (!value)
                return NULL;
@@ -418,12 +440,12 @@ static char *normalize_value(const char *key, const char *value)
                 */
                return xstrdup(value);
        if (type == TYPE_INT)
-               return xstrfmt("%"PRId64, git_config_int64(key, value));
+               return xstrfmt("%"PRId64, git_config_int64(key, value, kvi));
        if (type == TYPE_BOOL)
                return xstrdup(git_config_bool(key, value) ?  "true" : "false");
        if (type == TYPE_BOOL_OR_INT) {
                int is_bool, v;
-               v = git_config_bool_or_int(key, value, &is_bool);
+               v = git_config_bool_or_int(key, value, kvi, &is_bool);
                if (!is_bool)
                        return xstrfmt("%d", v);
                else
@@ -460,6 +482,7 @@ static const char *get_colorbool_slot;
 static char parsed_color[COLOR_MAXLEN];
 
 static int git_get_color_config(const char *var, const char *value,
+                               const struct config_context *ctx UNUSED,
                                void *cb UNUSED)
 {
        if (!strcmp(var, get_color_slot)) {
@@ -478,7 +501,8 @@ static void get_color(const char *var, const char *def_color)
        get_color_found = 0;
        parsed_color[0] = '\0';
        config_with_options(git_get_color_config, NULL,
-                           &given_config_source, &config_options);
+                           &given_config_source, the_repository,
+                           &config_options);
 
        if (!get_color_found && def_color) {
                if (color_parse(def_color, parsed_color) < 0)
@@ -492,6 +516,7 @@ static int get_colorbool_found;
 static int get_diff_color_found;
 static int get_color_ui_found;
 static int git_get_colorbool_config(const char *var, const char *value,
+                                   const struct config_context *ctx UNUSED,
                                    void *data UNUSED)
 {
        if (!strcmp(var, get_colorbool_slot))
@@ -510,7 +535,8 @@ static int get_colorbool(const char *var, int print)
        get_diff_color_found = -1;
        get_color_ui_found = -1;
        config_with_options(git_get_colorbool_config, NULL,
-                           &given_config_source, &config_options);
+                           &given_config_source, the_repository,
+                           &config_options);
 
        if (get_colorbool_found < 0) {
                if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -547,13 +573,17 @@ static void check_write(void)
 struct urlmatch_current_candidate_value {
        char value_is_null;
        struct strbuf value;
+       struct key_value_info kvi;
 };
 
-static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
+static int urlmatch_collect_fn(const char *var, const char *value,
+                              const struct config_context *ctx,
+                              void *cb)
 {
        struct string_list *values = cb;
        struct string_list_item *item = string_list_insert(values, var);
        struct urlmatch_current_candidate_value *matched = item->util;
+       const struct key_value_info *kvi = ctx->kvi;
 
        if (!matched) {
                matched = xmalloc(sizeof(*matched));
@@ -562,6 +592,7 @@ static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
        } else {
                strbuf_reset(&matched->value);
        }
+       matched->kvi = *kvi;
 
        if (value) {
                strbuf_addstr(&matched->value, value);
@@ -599,7 +630,8 @@ static int get_urlmatch(const char *var, const char *url)
        }
 
        config_with_options(urlmatch_config_entry, &config,
-                           &given_config_source, &config_options);
+                           &given_config_source, the_repository,
+                           &config_options);
 
        ret = !values.nr;
 
@@ -608,7 +640,8 @@ static int get_urlmatch(const char *var, const char *url)
                struct strbuf buf = STRBUF_INIT;
 
                format_config(&buf, item->string,
-                             matched->value_is_null ? NULL : matched->value.buf);
+                             matched->value_is_null ? NULL : matched->value.buf,
+                             &matched->kvi);
                fwrite(buf.buf, 1, buf.len, stdout);
                strbuf_release(&buf);
 
@@ -639,8 +672,10 @@ static char *default_user_config(void)
 int cmd_config(int argc, const char **argv, const char *prefix)
 {
        int nongit = !startup_info->have_repository;
-       char *value;
+       char *value = NULL;
        int flags = 0;
+       int ret = 0;
+       struct key_value_info default_kvi = KVI_INIT;
 
        given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
@@ -704,7 +739,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                given_config_source.scope = CONFIG_SCOPE_LOCAL;
        } else if (use_worktree_config) {
                struct worktree **worktrees = get_worktrees();
-               if (repository_format_worktree_config)
+               if (the_repository->repository_format_worktree_config)
                        given_config_source.file = git_pathdup("config.worktree");
                else if (worktrees[0] && worktrees[1])
                        die(_("--worktree cannot be used with multiple "
@@ -818,7 +853,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        if (actions == ACTION_LIST) {
                check_argc(argc, 0, 0);
                if (config_with_options(show_all_config, NULL,
-                                       &given_config_source,
+                                       &given_config_source, the_repository,
                                        &config_options) < 0) {
                        if (given_config_source.file)
                                die_errno(_("unable to read config file '%s'"),
@@ -856,44 +891,38 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                free(config_file);
        }
        else if (actions == ACTION_SET) {
-               int ret;
                check_write();
                check_argc(argc, 2, 2);
-               value = normalize_value(argv[0], argv[1]);
-               UNLEAK(value);
+               value = normalize_value(argv[0], argv[1], &default_kvi);
                ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
                if (ret == CONFIG_NOTHING_SET)
                        error(_("cannot overwrite multiple values with a single value\n"
                        "       Use a regexp, --add or --replace-all to change %s."), argv[0]);
-               return ret;
        }
        else if (actions == ACTION_SET_ALL) {
                check_write();
                check_argc(argc, 2, 3);
-               value = normalize_value(argv[0], argv[1]);
-               UNLEAK(value);
-               return git_config_set_multivar_in_file_gently(given_config_source.file,
-                                                             argv[0], value, argv[2],
-                                                             flags);
+               value = normalize_value(argv[0], argv[1], &default_kvi);
+               ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                            argv[0], value, argv[2],
+                                                            flags);
        }
        else if (actions == ACTION_ADD) {
                check_write();
                check_argc(argc, 2, 2);
-               value = normalize_value(argv[0], argv[1]);
-               UNLEAK(value);
-               return git_config_set_multivar_in_file_gently(given_config_source.file,
-                                                             argv[0], value,
-                                                             CONFIG_REGEX_NONE,
-                                                             flags);
+               value = normalize_value(argv[0], argv[1], &default_kvi);
+               ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                            argv[0], value,
+                                                            CONFIG_REGEX_NONE,
+                                                            flags);
        }
        else if (actions == ACTION_REPLACE_ALL) {
                check_write();
                check_argc(argc, 2, 3);
-               value = normalize_value(argv[0], argv[1]);
-               UNLEAK(value);
-               return git_config_set_multivar_in_file_gently(given_config_source.file,
-                                                             argv[0], value, argv[2],
-                                                             flags | CONFIG_FLAGS_MULTI_REPLACE);
+               value = normalize_value(argv[0], argv[1], &default_kvi);
+               ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                            argv[0], value, argv[2],
+                                                            flags | CONFIG_FLAGS_MULTI_REPLACE);
        }
        else if (actions == ACTION_GET) {
                check_argc(argc, 1, 2);
@@ -934,26 +963,28 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                                                              flags | CONFIG_FLAGS_MULTI_REPLACE);
        }
        else if (actions == ACTION_RENAME_SECTION) {
-               int ret;
                check_write();
                check_argc(argc, 2, 2);
                ret = git_config_rename_section_in_file(given_config_source.file,
                                                        argv[0], argv[1]);
                if (ret < 0)
                        return ret;
-               if (ret == 0)
+               else if (!ret)
                        die(_("no such section: %s"), argv[0]);
+               else
+                       ret = 0;
        }
        else if (actions == ACTION_REMOVE_SECTION) {
-               int ret;
                check_write();
                check_argc(argc, 1, 1);
                ret = git_config_rename_section_in_file(given_config_source.file,
                                                        argv[0], NULL);
                if (ret < 0)
                        return ret;
-               if (ret == 0)
+               else if (!ret)
                        die(_("no such section: %s"), argv[0]);
+               else
+                       ret = 0;
        }
        else if (actions == ACTION_GET_COLOR) {
                check_argc(argc, 1, 2);
@@ -966,5 +997,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                return get_colorbool(argv[0], argc == 2);
        }
 
-       return 0;
+       free(value);
+       return ret;
 }
index 07b941959628b9d02dbf0282e779040cea5b27be..2d4bb5e8d0a8dfce1290760f7cd99dac7ba39a3e 100644 (file)
@@ -4,15 +4,17 @@
  * Copyright (c) 2006 Junio C Hamano
  */
 
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "path.h"
 #include "repository.h"
-#include "builtin.h"
 #include "parse-options.h"
 #include "quote.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 static unsigned long garbage;
 static off_t size_garbage;
@@ -57,7 +59,8 @@ static void loose_garbage(const char *path)
                report_garbage(PACKDIR_FILE_GARBAGE, path);
 }
 
-static int count_loose(const struct object_id *oid, const char *path, void *data)
+static int count_loose(const struct object_id *oid, const char *path,
+                      void *data UNUSED)
 {
        struct stat st;
 
@@ -72,13 +75,14 @@ static int count_loose(const struct object_id *oid, const char *path, void *data
        return 0;
 }
 
-static int count_cruft(const char *basename, const char *path, void *data)
+static int count_cruft(const char *basename UNUSED, const char *path,
+                      void *data UNUSED)
 {
        loose_garbage(path);
        return 0;
 }
 
-static int print_alternate(struct object_directory *odb, void *data)
+static int print_alternate(struct object_directory *odb, void *data UNUSED)
 {
        printf("alternate: ");
        quote_c_style(odb->path, NULL, stdout, 0);
index 4c6c89ab0de2eb3dbc2785033249e853aea32f16..3a6a750a8eb320bb3622184843ede3d2b9884385 100644 (file)
@@ -1,4 +1,7 @@
 #include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "object-file.h"
 #include "parse-options.h"
 
 #ifndef NO_UNIX_SOCKETS
@@ -34,19 +37,22 @@ static struct credential_cache_entry *lookup_credential(const struct credential
        int i;
        for (i = 0; i < entries_nr; i++) {
                struct credential *e = &entries[i].item;
-               if (credential_match(c, e))
+               if (credential_match(c, e, 0))
                        return &entries[i];
        }
        return NULL;
 }
 
-static void remove_credential(const struct credential *c)
+static void remove_credential(const struct credential *c, int match_password)
 {
        struct credential_cache_entry *e;
 
-       e = lookup_credential(c);
-       if (e)
-               e->expiration = 0;
+       int i;
+       for (i = 0; i < entries_nr; i++) {
+               e = &entries[i];
+               if (credential_match(c, &e->item, match_password))
+                       e->expiration = 0;
+       }
 }
 
 static timestamp_t check_expirations(void)
@@ -127,6 +133,12 @@ static void serve_one_client(FILE *in, FILE *out)
                if (e) {
                        fprintf(out, "username=%s\n", e->item.username);
                        fprintf(out, "password=%s\n", e->item.password);
+                       if (e->item.password_expiry_utc != TIME_MAX)
+                               fprintf(out, "password_expiry_utc=%"PRItime"\n",
+                                       e->item.password_expiry_utc);
+                       if (e->item.oauth_refresh_token)
+                               fprintf(out, "oauth_refresh_token=%s\n",
+                                       e->item.oauth_refresh_token);
                }
        }
        else if (!strcmp(action.buf, "exit")) {
@@ -141,14 +153,14 @@ static void serve_one_client(FILE *in, FILE *out)
                exit(0);
        }
        else if (!strcmp(action.buf, "erase"))
-               remove_credential(&c);
+               remove_credential(&c, 1);
        else if (!strcmp(action.buf, "store")) {
                if (timeout < 0)
                        warning("cache client didn't specify a timeout");
                else if (!c.username || !c.password)
                        warning("cache client gave us a partial credential");
                else {
-                       remove_credential(&c);
+                       remove_credential(&c, 0);
                        cache_credential(&c, timeout);
                }
        }
@@ -267,7 +279,7 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
        const char *socket_path;
        int ignore_sighup = 0;
        static const char *usage[] = {
-               "git-credential-cache--daemon [opts] <socket_path>",
+               "git credential-cache--daemon [--debug] <socket-path>",
                NULL
        };
        int debug = 0;
@@ -305,7 +317,7 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
 int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
 {
        const char * const usage[] = {
-               "git credential-cache--daemon [options] <action>",
+               "git credential-cache--daemon [--debug] <socket-path>",
                "",
                "credential-cache--daemon is disabled in this build of Git",
                NULL
index 78c02ad53192323e8237a3b0555b4f4f1b4dbceb..bba96d4ffd6f198adb186aaba0c853e34a93dd11 100644 (file)
@@ -1,10 +1,12 @@
 #include "builtin.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "path.h"
+#include "strbuf.h"
+#include "write-or-die.h"
 
 #ifndef NO_UNIX_SOCKETS
 
-#include "credential.h"
-#include "string-list.h"
 #include "unix-socket.h"
 #include "run-command.h"
 
index 62a4f3c26531432da689dabe38b724c6e5484c7d..4a492411bbf3d2a7f0a34ada0bd127ae97cf5552 100644 (file)
@@ -1,16 +1,20 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
 #include "lockfile.h"
 #include "credential.h"
+#include "path.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "write-or-die.h"
 
 static struct lock_file credential_lock;
 
 static int parse_credential_file(const char *fn,
                                  struct credential *c,
                                  void (*match_cb)(struct credential *),
-                                 void (*other_cb)(struct strbuf *))
+                                 void (*other_cb)(struct strbuf *),
+                                 int match_password)
 {
        FILE *fh;
        struct strbuf line = STRBUF_INIT;
@@ -27,7 +31,7 @@ static int parse_credential_file(const char *fn,
        while (strbuf_getline_lf(&line, fh) != EOF) {
                if (!credential_from_url_gently(&entry, line.buf, 1) &&
                    entry.username && entry.password &&
-                   credential_match(c, &entry)) {
+                   credential_match(c, &entry, match_password)) {
                        found_credential = 1;
                        if (match_cb) {
                                match_cb(&entry);
@@ -57,7 +61,7 @@ static void print_line(struct strbuf *buf)
 }
 
 static void rewrite_credential_file(const char *fn, struct credential *c,
-                                   struct strbuf *extra)
+                                   struct strbuf *extra, int match_password)
 {
        int timeout_ms = 1000;
 
@@ -66,11 +70,30 @@ static void rewrite_credential_file(const char *fn, struct credential *c,
                die_errno(_("unable to get credential storage lock in %d ms"), timeout_ms);
        if (extra)
                print_line(extra);
-       parse_credential_file(fn, c, NULL, print_line);
+       parse_credential_file(fn, c, NULL, print_line, match_password);
        if (commit_lock_file(&credential_lock) < 0)
                die_errno("unable to write credential store");
 }
 
+static int is_rfc3986_unreserved(char ch)
+{
+       return isalnum(ch) ||
+               ch == '-' || ch == '_' || ch == '.' || ch == '~';
+}
+
+static int is_rfc3986_reserved_or_unreserved(char ch)
+{
+       if (is_rfc3986_unreserved(ch))
+               return 1;
+       switch (ch) {
+               case '!': case '*': case '\'': case '(': case ')': case ';':
+               case ':': case '@': case '&': case '=': case '+': case '$':
+               case ',': case '/': case '?': case '#': case '[': case ']':
+                       return 1;
+       }
+       return 0;
+}
+
 static void store_credential_file(const char *fn, struct credential *c)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -88,7 +111,7 @@ static void store_credential_file(const char *fn, struct credential *c)
                                        is_rfc3986_reserved_or_unreserved);
        }
 
-       rewrite_credential_file(fn, c, &buf);
+       rewrite_credential_file(fn, c, &buf, 0);
        strbuf_release(&buf);
 }
 
@@ -135,7 +158,7 @@ static void remove_credential(const struct string_list *fns, struct credential *
                return;
        for_each_string_list_item(fn, fns)
                if (!access(fn->string, F_OK))
-                       rewrite_credential_file(fn->string, c, NULL);
+                       rewrite_credential_file(fn->string, c, NULL, 1);
 }
 
 static void lookup_credential(const struct string_list *fns, struct credential *c)
@@ -143,7 +166,7 @@ static void lookup_credential(const struct string_list *fns, struct credential *
        struct string_list_item *fn;
 
        for_each_string_list_item(fn, fns)
-               if (parse_credential_file(fn->string, c, print_entry, NULL))
+               if (parse_credential_file(fn->string, c, print_entry, NULL, 0))
                        return; /* Found credential */
 }
 
index d7b304fa084fd6c714ce9a6853d903ae6636bb33..70107529876372c693fa15bbf6aff54ca63b4ce7 100644 (file)
@@ -6,7 +6,7 @@
 static const char usage_msg[] =
        "git credential (fill|approve|reject)";
 
-int cmd_credential(int argc, const char **argv, const char *prefix)
+int cmd_credential(int argc, const char **argv, const char *prefix UNUSED)
 {
        const char *op;
        struct credential c = CREDENTIAL_INIT;
index e17c4b4c69b0feb7b9c8a897ca6120e46e502251..d6c77a714f46194faca18739d0118294bc233f81 100644 (file)
@@ -1,30 +1,36 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
-#include "blob.h"
 #include "refs.h"
-#include "builtin.h"
-#include "exec-cmd.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
 #include "revision.h"
 #include "diff.h"
 #include "hashmap.h"
+#include "setup.h"
 #include "strvec.h"
 #include "run-command.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "list-objects.h"
 #include "commit-slab.h"
+#include "wildmatch.h"
 
 #define MAX_TAGS       (FLAG_BITS - 1)
+#define DEFAULT_CANDIDATES 10
 
 define_commit_slab(commit_names, struct commit_name *);
 
 static const char * const describe_usage[] = {
-       N_("git describe [<options>] [<commit-ish>...]"),
-       N_("git describe [<options>] --dirty"),
+       N_("git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"),
+       N_("git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"),
+       N_("git describe <blob>"),
        NULL
 };
 
@@ -34,7 +40,7 @@ static int tags;      /* Allow lightweight tags */
 static int longformat;
 static int first_parent;
 static int abbrev = -1; /* unspecified */
-static int max_candidates = 10;
+static int max_candidates = DEFAULT_CANDIDATES;
 static struct hashmap names;
 static int have_util;
 static struct string_list patterns = STRING_LIST_INIT_NODUP;
@@ -260,7 +266,7 @@ static unsigned long finish_depth_computation(
                        best->depth++;
                while (parents) {
                        struct commit *p = parents->item;
-                       parse_commit(p);
+                       repo_parse_commit(the_repository, p);
                        if (!(p->object.flags & SEEN))
                                commit_list_insert_by_date(p, list);
                        p->object.flags |= c->object.flags;
@@ -297,7 +303,8 @@ static void append_name(struct commit_name *n, struct strbuf *dst)
 
 static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
 {
-       strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev));
+       strbuf_addf(dst, "-%d-g%s", depth,
+                   repo_find_unique_abbrev(the_repository, oid, abbrev));
 }
 
 static void describe_commit(struct object_id *oid, struct strbuf *dst)
@@ -402,7 +409,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
                }
                while (parents) {
                        struct commit *p = parents->item;
-                       parse_commit(p);
+                       repo_parse_commit(the_repository, p);
                        if (!(p->object.flags & SEEN))
                                commit_list_insert_by_date(p, &list);
                        p->object.flags |= c->object.flags;
@@ -530,7 +537,7 @@ static void describe(const char *arg, int last_one)
        if (debug)
                fprintf(stderr, _("describe %s\n"), arg);
 
-       if (get_oid(arg, &oid))
+       if (repo_get_oid(the_repository, arg, &oid))
                die(_("Not a valid object name %s"), arg);
        cmit = lookup_commit_reference_gently(the_repository, &oid, 1);
 
@@ -549,6 +556,17 @@ static void describe(const char *arg, int last_one)
        strbuf_release(&sb);
 }
 
+static int option_parse_exact_match(const struct option *opt, const char *arg,
+                                   int unset)
+{
+       int *val = opt->value;
+
+       BUG_ON_OPT_ARG(arg);
+
+       *val = unset ? DEFAULT_CANDIDATES : 0;
+       return 0;
+}
+
 int cmd_describe(int argc, const char **argv, const char *prefix)
 {
        int contains = 0;
@@ -560,8 +578,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "long",       &longformat, N_("always use long format")),
                OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")),
                OPT__ABBREV(&abbrev),
-               OPT_SET_INT(0, "exact-match", &max_candidates,
-                           N_("only output exact matches"), 0),
+               OPT_CALLBACK_F(0, "exact-match", &max_candidates, NULL,
+                              N_("only output exact matches"),
+                              PARSE_OPT_NOARG, option_parse_exact_match),
                OPT_INTEGER(0, "candidates", &max_candidates,
                            N_("consider <n> most recent tags (default: 10)")),
                OPT_STRING_LIST(0, "match", &patterns, N_("pattern"),
@@ -649,13 +668,16 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                        struct lock_file index_lock = LOCK_INIT;
                        struct rev_info revs;
                        struct strvec args = STRVEC_INIT;
-                       int fd, result;
+                       int fd;
 
                        setup_work_tree();
-                       read_cache();
+                       prepare_repo_settings(the_repository);
+                       the_repository->settings.command_requires_full_index = 0;
+                       repo_read_index(the_repository);
                        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
                                      NULL, NULL, NULL);
-                       fd = hold_locked_index(&index_lock, 0);
+                       fd = repo_hold_locked_index(the_repository,
+                                                   &index_lock, 0);
                        if (0 <= fd)
                                repo_update_index_if_able(the_repository, &index_lock);
 
@@ -663,9 +685,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                        strvec_pushv(&args, diff_index_args);
                        if (setup_revisions(args.nr, args.v, &revs, NULL) != 1)
                                BUG("malformed internal diff-index command line");
-                       result = run_diff_index(&revs, 0);
+                       run_diff_index(&revs, 0);
 
-                       if (!diff_result_code(&revs.diffopt, result))
+                       if (!diff_result_code(&revs.diffopt))
                                suffix = NULL;
                        else
                                suffix = dirty;
index 576e0e8e385c97961eda6292d7db14e55e4169cc..4f22eb2b55d06968896d12ae9257d470d5d3a8fb 100644 (file)
@@ -1,9 +1,13 @@
 #include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "object-file.h"
 #include "parse-options.h"
 #include "diagnose.h"
 
 static const char * const diagnose_usage[] = {
-       N_("git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--mode=<mode>]"),
+       N_("git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+          "             [--mode=<mode>]"),
        NULL
 };
 
index 92cf6e1e92229be63d15e950c85bd93adbbdad94..018011f29ea26be24b567e9af380699dd7d893a3 100644 (file)
@@ -3,18 +3,18 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "diff.h"
 #include "diff-merges.h"
 #include "commit.h"
+#include "preload-index.h"
+#include "repository.h"
 #include "revision.h"
-#include "builtin.h"
-#include "submodule.h"
 
 static const char diff_files_usage[] =
 "git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]"
+"\n"
 COMMON_DIFF_OPTIONS_HELP;
 
 int cmd_diff_files(int argc, const char **argv, const char *prefix)
@@ -27,6 +27,10 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
                usage(diff_files_usage);
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
+
        repo_init_revisions(the_repository, &rev, prefix);
        rev.abbrev = 0;
 
@@ -75,14 +79,10 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
            (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
                diff_merges_set_dense_combined_if_unset(&rev);
 
-       if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
-               perror("read_cache_preload");
-               result = -1;
-               goto cleanup;
-       }
-       result = run_diff_files(&rev, options);
-       result = diff_result_code(&rev.diffopt, result);
-cleanup:
+       if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0)
+               die_errno("repo_read_index_preload");
+       run_diff_files(&rev, options);
+       result = diff_result_code(&rev.diffopt);
        release_revisions(&rev);
        return result;
 }
index 7d158af6b6d4a88de34569dedee0bb425c8f89e1..3e05260ac0e0401188163aeaf4c4d1aea0ba56d1 100644 (file)
@@ -1,16 +1,17 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "diff.h"
 #include "diff-merges.h"
 #include "commit.h"
+#include "preload-index.h"
+#include "repository.h"
 #include "revision.h"
-#include "builtin.h"
-#include "submodule.h"
+#include "setup.h"
 
 static const char diff_cache_usage[] =
-"git diff-index [-m] [--cached] "
+"git diff-index [-m] [--cached] [--merge-base] "
 "[<common-diff-options>] <tree-ish> [<path>...]"
+"\n"
 COMMON_DIFF_OPTIONS_HELP;
 
 int cmd_diff_index(int argc, const char **argv, const char *prefix)
@@ -61,16 +62,16 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
                usage(diff_cache_usage);
        if (!(option & DIFF_INDEX_CACHED)) {
                setup_work_tree();
-               if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
-                       perror("read_cache_preload");
+               if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
+                       perror("repo_read_index_preload");
                        return -1;
                }
-       } else if (read_cache() < 0) {
-               perror("read_cache");
+       } else if (repo_read_index(the_repository) < 0) {
+               perror("repo_read_index");
                return -1;
        }
-       result = run_diff_index(&rev, option);
-       result = diff_result_code(&rev.diffopt, result);
+       run_diff_index(&rev, option);
+       result = diff_result_code(&rev.diffopt);
        release_revisions(&rev);
        return result;
 }
index 116097a404a1f1a2e6c3ea735e9159c9eca66066..a8e68ce8ef6275a99e2a4f62545d0e7d962dcf45 100644 (file)
@@ -1,12 +1,15 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
 #include "config.h"
 #include "diff.h"
 #include "commit.h"
+#include "gettext.h"
+#include "hex.h"
 #include "log-tree.h"
-#include "builtin.h"
-#include "submodule.h"
+#include "read-cache-ll.h"
 #include "repository.h"
+#include "revision.h"
+#include "tree.h"
 
 static struct rev_info log_tree_opt;
 
@@ -83,8 +86,10 @@ static int diff_tree_stdin(char *line)
 }
 
 static const char diff_tree_usage[] =
-"git diff-tree [--stdin] [-m] [-c | --cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
-"[<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
+"git diff-tree [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]\n"
+"              [-t] [-r] [-c | --cc] [--combined-all-paths] [--root] [--merge-base]\n"
+"              [<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
+"\n"
 "  -r            diff recursively\n"
 "  -c            show combined diff for merge commits\n"
 "  --cc          show combined diff for merge commits removing uninteresting hunks\n"
@@ -93,7 +98,7 @@ static const char diff_tree_usage[] =
 "  --root        include the initial commit as diff against /dev/null\n"
 COMMON_DIFF_OPTIONS_HELP;
 
-static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+static void diff_tree_tweak_rev(struct rev_info *rev)
 {
        if (!rev->diffopt.output_format) {
                if (rev->dense_combined_merges)
@@ -117,8 +122,12 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                usage(diff_tree_usage);
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
+
        repo_init_revisions(the_repository, opt, prefix);
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                die(_("index file corrupt"));
        opt->abbrev = 0;
        opt->diff = 1;
@@ -222,5 +231,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                diff_free(&opt->diffopt);
        }
 
-       return diff_result_code(&opt->diffopt, 0);
+       return diff_result_code(&opt->diffopt);
 }
index 54bb3de964cb5a1e815702a8cbcedab166b96ee3..6e196e0c7d2d055df1557ffd256d87a737053324 100644 (file)
@@ -3,23 +3,25 @@
  *
  * Copyright (c) 2006 Junio C Hamano
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
 #include "config.h"
 #include "ewah/ewok.h"
 #include "lockfile.h"
 #include "color.h"
 #include "commit.h"
-#include "blob.h"
+#include "gettext.h"
 #include "tag.h"
 #include "diff.h"
 #include "diff-merges.h"
 #include "diffcore.h"
+#include "preload-index.h"
+#include "read-cache-ll.h"
 #include "revision.h"
 #include "log-tree.h"
-#include "builtin.h"
-#include "submodule.h"
+#include "setup.h"
 #include "oid-array.h"
+#include "tree.h"
 
 #define DIFF_NO_INDEX_EXPLICIT 1
 #define DIFF_NO_INDEX_IMPLICIT 2
@@ -30,7 +32,8 @@ static const char builtin_diff_usage[] =
 "   or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n"
 "   or: git diff [<options>] <commit>...<commit> [--] [<path>...]\n"
 "   or: git diff [<options>] <blob> <blob>\n"
-"   or: git diff [<options>] --no-index [--] <path> <path>\n"
+"   or: git diff [<options>] --no-index [--] <path> <path>"
+"\n"
 COMMON_DIFF_OPTIONS_HELP;
 
 static const char *blob_path(struct object_array_entry *entry)
@@ -72,9 +75,9 @@ static void stuff_change(struct diff_options *opt,
        diff_queue(&diff_queued_diff, one, two);
 }
 
-static int builtin_diff_b_f(struct rev_info *revs,
-                           int argc, const char **argv,
-                           struct object_array_entry **blob)
+static void builtin_diff_b_f(struct rev_info *revs,
+                            int argc, const char **argv UNUSED,
+                            struct object_array_entry **blob)
 {
        /* Blob vs file in the working tree*/
        struct stat st;
@@ -104,12 +107,11 @@ static int builtin_diff_b_f(struct rev_info *revs,
                     path);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
-       return 0;
 }
 
-static int builtin_diff_blobs(struct rev_info *revs,
-                             int argc, const char **argv,
-                             struct object_array_entry **blob)
+static void builtin_diff_blobs(struct rev_info *revs,
+                              int argc, const char **argv UNUSED,
+                              struct object_array_entry **blob)
 {
        const unsigned mode = canon_mode(S_IFREG | 0644);
 
@@ -129,11 +131,10 @@ static int builtin_diff_blobs(struct rev_info *revs,
                     blob_path(blob[0]), blob_path(blob[1]));
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
-       return 0;
 }
 
-static int builtin_diff_index(struct rev_info *revs,
-                             int argc, const char **argv)
+static void builtin_diff_index(struct rev_info *revs,
+                              int argc, const char **argv)
 {
        unsigned int option = 0;
        while (1 < argc) {
@@ -156,21 +157,20 @@ static int builtin_diff_index(struct rev_info *revs,
                usage(builtin_diff_usage);
        if (!(option & DIFF_INDEX_CACHED)) {
                setup_work_tree();
-               if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
-                       perror("read_cache_preload");
-                       return -1;
+               if (repo_read_index_preload(the_repository,
+                                           &revs->diffopt.pathspec, 0) < 0) {
+                       die_errno("repo_read_index_preload");
                }
-       } else if (read_cache() < 0) {
-               perror("read_cache");
-               return -1;
+       } else if (repo_read_index(the_repository) < 0) {
+               die_errno("repo_read_cache");
        }
-       return run_diff_index(revs, option);
+       run_diff_index(revs, option);
 }
 
-static int builtin_diff_tree(struct rev_info *revs,
-                            int argc, const char **argv,
-                            struct object_array_entry *ent0,
-                            struct object_array_entry *ent1)
+static void builtin_diff_tree(struct rev_info *revs,
+                             int argc, const char **argv,
+                             struct object_array_entry *ent0,
+                             struct object_array_entry *ent1)
 {
        const struct object_id *(oid[2]);
        struct object_id mb_oid;
@@ -203,13 +203,12 @@ static int builtin_diff_tree(struct rev_info *revs,
        }
        diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
        log_tree_diff_flush(revs);
-       return 0;
 }
 
-static int builtin_diff_combined(struct rev_info *revs,
-                                int argc, const char **argv,
-                                struct object_array_entry *ent,
-                                int ents)
+static void builtin_diff_combined(struct rev_info *revs,
+                                 int argc, const char **argv UNUSED,
+                                 struct object_array_entry *ent,
+                                 int ents, int first_non_parent)
 {
        struct oid_array parents = OID_ARRAY_INIT;
        int i;
@@ -217,13 +216,19 @@ static int builtin_diff_combined(struct rev_info *revs,
        if (argc > 1)
                usage(builtin_diff_usage);
 
+       if (first_non_parent < 0)
+               die(_("no merge given, only parents."));
+       if (first_non_parent >= ents)
+               BUG("first_non_parent out of range: %d", first_non_parent);
+
        diff_merges_set_dense_combined_if_unset(revs);
 
-       for (i = 1; i < ents; i++)
-               oid_array_append(&parents, &ent[i].item->oid);
-       diff_tree_combined(&ent[0].item->oid, &parents, revs);
+       for (i = 0; i < ents; i++) {
+               if (i != first_non_parent)
+                       oid_array_append(&parents, &ent[i].item->oid);
+       }
+       diff_tree_combined(&ent[first_non_parent].item->oid, &parents, revs);
        oid_array_clear(&parents);
-       return 0;
 }
 
 static void refresh_index_quietly(void)
@@ -231,16 +236,17 @@ static void refresh_index_quietly(void)
        struct lock_file lock_file = LOCK_INIT;
        int fd;
 
-       fd = hold_locked_index(&lock_file, 0);
+       fd = repo_hold_locked_index(the_repository, &lock_file, 0);
        if (fd < 0)
                return;
-       discard_cache();
-       read_cache();
-       refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
+       discard_index(&the_index);
+       repo_read_index(the_repository);
+       refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
+                     NULL);
        repo_update_index_if_able(the_repository, &lock_file);
 }
 
-static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
+static void builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
 {
        unsigned int options = 0;
 
@@ -255,8 +261,10 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
                        options |= DIFF_SILENT_ON_REMOVED;
                else if (!strcmp(argv[1], "-h"))
                        usage(builtin_diff_usage);
-               else
-                       return error(_("invalid option: %s"), argv[1]);
+               else {
+                       error(_("invalid option: %s"), argv[1]);
+                       usage(builtin_diff_usage);
+               }
                argv++; argc--;
        }
 
@@ -271,11 +279,11 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
                diff_merges_set_dense_combined_if_unset(revs);
 
        setup_work_tree();
-       if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
-               perror("read_cache_preload");
-               return -1;
+       if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec,
+                                   0) < 0) {
+               die_errno("repo_read_index_preload");
        }
-       return run_diff_files(revs, options);
+       run_diff_files(revs, options);
 }
 
 struct symdiff {
@@ -385,10 +393,11 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        int i;
        struct rev_info rev;
        struct object_array ent = OBJECT_ARRAY_INIT;
+       int first_non_parent = -1;
        int blobs = 0, paths = 0;
        struct object_array_entry *blob[2];
        int nongit = 0, no_index = 0;
-       int result = 0;
+       int result;
        struct symdiff sdiff;
 
        /*
@@ -463,8 +472,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        repo_init_revisions(the_repository, &rev, prefix);
 
        /* Set up defaults that will apply to both no-index and regular diffs. */
-       rev.diffopt.stat_width = -1;
-       rev.diffopt.stat_graph_width = -1;
+       init_diffstat_widths(&rev.diffopt);
        rev.diffopt.flags.allow_external = 1;
        rev.diffopt.flags.allow_textconv = 1;
 
@@ -536,13 +544,18 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                if (!obj)
                        die(_("invalid object '%s' given."), name);
                if (obj->type == OBJ_COMMIT)
-                       obj = &get_commit_tree(((struct commit *)obj))->object;
+                       obj = &repo_get_commit_tree(the_repository,
+                                                   ((struct commit *)obj))->object;
 
                if (obj->type == OBJ_TREE) {
                        if (sdiff.skip && bitmap_get(sdiff.skip, i))
                                continue;
                        obj->flags |= flags;
                        add_object_array(obj, name, &ent);
+                       if (first_non_parent < 0 &&
+                           (i >= rev.cmdline.nr || /* HEAD by hand. */
+                            rev.cmdline.rev[i].whence != REV_CMD_PARENTS_ONLY))
+                               first_non_parent = ent.nr - 1;
                } else if (obj->type == OBJ_BLOB) {
                        if (2 <= blobs)
                                die(_("more than two blobs given: '%s'"), name);
@@ -562,17 +575,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        if (!ent.nr) {
                switch (blobs) {
                case 0:
-                       result = builtin_diff_files(&rev, argc, argv);
+                       builtin_diff_files(&rev, argc, argv);
                        break;
                case 1:
                        if (paths != 1)
                                usage(builtin_diff_usage);
-                       result = builtin_diff_b_f(&rev, argc, argv, blob);
+                       builtin_diff_b_f(&rev, argc, argv, blob);
                        break;
                case 2:
                        if (paths)
                                usage(builtin_diff_usage);
-                       result = builtin_diff_blobs(&rev, argc, argv, blob);
+                       builtin_diff_blobs(&rev, argc, argv, blob);
                        break;
                default:
                        usage(builtin_diff_usage);
@@ -581,21 +594,22 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        else if (blobs)
                usage(builtin_diff_usage);
        else if (ent.nr == 1)
-               result = builtin_diff_index(&rev, argc, argv);
+               builtin_diff_index(&rev, argc, argv);
        else if (ent.nr == 2) {
                if (sdiff.warn)
                        warning(_("%s...%s: multiple merge bases, using %s"),
                                sdiff.left, sdiff.right, sdiff.base);
-               result = builtin_diff_tree(&rev, argc, argv,
-                                          &ent.objects[0], &ent.objects[1]);
+               builtin_diff_tree(&rev, argc, argv,
+                                 &ent.objects[0], &ent.objects[1]);
        } else
-               result = builtin_diff_combined(&rev, argc, argv,
-                                              ent.objects, ent.nr);
-       result = diff_result_code(&rev.diffopt, result);
+               builtin_diff_combined(&rev, argc, argv,
+                                     ent.objects, ent.nr,
+                                     first_non_parent);
+       result = diff_result_code(&rev.diffopt);
        if (1 < rev.diffopt.skip_stat_unmatch)
                refresh_index_quietly();
        release_revisions(&rev);
-       UNLEAK(ent);
+       object_array_clear(&ent);
        UNLEAK(blob);
        return result;
 }
index 4b10ad1a36908fe8067daa1e66a000c8c8e85718..a3c72b8258e7cb34e6e87e679e6a996d6086ecb0 100644 (file)
  *
  * Copyright (C) 2016 Johannes Schindelin
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "config.h"
+#include "copy.h"
 #include "run-command.h"
-#include "exec-cmd.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
+#include "sparse-index.h"
 #include "strvec.h"
 #include "strbuf.h"
 #include "lockfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "dir.h"
 #include "entry.h"
+#include "setup.h"
 
 static int trust_exit_code;
 
@@ -32,20 +39,24 @@ static const char *const builtin_difftool_usage[] = {
        NULL
 };
 
-static int difftool_config(const char *var, const char *value, void *cb)
+static int difftool_config(const char *var, const char *value,
+                          const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "difftool.trustexitcode")) {
                trust_exit_code = git_config_bool(var, value);
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static int print_tool_help(void)
 {
-       const char *argv[] = { "mergetool", "--tool-help=diff", NULL };
-       return run_command_v_opt(argv, RUN_GIT_CMD);
+       struct child_process cmd = CHILD_PROCESS_INIT;
+
+       cmd.git_cmd = 1;
+       strvec_pushl(&cmd.args, "mergetool", "--tool-help=diff", NULL);
+       return run_command(&cmd);
 }
 
 static int parse_index_info(char *p, int *mode1, int *mode2,
@@ -292,7 +303,8 @@ static char *get_symlink(const struct object_id *oid, const char *path)
        } else {
                enum object_type type;
                unsigned long size;
-               data = read_object_file(oid, &type, &size);
+               data = repo_read_object_file(the_repository, oid, &type,
+                                            &size);
                if (!data)
                        die(_("could not read object %s for symlink %s"),
                                oid_to_hex(oid), path);
@@ -358,10 +370,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL);
        struct hashmap_iter iter;
        struct pair_entry *entry;
-       struct index_state wtindex;
+       struct index_state wtindex = INDEX_STATE_INIT(the_repository);
        struct checkout lstate, rstate;
-       int flags = RUN_GIT_CMD, err = 0;
-       const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
+       int err = 0;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        struct hashmap wt_modified, tmp_modified;
        int indices_loaded = 0;
 
@@ -384,8 +396,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        mkdir(ldir.buf, 0700);
        mkdir(rdir.buf, 0700);
 
-       memset(&wtindex, 0, sizeof(wtindex));
-
        memset(&lstate, 0, sizeof(lstate));
        lstate.base_dir = lbase_dir = xstrdup(ldir.buf);
        lstate.base_dir_len = ldir.len;
@@ -563,16 +573,17 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        }
 
        strbuf_setlen(&ldir, ldir_len);
-       helper_argv[1] = ldir.buf;
        strbuf_setlen(&rdir, rdir_len);
-       helper_argv[2] = rdir.buf;
 
        if (extcmd) {
-               helper_argv[0] = extcmd;
-               flags = 0;
-       } else
+               strvec_push(&cmd.args, extcmd);
+       } else {
+               strvec_push(&cmd.args, "difftool--helper");
+               cmd.git_cmd = 1;
                setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1);
-       ret = run_command_v_opt(helper_argv, flags);
+       }
+       strvec_pushl(&cmd.args, ldir.buf, rdir.buf, NULL);
+       ret = run_command(&cmd);
 
        /* TODO: audit for interaction with sparse-index. */
        ensure_full_index(&wtindex);
@@ -682,7 +693,7 @@ static int run_file_diff(int prompt, const char *prefix,
 
 int cmd_difftool(int argc, const char **argv, const char *prefix)
 {
-       int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,
+       int use_gui_tool = -1, dir_diff = 0, prompt = -1, symlinks = 0,
            tool_help = 0, no_index = 0;
        static char *difftool_cmd = NULL, *extcmd = NULL;
        struct option builtin_difftool_options[] = {
@@ -732,13 +743,21 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
        } else if (dir_diff)
                die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
 
-       die_for_incompatible_opt3(use_gui_tool, "--gui",
+       die_for_incompatible_opt3(use_gui_tool == 1, "--gui",
                                  !!difftool_cmd, "--tool",
                                  !!extcmd, "--extcmd");
 
-       if (use_gui_tool)
+       /*
+        * Explicitly specified GUI option is forwarded to git-mergetool--lib.sh;
+        * empty or unset means "use the difftool.guiDefault config or default to
+        * false".
+        */
+       if (use_gui_tool == 1)
                setenv("GIT_MERGETOOL_GUI", "true", 1);
-       else if (difftool_cmd) {
+       else if (use_gui_tool == 0)
+               setenv("GIT_MERGETOOL_GUI", "false", 1);
+
+       if (difftool_cmd) {
                if (*difftool_cmd)
                        setenv("GIT_DIFF_TOOL", difftool_cmd, 1);
                else
index 3b3314e7b2a3343b78d14ee3bc9d4a92fb578fd6..f18f0809f9c7f78874eaddfd869c5fc5d11831e0 100644 (file)
@@ -4,11 +4,13 @@
  * Copyright (C) 2007 Johannes E. Schindelin
  */
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "commit.h"
 #include "object.h"
 #include "tag.h"
@@ -23,7 +25,6 @@
 #include "quote.h"
 #include "remote.h"
 #include "blob.h"
-#include "commit-slab.h"
 
 static const char *fast_export_usage[] = {
        N_("git fast-export [<rev-list-opts>]"),
@@ -31,9 +32,9 @@ static const char *fast_export_usage[] = {
 };
 
 static int progress;
-static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
-static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
-static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
+static enum signed_tag_mode { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
+static enum tag_of_filtered_mode { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
+static enum reencode_mode { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
 static int fake_missing_tagger;
 static int use_done_feature;
 static int no_data;
@@ -51,16 +52,18 @@ static struct revision_sources revision_sources;
 static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
 {
+       enum signed_tag_mode *val = opt->value;
+
        if (unset || !strcmp(arg, "abort"))
-               signed_tag_mode = SIGNED_TAG_ABORT;
+               *val = SIGNED_TAG_ABORT;
        else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
-               signed_tag_mode = VERBATIM;
+               *val = VERBATIM;
        else if (!strcmp(arg, "warn"))
-               signed_tag_mode = WARN;
+               *val = WARN;
        else if (!strcmp(arg, "warn-strip"))
-               signed_tag_mode = WARN_STRIP;
+               *val = WARN_STRIP;
        else if (!strcmp(arg, "strip"))
-               signed_tag_mode = STRIP;
+               *val = STRIP;
        else
                return error("Unknown signed-tags mode: %s", arg);
        return 0;
@@ -69,12 +72,14 @@ static int parse_opt_signed_tag_mode(const struct option *opt,
 static int parse_opt_tag_of_filtered_mode(const struct option *opt,
                                          const char *arg, int unset)
 {
+       enum tag_of_filtered_mode *val = opt->value;
+
        if (unset || !strcmp(arg, "abort"))
-               tag_of_filtered_mode = TAG_FILTERING_ABORT;
+               *val = TAG_FILTERING_ABORT;
        else if (!strcmp(arg, "drop"))
-               tag_of_filtered_mode = DROP;
+               *val = DROP;
        else if (!strcmp(arg, "rewrite"))
-               tag_of_filtered_mode = REWRITE;
+               *val = REWRITE;
        else
                return error("Unknown tag-of-filtered mode: %s", arg);
        return 0;
@@ -83,21 +88,23 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt,
 static int parse_opt_reencode_mode(const struct option *opt,
                                   const char *arg, int unset)
 {
+       enum reencode_mode *val = opt->value;
+
        if (unset) {
-               reencode_mode = REENCODE_ABORT;
+               *val = REENCODE_ABORT;
                return 0;
        }
 
        switch (git_parse_maybe_bool(arg)) {
        case 0:
-               reencode_mode = REENCODE_NO;
+               *val = REENCODE_NO;
                break;
        case 1:
-               reencode_mode = REENCODE_YES;
+               *val = REENCODE_YES;
                break;
        default:
                if (!strcasecmp(arg, "abort"))
-                       reencode_mode = REENCODE_ABORT;
+                       *val = REENCODE_ABORT;
                else
                        return error("Unknown reencoding mode: %s", arg);
        }
@@ -109,7 +116,7 @@ static struct decoration idnums;
 static uint32_t last_idnum;
 struct anonymized_entry {
        struct hashmap_entry hash;
-       const char *anon;
+       char *anon;
        const char orig[FLEX_ARRAY];
 };
 
@@ -138,43 +145,56 @@ static int anonymized_entry_cmp(const void *cmp_data UNUSED,
        return strcmp(a->orig, b->orig);
 }
 
+static struct anonymized_entry *add_anonymized_entry(struct hashmap *map,
+                                                    unsigned hash,
+                                                    const char *orig, size_t len,
+                                                    char *anon)
+{
+       struct anonymized_entry *ret, *old;
+
+       if (!map->cmpfn)
+               hashmap_init(map, anonymized_entry_cmp, NULL, 0);
+
+       FLEX_ALLOC_MEM(ret, orig, orig, len);
+       hashmap_entry_init(&ret->hash, hash);
+       ret->anon = anon;
+       old = hashmap_put_entry(map, ret, hash);
+
+       if (old) {
+               free(old->anon);
+               free(old);
+       }
+
+       return ret;
+}
+
 /*
  * Basically keep a cache of X->Y so that we can repeatedly replace
  * the same anonymized string with another. The actual generation
  * is farmed out to the generate function.
  */
 static const char *anonymize_str(struct hashmap *map,
-                                char *(*generate)(void *),
-                                const char *orig, size_t len,
-                                void *data)
+                                char *(*generate)(void),
+                                const char *orig, size_t len)
 {
        struct anonymized_entry_key key;
        struct anonymized_entry *ret;
 
-       if (!map->cmpfn)
-               hashmap_init(map, anonymized_entry_cmp, NULL, 0);
-
        hashmap_entry_init(&key.hash, memhash(orig, len));
        key.orig = orig;
        key.orig_len = len;
 
        /* First check if it's a token the user configured manually... */
-       if (anonymized_seeds.cmpfn)
-               ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
-       else
-               ret = NULL;
+       ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
 
        /* ...otherwise check if we've already seen it in this context... */
        if (!ret)
                ret = hashmap_get_entry(map, &key, hash, &key);
 
        /* ...and finally generate a new mapping if necessary */
-       if (!ret) {
-               FLEX_ALLOC_MEM(ret, orig, orig, len);
-               hashmap_entry_init(&ret->hash, key.hash.hash);
-               ret->anon = generate(data);
-               hashmap_put(map, &ret->hash);
-       }
+       if (!ret)
+               ret = add_anonymized_entry(map, key.hash.hash,
+                                          orig, len, generate());
 
        return ret->anon;
 }
@@ -187,12 +207,12 @@ static const char *anonymize_str(struct hashmap *map,
  */
 static void anonymize_path(struct strbuf *out, const char *path,
                           struct hashmap *map,
-                          char *(*generate)(void *))
+                          char *(*generate)(void))
 {
        while (*path) {
                const char *end_of_component = strchrnul(path, '/');
                size_t len = end_of_component - path;
-               const char *c = anonymize_str(map, generate, path, len, NULL);
+               const char *c = anonymize_str(map, generate, path, len);
                strbuf_addstr(out, c);
                path = end_of_component;
                if (*path)
@@ -296,7 +316,7 @@ static void export_blob(const struct object_id *oid)
                object = (struct object *)lookup_blob(the_repository, oid);
                eaten = 0;
        } else {
-               buf = read_object_file(oid, &type, &size);
+               buf = repo_read_object_file(the_repository, oid, &type, &size);
                if (!buf)
                        die("could not read blob %s", oid_to_hex(oid));
                if (check_object_signature(the_repository, oid, buf, size,
@@ -367,7 +387,7 @@ static void print_path_1(const char *path)
                printf("%s", path);
 }
 
-static char *anonymize_path_component(void *data)
+static char *anonymize_path_component(void)
 {
        static int counter;
        struct strbuf out = STRBUF_INIT;
@@ -389,7 +409,7 @@ static void print_path(const char *path)
        }
 }
 
-static char *generate_fake_oid(void *data)
+static char *generate_fake_oid(void)
 {
        static uint32_t counter = 1; /* avoid null oid */
        const unsigned hashsz = the_hash_algo->rawsz;
@@ -405,11 +425,11 @@ static const char *anonymize_oid(const char *oid_hex)
 {
        static struct hashmap objs;
        size_t len = strlen(oid_hex);
-       return anonymize_str(&objs, generate_fake_oid, oid_hex, len, NULL);
+       return anonymize_str(&objs, generate_fake_oid, oid_hex, len);
 }
 
 static void show_filemodify(struct diff_queue_struct *q,
-                           struct diff_options *options, void *data)
+                           struct diff_options *options UNUSED, void *data)
 {
        int i;
        struct string_list *changed = data;
@@ -502,7 +522,7 @@ static const char *find_encoding(const char *begin, const char *end)
        return bol;
 }
 
-static char *anonymize_ref_component(void *data)
+static char *anonymize_ref_component(void)
 {
        static int counter;
        struct strbuf out = STRBUF_INIT;
@@ -542,13 +562,13 @@ static const char *anonymize_refname(const char *refname)
  * We do not even bother to cache commit messages, as they are unlikely
  * to be repeated verbatim, and it is not that interesting when they are.
  */
-static char *anonymize_commit_message(const char *old)
+static char *anonymize_commit_message(void)
 {
        static int counter;
        return xstrfmt("subject %d\n\nbody\n", counter++);
 }
 
-static char *anonymize_ident(void *data)
+static char *anonymize_ident(void)
 {
        static int counter;
        struct strbuf out = STRBUF_INIT;
@@ -591,7 +611,7 @@ static void anonymize_ident_line(const char **beg, const char **end)
 
                len = split.mail_end - split.name_begin;
                ident = anonymize_str(&idents, anonymize_ident,
-                                     split.name_begin, len, NULL);
+                                     split.name_begin, len);
                strbuf_addstr(out, ident);
                strbuf_addch(out, ' ');
                strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
@@ -618,7 +638,7 @@ 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 = get_commit_buffer(commit, NULL);
+       commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL);
        author = strstr(commit_buffer, "\nauthor ");
        if (!author)
                die("could not find author in commit %s",
@@ -669,7 +689,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
 
        mark_next_object(&commit->object);
        if (anonymize) {
-               reencoded = anonymize_commit_message(message);
+               reencoded = anonymize_commit_message();
        } else if (encoding) {
                switch(reencode_mode) {
                case REENCODE_YES:
@@ -699,7 +719,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
                          ? strlen(message) : 0),
               reencoded ? reencoded : message ? message : "");
        free(reencoded);
-       unuse_commit_buffer(commit, commit_buffer);
+       repo_unuse_commit_buffer(the_repository, commit, commit_buffer);
 
        for (i = 0, p = commit->parents; p; p = p->next) {
                struct object *obj = &p->item->object;
@@ -732,7 +752,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
        show_progress();
 }
 
-static char *anonymize_tag(void *data)
+static char *anonymize_tag(void)
 {
        static int counter;
        struct strbuf out = STRBUF_INIT;
@@ -766,7 +786,8 @@ static void handle_tag(const char *name, struct tag *tag)
                return;
        }
 
-       buf = read_object_file(&tag->object.oid, &type, &size);
+       buf = repo_read_object_file(the_repository, &tag->object.oid, &type,
+                                   &size);
        if (!buf)
                die("could not read tag %s", oid_to_hex(&tag->object.oid));
        message = memmem(buf, size, "\n\n", 2);
@@ -794,7 +815,7 @@ static void handle_tag(const char *name, struct tag *tag)
                if (message) {
                        static struct hashmap tags;
                        message = anonymize_str(&tags, anonymize_tag,
-                                               message, message_size, NULL);
+                                               message, message_size);
                        message_size = strlen(message);
                }
        }
@@ -917,7 +938,8 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                if (e->flags & UNINTERESTING)
                        continue;
 
-               if (dwim_ref(e->name, strlen(e->name), &oid, &full_name, 0) != 1)
+               if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
+                                 &oid, &full_name, 0) != 1)
                        continue;
 
                if (refspecs.nr) {
@@ -1125,11 +1147,6 @@ static void handle_deletes(void)
        }
 }
 
-static char *anonymize_seed(void *data)
-{
-       return xstrdup(data);
-}
-
 static int parse_opt_anonymize_map(const struct option *opt,
                                   const char *arg, int unset)
 {
@@ -1151,7 +1168,8 @@ static int parse_opt_anonymize_map(const struct option *opt,
        if (!keylen || !*value)
                return error(_("--anonymize-map token cannot be empty"));
 
-       anonymize_str(map, anonymize_seed, arg, keylen, (void *)value);
+       add_anonymized_entry(map, memhash(arg, keylen), arg, keylen,
+                            xstrdup(value));
 
        return 0;
 }
index 7134683ab93f96d4c213d478fc7fadc8a17856c0..92eda20683ca2d07c24a03f5903d1e604ce54763 100644 (file)
@@ -1,5 +1,8 @@
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "lockfile.h"
 #include "commit.h"
 #include "delta.h"
 #include "pack.h"
+#include "path.h"
 #include "refs.h"
 #include "csum-file.h"
 #include "quote.h"
 #include "dir.h"
 #include "run-command.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "mem-pool.h"
 #include "commit-reach.h"
 #include "khash.h"
@@ -175,6 +181,7 @@ static FILE *pack_edges;
 static unsigned int show_stats = 1;
 static int global_argc;
 static const char **global_argv;
+static const char *global_prefix;
 
 /* Memory pools */
 static struct mem_pool fi_mem_pool = {
@@ -436,7 +443,7 @@ static void set_checkpoint_signal(void)
 
 #else
 
-static void checkpoint_signal(int signo)
+static void checkpoint_signal(int signo UNUSED)
 {
        checkpoint_requested = 1;
 }
@@ -1095,6 +1102,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
                || (pack_size + PACK_SIZE_THRESHOLD + len) < pack_size)
                cycle_packfile();
 
+       the_hash_algo->init_fn(&checkpoint.ctx);
        hashfile_checkpoint(pack_file, &checkpoint);
        offset = checkpoint.offset;
 
@@ -1265,7 +1273,7 @@ static void load_tree(struct tree_entry *root)
                        die("Can't load tree %s", oid_to_hex(oid));
        } else {
                enum object_type type;
-               buf = read_object_file(oid, &type, &size);
+               buf = repo_read_object_file(the_repository, oid, &type, &size);
                if (!buf || type != OBJ_TREE)
                        die("Can't load tree %s", oid_to_hex(oid));
        }
@@ -1625,7 +1633,7 @@ static int update_branch(struct branch *b)
                if (!old_cmit || !new_cmit)
                        return error("Branch %s is missing commits.", b->name);
 
-               if (!in_merge_bases(old_cmit, new_cmit)) {
+               if (!repo_in_merge_bases(the_repository, old_cmit, new_cmit)) {
                        warning("Not updating %s"
                                " (new tip %s does not contain %s)",
                                b->name, oid_to_hex(&b->oid),
@@ -2486,7 +2494,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
                if (commit_oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", commit_mark);
                oidcpy(&commit_oid, &commit_oe->idx.oid);
-       } else if (!get_oid(p, &commit_oid)) {
+       } else if (!repo_get_oid(the_repository, p, &commit_oid)) {
                unsigned long size;
                char *buf = read_object_with_reference(the_repository,
                                                       &commit_oid,
@@ -2599,7 +2607,7 @@ static int parse_objectish(struct branch *b, const char *objectish)
                        } else
                                parse_from_existing(b);
                }
-       } else if (!get_oid(objectish, &b->oid)) {
+       } else if (!repo_get_oid(the_repository, objectish, &b->oid)) {
                parse_from_existing(b);
                if (is_null_oid(&b->oid))
                        b->delete = 1;
@@ -2654,7 +2662,7 @@ static struct hash_list *parse_merge(unsigned int *count)
                        if (oe->type != OBJ_COMMIT)
                                die("Mark :%" PRIuMAX " not a commit", idnum);
                        oidcpy(&n->oid, &oe->idx.oid);
-               } else if (!get_oid(from, &n->oid)) {
+               } else if (!repo_get_oid(the_repository, from, &n->oid)) {
                        unsigned long size;
                        char *buf = read_object_with_reference(the_repository,
                                                               &n->oid,
@@ -2801,8 +2809,7 @@ static void parse_new_tag(const char *arg)
        enum object_type type;
        const char *v;
 
-       t = mem_pool_alloc(&fi_mem_pool, sizeof(struct tag));
-       memset(t, 0, sizeof(struct tag));
+       t = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct tag));
        t->name = mem_pool_strdup(&fi_mem_pool, arg);
        if (last_tag)
                last_tag->next_tag = t;
@@ -2827,7 +2834,7 @@ static void parse_new_tag(const char *arg)
                oe = find_mark(marks, from_mark);
                type = oe->type;
                oidcpy(&oid, &oe->idx.oid);
-       } else if (!get_oid(from, &oid)) {
+       } else if (!repo_get_oid(the_repository, from, &oid)) {
                struct object_entry *oe = find_object(&oid);
                if (!oe) {
                        type = oid_object_info(the_repository, &oid, NULL);
@@ -2936,7 +2943,7 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid)
        char *buf;
 
        if (!oe || oe->pack_id == MAX_PACK_ID) {
-               buf = read_object_file(oid, &type, &size);
+               buf = repo_read_object_file(the_repository, oid, &type, &size);
        } else {
                type = oe->type;
                buf = gfi_unpack_entry(oe, &size);
@@ -3044,7 +3051,8 @@ static struct object_entry *dereference(struct object_entry *oe,
                buf = gfi_unpack_entry(oe, &size);
        } else {
                enum object_type unused;
-               buf = read_object_file(oid, &unused, &size);
+               buf = repo_read_object_file(the_repository, oid, &unused,
+                                           &size);
        }
        if (!buf)
                die("Can't load object %s", oid_to_hex(oid));
@@ -3245,7 +3253,7 @@ static void parse_alias(void)
 static char* make_fast_import_path(const char *path)
 {
        if (!relative_marks_paths || is_absolute_path(path))
-               return xstrdup(path);
+               return prefix_filename(global_prefix, path);
        return git_pathdup("info/fast-import/%s", path);
 }
 
@@ -3316,9 +3324,11 @@ static void option_cat_blob_fd(const char *fd)
 
 static void option_export_pack_edges(const char *edges)
 {
+       char *fn = prefix_filename(global_prefix, edges);
        if (pack_edges)
                fclose(pack_edges);
-       pack_edges = xfopen(edges, "a");
+       pack_edges = xfopen(fn, "a");
+       free(fn);
 }
 
 static void option_rewrite_submodules(const char *arg, struct string_list *list)
@@ -3333,11 +3343,13 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list)
        f++;
        CALLOC_ARRAY(ms, 1);
 
+       f = prefix_filename(global_prefix, f);
        fp = fopen(f, "r");
        if (!fp)
                die_errno("cannot read '%s'", f);
        read_mark_file(&ms, fp, insert_oid_entry);
        fclose(fp);
+       free(f);
 
        string_list_insert(list, s)->util = ms;
 }
@@ -3551,6 +3563,7 @@ int cmd_fast_import(int argc, const char **argv, const char *prefix)
 
        global_argc = argc;
        global_argv = argv;
+       global_prefix = prefix;
 
        rc_free = mem_pool_alloc(&fi_mem_pool, cmd_save * sizeof(*rc_free));
        for (i = 0; i < (cmd_save - 1); i++)
index afe679368deec2a0288de1606259b73847ce434b..44c05ee86cc7048e72e455f4c2a28a1d42a1f7fa 100644 (file)
@@ -1,4 +1,7 @@
 #include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
 #include "pkt-line.h"
 #include "fetch-pack.h"
 #include "remote.h"
@@ -40,7 +43,7 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
        (*sought)[*nr - 1] = ref;
 }
 
-int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
+int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED)
 {
        int i, ret;
        struct ref *ref = NULL;
@@ -211,8 +214,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                int flags = args.verbose ? CONNECT_VERBOSE : 0;
                if (args.diag_url)
                        flags |= CONNECT_DIAG_URL;
-               conn = git_connect(fd, dest, args.uploadpack,
-                                  flags);
+               conn = git_connect(fd, dest, "git-upload-pack",
+                                  args.uploadpack, flags);
                if (!conn)
                        return args.diag_url ? 0 : 1;
        }
index a0fca93bb6a63e441e3921832f7c7d9a7bd3dd78..3aedfd1bb6361c6bbfd651970f9e9767d0595734 100644 (file)
@@ -1,15 +1,20 @@
 /*
  * "git fetch"
  */
-#include "cache.h"
+#include "builtin.h"
+#include "advice.h"
 #include "config.h"
+#include "gettext.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "oidset.h"
+#include "oid-array.h"
 #include "commit.h"
-#include "builtin.h"
 #include "string-list.h"
 #include "remote.h"
 #include "transport.h"
 #include "connected.h"
 #include "strvec.h"
 #include "utf8.h"
-#include "packfile.h"
+#include "pager.h"
+#include "path.h"
+#include "pkt-line.h"
 #include "list-objects-filter-options.h"
 #include "commit-reach.h"
 #include "branch.h"
 #include "promisor-remote.h"
 #include "commit-graph.h"
 #include "shallow.h"
-#include "worktree.h"
+#include "trace.h"
+#include "trace2.h"
+#include "bundle-uri.h"
 
 #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
 
@@ -46,25 +55,35 @@ enum {
        TAGS_SET = 2
 };
 
-static int fetch_prune_config = -1; /* unspecified */
-static int fetch_show_forced_updates = 1;
+enum display_format {
+       DISPLAY_FORMAT_FULL,
+       DISPLAY_FORMAT_COMPACT,
+       DISPLAY_FORMAT_PORCELAIN,
+};
+
+struct display_state {
+       struct strbuf buf;
+
+       int refcol_width;
+       enum display_format format;
+
+       char *url;
+       int url_len, shown_url;
+};
+
 static uint64_t forced_updates_ms = 0;
 static int prefetch = 0;
 static int prune = -1; /* unspecified */
 #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
 
-static int fetch_prune_tags_config = -1; /* unspecified */
 static int prune_tags = -1; /* unspecified */
 #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
 
-static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int append, dry_run, force, keep, update_head_ok;
 static int write_fetch_head = 1;
 static int verbosity, deepen_relative, set_upstream, refetch;
 static int progress = -1;
-static int enable_auto_gc = 1;
-static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_jobs = -1, submodule_fetch_jobs_config = -1;
-static int fetch_parallel_config = 1;
+static int tags = TAGS_DEFAULT, update_shallow, deepen;
 static int atomic_fetch;
 static enum transport_family family;
 static const char *depth;
@@ -74,58 +93,83 @@ static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
 static struct strbuf default_rla = STRBUF_INIT;
 static struct transport *gtransport;
 static struct transport *gsecondary;
-static const char *submodule_prefix = "";
-static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
-static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
-static int shown_url = 0;
 static struct refspec refmap = REFSPEC_INIT_FETCH;
 static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
 static struct string_list server_options = STRING_LIST_INIT_DUP;
 static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
-static int fetch_write_commit_graph = -1;
-static int stdin_refspecs = 0;
-static int negotiate_only;
 
-static int git_fetch_config(const char *k, const char *v, void *cb)
+struct fetch_config {
+       enum display_format display_format;
+       int all;
+       int prune;
+       int prune_tags;
+       int show_forced_updates;
+       int recurse_submodules;
+       int parallel;
+       int submodule_fetch_jobs;
+};
+
+static int git_fetch_config(const char *k, const char *v,
+                           const struct config_context *ctx, void *cb)
 {
+       struct fetch_config *fetch_config = cb;
+
+       if (!strcmp(k, "fetch.all")) {
+               fetch_config->all = git_config_bool(k, v);
+               return 0;
+       }
+
        if (!strcmp(k, "fetch.prune")) {
-               fetch_prune_config = git_config_bool(k, v);
+               fetch_config->prune = git_config_bool(k, v);
                return 0;
        }
 
        if (!strcmp(k, "fetch.prunetags")) {
-               fetch_prune_tags_config = git_config_bool(k, v);
+               fetch_config->prune_tags = git_config_bool(k, v);
                return 0;
        }
 
        if (!strcmp(k, "fetch.showforcedupdates")) {
-               fetch_show_forced_updates = git_config_bool(k, v);
+               fetch_config->show_forced_updates = git_config_bool(k, v);
                return 0;
        }
 
        if (!strcmp(k, "submodule.recurse")) {
                int r = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
-               recurse_submodules = r;
+               fetch_config->recurse_submodules = r;
        }
 
        if (!strcmp(k, "submodule.fetchjobs")) {
-               submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
+               fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v, ctx->kvi);
                return 0;
        } else if (!strcmp(k, "fetch.recursesubmodules")) {
-               recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+               fetch_config->recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
                return 0;
        }
 
        if (!strcmp(k, "fetch.parallel")) {
-               fetch_parallel_config = git_config_int(k, v);
-               if (fetch_parallel_config < 0)
+               fetch_config->parallel = git_config_int(k, v, ctx->kvi);
+               if (fetch_config->parallel < 0)
                        die(_("fetch.parallel cannot be negative"));
+               if (!fetch_config->parallel)
+                       fetch_config->parallel = online_cpus();
                return 0;
        }
 
-       return git_default_config(k, v, cb);
+       if (!strcmp(k, "fetch.output")) {
+               if (!v)
+                       return config_error_nonbool(k);
+               else if (!strcasecmp(v, "full"))
+                       fetch_config->display_format = DISPLAY_FORMAT_FULL;
+               else if (!strcasecmp(v, "compact"))
+                       fetch_config->display_format = DISPLAY_FORMAT_COMPACT;
+               else
+                       die(_("invalid value for '%s': '%s'"),
+                           "fetch.output", v);
+       }
+
+       return git_default_config(k, v, ctx, cb);
 }
 
 static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
@@ -136,97 +180,11 @@ static int parse_refmap_arg(const struct option *opt, const char *arg, int unset
         * "git fetch --refmap='' origin foo"
         * can be used to tell the command not to store anywhere
         */
-       refspec_append(&refmap, arg);
+       refspec_append(opt->value, arg);
 
        return 0;
 }
 
-static struct option builtin_fetch_options[] = {
-       OPT__VERBOSITY(&verbosity),
-       OPT_BOOL(0, "all", &all,
-                N_("fetch from all remotes")),
-       OPT_BOOL(0, "set-upstream", &set_upstream,
-                N_("set upstream for git pull/fetch")),
-       OPT_BOOL('a', "append", &append,
-                N_("append to .git/FETCH_HEAD instead of overwriting")),
-       OPT_BOOL(0, "atomic", &atomic_fetch,
-                N_("use atomic transaction to update references")),
-       OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
-                  N_("path to upload pack on remote end")),
-       OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
-       OPT_BOOL('m', "multiple", &multiple,
-                N_("fetch from multiple remotes")),
-       OPT_SET_INT('t', "tags", &tags,
-                   N_("fetch all tags and associated objects"), TAGS_SET),
-       OPT_SET_INT('n', NULL, &tags,
-                   N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
-       OPT_INTEGER('j', "jobs", &max_jobs,
-                   N_("number of submodules fetched in parallel")),
-       OPT_BOOL(0, "prefetch", &prefetch,
-                N_("modify the refspec to place all refs within refs/prefetch/")),
-       OPT_BOOL('p', "prune", &prune,
-                N_("prune remote-tracking branches no longer on remote")),
-       OPT_BOOL('P', "prune-tags", &prune_tags,
-                N_("prune local tags no longer on remote and clobber changed tags")),
-       OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
-                   N_("control recursive fetching of submodules"),
-                   PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
-       OPT_BOOL(0, "dry-run", &dry_run,
-                N_("dry run")),
-       OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
-                N_("write fetched references to the FETCH_HEAD file")),
-       OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
-       OPT_BOOL('u', "update-head-ok", &update_head_ok,
-                   N_("allow updating of HEAD ref")),
-       OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
-       OPT_STRING(0, "depth", &depth, N_("depth"),
-                  N_("deepen history of shallow clone")),
-       OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
-                  N_("deepen history of shallow repository based on time")),
-       OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
-                       N_("deepen history of shallow clone, excluding rev")),
-       OPT_INTEGER(0, "deepen", &deepen_relative,
-                   N_("deepen history of shallow clone")),
-       OPT_SET_INT_F(0, "unshallow", &unshallow,
-                     N_("convert to a complete repository"),
-                     1, PARSE_OPT_NONEG),
-       OPT_SET_INT_F(0, "refetch", &refetch,
-                     N_("re-fetch without negotiating common commits"),
-                     1, PARSE_OPT_NONEG),
-       { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
-                  N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
-       OPT_CALLBACK_F(0, "recurse-submodules-default",
-                  &recurse_submodules_default, N_("on-demand"),
-                  N_("default for recursive fetching of submodules "
-                     "(lower priority than config files)"),
-                  PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
-       OPT_BOOL(0, "update-shallow", &update_shallow,
-                N_("accept refs that update .git/shallow")),
-       OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
-                      N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
-       OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
-       OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
-                       TRANSPORT_FAMILY_IPV4),
-       OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
-                       TRANSPORT_FAMILY_IPV6),
-       OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
-                       N_("report that we have only objects reachable from this object")),
-       OPT_BOOL(0, "negotiate-only", &negotiate_only,
-                N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
-       OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
-       OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
-                N_("run 'maintenance --auto' after fetching")),
-       OPT_BOOL(0, "auto-gc", &enable_auto_gc,
-                N_("run 'maintenance --auto' after fetching")),
-       OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
-                N_("check for forced-updates on all updated branches")),
-       OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
-                N_("write the commit-graph after fetching")),
-       OPT_BOOL(0, "stdin", &stdin_refspecs,
-                N_("accept refspecs from stdin")),
-       OPT_END()
-};
-
 static void unlock_pack(unsigned int flags)
 {
        if (gtransport)
@@ -354,7 +312,7 @@ static void clear_item(struct refname_hash_entry *item)
 
 
 static void add_already_queued_tags(const char *refname,
-                                   const struct object_id *old_oid,
+                                   const struct object_id *old_oid UNUSED,
                                    const struct object_id *new_oid,
                                    void *cb_data)
 {
@@ -404,9 +362,9 @@ static void find_non_local_tags(const struct ref *refs,
                 */
                if (ends_with(ref->name, "^{}")) {
                        if (item &&
-                           !has_object_file_with_flags(&ref->old_oid, quick_flags) &&
+                           !repo_has_object_file_with_flags(the_repository, &ref->old_oid, quick_flags) &&
                            !oidset_contains(&fetch_oids, &ref->old_oid) &&
-                           !has_object_file_with_flags(&item->oid, quick_flags) &&
+                           !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
                            !oidset_contains(&fetch_oids, &item->oid))
                                clear_item(item);
                        item = NULL;
@@ -420,7 +378,7 @@ static void find_non_local_tags(const struct ref *refs,
                 * fetch.
                 */
                if (item &&
-                   !has_object_file_with_flags(&item->oid, quick_flags) &&
+                   !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
                    !oidset_contains(&fetch_oids, &item->oid))
                        clear_item(item);
 
@@ -441,7 +399,7 @@ static void find_non_local_tags(const struct ref *refs,
         * checked to see if it needs fetching.
         */
        if (item &&
-           !has_object_file_with_flags(&item->oid, quick_flags) &&
+           !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
            !oidset_contains(&fetch_oids, &item->oid))
                clear_item(item);
 
@@ -738,76 +696,97 @@ out:
        return ret;
 }
 
-static int refcol_width = 10;
-static int compact_format;
-
-static void adjust_refcol_width(const struct ref *ref)
+static int refcol_width(const struct ref *ref_map, int compact_format)
 {
-       int max, rlen, llen, len;
+       const struct ref *ref;
+       int max, width = 10;
 
-       /* uptodate lines are only shown on high verbosity level */
-       if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
-               return;
+       max = term_columns();
+       if (compact_format)
+               max = max * 2 / 3;
 
-       max    = term_columns();
-       rlen   = utf8_strwidth(prettify_refname(ref->name));
+       for (ref = ref_map; ref; ref = ref->next) {
+               int rlen, llen = 0, len;
 
-       llen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+               if (ref->status == REF_STATUS_REJECT_SHALLOW ||
+                   !ref->peer_ref ||
+                   !strcmp(ref->name, "HEAD"))
+                       continue;
 
-       /*
-        * rough estimation to see if the output line is too long and
-        * should not be counted (we can't do precise calculation
-        * anyway because we don't know if the error explanation part
-        * will be printed in update_local_ref)
-        */
-       if (compact_format) {
-               llen = 0;
-               max = max * 2 / 3;
+               /* uptodate lines are only shown on high verbosity level */
+               if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
+                       continue;
+
+               rlen = utf8_strwidth(prettify_refname(ref->name));
+               if (!compact_format)
+                       llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+
+               /*
+                * rough estimation to see if the output line is too long and
+                * should not be counted (we can't do precise calculation
+                * anyway because we don't know if the error explanation part
+                * will be printed in update_local_ref)
+                */
+               len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
+               if (len >= max)
+                       continue;
+
+               if (width < rlen)
+                       width = rlen;
        }
-       len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
-       if (len >= max)
-               return;
 
-       /*
-        * Not precise calculation for compact mode because '*' can
-        * appear on the left hand side of '->' and shrink the column
-        * back.
-        */
-       if (refcol_width < rlen)
-               refcol_width = rlen;
+       return width;
 }
 
-static void prepare_format_display(struct ref *ref_map)
+static void display_state_init(struct display_state *display_state, struct ref *ref_map,
+                              const char *raw_url, enum display_format format)
 {
-       struct ref *rm;
-       const char *format = "full";
+       int i;
 
-       if (verbosity < 0)
-               return;
+       memset(display_state, 0, sizeof(*display_state));
+       strbuf_init(&display_state->buf, 0);
+       display_state->format = format;
 
-       git_config_get_string_tmp("fetch.output", &format);
-       if (!strcasecmp(format, "full"))
-               compact_format = 0;
-       else if (!strcasecmp(format, "compact"))
-               compact_format = 1;
+       if (raw_url)
+               display_state->url = transport_anonymize_url(raw_url);
        else
-               die(_("invalid value for '%s': '%s'"),
-                   "fetch.output", format);
+               display_state->url = xstrdup("foreign");
 
-       for (rm = ref_map; rm; rm = rm->next) {
-               if (rm->status == REF_STATUS_REJECT_SHALLOW ||
-                   !rm->peer_ref ||
-                   !strcmp(rm->name, "HEAD"))
-                       continue;
+       display_state->url_len = strlen(display_state->url);
+       for (i = display_state->url_len - 1; display_state->url[i] == '/' && 0 <= i; i--)
+               ;
+       display_state->url_len = i + 1;
+       if (4 < i && !strncmp(".git", display_state->url + i - 3, 4))
+               display_state->url_len = i - 3;
+
+       if (verbosity < 0)
+               return;
 
-               adjust_refcol_width(rm);
+       switch (display_state->format) {
+       case DISPLAY_FORMAT_FULL:
+       case DISPLAY_FORMAT_COMPACT:
+               display_state->refcol_width = refcol_width(ref_map,
+                                                          display_state->format == DISPLAY_FORMAT_COMPACT);
+               break;
+       case DISPLAY_FORMAT_PORCELAIN:
+               /* We don't need to precompute anything here. */
+               break;
+       default:
+               BUG("unexpected display format %d", display_state->format);
        }
 }
 
-static void print_remote_to_local(struct strbuf *display,
+static void display_state_release(struct display_state *display_state)
+{
+       strbuf_release(&display_state->buf);
+       free(display_state->url);
+}
+
+static void print_remote_to_local(struct display_state *display_state,
                                  const char *remote, const char *local)
 {
-       strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+       strbuf_addf(&display_state->buf, "%-*s -> %s",
+                   display_state->refcol_width, remote, local);
 }
 
 static int find_and_replace(struct strbuf *haystack,
@@ -837,14 +816,14 @@ static int find_and_replace(struct strbuf *haystack,
        return 1;
 }
 
-static void print_compact(struct strbuf *display,
+static void print_compact(struct display_state *display_state,
                          const char *remote, const char *local)
 {
        struct strbuf r = STRBUF_INIT;
        struct strbuf l = STRBUF_INIT;
 
        if (!strcmp(remote, local)) {
-               strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+               strbuf_addf(&display_state->buf, "%-*s -> *", display_state->refcol_width, remote);
                return;
        }
 
@@ -853,40 +832,74 @@ static void print_compact(struct strbuf *display,
 
        if (!find_and_replace(&r, local, "*"))
                find_and_replace(&l, remote, "*");
-       print_remote_to_local(display, r.buf, l.buf);
+       print_remote_to_local(display_state, r.buf, l.buf);
 
        strbuf_release(&r);
        strbuf_release(&l);
 }
 
-static void format_display(struct strbuf *display, char code,
-                          const char *summary, const char *error,
-                          const char *remote, const char *local,
-                          int summary_width)
+static void display_ref_update(struct display_state *display_state, char code,
+                              const char *summary, const char *error,
+                              const char *remote, const char *local,
+                              const struct object_id *old_oid,
+                              const struct object_id *new_oid,
+                              int summary_width)
 {
-       int width;
+       FILE *f = stderr;
 
        if (verbosity < 0)
                return;
 
-       width = (summary_width + strlen(summary) - gettext_width(summary));
+       strbuf_reset(&display_state->buf);
 
-       strbuf_addf(display, "%c %-*s ", code, width, summary);
-       if (!compact_format)
-               print_remote_to_local(display, remote, local);
-       else
-               print_compact(display, remote, local);
-       if (error)
-               strbuf_addf(display, "  (%s)", error);
+       switch (display_state->format) {
+       case DISPLAY_FORMAT_FULL:
+       case DISPLAY_FORMAT_COMPACT: {
+               int width;
+
+               if (!display_state->shown_url) {
+                       strbuf_addf(&display_state->buf, _("From %.*s\n"),
+                                   display_state->url_len, display_state->url);
+                       display_state->shown_url = 1;
+               }
+
+               width = (summary_width + strlen(summary) - gettext_width(summary));
+               remote = prettify_refname(remote);
+               local = prettify_refname(local);
+
+               strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary);
+
+               if (display_state->format != DISPLAY_FORMAT_COMPACT)
+                       print_remote_to_local(display_state, remote, local);
+               else
+                       print_compact(display_state, remote, local);
+
+               if (error)
+                       strbuf_addf(&display_state->buf, "  (%s)", error);
+
+               break;
+       }
+       case DISPLAY_FORMAT_PORCELAIN:
+               strbuf_addf(&display_state->buf, "%c %s %s %s", code,
+                           oid_to_hex(old_oid), oid_to_hex(new_oid), local);
+               f = stdout;
+               break;
+       default:
+               BUG("unexpected display format %d", display_state->format);
+       };
+       strbuf_addch(&display_state->buf, '\n');
+
+       fputs(display_state->buf.buf, f);
 }
 
 static int update_local_ref(struct ref *ref,
                            struct ref_transaction *transaction,
-                           const char *remote, const struct ref *remote_ref,
-                           struct strbuf *display, int summary_width)
+                           struct display_state *display_state,
+                           const struct ref *remote_ref,
+                           int summary_width,
+                           const struct fetch_config *config)
 {
        struct commit *current = NULL, *updated;
-       const char *pretty_ref = prettify_refname(ref->name);
        int fast_forward = 0;
 
        if (!repo_has_object_file(the_repository, &ref->new_oid))
@@ -894,8 +907,9 @@ static int update_local_ref(struct ref *ref,
 
        if (oideq(&ref->old_oid, &ref->new_oid)) {
                if (verbosity > 0)
-                       format_display(display, '=', _("[up to date]"), NULL,
-                                      remote, pretty_ref, summary_width);
+                       display_ref_update(display_state, '=', _("[up to date]"), NULL,
+                                          remote_ref->name, ref->name,
+                                          &ref->old_oid, &ref->new_oid, summary_width);
                return 0;
        }
 
@@ -906,9 +920,10 @@ static int update_local_ref(struct ref *ref,
                 * If this is the head, and it's not okay to update
                 * the head, and the old value of the head isn't empty...
                 */
-               format_display(display, '!', _("[rejected]"),
-                              _("can't fetch into checked-out branch"),
-                              remote, pretty_ref, summary_width);
+               display_ref_update(display_state, '!', _("[rejected]"),
+                                  _("can't fetch into checked-out branch"),
+                                  remote_ref->name, ref->name,
+                                  &ref->old_oid, &ref->new_oid, summary_width);
                return 1;
        }
 
@@ -917,13 +932,16 @@ static int update_local_ref(struct ref *ref,
                if (force || ref->force) {
                        int r;
                        r = s_update_ref("updating tag", ref, transaction, 0);
-                       format_display(display, r ? '!' : 't', _("[tag update]"),
-                                      r ? _("unable to update local ref") : NULL,
-                                      remote, pretty_ref, summary_width);
+                       display_ref_update(display_state, r ? '!' : 't', _("[tag update]"),
+                                          r ? _("unable to update local ref") : NULL,
+                                          remote_ref->name, ref->name,
+                                          &ref->old_oid, &ref->new_oid, summary_width);
                        return r;
                } else {
-                       format_display(display, '!', _("[rejected]"), _("would clobber existing tag"),
-                                      remote, pretty_ref, summary_width);
+                       display_ref_update(display_state, '!', _("[rejected]"),
+                                          _("would clobber existing tag"),
+                                          remote_ref->name, ref->name,
+                                          &ref->old_oid, &ref->new_oid, summary_width);
                        return 1;
                }
        }
@@ -941,11 +959,10 @@ static int update_local_ref(struct ref *ref,
                 * Base this on the remote's ref name, as it's
                 * more likely to follow a standard layout.
                 */
-               const char *name = remote_ref ? remote_ref->name : "";
-               if (starts_with(name, "refs/tags/")) {
+               if (starts_with(remote_ref->name, "refs/tags/")) {
                        msg = "storing tag";
                        what = _("[new tag]");
-               } else if (starts_with(name, "refs/heads/")) {
+               } else if (starts_with(remote_ref->name, "refs/heads/")) {
                        msg = "storing head";
                        what = _("[new branch]");
                } else {
@@ -954,15 +971,17 @@ static int update_local_ref(struct ref *ref,
                }
 
                r = s_update_ref(msg, ref, transaction, 0);
-               format_display(display, r ? '!' : '*', what,
-                              r ? _("unable to update local ref") : NULL,
-                              remote, pretty_ref, summary_width);
+               display_ref_update(display_state, r ? '!' : '*', what,
+                                  r ? _("unable to update local ref") : NULL,
+                                  remote_ref->name, ref->name,
+                                  &ref->old_oid, &ref->new_oid, summary_width);
                return r;
        }
 
-       if (fetch_show_forced_updates) {
+       if (config->show_forced_updates) {
                uint64_t t_before = getnanotime();
-               fast_forward = in_merge_bases(current, updated);
+               fast_forward = repo_in_merge_bases(the_repository, current,
+                                                  updated);
                forced_updates_ms += (getnanotime() - t_before) / 1000000;
        } else {
                fast_forward = 1;
@@ -976,9 +995,10 @@ static int update_local_ref(struct ref *ref,
                strbuf_addstr(&quickref, "..");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
                r = s_update_ref("fast-forward", ref, transaction, 1);
-               format_display(display, r ? '!' : ' ', quickref.buf,
-                              r ? _("unable to update local ref") : NULL,
-                              remote, pretty_ref, summary_width);
+               display_ref_update(display_state, r ? '!' : ' ', quickref.buf,
+                                  r ? _("unable to update local ref") : NULL,
+                                  remote_ref->name, ref->name,
+                                  &ref->old_oid, &ref->new_oid, summary_width);
                strbuf_release(&quickref);
                return r;
        } else if (force || ref->force) {
@@ -988,14 +1008,16 @@ static int update_local_ref(struct ref *ref,
                strbuf_addstr(&quickref, "...");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
                r = s_update_ref("forced-update", ref, transaction, 1);
-               format_display(display, r ? '!' : '+', quickref.buf,
-                              r ? _("unable to update local ref") : _("forced update"),
-                              remote, pretty_ref, summary_width);
+               display_ref_update(display_state, r ? '!' : '+', quickref.buf,
+                                  r ? _("unable to update local ref") : _("forced update"),
+                                  remote_ref->name, ref->name,
+                                  &ref->old_oid, &ref->new_oid, summary_width);
                strbuf_release(&quickref);
                return r;
        } else {
-               format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
-                              remote, pretty_ref, summary_width);
+               display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"),
+                                  remote_ref->name, ref->name,
+                                  &ref->old_oid, &ref->new_oid, summary_width);
                return 1;
        }
 }
@@ -1105,39 +1127,35 @@ N_("it took %.2f seconds to check forced updates; you can use\n"
    "'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
    "to avoid this check\n");
 
-static int store_updated_refs(const char *raw_url, const char *remote_name,
+static int store_updated_refs(struct display_state *display_state,
+                             const char *remote_name,
                              int connectivity_checked,
                              struct ref_transaction *transaction, struct ref *ref_map,
-                             struct fetch_head *fetch_head)
+                             struct fetch_head *fetch_head,
+                             const struct fetch_config *config)
 {
-       int url_len, i, rc = 0;
+       int rc = 0;
        struct strbuf note = STRBUF_INIT;
        const char *what, *kind;
        struct ref *rm;
-       char *url;
        int want_status;
        int summary_width = 0;
 
        if (verbosity >= 0)
                summary_width = transport_summary_width(ref_map);
 
-       if (raw_url)
-               url = transport_anonymize_url(raw_url);
-       else
-               url = xstrdup("foreign");
-
        if (!connectivity_checked) {
                struct check_connected_options opt = CHECK_CONNECTED_INIT;
 
+               opt.exclude_hidden_refs_section = "fetch";
                rm = ref_map;
                if (check_connected(iterate_ref_map, &rm, &opt)) {
-                       rc = error(_("%s did not send all necessary objects\n"), url);
+                       rc = error(_("%s did not send all necessary objects\n"),
+                                  display_state->url);
                        goto abort;
                }
        }
 
-       prepare_format_display(ref_map);
-
        /*
         * We do a pass for each fetch_head_status type in their enum order, so
         * merged entries are written before not-for-merge. That lets readers
@@ -1197,7 +1215,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                ref->force = rm->peer_ref->force;
                        }
 
-                       if (recurse_submodules != RECURSE_SUBMODULES_OFF &&
+                       if (config->recurse_submodules != RECURSE_SUBMODULES_OFF &&
                            (!rm->peer_ref || !oideq(&ref->old_oid, &ref->new_oid))) {
                                check_for_new_submodule_commits(&rm->old_oid);
                        }
@@ -1205,25 +1223,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                        if (!strcmp(rm->name, "HEAD")) {
                                kind = "";
                                what = "";
-                       }
-                       else if (skip_prefix(rm->name, "refs/heads/", &what))
+                       } else if (skip_prefix(rm->name, "refs/heads/", &what)) {
                                kind = "branch";
-                       else if (skip_prefix(rm->name, "refs/tags/", &what))
+                       } else if (skip_prefix(rm->name, "refs/tags/", &what)) {
                                kind = "tag";
-                       else if (skip_prefix(rm->name, "refs/remotes/", &what))
+                       } else if (skip_prefix(rm->name, "refs/remotes/", &what)) {
                                kind = "remote-tracking branch";
-                       else {
+                       else {
                                kind = "";
                                what = rm->name;
                        }
 
-                       url_len = strlen(url);
-                       for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
-                               ;
-                       url_len = i + 1;
-                       if (4 < i && !strncmp(".git", url + i - 3, 4))
-                               url_len = i - 3;
-
                        strbuf_reset(&note);
                        if (*what) {
                                if (*kind)
@@ -1233,12 +1243,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 
                        append_fetch_head(fetch_head, &rm->old_oid,
                                          rm->fetch_head_status,
-                                         note.buf, url, url_len);
+                                         note.buf, display_state->url,
+                                         display_state->url_len);
 
-                       strbuf_reset(&note);
                        if (ref) {
-                               rc |= update_local_ref(ref, transaction, what,
-                                                      rm, &note, summary_width);
+                               rc |= update_local_ref(ref, transaction, display_state,
+                                                      rm, summary_width, config);
                                free(ref);
                        } else if (write_fetch_head || dry_run) {
                                /*
@@ -1246,18 +1256,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                 * would be written to FETCH_HEAD, if --dry-run
                                 * is set).
                                 */
-                               format_display(&note, '*',
-                                              *kind ? kind : "branch", NULL,
-                                              *what ? what : "HEAD",
-                                              "FETCH_HEAD", summary_width);
-                       }
-                       if (note.len) {
-                               if (!shown_url) {
-                                       fprintf(stderr, _("From %.*s\n"),
-                                                       url_len, url);
-                                       shown_url = 1;
-                               }
-                               fprintf(stderr, " %s\n", note.buf);
+                               display_ref_update(display_state, '*',
+                                                  *kind ? kind : "branch", NULL,
+                                                  rm->name,
+                                                  "FETCH_HEAD",
+                                                  &rm->new_oid, &rm->old_oid,
+                                                  summary_width);
                        }
                }
        }
@@ -1268,7 +1272,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                      "branches"), remote_name);
 
        if (advice_enabled(ADVICE_FETCH_SHOW_FORCED_UPDATES)) {
-               if (!fetch_show_forced_updates) {
+               if (!config->show_forced_updates) {
                        warning(_(warn_show_forced_updates));
                } else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
                        warning(_(warn_time_show_forced_updates),
@@ -1278,7 +1282,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 
  abort:
        strbuf_release(&note);
-       free(url);
        return rc;
 }
 
@@ -1316,19 +1319,22 @@ static int check_exist_and_connected(struct ref *ref_map)
         * we need all direct targets to exist.
         */
        for (r = rm; r; r = r->next) {
-               if (!has_object_file_with_flags(&r->old_oid,
-                                               OBJECT_INFO_SKIP_FETCH_OBJECT))
+               if (!repo_has_object_file_with_flags(the_repository, &r->old_oid,
+                                                    OBJECT_INFO_SKIP_FETCH_OBJECT))
                        return -1;
        }
 
        opt.quiet = 1;
+       opt.exclude_hidden_refs_section = "fetch";
        return check_connected(iterate_ref_map, &rm, &opt);
 }
 
-static int fetch_and_consume_refs(struct transport *transport,
+static int fetch_and_consume_refs(struct display_state *display_state,
+                                 struct transport *transport,
                                  struct ref_transaction *transaction,
                                  struct ref *ref_map,
-                                 struct fetch_head *fetch_head)
+                                 struct fetch_head *fetch_head,
+                                 const struct fetch_config *config)
 {
        int connectivity_checked = 1;
        int ret;
@@ -1349,9 +1355,9 @@ static int fetch_and_consume_refs(struct transport *transport,
        }
 
        trace2_region_enter("fetch", "consume_refs", the_repository);
-       ret = store_updated_refs(transport->url, transport->remote->name,
+       ret = store_updated_refs(display_state, transport->remote->name,
                                 connectivity_checked, transaction, ref_map,
-                                fetch_head);
+                                fetch_head, config);
        trace2_region_leave("fetch", "consume_refs", the_repository);
 
 out:
@@ -1359,32 +1365,18 @@ out:
        return ret;
 }
 
-static int prune_refs(struct refspec *rs,
+static int prune_refs(struct display_state *display_state,
+                     struct refspec *rs,
                      struct ref_transaction *transaction,
-                     struct ref *ref_map,
-                     const char *raw_url)
+                     struct ref *ref_map)
 {
-       int url_len, i, result = 0;
+       int result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
        struct strbuf err = STRBUF_INIT;
-       char *url;
        const char *dangling_msg = dry_run
                ? _("   (%s will become dangling)")
                : _("   (%s has become dangling)");
 
-       if (raw_url)
-               url = transport_anonymize_url(raw_url);
-       else
-               url = xstrdup("foreign");
-
-       url_len = strlen(url);
-       for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
-               ;
-
-       url_len = i + 1;
-       if (4 < i && !strncmp(".git", url + i - 3, 4))
-               url_len = i - 3;
-
        if (!dry_run) {
                if (transaction) {
                        for (ref = stale_refs; ref; ref = ref->next) {
@@ -1408,23 +1400,16 @@ static int prune_refs(struct refspec *rs,
                int summary_width = transport_summary_width(stale_refs);
 
                for (ref = stale_refs; ref; ref = ref->next) {
-                       struct strbuf sb = STRBUF_INIT;
-                       if (!shown_url) {
-                               fprintf(stderr, _("From %.*s\n"), url_len, url);
-                               shown_url = 1;
-                       }
-                       format_display(&sb, '-', _("[deleted]"), NULL,
-                                      _("(none)"), prettify_refname(ref->name),
-                                      summary_width);
-                       fprintf(stderr, " %s\n",sb.buf);
-                       strbuf_release(&sb);
+                       display_ref_update(display_state, '-', _("[deleted]"), NULL,
+                                          _("(none)"), ref->name,
+                                          &ref->new_oid, &ref->old_oid,
+                                          summary_width);
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
 
 cleanup:
        strbuf_release(&err);
-       free(url);
        free_refs(stale_refs);
        return result;
 }
@@ -1484,7 +1469,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
                int old_nr;
                if (!has_glob_specials(s)) {
                        struct object_id oid;
-                       if (get_oid(s, &oid))
+                       if (repo_get_oid(the_repository, s, &oid))
                                die(_("%s is not a valid object"), s);
                        if (!has_object(the_repository, &oid, 0))
                                die(_("the object %s does not exist"), s);
@@ -1539,10 +1524,12 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
        return transport;
 }
 
-static int backfill_tags(struct transport *transport,
+static int backfill_tags(struct display_state *display_state,
+                        struct transport *transport,
                         struct ref_transaction *transaction,
                         struct ref *ref_map,
-                        struct fetch_head *fetch_head)
+                        struct fetch_head *fetch_head,
+                        const struct fetch_config *config)
 {
        int retcode, cannot_reuse;
 
@@ -1563,7 +1550,8 @@ static int backfill_tags(struct transport *transport,
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
        transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
-       retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head);
+       retcode = fetch_and_consume_refs(display_state, transport, transaction, ref_map,
+                                        fetch_head, config);
 
        if (gsecondary) {
                transport_disconnect(gsecondary);
@@ -1574,10 +1562,12 @@ static int backfill_tags(struct transport *transport,
 }
 
 static int do_fetch(struct transport *transport,
-                   struct refspec *rs)
+                   struct refspec *rs,
+                   const struct fetch_config *config)
 {
        struct ref_transaction *transaction = NULL;
        struct ref *ref_map = NULL;
+       struct display_state display_state = { 0 };
        int autotags = (transport->remote->fetch_tags == 1);
        int retcode = 0;
        const struct ref *remote_refs;
@@ -1659,10 +1649,13 @@ static int do_fetch(struct transport *transport,
        if (retcode)
                goto cleanup;
 
+       display_state_init(&display_state, ref_map, transport->url,
+                          config->display_format);
+
        if (atomic_fetch) {
                transaction = ref_transaction_begin(&err);
                if (!transaction) {
-                       retcode = error("%s", err.buf);
+                       retcode = -1;
                        goto cleanup;
                }
        }
@@ -1676,17 +1669,17 @@ static int do_fetch(struct transport *transport,
                 * don't care whether --tags was specified.
                 */
                if (rs->nr) {
-                       retcode = prune_refs(rs, transaction, ref_map, transport->url);
+                       retcode = prune_refs(&display_state, rs, transaction, ref_map);
                } else {
-                       retcode = prune_refs(&transport->remote->fetch,
-                                            transaction, ref_map,
-                                            transport->url);
+                       retcode = prune_refs(&display_state, &transport->remote->fetch,
+                                            transaction, ref_map);
                }
                if (retcode != 0)
                        retcode = 1;
        }
 
-       if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head)) {
+       if (fetch_and_consume_refs(&display_state, transport, transaction, ref_map,
+                                  &fetch_head, config)) {
                retcode = 1;
                goto cleanup;
        }
@@ -1708,8 +1701,8 @@ static int do_fetch(struct transport *transport,
                         * when `--atomic` is passed: in that case we'll abort
                         * the transaction and don't commit anything.
                         */
-                       if (backfill_tags(transport, transaction, tags_ref_map,
-                                         &fetch_head))
+                       if (backfill_tags(&display_state, transport, transaction, tags_ref_map,
+                                         &fetch_head, config))
                                retcode = 1;
                }
 
@@ -1722,7 +1715,6 @@ static int do_fetch(struct transport *transport,
 
                retcode = ref_transaction_commit(transaction, &err);
                if (retcode) {
-                       error("%s", err.buf);
                        ref_transaction_free(transaction);
                        transaction = NULL;
                        goto cleanup;
@@ -1786,11 +1778,17 @@ static int do_fetch(struct transport *transport,
        }
 
 cleanup:
-       if (retcode && transaction) {
-               ref_transaction_abort(transaction, &err);
-               error("%s", err.buf);
+       if (retcode) {
+               if (err.len) {
+                       error("%s", err.buf);
+                       strbuf_reset(&err);
+               }
+               if (transaction && ref_transaction_abort(transaction, &err) &&
+                   err.len)
+                       error("%s", err.buf);
        }
 
+       display_state_release(&display_state);
        close_fetch_head(&fetch_head);
        strbuf_release(&err);
        free_refs(ref_map);
@@ -1810,7 +1808,9 @@ struct remote_group_data {
        struct string_list *list;
 };
 
-static int get_remote_group(const char *key, const char *value, void *priv)
+static int get_remote_group(const char *key, const char *value,
+                           const struct config_context *ctx UNUSED,
+                           void *priv)
 {
        struct remote_group_data *g = priv;
 
@@ -1845,7 +1845,8 @@ static int add_remote_or_group(const char *name, struct string_list *list)
        return 1;
 }
 
-static void add_options_to_argv(struct strvec *argv)
+static void add_options_to_argv(struct strvec *argv,
+                               const struct fetch_config *config)
 {
        if (dry_run)
                strvec_push(argv, "--dry-run");
@@ -1859,9 +1860,11 @@ static void add_options_to_argv(struct strvec *argv)
                strvec_push(argv, "--force");
        if (keep)
                strvec_push(argv, "--keep");
-       if (recurse_submodules == RECURSE_SUBMODULES_ON)
+       if (config->recurse_submodules == RECURSE_SUBMODULES_ON)
                strvec_push(argv, "--recurse-submodules");
-       else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+       else if (config->recurse_submodules == RECURSE_SUBMODULES_OFF)
+               strvec_push(argv, "--no-recurse-submodules");
+       else if (config->recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
                strvec_push(argv, "--recurse-submodules=on-demand");
        if (tags == TAGS_SET)
                strvec_push(argv, "--tags");
@@ -1877,6 +1880,10 @@ static void add_options_to_argv(struct strvec *argv)
                strvec_push(argv, "--ipv4");
        else if (family == TRANSPORT_FAMILY_IPV6)
                strvec_push(argv, "--ipv6");
+       if (!write_fetch_head)
+               strvec_push(argv, "--no-write-fetch-head");
+       if (config->display_format == DISPLAY_FORMAT_PORCELAIN)
+               strvec_pushf(argv, "--porcelain");
 }
 
 /* Fetch multiple remotes in parallel */
@@ -1885,9 +1892,11 @@ struct parallel_fetch_state {
        const char **argv;
        struct string_list *remotes;
        int next, result;
+       const struct fetch_config *config;
 };
 
-static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
+static int fetch_next_remote(struct child_process *cp,
+                            struct strbuf *out UNUSED,
                             void *cb, void **task_cb)
 {
        struct parallel_fetch_state *state = cb;
@@ -1903,13 +1912,14 @@ static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
        strvec_push(&cp->args, remote);
        cp->git_cmd = 1;
 
-       if (verbosity >= 0)
+       if (verbosity >= 0 && state->config->display_format != DISPLAY_FORMAT_PORCELAIN)
                printf(_("Fetching %s\n"), remote);
 
        return 1;
 }
 
-static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb)
+static int fetch_failed_to_start(struct strbuf *out UNUSED,
+                                void *cb, void *task_cb)
 {
        struct parallel_fetch_state *state = cb;
        const char *remote = task_cb;
@@ -1934,7 +1944,8 @@ static int fetch_finished(int result, struct strbuf *out,
        return 0;
 }
 
-static int fetch_multiple(struct string_list *list, int max_children)
+static int fetch_multiple(struct string_list *list, int max_children,
+                         const struct fetch_config *config)
 {
        int i, result = 0;
        struct strvec argv = STRVEC_INIT;
@@ -1945,34 +1956,47 @@ static int fetch_multiple(struct string_list *list, int max_children)
                        return errcode;
        }
 
-       strvec_pushl(&argv, "fetch", "--append", "--no-auto-gc",
+       /*
+        * Cancel out the fetch.bundleURI config when running subprocesses,
+        * to avoid fetching from the same bundle list multiple times.
+        */
+       strvec_pushl(&argv, "-c", "fetch.bundleURI=",
+                    "fetch", "--append", "--no-auto-gc",
                     "--no-write-commit-graph", NULL);
-       add_options_to_argv(&argv);
+       add_options_to_argv(&argv, config);
 
        if (max_children != 1 && list->nr != 1) {
-               struct parallel_fetch_state state = { argv.v, list, 0, 0 };
+               struct parallel_fetch_state state = { argv.v, list, 0, 0, config };
+               const struct run_process_parallel_opts opts = {
+                       .tr2_category = "fetch",
+                       .tr2_label = "parallel/fetch",
+
+                       .processes = max_children,
+
+                       .get_next_task = &fetch_next_remote,
+                       .start_failure = &fetch_failed_to_start,
+                       .task_finished = &fetch_finished,
+                       .data = &state,
+               };
 
                strvec_push(&argv, "--end-of-options");
-               result = run_processes_parallel_tr2(max_children,
-                                                   &fetch_next_remote,
-                                                   &fetch_failed_to_start,
-                                                   &fetch_finished,
-                                                   &state,
-                                                   "fetch", "parallel/fetch");
-
-               if (!result)
-                       result = state.result;
+
+               run_processes_parallel(&opts);
+               result = state.result;
        } else
                for (i = 0; i < list->nr; i++) {
                        const char *name = list->items[i].string;
-                       strvec_push(&argv, name);
-                       if (verbosity >= 0)
+                       struct child_process cmd = CHILD_PROCESS_INIT;
+
+                       strvec_pushv(&cmd.args, argv.v);
+                       strvec_push(&cmd.args, name);
+                       if (verbosity >= 0 && config->display_format != DISPLAY_FORMAT_PORCELAIN)
                                printf(_("Fetching %s\n"), name);
-                       if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
+                       cmd.git_cmd = 1;
+                       if (run_command(&cmd)) {
                                error(_("could not fetch %s"), name);
                                result = 1;
                        }
-                       strvec_pop(&argv);
                }
 
        strvec_clear(&argv);
@@ -1996,7 +2020,7 @@ static inline void fetch_one_setup_partial(struct remote *remote)
         * If no prior partial clone/fetch and the current fetch DID NOT
         * request a partial-fetch, do a normal fetch.
         */
-       if (!has_promisor_remote() && !filter_options.choice)
+       if (!repo_has_promisor_remote(the_repository) && !filter_options.choice)
                return;
 
        /*
@@ -2021,7 +2045,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
 }
 
 static int fetch_one(struct remote *remote, int argc, const char **argv,
-                    int prune_tags_ok, int use_stdin_refspecs)
+                    int prune_tags_ok, int use_stdin_refspecs,
+                    const struct fetch_config *config)
 {
        struct refspec rs = REFSPEC_INIT_FETCH;
        int i;
@@ -2039,8 +2064,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
                /* no command line request */
                if (0 <= remote->prune)
                        prune = remote->prune;
-               else if (0 <= fetch_prune_config)
-                       prune = fetch_prune_config;
+               else if (0 <= config->prune)
+                       prune = config->prune;
                else
                        prune = PRUNE_BY_DEFAULT;
        }
@@ -2049,8 +2074,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
                /* no command line request */
                if (0 <= remote->prune_tags)
                        prune_tags = remote->prune_tags;
-               else if (0 <= fetch_prune_tags_config)
-                       prune_tags = fetch_prune_tags_config;
+               else if (0 <= config->prune_tags)
+                       prune_tags = config->prune_tags;
                else
                        prune_tags = PRUNE_TAGS_BY_DEFAULT;
        }
@@ -2088,7 +2113,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack_atexit);
        sigchain_push(SIGPIPE, SIG_IGN);
-       exit_code = do_fetch(gtransport, &rs);
+       exit_code = do_fetch(gtransport, &rs, config);
        sigchain_pop(SIGPIPE);
        refspec_clear(&rs);
        transport_disconnect(gtransport);
@@ -2098,11 +2123,116 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
 
 int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
-       int i;
+       struct fetch_config config = {
+               .display_format = DISPLAY_FORMAT_FULL,
+               .prune = -1,
+               .prune_tags = -1,
+               .show_forced_updates = 1,
+               .recurse_submodules = RECURSE_SUBMODULES_DEFAULT,
+               .parallel = 1,
+               .submodule_fetch_jobs = -1,
+       };
+       const char *submodule_prefix = "";
+       const char *bundle_uri;
        struct string_list list = STRING_LIST_INIT_DUP;
        struct remote *remote = NULL;
+       int all = -1, multiple = 0;
        int result = 0;
        int prune_tags_ok = 1;
+       int enable_auto_gc = 1;
+       int unshallow = 0;
+       int max_jobs = -1;
+       int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
+       int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
+       int fetch_write_commit_graph = -1;
+       int stdin_refspecs = 0;
+       int negotiate_only = 0;
+       int porcelain = 0;
+       int i;
+
+       struct option builtin_fetch_options[] = {
+               OPT__VERBOSITY(&verbosity),
+               OPT_BOOL(0, "all", &all,
+                        N_("fetch from all remotes")),
+               OPT_BOOL(0, "set-upstream", &set_upstream,
+                        N_("set upstream for git pull/fetch")),
+               OPT_BOOL('a', "append", &append,
+                        N_("append to .git/FETCH_HEAD instead of overwriting")),
+               OPT_BOOL(0, "atomic", &atomic_fetch,
+                        N_("use atomic transaction to update references")),
+               OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
+                          N_("path to upload pack on remote end")),
+               OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
+               OPT_BOOL('m', "multiple", &multiple,
+                        N_("fetch from multiple remotes")),
+               OPT_SET_INT('t', "tags", &tags,
+                           N_("fetch all tags and associated objects"), TAGS_SET),
+               OPT_SET_INT('n', NULL, &tags,
+                           N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
+               OPT_INTEGER('j', "jobs", &max_jobs,
+                           N_("number of submodules fetched in parallel")),
+               OPT_BOOL(0, "prefetch", &prefetch,
+                        N_("modify the refspec to place all refs within refs/prefetch/")),
+               OPT_BOOL('p', "prune", &prune,
+                        N_("prune remote-tracking branches no longer on remote")),
+               OPT_BOOL('P', "prune-tags", &prune_tags,
+                        N_("prune local tags no longer on remote and clobber changed tags")),
+               OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
+                           N_("control recursive fetching of submodules"),
+                           PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
+               OPT_BOOL(0, "dry-run", &dry_run,
+                        N_("dry run")),
+               OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+               OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
+                        N_("write fetched references to the FETCH_HEAD file")),
+               OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
+               OPT_BOOL('u', "update-head-ok", &update_head_ok,
+                           N_("allow updating of HEAD ref")),
+               OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+               OPT_STRING(0, "depth", &depth, N_("depth"),
+                          N_("deepen history of shallow clone")),
+               OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
+                          N_("deepen history of shallow repository based on time")),
+               OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
+                               N_("deepen history of shallow clone, excluding rev")),
+               OPT_INTEGER(0, "deepen", &deepen_relative,
+                           N_("deepen history of shallow clone")),
+               OPT_SET_INT_F(0, "unshallow", &unshallow,
+                             N_("convert to a complete repository"),
+                             1, PARSE_OPT_NONEG),
+               OPT_SET_INT_F(0, "refetch", &refetch,
+                             N_("re-fetch without negotiating common commits"),
+                             1, PARSE_OPT_NONEG),
+               { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
+                          N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
+               OPT_CALLBACK_F(0, "recurse-submodules-default",
+                          &recurse_submodules_default, N_("on-demand"),
+                          N_("default for recursive fetching of submodules "
+                             "(lower priority than config files)"),
+                          PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
+               OPT_BOOL(0, "update-shallow", &update_shallow,
+                        N_("accept refs that update .git/shallow")),
+               OPT_CALLBACK_F(0, "refmap", &refmap, N_("refmap"),
+                              N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
+               OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
+               OPT_IPVERSION(&family),
+               OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
+                               N_("report that we have only objects reachable from this object")),
+               OPT_BOOL(0, "negotiate-only", &negotiate_only,
+                        N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
+               OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+               OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
+                        N_("run 'maintenance --auto' after fetching")),
+               OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+                        N_("run 'maintenance --auto' after fetching")),
+               OPT_BOOL(0, "show-forced-updates", &config.show_forced_updates,
+                        N_("check for forced-updates on all updated branches")),
+               OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
+                        N_("write the commit-graph after fetching")),
+               OPT_BOOL(0, "stdin", &stdin_refspecs,
+                        N_("accept refspecs from stdin")),
+               OPT_END()
+       };
 
        packet_trace_identity("fetch");
 
@@ -2116,7 +2246,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                free(anon);
        }
 
-       git_config(git_fetch_config, NULL);
+       git_config(git_fetch_config, &config);
        if (the_repository->gitdir) {
                prepare_repo_settings(the_repository);
                the_repository->settings.command_requires_full_index = 0;
@@ -2126,7 +2256,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                             builtin_fetch_options, builtin_fetch_usage, 0);
 
        if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
-               recurse_submodules = recurse_submodules_cli;
+               config.recurse_submodules = recurse_submodules_cli;
 
        if (negotiate_only) {
                switch (recurse_submodules_cli) {
@@ -2137,7 +2267,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                         * submodules. Skip it by setting recurse_submodules to
                         * RECURSE_SUBMODULES_OFF.
                         */
-                       recurse_submodules = RECURSE_SUBMODULES_OFF;
+                       config.recurse_submodules = RECURSE_SUBMODULES_OFF;
                        break;
 
                default:
@@ -2146,15 +2276,35 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
-               int *sfjc = submodule_fetch_jobs_config == -1
-                           ? &submodule_fetch_jobs_config : NULL;
-               int *rs = recurse_submodules == RECURSE_SUBMODULES_DEFAULT
-                         ? &recurse_submodules : NULL;
+       if (config.recurse_submodules != RECURSE_SUBMODULES_OFF) {
+               int *sfjc = config.submodule_fetch_jobs == -1
+                           ? &config.submodule_fetch_jobs : NULL;
+               int *rs = config.recurse_submodules == RECURSE_SUBMODULES_DEFAULT
+                         ? &config.recurse_submodules : NULL;
 
                fetch_config_from_gitmodules(sfjc, rs);
        }
 
+
+       if (porcelain) {
+               switch (recurse_submodules_cli) {
+               case RECURSE_SUBMODULES_OFF:
+               case RECURSE_SUBMODULES_DEFAULT:
+                       /*
+                        * Reference updates in submodules would be ambiguous
+                        * in porcelain mode, so we reject this combination.
+                        */
+                       config.recurse_submodules = RECURSE_SUBMODULES_OFF;
+                       break;
+
+               default:
+                       die(_("options '%s' and '%s' cannot be used together"),
+                           "--porcelain", "--recurse-submodules");
+               }
+
+               config.display_format = DISPLAY_FORMAT_PORCELAIN;
+       }
+
        if (negotiate_only && !negotiation_tip.nr)
                die(_("--negotiate-only needs one or more --negotiation-tip=*"));
 
@@ -2184,11 +2334,27 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (dry_run)
                write_fetch_head = 0;
 
+       if (!max_jobs)
+               max_jobs = online_cpus();
+
+       if (!git_config_get_string_tmp("fetch.bundleuri", &bundle_uri) &&
+           fetch_bundle_uri(the_repository, bundle_uri, NULL))
+               warning(_("failed to fetch bundles from '%s'"), bundle_uri);
+
+       if (all < 0) {
+               /*
+                * no --[no-]all given;
+                * only use config option if no remote was explicitly specified
+                */
+               all = (!argc) ? config.all : 0;
+       }
+
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
                else if (argc > 1)
                        die(_("fetch --all does not make sense with refspecs"));
+
                (void) for_each_remote(get_one_remote_for_fetch, &list);
 
                /* do not do fetch_multiple() of one */
@@ -2218,6 +2384,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                        argv++;
                }
        }
+       string_list_remove_duplicates(&list, 0);
 
        if (negotiate_only) {
                struct oidset acked_commits = OIDSET_INIT;
@@ -2243,9 +2410,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                        printf("%s\n", oid_to_hex(oid));
                oidset_clear(&acked_commits);
        } else if (remote) {
-               if (filter_options.choice || has_promisor_remote())
+               if (filter_options.choice || repo_has_promisor_remote(the_repository))
                        fetch_one_setup_partial(remote);
-               result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
+               result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs,
+                                  &config);
        } else {
                int max_children = max_jobs;
 
@@ -2262,13 +2430,12 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                              "from one remote"));
 
                if (max_children < 0)
-                       max_children = fetch_parallel_config;
+                       max_children = config.parallel;
 
                /* TODO should this also die if we have a previous partial-clone? */
-               result = fetch_multiple(&list, max_children);
+               result = fetch_multiple(&list, max_children, &config);
        }
 
-
        /*
         * This is only needed after fetch_one(), which does not fetch
         * submodules by itself.
@@ -2278,20 +2445,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
         * the fetched history from each remote, so there is no need
         * to fetch submodules from here.
         */
-       if (!result && remote && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
+       if (!result && remote && (config.recurse_submodules != RECURSE_SUBMODULES_OFF)) {
                struct strvec options = STRVEC_INIT;
                int max_children = max_jobs;
 
                if (max_children < 0)
-                       max_children = submodule_fetch_jobs_config;
+                       max_children = config.submodule_fetch_jobs;
                if (max_children < 0)
-                       max_children = fetch_parallel_config;
+                       max_children = config.parallel;
 
-               add_options_to_argv(&options);
+               add_options_to_argv(&options, &config);
                result = fetch_submodules(the_repository,
                                          &options,
                                          submodule_prefix,
-                                         recurse_submodules,
+                                         config.recurse_submodules,
                                          recurse_submodules_default,
                                          verbosity < 0,
                                          max_children);
index 8d8fd393f8925c93155091db4f0959b7f03c31bb..0f9855b680eb7bf4e534af40847e32af835cfa13 100644 (file)
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "config.h"
 #include "fmt-merge-msg.h"
+#include "gettext.h"
 #include "parse-options.h"
 
 static const char * const fmt_merge_msg_usage[] = {
index 6f62f40d1263f44f8977125d9d54eca76f101685..3885a9c28e149e5e4133bbf25aa557c38bd4193b 100644 (file)
@@ -1,10 +1,12 @@
 #include "builtin.h"
-#include "cache.h"
+#include "commit.h"
 #include "config.h"
-#include "refs.h"
+#include "gettext.h"
 #include "object.h"
 #include "parse-options.h"
 #include "ref-filter.h"
+#include "strbuf.h"
+#include "strvec.h"
 
 static char const * const for_each_ref_usage[] = {
        N_("git for-each-ref [<options>] [<pattern>]"),
@@ -16,15 +18,13 @@ static char const * const for_each_ref_usage[] = {
 
 int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 {
-       int i;
        struct ref_sorting *sorting;
        struct string_list sorting_options = STRING_LIST_INIT_DUP;
-       int maxcount = 0, icase = 0;
-       struct ref_array array;
-       struct ref_filter filter;
+       int icase = 0;
+       struct ref_filter filter = REF_FILTER_INIT;
        struct ref_format format = REF_FORMAT_INIT;
-       struct strbuf output = STRBUF_INIT;
-       struct strbuf err = STRBUF_INIT;
+       int from_stdin = 0;
+       struct strvec vec = STRVEC_INIT;
 
        struct option opts[] = {
                OPT_BIT('s', "shell", &format.quote_style,
@@ -35,11 +35,14 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                        N_("quote placeholders suitably for python"), QUOTE_PYTHON),
                OPT_BIT(0 , "tcl",  &format.quote_style,
                        N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
+               OPT_BOOL(0, "omit-empty",  &format.array_opts.omit_empty,
+                       N_("do not output a newline after empty formatted refs")),
 
                OPT_GROUP(""),
-               OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
+               OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")),
                OPT_STRING(  0 , "format", &format.format, N_("format"), N_("format to use for the output")),
                OPT__COLOR(&format.use_color, N_("respect format colors")),
+               OPT_REF_FILTER_EXCLUDE(&filter),
                OPT_REF_SORT(&sorting_options),
                OPT_CALLBACK(0, "points-at", &filter.points_at,
                             N_("object"), N_("print only refs which points at the given object"),
@@ -49,19 +52,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
                OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
                OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+               OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
                OPT_END(),
        };
 
-       memset(&array, 0, sizeof(array));
-       memset(&filter, 0, sizeof(filter));
-
        format.format = "%(objectname) %(objecttype)\t%(refname)";
 
        git_config(git_default_config, NULL);
 
+       /* Set default (refname) sorting */
+       string_list_append(&sorting_options, "refname");
+
        parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
-       if (maxcount < 0) {
-               error("invalid --count argument: `%d'", maxcount);
+       if (format.array_opts.max_count < 0) {
+               error("invalid --count argument: `%d'", format.array_opts.max_count);
                usage_with_options(for_each_ref_usage, opts);
        }
        if (HAS_MULTI_BITS(format.quote_style)) {
@@ -75,27 +79,28 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
        filter.ignore_case = icase;
 
-       filter.name_patterns = argv;
-       filter.match_as_path = 1;
-       filter_refs(&array, &filter, FILTER_REFS_ALL);
-       ref_array_sort(sorting, &array);
-
-       if (!maxcount || array.nr < maxcount)
-               maxcount = array.nr;
-       for (i = 0; i < maxcount; i++) {
-               strbuf_reset(&err);
-               strbuf_reset(&output);
-               if (format_ref_array_item(array.items[i], &format, &output, &err))
-                       die("%s", err.buf);
-               fwrite(output.buf, 1, output.len, stdout);
-               putchar('\n');
+       if (from_stdin) {
+               struct strbuf line = STRBUF_INIT;
+
+               if (argv[0])
+                       die(_("unknown arguments supplied with --stdin"));
+
+               while (strbuf_getline(&line, stdin) != EOF)
+                       strvec_push(&vec, line.buf);
+
+               strbuf_release(&line);
+
+               /* vec.v is NULL-terminated, just like 'argv'. */
+               filter.name_patterns = vec.v;
+       } else {
+               filter.name_patterns = argv;
        }
 
-       strbuf_release(&err);
-       strbuf_release(&output);
-       ref_array_clear(&array);
-       free_commit_list(filter.with_commit);
-       free_commit_list(filter.no_commit);
+       filter.match_as_path = 1;
+       filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format);
+
+       ref_filter_clear(&filter);
        ref_sorting_release(sorting);
+       strvec_clear(&vec);
        return 0;
 }
index fd86e5a8619256fd903122b08e937141eb1725f0..28186b30f54818cdbb42a4876358b7ceb20d20e2 100644 (file)
@@ -1,12 +1,14 @@
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "path.h"
+#include "repository.h"
 #include "run-command.h"
 #include "string-list.h"
 
 static const char * const for_each_repo_usage[] = {
-       N_("git for-each-repo --config=<config> <command-args>"),
+       N_("git for-each-repo --config=<config> [--] <arguments>"),
        NULL
 };
 
@@ -14,13 +16,16 @@ static int run_command_on_repo(const char *path, int argc, const char ** argv)
 {
        int i;
        struct child_process child = CHILD_PROCESS_INIT;
+       char *abspath = interpolate_path(path, 0);
 
        child.git_cmd = 1;
-       strvec_pushl(&child.args, "-C", path, NULL);
+       strvec_pushl(&child.args, "-C", abspath, NULL);
 
        for (i = 0; i < argc; i++)
                strvec_push(&child.args, argv[i]);
 
+       free(abspath);
+
        return run_command(&child);
 }
 
@@ -29,6 +34,7 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
        static const char *config_key = NULL;
        int i, result = 0;
        const struct string_list *values;
+       int err;
 
        const struct option options[] = {
                OPT_STRING(0, "config", &config_key, N_("config"),
@@ -42,14 +48,11 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
        if (!config_key)
                die(_("missing --config=<config>"));
 
-       values = repo_config_get_value_multi(the_repository,
-                                            config_key);
-
-       /*
-        * Do nothing on an empty list, which is equivalent to the case
-        * where the config variable does not exist at all.
-        */
-       if (!values)
+       err = repo_config_get_string_multi(the_repository, config_key, &values);
+       if (err < 0)
+               usage_msg_optf(_("got bad config --config=%s"),
+                              for_each_repo_usage, options, config_key);
+       else if (err)
                return 0;
 
        for (i = 0; !result && i < values->nr; i++)
index 41acbc229e468f68092a5b6e3f2df95ece62fd7c..a7cf94f67edf3aaace9fdbbca29778d6857d28af 100644 (file)
@@ -1,6 +1,6 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "commit.h"
 #include "refs.h"
 #include "pack.h"
 #include "cache-tree.h"
-#include "tree-walk.h"
 #include "fsck.h"
 #include "parse-options.h"
-#include "dir.h"
 #include "progress.h"
 #include "streaming.h"
-#include "decorate.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "replace-object.h"
 #include "resolve-undo.h"
 #include "run-command.h"
+#include "sparse-index.h"
 #include "worktree.h"
+#include "pack-revindex.h"
+#include "pack-bitmap.h"
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
@@ -51,6 +56,8 @@ static int name_objects;
 #define ERROR_REFS 010
 #define ERROR_COMMIT_GRAPH 020
 #define ERROR_MULTI_PACK_INDEX 040
+#define ERROR_PACK_REV_INDEX 0100
+#define ERROR_BITMAP 0200
 
 static const char *describe_object(const struct object_id *oid)
 {
@@ -82,11 +89,11 @@ static int objerror(struct object *obj, const char *err)
        return -1;
 }
 
-static int fsck_error_func(struct fsck_options *o,
+static int fsck_error_func(struct fsck_options *o UNUSED,
                           const struct object_id *oid,
                           enum object_type object_type,
                           enum fsck_msg_type msg_type,
-                          enum fsck_msg_id msg_id,
+                          enum fsck_msg_id msg_id UNUSED,
                           const char *message)
 {
        switch (msg_type) {
@@ -111,7 +118,7 @@ static int fsck_error_func(struct fsck_options *o,
 static struct object_array pending;
 
 static int mark_object(struct object *obj, enum object_type type,
-                      void *data, struct fsck_options *options)
+                      void *data, struct fsck_options *options UNUSED)
 {
        struct object *parent = data;
 
@@ -196,8 +203,8 @@ static int traverse_reachable(void)
        return !!result;
 }
 
-static int mark_used(struct object *obj, enum object_type object_type,
-                    void *data, struct fsck_options *options)
+static int mark_used(struct object *obj, enum object_type type UNUSED,
+                    void *data UNUSED, struct fsck_options *options UNUSED)
 {
        if (!obj)
                return 1;
@@ -233,17 +240,17 @@ static void mark_unreachable_referents(const struct object_id *oid)
 }
 
 static int mark_loose_unreachable_referents(const struct object_id *oid,
-                                           const char *path,
-                                           void *data)
+                                           const char *path UNUSED,
+                                           void *data UNUSED)
 {
        mark_unreachable_referents(oid);
        return 0;
 }
 
 static int mark_packed_unreachable_referents(const struct object_id *oid,
-                                            struct packed_git *pack,
-                                            uint32_t pos,
-                                            void *data)
+                                            struct packed_git *pack UNUSED,
+                                            uint32_t pos UNUSED,
+                                            void *data UNUSED)
 {
        mark_unreachable_referents(oid);
        return 0;
@@ -661,14 +668,15 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
        return 0; /* keep checking other objects, even if we saw an error */
 }
 
-static int fsck_cruft(const char *basename, const char *path, void *data)
+static int fsck_cruft(const char *basename, const char *path,
+                     void *data UNUSED)
 {
        if (!starts_with(basename, "tmp_obj_"))
                fprintf_ln(stderr, _("bad sha1 file: %s"), path);
        return 0;
 }
 
-static int fsck_subdir(unsigned int nr, const char *path, void *data)
+static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data)
 {
        struct for_each_loose_cb *cb_data = data;
        struct progress *progress = cb_data->progress;
@@ -732,19 +740,19 @@ static int fsck_head_link(const char *head_ref_name,
        return 0;
 }
 
-static int fsck_cache_tree(struct cache_tree *it)
+static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
 {
        int i;
        int err = 0;
 
        if (verbose)
-               fprintf_ln(stderr, _("Checking cache tree"));
+               fprintf_ln(stderr, _("Checking cache tree of %s"), index_path);
 
        if (0 <= it->entry_count) {
                struct object *obj = parse_object(the_repository, &it->oid);
                if (!obj) {
-                       error(_("%s: invalid sha1 pointer in cache-tree"),
-                             oid_to_hex(&it->oid));
+                       error(_("%s: invalid sha1 pointer in cache-tree of %s"),
+                             oid_to_hex(&it->oid), index_path);
                        errors_found |= ERROR_REFS;
                        return 1;
                }
@@ -755,11 +763,12 @@ static int fsck_cache_tree(struct cache_tree *it)
                        err |= objerror(obj, _("non-tree in cache-tree"));
        }
        for (i = 0; i < it->subtree_nr; i++)
-               err |= fsck_cache_tree(it->down[i]->cache_tree);
+               err |= fsck_cache_tree(it->down[i]->cache_tree, index_path);
        return err;
 }
 
-static int fsck_resolve_undo(struct index_state *istate)
+static int fsck_resolve_undo(struct index_state *istate,
+                            const char *index_path)
 {
        struct string_list_item *item;
        struct string_list *resolve_undo = istate->resolve_undo;
@@ -782,8 +791,9 @@ static int fsck_resolve_undo(struct index_state *istate)
 
                        obj = parse_object(the_repository, &ru->oid[i]);
                        if (!obj) {
-                               error(_("%s: invalid sha1 pointer in resolve-undo"),
-                                     oid_to_hex(&ru->oid[i]));
+                               error(_("%s: invalid sha1 pointer in resolve-undo of %s"),
+                                     oid_to_hex(&ru->oid[i]),
+                                     index_path);
                                errors_found |= ERROR_REFS;
                                continue;
                        }
@@ -796,6 +806,38 @@ static int fsck_resolve_undo(struct index_state *istate)
        return 0;
 }
 
+static void fsck_index(struct index_state *istate, const char *index_path,
+                      int is_current_worktree)
+{
+       unsigned int i;
+
+       /* TODO: audit for interaction with sparse-index. */
+       ensure_full_index(istate);
+       for (i = 0; i < istate->cache_nr; i++) {
+               unsigned int mode;
+               struct blob *blob;
+               struct object *obj;
+
+               mode = istate->cache[i]->ce_mode;
+               if (S_ISGITLINK(mode))
+                       continue;
+               blob = lookup_blob(the_repository,
+                                  &istate->cache[i]->oid);
+               if (!blob)
+                       continue;
+               obj = &blob->object;
+               obj->flags |= USED;
+               fsck_put_object_name(&fsck_walk_options, &obj->oid,
+                                    "%s:%s",
+                                    is_current_worktree ? "" : index_path,
+                                    istate->cache[i]->name);
+               mark_object_reachable(obj);
+       }
+       if (istate->cache_tree)
+               fsck_cache_tree(istate->cache_tree, index_path);
+       fsck_resolve_undo(istate, index_path);
+}
+
 static void mark_object_for_connectivity(const struct object_id *oid)
 {
        struct object *obj = lookup_unknown_object(the_repository, oid);
@@ -803,24 +845,59 @@ static void mark_object_for_connectivity(const struct object_id *oid)
 }
 
 static int mark_loose_for_connectivity(const struct object_id *oid,
-                                      const char *path,
-                                      void *data)
+                                      const char *path UNUSED,
+                                      void *data UNUSED)
 {
        mark_object_for_connectivity(oid);
        return 0;
 }
 
 static int mark_packed_for_connectivity(const struct object_id *oid,
-                                       struct packed_git *pack,
-                                       uint32_t pos,
-                                       void *data)
+                                       struct packed_git *pack UNUSED,
+                                       uint32_t pos UNUSED,
+                                       void *data UNUSED)
 {
        mark_object_for_connectivity(oid);
        return 0;
 }
 
+static int check_pack_rev_indexes(struct repository *r, int show_progress)
+{
+       struct progress *progress = NULL;
+       uint32_t pack_count = 0;
+       int res = 0;
+
+       if (show_progress) {
+               for (struct packed_git *p = get_all_packs(r); p; p = p->next)
+                       pack_count++;
+               progress = start_delayed_progress("Verifying reverse pack-indexes", pack_count);
+               pack_count = 0;
+       }
+
+       for (struct packed_git *p = get_all_packs(r); p; p = p->next) {
+               int load_error = load_pack_revindex_from_disk(p);
+
+               if (load_error < 0) {
+                       error(_("unable to load rev-index for pack '%s'"), p->pack_name);
+                       res = ERROR_PACK_REV_INDEX;
+               } else if (!load_error &&
+                          !load_pack_revindex(r, p) &&
+                          verify_pack_revindex(p)) {
+                       error(_("invalid rev-index for pack '%s'"), p->pack_name);
+                       res = ERROR_PACK_REV_INDEX;
+               }
+               display_progress(progress, ++pack_count);
+       }
+       stop_progress(&progress);
+
+       return res;
+}
+
 static char const * const fsck_usage[] = {
-       N_("git fsck [<options>] [<object>...]"),
+       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>...]"),
        NULL
 };
 
@@ -851,7 +928,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        fetch_if_missing = 0;
 
        errors_found = 0;
-       read_replace_refs = 0;
+       disable_replace_refs();
        save_commit_buffer = 0;
 
        argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
@@ -920,7 +997,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                struct object_id oid;
-               if (!get_oid(arg, &oid)) {
+               if (!repo_get_oid(the_repository, arg, &oid)) {
                        struct object *obj = lookup_object(the_repository,
                                                           &oid);
 
@@ -953,34 +1030,36 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        }
 
        if (keep_cache_objects) {
+               struct worktree **worktrees, **p;
+
                verify_index_checksum = 1;
                verify_ce_order = 1;
-               read_cache();
-               /* TODO: audit for interaction with sparse-index. */
-               ensure_full_index(&the_index);
-               for (i = 0; i < active_nr; i++) {
-                       unsigned int mode;
-                       struct blob *blob;
-                       struct object *obj;
 
-                       mode = active_cache[i]->ce_mode;
-                       if (S_ISGITLINK(mode))
-                               continue;
-                       blob = lookup_blob(the_repository,
-                                          &active_cache[i]->oid);
-                       if (!blob)
-                               continue;
-                       obj = &blob->object;
-                       obj->flags |= USED;
-                       fsck_put_object_name(&fsck_walk_options, &obj->oid,
-                                            ":%s", active_cache[i]->name);
-                       mark_object_reachable(obj);
+               worktrees = get_worktrees();
+               for (p = worktrees; *p; p++) {
+                       struct worktree *wt = *p;
+                       struct index_state istate =
+                               INDEX_STATE_INIT(the_repository);
+                       char *path;
+
+                       /*
+                        * Make a copy since the buffer is reusable
+                        * and may get overwritten by other calls
+                        * while we're examining the index.
+                        */
+                       path = xstrdup(worktree_git_path(wt, "index"));
+                       read_index_from(&istate, path, get_worktree_git_dir(wt));
+                       fsck_index(&istate, path, wt->is_current);
+                       discard_index(&istate);
+                       free(path);
                }
-               if (active_cache_tree)
-                       fsck_cache_tree(active_cache_tree);
-               fsck_resolve_undo(&the_index);
+               free_worktrees(worktrees);
        }
 
+       errors_found |= check_pack_rev_indexes(the_repository, show_progress);
+       if (verify_bitmap_files(the_repository))
+               errors_found |= ERROR_BITMAP;
+
        check_connectivity();
 
        if (the_repository->settings.core_commit_graph) {
@@ -992,6 +1071,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        commit_graph_verify.git_cmd = 1;
                        strvec_pushl(&commit_graph_verify.args, "commit-graph",
                                     "verify", "--object-dir", odb->path, NULL);
+                       if (show_progress)
+                               strvec_push(&commit_graph_verify.args, "--progress");
+                       else
+                               strvec_push(&commit_graph_verify.args, "--no-progress");
                        if (run_command(&commit_graph_verify))
                                errors_found |= ERROR_COMMIT_GRAPH;
                }
@@ -1006,6 +1089,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        midx_verify.git_cmd = 1;
                        strvec_pushl(&midx_verify.args, "multi-pack-index",
                                     "verify", "--object-dir", odb->path, NULL);
+                       if (show_progress)
+                               strvec_push(&midx_verify.args, "--progress");
+                       else
+                               strvec_push(&midx_verify.args, "--no-progress");
                        if (run_command(&midx_verify))
                                errors_found |= ERROR_MULTI_PACK_INDEX;
                }
index c69da93ecebe54b8af93a51452c9796eda15b377..1593713f4cb29fe8e608f4c3c7502fa5a624d1f9 100644 (file)
@@ -1,14 +1,22 @@
 #include "builtin.h"
+#include "abspath.h"
 #include "config.h"
+#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
 #include "parse-options.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsmonitor-ipc.h"
+#include "fsmonitor-settings.h"
 #include "compat/fsmonitor/fsm-health.h"
 #include "compat/fsmonitor/fsm-listen.h"
 #include "fsmonitor--daemon.h"
+#include "repository.h"
 #include "simple-ipc.h"
 #include "khash.h"
-#include "pkt-line.h"
+#include "run-command.h"
+#include "trace.h"
+#include "trace2.h"
 
 static const char * const builtin_fsmonitor__daemon_usage[] = {
        N_("git fsmonitor--daemon start [<options>]"),
@@ -31,10 +39,11 @@ static int fsmonitor__start_timeout_sec = 60;
 #define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
 static int fsmonitor__announce_startup = 0;
 
-static int fsmonitor_config(const char *var, const char *value, void *cb)
+static int fsmonitor_config(const char *var, const char *value,
+                           const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
-               int i = git_config_int(var, value);
+               int i = git_config_int(var, value, ctx->kvi);
                if (i < 1)
                        return error(_("value of '%s' out of range: %d"),
                                     FSMONITOR__IPC_THREADS, i);
@@ -43,7 +52,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, FSMONITOR__START_TIMEOUT)) {
-               int i = git_config_int(var, value);
+               int i = git_config_int(var, value, ctx->kvi);
                if (i < 0)
                        return error(_("value of '%s' out of range: %d"),
                                     FSMONITOR__START_TIMEOUT, i);
@@ -53,7 +62,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
                int is_bool;
-               int i = git_config_bool_or_int(var, value, &is_bool);
+               int i = git_config_bool_or_int(var, value, ctx->kvi, &is_bool);
                if (i < 0)
                        return error(_("value of '%s' not bool or int: %d"),
                                     var, i);
@@ -61,7 +70,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 /*
@@ -121,8 +130,9 @@ struct fsmonitor_cookie_item {
        enum fsmonitor_cookie_item_result result;
 };
 
-static int cookies_cmp(const void *data, const struct hashmap_entry *he1,
-                    const struct hashmap_entry *he2, const void *keydata)
+static int cookies_cmp(const void *data UNUSED,
+                      const struct hashmap_entry *he1,
+                      const struct hashmap_entry *he2, const void *keydata)
 {
        const struct fsmonitor_cookie_item *a =
                container_of(he1, const struct fsmonitor_cookie_item, entry);
@@ -709,6 +719,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
                                  "fsmonitor: unsupported V1 protocol '%s'"),
                                 command);
                do_trivial = 1;
+               do_cookie = 1;
 
        } else {
                /* We have "builtin:*" */
@@ -718,6 +729,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
                                         "fsmonitor: invalid V2 protocol token '%s'",
                                         command);
                        do_trivial = 1;
+                       do_cookie = 1;
 
                } else {
                        /*
@@ -1208,7 +1220,7 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
         * events.
         */
        if (pthread_create(&state->listener_thread, NULL,
-                          fsm_listen__thread_proc, state) < 0) {
+                          fsm_listen__thread_proc, state)) {
                ipc_server_stop_async(state->ipc_server_data);
                err = error(_("could not start fsmonitor listener thread"));
                goto cleanup;
@@ -1219,7 +1231,7 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
         * Start the health thread to watch over our process.
         */
        if (pthread_create(&state->health_thread, NULL,
-                          fsm_health__thread_proc, state) < 0) {
+                          fsm_health__thread_proc, state)) {
                ipc_server_stop_async(state->ipc_server_data);
                err = error(_("could not start fsmonitor health thread"));
                goto cleanup;
@@ -1282,6 +1294,11 @@ static int fsmonitor_run_daemon(void)
        strbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));
        state.nr_paths_watching = 1;
 
+       strbuf_init(&state.alias.alias, 0);
+       strbuf_init(&state.alias.points_to, 0);
+       if ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)))
+               goto done;
+
        /*
         * We create and delete cookie files somewhere inside the .git
         * directory to help us keep sync with the file system.  If
@@ -1343,7 +1360,8 @@ static int fsmonitor_run_daemon(void)
         * directory.)
         */
        strbuf_init(&state.path_ipc, 0);
-       strbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));
+       strbuf_addstr(&state.path_ipc,
+               absolute_path(fsmonitor_ipc__get_path(the_repository)));
 
        /*
         * Confirm that we can create platform-specific resources for the
@@ -1390,11 +1408,13 @@ done:
        strbuf_release(&state.path_gitdir_watch);
        strbuf_release(&state.path_cookie_prefix);
        strbuf_release(&state.path_ipc);
+       strbuf_release(&state.alias.alias);
+       strbuf_release(&state.alias.points_to);
 
        return err;
 }
 
-static int try_to_run_foreground_daemon(int detach_console)
+static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED)
 {
        /*
         * Technically, we don't need to probe for an existing daemon
@@ -1424,7 +1444,8 @@ static int try_to_run_foreground_daemon(int detach_console)
 
 static start_bg_wait_cb bg_wait_cb;
 
-static int bg_wait_cb(const struct child_process *cp, void *cb_data)
+static int bg_wait_cb(const struct child_process *cp UNUSED,
+                     void *cb_data UNUSED)
 {
        enum ipc_active_state s = fsmonitor_ipc__get_state();
 
@@ -1563,7 +1584,7 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
 }
 
 #else
-int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
+int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix UNUSED)
 {
        struct option options[] = {
                OPT_END()
index ceff31ea002b873e8e2e18cdd965aa47f4ddabbc..7c11d5ebef052a46cd06b411960fb1c2b3d6ac08 100644 (file)
  */
 
 #include "builtin.h"
+#include "abspath.h"
+#include "date.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "tempfile.h"
 #include "commit.h"
 #include "commit-graph.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "pack.h"
 #include "pack-objects.h"
+#include "path.h"
 #include "blob.h"
 #include "tree.h"
 #include "promisor-remote.h"
 #include "refs.h"
 #include "remote.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "hook.h"
+#include "setup.h"
+#include "trace2.h"
 
 #define FAILED_RUN "failed to run %s"
 
@@ -42,7 +51,8 @@ static const char * const builtin_gc_usage[] = {
 
 static int pack_refs = 1;
 static int prune_reflogs = 1;
-static int cruft_packs = 0;
+static int cruft_packs = 1;
+static unsigned long max_cruft_size;
 static int aggressive_depth = 50;
 static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
@@ -52,6 +62,8 @@ static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
 static const char *prune_expire = "2.weeks.ago";
 static const char *prune_worktrees_expire = "3.months.ago";
+static char *repack_filter;
+static char *repack_filter_to;
 static unsigned long big_pack_threshold;
 static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
 
@@ -154,6 +166,7 @@ static void gc_config(void)
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
        git_config_get_bool("gc.autodetach", &detach_auto);
        git_config_get_bool("gc.cruftpacks", &cruft_packs);
+       git_config_get_ulong("gc.maxcruftsize", &max_cruft_size);
        git_config_get_expiry("gc.pruneexpire", &prune_expire);
        git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
        git_config_get_expiry("gc.logexpiry", &gc_log_expire);
@@ -161,15 +174,20 @@ static void gc_config(void)
        git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold);
        git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size);
 
+       git_config_get_string("gc.repackfilter", &repack_filter);
+       git_config_get_string("gc.repackfilterto", &repack_filter_to);
+
        git_config(git_default_config, NULL);
 }
 
 struct maintenance_run_opts;
 static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
 {
-       const char *argv[] = { "pack-refs", "--all", "--prune", NULL };
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
-       return run_command_v_opt(argv, RUN_GIT_CMD);
+       cmd.git_cmd = 1;
+       strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL);
+       return run_command(&cmd);
 }
 
 static int too_many_loose_objects(void)
@@ -211,7 +229,7 @@ static struct packed_git *find_base_packs(struct string_list *packs,
        struct packed_git *p, *base = NULL;
 
        for (p = get_all_packs(the_repository); p; p = p->next) {
-               if (!p->pack_local)
+               if (!p->pack_local || p->is_cruft)
                        continue;
                if (limit) {
                        if (p->pack_size >= limit)
@@ -282,7 +300,7 @@ static uint64_t total_ram(void)
 
 static uint64_t estimate_repack_memory(struct packed_git *pack)
 {
-       unsigned long nr_objects = approximate_object_count();
+       unsigned long nr_objects = repo_approximate_object_count(the_repository);
        size_t os_cache, heap;
 
        if (!pack || !nr_objects)
@@ -322,7 +340,7 @@ static uint64_t estimate_repack_memory(struct packed_git *pack)
        return os_cache + heap;
 }
 
-static int keep_one_pack(struct string_list_item *item, void *data)
+static int keep_one_pack(struct string_list_item *item, void *data UNUSED)
 {
        strvec_pushf(&repack, "--keep-pack=%s", basename(item->string));
        return 0;
@@ -336,6 +354,9 @@ static void add_repack_all_option(struct string_list *keep_pack)
                strvec_push(&repack, "--cruft");
                if (prune_expire)
                        strvec_pushf(&repack, "--cruft-expiration=%s", prune_expire);
+               if (max_cruft_size)
+                       strvec_pushf(&repack, "--max-cruft-size=%lu",
+                                    max_cruft_size);
        } else {
                strvec_push(&repack, "-A");
                if (prune_expire)
@@ -344,6 +365,11 @@ static void add_repack_all_option(struct string_list *keep_pack)
 
        if (keep_pack)
                for_each_string_list(keep_pack, keep_one_pack, NULL);
+
+       if (repack_filter && *repack_filter)
+               strvec_pushf(&repack, "--filter=%s", repack_filter);
+       if (repack_filter_to && *repack_filter_to)
+               strvec_pushf(&repack, "--filter-to=%s", repack_filter_to);
 }
 
 static void add_repack_incremental_option(void)
@@ -535,8 +561,14 @@ static void gc_before_repack(void)
        if (pack_refs && maintenance_task_pack_refs(NULL))
                die(FAILED_RUN, "pack-refs");
 
-       if (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD))
-               die(FAILED_RUN, reflog.v[0]);
+       if (prune_reflogs) {
+               struct child_process cmd = CHILD_PROCESS_INIT;
+
+               cmd.git_cmd = 1;
+               strvec_pushv(&cmd.args, reflog.v);
+               if (run_command(&cmd))
+                       die(FAILED_RUN, reflog.v[0]);
+       }
 }
 
 int cmd_gc(int argc, const char **argv, const char *prefix)
@@ -550,6 +582,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        int daemonized = 0;
        int keep_largest_pack = -1;
        timestamp_t dummy;
+       struct child_process rerere_cmd = CHILD_PROCESS_INIT;
 
        struct option builtin_gc_options[] = {
                OPT__QUIET(&quiet, N_("suppress progress reporting")),
@@ -557,6 +590,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        N_("prune unreferenced objects"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
                OPT_BOOL(0, "cruft", &cruft_packs, N_("pack unreferenced objects separately")),
+               OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size,
+                             N_("with --cruft, limit the size of new cruft packs")),
                OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
                OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
                           PARSE_OPT_NOCOMPLETE),
@@ -671,30 +706,44 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        gc_before_repack();
 
        if (!repository_format_precious_objects) {
-               if (run_command_v_opt(repack.v,
-                                     RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE))
+               struct child_process repack_cmd = CHILD_PROCESS_INIT;
+
+               repack_cmd.git_cmd = 1;
+               repack_cmd.close_object_store = 1;
+               strvec_pushv(&repack_cmd.args, repack.v);
+               if (run_command(&repack_cmd))
                        die(FAILED_RUN, repack.v[0]);
 
                if (prune_expire) {
+                       struct child_process prune_cmd = CHILD_PROCESS_INIT;
+
                        /* run `git prune` even if using cruft packs */
                        strvec_push(&prune, prune_expire);
                        if (quiet)
                                strvec_push(&prune, "--no-progress");
-                       if (has_promisor_remote())
+                       if (repo_has_promisor_remote(the_repository))
                                strvec_push(&prune,
                                            "--exclude-promisor-objects");
-                       if (run_command_v_opt(prune.v, RUN_GIT_CMD))
+                       prune_cmd.git_cmd = 1;
+                       strvec_pushv(&prune_cmd.args, prune.v);
+                       if (run_command(&prune_cmd))
                                die(FAILED_RUN, prune.v[0]);
                }
        }
 
        if (prune_worktrees_expire) {
+               struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;
+
                strvec_push(&prune_worktrees, prune_worktrees_expire);
-               if (run_command_v_opt(prune_worktrees.v, RUN_GIT_CMD))
+               prune_worktrees_cmd.git_cmd = 1;
+               strvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v);
+               if (run_command(&prune_worktrees_cmd))
                        die(FAILED_RUN, prune_worktrees.v[0]);
        }
 
-       if (run_command_v_opt(rerere.v, RUN_GIT_CMD))
+       rerere_cmd.git_cmd = 1;
+       strvec_pushv(&rerere_cmd.args, rerere.v);
+       if (run_command(&rerere_cmd))
                die(FAILED_RUN, rerere.v[0]);
 
        report_garbage = report_pack_garbage;
@@ -704,7 +753,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                clean_pack_garbage();
        }
 
-       prepare_repo_settings(the_repository);
        if (the_repository->settings.gc_write_commit_graph == 1)
                write_commit_graph_reachable(the_repository->objects->odb,
                                             !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
@@ -794,7 +842,7 @@ static int dfs_on_ref(const char *refname UNUSED,
        commit = lookup_commit(the_repository, oid);
        if (!commit)
                return 0;
-       if (parse_commit(commit) ||
+       if (repo_parse_commit(the_repository, commit) ||
            commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
                return 0;
 
@@ -811,7 +859,7 @@ static int dfs_on_ref(const char *refname UNUSED,
                commit = pop_commit(&stack);
 
                for (parent = commit->parents; parent; parent = parent->next) {
-                       if (parse_commit(parent->item) ||
+                       if (repo_parse_commit(the_repository, parent->item) ||
                            commit_graph_position(parent->item) != COMMIT_NOT_FROM_GRAPH ||
                            parent->item->object.flags & SEEN)
                                continue;
@@ -950,9 +998,9 @@ struct write_loose_object_data {
 
 static int loose_object_auto_limit = 100;
 
-static int loose_object_count(const struct object_id *oid,
-                              const char *path,
-                              void *data)
+static int loose_object_count(const struct object_id *oid UNUSED,
+                             const char *path UNUSED,
+                             void *data)
 {
        int *count = (int*)data;
        if (++(*count) >= loose_object_auto_limit)
@@ -977,15 +1025,15 @@ static int loose_object_auto_condition(void)
                                             NULL, NULL, &count);
 }
 
-static int bail_on_loose(const struct object_id *oid,
-                        const char *path,
-                        void *data)
+static int bail_on_loose(const struct object_id *oid UNUSED,
+                        const char *path UNUSED,
+                        void *data UNUSED)
 {
        return 1;
 }
 
 static int write_loose_object_to_stdin(const struct object_id *oid,
-                                      const char *path,
+                                      const char *path UNUSED,
                                       void *data)
 {
        struct write_loose_object_data *d = (struct write_loose_object_data *)data;
@@ -1372,7 +1420,7 @@ static void initialize_task_config(int schedule)
        strbuf_release(&config_name);
 }
 
-static int task_option_parse(const struct option *opt,
+static int task_option_parse(const struct option *opt UNUSED,
                             const char *arg, int unset)
 {
        int i, num_selected = 0;
@@ -1454,20 +1502,22 @@ static char *get_maintpath(void)
 }
 
 static char const * const builtin_maintenance_register_usage[] = {
-       "git maintenance register",
+       "git maintenance register [--config-file <path>]",
        NULL
 };
 
 static int maintenance_register(int argc, const char **argv, const char *prefix)
 {
+       char *config_file = NULL;
        struct option options[] = {
+               OPT_STRING(0, "config-file", &config_file, N_("file"), N_("use given config file")),
                OPT_END(),
        };
-       int rc;
-       char *config_value;
-       struct child_process config_set = CHILD_PROCESS_INIT;
-       struct child_process config_get = CHILD_PROCESS_INIT;
+       int found = 0;
+       const char *key = "maintenance.repo";
        char *maintpath = get_maintpath();
+       struct string_list_item *item;
+       const struct string_list *list;
 
        argc = parse_options(argc, argv, prefix, options,
                             builtin_maintenance_register_usage, 0);
@@ -1479,51 +1529,65 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
        git_config_set("maintenance.auto", "false");
 
        /* Set maintenance strategy, if unset */
-       if (!git_config_get_string("maintenance.strategy", &config_value))
-               free(config_value);
-       else
+       if (git_config_get("maintenance.strategy"))
                git_config_set("maintenance.strategy", "incremental");
 
-       config_get.git_cmd = 1;
-       strvec_pushl(&config_get.args, "config", "--global", "--get",
-                    "--fixed-value", "maintenance.repo", maintpath, NULL);
-       config_get.out = -1;
-
-       if (start_command(&config_get)) {
-               rc = error(_("failed to run 'git config'"));
-               goto done;
-       }
-
-       /* We already have this value in our config! */
-       if (!finish_command(&config_get)) {
-               rc = 0;
-               goto done;
+       if (!git_config_get_string_multi(key, &list)) {
+               for_each_string_list_item(item, list) {
+                       if (!strcmp(maintpath, item->string)) {
+                               found = 1;
+                               break;
+                       }
+               }
        }
 
-       config_set.git_cmd = 1;
-       strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo",
-                    maintpath, NULL);
+       if (!found) {
+               int rc;
+               char *user_config = NULL, *xdg_config = NULL;
 
-       rc = run_command(&config_set);
+               if (!config_file) {
+                       git_global_config(&user_config, &xdg_config);
+                       config_file = user_config;
+                       if (!user_config)
+                               die(_("$HOME not set"));
+               }
+               rc = git_config_set_multivar_in_file_gently(
+                       config_file, "maintenance.repo", maintpath,
+                       CONFIG_REGEX_NONE, 0);
+               free(user_config);
+               free(xdg_config);
+
+               if (rc)
+                       die(_("unable to add '%s' value of '%s'"),
+                           key, maintpath);
+       }
 
-done:
        free(maintpath);
-       return rc;
+       return 0;
 }
 
 static char const * const builtin_maintenance_unregister_usage[] = {
-       "git maintenance unregister",
+       "git maintenance unregister [--config-file <path>] [--force]",
        NULL
 };
 
 static int maintenance_unregister(int argc, const char **argv, const char *prefix)
 {
+       int force = 0;
+       char *config_file = NULL;
        struct option options[] = {
+               OPT_STRING(0, "config-file", &config_file, N_("file"), N_("use given config file")),
+               OPT__FORCE(&force,
+                          N_("return success even if repository was not registered"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_END(),
        };
-       int rc;
-       struct child_process config_unset = CHILD_PROCESS_INIT;
+       const char *key = "maintenance.repo";
        char *maintpath = get_maintpath();
+       int found = 0;
+       struct string_list_item *item;
+       const struct string_list *list;
+       struct config_set cs = { { 0 } };
 
        argc = parse_options(argc, argv, prefix, options,
                             builtin_maintenance_unregister_usage, 0);
@@ -1531,13 +1595,47 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
                usage_with_options(builtin_maintenance_unregister_usage,
                                   options);
 
-       config_unset.git_cmd = 1;
-       strvec_pushl(&config_unset.args, "config", "--global", "--unset",
-                    "--fixed-value", "maintenance.repo", maintpath, NULL);
+       if (config_file) {
+               git_configset_init(&cs);
+               git_configset_add_file(&cs, config_file);
+       }
+       if (!(config_file
+             ? git_configset_get_string_multi(&cs, key, &list)
+             : git_config_get_string_multi(key, &list))) {
+               for_each_string_list_item(item, list) {
+                       if (!strcmp(maintpath, item->string)) {
+                               found = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (found) {
+               int rc;
+               char *user_config = NULL, *xdg_config = NULL;
+               if (!config_file) {
+                       git_global_config(&user_config, &xdg_config);
+                       config_file = user_config;
+                       if (!user_config)
+                               die(_("$HOME not set"));
+               }
+               rc = git_config_set_multivar_in_file_gently(
+                       config_file, key, NULL, maintpath,
+                       CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
+               free(user_config);
+               free(xdg_config);
+
+               if (rc &&
+                   (!force || rc == CONFIG_NOTHING_SET))
+                       die(_("unable to unset '%s' value of '%s'"),
+                           key, maintpath);
+       } else if (!force) {
+               die(_("repository '%s' is not registered"), maintpath);
+       }
 
-       rc = run_command(&config_unset);
+       git_configset_clear(&cs);
        free(maintpath);
-       return rc;
+       return 0;
 }
 
 static const char *get_frequency(enum schedule_priority schedule)
@@ -1605,11 +1703,11 @@ static int get_schedule_cmd(const char **cmd, int *is_available)
        if (is_available)
                *is_available = 0;
 
-       string_list_split_in_place(&list, testing, ',', -1);
+       string_list_split_in_place(&list, testing, ",", -1);
        for_each_string_list_item(item, &list) {
                struct string_list pair = STRING_LIST_INIT_NODUP;
 
-               if (string_list_split_in_place(&pair, item->string, ':', 2) != 2)
+               if (string_list_split_in_place(&pair, item->string, ":", 2) != 2)
                        continue;
 
                if (!strcmp(*cmd, pair.items[0].string)) {
@@ -1627,6 +1725,15 @@ static int get_schedule_cmd(const char **cmd, int *is_available)
        return 1;
 }
 
+static int get_random_minute(void)
+{
+       /* Use a static value when under tests. */
+       if (getenv("GIT_TEST_MAINT_SCHEDULER"))
+               return 13;
+
+       return git_rand() % 60;
+}
+
 static int is_launchctl_available(void)
 {
        const char *cmd = "launchctl";
@@ -1739,6 +1846,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
        struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT;
        struct stat st;
        const char *cmd = "launchctl";
+       int minute = get_random_minute();
 
        get_schedule_cmd(&cmd, NULL);
        preamble = "<?xml version=\"1.0\"?>\n"
@@ -1764,29 +1872,30 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
        case SCHEDULE_HOURLY:
                repeat = "<dict>\n"
                         "<key>Hour</key><integer>%d</integer>\n"
-                        "<key>Minute</key><integer>0</integer>\n"
+                        "<key>Minute</key><integer>%d</integer>\n"
                         "</dict>\n";
                for (i = 1; i <= 23; i++)
-                       strbuf_addf(&plist, repeat, i);
+                       strbuf_addf(&plist, repeat, i, minute);
                break;
 
        case SCHEDULE_DAILY:
                repeat = "<dict>\n"
                         "<key>Day</key><integer>%d</integer>\n"
                         "<key>Hour</key><integer>0</integer>\n"
-                        "<key>Minute</key><integer>0</integer>\n"
+                        "<key>Minute</key><integer>%d</integer>\n"
                         "</dict>\n";
                for (i = 1; i <= 6; i++)
-                       strbuf_addf(&plist, repeat, i);
+                       strbuf_addf(&plist, repeat, i, minute);
                break;
 
        case SCHEDULE_WEEKLY:
-               strbuf_addstr(&plist,
-                             "<dict>\n"
-                             "<key>Day</key><integer>0</integer>\n"
-                             "<key>Hour</key><integer>0</integer>\n"
-                             "<key>Minute</key><integer>0</integer>\n"
-                             "</dict>\n");
+               strbuf_addf(&plist,
+                           "<dict>\n"
+                           "<key>Day</key><integer>0</integer>\n"
+                           "<key>Hour</key><integer>0</integer>\n"
+                           "<key>Minute</key><integer>%d</integer>\n"
+                           "</dict>\n",
+                           minute);
                break;
 
        default:
@@ -1842,7 +1951,7 @@ static int launchctl_add_plists(void)
               launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY);
 }
 
-static int launchctl_update_schedule(int run_maintenance, int fd)
+static int launchctl_update_schedule(int run_maintenance, int fd UNUSED)
 {
        if (run_maintenance)
                return launchctl_add_plists();
@@ -1874,20 +1983,16 @@ static char *schtasks_task_name(const char *frequency)
 static int schtasks_remove_task(enum schedule_priority schedule)
 {
        const char *cmd = "schtasks";
-       int result;
-       struct strvec args = STRVEC_INIT;
+       struct child_process child = CHILD_PROCESS_INIT;
        const char *frequency = get_frequency(schedule);
        char *name = schtasks_task_name(frequency);
 
        get_schedule_cmd(&cmd, NULL);
-       strvec_split(&args, cmd);
-       strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL);
-
-       result = run_command_v_opt(args.v, 0);
-
-       strvec_clear(&args);
+       strvec_split(&child.args, cmd);
+       strvec_pushl(&child.args, "/delete", "/tn", name, "/f", NULL);
        free(name);
-       return result;
+
+       return run_command(&child);
 }
 
 static int schtasks_remove_tasks(void)
@@ -1907,6 +2012,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
        const char *frequency = get_frequency(schedule);
        char *name = schtasks_task_name(frequency);
        struct strbuf tfilename = STRBUF_INIT;
+       int minute = get_random_minute();
 
        get_schedule_cmd(&cmd, NULL);
 
@@ -1927,7 +2033,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
        switch (schedule) {
        case SCHEDULE_HOURLY:
                fprintf(tfile->fp,
-                       "<StartBoundary>2020-01-01T01:00:00</StartBoundary>\n"
+                       "<StartBoundary>2020-01-01T01:%02d:00</StartBoundary>\n"
                        "<Enabled>true</Enabled>\n"
                        "<ScheduleByDay>\n"
                        "<DaysInterval>1</DaysInterval>\n"
@@ -1936,12 +2042,13 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
                        "<Interval>PT1H</Interval>\n"
                        "<Duration>PT23H</Duration>\n"
                        "<StopAtDurationEnd>false</StopAtDurationEnd>\n"
-                       "</Repetition>\n");
+                       "</Repetition>\n",
+                       minute);
                break;
 
        case SCHEDULE_DAILY:
                fprintf(tfile->fp,
-                       "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
+                       "<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n"
                        "<Enabled>true</Enabled>\n"
                        "<ScheduleByWeek>\n"
                        "<DaysOfWeek>\n"
@@ -1953,19 +2060,21 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
                        "<Saturday />\n"
                        "</DaysOfWeek>\n"
                        "<WeeksInterval>1</WeeksInterval>\n"
-                       "</ScheduleByWeek>\n");
+                       "</ScheduleByWeek>\n",
+                       minute);
                break;
 
        case SCHEDULE_WEEKLY:
                fprintf(tfile->fp,
-                       "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
+                       "<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n"
                        "<Enabled>true</Enabled>\n"
                        "<ScheduleByWeek>\n"
                        "<DaysOfWeek>\n"
                        "<Sunday />\n"
                        "</DaysOfWeek>\n"
                        "<WeeksInterval>1</WeeksInterval>\n"
-                       "</ScheduleByWeek>\n");
+                       "</ScheduleByWeek>\n",
+                       minute);
                break;
 
        default:
@@ -1991,7 +2100,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
              "</Settings>\n"
              "<Actions Context=\"Author\">\n"
              "<Exec>\n"
-             "<Command>\"%s\\git.exe\"</Command>\n"
+             "<Command>\"%s\\headless-git.exe\"</Command>\n"
              "<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
              "</Exec>\n"
              "</Actions>\n"
@@ -2023,7 +2132,7 @@ static int schtasks_schedule_tasks(void)
               schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY);
 }
 
-static int schtasks_update_schedule(int run_maintenance, int fd)
+static int schtasks_update_schedule(int run_maintenance, int fd UNUSED)
 {
        if (run_maintenance)
                return schtasks_schedule_tasks();
@@ -2082,6 +2191,7 @@ static int crontab_update_schedule(int run_maintenance, int fd)
        FILE *cron_list, *cron_in;
        struct strbuf line = STRBUF_INIT;
        struct tempfile *tmpedit = NULL;
+       int minute = get_random_minute();
 
        get_schedule_cmd(&cmd, NULL);
        strvec_split(&crontab_list.args, cmd);
@@ -2136,11 +2246,11 @@ static int crontab_update_schedule(int run_maintenance, int fd)
                        "# replaced in the future by a Git command.\n\n");
 
                strbuf_addf(&line_format,
-                           "%%s %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n",
+                           "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n",
                            exec_path, exec_path);
-               fprintf(cron_in, line_format.buf, "0", "1-23", "*", "hourly");
-               fprintf(cron_in, line_format.buf, "0", "0", "1-6", "daily");
-               fprintf(cron_in, line_format.buf, "0", "0", "0", "weekly");
+               fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly");
+               fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily");
+               fprintf(cron_in, line_format.buf, minute, "0", "0", "weekly");
                strbuf_release(&line_format);
 
                fprintf(cron_in, "\n%s\n", END_LINE);
@@ -2199,77 +2309,54 @@ static char *xdg_config_home_systemd(const char *filename)
        return xdg_config_home_for("systemd/user", filename);
 }
 
-static int systemd_timer_enable_unit(int enable,
-                                    enum schedule_priority schedule)
-{
-       const char *cmd = "systemctl";
-       struct child_process child = CHILD_PROCESS_INIT;
-       const char *frequency = get_frequency(schedule);
-
-       /*
-        * Disabling the systemd unit while it is already disabled makes
-        * systemctl print an error.
-        * Let's ignore it since it means we already are in the expected state:
-        * the unit is disabled.
-        *
-        * On the other hand, enabling a systemd unit which is already enabled
-        * produces no error.
-        */
-       if (!enable)
-               child.no_stderr = 1;
-
-       get_schedule_cmd(&cmd, NULL);
-       strvec_split(&child.args, cmd);
-       strvec_pushl(&child.args, "--user", enable ? "enable" : "disable",
-                    "--now", NULL);
-       strvec_pushf(&child.args, "git-maintenance@%s.timer", frequency);
-
-       if (start_command(&child))
-               return error(_("failed to start systemctl"));
-       if (finish_command(&child))
-               /*
-                * Disabling an already disabled systemd unit makes
-                * systemctl fail.
-                * Let's ignore this failure.
-                *
-                * Enabling an enabled systemd unit doesn't fail.
-                */
-               if (enable)
-                       return error(_("failed to run systemctl"));
-       return 0;
-}
+#define SYSTEMD_UNIT_FORMAT "git-maintenance@%s.%s"
 
-static int systemd_timer_delete_unit_templates(void)
+static int systemd_timer_delete_timer_file(enum schedule_priority priority)
 {
        int ret = 0;
-       char *filename = xdg_config_home_systemd("git-maintenance@.timer");
-       if (unlink(filename) && !is_missing_file_error(errno))
-               ret = error_errno(_("failed to delete '%s'"), filename);
-       FREE_AND_NULL(filename);
+       const char *frequency = get_frequency(priority);
+       char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer");
+       char *filename = xdg_config_home_systemd(local_timer_name);
 
-       filename = xdg_config_home_systemd("git-maintenance@.service");
        if (unlink(filename) && !is_missing_file_error(errno))
                ret = error_errno(_("failed to delete '%s'"), filename);
 
        free(filename);
+       free(local_timer_name);
        return ret;
 }
 
-static int systemd_timer_delete_units(void)
+static int systemd_timer_delete_service_template(void)
 {
-       return systemd_timer_enable_unit(0, SCHEDULE_HOURLY) ||
-              systemd_timer_enable_unit(0, SCHEDULE_DAILY) ||
-              systemd_timer_enable_unit(0, SCHEDULE_WEEKLY) ||
-              systemd_timer_delete_unit_templates();
+       int ret = 0;
+       char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service");
+       char *filename = xdg_config_home_systemd(local_service_name);
+       if (unlink(filename) && !is_missing_file_error(errno))
+               ret = error_errno(_("failed to delete '%s'"), filename);
+
+       free(filename);
+       free(local_service_name);
+       return ret;
 }
 
-static int systemd_timer_write_unit_templates(const char *exec_path)
+/*
+ * Write the schedule information into a git-maintenance@<schedule>.timer
+ * file using a custom minute. This timer file cannot use the templating
+ * system, so we generate a specific file for each.
+ */
+static int systemd_timer_write_timer_file(enum schedule_priority schedule,
+                                         int minute)
 {
+       int res = -1;
        char *filename;
        FILE *file;
        const char *unit;
+       char *schedule_pattern = NULL;
+       const char *frequency = get_frequency(schedule);
+       char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer");
+
+       filename = xdg_config_home_systemd(local_timer_name);
 
-       filename = xdg_config_home_systemd("git-maintenance@.timer");
        if (safe_create_leading_directories(filename)) {
                error(_("failed to create directories for '%s'"), filename);
                goto error;
@@ -2278,6 +2365,23 @@ static int systemd_timer_write_unit_templates(const char *exec_path)
        if (!file)
                goto error;
 
+       switch (schedule) {
+       case SCHEDULE_HOURLY:
+               schedule_pattern = xstrfmt("*-*-* 1..23:%02d:00", minute);
+               break;
+
+       case SCHEDULE_DAILY:
+               schedule_pattern = xstrfmt("Tue..Sun *-*-* 0:%02d:00", minute);
+               break;
+
+       case SCHEDULE_WEEKLY:
+               schedule_pattern = xstrfmt("Mon 0:%02d:00", minute);
+               break;
+
+       default:
+               BUG("Unhandled schedule_priority");
+       }
+
        unit = "# This file was created and is maintained by Git.\n"
               "# Any edits made in this file might be replaced in the future\n"
               "# by a Git command.\n"
@@ -2286,12 +2390,12 @@ static int systemd_timer_write_unit_templates(const char *exec_path)
               "Description=Optimize Git repositories data\n"
               "\n"
               "[Timer]\n"
-              "OnCalendar=%i\n"
+              "OnCalendar=%s\n"
               "Persistent=true\n"
               "\n"
               "[Install]\n"
               "WantedBy=timers.target\n";
-       if (fputs(unit, file) == EOF) {
+       if (fprintf(file, unit, schedule_pattern) < 0) {
                error(_("failed to write to '%s'"), filename);
                fclose(file);
                goto error;
@@ -2300,9 +2404,36 @@ static int systemd_timer_write_unit_templates(const char *exec_path)
                error_errno(_("failed to flush '%s'"), filename);
                goto error;
        }
+
+       res = 0;
+
+error:
+       free(schedule_pattern);
+       free(local_timer_name);
        free(filename);
+       return res;
+}
 
-       filename = xdg_config_home_systemd("git-maintenance@.service");
+/*
+ * No matter the schedule, we use the same service and can make use of the
+ * templating system. When installing git-maintenance@<schedule>.timer,
+ * systemd will notice that git-maintenance@.service exists as a template
+ * and will use this file and insert the <schedule> into the template at
+ * the position of "%i".
+ */
+static int systemd_timer_write_service_template(const char *exec_path)
+{
+       int res = -1;
+       char *filename;
+       FILE *file;
+       const char *unit;
+       char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service");
+
+       filename = xdg_config_home_systemd(local_service_name);
+       if (safe_create_leading_directories(filename)) {
+               error(_("failed to create directories for '%s'"), filename);
+               goto error;
+       }
        file = fopen_or_warn(filename, "w");
        if (!file)
                goto error;
@@ -2320,7 +2451,7 @@ static int systemd_timer_write_unit_templates(const char *exec_path)
               "LockPersonality=yes\n"
               "MemoryDenyWriteExecute=yes\n"
               "NoNewPrivileges=yes\n"
-              "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\n"
+              "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_VSOCK\n"
               "RestrictNamespaces=yes\n"
               "RestrictRealtime=yes\n"
               "RestrictSUIDSGID=yes\n"
@@ -2335,29 +2466,114 @@ static int systemd_timer_write_unit_templates(const char *exec_path)
                error_errno(_("failed to flush '%s'"), filename);
                goto error;
        }
+
+       res = 0;
+
+error:
+       free(local_service_name);
        free(filename);
+       return res;
+}
+
+static int systemd_timer_enable_unit(int enable,
+                                    enum schedule_priority schedule,
+                                    int minute)
+{
+       const char *cmd = "systemctl";
+       struct child_process child = CHILD_PROCESS_INIT;
+       const char *frequency = get_frequency(schedule);
+
+       /*
+        * Disabling the systemd unit while it is already disabled makes
+        * systemctl print an error.
+        * Let's ignore it since it means we already are in the expected state:
+        * the unit is disabled.
+        *
+        * On the other hand, enabling a systemd unit which is already enabled
+        * produces no error.
+        */
+       if (!enable)
+               child.no_stderr = 1;
+       else if (systemd_timer_write_timer_file(schedule, minute))
+               return -1;
+
+       get_schedule_cmd(&cmd, NULL);
+       strvec_split(&child.args, cmd);
+       strvec_pushl(&child.args, "--user", enable ? "enable" : "disable",
+                    "--now", NULL);
+       strvec_pushf(&child.args, SYSTEMD_UNIT_FORMAT, frequency, "timer");
+
+       if (start_command(&child))
+               return error(_("failed to start systemctl"));
+       if (finish_command(&child))
+               /*
+                * Disabling an already disabled systemd unit makes
+                * systemctl fail.
+                * Let's ignore this failure.
+                *
+                * Enabling an enabled systemd unit doesn't fail.
+                */
+               if (enable)
+                       return error(_("failed to run systemctl"));
        return 0;
+}
+
+/*
+ * A previous version of Git wrote the timer units as template files.
+ * Clean these up, if they exist.
+ */
+static void systemd_timer_delete_stale_timer_templates(void)
+{
+       char *timer_template_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "timer");
+       char *filename = xdg_config_home_systemd(timer_template_name);
+
+       if (unlink(filename) && !is_missing_file_error(errno))
+               warning(_("failed to delete '%s'"), filename);
 
-error:
        free(filename);
-       systemd_timer_delete_unit_templates();
-       return -1;
+       free(timer_template_name);
+}
+
+static int systemd_timer_delete_unit_files(void)
+{
+       systemd_timer_delete_stale_timer_templates();
+
+       /* Purposefully not short-circuited to make sure all are called. */
+       return systemd_timer_delete_timer_file(SCHEDULE_HOURLY) |
+              systemd_timer_delete_timer_file(SCHEDULE_DAILY) |
+              systemd_timer_delete_timer_file(SCHEDULE_WEEKLY) |
+              systemd_timer_delete_service_template();
+}
+
+static int systemd_timer_delete_units(void)
+{
+       int minute = get_random_minute();
+       /* Purposefully not short-circuited to make sure all are called. */
+       return systemd_timer_enable_unit(0, SCHEDULE_HOURLY, minute) |
+              systemd_timer_enable_unit(0, SCHEDULE_DAILY, minute) |
+              systemd_timer_enable_unit(0, SCHEDULE_WEEKLY, minute) |
+              systemd_timer_delete_unit_files();
 }
 
 static int systemd_timer_setup_units(void)
 {
+       int minute = get_random_minute();
        const char *exec_path = git_exec_path();
 
-       int ret = systemd_timer_write_unit_templates(exec_path) ||
-                 systemd_timer_enable_unit(1, SCHEDULE_HOURLY) ||
-                 systemd_timer_enable_unit(1, SCHEDULE_DAILY) ||
-                 systemd_timer_enable_unit(1, SCHEDULE_WEEKLY);
+       int ret = systemd_timer_write_service_template(exec_path) ||
+                 systemd_timer_enable_unit(1, SCHEDULE_HOURLY, minute) ||
+                 systemd_timer_enable_unit(1, SCHEDULE_DAILY, minute) ||
+                 systemd_timer_enable_unit(1, SCHEDULE_WEEKLY, minute);
+
        if (ret)
                systemd_timer_delete_units();
+       else
+               systemd_timer_delete_stale_timer_templates();
+
        return ret;
 }
 
-static int systemd_timer_update_schedule(int run_maintenance, int fd)
+static int systemd_timer_update_schedule(int run_maintenance, int fd UNUSED)
 {
        if (run_maintenance)
                return systemd_timer_setup_units();
@@ -2529,9 +2745,12 @@ static int maintenance_start(int argc, const char **argv, const char *prefix)
        opts.scheduler = resolve_scheduler(opts.scheduler);
        validate_scheduler(opts.scheduler);
 
+       if (update_background_schedule(&opts, 1))
+               die(_("failed to set up maintenance schedule"));
+
        if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL))
                warning(_("failed to add repo to global config"));
-       return update_background_schedule(&opts, 1);
+       return 0;
 }
 
 static const char *const builtin_maintenance_stop_usage[] = {
index 491af9202dc937339db83980e2bf96de6ff81646..66a7389f9f4a21df975ef12a00d93fd7d8eee46f 100644 (file)
@@ -1,11 +1,9 @@
 /*
  * Copyright (c) 2005, 2006 Rene Scharfe
  */
-#include "cache.h"
+#include "builtin.h"
 #include "commit.h"
 #include "tar.h"
-#include "builtin.h"
-#include "quote.h"
 
 static const char builtin_get_tar_commit_id_usage[] =
 "git get-tar-commit-id";
@@ -14,7 +12,7 @@ static const char builtin_get_tar_commit_id_usage[] =
 #define RECORDSIZE     (512)
 #define HEADERSIZE (2 * RECORDSIZE)
 
-int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
+int cmd_get_tar_commit_id(int argc, const char **argv UNUSED, const char *prefix)
 {
        char buffer[HEADERSIZE];
        struct ustar_header *header = (struct ustar_header *)buffer;
@@ -24,6 +22,8 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
        long len;
        char *end;
 
+       BUG_ON_NON_EMPTY_PREFIX(prefix);
+
        if (argc != 1)
                usage(builtin_get_tar_commit_id_usage);
 
@@ -32,7 +32,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
                die_errno("git get-tar-commit-id: read error");
        if (n != HEADERSIZE)
                die_errno("git get-tar-commit-id: EOF before reading tar header");
-       if (header->typeflag[0] != 'g')
+       if (header->typeflag[0] != TYPEFLAG_GLOBAL_HEADER)
                return 1;
 
        len = strtol(content, &end, 10);
index e6bcdf860cc96af2e70975eea2f66ac8ec8606eb..c8e33f977552081fcde8afc12d0aa92bfc4b5e62 100644 (file)
@@ -3,28 +3,32 @@
  *
  * Copyright (c) 2006 Junio C Hamano
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
-#include "blob.h"
-#include "tree.h"
-#include "commit.h"
 #include "tag.h"
 #include "tree-walk.h"
-#include "builtin.h"
 #include "parse-options.h"
 #include "string-list.h"
 #include "run-command.h"
-#include "userdiff.h"
 #include "grep.h"
 #include "quote.h"
 #include "dir.h"
 #include "pathspec.h"
+#include "setup.h"
 #include "submodule.h"
 #include "submodule-config.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "packfile.h"
+#include "pager.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "write-or-die.h"
 
 static const char *grep_prefix;
 
@@ -283,14 +287,18 @@ static int wait_all(void)
        return hit;
 }
 
-static int grep_cmd_config(const char *var, const char *value, void *cb)
+static int grep_cmd_config(const char *var, const char *value,
+                          const struct config_context *ctx, void *cb)
 {
-       int st = grep_config(var, value, cb);
-       if (git_color_default_config(var, value, NULL) < 0)
+       int st = grep_config(var, value, ctx, cb);
+
+       if (git_color_config(var, value, cb) < 0)
+               st = -1;
+       else if (git_default_config(var, value, ctx, cb) < 0)
                st = -1;
 
        if (!strcmp(var, "grep.threads")) {
-               num_threads = git_config_int(var, value);
+               num_threads = git_config_int(var, value, ctx->kvi);
                if (num_threads < 0)
                        die(_("invalid number of threads specified (%d) for %s"),
                            num_threads, var);
@@ -458,6 +466,33 @@ static int grep_submodule(struct grep_opt *opt,
         * subrepo's odbs to the in-memory alternates list.
         */
        obj_read_lock();
+
+       /*
+        * NEEDSWORK: when reading a submodule, the sparsity settings in the
+        * superproject are incorrectly forgotten or misused. For example:
+        *
+        * 1. "command_requires_full_index"
+        *      When this setting is turned on for `grep`, only the superproject
+        *      knows it. All the submodules are read with their own configs
+        *      and get prepare_repo_settings()'d. Therefore, these submodules
+        *      "forget" the sparse-index feature switch. As a result, the index
+        *      of these submodules are expanded unexpectedly.
+        *
+        * 2. "core_apply_sparse_checkout"
+        *      When running `grep` in the superproject, this setting is
+        *      populated using the superproject's configs. However, once
+        *      initialized, this config is globally accessible and is read by
+        *      prepare_repo_settings() for the submodules. For instance, if a
+        *      submodule is using a sparse-checkout, however, the superproject
+        *      is not, the result is that the config from the superproject will
+        *      dictate the behavior for the submodule, making it "forget" its
+        *      sparse-checkout state.
+        *
+        * 3. "core_sparse_checkout_cone"
+        *      ditto.
+        *
+        * Note that this list is not exhaustive.
+        */
        repo_read_gitmodules(subrepo, 0);
 
        /*
@@ -520,8 +555,6 @@ static int grep_cache(struct grep_opt *opt,
        if (repo_read_index(repo) < 0)
                die(_("index file corrupt"));
 
-       /* TODO: audit for interaction with sparse-index. */
-       ensure_full_index(repo->index);
        for (nr = 0; nr < repo->index->cache_nr; nr++) {
                const struct cache_entry *ce = repo->index->cache[nr];
 
@@ -530,8 +563,21 @@ static int grep_cache(struct grep_opt *opt,
 
                strbuf_setlen(&name, name_base_len);
                strbuf_addstr(&name, ce->name);
+               if (S_ISSPARSEDIR(ce->ce_mode)) {
+                       enum object_type type;
+                       struct tree_desc tree;
+                       void *data;
+                       unsigned long size;
+
+                       data = repo_read_object_file(the_repository, &ce->oid,
+                                                    &type, &size);
+                       init_tree_desc(&tree, data, size);
 
-               if (S_ISREG(ce->ce_mode) &&
+                       hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
+                       strbuf_setlen(&name, name_base_len);
+                       strbuf_addstr(&name, ce->name);
+                       free(data);
+               } else if (S_ISREG(ce->ce_mode) &&
                    match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL,
                                   S_ISDIR(ce->ce_mode) ||
                                   S_ISGITLINK(ce->ce_mode))) {
@@ -594,7 +640,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        strbuf_addstr(&name, base->buf + tn_len);
                        match = tree_entry_interesting(repo->index,
                                                       &entry, &name,
-                                                      0, pathspec);
+                                                      pathspec);
                        strbuf_setlen(&name, name_base_len);
 
                        if (match == all_entries_not_interesting)
@@ -614,7 +660,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        void *data;
                        unsigned long size;
 
-                       data = read_object_file(&entry.oid, &type, &size);
+                       data = repo_read_object_file(the_repository,
+                                                    &entry.oid, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
                                    oid_to_hex(&entry.oid));
@@ -762,14 +809,20 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
 {
        struct grep_opt *grep_opt = opt->value;
        int from_stdin;
+       const char *filename = arg;
        FILE *patterns;
        int lno = 0;
        struct strbuf sb = STRBUF_INIT;
 
        BUG_ON_OPT_NEG(unset);
 
-       from_stdin = !strcmp(arg, "-");
-       patterns = from_stdin ? stdin : fopen(arg, "r");
+       if (!*filename)
+               ; /* leave it as-is */
+       else
+               filename = prefix_filename_except_for_dash(grep_prefix, filename);
+
+       from_stdin = !strcmp(filename, "-");
+       patterns = from_stdin ? stdin : fopen(filename, "r");
        if (!patterns)
                die_errno(_("cannot open '%s'"), arg);
        while (strbuf_getline(&sb, patterns) == 0) {
@@ -783,6 +836,8 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
        if (!from_stdin)
                fclose(patterns);
        strbuf_release(&sb);
+       if (filename != arg)
+               free((void *)filename);
        return 0;
 }
 
@@ -874,9 +929,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                         N_("process binary files with textconv filters")),
                OPT_SET_INT('r', "recursive", &opt.max_depth,
                            N_("search in subdirectories (default)"), -1),
-               { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"),
-                       N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
-                       NULL, 1 },
+               OPT_INTEGER_F(0, "max-depth", &opt.max_depth,
+                       N_("descend at most <n> levels"), PARSE_OPT_NONEG),
                OPT_GROUP(""),
                OPT_SET_INT('E', "extended-regexp", &opt.pattern_type_option,
                            N_("use extended POSIX regular expressions"),
@@ -940,7 +994,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK_F(0, "and", &opt, NULL,
                        N_("combine patterns specified with -e"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback),
-               OPT_BOOL(0, "or", &dummy, ""),
+               OPT_BOOL_F(0, "or", &dummy, "", PARSE_OPT_NONEG),
                OPT_CALLBACK_F(0, "not", &opt, NULL, "",
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback),
                OPT_CALLBACK_F('(', NULL, &opt, NULL, "",
@@ -984,6 +1038,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_KEEP_DASHDASH |
                             PARSE_OPT_STOP_AT_NON_OPTION);
 
+       if (the_repository->gitdir) {
+               prepare_repo_settings(the_repository);
+               the_repository->settings.command_requires_full_index = 0;
+       }
+
        if (use_index && !startup_info->have_repository) {
                int fallback = 0;
                git_config_get_bool("grep.fallbacktonoindex", &fallback);
index fbae878c2b951599fcfb82021185c67cc942b5ca..82ca6d2bfdc7797a13646ff01271ceb803b0a973 100644 (file)
@@ -5,12 +5,18 @@
  * Copyright (C) Junio C Hamano, 2005
  */
 #include "builtin.h"
+#include "abspath.h"
 #include "config.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "blob.h"
 #include "quote.h"
 #include "parse-options.h"
-#include "exec-cmd.h"
+#include "setup.h"
+#include "strbuf.h"
+#include "write-or-die.h"
 
 /*
  * This is to create corrupt objects for debugging and as such it
@@ -27,6 +33,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig
        else
                ret = write_object_file_literally(buf.buf, buf.len, type, oid,
                                                 flags);
+       close(fd);
        strbuf_release(&buf);
        return ret;
 }
@@ -80,8 +87,9 @@ static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,
 int cmd_hash_object(int argc, const char **argv, const char *prefix)
 {
        static const char * const hash_object_usage[] = {
-               N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."),
-               "git hash-object  --stdin-paths",
+               N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+                  "                [--stdin [--literally]] [--] <file>..."),
+               N_("git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"),
                NULL
        };
        const char *type = blob_type;
index 6f2796f211e24c47c2efd3b1dfe52d8b8b757865..dc1fbe2b9862df507dc1e077258d3c44048eebef 100644 (file)
@@ -1,15 +1,18 @@
 /*
  * Builtin help command
  */
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
 #include "exec-cmd.h"
+#include "gettext.h"
+#include "pager.h"
 #include "parse-options.h"
+#include "path.h"
 #include "run-command.h"
 #include "config-list.h"
 #include "help.h"
 #include "alias.h"
+#include "setup.h"
 
 #ifndef DEFAULT_HELP_FORMAT
 #define DEFAULT_HELP_FORMAT "man"
@@ -88,7 +91,7 @@ static struct option builtin_help_options[] = {
 };
 
 static const char * const builtin_help_usage[] = {
-       "git help [-a|--all] [--[no-]verbose]] [--[no-]external-commands] [--[no-]aliases]",
+       "git help [-a|--all] [--[no-]verbose] [--[no-]external-commands] [--[no-]aliases]",
        N_("git help [[-i|--info] [-m|--man] [-w|--web]] [<command>|<doc>]"),
        "git help [-g|--guides]",
        "git help [-c|--config]",
@@ -394,7 +397,8 @@ static int add_man_viewer_info(const char *var, const char *value)
        return 0;
 }
 
-static int git_help_config(const char *var, const char *value, void *cb)
+static int git_help_config(const char *var, const char *value,
+                          const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "help.format")) {
                if (!value)
@@ -417,7 +421,7 @@ static int git_help_config(const char *var, const char *value, void *cb)
        if (starts_with(var, "man."))
                return add_man_viewer_info(var, value);
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static struct cmdnames main_cmds, other_cmds;
index b6530d189ad08dc76367fca61cc8eae95be13ffd..5234693a94b40e2a6dd7aa4a8d724f0ac7b06a3b 100644 (file)
@@ -1,13 +1,12 @@
-#include "cache.h"
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
 #include "hook.h"
 #include "parse-options.h"
-#include "strbuf.h"
 #include "strvec.h"
 
 #define BUILTIN_HOOK_RUN_USAGE \
-       N_("git hook run [--ignore-missing] <hook-name> [-- <hook-args>]")
+       N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
 
 static const char * const builtin_hook_usage[] = {
        BUILTIN_HOOK_RUN_USAGE,
@@ -28,6 +27,8 @@ static int run(int argc, const char **argv, const char *prefix)
        struct option run_options[] = {
                OPT_BOOL(0, "ignore-missing", &ignore_missing,
                         N_("silently ignore missing requested <hook-name>")),
+               OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"),
+                          N_("file to read into hooks' stdin")),
                OPT_END(),
        };
        int ret;
index 6648f2daef5cef38fd59dfba47c048ce72bb4c36..1ea87e01f2905ed70e3a17a847278ec2b7b68ec3 100644 (file)
@@ -1,20 +1,27 @@
 #include "builtin.h"
 #include "config.h"
 #include "delta.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "pack.h"
 #include "csum-file.h"
 #include "blob.h"
 #include "commit.h"
-#include "tag.h"
 #include "tree.h"
 #include "progress.h"
 #include "fsck.h"
-#include "exec-cmd.h"
+#include "strbuf.h"
 #include "streaming.h"
 #include "thread-utils.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "pack-revindex.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
+#include "replace-object.h"
 #include "promisor-remote.h"
+#include "setup.h"
 
 static const char index_pack_usage[] =
 "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -212,7 +219,8 @@ static void cleanup_thread(void)
 }
 
 static int mark_link(struct object *obj, enum object_type type,
-                    void *data, struct fsck_options *options)
+                    void *data UNUSED,
+                    struct fsck_options *options UNUSED)
 {
        if (!obj)
                return -1;
@@ -801,7 +809,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
        if (startup_info->have_repository) {
                read_lock();
                collision_test_needed =
-                       has_object_file_with_flags(oid, OBJECT_INFO_QUICK);
+                       repo_has_object_file_with_flags(the_repository, oid,
+                                                       OBJECT_INFO_QUICK);
                read_unlock();
        }
 
@@ -821,7 +830,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                        die(_("cannot read existing object info %s"), oid_to_hex(oid));
                if (has_type != type || has_size != size)
                        die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
-               has_data = read_object_file(oid, &has_type, &has_size);
+               has_data = repo_read_object_file(the_repository, oid,
+                                                &has_type, &has_size);
                read_unlock();
                if (!data)
                        data = new_data = get_data_from_pack(obj_entry);
@@ -1154,6 +1164,7 @@ static void parse_pack_objects(unsigned char *hash)
        struct ofs_delta_entry *ofs_delta = ofs_deltas;
        struct object_id ref_delta_oid;
        struct stat st;
+       git_hash_ctx tmp_ctx;
 
        if (verbose)
                progress = start_progress(
@@ -1190,7 +1201,9 @@ static void parse_pack_objects(unsigned char *hash)
 
        /* Check pack integrity */
        flush();
-       the_hash_algo->final_fn(hash, &input_ctx);
+       the_hash_algo->init_fn(&tmp_ctx);
+       the_hash_algo->clone_fn(&tmp_ctx, &input_ctx);
+       the_hash_algo->final_fn(hash, &tmp_ctx);
        if (!hasheq(fill(the_hash_algo->rawsz), hash))
                die(_("pack is corrupted (SHA1 mismatch)"));
        use(the_hash_algo->rawsz);
@@ -1242,6 +1255,7 @@ static void resolve_deltas(void)
        base_cache_limit = delta_base_cache_limit * nr_threads;
        if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
                init_thread();
+               work_lock();
                for (i = 0; i < nr_threads; i++) {
                        int ret = pthread_create(&thread_data[i].thread, NULL,
                                                 threaded_second_pass, thread_data + i);
@@ -1249,6 +1263,7 @@ static void resolve_deltas(void)
                                die(_("unable to create thread: %s"),
                                    strerror(ret));
                }
+               work_unlock();
                for (i = 0; i < nr_threads; i++)
                        pthread_join(thread_data[i].thread, NULL);
                cleanup_thread();
@@ -1388,7 +1403,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
                sorted_by_pos[i] = &ref_deltas[i];
        QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
 
-       if (has_promisor_remote()) {
+       if (repo_has_promisor_remote(the_repository)) {
                /*
                 * Prefetch the delta bases.
                 */
@@ -1414,7 +1429,8 @@ static void fix_unresolved_deltas(struct hashfile *f)
 
                if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
                        continue;
-               data = read_object_file(&d->oid, &type, &size);
+               data = repo_read_object_file(the_repository, &d->oid, &type,
+                                            &size);
                if (!data)
                        continue;
 
@@ -1568,18 +1584,19 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
        strbuf_release(&pack_name);
 }
 
-static int git_index_pack_config(const char *k, const char *v, void *cb)
+static int git_index_pack_config(const char *k, const char *v,
+                                const struct config_context *ctx, void *cb)
 {
        struct pack_idx_option *opts = cb;
 
        if (!strcmp(k, "pack.indexversion")) {
-               opts->version = git_config_int(k, v);
+               opts->version = git_config_int(k, v, ctx->kvi);
                if (opts->version > 2)
                        die(_("bad pack.indexVersion=%"PRIu32), opts->version);
                return 0;
        }
        if (!strcmp(k, "pack.threads")) {
-               nr_threads = git_config_int(k, v);
+               nr_threads = git_config_int(k, v, ctx->kvi);
                if (nr_threads < 0)
                        die(_("invalid number of threads specified (%d)"),
                            nr_threads);
@@ -1595,7 +1612,7 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
                else
                        opts->flags &= ~WRITE_REV;
        }
-       return git_default_config(k, v, cb);
+       return git_default_config(k, v, ctx, cb);
 }
 
 static int cmp_uint32(const void *a_, const void *b_)
@@ -1739,16 +1756,17 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(index_pack_usage);
 
-       read_replace_refs = 0;
+       disable_replace_refs();
        fsck_options.walk = mark_link;
 
        reset_pack_idx_option(&opts);
+       opts.flags |= WRITE_REV;
        git_config(git_index_pack_config, &opts);
        if (prefix && chdir(prefix))
                die(_("Cannot come back to cwd"));
 
-       if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
-               rev_index = 1;
+       if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0))
+               rev_index = 0;
        else
                rev_index = !!(opts.flags & (WRITE_REV_VERIFY | WRITE_REV));
 
index 546f9c595e7d8c04127d3d1a3216be4673d7e6c9..0170469b849e5e5ad52cf8586f4540088b0edd61 100644 (file)
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
-#include "config.h"
-#include "refs.h"
 #include "builtin.h"
-#include "exec-cmd.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-file.h"
 #include "parse-options.h"
-#include "worktree.h"
-
-#ifndef DEFAULT_GIT_TEMPLATE_DIR
-#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
-#endif
-
-#ifdef NO_TRUSTABLE_FILEMODE
-#define TEST_FILEMODE 0
-#else
-#define TEST_FILEMODE 1
-#endif
-
-#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
-
-static int init_is_bare_repository = 0;
-static int init_shared_repository = -1;
-
-static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
-                            DIR *dir)
-{
-       size_t path_baselen = path->len;
-       size_t template_baselen = template_path->len;
-       struct dirent *de;
-
-       /* Note: if ".git/hooks" file exists in the repository being
-        * re-initialized, /etc/core-git/templates/hooks/update would
-        * cause "git init" to fail here.  I think this is sane but
-        * it means that the set of templates we ship by default, along
-        * with the way the namespace under .git/ is organized, should
-        * be really carefully chosen.
-        */
-       safe_create_dir(path->buf, 1);
-       while ((de = readdir(dir)) != NULL) {
-               struct stat st_git, st_template;
-               int exists = 0;
-
-               strbuf_setlen(path, path_baselen);
-               strbuf_setlen(template_path, template_baselen);
-
-               if (de->d_name[0] == '.')
-                       continue;
-               strbuf_addstr(path, de->d_name);
-               strbuf_addstr(template_path, de->d_name);
-               if (lstat(path->buf, &st_git)) {
-                       if (errno != ENOENT)
-                               die_errno(_("cannot stat '%s'"), path->buf);
-               }
-               else
-                       exists = 1;
-
-               if (lstat(template_path->buf, &st_template))
-                       die_errno(_("cannot stat template '%s'"), template_path->buf);
-
-               if (S_ISDIR(st_template.st_mode)) {
-                       DIR *subdir = opendir(template_path->buf);
-                       if (!subdir)
-                               die_errno(_("cannot opendir '%s'"), template_path->buf);
-                       strbuf_addch(path, '/');
-                       strbuf_addch(template_path, '/');
-                       copy_templates_1(path, template_path, subdir);
-                       closedir(subdir);
-               }
-               else if (exists)
-                       continue;
-               else if (S_ISLNK(st_template.st_mode)) {
-                       struct strbuf lnk = STRBUF_INIT;
-                       if (strbuf_readlink(&lnk, template_path->buf,
-                                           st_template.st_size) < 0)
-                               die_errno(_("cannot readlink '%s'"), template_path->buf);
-                       if (symlink(lnk.buf, path->buf))
-                               die_errno(_("cannot symlink '%s' '%s'"),
-                                         lnk.buf, path->buf);
-                       strbuf_release(&lnk);
-               }
-               else if (S_ISREG(st_template.st_mode)) {
-                       if (copy_file(path->buf, template_path->buf, st_template.st_mode))
-                               die_errno(_("cannot copy '%s' to '%s'"),
-                                         template_path->buf, path->buf);
-               }
-               else
-                       error(_("ignoring template %s"), template_path->buf);
-       }
-}
-
-static void copy_templates(const char *template_dir, const char *init_template_dir)
-{
-       struct strbuf path = STRBUF_INIT;
-       struct strbuf template_path = STRBUF_INIT;
-       size_t template_len;
-       struct repository_format template_format = REPOSITORY_FORMAT_INIT;
-       struct strbuf err = STRBUF_INIT;
-       DIR *dir;
-       char *to_free = NULL;
-
-       if (!template_dir)
-               template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
-       if (!template_dir)
-               template_dir = init_template_dir;
-       if (!template_dir)
-               template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
-       if (!template_dir[0]) {
-               free(to_free);
-               return;
-       }
-
-       strbuf_addstr(&template_path, template_dir);
-       strbuf_complete(&template_path, '/');
-       template_len = template_path.len;
-
-       dir = opendir(template_path.buf);
-       if (!dir) {
-               warning(_("templates not found in %s"), template_dir);
-               goto free_return;
-       }
-
-       /* Make sure that template is from the correct vintage */
-       strbuf_addstr(&template_path, "config");
-       read_repository_format(&template_format, template_path.buf);
-       strbuf_setlen(&template_path, template_len);
-
-       /*
-        * No mention of version at all is OK, but anything else should be
-        * verified.
-        */
-       if (template_format.version >= 0 &&
-           verify_repository_format(&template_format, &err) < 0) {
-               warning(_("not copying templates from '%s': %s"),
-                         template_dir, err.buf);
-               strbuf_release(&err);
-               goto close_free_return;
-       }
-
-       strbuf_addstr(&path, get_git_common_dir());
-       strbuf_complete(&path, '/');
-       copy_templates_1(&path, &template_path, dir);
-close_free_return:
-       closedir(dir);
-free_return:
-       free(to_free);
-       strbuf_release(&path);
-       strbuf_release(&template_path);
-       clear_repository_format(&template_format);
-}
-
-/*
- * If the git_dir is not directly inside the working tree, then git will not
- * find it by default, and we need to set the worktree explicitly.
- */
-static int needs_work_tree_config(const char *git_dir, const char *work_tree)
-{
-       if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
-               return 0;
-       if (skip_prefix(git_dir, work_tree, &git_dir) &&
-           !strcmp(git_dir, "/.git"))
-               return 0;
-       return 1;
-}
-
-void initialize_repository_version(int hash_algo, int reinit)
-{
-       char repo_version_string[10];
-       int repo_version = GIT_REPO_VERSION;
-
-       if (hash_algo != GIT_HASH_SHA1)
-               repo_version = GIT_REPO_VERSION_READ;
-
-       /* This forces creation of new config file */
-       xsnprintf(repo_version_string, sizeof(repo_version_string),
-                 "%d", repo_version);
-       git_config_set("core.repositoryformatversion", repo_version_string);
-
-       if (hash_algo != GIT_HASH_SHA1)
-               git_config_set("extensions.objectformat",
-                              hash_algos[hash_algo].name);
-       else if (reinit)
-               git_config_set_gently("extensions.objectformat", NULL);
-}
-
-static int create_default_files(const char *template_path,
-                               const char *original_git_dir,
-                               const char *initial_branch,
-                               const struct repository_format *fmt,
-                               int quiet)
-{
-       struct stat st1;
-       struct strbuf buf = STRBUF_INIT;
-       char *path;
-       char junk[2];
-       int reinit;
-       int filemode;
-       struct strbuf err = STRBUF_INIT;
-       const char *init_template_dir = NULL;
-       const char *work_tree = get_git_work_tree();
-
-       /*
-        * First copy the templates -- we might have the default
-        * config file there, in which case we would want to read
-        * from it after installing.
-        *
-        * Before reading that config, we also need to clear out any cached
-        * values (since we've just potentially changed what's available on
-        * disk).
-        */
-       git_config_get_pathname("init.templatedir", &init_template_dir);
-       copy_templates(template_path, init_template_dir);
-       free((char *)init_template_dir);
-       git_config_clear();
-       reset_shared_repository();
-       git_config(git_default_config, NULL);
-
-       /*
-        * We must make sure command-line options continue to override any
-        * values we might have just re-read from the config.
-        */
-       is_bare_repository_cfg = init_is_bare_repository || !work_tree;
-       if (init_shared_repository != -1)
-               set_shared_repository(init_shared_repository);
-
-       /*
-        * We would have created the above under user's umask -- under
-        * shared-repository settings, we would need to fix them up.
-        */
-       if (get_shared_repository()) {
-               adjust_shared_perm(get_git_dir());
-       }
-
-       /*
-        * We need to create a "refs" dir in any case so that older
-        * versions of git can tell that this is a repository.
-        */
-       safe_create_dir(git_path("refs"), 1);
-       adjust_shared_perm(git_path("refs"));
-
-       if (refs_init_db(&err))
-               die("failed to set up refs db: %s", err.buf);
-
-       /*
-        * Point the HEAD symref to the initial branch with if HEAD does
-        * not yet exist.
-        */
-       path = git_path_buf(&buf, "HEAD");
-       reinit = (!access(path, R_OK)
-                 || readlink(path, junk, sizeof(junk)-1) != -1);
-       if (!reinit) {
-               char *ref;
-
-               if (!initial_branch)
-                       initial_branch = git_default_branch_name(quiet);
-
-               ref = xstrfmt("refs/heads/%s", initial_branch);
-               if (check_refname_format(ref, 0) < 0)
-                       die(_("invalid initial branch name: '%s'"),
-                           initial_branch);
-
-               if (create_symref("HEAD", ref, NULL) < 0)
-                       exit(1);
-               free(ref);
-       }
-
-       initialize_repository_version(fmt->hash_algo, 0);
-
-       /* Check filemode trustability */
-       path = git_path_buf(&buf, "config");
-       filemode = TEST_FILEMODE;
-       if (TEST_FILEMODE && !lstat(path, &st1)) {
-               struct stat st2;
-               filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
-                               !lstat(path, &st2) &&
-                               st1.st_mode != st2.st_mode &&
-                               !chmod(path, st1.st_mode));
-               if (filemode && !reinit && (st1.st_mode & S_IXUSR))
-                       filemode = 0;
-       }
-       git_config_set("core.filemode", filemode ? "true" : "false");
-
-       if (is_bare_repository())
-               git_config_set("core.bare", "true");
-       else {
-               git_config_set("core.bare", "false");
-               /* allow template config file to override the default */
-               if (log_all_ref_updates == LOG_REFS_UNSET)
-                       git_config_set("core.logallrefupdates", "true");
-               if (needs_work_tree_config(original_git_dir, work_tree))
-                       git_config_set("core.worktree", work_tree);
-       }
-
-       if (!reinit) {
-               /* Check if symlink is supported in the work tree */
-               path = git_path_buf(&buf, "tXXXXXX");
-               if (!close(xmkstemp(path)) &&
-                   !unlink(path) &&
-                   !symlink("testing", path) &&
-                   !lstat(path, &st1) &&
-                   S_ISLNK(st1.st_mode))
-                       unlink(path); /* good */
-               else
-                       git_config_set("core.symlinks", "false");
-
-               /* Check if the filesystem is case-insensitive */
-               path = git_path_buf(&buf, "CoNfIg");
-               if (!access(path, F_OK))
-                       git_config_set("core.ignorecase", "true");
-               probe_utf8_pathname_composition();
-       }
-
-       strbuf_release(&buf);
-       return reinit;
-}
-
-static void create_object_directory(void)
-{
-       struct strbuf path = STRBUF_INIT;
-       size_t baselen;
-
-       strbuf_addstr(&path, get_object_directory());
-       baselen = path.len;
-
-       safe_create_dir(path.buf, 1);
-
-       strbuf_setlen(&path, baselen);
-       strbuf_addstr(&path, "/pack");
-       safe_create_dir(path.buf, 1);
-
-       strbuf_setlen(&path, baselen);
-       strbuf_addstr(&path, "/info");
-       safe_create_dir(path.buf, 1);
-
-       strbuf_release(&path);
-}
-
-static void separate_git_dir(const char *git_dir, const char *git_link)
-{
-       struct stat st;
-
-       if (!stat(git_link, &st)) {
-               const char *src;
-
-               if (S_ISREG(st.st_mode))
-                       src = read_gitfile(git_link);
-               else if (S_ISDIR(st.st_mode))
-                       src = git_link;
-               else
-                       die(_("unable to handle file type %d"), (int)st.st_mode);
-
-               if (rename(src, git_dir))
-                       die_errno(_("unable to move %s to %s"), src, git_dir);
-               repair_worktrees(NULL, NULL);
-       }
-
-       write_file(git_link, "gitdir: %s", git_dir);
-}
-
-static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
-{
-       const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
-       /*
-        * If we already have an initialized repo, don't allow the user to
-        * specify a different algorithm, as that could cause corruption.
-        * Otherwise, if the user has specified one on the command line, use it.
-        */
-       if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
-               die(_("attempt to reinitialize repository with different hash"));
-       else if (hash != GIT_HASH_UNKNOWN)
-               repo_fmt->hash_algo = hash;
-       else if (env) {
-               int env_algo = hash_algo_by_name(env);
-               if (env_algo == GIT_HASH_UNKNOWN)
-                       die(_("unknown hash algorithm '%s'"), env);
-               repo_fmt->hash_algo = env_algo;
-       }
-}
-
-int init_db(const char *git_dir, const char *real_git_dir,
-           const char *template_dir, int hash, const char *initial_branch,
-           unsigned int flags)
-{
-       int reinit;
-       int exist_ok = flags & INIT_DB_EXIST_OK;
-       char *original_git_dir = real_pathdup(git_dir, 1);
-       struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
-
-       if (real_git_dir) {
-               struct stat st;
-
-               if (!exist_ok && !stat(git_dir, &st))
-                       die(_("%s already exists"), git_dir);
-
-               if (!exist_ok && !stat(real_git_dir, &st))
-                       die(_("%s already exists"), real_git_dir);
-
-               set_git_dir(real_git_dir, 1);
-               git_dir = get_git_dir();
-               separate_git_dir(git_dir, original_git_dir);
-       }
-       else {
-               set_git_dir(git_dir, 1);
-               git_dir = get_git_dir();
-       }
-       startup_info->have_repository = 1;
-
-       /* Ensure `core.hidedotfiles` is processed */
-       git_config(platform_core_config, NULL);
-
-       safe_create_dir(git_dir, 0);
-
-       init_is_bare_repository = is_bare_repository();
-
-       /* Check to see if the repository version is right.
-        * Note that a newly created repository does not have
-        * config file, so this will not fail.  What we are catching
-        * is an attempt to reinitialize new repository with an old tool.
-        */
-       check_repository_format(&repo_fmt);
-
-       validate_hash_algorithm(&repo_fmt, hash);
-
-       reinit = create_default_files(template_dir, original_git_dir,
-                                     initial_branch, &repo_fmt,
-                                     flags & INIT_DB_QUIET);
-       if (reinit && initial_branch)
-               warning(_("re-init: ignored --initial-branch=%s"),
-                       initial_branch);
-
-       create_object_directory();
-
-       if (get_shared_repository()) {
-               char buf[10];
-               /* We do not spell "group" and such, so that
-                * the configuration can be read by older version
-                * of git. Note, we use octal numbers for new share modes,
-                * and compatibility values for PERM_GROUP and
-                * PERM_EVERYBODY.
-                */
-               if (get_shared_repository() < 0)
-                       /* force to the mode value */
-                       xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
-               else if (get_shared_repository() == PERM_GROUP)
-                       xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
-               else if (get_shared_repository() == PERM_EVERYBODY)
-                       xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
-               else
-                       BUG("invalid value for shared_repository");
-               git_config_set("core.sharedrepository", buf);
-               git_config_set("receive.denyNonFastforwards", "true");
-       }
-
-       if (!(flags & INIT_DB_QUIET)) {
-               int len = strlen(git_dir);
-
-               if (reinit)
-                       printf(get_shared_repository()
-                              ? _("Reinitialized existing shared Git repository in %s%s\n")
-                              : _("Reinitialized existing Git repository in %s%s\n"),
-                              git_dir, len && git_dir[len-1] != '/' ? "/" : "");
-               else
-                       printf(get_shared_repository()
-                              ? _("Initialized empty shared Git repository in %s%s\n")
-                              : _("Initialized empty Git repository in %s%s\n"),
-                              git_dir, len && git_dir[len-1] != '/' ? "/" : "");
-       }
-
-       free(original_git_dir);
-       return 0;
-}
+#include "path.h"
+#include "refs.h"
+#include "repository.h"
+#include "setup.h"
+#include "strbuf.h"
 
 static int guess_repository_type(const char *git_dir)
 {
@@ -515,7 +56,11 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)
 }
 
 static const char *const init_db_usage[] = {
-       N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [<directory>]"),
+       N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+          "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+          "         [--ref-format=<format>]\n"
+          "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+          "         [--shared[=<permissions>]] [<directory>]"),
        NULL
 };
 
@@ -533,8 +78,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        const char *template_dir = NULL;
        unsigned int flags = 0;
        const char *object_format = NULL;
+       const char *ref_format = NULL;
        const char *initial_branch = NULL;
        int hash_algo = GIT_HASH_UNKNOWN;
+       unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+       int init_shared_repository = -1;
        const struct option init_db_options[] = {
                OPT_STRING(0, "template", &template_dir, N_("template-directory"),
                                N_("directory from which templates will be used")),
@@ -551,6 +99,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                           N_("override the name of the initial branch")),
                OPT_STRING(0, "object-format", &object_format, N_("hash"),
                           N_("specify the hash algorithm to use")),
+               OPT_STRING(0, "ref-format", &ref_format, N_("format"),
+                          N_("specify the reference format to use")),
                OPT_END()
        };
 
@@ -614,6 +164,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                        die(_("unknown hash algorithm '%s'"), object_format);
        }
 
+       if (ref_format) {
+               ref_storage_format = ref_storage_format_by_name(ref_format);
+               if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), ref_format);
+       }
+
        if (init_shared_repository != -1)
                set_shared_repository(init_shared_repository);
 
@@ -692,5 +248,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 
        flags |= INIT_DB_EXIST_OK;
        return init_db(git_dir, real_git_dir, template_dir, hash_algo,
-                      initial_branch, flags);
+                      ref_storage_format, initial_branch,
+                      init_shared_repository, flags);
 }
index 84748eafc01bf148dfe161161f918212cd577cee..033bd1556cf9f8d6e56c2730a3e868500388144e 100644 (file)
@@ -5,15 +5,17 @@
  *
  */
 
-#include "cache.h"
 #include "builtin.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "string-list.h"
 #include "trailer.h"
 #include "config.h"
 
 static const char * const git_interpret_trailers_usage[] = {
-       N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
+       N_("git interpret-trailers [--in-place] [--trim-empty]\n"
+          "                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+          "                       [--parse] [<file>...]"),
        NULL
 };
 
@@ -22,21 +24,24 @@ static enum trailer_if_exists if_exists;
 static enum trailer_if_missing if_missing;
 
 static int option_parse_where(const struct option *opt,
-                             const char *arg, int unset)
+                             const char *arg, int unset UNUSED)
 {
-       return trailer_set_where(&where, arg);
+       /* unset implies NULL arg, which is handled in our helper */
+       return trailer_set_where(opt->value, arg);
 }
 
 static int option_parse_if_exists(const struct option *opt,
-                                 const char *arg, int unset)
+                                 const char *arg, int unset UNUSED)
 {
-       return trailer_set_if_exists(&if_exists, arg);
+       /* unset implies NULL arg, which is handled in our helper */
+       return trailer_set_if_exists(opt->value, arg);
 }
 
 static int option_parse_if_missing(const struct option *opt,
-                                  const char *arg, int unset)
+                                  const char *arg, int unset UNUSED)
 {
-       return trailer_set_if_missing(&if_missing, arg);
+       /* unset implies NULL arg, which is handled in our helper */
+       return trailer_set_if_missing(opt->value, arg);
 }
 
 static void new_trailers_clear(struct list_head *trailers)
@@ -95,19 +100,19 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
                OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
 
-               OPT_CALLBACK(0, "where", NULL, N_("action"),
+               OPT_CALLBACK(0, "where", &where, N_("placement"),
                             N_("where to place the new trailer"), option_parse_where),
-               OPT_CALLBACK(0, "if-exists", NULL, N_("action"),
+               OPT_CALLBACK(0, "if-exists", &if_exists, N_("action"),
                             N_("action if trailer already exists"), option_parse_if_exists),
-               OPT_CALLBACK(0, "if-missing", NULL, N_("action"),
+               OPT_CALLBACK(0, "if-missing", &if_missing, N_("action"),
                             N_("action if trailer is missing"), option_parse_if_missing),
 
                OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
-               OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")),
-               OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
-               OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("set parsing options"),
+               OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.* configuration variables")),
+               OPT_BOOL(0, "unfold", &opts.unfold, N_("reformat multiline trailer values as single-line values")),
+               OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("alias for --only-trailers --only-input --unfold"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse),
-               OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")),
+               OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat \"---\" as the end of input")),
                OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
                                N_("trailer(s) to add"), option_parse_trailer),
                OPT_END()
index ee19dc5d450c57df89a864767a2b4f17d933781d..db1808d7c13dfd523e31d1da395122b52b6579ab 100644 (file)
@@ -4,11 +4,17 @@
  * (C) Copyright 2006 Linus Torvalds
  *              2006 Junio Hamano
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
 #include "color.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "oid-array.h"
 #include "tag.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
-#include "run-command.h"
 #include "shortlog.h"
 #include "remote.h"
 #include "string-list.h"
 #include "streaming.h"
 #include "version.h"
 #include "mailmap.h"
-#include "gpg-interface.h"
 #include "progress.h"
 #include "commit-slab.h"
 #include "repository.h"
 #include "commit-reach.h"
 #include "range-diff.h"
 #include "tmp-objdir.h"
+#include "tree.h"
+#include "write-or-die.h"
 
 #define MAIL_DEFAULT_WRAP 72
 #define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
@@ -53,9 +60,11 @@ static int decoration_style;
 static int decoration_given;
 static int use_mailmap_config = 1;
 static unsigned int force_in_body_from;
+static int stdout_mboxrd;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
 static const char *fmt_pretty;
+static int format_no_prefix;
 
 static const char * const builtin_log_usage[] = {
        N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
@@ -107,16 +116,19 @@ static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
 static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
 static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
 
-static int clear_decorations_callback(const struct option *opt,
-                                           const char *arg, int unset)
+static int clear_decorations_callback(const struct option *opt UNUSED,
+                                     const char *arg, int unset)
 {
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
        string_list_clear(&decorate_refs_include, 0);
        string_list_clear(&decorate_refs_exclude, 0);
        use_default_decoration_filter = 0;
        return 0;
 }
 
-static int decorate_callback(const struct option *opt, const char *arg, int unset)
+static int decorate_callback(const struct option *opt UNUSED, const char *arg,
+                            int unset)
 {
        if (unset)
                decoration_style = 0;
@@ -162,16 +174,15 @@ static void cmd_log_init_defaults(struct rev_info *rev)
        if (default_follow)
                rev->diffopt.flags.default_follow_renames = 1;
        rev->verbose_header = 1;
+       init_diffstat_widths(&rev->diffopt);
        rev->diffopt.flags.recursive = 1;
-       rev->diffopt.stat_width = -1; /* use full terminal width */
-       rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
+       rev->diffopt.flags.allow_textconv = 1;
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
        rev->patch_name_max = fmt_patch_name_max;
        rev->show_signature = default_show_signature;
        rev->encode_email_headers = default_encode_email_headers;
-       rev->diffopt.flags.allow_textconv = 1;
 
        if (default_date_mode)
                parse_date_format(default_date_mode, &rev->date_mode);
@@ -182,10 +193,10 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f
        int i;
        char *value = NULL;
        struct string_list *include = decoration_filter->include_ref_pattern;
-       const struct string_list *config_exclude =
-                       git_config_get_value_multi("log.excludeDecoration");
+       const struct string_list *config_exclude;
 
-       if (config_exclude) {
+       if (!git_config_get_string_multi("log.excludeDecoration",
+                                        &config_exclude)) {
                struct string_list_item *item;
                for_each_string_list_item(item, config_exclude)
                        string_list_append(decoration_filter->exclude_ref_config_pattern,
@@ -436,7 +447,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
        setitimer(ITIMER_REAL, &early_output_timer, NULL);
 }
 
-static void early_output(int signal)
+static void early_output(int signal UNUSED)
 {
        show_early_output = log_show_early;
 }
@@ -538,7 +549,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev)
            rev->diffopt.flags.check_failed) {
                return 02;
        }
-       return diff_result_code(&rev->diffopt, 0);
+       return diff_result_code(&rev->diffopt);
 }
 
 static int cmd_log_walk(struct rev_info *rev)
@@ -552,7 +563,8 @@ static int cmd_log_walk(struct rev_info *rev)
        return retval;
 }
 
-static int git_log_config(const char *var, const char *value, void *cb)
+static int git_log_config(const char *var, const char *value,
+                         const struct config_context *ctx, void *cb)
 {
        const char *slot_name;
 
@@ -561,7 +573,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "format.subjectprefix"))
                return git_config_string(&fmt_patch_subject_prefix, var, value);
        if (!strcmp(var, "format.filenamemaxlength")) {
-               fmt_patch_name_max = git_config_int(var, value);
+               fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
                return 0;
        }
        if (!strcmp(var, "format.encodeemailheaders")) {
@@ -580,8 +592,11 @@ static int git_log_config(const char *var, const char *value, void *cb)
                        decoration_style = 0; /* maybe warn? */
                return 0;
        }
-       if (!strcmp(var, "log.diffmerges"))
+       if (!strcmp(var, "log.diffmerges")) {
+               if (!value)
+                       return config_error_nonbool(var);
                return diff_merges_config(value);
+       }
        if (!strcmp(var, "log.showroot")) {
                default_show_root = git_config_bool(var, value);
                return 0;
@@ -601,9 +616,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (git_gpg_config(var, value, cb) < 0)
-               return -1;
-       return git_diff_ui_config(var, value, cb);
+       return git_diff_ui_config(var, value, ctx, cb);
 }
 
 int cmd_whatchanged(int argc, const char **argv, const char *prefix)
@@ -675,7 +688,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
 {
        unsigned long size;
        enum object_type type;
-       char *buf = read_object_file(oid, &type, &size);
+       char *buf = repo_read_object_file(the_repository, oid, &type, &size);
        int offset = 0;
 
        if (!buf)
@@ -708,8 +721,7 @@ static int show_tree_object(const struct object_id *oid UNUSED,
        return 0;
 }
 
-static void show_setup_revisions_tweak(struct rev_info *rev,
-                                      struct setup_revision_opt *opt)
+static void show_setup_revisions_tweak(struct rev_info *rev)
 {
        if (rev->first_parent_only)
                diff_merges_default_to_first_parent(rev);
@@ -852,11 +864,10 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
-static void log_setup_revisions_tweak(struct rev_info *rev,
-                                     struct setup_revision_opt *opt)
+static void log_setup_revisions_tweak(struct rev_info *rev)
 {
        if (rev->diffopt.flags.default_follow_renames &&
-           rev->prune_data.nr == 1)
+           diff_check_follow_pathspec(&rev->prune_data, 0))
                rev->diffopt.flags.follow_renames = 1;
 
        if (rev->first_parent_only)
@@ -969,7 +980,8 @@ static enum cover_from_description parse_cover_from_description(const char *arg)
                die(_("%s: invalid cover from description mode"), arg);
 }
 
-static int git_format_config(const char *var, const char *value, void *cb)
+static int git_format_config(const char *var, const char *value,
+                            const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "format.headers")) {
                if (!value)
@@ -1007,6 +1019,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "format.attach")) {
                if (value && *value)
                        default_attach = xstrdup(value);
+               else if (value && !*value)
+                       FREE_AND_NULL(default_attach);
                else
                        default_attach = xstrdup(git_version_string);
                return 0;
@@ -1078,8 +1092,25 @@ static int git_format_config(const char *var, const char *value, void *cb)
                cover_from_description_mode = parse_cover_from_description(value);
                return 0;
        }
+       if (!strcmp(var, "format.mboxrd")) {
+               stdout_mboxrd = git_config_bool(var, value);
+               return 0;
+       }
+       if (!strcmp(var, "format.noprefix")) {
+               format_no_prefix = 1;
+               return 0;
+       }
+
+       /*
+        * ignore some porcelain config which would otherwise be parsed by
+        * git_diff_ui_config(), via git_log_config(); we can't just avoid
+        * diff_ui_config completely, because we do care about some ui options
+        * like color.
+        */
+       if (!strcmp(var, "diff.noprefix"))
+               return 0;
 
-       return git_log_config(var, value, cb);
+       return git_log_config(var, value, ctx, cb);
 }
 
 static const char *output_directory = NULL;
@@ -1198,7 +1229,8 @@ static char *find_branch_name(struct rev_info *rev)
                return NULL;
        ref = rev->cmdline.rev[positive].name;
        tip_oid = &rev->cmdline.rev[positive].item->oid;
-       if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref, 0) &&
+       if (repo_dwim_ref(the_repository, ref, strlen(ref), &branch_oid,
+                         &full_ref, 0) &&
            skip_prefix(full_ref, "refs/heads/", &v) &&
            oideq(tip_oid, &branch_oid))
                branch = xstrdup(v);
@@ -1224,7 +1256,15 @@ static void show_diffstat(struct rev_info *rev,
        fprintf(rev->diffopt.file, "\n");
 }
 
+static void read_desc_file(struct strbuf *buf, const char *desc_file)
+{
+       if (strbuf_read_file(buf, desc_file, 0) < 0)
+               die_errno(_("unable to read branch description file '%s'"),
+                         desc_file);
+}
+
 static void prepare_cover_text(struct pretty_print_context *pp,
+                              const char *description_file,
                               const char *branch_name,
                               struct strbuf *sb,
                               const char *encoding,
@@ -1238,7 +1278,9 @@ static void prepare_cover_text(struct pretty_print_context *pp,
        if (cover_from_description_mode == COVER_FROM_NONE)
                goto do_pp;
 
-       if (branch_name && *branch_name)
+       if (description_file && *description_file)
+               read_desc_file(&description_sb, description_file);
+       else if (branch_name && *branch_name)
                read_branch_desc(&description_sb, branch_name);
        if (!description_sb.len)
                goto do_pp;
@@ -1284,6 +1326,7 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev)
 static void make_cover_letter(struct rev_info *rev, int use_separate_file,
                              struct commit *origin,
                              int nr, struct commit **list,
+                             const char *description_file,
                              const char *branch_name,
                              int quiet)
 {
@@ -1308,10 +1351,11 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
        log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);
 
        for (i = 0; !need_8bit_cte && i < nr; i++) {
-               const char *buf = get_commit_buffer(list[i], NULL);
+               const char *buf = repo_get_commit_buffer(the_repository,
+                                                        list[i], NULL);
                if (has_non_ascii(buf))
                        need_8bit_cte = 1;
-               unuse_commit_buffer(list[i], buf);
+               repo_unuse_commit_buffer(the_repository, list[i], buf);
        }
 
        if (!branch_name)
@@ -1321,8 +1365,10 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
        pp.date_mode.type = DATE_RFC2822;
        pp.rev = rev;
        pp.print_email_subject = 1;
+       pp.encode_email_headers = rev->encode_email_headers;
        pp_user_info(&pp, NULL, &sb, committer, encoding);
-       prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);
+       prepare_cover_text(&pp, description_file, branch_name, &sb,
+                          encoding, need_8bit_cte);
        fprintf(rev->diffopt.file, "%s\n", sb.buf);
 
        strbuf_release(&sb);
@@ -1334,6 +1380,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
        log.in2 = 4;
        log.file = rev->diffopt.file;
        log.groups = SHORTLOG_GROUP_AUTHOR;
+       shortlog_finish_setup(&log);
        for (i = 0; i < nr; i++)
                shortlog_add_commit(&log, list[i]);
 
@@ -1363,7 +1410,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
                        .other_arg = &other_arg
                };
 
-               diff_setup(&opts);
+               repo_diff_setup(the_repository, &opts);
                opts.file = rev->diffopt.file;
                opts.use_color = rev->diffopt.use_color;
                diff_setup_done(&opts);
@@ -1374,7 +1421,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
        }
 }
 
-static const char *clean_message_id(const char *msg_id)
+static char *clean_message_id(const char *msg_id)
 {
        char ch;
        const char *a, *z, *m;
@@ -1392,7 +1439,7 @@ static const char *clean_message_id(const char *msg_id)
        if (!z)
                die(_("insane in-reply-to: %s"), msg_id);
        if (++z == m)
-               return a;
+               return xstrdup(a);
        return xmemdupz(a, z - a);
 }
 
@@ -1437,19 +1484,16 @@ static int subject_prefix = 0;
 static int subject_prefix_callback(const struct option *opt, const char *arg,
                            int unset)
 {
+       struct strbuf *sprefix;
+
        BUG_ON_OPT_NEG(unset);
+       sprefix = opt->value;
        subject_prefix = 1;
-       ((struct rev_info *)opt->value)->subject_prefix = arg;
+       strbuf_reset(sprefix);
+       strbuf_addstr(sprefix, arg);
        return 0;
 }
 
-static int rfc_callback(const struct option *opt, const char *arg, int unset)
-{
-       BUG_ON_OPT_NEG(unset);
-       BUG_ON_OPT_ARG(arg);
-       return subject_prefix_callback(opt, "RFC PATCH", unset);
-}
-
 static int numbered_cmdline_opt = 0;
 
 static int numbered_callback(const struct option *opt, const char *arg,
@@ -1524,7 +1568,8 @@ static int inline_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
-static int header_callback(const struct option *opt, const char *arg, int unset)
+static int header_callback(const struct option *opt UNUSED, const char *arg,
+                          int unset)
 {
        if (unset) {
                string_list_clear(&extra_hdr, 0);
@@ -1536,24 +1581,6 @@ static int header_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
-static int to_callback(const struct option *opt, const char *arg, int unset)
-{
-       if (unset)
-               string_list_clear(&extra_to, 0);
-       else
-               string_list_append(&extra_to, arg);
-       return 0;
-}
-
-static int cc_callback(const struct option *opt, const char *arg, int unset)
-{
-       if (unset)
-               string_list_clear(&extra_cc, 0);
-       else
-               string_list_append(&extra_cc, arg);
-       return 0;
-}
-
 static int from_callback(const struct option *opt, const char *arg, int unset)
 {
        char **from = opt->value;
@@ -1635,14 +1662,16 @@ static struct commit *get_base_commit(const char *base_commit,
                        struct commit *commit;
                        struct object_id oid;
 
-                       if (get_oid(upstream, &oid)) {
+                       if (repo_get_oid(the_repository, upstream, &oid)) {
                                if (die_on_failure)
                                        die(_("failed to resolve '%s' as a valid ref"), upstream);
                                else
                                        return NULL;
                        }
                        commit = lookup_commit_or_die(&oid, "upstream base");
-                       base_list = get_merge_bases_many(commit, total, list);
+                       base_list = repo_get_merge_bases_many(the_repository,
+                                                             commit, total,
+                                                             list);
                        /* There should be one and only one merge base. */
                        if (!base_list || base_list->next) {
                                if (die_on_failure) {
@@ -1676,7 +1705,9 @@ static struct commit *get_base_commit(const char *base_commit,
        while (rev_nr > 1) {
                for (i = 0; i < rev_nr / 2; i++) {
                        struct commit_list *merge_base;
-                       merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
+                       merge_base = repo_get_merge_bases(the_repository,
+                                                         rev[2 * i],
+                                                         rev[2 * i + 1]);
                        if (!merge_base || merge_base->next) {
                                if (die_on_failure) {
                                        die(_("failed to find exact merge base"));
@@ -1694,7 +1725,7 @@ static struct commit *get_base_commit(const char *base_commit,
                rev_nr = DIV_ROUND_UP(rev_nr, 2);
        }
 
-       if (!in_merge_bases(base, rev[0])) {
+       if (!repo_in_merge_bases(the_repository, base, rev[0])) {
                if (die_on_failure) {
                        die(_("base commit should be the ancestor of revision list"));
                } else {
@@ -1763,7 +1794,7 @@ static void prepare_bases(struct base_tree_info *bases,
                struct object_id *patch_id;
                if (*commit_base_at(&commit_base, commit))
                        continue;
-               if (commit_patch_id(commit, &diffopt, &oid, 0, 1))
+               if (commit_patch_id(commit, &diffopt, &oid, 0))
                        die(_("cannot get patch id"));
                ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
                patch_id = bases->patch_id + bases->nr_patch_id;
@@ -1858,6 +1889,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        const char *reroll_count = NULL;
        char *cover_from_description_arg = NULL;
+       char *description_file = NULL;
        char *branch_name = NULL;
        char *base_commit = NULL;
        struct base_tree_info bases;
@@ -1870,7 +1902,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        struct strbuf rdiff1 = STRBUF_INIT;
        struct strbuf rdiff2 = STRBUF_INIT;
        struct strbuf rdiff_title = STRBUF_INIT;
+       struct strbuf sprefix = STRBUF_INIT;
        int creation_factor = -1;
+       int rfc = 0;
 
        const struct option builtin_format_patch_options[] = {
                OPT_CALLBACK_F('n', "numbered", &numbered, NULL,
@@ -1894,13 +1928,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            N_("mark the series as Nth re-roll")),
                OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
                            N_("max length of output filename")),
-               OPT_CALLBACK_F(0, "rfc", &rev, NULL,
-                           N_("use [RFC PATCH] instead of [PATCH]"),
-                           PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback),
+               OPT_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")),
                OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
                            N_("cover-from-description-mode"),
                            N_("generate parts of a cover letter based on a branch's description")),
-               OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"),
+               OPT_FILENAME(0, "description-file", &description_file,
+                            N_("use branch description from file")),
+               OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"),
                            N_("use [<prefix>] instead of [PATCH]"),
                            PARSE_OPT_NONEG, subject_prefix_callback),
                OPT_CALLBACK_F('o', "output-directory", &output_directory,
@@ -1921,8 +1955,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                OPT_GROUP(N_("Messaging")),
                OPT_CALLBACK(0, "add-header", NULL, N_("header"),
                            N_("add email header"), header_callback),
-               OPT_CALLBACK(0, "to", NULL, N_("email"), N_("add To: header"), to_callback),
-               OPT_CALLBACK(0, "cc", NULL, N_("email"), N_("add Cc: header"), cc_callback),
+               OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")),
+               OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")),
                OPT_CALLBACK_F(0, "from", &from, N_("ident"),
                            N_("set From address to <ident> (or committer ident if absent)"),
                            PARSE_OPT_OPTARG, from_callback),
@@ -1980,11 +2014,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.max_parents = 1;
        rev.diffopt.flags.recursive = 1;
        rev.diffopt.no_free = 1;
-       rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.def = "HEAD";
        s_r_opt.revarg_opt = REVARG_COMMITTISH;
 
+       strbuf_addstr(&sprefix, fmt_patch_subject_prefix);
+       if (format_no_prefix)
+               diff_set_noprefix(&rev.diffopt);
+
        if (default_attach) {
                rev.mime_boundary = default_attach;
                rev.no_inline = 1;
@@ -2009,15 +2046,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (cover_from_description_arg)
                cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-       if (reroll_count) {
-               struct strbuf sprefix = STRBUF_INIT;
+       if (rfc)
+               strbuf_insertstr(&sprefix, 0, "RFC ");
 
-               strbuf_addf(&sprefix, "%s v%s",
-                           rev.subject_prefix, reroll_count);
+       if (reroll_count) {
+               strbuf_addf(&sprefix, " v%s", reroll_count);
                rev.reroll_count = reroll_count;
-               rev.subject_prefix = strbuf_detach(&sprefix, NULL);
        }
 
+       rev.subject_prefix = sprefix.buf;
+
        for (i = 0; i < extra_hdr.nr; i++) {
                strbuf_addstr(&buf, extra_hdr.items[i].string);
                strbuf_addch(&buf, '\n');
@@ -2091,6 +2129,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        /* Always generate a patch */
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+       rev.always_show_header = 1;
 
        rev.zero_commit = zero_commit;
        rev.patch_name_max = fmt_patch_name_max;
@@ -2105,6 +2144,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                  rev.diffopt.close_file, "--output",
                                  !!output_directory, "--output-directory");
 
+       if (use_stdout && stdout_mboxrd)
+               rev.commit_format = CMIT_FMT_MBOXRD;
+
        if (use_stdout) {
                setup_pager();
        } else if (!rev.diffopt.close_file) {
@@ -2268,11 +2310,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        if (in_reply_to || thread || cover_letter) {
                rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
-               string_list_init_nodup(rev.ref_message_ids);
+               string_list_init_dup(rev.ref_message_ids);
        }
        if (in_reply_to) {
-               const char *msgid = clean_message_id(in_reply_to);
-               string_list_append(rev.ref_message_ids, msgid);
+               char *msgid = clean_message_id(in_reply_to);
+               string_list_append_nodup(rev.ref_message_ids, msgid);
        }
        rev.numbered_files = just_numbers;
        rev.patch_suffix = fmt_patch_suffix;
@@ -2280,7 +2322,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                if (thread)
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, !!output_directory,
-                                 origin, nr, list, branch_name, quiet);
+                                 origin, nr, list, description_file, branch_name, quiet);
                print_bases(&bases, rev.diffopt.file);
                print_signature(rev.diffopt.file);
                total++;
@@ -2328,8 +2370,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                    && (!cover_letter || rev.nr > 1))
                                        free(rev.message_id);
                                else
-                                       string_list_append(rev.ref_message_ids,
-                                                          rev.message_id);
+                                       string_list_append_nodup(rev.ref_message_ids,
+                                                                rev.message_id);
                        }
                        gen_message_id(&rev, oid_to_hex(&commit->object.oid));
                }
@@ -2376,7 +2418,9 @@ done:
        strbuf_release(&rdiff1);
        strbuf_release(&rdiff2);
        strbuf_release(&rdiff_title);
+       strbuf_release(&sprefix);
        free(to_free);
+       free(rev.message_id);
        if (rev.ref_message_ids)
                string_list_clear(rev.ref_message_ids, 0);
        free(rev.ref_message_ids);
@@ -2386,7 +2430,7 @@ done:
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
 {
        struct object_id oid;
-       if (get_oid(arg, &oid) == 0) {
+       if (repo_get_oid(the_repository, arg, &oid) == 0) {
                struct commit *commit = lookup_commit_reference(the_repository,
                                                                &oid);
                if (commit) {
@@ -2408,12 +2452,12 @@ static void print_commit(char sign, struct commit *commit, int verbose,
 {
        if (!verbose) {
                fprintf(file, "%c %s\n", sign,
-                      find_unique_abbrev(&commit->object.oid, abbrev));
+                      repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev));
        } else {
                struct strbuf buf = STRBUF_INIT;
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
                fprintf(file, "%c %s %s\n", sign,
-                      find_unique_abbrev(&commit->object.oid, abbrev),
+                      repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev),
                       buf.buf);
                strbuf_release(&buf);
        }
index 4cf8a2364835c9fc1ae39b8d52fd09a8aeeb368e..92f94e65bf065e900e59b9fcf2bc18f5af045975 100644 (file)
@@ -5,22 +5,27 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
+#include "builtin.h"
 #include "repository.h"
 #include "config.h"
+#include "convert.h"
 #include "quote.h"
 #include "dir.h"
-#include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "strbuf.h"
-#include "tree.h"
-#include "cache-tree.h"
 #include "parse-options.h"
 #include "resolve-undo.h"
 #include "string-list.h"
+#include "path.h"
 #include "pathspec.h"
-#include "run-command.h"
+#include "read-cache.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
+#include "object-store.h"
+#include "hex.h"
+
 
 static int abbrev;
 static int show_deleted;
@@ -89,12 +94,15 @@ static void write_name(const char *name)
 
 static void write_name_to_buf(struct strbuf *sb, const char *name)
 {
-       const char *rel = relative_path(name, prefix_len ? prefix : NULL, sb);
+       struct strbuf buf = STRBUF_INIT;
+       const char *rel = relative_path(name, prefix_len ? prefix : NULL, &buf);
 
        if (line_terminator)
                quote_c_style(rel, sb, NULL, 0);
        else
                strbuf_addstr(sb, rel);
+
+       strbuf_release(&buf);
 }
 
 static const char *get_tag(const struct cache_entry *ce, const char *tag)
@@ -234,68 +242,75 @@ static void show_submodule(struct repository *superproject,
        repo_clear(&subrepo);
 }
 
-struct show_index_data {
-       const char *pathname;
-       struct index_state *istate;
-       const struct cache_entry *ce;
-};
-
-static size_t expand_show_index(struct strbuf *sb, const char *start,
-                               void *context)
+static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
+                             const enum object_type type, unsigned int padded)
 {
-       struct show_index_data *data = context;
-       const char *end;
-       const char *p;
-       size_t len = strbuf_expand_literal_cb(sb, start, NULL);
-       struct stat st;
-
-       if (len)
-               return len;
-       if (*start != '(')
-               die(_("bad ls-files format: element '%s' "
-                     "does not start with '('"), start);
-
-       end = strchr(start + 1, ')');
-       if (!end)
-               die(_("bad ls-files format: element '%s' "
-                     "does not end in ')'"), start);
-
-       len = end - start + 1;
-       if (skip_prefix(start, "(objectmode)", &p))
-               strbuf_addf(sb, "%06o", data->ce->ce_mode);
-       else if (skip_prefix(start, "(objectname)", &p))
-               strbuf_add_unique_abbrev(sb, &data->ce->oid, abbrev);
-       else if (skip_prefix(start, "(stage)", &p))
-               strbuf_addf(sb, "%d", ce_stage(data->ce));
-       else if (skip_prefix(start, "(eolinfo:index)", &p))
-               strbuf_addstr(sb, S_ISREG(data->ce->ce_mode) ?
-                             get_cached_convert_stats_ascii(data->istate,
-                             data->ce->name) : "");
-       else if (skip_prefix(start, "(eolinfo:worktree)", &p))
-               strbuf_addstr(sb, !lstat(data->pathname, &st) &&
-                             S_ISREG(st.st_mode) ?
-                             get_wt_convert_stats_ascii(data->pathname) : "");
-       else if (skip_prefix(start, "(eolattr)", &p))
-               strbuf_addstr(sb, get_convert_attr_ascii(data->istate,
-                             data->pathname));
-       else if (skip_prefix(start, "(path)", &p))
-               write_name_to_buf(sb, data->pathname);
-       else
-               die(_("bad ls-files format: %%%.*s"), (int)len, start);
-
-       return len;
+       if (type == OBJ_BLOB) {
+               unsigned long size;
+               if (oid_object_info(the_repository, oid, &size) < 0)
+                       die(_("could not get object info about '%s'"),
+                           oid_to_hex(oid));
+               if (padded)
+                       strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size);
+               else
+                       strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size);
+       } else if (padded) {
+               strbuf_addf(line, "%7s", "-");
+       } else {
+               strbuf_addstr(line, "-");
+       }
 }
 
 static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
                        const char *format, const char *fullname) {
-       struct show_index_data data = {
-               .pathname = fullname,
-               .istate = repo->index,
-               .ce = ce,
-       };
        struct strbuf sb = STRBUF_INIT;
 
-       strbuf_expand(&sb, format, expand_show_index, &data);
+       while (strbuf_expand_step(&sb, &format)) {
+               const char *end;
+               size_t len;
+               struct stat st;
+
+               if (skip_prefix(format, "%", &format))
+                       strbuf_addch(&sb, '%');
+               else if ((len = strbuf_expand_literal(&sb, format)))
+                       format += len;
+               else if (*format != '(')
+                       die(_("bad ls-files format: element '%s' "
+                             "does not start with '('"), format);
+               else if (!(end = strchr(format + 1, ')')))
+                       die(_("bad ls-files format: element '%s' "
+                             "does not end in ')'"), format);
+               else if (skip_prefix(format, "(objectmode)", &format))
+                       strbuf_addf(&sb, "%06o", ce->ce_mode);
+               else if (skip_prefix(format, "(objectname)", &format))
+                       strbuf_add_unique_abbrev(&sb, &ce->oid, abbrev);
+               else if (skip_prefix(format, "(objecttype)", &format))
+                       strbuf_addstr(&sb, type_name(object_type(ce->ce_mode)));
+               else if (skip_prefix(format, "(objectsize:padded)", &format))
+                       expand_objectsize(&sb, &ce->oid,
+                                         object_type(ce->ce_mode), 1);
+               else if (skip_prefix(format, "(objectsize)", &format))
+                       expand_objectsize(&sb, &ce->oid,
+                                         object_type(ce->ce_mode), 0);
+               else if (skip_prefix(format, "(stage)", &format))
+                       strbuf_addf(&sb, "%d", ce_stage(ce));
+               else if (skip_prefix(format, "(eolinfo:index)", &format))
+                       strbuf_addstr(&sb, S_ISREG(ce->ce_mode) ?
+                                     get_cached_convert_stats_ascii(repo->index,
+                                     ce->name) : "");
+               else if (skip_prefix(format, "(eolinfo:worktree)", &format))
+                       strbuf_addstr(&sb, !lstat(fullname, &st) &&
+                                     S_ISREG(st.st_mode) ?
+                                     get_wt_convert_stats_ascii(fullname) : "");
+               else if (skip_prefix(format, "(eolattr)", &format))
+                       strbuf_addstr(&sb, get_convert_attr_ascii(repo->index,
+                                                                fullname));
+               else if (skip_prefix(format, "(path)", &format))
+                       write_name_to_buf(&sb, fullname);
+               else
+                       die(_("bad ls-files format: %%%.*s"),
+                           (int)(end - format + 1), format);
+       }
        strbuf_addch(&sb, line_terminator);
        fwrite(sb.buf, sb.len, 1, stdout);
        strbuf_release(&sb);
@@ -360,7 +375,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],
-                              find_unique_abbrev(&ui->oid[i], abbrev),
+                              repo_find_unique_abbrev(the_repository, &ui->oid[i], abbrev),
                               i + 1);
                        write_name(path);
                }
@@ -509,142 +524,6 @@ static int get_common_prefix_len(const char *common_prefix)
        return common_prefix_len;
 }
 
-static int read_one_entry_opt(struct index_state *istate,
-                             const struct object_id *oid,
-                             struct strbuf *base,
-                             const char *pathname,
-                             unsigned mode, int opt)
-{
-       int len;
-       struct cache_entry *ce;
-
-       if (S_ISDIR(mode))
-               return READ_TREE_RECURSIVE;
-
-       len = strlen(pathname);
-       ce = make_empty_cache_entry(istate, base->len + len);
-
-       ce->ce_mode = create_ce_mode(mode);
-       ce->ce_flags = create_ce_flags(1);
-       ce->ce_namelen = base->len + len;
-       memcpy(ce->name, base->buf, base->len);
-       memcpy(ce->name + base->len, pathname, len+1);
-       oidcpy(&ce->oid, oid);
-       return add_index_entry(istate, ce, opt);
-}
-
-static int read_one_entry(const struct object_id *oid, struct strbuf *base,
-                         const char *pathname, unsigned mode,
-                         void *context)
-{
-       struct index_state *istate = context;
-       return read_one_entry_opt(istate, oid, base, pathname,
-                                 mode,
-                                 ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
-}
-
-/*
- * This is used when the caller knows there is no existing entries at
- * the stage that will conflict with the entry being added.
- */
-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
-                               const char *pathname, unsigned mode,
-                               void *context)
-{
-       struct index_state *istate = context;
-       return read_one_entry_opt(istate, oid, base, pathname,
-                                 mode, ADD_CACHE_JUST_APPEND);
-}
-
-/*
- * Read the tree specified with --with-tree option
- * (typically, HEAD) into stage #1 and then
- * squash them down to stage #0.  This is used for
- * --error-unmatch to list and check the path patterns
- * that were given from the command line.  We are not
- * going to write this index out.
- */
-void overlay_tree_on_index(struct index_state *istate,
-                          const char *tree_name, const char *prefix)
-{
-       struct tree *tree;
-       struct object_id oid;
-       struct pathspec pathspec;
-       struct cache_entry *last_stage0 = NULL;
-       int i;
-       read_tree_fn_t fn = NULL;
-       int err;
-
-       if (get_oid(tree_name, &oid))
-               die("tree-ish %s not found.", tree_name);
-       tree = parse_tree_indirect(&oid);
-       if (!tree)
-               die("bad tree-ish %s", tree_name);
-
-       /* Hoist the unmerged entries up to stage #3 to make room */
-       /* TODO: audit for interaction with sparse-index. */
-       ensure_full_index(istate);
-       for (i = 0; i < istate->cache_nr; i++) {
-               struct cache_entry *ce = istate->cache[i];
-               if (!ce_stage(ce))
-                       continue;
-               ce->ce_flags |= CE_STAGEMASK;
-       }
-
-       if (prefix) {
-               static const char *(matchbuf[1]);
-               matchbuf[0] = NULL;
-               parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
-                              PATHSPEC_PREFER_CWD, prefix, matchbuf);
-       } else
-               memset(&pathspec, 0, sizeof(pathspec));
-
-       /*
-        * See if we have cache entry at the stage.  If so,
-        * do it the original slow way, otherwise, append and then
-        * sort at the end.
-        */
-       for (i = 0; !fn && i < istate->cache_nr; i++) {
-               const struct cache_entry *ce = istate->cache[i];
-               if (ce_stage(ce) == 1)
-                       fn = read_one_entry;
-       }
-
-       if (!fn)
-               fn = read_one_entry_quick;
-       err = read_tree(the_repository, tree, &pathspec, fn, istate);
-       if (err)
-               die("unable to read tree entries %s", tree_name);
-
-       /*
-        * Sort the cache entry -- we need to nuke the cache tree, though.
-        */
-       if (fn == read_one_entry_quick) {
-               cache_tree_free(&istate->cache_tree);
-               QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);
-       }
-
-       for (i = 0; i < istate->cache_nr; i++) {
-               struct cache_entry *ce = istate->cache[i];
-               switch (ce_stage(ce)) {
-               case 0:
-                       last_stage0 = ce;
-                       /* fallthru */
-               default:
-                       continue;
-               case 1:
-                       /*
-                        * If there is stage #0 entry for this, we do not
-                        * need to show it.  We use CE_UPDATE bit to mark
-                        * such an entry.
-                        */
-                       if (last_stage0 &&
-                           !strcmp(last_stage0->name, ce->name))
-                               ce->ce_flags |= CE_UPDATE;
-               }
-       }
-}
-
 static const char * const ls_files_usage[] = {
        N_("git ls-files [<options>] [<file>...]"),
        NULL
index df44e5cc0d1171334a846817ef19e6f98feca48f..e8d65ebbdc0afc864826d722a7da7514a4f382ea 100644 (file)
@@ -1,14 +1,17 @@
 #include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hex.h"
 #include "transport.h"
+#include "pkt-line.h"
 #include "ref-filter.h"
 #include "remote.h"
-#include "refs.h"
+#include "parse-options.h"
+#include "wildmatch.h"
 
 static const char * const ls_remote_usage[] = {
        N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-          "              [-q | --quiet] [--exit-code] [--get-url]\n"
-          "              [--symref] [<repository> [<refs>...]]"),
+          "              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+          "              [--symref] [<repository> [<patterns>...]]"),
        NULL
 };
 
@@ -54,6 +57,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        struct transport *transport;
        const struct ref *ref;
        struct ref_array ref_array;
+       struct ref_sorting *sorting;
        struct string_list sorting_options = STRING_LIST_INIT_DUP;
 
        struct option options[] = {
@@ -137,13 +141,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                item->symref = xstrdup_or_null(ref->symref);
        }
 
-       if (sorting_options.nr) {
-               struct ref_sorting *sorting;
-
-               sorting = ref_sorting_options(&sorting_options);
-               ref_array_sort(sorting, &ref_array);
-               ref_sorting_release(sorting);
-       }
+       sorting = ref_sorting_options(&sorting_options);
+       ref_array_sort(sorting, &ref_array);
 
        for (i = 0; i < ref_array.nr; i++) {
                const struct ref_array_item *ref = ref_array.items[i];
@@ -153,6 +152,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                status = 0; /* we found something */
        }
 
+       ref_sorting_release(sorting);
        ref_array_clear(&ref_array);
        if (transport_disconnect(transport))
                status = 1;
index c3ea09281afebe0c9aefb690f6f10044142a4d1a..e4a891337c3c619112af77bac014139b520e463f 100644 (file)
@@ -3,48 +3,23 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
-#include "object-store.h"
-#include "blob.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "tree.h"
-#include "commit.h"
+#include "path.h"
 #include "quote.h"
-#include "builtin.h"
 #include "parse-options.h"
 #include "pathspec.h"
 
-static int line_termination = '\n';
-#define LS_RECURSIVE 1
-#define LS_TREE_ONLY (1 << 1)
-#define LS_SHOW_TREES (1 << 2)
-static int abbrev;
-static int ls_options;
-static struct pathspec pathspec;
-static int chomp_prefix;
-static const char *ls_tree_prefix;
-static const char *format;
-struct show_tree_data {
-       unsigned mode;
-       enum object_type type;
-       const struct object_id *oid;
-       const char *pathname;
-       struct strbuf *base;
-};
-
-static const  char * const ls_tree_usage[] = {
+static const char * const ls_tree_usage[] = {
        N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
        NULL
 };
 
-static enum ls_tree_cmdmode {
-       MODE_DEFAULT = 0,
-       MODE_LONG,
-       MODE_NAME_ONLY,
-       MODE_NAME_STATUS,
-       MODE_OBJECT_ONLY,
-} cmdmode;
-
 static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
                              const enum object_type type, unsigned int padded)
 {
@@ -64,65 +39,32 @@ static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
        }
 }
 
-static size_t expand_show_tree(struct strbuf *sb, const char *start,
-                              void *context)
-{
-       struct show_tree_data *data = context;
-       const char *end;
-       const char *p;
-       unsigned int errlen;
-       size_t len = strbuf_expand_literal_cb(sb, start, NULL);
-
-       if (len)
-               return len;
-       if (*start != '(')
-               die(_("bad ls-tree format: element '%s' does not start with '('"), start);
-
-       end = strchr(start + 1, ')');
-       if (!end)
-               die(_("bad ls-tree format: element '%s' does not end in ')'"), start);
-
-       len = end - start + 1;
-       if (skip_prefix(start, "(objectmode)", &p)) {
-               strbuf_addf(sb, "%06o", data->mode);
-       } else if (skip_prefix(start, "(objecttype)", &p)) {
-               strbuf_addstr(sb, type_name(data->type));
-       } else if (skip_prefix(start, "(objectsize:padded)", &p)) {
-               expand_objectsize(sb, data->oid, data->type, 1);
-       } else if (skip_prefix(start, "(objectsize)", &p)) {
-               expand_objectsize(sb, data->oid, data->type, 0);
-       } else if (skip_prefix(start, "(objectname)", &p)) {
-               strbuf_add_unique_abbrev(sb, data->oid, abbrev);
-       } else if (skip_prefix(start, "(path)", &p)) {
-               const char *name = data->base->buf;
-               const char *prefix = chomp_prefix ? ls_tree_prefix : NULL;
-               struct strbuf quoted = STRBUF_INIT;
-               struct strbuf sbuf = STRBUF_INIT;
-               strbuf_addstr(data->base, data->pathname);
-               name = relative_path(data->base->buf, prefix, &sbuf);
-               quote_c_style(name, &quoted, NULL, 0);
-               strbuf_addbuf(sb, &quoted);
-               strbuf_release(&sbuf);
-               strbuf_release(&quoted);
-       } else {
-               errlen = (unsigned long)len;
-               die(_("bad ls-tree format: %%%.*s"), errlen, start);
-       }
-       return len;
-}
+struct ls_tree_options {
+       unsigned null_termination:1;
+       int abbrev;
+       enum ls_tree_path_options {
+               LS_RECURSIVE = 1 << 0,
+               LS_TREE_ONLY = 1 << 1,
+               LS_SHOW_TREES = 1 << 2,
+       } ls_options;
+       struct pathspec pathspec;
+       const char *prefix;
+       const char *format;
+};
 
-static int show_recursive(const char *base, size_t baselen, const char *pathname)
+static int show_recursive(struct ls_tree_options *options, const char *base,
+                         size_t baselen, const char *pathname)
 {
        int i;
 
-       if (ls_options & LS_RECURSIVE)
+       if (options->ls_options & LS_RECURSIVE)
                return 1;
 
-       if (!pathspec.nr)
+       if (!options->pathspec.nr)
                return 0;
 
-       for (i = 0; i < pathspec.nr; i++) {
-               const char *spec = pathspec.items[i].match;
+       for (i = 0; i < options->pathspec.nr; i++) {
+               const char *spec = options->pathspec.items[i].match;
                size_t len, speclen;
 
                if (strncmp(base, spec, baselen))
@@ -142,109 +84,146 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname
 }
 
 static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
-                        const char *pathname, unsigned mode, void *context UNUSED)
+                        const char *pathname, unsigned mode, void *context)
 {
-       size_t baselen;
+       struct ls_tree_options *options = context;
        int recurse = 0;
        struct strbuf sb = STRBUF_INIT;
        enum object_type type = object_type(mode);
+       const char *format = options->format;
 
-       struct show_tree_data data = {
-               .mode = mode,
-               .type = type,
-               .oid = oid,
-               .pathname = pathname,
-               .base = base,
-       };
-
-       if (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))
+       if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname))
                recurse = READ_TREE_RECURSIVE;
-       if (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))
+       if (type == OBJ_TREE && recurse && !(options->ls_options & LS_SHOW_TREES))
                return recurse;
-       if (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))
+       if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY))
                return 0;
 
-       baselen = base->len;
-       strbuf_expand(&sb, format, expand_show_tree, &data);
-       strbuf_addch(&sb, line_termination);
+       while (strbuf_expand_step(&sb, &format)) {
+               const char *end;
+               size_t len;
+
+               if (skip_prefix(format, "%", &format))
+                       strbuf_addch(&sb, '%');
+               else if ((len = strbuf_expand_literal(&sb, format)))
+                       format += len;
+               else if (*format != '(')
+                       die(_("bad ls-tree format: element '%s' "
+                             "does not start with '('"), format);
+               else if (!(end = strchr(format + 1, ')')))
+                       die(_("bad ls-tree format: element '%s' "
+                             "does not end in ')'"), format);
+               else if (skip_prefix(format, "(objectmode)", &format))
+                       strbuf_addf(&sb, "%06o", mode);
+               else if (skip_prefix(format, "(objecttype)", &format))
+                       strbuf_addstr(&sb, type_name(type));
+               else if (skip_prefix(format, "(objectsize:padded)", &format))
+                       expand_objectsize(&sb, oid, type, 1);
+               else if (skip_prefix(format, "(objectsize)", &format))
+                       expand_objectsize(&sb, oid, type, 0);
+               else if (skip_prefix(format, "(objectname)", &format))
+                       strbuf_add_unique_abbrev(&sb, oid, options->abbrev);
+               else if (skip_prefix(format, "(path)", &format)) {
+                       const char *name;
+                       const char *prefix = options->prefix;
+                       struct strbuf sbuf = STRBUF_INIT;
+                       size_t baselen = base->len;
+
+                       strbuf_addstr(base, pathname);
+                       name = relative_path(base->buf, prefix, &sbuf);
+                       quote_c_style(name, &sb, NULL, 0);
+                       strbuf_setlen(base, baselen);
+                       strbuf_release(&sbuf);
+               } else
+                       die(_("bad ls-tree format: %%%.*s"),
+                           (int)(end - format + 1), format);
+       }
+       strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
        fwrite(sb.buf, sb.len, 1, stdout);
        strbuf_release(&sb);
-       strbuf_setlen(base, baselen);
        return recurse;
 }
 
-static int show_tree_common(struct show_tree_data *data, int *recurse,
-                           const struct object_id *oid, struct strbuf *base,
-                           const char *pathname, unsigned mode)
+static int show_tree_common(struct ls_tree_options *options, int *recurse,
+                           struct strbuf *base, const char *pathname,
+                           enum object_type type)
 {
-       enum object_type type = object_type(mode);
        int ret = -1;
-
        *recurse = 0;
-       data->mode = mode;
-       data->type = type;
-       data->oid = oid;
-       data->pathname = pathname;
-       data->base = base;
 
        if (type == OBJ_BLOB) {
-               if (ls_options & LS_TREE_ONLY)
+               if (options->ls_options & LS_TREE_ONLY)
                        ret = 0;
        } else if (type == OBJ_TREE &&
-                  show_recursive(base->buf, base->len, pathname)) {
+                  show_recursive(options, base->buf, base->len, pathname)) {
                *recurse = READ_TREE_RECURSIVE;
-               if (!(ls_options & LS_SHOW_TREES))
+               if (!(options->ls_options & LS_SHOW_TREES))
                        ret = *recurse;
        }
 
        return ret;
 }
 
-static void show_tree_common_default_long(struct strbuf *base,
+static void show_tree_common_default_long(struct ls_tree_options *options,
+                                         struct strbuf *base,
                                          const char *pathname,
                                          const size_t baselen)
 {
+       const char *prefix = options->prefix;
+
        strbuf_addstr(base, pathname);
-       write_name_quoted_relative(base->buf,
-                                  chomp_prefix ? ls_tree_prefix : NULL, stdout,
-                                  line_termination);
+
+       if (options->null_termination) {
+               struct strbuf sb = STRBUF_INIT;
+               const char *name = relative_path(base->buf, prefix, &sb);
+
+               fputs(name, stdout);
+               fputc('\0', stdout);
+
+               strbuf_release(&sb);
+       } else {
+               write_name_quoted_relative(base->buf, prefix, stdout, '\n');
+       }
+
        strbuf_setlen(base, baselen);
 }
 
 static int show_tree_default(const struct object_id *oid, struct strbuf *base,
                             const char *pathname, unsigned mode,
-                            void *context UNUSED)
+                            void *context)
 {
+       struct ls_tree_options *options = context;
        int early;
        int recurse;
-       struct show_tree_data data = { 0 };
+       enum object_type type = object_type(mode);
 
-       early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+       early = show_tree_common(options, &recurse, base, pathname, type);
        if (early >= 0)
                return early;
 
-       printf("%06o %s %s\t", data.mode, type_name(data.type),
-              find_unique_abbrev(data.oid, abbrev));
-       show_tree_common_default_long(base, pathname, data.base->len);
+       printf("%06o %s %s\t", mode, type_name(object_type(mode)),
+              repo_find_unique_abbrev(the_repository, oid, options->abbrev));
+       show_tree_common_default_long(options, base, pathname, base->len);
        return recurse;
 }
 
 static int show_tree_long(const struct object_id *oid, struct strbuf *base,
                          const char *pathname, unsigned mode,
-                         void *context UNUSED)
+                         void *context)
 {
+       struct ls_tree_options *options = context;
        int early;
        int recurse;
-       struct show_tree_data data = { 0 };
        char size_text[24];
+       enum object_type type = object_type(mode);
 
-       early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+       early = show_tree_common(options, &recurse, base, pathname, type);
        if (early >= 0)
                return early;
 
-       if (data.type == OBJ_BLOB) {
+       if (type == OBJ_BLOB) {
                unsigned long size;
-               if (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)
+               if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
                        xsnprintf(size_text, sizeof(size_text), "BAD");
                else
                        xsnprintf(size_text, sizeof(size_text),
@@ -253,49 +232,78 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base,
                xsnprintf(size_text, sizeof(size_text), "-");
        }
 
-       printf("%06o %s %s %7s\t", data.mode, type_name(data.type),
-              find_unique_abbrev(data.oid, abbrev), size_text);
-       show_tree_common_default_long(base, pathname, data.base->len);
+       printf("%06o %s %s %7s\t", mode, type_name(type),
+              repo_find_unique_abbrev(the_repository, oid, options->abbrev),
+              size_text);
+       show_tree_common_default_long(options, base, pathname, base->len);
        return recurse;
 }
 
-static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,
+static int show_tree_name_only(const struct object_id *oid UNUSED,
+                              struct strbuf *base,
                               const char *pathname, unsigned mode,
-                              void *context UNUSED)
+                              void *context)
 {
+       struct ls_tree_options *options = context;
        int early;
        int recurse;
        const size_t baselen = base->len;
-       struct show_tree_data data = { 0 };
+       enum object_type type = object_type(mode);
+       const char *prefix;
 
-       early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+       early = show_tree_common(options, &recurse, base, pathname, type);
        if (early >= 0)
                return early;
 
+       prefix = options->prefix;
        strbuf_addstr(base, pathname);
-       write_name_quoted_relative(base->buf,
-                                  chomp_prefix ? ls_tree_prefix : NULL,
-                                  stdout, line_termination);
+       if (options->null_termination) {
+               struct strbuf sb = STRBUF_INIT;
+               const char *name = relative_path(base->buf, prefix, &sb);
+
+               fputs(name, stdout);
+               fputc('\0', stdout);
+
+               strbuf_release(&sb);
+       } else {
+               write_name_quoted_relative(base->buf, prefix, stdout, '\n');
+       }
        strbuf_setlen(base, baselen);
        return recurse;
 }
 
 static int show_tree_object(const struct object_id *oid, struct strbuf *base,
                            const char *pathname, unsigned mode,
-                           void *context UNUSED)
+                           void *context)
 {
+       struct ls_tree_options *options = context;
        int early;
        int recurse;
-       struct show_tree_data data = { 0 };
+       enum object_type type = object_type(mode);
+       const char *str;
 
-       early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+       early = show_tree_common(options, &recurse, base, pathname, type);
        if (early >= 0)
                return early;
 
-       printf("%s%c", find_unique_abbrev(oid, abbrev), line_termination);
+       str = repo_find_unique_abbrev(the_repository, oid, options->abbrev);
+       if (options->null_termination) {
+               fputs(str, stdout);
+               fputc('\0', stdout);
+       } else  {
+               puts(str);
+       }
        return recurse;
 }
 
+enum ls_tree_cmdmode {
+       MODE_DEFAULT = 0,
+       MODE_LONG,
+       MODE_NAME_ONLY,
+       MODE_NAME_STATUS,
+       MODE_OBJECT_ONLY,
+};
+
 struct ls_tree_cmdmode_to_fmt {
        enum ls_tree_cmdmode mode;
        const char *const fmt;
@@ -334,16 +342,20 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
        struct object_id oid;
        struct tree *tree;
        int i, full_tree = 0;
+       int full_name = !prefix || !*prefix;
        read_tree_fn_t fn = NULL;
+       enum ls_tree_cmdmode cmdmode = MODE_DEFAULT;
+       int null_termination = 0;
+       struct ls_tree_options options = { 0 };
        const struct option ls_tree_options[] = {
-               OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
+               OPT_BIT('d', NULL, &options.ls_options, N_("only show trees"),
                        LS_TREE_ONLY),
-               OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
+               OPT_BIT('r', NULL, &options.ls_options, N_("recurse into subtrees"),
                        LS_RECURSIVE),
-               OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
+               OPT_BIT('t', NULL, &options.ls_options, N_("show trees when recursing"),
                        LS_SHOW_TREES),
-               OPT_SET_INT('z', NULL, &line_termination,
-                           N_("terminate entries with NUL byte"), 0),
+               OPT_BOOL('z', NULL, &null_termination,
+                        N_("terminate entries with NUL byte")),
                OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"),
                            MODE_LONG),
                OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"),
@@ -352,30 +364,29 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                            MODE_NAME_STATUS),
                OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
                            MODE_OBJECT_ONLY),
-               OPT_SET_INT(0, "full-name", &chomp_prefix,
-                           N_("use full path names"), 0),
+               OPT_BOOL(0, "full-name", &full_name, N_("use full path names")),
                OPT_BOOL(0, "full-tree", &full_tree,
                         N_("list entire tree; not just current directory "
                            "(implies --full-name)")),
-               OPT_STRING_F(0, "format", &format, N_("format"),
+               OPT_STRING_F(0, "format", &options.format, N_("format"),
                                         N_("format to use for the output"),
                                         PARSE_OPT_NONEG),
-               OPT__ABBREV(&abbrev),
+               OPT__ABBREV(&options.abbrev),
                OPT_END()
        };
        struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
+       int ret;
 
        git_config(git_default_config, NULL);
-       ls_tree_prefix = prefix;
-       if (prefix)
-               chomp_prefix = strlen(prefix);
 
        argc = parse_options(argc, argv, prefix, ls_tree_options,
                             ls_tree_usage, 0);
-       if (full_tree) {
-               ls_tree_prefix = prefix = NULL;
-               chomp_prefix = 0;
-       }
+       options.null_termination = null_termination;
+
+       if (full_tree)
+               prefix = NULL;
+       options.prefix = full_name ? NULL : prefix;
+
        /*
         * We wanted to detect conflicts between --name-only and
         * --name-status, but once we're done with that subsequent
@@ -386,16 +397,16 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 
        /* -d -r should imply -t, but -d by itself should not have to. */
        if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
-           ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
-               ls_options |= LS_SHOW_TREES;
+           ((LS_TREE_ONLY|LS_RECURSIVE) & options.ls_options))
+               options.ls_options |= LS_SHOW_TREES;
 
-       if (format && cmdmode)
+       if (options.format && cmdmode)
                usage_msg_opt(
                        _("--format can't be combined with other format-altering options"),
                        ls_tree_usage, ls_tree_options);
        if (argc < 1)
                usage_with_options(ls_tree_usage, ls_tree_options);
-       if (get_oid(argv[0], &oid))
+       if (repo_get_oid(the_repository, argv[0], &oid))
                die("Not a valid object name %s", argv[0]);
 
        /*
@@ -404,13 +415,13 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
         * cannot be lifted until it is converted to use
         * match_pathspec() or tree_entry_interesting()
         */
-       parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
-                                 ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
+       parse_pathspec(&options.pathspec, PATHSPEC_ALL_MAGIC &
+                      ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
                       PATHSPEC_PREFER_CWD,
                       prefix, argv + 1);
-       for (i = 0; i < pathspec.nr; i++)
-               pathspec.items[i].nowildcard_len = pathspec.items[i].len;
-       pathspec.has_wildcard = 0;
+       for (i = 0; i < options.pathspec.nr; i++)
+               options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len;
+       options.pathspec.has_wildcard = 0;
        tree = parse_tree_indirect(&oid);
        if (!tree)
                die("not a tree object");
@@ -420,11 +431,11 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
         */
        while (m2f) {
                if (!m2f->fmt) {
-                       fn = format ? show_tree_fmt : show_tree_default;
-               } else if (format && !strcmp(format, m2f->fmt)) {
+                       fn = options.format ? show_tree_fmt : show_tree_default;
+               } else if (options.format && !strcmp(options.format, m2f->fmt)) {
                        cmdmode = m2f->mode;
                        fn = m2f->fn;
-               } else if (!format && cmdmode == m2f->mode) {
+               } else if (!options.format && cmdmode == m2f->mode) {
                        fn = m2f->fn;
                } else {
                        m2f++;
@@ -433,5 +444,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                break;
        }
 
-       return !!read_tree(the_repository, tree, &pathspec, fn, NULL);
+       ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options);
+       clear_pathspec(&options.pathspec);
+       return ret;
 }
index 01d16ef9e5a2d696130eab00fd1aaa58116599d3..53a22645da58b8c18bb825ca9012e2f072cbf3a3 100644 (file)
@@ -2,9 +2,10 @@
  * Another stupid program, this one parsing the headers of an
  * email to figure out authorship and subject
  */
-#include "cache.h"
 #include "builtin.h"
-#include "utf8.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
 #include "strbuf.h"
 #include "mailinfo.h"
 #include "parse-options.h"
index 73509f651bda4805e8399d75eda80ec8976bda07..3af9ddb8ae5c7e902937ada927590d908c193e0f 100644 (file)
@@ -4,8 +4,8 @@
  * It just splits a mbox into a list of files: "0001" "0002" ..
  * so you can process them further from there.
  */
-#include "cache.h"
 #include "builtin.h"
+#include "gettext.h"
 #include "string-list.h"
 #include "strbuf.h"
 
@@ -277,6 +277,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
        const char **argp;
        static const char *stdin_only[] = { "-", NULL };
 
+       BUG_ON_NON_EMPTY_PREFIX(prefix);
+
        for (argp = argv+1; *argp; argp++) {
                const char *arg = *argp;
 
index a11f8c6e4bb04a25a8763ba9930ab86079a1f14c..d26e8fbf6f75d9971762afdc0ea5c04e209db387 100644 (file)
@@ -1,10 +1,9 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "commit.h"
-#include "refs.h"
-#include "diff.h"
-#include "revision.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "repository.h"
 #include "commit-reach.h"
@@ -13,7 +12,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
        struct commit_list *result, *r;
 
-       result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1);
+       result = repo_get_merge_bases_many_dirty(the_repository, rev[0],
+                                                rev_nr - 1, rev + 1);
 
        if (!result)
                return 1;
@@ -31,8 +31,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 static const char * const merge_base_usage[] = {
        N_("git merge-base [-a | --all] <commit> <commit>..."),
        N_("git merge-base [-a | --all] --octopus <commit>..."),
-       N_("git merge-base --independent <commit>..."),
        N_("git merge-base --is-ancestor <commit> <commit>"),
+       N_("git merge-base --independent <commit>..."),
        N_("git merge-base --fork-point <ref> [<commit>]"),
        NULL
 };
@@ -42,7 +42,7 @@ static struct commit *get_commit_reference(const char *arg)
        struct object_id revkey;
        struct commit *r;
 
-       if (get_oid(arg, &revkey))
+       if (repo_get_oid(the_repository, arg, &revkey))
                die("Not a valid object name %s", arg);
        r = lookup_commit_reference(the_repository, &revkey);
        if (!r)
@@ -105,7 +105,7 @@ static int handle_is_ancestor(int argc, const char **argv)
                die("--is-ancestor takes exactly two commits");
        one = get_commit_reference(argv[0]);
        two = get_commit_reference(argv[1]);
-       if (in_merge_bases(one, two))
+       if (repo_in_merge_bases(the_repository, one, two))
                return 0;
        else
                return 1;
@@ -118,7 +118,7 @@ static int handle_fork_point(int argc, const char **argv)
        const char *commitname;
 
        commitname = (argc == 2) ? argv[1] : "HEAD";
-       if (get_oid(commitname, &oid))
+       if (repo_get_oid(the_repository, commitname, &oid))
                die("Not a valid object name: '%s'", commitname);
 
        derived = lookup_commit_reference(the_repository, &oid);
index c923bbf2abbdfa9d8a5461fcf5c0402a341a061b..1f987334a31b0e19904825297c1bb0d37a1fc2fe 100644 (file)
@@ -1,6 +1,12 @@
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
+#include "diff.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store.h"
 #include "config.h"
+#include "gettext.h"
+#include "setup.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 #include "parse-options.h"
@@ -23,16 +29,41 @@ static int label_cb(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int set_diff_algorithm(xpparam_t *xpp,
+                             const char *alg)
+{
+       long diff_algorithm = parse_algorithm_value(alg);
+       if (diff_algorithm < 0)
+               return -1;
+       xpp->flags = (xpp->flags & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
+       return 0;
+}
+
+static int diff_algorithm_cb(const struct option *opt,
+                               const char *arg, int unset)
+{
+       xpparam_t *xpp = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+
+       if (set_diff_algorithm(xpp, arg))
+               return error(_("option diff-algorithm accepts \"myers\", "
+                              "\"minimal\", \"patience\" and \"histogram\""));
+
+       return 0;
+}
+
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
        const char *names[3] = { 0 };
        mmfile_t mmfs[3] = { 0 };
        mmbuffer_t result = { 0 };
        xmparam_t xmp = { 0 };
-       int ret = 0, i = 0, to_stdout = 0;
+       int ret = 0, i = 0, to_stdout = 0, object_id = 0;
        int quiet = 0;
        struct option options[] = {
                OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
+               OPT_BOOL(0,   "object-id", &object_id, N_("use object IDs instead of filenames")),
                OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
                OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"),
                                XDL_MERGE_ZEALOUS_DIFF3),
@@ -42,6 +73,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                            XDL_MERGE_FAVOR_THEIRS),
                OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
                            XDL_MERGE_FAVOR_UNION),
+               OPT_CALLBACK_F(0, "diff-algorithm", &xmp.xpp, N_("<algorithm>"),
+                            N_("choose a diff algorithm"),
+                            PARSE_OPT_NONEG, diff_algorithm_cb),
                OPT_INTEGER(0, "marker-size", &xmp.marker_size,
                            N_("for conflicts, use this marker size")),
                OPT__QUIET(&quiet, N_("do not warn about conflicts")),
@@ -69,8 +103,12 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                        return error_errno("failed to redirect stderr to /dev/null");
        }
 
+       if (object_id)
+               setup_git_directory();
+
        for (i = 0; i < 3; i++) {
                char *fname;
+               struct object_id oid;
                mmfile_t *mmf = mmfs + i;
 
                if (!names[i])
@@ -78,12 +116,22 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 
                fname = prefix_filename(prefix, argv[i]);
 
-               if (read_mmfile(mmf, fname))
+               if (object_id) {
+                       if (repo_get_oid(the_repository, argv[i], &oid))
+                               ret = error(_("object '%s' does not exist"),
+                                             argv[i]);
+                       else if (!oideq(&oid, the_hash_algo->empty_blob))
+                               read_mmblob(mmf, &oid);
+                       else
+                               read_mmfile(mmf, "/dev/null");
+               } else if (read_mmfile(mmf, fname)) {
                        ret = -1;
-               else if (mmf->size > MAX_XDIFF_SIZE ||
-                        buffer_is_binary(mmf->ptr, mmf->size))
+               }
+               if (ret != -1 && (mmf->size > MAX_XDIFF_SIZE ||
+                   buffer_is_binary(mmf->ptr, mmf->size))) {
                        ret = error("Cannot merge binary files: %s",
                                    argv[i]);
+               }
 
                free(fname);
                if (ret)
@@ -97,20 +145,32 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
 
        if (ret >= 0) {
-               const char *filename = argv[0];
-               char *fpath = prefix_filename(prefix, argv[0]);
-               FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
-
-               if (!f)
-                       ret = error_errno("Could not open %s for writing",
-                                         filename);
-               else if (result.size &&
-                        fwrite(result.ptr, result.size, 1, f) != 1)
-                       ret = error_errno("Could not write to %s", filename);
-               else if (fclose(f))
-                       ret = error_errno("Could not close %s", filename);
+               if (object_id && !to_stdout) {
+                       struct object_id oid;
+                       if (result.size) {
+                               if (write_object_file(result.ptr, result.size, OBJ_BLOB, &oid) < 0)
+                                       ret = error(_("Could not write object file"));
+                       } else {
+                               oidcpy(&oid, the_hash_algo->empty_blob);
+                       }
+                       if (ret >= 0)
+                               printf("%s\n", oid_to_hex(&oid));
+               } else {
+                       const char *filename = argv[0];
+                       char *fpath = prefix_filename(prefix, argv[0]);
+                       FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
+
+                       if (!f)
+                               ret = error_errno("Could not open %s for writing",
+                                                 filename);
+                       else if (result.size &&
+                                fwrite(result.ptr, result.size, 1, f) != 1)
+                               ret = error_errno("Could not write to %s", filename);
+                       else if (fclose(f))
+                               ret = error_errno("Could not close %s", filename);
+                       free(fpath);
+               }
                free(result.ptr);
-               free(fpath);
        }
 
        if (ret > 127)
index c0383fe9df9a3eb2e34f8c5869f996ebc81fce2e..270d5f644ac22e1a8a1b43b282b9ebf0f5cb12b0 100644 (file)
@@ -1,6 +1,10 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
 #include "run-command.h"
+#include "sparse-index.h"
 
 static const char *pgm;
 static int one_shot, quiet;
@@ -12,12 +16,13 @@ static int merge_entry(int pos, const char *path)
        const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
        char hexbuf[4][GIT_MAX_HEXSZ + 1];
        char ownbuf[4][60];
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
-       if (pos >= active_nr)
+       if (pos >= the_index.cache_nr)
                die("git merge-index: %s not in the cache", path);
        found = 0;
        do {
-               const struct cache_entry *ce = active_cache[pos];
+               const struct cache_entry *ce = the_index.cache[pos];
                int stage = ce_stage(ce);
 
                if (strcmp(ce->name, path))
@@ -27,11 +32,12 @@ static int merge_entry(int pos, const char *path)
                xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
                arguments[stage] = hexbuf[stage];
                arguments[stage + 4] = ownbuf[stage];
-       } while (++pos < active_nr);
+       } while (++pos < the_index.cache_nr);
        if (!found)
                die("git merge-index: %s not in the cache", path);
 
-       if (run_command_v_opt(arguments, 0)) {
+       strvec_pushv(&cmd.args, arguments);
+       if (run_command(&cmd)) {
                if (one_shot)
                        err++;
                else {
@@ -45,7 +51,7 @@ static int merge_entry(int pos, const char *path)
 
 static void merge_one_path(const char *path)
 {
-       int pos = cache_name_pos(path, strlen(path));
+       int pos = index_name_pos(&the_index, path, strlen(path));
 
        /*
         * If it already exists in the cache as stage0, it's
@@ -60,15 +66,15 @@ static void merge_all(void)
        int i;
        /* TODO: audit for interaction with sparse-index. */
        ensure_full_index(&the_index);
-       for (i = 0; i < active_nr; i++) {
-               const struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < the_index.cache_nr; i++) {
+               const struct cache_entry *ce = the_index.cache[i];
                if (!ce_stage(ce))
                        continue;
                i += merge_entry(i, ce->name)-1;
        }
 }
 
-int cmd_merge_index(int argc, const char **argv, const char *prefix)
+int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED)
 {
        int i, force_file = 0;
 
@@ -80,7 +86,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix)
        if (argc < 3)
                usage("git merge-index [-o] [-q] <merge-program> (-a | [--] [<filename>...])");
 
-       read_cache();
+       repo_read_index(the_repository);
 
        /* TODO: audit for interaction with sparse-index. */
        ensure_full_index(&the_index);
index 3583cff71c7c7a6d1e78fc7e3a6f902015f0741f..932924e5d0e573959e2d8c70168f77224de45bc8 100644 (file)
@@ -7,15 +7,15 @@
  *
  * Pretend we resolved the heads, but declare our tree trumps everybody else.
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "git-compat-util.h"
 #include "builtin.h"
 #include "diff.h"
+#include "repository.h"
 
 static const char builtin_merge_ours_usage[] =
        "git merge-ours <base>... -- HEAD <remote>...";
 
-int cmd_merge_ours(int argc, const char **argv, const char *prefix)
+int cmd_merge_ours(int argc, const char **argv, const char *prefix UNUSED)
 {
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(builtin_merge_ours_usage);
@@ -25,7 +25,7 @@ int cmd_merge_ours(int argc, const char **argv, const char *prefix)
         * commit.  The index must match HEAD, or this merge cannot go
         * through.
         */
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                die_errno("read_cache failed");
        if (index_differs_from(the_repository, "HEAD", NULL, 0))
                return 2;
index b9acbf5d3427e7dd88533351db5d2fc245cc25f6..c2ce044a201366d7eea11ab3d2eeed2c15113af4 100644 (file)
@@ -1,9 +1,10 @@
-#include "cache.h"
 #include "builtin.h"
-#include "commit.h"
-#include "tag.h"
+#include "advice.h"
+#include "gettext.h"
+#include "hash.h"
 #include "merge-recursive.h"
-#include "xdiff-interface.h"
+#include "object-name.h"
+#include "repository.h"
 
 static const char builtin_merge_recursive_usage[] =
        "git %s <base>... -- <head> <remote> ...";
@@ -20,7 +21,7 @@ static char *better_branch_name(const char *branch)
        return xstrdup(name ? name : branch);
 }
 
-int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED)
 {
        const struct object_id *bases[21];
        unsigned bases_count = 0;
@@ -49,7 +50,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                }
                if (bases_count < ARRAY_SIZE(bases)-1) {
                        struct object_id *oid = xmalloc(sizeof(struct object_id));
-                       if (get_oid(argv[i], oid))
+                       if (repo_get_oid(the_repository, argv[i], oid))
                                die(_("could not parse object '%s'"), argv[i]);
                        bases[bases_count++] = oid;
                }
@@ -70,9 +71,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        o.branch1 = argv[++i];
        o.branch2 = argv[++i];
 
-       if (get_oid(o.branch1, &h1))
+       if (repo_get_oid(the_repository, o.branch1, &h1))
                die(_("could not resolve ref '%s'"), o.branch1);
-       if (get_oid(o.branch2, &h2))
+       if (repo_get_oid(the_repository, o.branch2, &h2))
                die(_("could not resolve ref '%s'"), o.branch2);
 
        o.branch1 = better1 = better_branch_name(o.branch1);
index ae5782917b96c57917b60b7e192f74f18e77794c..3bdec53fbe58bb5edef6e4d425829a58ce87bffe 100644 (file)
@@ -1,17 +1,23 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "tree-walk.h"
 #include "xdiff-interface.h"
 #include "help.h"
+#include "gettext.h"
+#include "hex.h"
+#include "commit.h"
 #include "commit-reach.h"
 #include "merge-ort.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "parse-options.h"
 #include "repository.h"
 #include "blob.h"
-#include "exec-cmd.h"
 #include "merge-blobs.h"
 #include "quote.h"
+#include "tree.h"
+#include "config.h"
+#include "strvec.h"
 
 static int line_termination = '\n';
 
@@ -68,7 +74,9 @@ static void *result(struct merge_list *entry, unsigned long *size)
        const char *path = entry->path;
 
        if (!entry->stage)
-               return read_object_file(&entry->blob->object.oid, &type, size);
+               return repo_read_object_file(the_repository,
+                                            &entry->blob->object.oid, &type,
+                                            size);
        base = NULL;
        if (entry->stage == 1) {
                base = entry->blob;
@@ -91,14 +99,15 @@ static void *origin(struct merge_list *entry, unsigned long *size)
        enum object_type type;
        while (entry) {
                if (entry->stage == 2)
-                       return read_object_file(&entry->blob->object.oid,
-                                               &type, size);
+                       return repo_read_object_file(the_repository,
+                                                    &entry->blob->object.oid,
+                                                    &type, size);
                entry = entry->link;
        }
        return NULL;
 }
 
-static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+static int show_outf(void *priv UNUSED, mmbuffer_t *mb, int nbuf)
 {
        int i;
        for (i = 0; i < nbuf; i++)
@@ -315,7 +324,9 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])
  * The successful merge rules are the same as for the three-way merge
  * in git-read-tree.
  */
-static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
+static int threeway_callback(int n UNUSED, unsigned long mask,
+                            unsigned long dirmask UNUSED,
+                            struct name_entry *entry, struct traverse_info *info)
 {
        /* Same in both? */
        if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) {
@@ -402,17 +413,22 @@ struct merge_tree_options {
        int allow_unrelated_histories;
        int show_messages;
        int name_only;
+       int use_stdin;
+       struct merge_options merge_options;
 };
 
 static int real_merge(struct merge_tree_options *o,
+                     const char *merge_base,
                      const char *branch1, const char *branch2,
                      const char *prefix)
 {
        struct commit *parent1, *parent2;
        struct commit_list *merge_bases = NULL;
-       struct merge_options opt;
        struct merge_result result = { 0 };
+       int show_messages = o->show_messages;
+       struct merge_options opt;
 
+       copy_merge_options(&opt, &o->merge_options);
        parent1 = get_merge_parent(branch1);
        if (!parent1)
                help_unknown_ref(branch1, "merge-tree",
@@ -423,29 +439,45 @@ static int real_merge(struct merge_tree_options *o,
                help_unknown_ref(branch2, "merge-tree",
                                 _("not something we can merge"));
 
-       init_merge_options(&opt, the_repository);
-
        opt.show_rename_progress = 0;
 
        opt.branch1 = branch1;
        opt.branch2 = branch2;
 
-       /*
-        * Get the merge bases, in reverse order; see comment above
-        * merge_incore_recursive in merge-ort.h
-        */
-       merge_bases = get_merge_bases(parent1, parent2);
-       if (!merge_bases && !o->allow_unrelated_histories)
-               die(_("refusing to merge unrelated histories"));
-       merge_bases = reverse_commit_list(merge_bases);
+       if (merge_base) {
+               struct commit *base_commit;
+               struct tree *base_tree, *parent1_tree, *parent2_tree;
+
+               base_commit = lookup_commit_reference_by_name(merge_base);
+               if (!base_commit)
+                       die(_("could not lookup commit '%s'"), merge_base);
+
+               opt.ancestor = merge_base;
+               base_tree = repo_get_commit_tree(the_repository, base_commit);
+               parent1_tree = repo_get_commit_tree(the_repository, parent1);
+               parent2_tree = repo_get_commit_tree(the_repository, parent2);
+               merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
+       } else {
+               /*
+                * Get the merge bases, in reverse order; see comment above
+                * merge_incore_recursive in merge-ort.h
+                */
+               merge_bases = repo_get_merge_bases(the_repository, parent1,
+                                                  parent2);
+               if (!merge_bases && !o->allow_unrelated_histories)
+                       die(_("refusing to merge unrelated histories"));
+               merge_bases = reverse_commit_list(merge_bases);
+               merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
+       }
 
-       merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
        if (result.clean < 0)
                die(_("failure to merge"));
 
-       if (o->show_messages == -1)
-               o->show_messages = !result.clean;
+       if (show_messages == -1)
+               show_messages = !result.clean;
 
+       if (o->use_stdin)
+               printf("%d%c", result.clean, line_termination);
        printf("%s%c", oid_to_hex(&result.tree->object.oid), line_termination);
        if (!result.clean) {
                struct string_list conflicted_files = STRING_LIST_INIT_NODUP;
@@ -467,20 +499,25 @@ static int real_merge(struct merge_tree_options *o,
                }
                string_list_clear(&conflicted_files, 1);
        }
-       if (o->show_messages) {
+       if (show_messages) {
                putchar(line_termination);
                merge_display_update_messages(&opt, line_termination == '\0',
                                              &result);
        }
+       if (o->use_stdin)
+               putchar(line_termination);
        merge_finalize(&opt, &result);
+       clear_merge_options(&opt);
        return !result.clean; /* result.clean < 0 handled above */
 }
 
 int cmd_merge_tree(int argc, const char **argv, const char *prefix)
 {
        struct merge_tree_options o = { .show_messages = -1 };
+       struct strvec xopts = STRVEC_INIT;
        int expected_remaining_argc;
        int original_argc;
+       const char *merge_base = NULL;
 
        const char * const merge_tree_usage[] = {
                N_("git merge-tree [--write-tree] [<options>] <branch1> <branch2>"),
@@ -505,13 +542,78 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
                           &o.allow_unrelated_histories,
                           N_("allow merging unrelated histories"),
                           PARSE_OPT_NONEG),
+               OPT_BOOL_F(0, "stdin",
+                          &o.use_stdin,
+                          N_("perform multiple merges, one per line of input"),
+                          PARSE_OPT_NONEG),
+               OPT_STRING(0, "merge-base",
+                          &merge_base,
+                          N_("commit"),
+                          N_("specify a merge-base for the merge")),
+               OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
+                       N_("option for selected merge strategy")),
                OPT_END()
        };
 
+       /* Init merge options */
+       init_merge_options(&o.merge_options, the_repository);
+
        /* Parse arguments */
        original_argc = argc - 1; /* ignoring argv[0] */
        argc = parse_options(argc, argv, prefix, mt_options,
                             merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+
+       if (xopts.nr && o.mode == MODE_TRIVIAL)
+               die(_("--trivial-merge is incompatible with all other options"));
+       for (int x = 0; x < xopts.nr; x++)
+               if (parse_merge_opt(&o.merge_options, xopts.v[x]))
+                       die(_("unknown strategy option: -X%s"), xopts.v[x]);
+
+       /* Handle --stdin */
+       if (o.use_stdin) {
+               struct strbuf buf = STRBUF_INIT;
+
+               if (o.mode == MODE_TRIVIAL)
+                       die(_("--trivial-merge is incompatible with all other options"));
+               if (merge_base)
+                       die(_("options '%s' and '%s' cannot be used together"),
+                           "--merge-base", "--stdin");
+               line_termination = '\0';
+               while (strbuf_getline_lf(&buf, stdin) != EOF) {
+                       struct strbuf **split;
+                       int result;
+                       const char *input_merge_base = NULL;
+
+                       split = strbuf_split(&buf, ' ');
+                       if (!split[0] || !split[1])
+                               die(_("malformed input line: '%s'."), buf.buf);
+                       strbuf_rtrim(split[0]);
+                       strbuf_rtrim(split[1]);
+
+                       /* parse the merge-base */
+                       if (!strcmp(split[1]->buf, "--")) {
+                               input_merge_base = split[0]->buf;
+                       }
+
+                       if (input_merge_base && split[2] && split[3] && !split[4]) {
+                               strbuf_rtrim(split[2]);
+                               strbuf_rtrim(split[3]);
+                               result = real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix);
+                       } else if (!input_merge_base && !split[2]) {
+                               result = real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix);
+                       } else {
+                               die(_("malformed input line: '%s'."), buf.buf);
+                       }
+
+                       if (result < 0)
+                               die(_("merging cannot continue; got unclean result of %d"), result);
+                       strbuf_list_free(split);
+               }
+               strbuf_release(&buf);
+               return 0;
+       }
+
+       /* Figure out which mode to use */
        switch (o.mode) {
        default:
                BUG("unexpected command mode %d", o.mode);
@@ -543,9 +645,11 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
        if (argc != expected_remaining_argc)
                usage_with_options(merge_tree_usage, mt_options);
 
+       git_config(git_default_config, NULL);
+
        /* Do the relevant type of merge */
        if (o.mode == MODE_REAL)
-               return real_merge(&o, argv[0], argv[1], prefix);
+               return real_merge(&o, merge_base, argv[0], argv[1], prefix);
        else
                return trivial_merge(argv[0], argv[1], argv[2]);
 }
index 5900b81729d8da757d04613781d41f2e7621ebcd..ebbe05033e98bc796ceeb6c70e6259b8c76c18f2 100644 (file)
@@ -6,11 +6,17 @@
  * Based on git-merge.sh by Junio C Hamano.
  */
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
 #include "parse-options.h"
-#include "builtin.h"
 #include "lockfile.h"
 #include "run-command.h"
 #include "hook.h"
 #include "refspec.h"
 #include "commit.h"
 #include "diffcore.h"
+#include "path.h"
 #include "revision.h"
 #include "unpack-trees.h"
 #include "cache-tree.h"
 #include "dir.h"
-#include "utf8.h"
-#include "log-tree.h"
 #include "color.h"
 #include "rerere.h"
 #include "help.h"
+#include "merge.h"
 #include "merge-recursive.h"
 #include "merge-ort-wrappers.h"
 #include "resolve-undo.h"
 #include "remote.h"
 #include "fmt-merge-msg.h"
-#include "gpg-interface.h"
 #include "sequencer.h"
 #include "string-list.h"
-#include "packfile.h"
 #include "tag.h"
 #include "alias.h"
 #include "branch.h"
@@ -71,8 +75,7 @@ static int overwrite_ignore = 1;
 static struct strbuf merge_msg = STRBUF_INIT;
 static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
-static const char **xopts;
-static size_t xopts_nr, xopts_alloc;
+static struct strvec xopts = STRVEC_INIT;
 static const char *branch;
 static char *branch_mergeoptions;
 static int verbosity;
@@ -188,7 +191,7 @@ static struct strategy *get_strategy(const char *name)
                for (i = 0; i < main_cmds.cnt; i++) {
                        int j, found = 0;
                        struct cmdname *ent = main_cmds.names[i];
-                       for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+                       for (j = 0; !found && j < ARRAY_SIZE(all_strategy); j++)
                                if (!strncmp(ent->name, all_strategy[j].name, ent->len)
                                                && !all_strategy[j].name[ent->len])
                                        found = 1;
@@ -224,7 +227,7 @@ static void append_strategy(struct strategy *s)
        use_strategies[use_strategies_nr++] = s;
 }
 
-static int option_parse_strategy(const struct option *opt,
+static int option_parse_strategy(const struct option *opt UNUSED,
                                 const char *name, int unset)
 {
        if (unset)
@@ -234,29 +237,9 @@ static int option_parse_strategy(const struct option *opt,
        return 0;
 }
 
-static int option_parse_x(const struct option *opt,
-                         const char *arg, int unset)
-{
-       if (unset)
-               return 0;
-
-       ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
-       xopts[xopts_nr++] = xstrdup(arg);
-       return 0;
-}
-
-static int option_parse_n(const struct option *opt,
-                         const char *arg, int unset)
-{
-       BUG_ON_OPT_ARG(arg);
-       show_diffstat = unset;
-       return 0;
-}
-
 static struct option builtin_merge_options[] = {
-       OPT_CALLBACK_F('n', NULL, NULL, NULL,
-               N_("do not show a diffstat at the end of the merge"),
-               PARSE_OPT_NOARG, option_parse_n),
+       OPT_SET_INT('n', NULL, &show_diffstat,
+               N_("do not show a diffstat at the end of the merge"), 0),
        OPT_BOOL(0, "stat", &show_diffstat,
                N_("show a diffstat at the end of the merge")),
        OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
@@ -277,10 +260,10 @@ static struct option builtin_merge_options[] = {
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_BOOL(0, "verify-signatures", &verify_signatures,
                N_("verify that the named commit has a valid GPG signature")),
-       OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
+       OPT_CALLBACK('s', "strategy", NULL, N_("strategy"),
                N_("merge strategy to use"), option_parse_strategy),
-       OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
-               N_("option for selected merge strategy"), option_parse_x),
+       OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
+               N_("option for selected merge strategy")),
        OPT_CALLBACK('m', "message", &merge_msg, N_("message"),
                N_("merge commit message (for a non-fast-forward merge)"),
                option_parse_message),
@@ -318,7 +301,7 @@ static int save_state(struct object_id *stash)
        int rc = -1;
 
        fd = repo_hold_locked_index(the_repository, &lock_file, 0);
-       refresh_cache(REFRESH_QUIET);
+       refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
        if (0 <= fd)
                repo_update_index_if_able(the_repository, &lock_file);
        rollback_lock_file(&lock_file);
@@ -337,7 +320,7 @@ static int save_state(struct object_id *stash)
        else if (!len)          /* no changes */
                goto out;
        strbuf_setlen(&buffer, buffer.len-1);
-       if (get_oid(buffer.buf, stash))
+       if (repo_get_oid(the_repository, buffer.buf, stash))
                die(_("not a valid object: %s"), buffer.buf);
        rc = 0;
 out:
@@ -345,63 +328,53 @@ out:
        return rc;
 }
 
-static void read_empty(const struct object_id *oid, int verbose)
+static void read_empty(const struct object_id *oid)
 {
-       int i = 0;
-       const char *args[7];
-
-       args[i++] = "read-tree";
-       if (verbose)
-               args[i++] = "-v";
-       args[i++] = "-m";
-       args[i++] = "-u";
-       args[i++] = empty_tree_oid_hex();
-       args[i++] = oid_to_hex(oid);
-       args[i] = NULL;
+       struct child_process cmd = CHILD_PROCESS_INIT;
+
+       strvec_pushl(&cmd.args, "read-tree", "-m", "-u", empty_tree_oid_hex(),
+                    oid_to_hex(oid), NULL);
+       cmd.git_cmd = 1;
 
-       if (run_command_v_opt(args, RUN_GIT_CMD))
+       if (run_command(&cmd))
                die(_("read-tree failed"));
 }
 
-static void reset_hard(const struct object_id *oid, int verbose)
+static void reset_hard(const struct object_id *oid)
 {
-       int i = 0;
-       const char *args[6];
-
-       args[i++] = "read-tree";
-       if (verbose)
-               args[i++] = "-v";
-       args[i++] = "--reset";
-       args[i++] = "-u";
-       args[i++] = oid_to_hex(oid);
-       args[i] = NULL;
+       struct child_process cmd = CHILD_PROCESS_INIT;
+
+       strvec_pushl(&cmd.args, "read-tree", "-v", "--reset", "-u",
+                    oid_to_hex(oid), NULL);
+       cmd.git_cmd = 1;
 
-       if (run_command_v_opt(args, RUN_GIT_CMD))
+       if (run_command(&cmd))
                die(_("read-tree failed"));
 }
 
 static void restore_state(const struct object_id *head,
                          const struct object_id *stash)
 {
-       struct strvec args = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
-       reset_hard(head, 1);
+       reset_hard(head);
 
        if (is_null_oid(stash))
                goto refresh_cache;
 
-       strvec_pushl(&args, "stash", "apply", "--index", "--quiet", NULL);
-       strvec_push(&args, oid_to_hex(stash));
+       strvec_pushl(&cmd.args, "stash", "apply", "--index", "--quiet", NULL);
+       strvec_push(&cmd.args, oid_to_hex(stash));
 
        /*
         * It is OK to ignore error here, for example when there was
         * nothing to restore.
         */
-       run_command_v_opt(args.v, RUN_GIT_CMD);
-       strvec_clear(&args);
+       cmd.git_cmd = 1;
+       run_command(&cmd);
 
 refresh_cache:
-       if (discard_cache() < 0 || read_cache() < 0)
+       discard_index(&the_index);
+       if (repo_read_index(the_repository) < 0)
                die(_("could not read index"));
 }
 
@@ -489,8 +462,7 @@ static void finish(struct commit *head_commit,
        if (new_head && show_diffstat) {
                struct diff_options opts;
                repo_diff_setup(the_repository, &opts);
-               opts.stat_width = -1; /* use full terminal width */
-               opts.stat_graph_width = -1; /* respect statGraphWidth config */
+               init_diffstat_widths(&opts);
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
@@ -527,7 +499,8 @@ static void merge_name(const char *remote, struct strbuf *msg)
        if (!remote_head)
                die(_("'%s' does not point to a commit"), remote);
 
-       if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref, 0) > 0) {
+       if (repo_dwim_ref(the_repository, remote, strlen(remote), &branch_head,
+                         &found_ref, 0) > 0) {
                if (starts_with(found_ref, "refs/heads/")) {
                        strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
                                    oid_to_hex(&branch_head), remote);
@@ -623,7 +596,8 @@ static void parse_branch_merge_options(char *bmo)
        free(argv);
 }
 
-static int git_merge_config(const char *k, const char *v, void *cb)
+static int git_merge_config(const char *k, const char *v,
+                           const struct config_context *ctx, void *cb)
 {
        int status;
        const char *str;
@@ -668,13 +642,10 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                return 0;
        }
 
-       status = fmt_merge_msg_config(k, v, cb);
+       status = fmt_merge_msg_config(k, v, ctx, cb);
        if (status)
                return status;
-       status = git_gpg_config(k, v, NULL);
-       if (status)
-               return status;
-       return git_diff_ui_config(k, v, cb);
+       return git_diff_ui_config(k, v, ctx, cb);
 }
 
 static int read_tree_trivial(struct object_id *common, struct object_id *head,
@@ -704,7 +675,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
        if (!trees[nr_trees++])
                return -1;
        opts.fn = threeway_merge;
-       cache_tree_free(&active_cache_tree);
+       cache_tree_free(&the_index.cache_tree);
        for (i = 0; i < nr_trees; i++) {
                parse_tree(trees[i]);
                init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
@@ -716,7 +687,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 
 static void write_tree_trivial(struct object_id *oid)
 {
-       if (write_cache_as_tree(oid, 0, NULL))
+       if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
                die(_("git write-tree failed to write a tree"));
 }
 
@@ -726,7 +697,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 {
        const char *head_arg = "HEAD";
 
-       if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
+       if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET,
+                                        SKIP_IF_UNCHANGED, 0, NULL, NULL,
+                                        NULL) < 0)
                return error(_("Unable to write index."));
 
        if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree") ||
@@ -750,9 +723,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                o.show_rename_progress =
                        show_progress == -1 ? isatty(2) : show_progress;
 
-               for (x = 0; x < xopts_nr; x++)
-                       if (parse_merge_opt(&o, xopts[x]))
-                               die(_("unknown strategy option: -X%s"), xopts[x]);
+               for (x = 0; x < xopts.nr; x++)
+                       if (parse_merge_opt(&o, xopts.v[x]))
+                               die(_("unknown strategy option: -X%s"), xopts.v[x]);
 
                o.branch1 = head_arg;
                o.branch2 = merge_remote_util(remoteheads->item)->name;
@@ -760,7 +733,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
 
-               hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+               repo_hold_locked_index(the_repository, &lock,
+                                      LOCK_DIE_ON_ERROR);
                if (!strcmp(strategy, "ort"))
                        clean = merge_ort_recursive(&o, head, remoteheads->item,
                                                    reversed, &result);
@@ -777,13 +751,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                return clean ? 0 : 1;
        } else {
                return try_merge_command(the_repository,
-                                        strategy, xopts_nr, xopts,
+                                        strategy, xopts.nr, xopts.v,
                                         common, head_arg, remoteheads);
        }
 }
 
 static void count_diff_files(struct diff_queue_struct *q,
-                            struct diff_options *opt, void *data)
+                            struct diff_options *opt UNUSED, void *data)
 {
        int *count = data;
 
@@ -794,8 +768,8 @@ static int count_unmerged_entries(void)
 {
        int i, ret = 0;
 
-       for (i = 0; i < active_nr; i++)
-               if (ce_stage(active_cache[i]))
+       for (i = 0; i < the_index.cache_nr; i++)
+               if (ce_stage(the_index.cache[i]))
                        ret++;
 
        return ret;
@@ -869,9 +843,9 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                 * the editor and after we invoke run_status above.
                 */
                if (invoked_hook)
-                       discard_cache();
+                       discard_index(&the_index);
        }
-       read_cache_from(index_file);
+       read_index_from(&the_index, index_file, get_git_dir());
        strbuf_addbuf(&msg, &merge_msg);
        if (squash)
                BUG("the control must not reach here under --squash");
@@ -879,17 +853,19 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                strbuf_addch(&msg, '\n');
                if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
                        wt_status_append_cut_line(&msg);
-                       strbuf_commented_addf(&msg, "\n");
+                       strbuf_commented_addf(&msg, comment_line_char, "\n");
                }
-               strbuf_commented_addf(&msg, _(merge_editor_comment));
+               strbuf_commented_addf(&msg, comment_line_char,
+                                     _(merge_editor_comment));
                if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
-                       strbuf_commented_addf(&msg, _(scissors_editor_comment));
+                       strbuf_commented_addf(&msg, comment_line_char,
+                                             _(scissors_editor_comment));
                else
-                       strbuf_commented_addf(&msg,
+                       strbuf_commented_addf(&msg, comment_line_char,
                                _(no_scissors_editor_comment), comment_line_char);
        }
        if (signoff)
-               append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
+               append_signoff(&msg, ignored_log_message_bytes(msg.buf, msg.len), 0);
        write_merge_heads(remoteheads);
        write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len);
        if (run_commit_hook(0 < option_edit, get_index_file(), NULL,
@@ -920,7 +896,9 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
        struct object_id result_tree, result_commit;
        struct commit_list *parents, **pptr = &parents;
 
-       if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
+       if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET,
+                                        SKIP_IF_UNCHANGED, 0, NULL, NULL,
+                                        NULL) < 0)
                return error(_("Unable to write index."));
 
        write_tree_trivial(&result_tree);
@@ -1386,7 +1364,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                goto done;
        }
 
-       if (read_cache_unmerged())
+       if (repo_read_index_unmerged(the_repository))
                die_resolve_conflict("merge");
 
        if (file_exists(git_path_merge_head(the_repository))) {
@@ -1407,7 +1385,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                else
                        die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
        }
-       resolve_undo_clear();
+       resolve_undo_clear_index(&the_index);
 
        if (option_edit < 0)
                option_edit = default_edit_option();
@@ -1470,7 +1448,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                               check_trust_level);
 
                remote_head_oid = &remoteheads->item->object.oid;
-               read_empty(remote_head_oid, 0);
+               read_empty(remote_head_oid);
                update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
                goto done;
@@ -1536,7 +1514,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (!remoteheads)
                ; /* already up-to-date */
        else if (!remoteheads->next)
-               common = get_merge_bases(head_commit, remoteheads->item);
+               common = repo_get_merge_bases(the_repository, head_commit,
+                                             remoteheads->item);
        else {
                struct commit_list *list = remoteheads;
                commit_list_insert(head_commit, &list);
@@ -1565,20 +1544,18 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        !common->next &&
                        oideq(&common->item->object.oid, &head_commit->object.oid)) {
                /* Again the most common case of merging one remote. */
-               struct strbuf msg = STRBUF_INIT;
+               const char *msg = have_message ?
+                       "Fast-forward (no commit created; -m option ignored)" :
+                       "Fast-forward";
                struct commit *commit;
 
                if (verbosity >= 0) {
                        printf(_("Updating %s..%s\n"),
-                              find_unique_abbrev(&head_commit->object.oid,
-                                                 DEFAULT_ABBREV),
-                              find_unique_abbrev(&remoteheads->item->object.oid,
-                                                 DEFAULT_ABBREV));
+                              repo_find_unique_abbrev(the_repository, &head_commit->object.oid,
+                                                      DEFAULT_ABBREV),
+                              repo_find_unique_abbrev(the_repository, &remoteheads->item->object.oid,
+                                                      DEFAULT_ABBREV));
                }
-               strbuf_addstr(&msg, "Fast-forward");
-               if (have_message)
-                       strbuf_addstr(&msg,
-                               " (no commit created; -m option ignored)");
                commit = remoteheads->item;
                if (!commit) {
                        ret = 1;
@@ -1597,9 +1574,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
+               finish(head_commit, remoteheads, &commit->object.oid, msg);
                remove_merge_branch_state(the_repository);
-               strbuf_release(&msg);
                goto done;
        } else if (!remoteheads->next && common->next)
                ;
@@ -1612,13 +1588,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * We are not doing octopus, not fast-forward, and have
                 * only one common.
                 */
-               refresh_cache(REFRESH_QUIET);
+               refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
                if (allow_trivial && fast_forward != FF_ONLY) {
                        /*
                         * Must first ensure that index matches HEAD before
                         * attempting a trivial merge.
                         */
-                       struct tree *head_tree = get_commit_tree(head_commit);
+                       struct tree *head_tree = repo_get_commit_tree(the_repository,
+                                                                     head_commit);
                        struct strbuf sb = STRBUF_INIT;
 
                        if (repo_index_has_changes(the_repository, head_tree,
@@ -1626,7 +1603,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                error(_("Your local changes to the following files would be overwritten by merge:\n  %s"),
                                      sb.buf);
                                strbuf_release(&sb);
-                               return 2;
+                               ret = 2;
+                               goto done;
                        }
 
                        /* See if it is really trivial. */
@@ -1650,14 +1628,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
                for (j = remoteheads; j; j = j->next) {
                        struct commit_list *common_one;
+                       struct commit *common_item;
 
                        /*
                         * Here we *have* to calculate the individual
                         * merge_bases again, otherwise "git merge HEAD^
                         * HEAD^^" would be missed.
                         */
-                       common_one = get_merge_bases(head_commit, j->item);
-                       if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) {
+                       common_one = repo_get_merge_bases(the_repository,
+                                                         head_commit,
+                                                         j->item);
+                       common_item = common_one->item;
+                       free_commit_list(common_one);
+                       if (!oideq(&common_item->object.oid, &j->item->object.oid)) {
                                up_to_date = 0;
                                break;
                        }
@@ -1794,5 +1777,6 @@ done:
        }
        strbuf_release(&buf);
        free(branch_to_free);
+       discard_index(&the_index);
        return ret;
 }
index 5d22909122d195873f2427812084dc8bd4319180..4767f1a97e6df25ca09eba0846feaf404e08fe46 100644 (file)
@@ -1,8 +1,11 @@
 #include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
-#include "tag.h"
+#include "strbuf.h"
 #include "replace-object.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "fsck.h"
 #include "config.h"
 
@@ -14,11 +17,11 @@ static int option_strict = 1;
 
 static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
 
-static int mktag_fsck_error_func(struct fsck_options *o,
-                                const struct object_id *oid,
-                                enum object_type object_type,
+static int mktag_fsck_error_func(struct fsck_options *o UNUSED,
+                                const struct object_id *oid UNUSED,
+                                enum object_type object_type UNUSED,
                                 enum fsck_msg_type msg_type,
-                                enum fsck_msg_id msg_id,
+                                enum fsck_msg_id msg_id UNUSED,
                                 const char *message)
 {
        switch (msg_type) {
@@ -51,7 +54,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
        void *buffer;
        const struct object_id *repl;
 
-       buffer = read_object_file(tagged_oid, &type, &size);
+       buffer = repo_read_object_file(the_repository, tagged_oid, &type,
+                                      &size);
        if (!buffer)
                die(_("could not read tagged object '%s'"),
                    oid_to_hex(tagged_oid));
@@ -80,7 +84,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)
        int tagged_type;
        struct object_id result;
 
-       argc = parse_options(argc, argv, NULL,
+       argc = parse_options(argc, argv, prefix,
                             builtin_mktag_options,
                             builtin_mktag_usage, 0);
 
index 06d81400f558152292718a57c384e12078e2b9be..9a22d4e27735d55265fbf1a16cec8aeb66c27883 100644 (file)
@@ -4,10 +4,13 @@
  * Copyright (c) Junio C Hamano, 2006, 2009
  */
 #include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
 #include "quote.h"
+#include "strbuf.h"
 #include "tree.h"
 #include "parse-options.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 static struct treeent {
        unsigned mode;
index 9a18a82b0575efdff2c98707179d827fd73eacb9..a72aebecaa2f3aa96f802b635b4d55c5f122d56c 100644 (file)
@@ -1,10 +1,13 @@
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "midx.h"
+#include "strbuf.h"
 #include "trace2.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 #define BUILTIN_MIDX_WRITE_USAGE \
        N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
@@ -79,6 +82,7 @@ static struct option *add_common_options(struct option *prev)
 }
 
 static int git_multi_pack_index_write_config(const char *var, const char *value,
+                                            const struct config_context *ctx UNUSED,
                                             void *cb UNUSED)
 {
        if (!strcmp(var, "pack.writebitmaphashcache")) {
index 3413ad1c9b14ca7f0ed8946682c293a371b70f17..22e64fc2900ec8e654aafa6cfaf8a10cdb5d58a7 100644 (file)
@@ -3,15 +3,23 @@
  *
  * Copyright (C) 2006 Johannes Schindelin
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "object-file.h"
 #include "pathspec.h"
 #include "lockfile.h"
 #include "dir.h"
-#include "cache-tree.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "submodule.h"
 #include "entry.h"
 
@@ -87,7 +95,7 @@ static void prepare_move_submodule(const char *src, int first,
                                   const char **submodule_gitfile)
 {
        struct strbuf submodule_dotgit = STRBUF_INIT;
-       if (!S_ISGITLINK(active_cache[first]->ce_mode))
+       if (!S_ISGITLINK(the_index.cache[first]->ce_mode))
                die(_("Directory %s is in index and no submodule?"), src);
        if (!is_staging_gitmodules_ok(&the_index))
                die(_("Please stage your changes to .gitmodules or stash them to proceed"));
@@ -106,13 +114,13 @@ static int index_range_of_same_dir(const char *src, int length,
        const char *src_w_slash = add_slash(src);
        int first, last, len_w_slash = length + 1;
 
-       first = cache_name_pos(src_w_slash, len_w_slash);
+       first = index_name_pos(&the_index, src_w_slash, len_w_slash);
        if (first >= 0)
                die(_("%.*s is in index"), len_w_slash, src_w_slash);
 
        first = -1 - first;
-       for (last = first; last < active_nr; last++) {
-               const char *path = active_cache[last]->name;
+       for (last = first; last < the_index.cache_nr; last++) {
+               const char *path = the_index.cache[last]->name;
                if (strncmp(path, src_w_slash, len_w_slash))
                        break;
        }
@@ -136,14 +144,14 @@ static int empty_dir_has_sparse_contents(const char *name)
        const char *with_slash = add_slash(name);
        int length = strlen(with_slash);
 
-       int pos = cache_name_pos(with_slash, length);
+       int pos = index_name_pos(&the_index, with_slash, length);
        const struct cache_entry *ce;
 
        if (pos < 0) {
                pos = -pos - 1;
                if (pos >= the_index.cache_nr)
                        goto free_return;
-               ce = active_cache[pos];
+               ce = the_index.cache[pos];
                if (strncmp(with_slash, ce->name, length))
                        goto free_return;
                if (ce_skip_worktree(ce))
@@ -175,7 +183,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        int src_dir_nr = 0, src_dir_alloc = 0;
        struct strbuf a_src_dir = STRBUF_INIT;
        enum update_mode *modes, dst_mode = 0;
-       struct stat st;
+       struct stat st, dest_st;
        struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
        struct lock_file lock_file = LOCK_INIT;
        struct cache_entry *ce;
@@ -189,8 +197,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        if (--argc < 1)
                usage_with_options(builtin_mv_usage, builtin_mv_options);
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
-       if (read_cache() < 0)
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
+       if (repo_read_index(the_repository) < 0)
                die(_("index file corrupt"));
 
        source = internal_prefix_pathspec(prefix, argv, argc, 0);
@@ -255,7 +263,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        int pos;
                        const struct cache_entry *ce;
 
-                       pos = cache_name_pos(src, length);
+                       pos = index_name_pos(&the_index, src, length);
                        if (pos < 0) {
                                const char *src_w_slash = add_slash(src);
                                if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
@@ -268,7 +276,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                        bad = _("bad source");
                                goto act_on_entry;
                        }
-                       ce = active_cache[pos];
+                       ce = the_index.cache[pos];
                        if (!ce_skip_worktree(ce)) {
                                bad = _("bad source");
                                goto act_on_entry;
@@ -278,7 +286,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                goto act_on_entry;
                        }
                        /* Check if dst exists in index */
-                       if (cache_name_pos(dst, strlen(dst)) < 0) {
+                       if (index_name_pos(&the_index, dst, strlen(dst)) < 0) {
                                modes[i] |= SPARSE;
                                goto act_on_entry;
                        }
@@ -295,15 +303,15 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        goto act_on_entry;
                }
                if (S_ISDIR(st.st_mode)
-                   && lstat(dst, &st) == 0) {
-                       bad = _("cannot move directory over file");
+                   && lstat(dst, &dest_st) == 0) {
+                       bad = _("destination already exists");
                        goto act_on_entry;
                }
 
 dir_check:
                if (S_ISDIR(st.st_mode)) {
                        int j, dst_len, n;
-                       int first = cache_name_pos(src, length), last;
+                       int first = index_name_pos(&the_index, src, length), last;
 
                        if (first >= 0) {
                                prepare_move_submodule(src, first,
@@ -331,7 +339,7 @@ dir_check:
                        dst_len = strlen(dst);
 
                        for (j = 0; j < last - first; j++) {
-                               const struct cache_entry *ce = active_cache[first + j];
+                               const struct cache_entry *ce = the_index.cache[first + j];
                                const char *path = ce->name;
                                source[argc + j] = path;
                                destination[argc + j] =
@@ -343,7 +351,7 @@ dir_check:
                        argc += last - first;
                        goto act_on_entry;
                }
-               if (!(ce = cache_file_exists(src, length, 0))) {
+               if (!(ce = index_file_exists(&the_index, src, length, 0))) {
                        bad = _("not under version control");
                        goto act_on_entry;
                }
@@ -468,11 +476,14 @@ remove_entry:
                if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
                        continue;
 
-               pos = cache_name_pos(src, strlen(src));
+               pos = index_name_pos(&the_index, src, strlen(src));
                assert(pos >= 0);
                if (!(mode & SPARSE) && !lstat(src, &st))
-                       sparse_and_dirty = ce_modified(active_cache[pos], &st, 0);
-               rename_cache_entry_at(pos, dst);
+                       sparse_and_dirty = ie_modified(&the_index,
+                                                      the_index.cache[pos],
+                                                      &st,
+                                                      0);
+               rename_index_entry_at(&the_index, pos, dst);
 
                if (ignore_sparse &&
                    core_apply_sparse_checkout &&
@@ -486,8 +497,9 @@ remove_entry:
                        if ((mode & SPARSE) &&
                            path_in_sparse_checkout(dst, &the_index)) {
                                /* from out-of-cone to in-cone */
-                               int dst_pos = cache_name_pos(dst, strlen(dst));
-                               struct cache_entry *dst_ce = active_cache[dst_pos];
+                               int dst_pos = index_name_pos(&the_index, dst,
+                                                            strlen(dst));
+                               struct cache_entry *dst_ce = the_index.cache[dst_pos];
 
                                dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
 
@@ -497,8 +509,9 @@ remove_entry:
                                   !(mode & SPARSE) &&
                                   !path_in_sparse_checkout(dst, &the_index)) {
                                /* from in-cone to out-of-cone */
-                               int dst_pos = cache_name_pos(dst, strlen(dst));
-                               struct cache_entry *dst_ce = active_cache[dst_pos];
+                               int dst_pos = index_name_pos(&the_index, dst,
+                                                            strlen(dst));
+                               struct cache_entry *dst_ce = the_index.cache[dst_pos];
 
                                /*
                                 * if src is clean, it will suffice to remove it
index 15535e914a6939d661296798e0b325da9f1ac7b6..2dd1807c4e09f8dc7ff557c628fdd850dc653361 100644 (file)
@@ -1,15 +1,20 @@
 #include "builtin.h"
-#include "cache.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "commit.h"
 #include "tag.h"
 #include "refs.h"
+#include "object-name.h"
+#include "pager.h"
 #include "parse-options.h"
 #include "prio-queue.h"
 #include "hash-lookup.h"
 #include "commit-slab.h"
 #include "commit-graph.h"
+#include "wildmatch.h"
 
 /*
  * One day.  See the 'name a rev shortly after epoch' test in t6120 when
@@ -108,19 +113,11 @@ static int is_better_name(struct rev_name *name,
        int name_distance = effective_distance(name->distance, name->generation);
        int new_distance = effective_distance(distance, generation);
 
-       /*
-        * When comparing names based on tags, prefer names
-        * based on the older tag, even if it is farther away.
-        */
+       /* If both are tags, we prefer the nearer one. */
        if (from_tag && name->from_tag)
-               return (name->taggerdate > taggerdate ||
-                       (name->taggerdate == taggerdate &&
-                        name_distance > new_distance));
+               return name_distance > new_distance;
 
-       /*
-        * We know that at least one of them is a non-tag at this point.
-        * favor a tag over a non-tag.
-        */
+       /* Favor a tag over a non-tag. */
        if (name->from_tag != from_tag)
                return from_tag;
 
@@ -189,7 +186,7 @@ static void name_rev(struct commit *start_commit,
        size_t parents_to_queue_nr, parents_to_queue_alloc = 0;
        struct rev_name *start_name;
 
-       parse_commit(start_commit);
+       repo_parse_commit(the_repository, start_commit);
        if (commit_is_before_cutoff(start_commit))
                return;
 
@@ -219,7 +216,7 @@ static void name_rev(struct commit *start_commit,
                        struct rev_name *parent_name;
                        int generation, distance;
 
-                       parse_commit(parent);
+                       repo_parse_commit(the_repository, parent);
                        if (commit_is_before_cutoff(parent))
                                continue;
 
@@ -273,17 +270,6 @@ static int subpath_matches(const char *path, const char *filter)
        return -1;
 }
 
-static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
-{
-       if (shorten_unambiguous)
-               refname = shorten_unambiguous_ref(refname, 0);
-       else if (skip_prefix(refname, "refs/heads/", &refname))
-               ; /* refname already advanced */
-       else
-               skip_prefix(refname, "refs/", &refname);
-       return refname;
-}
-
 struct name_ref_data {
        int tags_only;
        int name_only;
@@ -309,11 +295,19 @@ static void add_to_tip_table(const struct object_id *oid, const char *refname,
                             int shorten_unambiguous, struct commit *commit,
                             timestamp_t taggerdate, int from_tag, int deref)
 {
-       refname = name_ref_abbrev(refname, shorten_unambiguous);
+       char *short_refname = NULL;
+
+       if (shorten_unambiguous)
+               short_refname = shorten_unambiguous_ref(refname, 0);
+       else if (skip_prefix(refname, "refs/heads/", &refname))
+               ; /* refname already advanced */
+       else
+               skip_prefix(refname, "refs/", &refname);
 
        ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
        oidcpy(&tip_table.table[tip_table.nr].oid, oid);
-       tip_table.table[tip_table.nr].refname = xstrdup(refname);
+       tip_table.table[tip_table.nr].refname = short_refname ?
+               short_refname : xstrdup(refname);
        tip_table.table[tip_table.nr].commit = commit;
        tip_table.table[tip_table.nr].taggerdate = taggerdate;
        tip_table.table[tip_table.nr].from_tag = from_tag;
@@ -504,7 +498,8 @@ static void show_name(const struct object *obj,
        else if (allow_undefined)
                printf("undefined\n");
        else if (always)
-               printf("%s\n", find_unique_abbrev(oid, DEFAULT_ABBREV));
+               printf("%s\n",
+                      repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
        else
                die("cannot describe '%s'", oid_to_hex(oid));
        strbuf_release(&buf);
@@ -538,7 +533,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
                        counter = 0;
 
                        *(p+1) = 0;
-                       if (!get_oid(p - (hexsz - 1), &oid)) {
+                       if (!repo_get_oid(the_repository, p - (hexsz - 1), &oid)) {
                                struct object *o =
                                        lookup_object(the_repository, &oid);
                                if (o)
@@ -578,17 +573,17 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                                   N_("ignore refs matching <pattern>")),
                OPT_GROUP(""),
                OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
-               OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use --annotate-stdin instead")),
+               OPT_BOOL_F(0,
+                          "stdin",
+                          &transform_stdin,
+                          N_("deprecated: use --annotate-stdin instead"),
+                          PARSE_OPT_HIDDEN),
                OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
                OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
                OPT_BOOL(0, "always",     &always,
                           N_("show abbreviated commit object as fallback")),
-               {
-                       /* A Hidden OPT_BOOL */
-                       OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
-                       N_("dereference tags in the input (internal use)"),
-                       PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
-               },
+               OPT_HIDDEN_BOOL(0, "peel-tag", &peel_tag,
+                          N_("dereference tags in the input (internal use)")),
                OPT_END(),
        };
 
@@ -615,7 +610,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                struct object *object;
                struct commit *commit;
 
-               if (get_oid(*argv, &oid)) {
+               if (repo_get_oid(the_repository, *argv, &oid)) {
                        fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
                                        *argv);
                        continue;
index be51f692257f67e2765feb6ff63e8037f3a4f00d..e65cae0bcf702bf939dcd3ddfb2af5793e73fda0 100644 (file)
@@ -7,13 +7,17 @@
  * and builtin/tag.c by Kristian Høgsberg and Carlos Rica.
  */
 
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "notes.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
-#include "blob.h"
 #include "pretty.h"
 #include "refs.h"
 #include "exec-cmd.h"
 #include "notes-merge.h"
 #include "notes-utils.h"
 #include "worktree.h"
+#include "write-or-die.h"
 
+static const char *separator = "\n";
 static const char * const git_notes_usage[] = {
        N_("git notes [--ref <notes-ref>] [list [<object>]]"),
-       N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+       N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
        N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
-       N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+       N_("git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
        N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
        N_("git notes [--ref <notes-ref>] show [<object>]"),
        N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
@@ -96,11 +102,26 @@ static const char * const git_notes_get_ref_usage[] = {
 static const char note_template[] =
        N_("Write/edit the notes for the following object:");
 
+enum notes_stripspace {
+       UNSPECIFIED = -1,
+       NO_STRIPSPACE = 0,
+       STRIPSPACE = 1,
+};
+
+struct note_msg {
+       enum notes_stripspace stripspace;
+       struct strbuf buf;
+};
+
 struct note_data {
        int given;
        int use_editor;
+       int stripspace;
        char *edit_path;
        struct strbuf buf;
+       struct note_msg **messages;
+       size_t msg_nr;
+       size_t msg_alloc;
 };
 
 static void free_note_data(struct note_data *d)
@@ -110,11 +131,18 @@ static void free_note_data(struct note_data *d)
                free(d->edit_path);
        }
        strbuf_release(&d->buf);
+
+       while (d->msg_nr--) {
+               strbuf_release(&d->messages[d->msg_nr]->buf);
+               free(d->messages[d->msg_nr]);
+       }
+       free(d->messages);
 }
 
 static int list_each_note(const struct object_id *object_oid,
-               const struct object_id *note_oid, char *note_path,
-               void *cb_data)
+                         const struct object_id *note_oid,
+                         char *note_path UNUSED,
+                         void *cb_data UNUSED)
 {
        printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid));
        return 0;
@@ -124,7 +152,7 @@ static void copy_obj_to_fd(int fd, const struct object_id *oid)
 {
        unsigned long size;
        enum object_type type;
-       char *buf = read_object_file(oid, &type, &size);
+       char *buf = repo_read_object_file(the_repository, oid, &type, &size);
        if (buf) {
                if (size)
                        write_or_die(fd, buf, size);
@@ -151,7 +179,7 @@ static void write_commented_object(int fd, const struct object_id *object)
 
        if (strbuf_read(&buf, show.out, 0) < 0)
                die_errno(_("could not read 'show' output"));
-       strbuf_add_commented_lines(&cbuf, buf.buf, buf.len);
+       strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_char);
        write_or_die(fd, cbuf.buf, cbuf.len);
 
        strbuf_release(&cbuf);
@@ -179,9 +207,10 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
                        copy_obj_to_fd(fd, old_note);
 
                strbuf_addch(&buf, '\n');
-               strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
-               strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
-               strbuf_addch(&buf, '\n');
+               strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
+               strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)),
+                                          comment_line_char);
+               strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
                write_or_die(fd, buf.buf, buf.len);
 
                write_commented_object(fd, object);
@@ -193,7 +222,8 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
                if (launch_editor(d->edit_path, &d->buf, NULL)) {
                        die(_("please supply the note contents using either -m or -F option"));
                }
-               strbuf_stripspace(&d->buf, 1);
+               if (d->stripspace)
+                       strbuf_stripspace(&d->buf, comment_line_char);
        }
 }
 
@@ -209,66 +239,102 @@ static void write_note_data(struct note_data *d, struct object_id *oid)
        }
 }
 
+static void append_separator(struct strbuf *message)
+{
+       size_t sep_len = 0;
+
+       if (!separator)
+               return;
+       else if ((sep_len = strlen(separator)) && separator[sep_len - 1] == '\n')
+               strbuf_addstr(message, separator);
+       else
+               strbuf_addf(message, "%s%s", separator, "\n");
+}
+
+static void concat_messages(struct note_data *d)
+{
+       struct strbuf msg = STRBUF_INIT;
+       size_t i;
+
+       for (i = 0; i < d->msg_nr ; i++) {
+               if (d->buf.len)
+                       append_separator(&d->buf);
+               strbuf_add(&msg, d->messages[i]->buf.buf, d->messages[i]->buf.len);
+               strbuf_addbuf(&d->buf, &msg);
+               if ((d->stripspace == UNSPECIFIED &&
+                    d->messages[i]->stripspace == STRIPSPACE) ||
+                   d->stripspace == STRIPSPACE)
+                       strbuf_stripspace(&d->buf, 0);
+               strbuf_reset(&msg);
+       }
+       strbuf_release(&msg);
+}
+
 static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 {
        struct note_data *d = opt->value;
+       struct note_msg *msg = xmalloc(sizeof(*msg));
 
        BUG_ON_OPT_NEG(unset);
 
-       strbuf_grow(&d->buf, strlen(arg) + 2);
-       if (d->buf.len)
-               strbuf_addch(&d->buf, '\n');
-       strbuf_addstr(&d->buf, arg);
-       strbuf_stripspace(&d->buf, 0);
-
-       d->given = 1;
+       strbuf_init(&msg->buf, strlen(arg));
+       strbuf_addstr(&msg->buf, arg);
+       ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+       d->messages[d->msg_nr - 1] = msg;
+       msg->stripspace = STRIPSPACE;
        return 0;
 }
 
 static int parse_file_arg(const struct option *opt, const char *arg, int unset)
 {
        struct note_data *d = opt->value;
+       struct note_msg *msg = xmalloc(sizeof(*msg));
 
        BUG_ON_OPT_NEG(unset);
 
-       if (d->buf.len)
-               strbuf_addch(&d->buf, '\n');
+       strbuf_init(&msg->buf , 0);
        if (!strcmp(arg, "-")) {
-               if (strbuf_read(&d->buf, 0, 1024) < 0)
+               if (strbuf_read(&msg->buf, 0, 1024) < 0)
                        die_errno(_("cannot read '%s'"), arg);
-       } else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
+       } else if (strbuf_read_file(&msg->buf, arg, 1024) < 0)
                die_errno(_("could not open or read '%s'"), arg);
-       strbuf_stripspace(&d->buf, 0);
 
-       d->given = 1;
+       ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+       d->messages[d->msg_nr - 1] = msg;
+       msg->stripspace = STRIPSPACE;
        return 0;
 }
 
 static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
 {
        struct note_data *d = opt->value;
-       char *buf;
+       struct note_msg *msg = xmalloc(sizeof(*msg));
+       char *value;
        struct object_id object;
        enum object_type type;
        unsigned long len;
 
        BUG_ON_OPT_NEG(unset);
 
-       if (d->buf.len)
-               strbuf_addch(&d->buf, '\n');
-
-       if (get_oid(arg, &object))
+       strbuf_init(&msg->buf, 0);
+       if (repo_get_oid(the_repository, arg, &object))
                die(_("failed to resolve '%s' as a valid ref."), arg);
-       if (!(buf = read_object_file(&object, &type, &len)))
+       if (!(value = repo_read_object_file(the_repository, &object, &type, &len)))
                die(_("failed to read object '%s'."), arg);
        if (type != OBJ_BLOB) {
-               free(buf);
+               strbuf_release(&msg->buf);
+               free(value);
+               free(msg);
                die(_("cannot read note data from non-blob object '%s'."), arg);
        }
-       strbuf_add(&d->buf, buf, len);
-       free(buf);
 
-       d->given = 1;
+       strbuf_add(&msg->buf, value, len);
+       free(value);
+
+       msg->buf.len = len;
+       ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+       d->messages[d->msg_nr - 1] = msg;
+       msg->stripspace = NO_STRIPSPACE;
        return 0;
 }
 
@@ -280,6 +346,16 @@ static int parse_reedit_arg(const struct option *opt, const char *arg, int unset
        return parse_reuse_arg(opt, arg, unset);
 }
 
+static int parse_separator_arg(const struct option *opt, const char *arg,
+                              int unset)
+{
+       if (unset)
+               *(const char **)opt->value = NULL;
+       else
+               *(const char **)opt->value = arg ? arg : "\n";
+       return 0;
+}
+
 static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -307,9 +383,9 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
                        die(_("malformed input line: '%s'."), buf.buf);
                strbuf_rtrim(split[0]);
                strbuf_rtrim(split[1]);
-               if (get_oid(split[0]->buf, &from_obj))
+               if (repo_get_oid(the_repository, split[0]->buf, &from_obj))
                        die(_("failed to resolve '%s' as a valid ref."), split[0]->buf);
-               if (get_oid(split[1]->buf, &to_obj))
+               if (repo_get_oid(the_repository, split[1]->buf, &to_obj))
                        die(_("failed to resolve '%s' as a valid ref."), split[1]->buf);
 
                if (rewrite_cmd)
@@ -377,7 +453,7 @@ static int list(int argc, const char **argv, const char *prefix)
 
        t = init_notes_check("list", 0);
        if (argc) {
-               if (get_oid(argv[0], &object))
+               if (repo_get_oid(the_repository, argv[0], &object))
                        die(_("failed to resolve '%s' as a valid ref."), argv[0]);
                note = get_note(t, &object);
                if (note) {
@@ -402,7 +478,8 @@ static int add(int argc, const char **argv, const char *prefix)
        struct notes_tree *t;
        struct object_id object, new_note;
        const struct object_id *note;
-       struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+       struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
+
        struct option options[] = {
                OPT_CALLBACK_F('m', "message", &d, N_("message"),
                        N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -419,6 +496,12 @@ static int add(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "allow-empty", &allow_empty,
                        N_("allow storing empty note")),
                OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
+               OPT_CALLBACK_F(0, "separator", &separator,
+                       N_("<paragraph-break>"),
+                       N_("insert <paragraph-break> between paragraphs"),
+                       PARSE_OPT_OPTARG, parse_separator_arg),
+               OPT_BOOL(0, "stripspace", &d.stripspace,
+                       N_("remove unnecessary whitespace")),
                OPT_END()
        };
 
@@ -430,9 +513,13 @@ static int add(int argc, const char **argv, const char *prefix)
                usage_with_options(git_notes_add_usage, options);
        }
 
+       if (d.msg_nr)
+               concat_messages(&d);
+       d.given = !!d.buf.len;
+
        object_ref = argc > 1 ? argv[1] : "HEAD";
 
-       if (get_oid(object_ref, &object))
+       if (repo_get_oid(the_repository, object_ref, &object))
                die(_("failed to resolve '%s' as a valid ref."), object_ref);
 
        t = init_notes_check("add", NOTES_INIT_WRITABLE);
@@ -520,12 +607,12 @@ static int copy(int argc, const char **argv, const char *prefix)
                usage_with_options(git_notes_copy_usage, options);
        }
 
-       if (get_oid(argv[0], &from_obj))
+       if (repo_get_oid(the_repository, argv[0], &from_obj))
                die(_("failed to resolve '%s' as a valid ref."), argv[0]);
 
        object_ref = 1 < argc ? argv[1] : "HEAD";
 
-       if (get_oid(object_ref, &object))
+       if (repo_get_oid(the_repository, object_ref, &object))
                die(_("failed to resolve '%s' as a valid ref."), object_ref);
 
        t = init_notes_check("copy", NOTES_INIT_WRITABLE);
@@ -568,7 +655,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
        const struct object_id *note;
        char *logmsg;
        const char * const *usage;
-       struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+       struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
        struct option options[] = {
                OPT_CALLBACK_F('m', "message", &d, N_("message"),
                        N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -584,6 +671,12 @@ static int append_edit(int argc, const char **argv, const char *prefix)
                        parse_reuse_arg),
                OPT_BOOL(0, "allow-empty", &allow_empty,
                        N_("allow storing empty note")),
+               OPT_CALLBACK_F(0, "separator", &separator,
+                       N_("<paragraph-break>"),
+                       N_("insert <paragraph-break> between paragraphs"),
+                       PARSE_OPT_OPTARG, parse_separator_arg),
+               OPT_BOOL(0, "stripspace", &d.stripspace,
+                       N_("remove unnecessary whitespace")),
                OPT_END()
        };
        int edit = !strcmp(argv[0], "edit");
@@ -597,6 +690,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
                usage_with_options(usage, options);
        }
 
+       if (d.msg_nr)
+               concat_messages(&d);
+       d.given = !!d.buf.len;
+
        if (d.given && edit)
                fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
                        "for the 'edit' subcommand.\n"
@@ -604,7 +701,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
 
        object_ref = 1 < argc ? argv[1] : "HEAD";
 
-       if (get_oid(object_ref, &object))
+       if (repo_get_oid(the_repository, object_ref, &object))
                die(_("failed to resolve '%s' as a valid ref."), object_ref);
 
        t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
@@ -616,14 +713,17 @@ static int append_edit(int argc, const char **argv, const char *prefix)
                /* Append buf to previous note contents */
                unsigned long size;
                enum object_type type;
-               char *prev_buf = read_object_file(note, &type, &size);
+               struct strbuf buf = STRBUF_INIT;
+               char *prev_buf = repo_read_object_file(the_repository, note, &type, &size);
 
-               strbuf_grow(&d.buf, size + 1);
-               if (d.buf.len && prev_buf && size)
-                       strbuf_insertstr(&d.buf, 0, "\n");
                if (prev_buf && size)
-                       strbuf_insert(&d.buf, 0, prev_buf, size);
+                       strbuf_add(&buf, prev_buf, size);
+               if (d.buf.len && prev_buf && size)
+                       append_separator(&buf);
+               strbuf_insert(&d.buf, 0, buf.buf, buf.len);
+
                free(prev_buf);
+               strbuf_release(&buf);
        }
 
        if (d.buf.len || allow_empty) {
@@ -666,7 +766,7 @@ static int show(int argc, const char **argv, const char *prefix)
 
        object_ref = argc ? argv[0] : "HEAD";
 
-       if (get_oid(object_ref, &object))
+       if (repo_get_oid(the_repository, object_ref, &object))
                die(_("failed to resolve '%s' as a valid ref."), object_ref);
 
        t = init_notes_check("show", 0);
@@ -716,11 +816,11 @@ static int merge_commit(struct notes_merge_options *o)
         * and target notes ref from .git/NOTES_MERGE_REF.
         */
 
-       if (get_oid("NOTES_MERGE_PARTIAL", &oid))
+       if (repo_get_oid(the_repository, "NOTES_MERGE_PARTIAL", &oid))
                die(_("failed to read ref NOTES_MERGE_PARTIAL"));
        else if (!(partial = lookup_commit_reference(the_repository, &oid)))
                die(_("could not find commit from NOTES_MERGE_PARTIAL."));
-       else if (parse_commit(partial))
+       else if (repo_parse_commit(the_repository, partial))
                die(_("could not parse commit from NOTES_MERGE_PARTIAL."));
 
        if (partial->parents)
@@ -741,7 +841,8 @@ static int merge_commit(struct notes_merge_options *o)
 
        /* Reuse existing commit message in reflog message */
        memset(&pretty_ctx, 0, sizeof(pretty_ctx));
-       format_commit_message(partial, "%s", &msg, &pretty_ctx);
+       repo_format_commit_message(the_repository, partial, "%s", &msg,
+                                  &pretty_ctx);
        strbuf_trim(&msg);
        strbuf_insertstr(&msg, 0, "notes: ");
        update_ref(msg.buf, o->local_ref, &oid,
@@ -895,7 +996,7 @@ static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag
 {
        int status;
        struct object_id oid;
-       if (get_oid(name, &oid))
+       if (repo_get_oid(the_repository, name, &oid))
                return error(_("Failed to resolve '%s' as a valid ref."), name);
        status = remove_note(t, oid.hash);
        if (status)
index 3658c05cafce7a1735ece0510a9aaacadb46fae9..d8c2128a97928229e93212461027b700d219ec6a 100644 (file)
@@ -1,13 +1,13 @@
 #include "builtin.h"
-#include "cache.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "attr.h"
 #include "object.h"
-#include "blob.h"
 #include "commit.h"
 #include "tag.h"
-#include "tree.h"
 #include "delta.h"
 #include "pack.h"
 #include "pack-revindex.h"
@@ -16,7 +16,6 @@
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "pack-objects.h"
 #include "progress.h"
 #include "strvec.h"
 #include "list.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
 #include "dir.h"
 #include "midx.h"
 #include "trace2.h"
 #include "shallow.h"
 #include "promisor-remote.h"
 #include "pack-mtimes.h"
+#include "parse-options.h"
 
 /*
  * Objects we are going to pack are collected in the `to_pack` structure.
@@ -180,8 +182,8 @@ static inline void oe_set_delta_size(struct packing_data *pack,
 #define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val)
 
 static const char *pack_usage[] = {
-       N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
-       N_("git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"),
+       N_("git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"),
+       N_("git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"),
        NULL
 };
 
@@ -216,13 +218,19 @@ static int thin;
 static int num_preferred_base;
 static struct progress *progress_state;
 
-static struct packed_git *reuse_packfile;
+static struct bitmapped_pack *reuse_packfiles;
+static size_t reuse_packfiles_nr;
+static size_t reuse_packfiles_used_nr;
 static uint32_t reuse_packfile_objects;
 static struct bitmap *reuse_packfile_bitmap;
 
 static int use_bitmap_index_default = 1;
 static int use_bitmap_index = -1;
-static int allow_pack_reuse = 1;
+static enum {
+       NO_PACK_REUSE = 0,
+       SINGLE_PACK_REUSE,
+       MULTI_PACK_REUSE,
+} allow_pack_reuse = SINGLE_PACK_REUSE;
 static enum {
        WRITE_BITMAP_FALSE = 0,
        WRITE_BITMAP_QUIET,
@@ -288,11 +296,13 @@ static void *get_delta(struct object_entry *entry)
        void *buf, *base_buf, *delta_buf;
        enum object_type type;
 
-       buf = read_object_file(&entry->idx.oid, &type, &size);
+       buf = repo_read_object_file(the_repository, &entry->idx.oid, &type,
+                                   &size);
        if (!buf)
                die(_("unable to read %s"), oid_to_hex(&entry->idx.oid));
-       base_buf = read_object_file(&DELTA(entry)->idx.oid, &type,
-                                   &base_size);
+       base_buf = repo_read_object_file(the_repository,
+                                        &DELTA(entry)->idx.oid, &type,
+                                        &base_size);
        if (!base_buf)
                die("unable to read %s",
                    oid_to_hex(&DELTA(entry)->idx.oid));
@@ -454,7 +464,9 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
                                       &size, NULL)) != NULL)
                        buf = NULL;
                else {
-                       buf = read_object_file(&entry->idx.oid, &type, &size);
+                       buf = repo_read_object_file(the_repository,
+                                                   &entry->idx.oid, &type,
+                                                   &size);
                        if (!buf)
                                die(_("unable to read %s"),
                                    oid_to_hex(&entry->idx.oid));
@@ -929,8 +941,10 @@ static struct object_entry **compute_write_order(void)
         */
        for_each_tag_ref(mark_tagged, NULL);
 
-       if (use_delta_islands)
+       if (use_delta_islands) {
                max_layers = compute_pack_layers(&to_pack);
+               free_island_marks();
+       }
 
        ALLOC_ARRAY(wo, to_pack.nr_objects);
        wo_end = 0;
@@ -1002,7 +1016,9 @@ static off_t find_reused_offset(off_t where)
        return reused_chunks[lo-1].difference;
 }
 
-static void write_reused_pack_one(size_t pos, struct hashfile *out,
+static void write_reused_pack_one(struct packed_git *reuse_packfile,
+                                 size_t pos, struct hashfile *out,
+                                 off_t pack_start,
                                  struct pack_window **w_curs)
 {
        off_t offset, next, cur;
@@ -1012,7 +1028,8 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out,
        offset = pack_pos_to_offset(reuse_packfile, pos);
        next = pack_pos_to_offset(reuse_packfile, pos + 1);
 
-       record_reused_object(offset, offset - hashfile_total(out));
+       record_reused_object(offset,
+                            offset - (hashfile_total(out) - pack_start));
 
        cur = offset;
        type = unpack_object_header(reuse_packfile, w_curs, &cur, &size);
@@ -1080,41 +1097,93 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out,
        copy_pack_data(out, reuse_packfile, w_curs, offset, next - offset);
 }
 
-static size_t write_reused_pack_verbatim(struct hashfile *out,
+static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile,
+                                        struct hashfile *out,
+                                        off_t pack_start,
                                         struct pack_window **w_curs)
 {
-       size_t pos = 0;
+       size_t pos = reuse_packfile->bitmap_pos;
+       size_t end;
+
+       if (pos % BITS_IN_EWORD) {
+               size_t word_pos = (pos / BITS_IN_EWORD);
+               size_t offset = pos % BITS_IN_EWORD;
+               size_t last;
+               eword_t word = reuse_packfile_bitmap->words[word_pos];
+
+               if (offset + reuse_packfile->bitmap_nr < BITS_IN_EWORD)
+                       last = offset + reuse_packfile->bitmap_nr;
+               else
+                       last = BITS_IN_EWORD;
+
+               for (; offset < last; offset++) {
+                       if (word >> offset == 0)
+                               return word_pos;
+                       if (!bitmap_get(reuse_packfile_bitmap,
+                                       word_pos * BITS_IN_EWORD + offset))
+                               return word_pos;
+               }
+
+               pos += BITS_IN_EWORD - (pos % BITS_IN_EWORD);
+       }
+
+       /*
+        * Now we're going to copy as many whole eword_t's as possible.
+        * "end" is the index of the last whole eword_t we copy, but
+        * there may be additional bits to process. Those are handled
+        * individually by write_reused_pack().
+        *
+        * Begin by advancing to the first word boundary in range of the
+        * bit positions occupied by objects in "reuse_packfile". Then
+        * pick the last word boundary in the same range. If we have at
+        * least one word's worth of bits to process, continue on.
+        */
+       end = reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr;
+       if (end % BITS_IN_EWORD)
+               end -= end % BITS_IN_EWORD;
+       if (pos >= end)
+               return reuse_packfile->bitmap_pos / BITS_IN_EWORD;
 
-       while (pos < reuse_packfile_bitmap->word_alloc &&
-                       reuse_packfile_bitmap->words[pos] == (eword_t)~0)
-               pos++;
+       while (pos < end &&
+              reuse_packfile_bitmap->words[pos / BITS_IN_EWORD] == (eword_t)~0)
+               pos += BITS_IN_EWORD;
 
-       if (pos) {
-               off_t to_write;
+       if (pos > end)
+               pos = end;
 
-               written = (pos * BITS_IN_EWORD);
-               to_write = pack_pos_to_offset(reuse_packfile, written)
-                       - sizeof(struct pack_header);
+       if (reuse_packfile->bitmap_pos < pos) {
+               off_t pack_start_off = pack_pos_to_offset(reuse_packfile->p, 0);
+               off_t pack_end_off = pack_pos_to_offset(reuse_packfile->p,
+                                                       pos - reuse_packfile->bitmap_pos);
+
+               written += pos - reuse_packfile->bitmap_pos;
 
                /* We're recording one chunk, not one object. */
-               record_reused_object(sizeof(struct pack_header), 0);
+               record_reused_object(pack_start_off,
+                                    pack_start_off - (hashfile_total(out) - pack_start));
                hashflush(out);
-               copy_pack_data(out, reuse_packfile, w_curs,
-                       sizeof(struct pack_header), to_write);
+               copy_pack_data(out, reuse_packfile->p, w_curs,
+                       pack_start_off, pack_end_off - pack_start_off);
 
                display_progress(progress_state, written);
        }
-       return pos;
+       if (pos % BITS_IN_EWORD)
+               BUG("attempted to jump past a word boundary to %"PRIuMAX,
+                   (uintmax_t)pos);
+       return pos / BITS_IN_EWORD;
 }
 
-static void write_reused_pack(struct hashfile *f)
+static void write_reused_pack(struct bitmapped_pack *reuse_packfile,
+                             struct hashfile *f)
 {
-       size_t i = 0;
+       size_t i = reuse_packfile->bitmap_pos / BITS_IN_EWORD;
        uint32_t offset;
+       off_t pack_start = hashfile_total(f) - sizeof(struct pack_header);
        struct pack_window *w_curs = NULL;
 
        if (allow_ofs_delta)
-               i = write_reused_pack_verbatim(f, &w_curs);
+               i = write_reused_pack_verbatim(reuse_packfile, f, pack_start,
+                                              &w_curs);
 
        for (; i < reuse_packfile_bitmap->word_alloc; ++i) {
                eword_t word = reuse_packfile_bitmap->words[i];
@@ -1125,16 +1194,23 @@ static void write_reused_pack(struct hashfile *f)
                                break;
 
                        offset += ewah_bit_ctz64(word >> offset);
+                       if (pos + offset < reuse_packfile->bitmap_pos)
+                               continue;
+                       if (pos + offset >= reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr)
+                               goto done;
                        /*
                         * Can use bit positions directly, even for MIDX
                         * bitmaps. See comment in try_partial_reuse()
                         * for why.
                         */
-                       write_reused_pack_one(pos + offset, f, &w_curs);
+                       write_reused_pack_one(reuse_packfile->p,
+                                             pos + offset - reuse_packfile->bitmap_pos,
+                                             f, pack_start, &w_curs);
                        display_progress(progress_state, ++written);
                }
        }
 
+done:
        unuse_pack(&w_curs);
 }
 
@@ -1186,9 +1262,14 @@ static void write_pack_file(void)
 
                offset = write_pack_header(f, nr_remaining);
 
-               if (reuse_packfile) {
+               if (reuse_packfiles_nr) {
                        assert(pack_to_stdout);
-                       write_reused_pack(f);
+                       for (j = 0; j < reuse_packfiles_nr; j++) {
+                               reused_chunks_nr = 0;
+                               write_reused_pack(&reuse_packfiles[j], f);
+                               if (reused_chunks_nr)
+                                       reuse_packfiles_used_nr++;
+                       }
                        offset = hashfile_total(f);
                }
 
@@ -1588,7 +1669,7 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,
 
 static int add_object_entry_from_bitmap(const struct object_id *oid,
                                        enum object_type type,
-                                       int flags, uint32_t name_hash,
+                                       int flags UNUSED, uint32_t name_hash,
                                        struct packed_git *pack, off_t offset)
 {
        display_progress(progress_state, ++nr_seen);
@@ -1663,7 +1744,7 @@ static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid)
        /* Did not find one.  Either we got a bogus request or
         * we need to read and perhaps cache.
         */
-       data = read_object_file(oid, &type, &size);
+       data = repo_read_object_file(the_repository, oid, &type, &size);
        if (!data)
                return NULL;
        if (type != OBJ_TREE) {
@@ -1708,17 +1789,14 @@ static void pbase_tree_put(struct pbase_tree_cache *cache)
        free(cache);
 }
 
-static int name_cmp_len(const char *name)
+static size_t name_cmp_len(const char *name)
 {
-       int i;
-       for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
-               ;
-       return i;
+       return strcspn(name, "\n/");
 }
 
 static void add_pbase_object(struct tree_desc *tree,
                             const char *name,
-                            int cmplen,
+                            size_t cmplen,
                             const char *fullname)
 {
        struct name_entry entry;
@@ -1743,7 +1821,7 @@ static void add_pbase_object(struct tree_desc *tree,
                        struct tree_desc sub;
                        struct pbase_tree_cache *tree;
                        const char *down = name+cmplen+1;
-                       int downlen = name_cmp_len(down);
+                       size_t downlen = name_cmp_len(down);
 
                        tree = pbase_tree_get(&entry.oid);
                        if (!tree)
@@ -1795,7 +1873,7 @@ static int check_pbase_path(unsigned hash)
 static void add_preferred_base_object(const char *name)
 {
        struct pbase_tree *it;
-       int cmplen;
+       size_t cmplen;
        unsigned hash = pack_name_hash(name);
 
        if (!num_preferred_base || check_pbase_path(hash))
@@ -2075,7 +2153,7 @@ static void check_object(struct object_entry *entry, uint32_t object_index)
 
        if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
                                     OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) {
-               if (has_promisor_remote()) {
+               if (repo_has_promisor_remote(the_repository)) {
                        prefetch_to_pack(object_index);
                        if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
                                                     OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0)
@@ -2526,7 +2604,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
        /* Load data if not already done */
        if (!trg->data) {
                packing_data_lock(&to_pack);
-               trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz);
+               trg->data = repo_read_object_file(the_repository,
+                                                 &trg_entry->idx.oid, &type,
+                                                 &sz);
                packing_data_unlock(&to_pack);
                if (!trg->data)
                        die(_("object %s cannot be read"),
@@ -2539,7 +2619,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
        }
        if (!src->data) {
                packing_data_lock(&to_pack);
-               src->data = read_object_file(&src_entry->idx.oid, &type, &sz);
+               src->data = repo_read_object_file(the_repository,
+                                                 &src_entry->idx.oid, &type,
+                                                 &sz);
                packing_data_unlock(&to_pack);
                if (!src->data) {
                        if (src_entry->preferred_base) {
@@ -3121,26 +3203,27 @@ static void prepare_pack(int window, int depth)
        free(delta_list);
 }
 
-static int git_pack_config(const char *k, const char *v, void *cb)
+static int git_pack_config(const char *k, const char *v,
+                          const struct config_context *ctx, void *cb)
 {
        if (!strcmp(k, "pack.window")) {
-               window = git_config_int(k, v);
+               window = git_config_int(k, v, ctx->kvi);
                return 0;
        }
        if (!strcmp(k, "pack.windowmemory")) {
-               window_memory_limit = git_config_ulong(k, v);
+               window_memory_limit = git_config_ulong(k, v, ctx->kvi);
                return 0;
        }
        if (!strcmp(k, "pack.depth")) {
-               depth = git_config_int(k, v);
+               depth = git_config_int(k, v, ctx->kvi);
                return 0;
        }
        if (!strcmp(k, "pack.deltacachesize")) {
-               max_delta_cache_size = git_config_int(k, v);
+               max_delta_cache_size = git_config_int(k, v, ctx->kvi);
                return 0;
        }
        if (!strcmp(k, "pack.deltacachelimit")) {
-               cache_max_small_delta_size = git_config_int(k, v);
+               cache_max_small_delta_size = git_config_int(k, v, ctx->kvi);
                return 0;
        }
        if (!strcmp(k, "pack.writebitmaphashcache")) {
@@ -3162,11 +3245,23 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                return 0;
        }
        if (!strcmp(k, "pack.allowpackreuse")) {
-               allow_pack_reuse = git_config_bool(k, v);
+               int res = git_parse_maybe_bool_text(v);
+               if (res < 0) {
+                       if (!strcasecmp(v, "single"))
+                               allow_pack_reuse = SINGLE_PACK_REUSE;
+                       else if (!strcasecmp(v, "multi"))
+                               allow_pack_reuse = MULTI_PACK_REUSE;
+                       else
+                               die(_("invalid pack.allowPackReuse value: '%s'"), v);
+               } else if (res) {
+                       allow_pack_reuse = SINGLE_PACK_REUSE;
+               } else {
+                       allow_pack_reuse = NO_PACK_REUSE;
+               }
                return 0;
        }
        if (!strcmp(k, "pack.threads")) {
-               delta_search_threads = git_config_int(k, v);
+               delta_search_threads = git_config_int(k, v, ctx->kvi);
                if (delta_search_threads < 0)
                        die(_("invalid number of threads specified (%d)"),
                            delta_search_threads);
@@ -3177,7 +3272,7 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                return 0;
        }
        if (!strcmp(k, "pack.indexversion")) {
-               pack_idx_opts.version = git_config_int(k, v);
+               pack_idx_opts.version = git_config_int(k, v, ctx->kvi);
                if (pack_idx_opts.version > 2)
                        die(_("bad pack.indexVersion=%"PRIu32),
                            pack_idx_opts.version);
@@ -3191,7 +3286,7 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                return 0;
        }
        if (!strcmp(k, "uploadpack.blobpackfileuri")) {
-               struct configured_exclusion *ex = xmalloc(sizeof(*ex));
+               struct configured_exclusion *ex;
                const char *oid_end, *pack_end;
                /*
                 * Stores the pack hash. This is not a true object ID, but is
@@ -3199,6 +3294,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                 */
                struct object_id pack_hash;
 
+               if (!v)
+                       return config_error_nonbool(k);
+
+               ex = xmalloc(sizeof(*ex));
                if (parse_oid_hex(v, &ex->e.oid, &oid_end) ||
                    *oid_end != ' ' ||
                    parse_oid_hex(oid_end + 1, &pack_hash, &pack_end) ||
@@ -3213,7 +3312,7 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                ex->uri = xstrdup(pack_end + 1);
                oidmap_put(&configured_exclusions, ex);
        }
-       return git_default_config(k, v, cb);
+       return git_default_config(k, v, ctx, cb);
 }
 
 /* Counters for trace2 output when in --stdin-packs mode. */
@@ -3261,13 +3360,14 @@ static int add_object_entry_from_pack(const struct object_id *oid,
        return 0;
 }
 
-static void show_commit_pack_hint(struct commit *commit, void *_data)
+static void show_commit_pack_hint(struct commit *commit UNUSED,
+                                 void *data UNUSED)
 {
        /* nothing to do; commits don't have a namehash */
 }
 
 static void show_object_pack_hint(struct object *object, const char *name,
-                                 void *_data)
+                                 void *data UNUSED)
 {
        struct object_entry *oe = packlist_find(&to_pack, &object->oid);
        if (!oe)
@@ -3345,16 +3445,16 @@ static void read_packs_list_from_stdin(void)
        }
 
        string_list_sort(&include_packs);
+       string_list_remove_duplicates(&include_packs, 0);
        string_list_sort(&exclude_packs);
+       string_list_remove_duplicates(&exclude_packs, 0);
 
        for (p = get_all_packs(the_repository); p; p = p->next) {
                const char *pack_name = pack_basename(p);
 
-               item = string_list_lookup(&include_packs, pack_name);
-               if (!item)
-                       item = string_list_lookup(&exclude_packs, pack_name);
-
-               if (item)
+               if ((item = string_list_lookup(&include_packs, pack_name)))
+                       item->util = p;
+               if ((item = string_list_lookup(&exclude_packs, pack_name)))
                        item->util = p;
        }
 
@@ -3465,7 +3565,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type
        return;
 }
 
-static void show_cruft_object(struct object *obj, const char *name, void *data)
+static void show_cruft_object(struct object *obj, const char *name, void *data UNUSED)
 {
        /*
         * if we did not record it earlier, it's at least as old as our
@@ -3485,7 +3585,7 @@ static void show_cruft_commit(struct commit *commit, void *data)
        show_cruft_object((struct object*)commit, NULL, data);
 }
 
-static int cruft_include_check_obj(struct object *obj, void *data)
+static int cruft_include_check_obj(struct object *obj, void *data UNUSED)
 {
        return !has_object_kept_pack(&obj->oid, IN_CORE_KEEP_PACKS);
 }
@@ -3589,7 +3689,6 @@ static void read_cruft_objects(void)
                        string_list_append(&discard_packs, buf.buf + 1);
                else
                        string_list_append(&fresh_packs, buf.buf);
-               strbuf_reset(&buf);
        }
 
        string_list_sort(&discard_packs);
@@ -3664,7 +3763,7 @@ static void read_object_list_from_stdin(void)
        }
 }
 
-static void show_commit(struct commit *commit, void *data)
+static void show_commit(struct commit *commit, void *data UNUSED)
 {
        add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
 
@@ -3675,7 +3774,8 @@ static void show_commit(struct commit *commit, void *data)
                propagate_island_marks(commit);
 }
 
-static void show_object(struct object *obj, const char *name, void *data)
+static void show_object(struct object *obj, const char *name,
+                       void *data UNUSED)
 {
        add_preferred_base_object(name);
        add_object_entry(&obj->oid, obj->type, name, 0);
@@ -3724,7 +3824,7 @@ static void show_object__ma_allow_promisor(struct object *obj, const char *name,
        show_object(obj, name, data);
 }
 
-static int option_parse_missing_action(const struct option *opt,
+static int option_parse_missing_action(const struct option *opt UNUSED,
                                       const char *arg, int unset)
 {
        assert(arg);
@@ -3762,7 +3862,7 @@ static void show_edge(struct commit *commit)
 static int add_object_in_unpacked_pack(const struct object_id *oid,
                                       struct packed_git *pack,
                                       uint32_t pos,
-                                      void *_data)
+                                      void *data UNUSED)
 {
        if (cruft) {
                off_t offset;
@@ -3796,7 +3896,7 @@ static void add_objects_in_unpacked_packs(void)
 }
 
 static int add_loose_object(const struct object_id *oid, const char *path,
-                           void *data)
+                           void *data UNUSED)
 {
        enum object_type type = oid_object_info(the_repository, oid, NULL);
 
@@ -3916,7 +4016,7 @@ static void loosen_unused_packed_objects(void)
  */
 static int pack_options_allow_reuse(void)
 {
-       return allow_pack_reuse &&
+       return allow_pack_reuse != NO_PACK_REUSE &&
               pack_to_stdout &&
               !ignore_packed_keep_on_disk &&
               !ignore_packed_keep_in_core &&
@@ -3929,13 +4029,18 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
        if (!(bitmap_git = prepare_bitmap_walk(revs, 0)))
                return -1;
 
-       if (pack_options_allow_reuse() &&
-           !reuse_partial_packfile_from_bitmap(
-                       bitmap_git,
-                       &reuse_packfile,
-                       &reuse_packfile_objects,
-                       &reuse_packfile_bitmap)) {
-               assert(reuse_packfile_objects);
+       if (pack_options_allow_reuse())
+               reuse_partial_packfile_from_bitmap(bitmap_git,
+                                                  &reuse_packfiles,
+                                                  &reuse_packfiles_nr,
+                                                  &reuse_packfile_bitmap,
+                                                  allow_pack_reuse == MULTI_PACK_REUSE);
+
+       if (reuse_packfiles) {
+               reuse_packfile_objects = bitmap_popcount(reuse_packfile_bitmap);
+               if (!reuse_packfile_objects)
+                       BUG("expected non-empty reuse bitmap");
+
                nr_result += reuse_packfile_objects;
                nr_seen += reuse_packfile_objects;
                display_progress(progress_state, nr_seen);
@@ -3947,13 +4052,13 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
 }
 
 static void record_recent_object(struct object *obj,
-                                const char *name,
-                                void *data)
+                                const char *name UNUSED,
+                                void *data UNUSED)
 {
        oid_array_append(&recent_objects, &obj->oid);
 }
 
-static void record_recent_commit(struct commit *commit, void *data)
+static void record_recent_commit(struct commit *commit, void *data UNUSED)
 {
        oid_array_append(&recent_objects, &commit->object.oid);
 }
@@ -4102,25 +4207,40 @@ static void add_extra_kept_packs(const struct string_list *names)
        }
 }
 
+static int option_parse_quiet(const struct option *opt, const char *arg,
+                             int unset)
+{
+       int *val = opt->value;
+
+       BUG_ON_OPT_ARG(arg);
+
+       if (!unset)
+               *val = 0;
+       else if (!*val)
+               *val = 1;
+       return 0;
+}
+
 static int option_parse_index_version(const struct option *opt,
                                      const char *arg, int unset)
 {
+       struct pack_idx_option *popts = opt->value;
        char *c;
        const char *val = arg;
 
        BUG_ON_OPT_NEG(unset);
 
-       pack_idx_opts.version = strtoul(val, &c, 10);
-       if (pack_idx_opts.version > 2)
+       popts->version = strtoul(val, &c, 10);
+       if (popts->version > 2)
                die(_("unsupported index version %s"), val);
        if (*c == ',' && c[1])
-               pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
-       if (*c || pack_idx_opts.off32_limit & 0x80000000)
+               popts->off32_limit = strtoul(c+1, &c, 0);
+       if (*c || popts->off32_limit & 0x80000000)
                die(_("bad index version '%s'"), val);
        return 0;
 }
 
-static int option_parse_unpack_unreachable(const struct option *opt,
+static int option_parse_unpack_unreachable(const struct option *opt UNUSED,
                                           const char *arg, int unset)
 {
        if (unset) {
@@ -4135,7 +4255,7 @@ static int option_parse_unpack_unreachable(const struct option *opt,
        return 0;
 }
 
-static int option_parse_cruft_expiration(const struct option *opt,
+static int option_parse_cruft_expiration(const struct option *opt UNUSED,
                                         const char *arg, int unset)
 {
        if (unset) {
@@ -4149,21 +4269,6 @@ static int option_parse_cruft_expiration(const struct option *opt,
        return 0;
 }
 
-struct po_filter_data {
-       unsigned have_revs:1;
-       struct rev_info revs;
-};
-
-static struct list_objects_filter_options *po_filter_revs_init(void *value)
-{
-       struct po_filter_data *data = value;
-
-       repo_init_revisions(the_repository, &data->revs, NULL);
-       data->have_revs = 1;
-
-       return &data->revs.filter;
-}
-
 int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
        int use_internal_rev_list = 0;
@@ -4174,11 +4279,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        int rev_list_index = 0;
        int stdin_packs = 0;
        struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
-       struct po_filter_data pfd = { .have_revs = 0 };
+       struct list_objects_filter_options filter_options =
+               LIST_OBJECTS_FILTER_INIT;
 
        struct option pack_objects_options[] = {
-               OPT_SET_INT('q', "quiet", &progress,
-                           N_("do not show progress meter"), 0),
+               OPT_CALLBACK_F('q', "quiet", &progress, NULL,
+                              N_("do not show progress meter"),
+                              PARSE_OPT_NOARG, option_parse_quiet),
                OPT_SET_INT(0, "progress", &progress,
                            N_("show progress meter"), 1),
                OPT_SET_INT(0, "all-progress", &progress,
@@ -4186,7 +4293,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "all-progress-implied",
                         &all_progress_implied,
                         N_("similar to --all-progress when progress meter is shown")),
-               OPT_CALLBACK_F(0, "index-version", NULL, N_("<version>[,<offset>]"),
+               OPT_CALLBACK_F(0, "index-version", &pack_idx_opts, N_("<version>[,<offset>]"),
                  N_("write the pack index file in the specified idx format version"),
                  PARSE_OPT_NONEG, option_parse_index_version),
                OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit,
@@ -4254,8 +4361,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                                N_("ignore this pack")),
                OPT_INTEGER(0, "compression", &pack_compression_level,
                            N_("pack compression level")),
-               OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
-                           N_("do not hide commits by grafts"), 0),
+               OPT_BOOL(0, "keep-true-parents", &grafts_keep_true_parents,
+                        N_("do not hide commits by grafts")),
                OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index,
                         N_("use a bitmap index if available to speed up counting objects")),
                OPT_SET_INT(0, "write-bitmap-index", &write_bitmap_index,
@@ -4265,7 +4372,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                              &write_bitmap_index,
                              N_("write a bitmap index if possible"),
                              WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN),
-               OPT_PARSE_LIST_OBJECTS_FILTER_INIT(&pfd, po_filter_revs_init),
+               OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
                OPT_CALLBACK_F(0, "missing", NULL, N_("action"),
                  N_("handling for missing objects"), PARSE_OPT_NONEG,
                  option_parse_missing_action),
@@ -4282,7 +4389,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS))
                BUG("too many dfs states, increase OE_DFS_STATE_BITS");
 
-       read_replace_refs = 0;
+       disable_replace_refs();
 
        sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1);
        if (the_repository->gitdir) {
@@ -4292,9 +4399,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        }
 
        reset_pack_idx_option(&pack_idx_opts);
+       pack_idx_opts.flags |= WRITE_REV;
        git_config(git_pack_config, NULL);
-       if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
-               pack_idx_opts.flags |= WRITE_REV;
+       if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0))
+               pack_idx_opts.flags &= ~WRITE_REV;
 
        progress = isatty(2);
        argc = parse_options(argc, argv, prefix, pack_objects_options,
@@ -4368,7 +4476,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 
        if (!HAVE_THREADS && delta_search_threads != 1)
                warning(_("no threads support, ignoring --threads"));
-       if (!pack_to_stdout && !pack_size_limit && !cruft)
+       if (!pack_to_stdout && !pack_size_limit)
                pack_size_limit = pack_size_limit_cfg;
        if (pack_to_stdout && pack_size_limit)
                die(_("--max-pack-size cannot be used to build a pack for transfer"));
@@ -4385,12 +4493,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (!rev_list_all || !rev_list_reflog || !rev_list_index)
                unpack_unreachable_expiration = 0;
 
-       if (pfd.have_revs && pfd.revs.filter.choice) {
-               if (!pack_to_stdout)
-                       die(_("cannot use --filter without --stdout"));
-               if (stdin_packs)
-                       die(_("cannot use --filter with --stdin-packs"));
-       }
+       if (stdin_packs && filter_options.choice)
+               die(_("cannot use --filter with --stdin-packs"));
 
        if (stdin_packs && use_internal_rev_list)
                die(_("cannot use internal rev list with --stdin-packs"));
@@ -4400,8 +4504,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        die(_("cannot use internal rev list with --cruft"));
                if (stdin_packs)
                        die(_("cannot use --stdin-packs with --cruft"));
-               if (pack_size_limit)
-                       die(_("cannot use --max-pack-size with --cruft"));
        }
 
        /*
@@ -4472,13 +4574,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                read_cruft_objects();
        } else if (!use_internal_rev_list) {
                read_object_list_from_stdin();
-       } else if (pfd.have_revs) {
-               get_object_list(&pfd.revs, rp.nr, rp.v);
-               release_revisions(&pfd.revs);
        } else {
                struct rev_info revs;
 
                repo_init_revisions(the_repository, &revs, NULL);
+               list_objects_filter_copy(&revs.filter, &filter_options);
                get_object_list(&revs, rp.nr, rp.v);
                release_revisions(&revs);
        }
@@ -4508,11 +4608,21 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                fprintf_ln(stderr,
                           _("Total %"PRIu32" (delta %"PRIu32"),"
                             " reused %"PRIu32" (delta %"PRIu32"),"
-                            " pack-reused %"PRIu32),
+                            " pack-reused %"PRIu32" (from %"PRIuMAX")"),
                           written, written_delta, reused, reused_delta,
-                          reuse_packfile_objects);
+                          reuse_packfile_objects,
+                          (uintmax_t)reuse_packfiles_used_nr);
+
+       trace2_data_intmax("pack-objects", the_repository, "written", written);
+       trace2_data_intmax("pack-objects", the_repository, "written/delta", written_delta);
+       trace2_data_intmax("pack-objects", the_repository, "reused", reused);
+       trace2_data_intmax("pack-objects", the_repository, "reused/delta", reused_delta);
+       trace2_data_intmax("pack-objects", the_repository, "pack-reused", reuse_packfile_objects);
+       trace2_data_intmax("pack-objects", the_repository, "packs-reused", reuse_packfiles_used_nr);
 
 cleanup:
+       clear_packing_data(&to_pack);
+       list_objects_filter_release(&filter_options);
        strvec_clear(&rp);
 
        return 0;
index ed9b9013a5fea10c0e99616ac928661130b4c21e..4c735ba069caf6626791d85741f78c3869a5c89d 100644 (file)
@@ -7,14 +7,16 @@
 */
 
 #include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 #define BLKSIZE 512
 
 static const char pack_redundant_usage[] =
-"git pack-redundant [--verbose] [--alt-odb] (--all | <filename.pack>...)";
+"git pack-redundant [--verbose] [--alt-odb] (--all | <pack-filename>...)";
 
 static int load_all_packs, verbose, alt_odb;
 
@@ -557,7 +559,7 @@ static void load_all(void)
        }
 }
 
-int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
+int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED)
 {
        int i;
        int i_still_use_this = 0;
@@ -603,6 +605,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
                        "option, '--i-still-use-this', on the command line\n"
                        "and let us know you still use it by sending an e-mail\n"
                        "to <git@vger.kernel.org>.  Thanks.\n"), stderr);
+               die(_("refusing to run without --i-still-use-this"));
        }
 
        if (load_all_packs)
index cfbd5c36c7640bc841a68bb5a524f025c43212fd..bcf383cac9dd875354d3c91152f7b8d635f82ff4 100644 (file)
@@ -1,24 +1,48 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "refs.h"
 #include "repository.h"
+#include "revision.h"
 
 static char const * const pack_refs_usage[] = {
-       N_("git pack-refs [<options>]"),
+       N_("git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]"),
        NULL
 };
 
 int cmd_pack_refs(int argc, const char **argv, const char *prefix)
 {
        unsigned int flags = PACK_REFS_PRUNE;
+       static struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
+       static struct string_list included_refs = STRING_LIST_INIT_NODUP;
+       struct pack_refs_opts pack_refs_opts = { .exclusions = &excludes,
+                                                .includes = &included_refs,
+                                                .flags = flags };
+       static struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
+       struct string_list_item *item;
+
        struct option opts[] = {
-               OPT_BIT(0, "all",   &flags, N_("pack everything"), PACK_REFS_ALL),
-               OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+               OPT_BIT(0, "all",   &pack_refs_opts.flags, N_("pack everything"), PACK_REFS_ALL),
+               OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+               OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"),
+                       N_("references to include")),
+               OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"),
+                       N_("references to exclude")),
                OPT_END(),
        };
        git_config(git_default_config, NULL);
        if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
                usage_with_options(pack_refs_usage, opts);
-       return refs_pack_refs(get_main_ref_store(the_repository), flags);
+
+       for_each_string_list_item(item, &option_excluded_refs)
+               add_ref_exclusion(pack_refs_opts.exclusions, item->string);
+
+       if (pack_refs_opts.flags & PACK_REFS_ALL)
+               string_list_append(pack_refs_opts.includes, "*");
+
+       if (!pack_refs_opts.includes->nr)
+               string_list_append(pack_refs_opts.includes, "refs/tags/*");
+
+       return refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
 }
index 881fcf32732caf810789c254d31eb05058ca0187..3894d2b970612cab4a47250dd53da5b0908c820e 100644 (file)
@@ -1,7 +1,10 @@
-#include "cache.h"
 #include "builtin.h"
 #include "config.h"
 #include "diff.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "parse-options.h"
 
 static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
 {
@@ -57,10 +60,12 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
 }
 
 static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
-                          struct strbuf *line_buf, int stable)
+                          struct strbuf *line_buf, int stable, int verbatim)
 {
        int patchlen = 0, found_next = 0;
        int before = -1, after = -1;
+       int diff_is_binary = 0;
+       char pre_oid_str[GIT_MAX_HEXSZ + 1], post_oid_str[GIT_MAX_HEXSZ + 1];
        git_hash_ctx ctx;
 
        the_hash_algo->init_fn(&ctx);
@@ -71,11 +76,14 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
                const char *p = line;
                int len;
 
-               if (!skip_prefix(line, "diff-tree ", &p) &&
-                   !skip_prefix(line, "commit ", &p) &&
+               /* Possibly skip over the prefix added by "log" or "format-patch" */
+               if (!skip_prefix(line, "commit ", &p) &&
                    !skip_prefix(line, "From ", &p) &&
-                   starts_with(line, "\\ ") && 12 < strlen(line))
+                   starts_with(line, "\\ ") && 12 < strlen(line)) {
+                       if (verbatim)
+                               the_hash_algo->update_fn(&ctx, line, strlen(line));
                        continue;
+               }
 
                if (!get_oid_hex(p, next_oid)) {
                        found_next = 1;
@@ -88,14 +96,44 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
 
                /* Parsing diff header?  */
                if (before == -1) {
-                       if (starts_with(line, "index "))
+                       if (starts_with(line, "GIT binary patch") ||
+                           starts_with(line, "Binary files")) {
+                               diff_is_binary = 1;
+                               before = 0;
+                               the_hash_algo->update_fn(&ctx, pre_oid_str,
+                                                        strlen(pre_oid_str));
+                               the_hash_algo->update_fn(&ctx, post_oid_str,
+                                                        strlen(post_oid_str));
+                               if (stable)
+                                       flush_one_hunk(result, &ctx);
+                               continue;
+                       } else if (skip_prefix(line, "index ", &p)) {
+                               char *oid1_end = strstr(line, "..");
+                               char *oid2_end = NULL;
+                               if (oid1_end)
+                                       oid2_end = strstr(oid1_end, " ");
+                               if (!oid2_end)
+                                       oid2_end = line + strlen(line) - 1;
+                               if (oid1_end != NULL && oid2_end != NULL) {
+                                       *oid1_end = *oid2_end = '\0';
+                                       strlcpy(pre_oid_str, p, GIT_MAX_HEXSZ + 1);
+                                       strlcpy(post_oid_str, oid1_end + 2, GIT_MAX_HEXSZ + 1);
+                               }
                                continue;
-                       else if (starts_with(line, "--- "))
+                       else if (starts_with(line, "--- "))
                                before = after = 1;
                        else if (!isalpha(line[0]))
                                break;
                }
 
+               if (diff_is_binary) {
+                       if (starts_with(line, "diff ")) {
+                               diff_is_binary = 0;
+                               before = -1;
+                       }
+                       continue;
+               }
+
                /* Looking for a valid hunk header?  */
                if (before == 0 && after == 0) {
                        if (starts_with(line, "@@ -")) {
@@ -120,8 +158,8 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
                if (line[0] == '+' || line[0] == ' ')
                        after--;
 
-               /* Compute the sha without whitespace */
-               len = remove_space(line);
+               /* Add line to hash algo (possibly removing whitespace) */
+               len = verbatim ? strlen(line) : remove_space(line);
                patchlen += len;
                the_hash_algo->update_fn(&ctx, line, len);
        }
@@ -134,7 +172,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
        return patchlen;
 }
 
-static void generate_id_list(int stable)
+static void generate_id_list(int stable, int verbatim)
 {
        struct object_id oid, n, result;
        int patchlen;
@@ -142,44 +180,64 @@ static void generate_id_list(int stable)
 
        oidclr(&oid);
        while (!feof(stdin)) {
-               patchlen = get_one_patchid(&n, &result, &line_buf, stable);
+               patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim);
                flush_current_id(patchlen, &oid, &result);
                oidcpy(&oid, &n);
        }
        strbuf_release(&line_buf);
 }
 
-static const char patch_id_usage[] = "git patch-id [--stable | --unstable]";
+static const char *const patch_id_usage[] = {
+       N_("git patch-id [--stable | --unstable | --verbatim]"), NULL
+};
+
+struct patch_id_opts {
+       int stable;
+       int verbatim;
+};
 
-static int git_patch_id_config(const char *var, const char *value, void *cb)
+static int git_patch_id_config(const char *var, const char *value,
+                              const struct config_context *ctx, void *cb)
 {
-       int *stable = cb;
+       struct patch_id_opts *opts = cb;
 
        if (!strcmp(var, "patchid.stable")) {
-               *stable = git_config_bool(var, value);
+               opts->stable = git_config_bool(var, value);
+               return 0;
+       }
+       if (!strcmp(var, "patchid.verbatim")) {
+               opts->verbatim = git_config_bool(var, value);
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_patch_id(int argc, const char **argv, const char *prefix)
 {
-       int stable = -1;
-
-       git_config(git_patch_id_config, &stable);
-
-       /* If nothing is set, default to unstable. */
-       if (stable < 0)
-               stable = 0;
-
-       if (argc == 2 && !strcmp(argv[1], "--stable"))
-               stable = 1;
-       else if (argc == 2 && !strcmp(argv[1], "--unstable"))
-               stable = 0;
-       else if (argc != 1)
-               usage(patch_id_usage);
-
-       generate_id_list(stable);
+       /* if nothing is set, default to unstable */
+       struct patch_id_opts config = {0, 0};
+       int opts = 0;
+       struct option builtin_patch_id_options[] = {
+               OPT_CMDMODE(0, "unstable", &opts,
+                   N_("use the unstable patch-id algorithm"), 1),
+               OPT_CMDMODE(0, "stable", &opts,
+                   N_("use the stable patch-id algorithm"), 2),
+               OPT_CMDMODE(0, "verbatim", &opts,
+                       N_("don't strip whitespace from the patch"), 3),
+               OPT_END()
+       };
+
+       git_config(git_patch_id_config, &config);
+
+       /* verbatim implies stable */
+       if (config.verbatim)
+               config.stable = 1;
+
+       argc = parse_options(argc, argv, prefix, builtin_patch_id_options,
+                            patch_id_usage, 0);
+
+       generate_id_list(opts ? opts > 1 : config.stable,
+                        opts ? opts == 3 : config.verbatim);
        return 0;
 }
index da3273a268b47d89be86ac05780905fa9ad766d8..ca3578e158840148aea2b074a3d44fdc64332b58 100644 (file)
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "prune-packed.h"
 
index df376b2ed1e0920e727e5e0a373fbb053ddf3d5a..57fe31467fe5ac9768f1b04f7c7e6dd3e9409468 100644 (file)
@@ -1,13 +1,20 @@
-#include "cache.h"
+#include "builtin.h"
 #include "commit.h"
 #include "diff.h"
+#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "revision.h"
-#include "builtin.h"
 #include "reachable.h"
 #include "parse-options.h"
+#include "path.h"
 #include "progress.h"
 #include "prune-packed.h"
-#include "object-store.h"
+#include "replace-object.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "shallow.h"
 
 static const char * const prune_usage[] = {
@@ -98,7 +105,8 @@ static int prune_object(const struct object_id *oid, const char *fullpath,
        return 0;
 }
 
-static int prune_cruft(const char *basename, const char *path, void *data)
+static int prune_cruft(const char *basename, const char *path,
+                      void *data UNUSED)
 {
        if (starts_with(basename, "tmp_obj_"))
                prune_tmp_file(path);
@@ -107,7 +115,8 @@ static int prune_cruft(const char *basename, const char *path, void *data)
        return 0;
 }
 
-static int prune_subdir(unsigned int nr, const char *path, void *data)
+static int prune_subdir(unsigned int nr UNUSED, const char *path,
+                       void *data UNUSED)
 {
        if (!show_only)
                rmdir(path);
@@ -127,7 +136,9 @@ static void remove_temporary_files(const char *path)
 
        dir = opendir(path);
        if (!dir) {
-               fprintf(stderr, "Unable to open directory %s\n", path);
+               if (errno != ENOENT)
+                       fprintf(stderr, "Unable to open directory %s: %s\n",
+                               path, strerror(errno));
                return;
        }
        while ((de = readdir(dir)) != NULL)
@@ -154,7 +165,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 
        expire = TIME_MAX;
        save_commit_buffer = 0;
-       read_replace_refs = 0;
+       disable_replace_refs();
        repo_init_revisions(the_repository, &revs, prefix);
 
        argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
@@ -166,7 +177,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
                struct object_id oid;
                const char *name = *argv++;
 
-               if (!get_oid(name, &oid)) {
+               if (!repo_get_oid(the_repository, name, &oid)) {
                        struct object *object = parse_object_or_die(&oid,
                                                                    name);
                        add_pending_object(&revs, object, "");
index 403a24d7ca670f9a5c56ebfb6d8429b5e5817b54..73a68b75b0672487013299f992d382d1957a02a4 100644 (file)
@@ -5,28 +5,29 @@
  *
  * Fetch one or more remote refs and merge it/them into the current HEAD.
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
-#include "config.h"
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "advice.h"
+#include "config.h"
+#include "gettext.h"
+#include "hex.h"
+#include "merge.h"
+#include "object-name.h"
 #include "parse-options.h"
-#include "exec-cmd.h"
 #include "run-command.h"
 #include "oid-array.h"
 #include "remote.h"
 #include "dir.h"
+#include "path.h"
+#include "read-cache-ll.h"
 #include "rebase.h"
 #include "refs.h"
 #include "refspec.h"
-#include "revision.h"
 #include "submodule.h"
 #include "submodule-config.h"
-#include "tempfile.h"
-#include "lockfile.h"
 #include "wt-status.h"
 #include "commit-reach.h"
 #include "sequencer.h"
-#include "packfile.h"
 
 /**
  * Parses the value of --rebase. If value is a false value, returns
@@ -357,10 +358,9 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified)
 /**
  * Read config variables.
  */
-static int git_pull_config(const char *var, const char *value, void *cb)
+static int git_pull_config(const char *var, const char *value,
+                          const struct config_context *ctx, void *cb)
 {
-       int status;
-
        if (!strcmp(var, "rebase.autostash")) {
                config_autostash = git_config_bool(var, value);
                return 0;
@@ -372,11 +372,7 @@ static int git_pull_config(const char *var, const char *value, void *cb)
                check_trust_level = 0;
        }
 
-       status = git_gpg_config(var, value, cb);
-       if (status)
-               return status;
-
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 /**
@@ -515,76 +511,75 @@ static void parse_repo_refspecs(int argc, const char **argv, const char **repo,
  */
 static int run_fetch(const char *repo, const char **refspecs)
 {
-       struct strvec args = STRVEC_INIT;
-       int ret;
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
-       strvec_pushl(&args, "fetch", "--update-head-ok", NULL);
+       strvec_pushl(&cmd.args, "fetch", "--update-head-ok", NULL);
 
        /* Shared options */
-       argv_push_verbosity(&args);
+       argv_push_verbosity(&cmd.args);
        if (opt_progress)
-               strvec_push(&args, opt_progress);
+               strvec_push(&cmd.args, opt_progress);
 
        /* Options passed to git-fetch */
        if (opt_all)
-               strvec_push(&args, opt_all);
+               strvec_push(&cmd.args, opt_all);
        if (opt_append)
-               strvec_push(&args, opt_append);
+               strvec_push(&cmd.args, opt_append);
        if (opt_upload_pack)
-               strvec_push(&args, opt_upload_pack);
-       argv_push_force(&args);
+               strvec_push(&cmd.args, opt_upload_pack);
+       argv_push_force(&cmd.args);
        if (opt_tags)
-               strvec_push(&args, opt_tags);
+               strvec_push(&cmd.args, opt_tags);
        if (opt_prune)
-               strvec_push(&args, opt_prune);
+               strvec_push(&cmd.args, opt_prune);
        if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
                switch (recurse_submodules_cli) {
                case RECURSE_SUBMODULES_ON:
-                       strvec_push(&args, "--recurse-submodules=on");
+                       strvec_push(&cmd.args, "--recurse-submodules=on");
                        break;
                case RECURSE_SUBMODULES_OFF:
-                       strvec_push(&args, "--recurse-submodules=no");
+                       strvec_push(&cmd.args, "--recurse-submodules=no");
                        break;
                case RECURSE_SUBMODULES_ON_DEMAND:
-                       strvec_push(&args, "--recurse-submodules=on-demand");
+                       strvec_push(&cmd.args, "--recurse-submodules=on-demand");
                        break;
                default:
                        BUG("submodule recursion option not understood");
                }
        if (max_children)
-               strvec_push(&args, max_children);
+               strvec_push(&cmd.args, max_children);
        if (opt_dry_run)
-               strvec_push(&args, "--dry-run");
+               strvec_push(&cmd.args, "--dry-run");
        if (opt_keep)
-               strvec_push(&args, opt_keep);
+               strvec_push(&cmd.args, opt_keep);
        if (opt_depth)
-               strvec_push(&args, opt_depth);
+               strvec_push(&cmd.args, opt_depth);
        if (opt_unshallow)
-               strvec_push(&args, opt_unshallow);
+               strvec_push(&cmd.args, opt_unshallow);
        if (opt_update_shallow)
-               strvec_push(&args, opt_update_shallow);
+               strvec_push(&cmd.args, opt_update_shallow);
        if (opt_refmap)
-               strvec_push(&args, opt_refmap);
+               strvec_push(&cmd.args, opt_refmap);
        if (opt_ipv4)
-               strvec_push(&args, opt_ipv4);
+               strvec_push(&cmd.args, opt_ipv4);
        if (opt_ipv6)
-               strvec_push(&args, opt_ipv6);
+               strvec_push(&cmd.args, opt_ipv6);
        if (opt_show_forced_updates > 0)
-               strvec_push(&args, "--show-forced-updates");
+               strvec_push(&cmd.args, "--show-forced-updates");
        else if (opt_show_forced_updates == 0)
-               strvec_push(&args, "--no-show-forced-updates");
+               strvec_push(&cmd.args, "--no-show-forced-updates");
        if (set_upstream)
-               strvec_push(&args, set_upstream);
-       strvec_pushv(&args, opt_fetch.v);
+               strvec_push(&cmd.args, set_upstream);
+       strvec_pushv(&cmd.args, opt_fetch.v);
 
        if (repo) {
-               strvec_push(&args, repo);
-               strvec_pushv(&args, refspecs);
+               strvec_push(&cmd.args, repo);
+               strvec_pushv(&cmd.args, refspecs);
        } else if (*refspecs)
                BUG("refspecs without repo?");
-       ret = run_command_v_opt(args.v, RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE);
-       strvec_clear(&args);
-       return ret;
+       cmd.git_cmd = 1;
+       cmd.close_object_store = 1;
+       return run_command(&cmd);
 }
 
 /**
@@ -653,52 +648,50 @@ static int update_submodules(void)
  */
 static int run_merge(void)
 {
-       int ret;
-       struct strvec args = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
-       strvec_pushl(&args, "merge", NULL);
+       strvec_pushl(&cmd.args, "merge", NULL);
 
        /* Shared options */
-       argv_push_verbosity(&args);
+       argv_push_verbosity(&cmd.args);
        if (opt_progress)
-               strvec_push(&args, opt_progress);
+               strvec_push(&cmd.args, opt_progress);
 
        /* Options passed to git-merge */
        if (opt_diffstat)
-               strvec_push(&args, opt_diffstat);
+               strvec_push(&cmd.args, opt_diffstat);
        if (opt_log)
-               strvec_push(&args, opt_log);
+               strvec_push(&cmd.args, opt_log);
        if (opt_signoff)
-               strvec_push(&args, opt_signoff);
+               strvec_push(&cmd.args, opt_signoff);
        if (opt_squash)
-               strvec_push(&args, opt_squash);
+               strvec_push(&cmd.args, opt_squash);
        if (opt_commit)
-               strvec_push(&args, opt_commit);
+               strvec_push(&cmd.args, opt_commit);
        if (opt_edit)
-               strvec_push(&args, opt_edit);
+               strvec_push(&cmd.args, opt_edit);
        if (cleanup_arg)
-               strvec_pushf(&args, "--cleanup=%s", cleanup_arg);
+               strvec_pushf(&cmd.args, "--cleanup=%s", cleanup_arg);
        if (opt_ff)
-               strvec_push(&args, opt_ff);
+               strvec_push(&cmd.args, opt_ff);
        if (opt_verify)
-               strvec_push(&args, opt_verify);
+               strvec_push(&cmd.args, opt_verify);
        if (opt_verify_signatures)
-               strvec_push(&args, opt_verify_signatures);
-       strvec_pushv(&args, opt_strategies.v);
-       strvec_pushv(&args, opt_strategy_opts.v);
+               strvec_push(&cmd.args, opt_verify_signatures);
+       strvec_pushv(&cmd.args, opt_strategies.v);
+       strvec_pushv(&cmd.args, opt_strategy_opts.v);
        if (opt_gpg_sign)
-               strvec_push(&args, opt_gpg_sign);
+               strvec_push(&cmd.args, opt_gpg_sign);
        if (opt_autostash == 0)
-               strvec_push(&args, "--no-autostash");
+               strvec_push(&cmd.args, "--no-autostash");
        else if (opt_autostash == 1)
-               strvec_push(&args, "--autostash");
+               strvec_push(&cmd.args, "--autostash");
        if (opt_allow_unrelated_histories > 0)
-               strvec_push(&args, "--allow-unrelated-histories");
+               strvec_push(&cmd.args, "--allow-unrelated-histories");
 
-       strvec_push(&args, "FETCH_HEAD");
-       ret = run_command_v_opt(args.v, RUN_GIT_CMD);
-       strvec_clear(&args);
-       return ret;
+       strvec_push(&cmd.args, "FETCH_HEAD");
+       cmd.git_cmd = 1;
+       return run_command(&cmd);
 }
 
 /**
@@ -879,43 +872,41 @@ static int get_rebase_newbase_and_upstream(struct object_id *newbase,
 static int run_rebase(const struct object_id *newbase,
                const struct object_id *upstream)
 {
-       int ret;
-       struct strvec args = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
-       strvec_push(&args, "rebase");
+       strvec_push(&cmd.args, "rebase");
 
        /* Shared options */
-       argv_push_verbosity(&args);
+       argv_push_verbosity(&cmd.args);
 
        /* Options passed to git-rebase */
        if (opt_rebase == REBASE_MERGES)
-               strvec_push(&args, "--rebase-merges");
+               strvec_push(&cmd.args, "--rebase-merges");
        else if (opt_rebase == REBASE_INTERACTIVE)
-               strvec_push(&args, "--interactive");
+               strvec_push(&cmd.args, "--interactive");
        if (opt_diffstat)
-               strvec_push(&args, opt_diffstat);
-       strvec_pushv(&args, opt_strategies.v);
-       strvec_pushv(&args, opt_strategy_opts.v);
+               strvec_push(&cmd.args, opt_diffstat);
+       strvec_pushv(&cmd.args, opt_strategies.v);
+       strvec_pushv(&cmd.args, opt_strategy_opts.v);
        if (opt_gpg_sign)
-               strvec_push(&args, opt_gpg_sign);
+               strvec_push(&cmd.args, opt_gpg_sign);
        if (opt_signoff)
-               strvec_push(&args, opt_signoff);
+               strvec_push(&cmd.args, opt_signoff);
        if (opt_autostash == 0)
-               strvec_push(&args, "--no-autostash");
+               strvec_push(&cmd.args, "--no-autostash");
        else if (opt_autostash == 1)
-               strvec_push(&args, "--autostash");
+               strvec_push(&cmd.args, "--autostash");
        if (opt_verify_signatures &&
            !strcmp(opt_verify_signatures, "--verify-signatures"))
                warning(_("ignoring --verify-signatures for rebase"));
 
-       strvec_push(&args, "--onto");
-       strvec_push(&args, oid_to_hex(newbase));
+       strvec_push(&cmd.args, "--onto");
+       strvec_push(&cmd.args, oid_to_hex(newbase));
 
-       strvec_push(&args, oid_to_hex(upstream));
+       strvec_push(&cmd.args, oid_to_hex(upstream));
 
-       ret = run_command_v_opt(args.v, RUN_GIT_CMD);
-       strvec_clear(&args);
-       return ret;
+       cmd.git_cmd = 1;
+       return run_command(&cmd);
 }
 
 static int get_can_ff(struct object_id *orig_head,
@@ -1035,26 +1026,26 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (opt_rebase < 0)
                opt_rebase = config_get_rebase(&rebase_unspecified);
 
-       if (read_cache_unmerged())
+       if (repo_read_index_unmerged(the_repository))
                die_resolve_conflict("pull");
 
        if (file_exists(git_path_merge_head(the_repository)))
                die_conclude_merge();
 
-       if (get_oid("HEAD", &orig_head))
+       if (repo_get_oid(the_repository, "HEAD", &orig_head))
                oidclr(&orig_head);
 
        if (opt_rebase) {
                if (opt_autostash == -1)
                        opt_autostash = config_autostash;
 
-               if (is_null_oid(&orig_head) && !is_cache_unborn())
+               if (is_null_oid(&orig_head) && !is_index_unborn(&the_index))
                        die(_("Updating an unborn branch with changes added to the index."));
 
                if (!opt_autostash)
                        require_clean_work_tree(the_repository,
                                N_("pull with rebase"),
-                               _("please commit or stash them."), 1, 0);
+                               _("Please commit or stash them."), 1, 0);
 
                if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
                        oidclr(&rebase_fork_point);
@@ -1066,7 +1057,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (opt_dry_run)
                return 0;
 
-       if (get_oid("HEAD", &curr_head))
+       if (repo_get_oid(the_repository, "HEAD", &curr_head))
                oidclr(&curr_head);
 
        if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
index df0d68e599857c74a4edf0a85ecdfb037516c47d..2fbb31c3ad8eb9b7a15ffac4e5d9aa65367d298f 100644 (file)
@@ -1,19 +1,23 @@
 /*
  * "git push"
  */
-#include "cache.h"
+#include "builtin.h"
+#include "advice.h"
 #include "branch.h"
 #include "config.h"
-#include "refs.h"
+#include "environment.h"
+#include "gettext.h"
 #include "refspec.h"
 #include "run-command.h"
-#include "builtin.h"
 #include "remote.h"
 #include "transport.h"
 #include "parse-options.h"
+#include "pkt-line.h"
+#include "repository.h"
 #include "submodule.h"
 #include "submodule-config.h"
 #include "send-pack.h"
+#include "trace2.h"
 #include "color.h"
 
 static const char * const push_usage[] = {
@@ -63,16 +67,9 @@ static struct refspec rs = REFSPEC_INIT_PUSH;
 static struct string_list push_options_config = STRING_LIST_INIT_DUP;
 
 static void refspec_append_mapped(struct refspec *refspec, const char *ref,
-                                 struct remote *remote, struct ref *local_refs)
+                                 struct remote *remote, struct ref *matched)
 {
        const char *branch_name;
-       struct ref *matched = NULL;
-
-       /* Does "ref" uniquely name our ref? */
-       if (count_refspec_match(ref, local_refs, &matched) != 1) {
-               refspec_append(refspec, ref);
-               return;
-       }
 
        if (remote->push.nr) {
                struct refspec_item query;
@@ -120,15 +117,28 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
                                die(_("--delete only accepts plain target ref names"));
                        refspec_appendf(&rs, ":%s", ref);
                } else if (!strchr(ref, ':')) {
-                       if (!remote) {
-                               /* lazily grab remote and local_refs */
-                               remote = remote_get(repo);
+                       struct ref *matched = NULL;
+
+                       /* lazily grab local_refs */
+                       if (!local_refs)
                                local_refs = get_local_heads();
+
+                       /* Does "ref" uniquely name our ref? */
+                       if (count_refspec_match(ref, local_refs, &matched) != 1) {
+                               refspec_append(&rs, ref);
+                       } else {
+                               /* lazily grab remote */
+                               if (!remote)
+                                       remote = remote_get(repo);
+                               if (!remote)
+                                       BUG("must get a remote for repo '%s'", repo);
+
+                               refspec_append_mapped(&rs, ref, remote, matched);
                        }
-                       refspec_append_mapped(&rs, ref, remote, local_refs);
                } else
                        refspec_append(&rs, ref);
        }
+       free_refs(local_refs);
 }
 
 static int push_url_of_remote(struct remote *remote, const char ***url_p)
@@ -169,8 +179,8 @@ static NORETURN void die_push_simple(struct branch *branch,
        if (git_branch_track != BRANCH_TRACK_SIMPLE)
                advice_automergesimple_maybe = _("\n"
                                 "To avoid automatically configuring "
-                                "upstream branches when their name\n"
-                                "doesn't match the local branch, see option "
+                                "an upstream branch when its name\n"
+                                "won't match the local branch, see option "
                                 "'simple' of branch.autoSetupMerge\n"
                                 "in 'git help config'.\n");
        die(_("The upstream branch of your current branch does not match\n"
@@ -290,21 +300,21 @@ static void setup_default_push_refspecs(int *flags, struct remote *remote)
 
 static const char message_advice_pull_before_push[] =
        N_("Updates were rejected because the tip of your current branch is behind\n"
-          "its remote counterpart. Integrate the remote changes (e.g.\n"
-          "'git pull ...') before pushing again.\n"
+          "its remote counterpart. If you want to integrate the remote changes,\n"
+          "use 'git pull' before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_checkout_pull_push[] =
        N_("Updates were rejected because a pushed branch tip is behind its remote\n"
-          "counterpart. Check out this branch and integrate the remote changes\n"
-          "(e.g. 'git pull ...') before pushing again.\n"
+          "counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+          "before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_fetch_first[] =
-       N_("Updates were rejected because the remote contains work that you do\n"
-          "not have locally. This is usually caused by another repository pushing\n"
-          "to the same ref. You may want to first integrate the remote changes\n"
-          "(e.g., 'git pull ...') before pushing again.\n"
+       N_("Updates were rejected because the remote contains work that you do not\n"
+          "have locally. This is usually caused by another repository pushing to\n"
+          "the same ref. If you want to integrate the remote changes, use\n"
+          "'git pull' before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_already_exists[] =
@@ -316,10 +326,10 @@ static const char message_advice_ref_needs_force[] =
           "without using the '--force' option.\n");
 
 static const char message_advice_ref_needs_update[] =
-       N_("Updates were rejected because the tip of the remote-tracking\n"
-          "branch has been updated since the last checkout. You may want\n"
-          "to integrate those changes locally (e.g., 'git pull ...')\n"
-          "before forcing an update.\n");
+       N_("Updates were rejected because the tip of the remote-tracking branch has\n"
+          "been updated since the last checkout. If you want to integrate the\n"
+          "remote changes, use 'git pull' before pushing again.\n"
+          "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static void advise_pull_before_push(void)
 {
@@ -381,7 +391,7 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
        if (!is_empty_cas(&cas)) {
                if (!transport->smart_options)
                        die("underlying transport does not support --%s option",
-                           CAS_OPT_NAME);
+                           "force-with-lease");
                transport->smart_options->cas = &cas;
        }
 
@@ -466,8 +476,16 @@ static int option_parse_recurse_submodules(const struct option *opt,
 
        if (unset)
                *recurse_submodules = RECURSE_SUBMODULES_OFF;
-       else
-               *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
+       else {
+               if (!strcmp(arg, "only-is-on-demand")) {
+                       if (*recurse_submodules == RECURSE_SUBMODULES_ONLY) {
+                               warning(_("recursing into submodule with push.recurseSubmodules=only; using on-demand instead"));
+                               *recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
+                       }
+               } else {
+                       *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
+               }
+       }
 
        return 0;
 }
@@ -490,15 +508,11 @@ static void set_push_cert_flags(int *flags, int v)
 }
 
 
-static int git_push_config(const char *k, const char *v, void *cb)
+static int git_push_config(const char *k, const char *v,
+                          const struct config_context *ctx, void *cb)
 {
        const char *slot_name;
        int *flags = cb;
-       int status;
-
-       status = git_gpg_config(k, v, NULL);
-       if (status)
-               return status;
 
        if (!strcmp(k, "push.followtags")) {
                if (git_config_bool(k, v))
@@ -511,26 +525,21 @@ static int git_push_config(const char *k, const char *v, void *cb)
                        *flags |= TRANSPORT_PUSH_AUTO_UPSTREAM;
                return 0;
        } else if (!strcmp(k, "push.gpgsign")) {
-               const char *value;
-               if (!git_config_get_value("push.gpgsign", &value)) {
-                       switch (git_parse_maybe_bool(value)) {
-                       case 0:
-                               set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
-                               break;
-                       case 1:
-                               set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS);
-                               break;
-                       default:
-                               if (value && !strcasecmp(value, "if-asked"))
-                                       set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
-                               else
-                                       return error(_("invalid value for '%s'"), k);
-                       }
+               switch (git_parse_maybe_bool(v)) {
+               case 0:
+                       set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
+                       break;
+               case 1:
+                       set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS);
+                       break;
+               default:
+                       if (!strcasecmp(v, "if-asked"))
+                               set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
+                       else
+                               return error(_("invalid value for '%s'"), k);
                }
        } else if (!strcmp(k, "push.recursesubmodules")) {
-               const char *value;
-               if (!git_config_get_value("push.recursesubmodules", &value))
-                       recurse_submodules = parse_push_recurse_submodules_arg(k, value);
+               recurse_submodules = parse_push_recurse_submodules_arg(k, v);
        } else if (!strcmp(k, "submodule.recurse")) {
                int val = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
@@ -562,7 +571,7 @@ static int git_push_config(const char *k, const char *v, void *cb)
                return 0;
        }
 
-       return git_default_config(k, v, NULL);
+       return git_default_config(k, v, ctx, NULL);
 }
 
 int cmd_push(int argc, const char **argv, const char *prefix)
@@ -580,15 +589,16 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT__VERBOSITY(&verbosity),
                OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
-               OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL),
+               OPT_BIT( 0 , "all", &flags, N_("push all branches"), TRANSPORT_PUSH_ALL),
+               OPT_ALIAS( 0 , "branches", "all"),
                OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"),
                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
                OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")),
-               OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
+               OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --branches or --mirror)")),
                OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
                OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
                OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
-               OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+               OPT_CALLBACK_F(0, "force-with-lease", &cas, N_("<refname>:<expect>"),
                               N_("require old value of ref to be at this value"),
                               PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option),
                OPT_BIT(0, TRANS_OPT_FORCE_IF_INCLUDES, &flags,
@@ -611,10 +621,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                                PARSE_OPT_OPTARG, option_parse_push_signed),
                OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
                OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
-               OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
-                               TRANSPORT_FAMILY_IPV4),
-               OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
-                               TRANSPORT_FAMILY_IPV6),
+               OPT_IPVERSION(&family),
                OPT_END()
        };
 
@@ -626,8 +633,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                : &push_options_config);
        set_push_cert_flags(&flags, push_cert);
 
-       if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
-               die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags");
+       die_for_incompatible_opt4(deleterefs, "--delete",
+                                 tags, "--tags",
+                                 flags & TRANSPORT_PUSH_ALL, "--all/--branches",
+                                 flags & TRANSPORT_PUSH_MIRROR, "--mirror");
        if (deleterefs && argc < 2)
                die(_("--delete doesn't make sense without any refs"));
 
@@ -664,19 +673,13 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
        if (flags & TRANSPORT_PUSH_ALL) {
-               if (tags)
-                       die(_("options '%s' and '%s' cannot be used together"), "--all", "--tags");
                if (argc >= 2)
                        die(_("--all can't be combined with refspecs"));
        }
        if (flags & TRANSPORT_PUSH_MIRROR) {
-               if (tags)
-                       die(_("options '%s' and '%s' cannot be used together"), "--mirror", "--tags");
                if (argc >= 2)
                        die(_("--mirror can't be combined with refspecs"));
        }
-       if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
-               die(_("options '%s' and '%s' cannot be used together"), "--all", "--mirror");
 
        if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES))
                cas.use_force_if_includes = 1;
index e2a74efb42a795a5139932bd052999d050cab420..f02cbac087db403cedae2e4b5269e5258059677f 100644 (file)
@@ -1,9 +1,10 @@
-#include "cache.h"
 #include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "range-diff.h"
 #include "config.h"
-#include "revision.h"
+#include "repository.h"
 
 static const char * const builtin_range_diff_usage[] = {
 N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"),
@@ -47,7 +48,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
 
        repo_diff_setup(the_repository, &diffopt);
 
-       options = parse_options_concat(range_diff_options, diffopt.parseopts);
+       options = add_diff_options(range_diff_options, &diffopt);
        argc = parse_options(argc, argv, prefix, options,
                             builtin_range_diff_usage, PARSE_OPT_KEEP_DASHDASH);
 
@@ -65,20 +66,20 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
 
        if (dash_dash == 3 ||
            (dash_dash < 0 && argc > 2 &&
-            !get_oid_committish(argv[0], &oid) &&
-            !get_oid_committish(argv[1], &oid) &&
-            !get_oid_committish(argv[2], &oid))) {
+            !repo_get_oid_committish(the_repository, argv[0], &oid) &&
+            !repo_get_oid_committish(the_repository, argv[1], &oid) &&
+            !repo_get_oid_committish(the_repository, argv[2], &oid))) {
                if (dash_dash < 0)
                        ; /* already validated arguments */
-               else if (get_oid_committish(argv[0], &oid))
+               else if (repo_get_oid_committish(the_repository, argv[0], &oid))
                        usage_msg_optf(_("not a revision: '%s'"),
                                       builtin_range_diff_usage, options,
                                       argv[0]);
-               else if (get_oid_committish(argv[1], &oid))
+               else if (repo_get_oid_committish(the_repository, argv[1], &oid))
                        usage_msg_optf(_("not a revision: '%s'"),
                                       builtin_range_diff_usage, options,
                                       argv[1]);
-               else if (get_oid_committish(argv[2], &oid))
+               else if (repo_get_oid_committish(the_repository, argv[2], &oid))
                        usage_msg_optf(_("not a revision: '%s'"),
                                       builtin_range_diff_usage, options,
                                       argv[2]);
index 9f1f33e95466a4b2ff4f99a3b5e95878e40c6d38..20e7db19737d5a5070068a131797042d37a936a5 100644 (file)
@@ -4,21 +4,24 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
 #include "config.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "object.h"
+#include "object-name.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
 #include "unpack-trees.h"
-#include "dir.h"
-#include "builtin.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "resolve-undo.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
 
 static int nr_trees;
 static int read_empty;
@@ -38,11 +41,13 @@ static int list_tree(struct object_id *oid)
 }
 
 static const char * const read_tree_usage[] = {
-       N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
+       N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>)\n"
+          "              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+          "              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
        NULL
 };
 
-static int index_output_cb(const struct option *opt, const char *arg,
+static int index_output_cb(const struct option *opt UNUSED, const char *arg,
                                 int unset)
 {
        BUG_ON_OPT_NEG(unset);
@@ -85,9 +90,9 @@ static int debug_merge(const struct cache_entry * const *stages,
 {
        int i;
 
-       printf("* %d-way merge\n", o->merge_size);
+       printf("* %d-way merge\n", o->internal.merge_size);
        debug_stage("index", stages[0], o);
-       for (i = 1; i <= o->merge_size; i++) {
+       for (i = 1; i <= o->internal.merge_size; i++) {
                char buf[24];
                xsnprintf(buf, sizeof(buf), "ent#%d", i);
                debug_stage(buf, stages[i], o);
@@ -95,12 +100,13 @@ static int debug_merge(const struct cache_entry * const *stages,
        return 0;
 }
 
-static int git_read_tree_config(const char *var, const char *value, void *cb)
+static int git_read_tree_config(const char *var, const char *value,
+                               const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "submodule.recurse"))
                return git_default_submodule_config(var, value, cb);
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
@@ -112,6 +118,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
        int prefix_set = 0;
        struct lock_file lock_file = LOCK_INIT;
        const struct option read_tree_options[] = {
+               OPT__SUPER_PREFIX(&opts.super_prefix),
                OPT_CALLBACK_F(0, "index-output", NULL, N_("file"),
                  N_("write resulting index to <file>"),
                  PARSE_OPT_NONEG, index_output_cb),
@@ -141,7 +148,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
                OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")),
                OPT_BOOL(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
                         N_("skip applying sparse checkout filter")),
-               OPT_BOOL(0, "debug-unpack", &opts.debug_unpack,
+               OPT_BOOL(0, "debug-unpack", &opts.internal.debug_unpack,
                         N_("debug unpack-trees")),
                OPT_CALLBACK_F(0, "recurse-submodules", NULL,
                            "checkout", "control recursive updating of submodules",
@@ -174,7 +181,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
        /*
         * NEEDSWORK
@@ -186,16 +193,16 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
         */
 
        if (opts.reset || opts.merge || opts.prefix) {
-               if (read_cache_unmerged() && (opts.prefix || opts.merge))
+               if (repo_read_index_unmerged(the_repository) && (opts.prefix || opts.merge))
                        die(_("You need to resolve your current index first"));
                stage = opts.merge = 1;
        }
-       resolve_undo_clear();
+       resolve_undo_clear_index(&the_index);
 
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
 
-               if (get_oid(arg, &oid))
+               if (repo_get_oid(the_repository, arg, &oid))
                        die("Not a valid object name %s", arg);
                if (list_tree(&oid) < 0)
                        die("failed to unpack tree object %s", arg);
@@ -230,7 +237,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
                        break;
                case 2:
                        opts.fn = twoway_merge;
-                       opts.initial_checkout = is_cache_unborn();
+                       opts.initial_checkout = is_index_unborn(&the_index);
                        break;
                case 3:
                default:
@@ -244,10 +251,14 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
                        opts.head_idx = 1;
        }
 
-       if (opts.debug_unpack)
+       if (opts.internal.debug_unpack)
                opts.fn = debug_merge;
 
-       cache_tree_free(&active_cache_tree);
+       /* If we're going to prime_cache_tree later, skip cache tree update */
+       if (nr_trees == 1 && !opts.prefix)
+               opts.skip_cache_tree_update = 1;
+
+       cache_tree_free(&the_index.cache_tree);
        for (i = 0; i < nr_trees; i++) {
                struct tree *tree = trees[i];
                parse_tree(tree);
@@ -256,7 +267,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
        if (unpack_trees(nr_trees, t, &opts))
                return 128;
 
-       if (opts.debug_unpack || opts.dry_run)
+       if (opts.internal.debug_unpack || opts.dry_run)
                return 0; /* do not write the index out */
 
        /*
index 56e4214b44104a445f8b68d185c0aadd3442247d..995818c28d2604d8bc3435e4947f90d581a87d59 100644 (file)
@@ -4,20 +4,23 @@
  * Copyright (c) 2018 Pratik Karki
  */
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "run-command.h"
-#include "exec-cmd.h"
 #include "strvec.h"
 #include "dir.h"
-#include "packfile.h"
 #include "refs.h"
-#include "quote.h"
 #include "config.h"
-#include "cache-tree.h"
 #include "unpack-trees.h"
 #include "lockfile.h"
+#include "object-file.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
 #include "commit.h"
 #include "diff.h"
 #include "wt-status.h"
 #include "sequencer.h"
 #include "rebase-interactive.h"
 #include "reset.h"
+#include "trace2.h"
 #include "hook.h"
 
-#define DEFAULT_REFLOG_ACTION "rebase"
-
 static char const * const builtin_rebase_usage[] = {
        N_("git rebase [-i] [options] [--exec <cmd>] "
                "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
@@ -59,6 +61,26 @@ enum empty_type {
        EMPTY_ASK
 };
 
+enum action {
+       ACTION_NONE = 0,
+       ACTION_CONTINUE,
+       ACTION_SKIP,
+       ACTION_ABORT,
+       ACTION_QUIT,
+       ACTION_EDIT_TODO,
+       ACTION_SHOW_CURRENT_PATCH
+};
+
+static const char *action_names[] = {
+       "undefined",
+       "continue",
+       "skip",
+       "abort",
+       "quit",
+       "edit_todo",
+       "show_current_patch"
+};
+
 struct rebase_options {
        enum rebase_type type;
        enum empty_type empty;
@@ -68,7 +90,7 @@ struct rebase_options {
        const char *upstream_name;
        const char *upstream_arg;
        char *head_name;
-       struct object_id orig_head;
+       struct commit *orig_head;
        struct commit *onto;
        const char *onto_name;
        const char *revisions;
@@ -85,7 +107,8 @@ struct rebase_options {
                REBASE_INTERACTIVE_EXPLICIT = 1<<4,
        } flags;
        struct strvec git_am_opts;
-       const char *action;
+       enum action action;
+       char *reflog_action;
        int signoff;
        int allow_rerere_autoupdate;
        int keep_empty;
@@ -94,15 +117,19 @@ struct rebase_options {
        int autostash;
        int committer_date_is_author_date;
        int ignore_date;
-       char *cmd;
+       struct string_list exec;
        int allow_empty_message;
        int rebase_merges, rebase_cousins;
-       char *strategy, *strategy_opts;
+       char *strategy;
+       struct string_list strategy_opts;
        struct strbuf git_format_patch_opt;
        int reschedule_failed_exec;
        int reapply_cherry_picks;
        int fork_point;
        int update_refs;
+       int config_autosquash;
+       int config_rebase_merges;
+       int config_update_refs;
 };
 
 #define REBASE_OPTIONS_INIT {                          \
@@ -112,8 +139,17 @@ struct rebase_options {
                .default_backend = "merge",             \
                .flags = REBASE_NO_QUIET,               \
                .git_am_opts = STRVEC_INIT,             \
+               .exec = STRING_LIST_INIT_NODUP,         \
                .git_format_patch_opt = STRBUF_INIT,    \
                .fork_point = -1,                       \
+               .reapply_cherry_picks = -1,             \
+               .allow_empty_message = 1,               \
+               .autosquash = -1,                       \
+               .rebase_merges = -1,                    \
+               .config_rebase_merges = -1,             \
+               .update_refs = -1,                      \
+               .config_update_refs = -1,               \
+               .strategy_opts = STRING_LIST_INIT_NODUP,\
        }
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -139,6 +175,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
                                        opts->committer_date_is_author_date;
        replay.ignore_date = opts->ignore_date;
        replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
+       replay.reflog_action = xstrdup(opts->reflog_action);
        if (opts->strategy)
                replay.strategy = xstrdup_or_null(opts->strategy);
        else if (!replay.strategy && replay.default_strategy) {
@@ -146,8 +183,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
                replay.default_strategy = NULL;
        }
 
-       if (opts->strategy_opts)
-               parse_strategy_opts(&replay, opts->strategy_opts);
+       for (size_t i = 0; i < opts->strategy_opts.nr; i++)
+               strvec_push(&replay.xopts, opts->strategy_opts.items[i].string);
 
        if (opts->squash_onto) {
                oidcpy(&replay.squash_onto, opts->squash_onto);
@@ -157,24 +194,6 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
        return replay;
 }
 
-enum action {
-       ACTION_NONE = 0,
-       ACTION_CONTINUE,
-       ACTION_SKIP,
-       ACTION_ABORT,
-       ACTION_QUIT,
-       ACTION_EDIT_TODO,
-       ACTION_SHOW_CURRENT_PATCH
-};
-
-static const char *action_names[] = { "undefined",
-                                     "continue",
-                                     "skip",
-                                     "abort",
-                                     "quit",
-                                     "edit_todo",
-                                     "show_current_patch" };
-
 static int edit_todo_file(unsigned flags)
 {
        const char *todo_file = rebase_path_todo();
@@ -185,7 +204,7 @@ static int edit_todo_file(unsigned flags)
        if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
                return error_errno(_("could not read '%s'."), todo_file);
 
-       strbuf_stripspace(&todo_list.buf, 1);
+       strbuf_stripspace(&todo_list.buf, comment_line_char);
        res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
        if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
                                            NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
@@ -207,13 +226,15 @@ static int get_revision_ranges(struct commit *upstream, struct commit *onto,
        *revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
                             oid_to_hex(orig_head));
 
-       shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV);
+       shorthead = repo_find_unique_abbrev(the_repository, orig_head,
+                                           DEFAULT_ABBREV);
 
        if (upstream) {
                const char *shortrev;
 
-               shortrev = find_unique_abbrev(&base_rev->object.oid,
-                                             DEFAULT_ABBREV);
+               shortrev = repo_find_unique_abbrev(the_repository,
+                                                  &base_rev->object.oid,
+                                                  DEFAULT_ABBREV);
 
                *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
        } else
@@ -241,38 +262,22 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,
        return write_basic_state(opts, head_name, onto, orig_head);
 }
 
-static void split_exec_commands(const char *cmd, struct string_list *commands)
-{
-       if (cmd && *cmd) {
-               string_list_split(commands, cmd, '\n', -1);
-
-               /* rebase.c adds a new line to cmd after every command,
-                * so here the last command is always empty */
-               string_list_remove_empty_items(commands, 0);
-       }
-}
-
 static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
 {
-       int ret;
+       int ret = -1;
        char *revisions = NULL, *shortrevisions = NULL;
        struct strvec make_script_args = STRVEC_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
        struct replay_opts replay = get_replay_opts(opts);
-       struct string_list commands = STRING_LIST_INIT_DUP;
 
-       if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head,
+       if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head->object.oid,
                                &revisions, &shortrevisions))
-               return -1;
+               goto cleanup;
 
        if (init_basic_state(&replay,
                             opts->head_name ? opts->head_name : "detached HEAD",
-                            opts->onto, &opts->orig_head)) {
-               free(revisions);
-               free(shortrevisions);
-
-               return -1;
-       }
+                            opts->onto, &opts->orig_head->object.oid))
+               goto cleanup;
 
        if (!opts->upstream && opts->squash_onto)
                write_file(path_squash_onto(), "%s\n",
@@ -290,20 +295,19 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
        if (ret)
                error(_("could not generate todo list"));
        else {
-               discard_cache();
+               discard_index(&the_index);
                if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
                                                &todo_list))
                        BUG("unusable todo list");
 
-               split_exec_commands(opts->cmd, &commands);
                ret = complete_action(the_repository, &replay, flags,
                        shortrevisions, opts->onto_name, opts->onto,
-                       &opts->orig_head, &commands, opts->autosquash,
-                       opts->update_refs,
-                       &todo_list);
+                       &opts->orig_head->object.oid, &opts->exec,
+                       opts->autosquash, opts->update_refs, &todo_list);
        }
 
-       string_list_clear(&commands, 0);
+cleanup:
+       replay_opts_release(&replay);
        free(revisions);
        free(shortrevisions);
        todo_list_release(&todo_list);
@@ -312,8 +316,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
        return ret;
 }
 
-static int run_sequencer_rebase(struct rebase_options *opts,
-                                 enum action command)
+static int run_sequencer_rebase(struct rebase_options *opts)
 {
        unsigned flags = 0;
        int abbreviate_commands = 0, ret = 0;
@@ -328,7 +331,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
        flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
        flags |= opts->flags & REBASE_NO_QUIET ? TODO_LIST_WARN_SKIPPED_CHERRY_PICKS : 0;
 
-       switch (command) {
+       switch (opts->action) {
        case ACTION_NONE: {
                if (!opts->onto && !opts->upstream)
                        die(_("a base commit must be provided with --upstream or --onto"));
@@ -346,6 +349,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
                struct replay_opts replay_opts = get_replay_opts(opts);
 
                ret = sequencer_continue(the_repository, &replay_opts);
+               replay_opts_release(&replay_opts);
                break;
        }
        case ACTION_EDIT_TODO:
@@ -361,26 +365,12 @@ static int run_sequencer_rebase(struct rebase_options *opts,
                break;
        }
        default:
-               BUG("invalid command '%d'", command);
+               BUG("invalid command '%d'", opts->action);
        }
 
        return ret;
 }
 
-static void imply_merge(struct rebase_options *opts, const char *option);
-static int parse_opt_keep_empty(const struct option *opt, const char *arg,
-                               int unset)
-{
-       struct rebase_options *opts = opt->value;
-
-       BUG_ON_OPT_ARG(arg);
-
-       imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
-       opts->keep_empty = !unset;
-       opts->type = REBASE_MERGE;
-       return 0;
-}
-
 static int is_merge(struct rebase_options *opts)
 {
        return opts->type == REBASE_MERGE;
@@ -431,9 +421,9 @@ static int read_basic_state(struct rebase_options *opts)
        opts->head_name = starts_with(head_name.buf, "refs/") ?
                xstrdup(head_name.buf) : NULL;
        strbuf_release(&head_name);
-       if (get_oid(buf.buf, &oid))
-               return error(_("could not get 'onto': '%s'"), buf.buf);
-       opts->onto = lookup_commit_or_die(&oid, buf.buf);
+       if (get_oid_hex(buf.buf, &oid) ||
+           !(opts->onto = lookup_commit_object(the_repository, &oid)))
+               return error(_("invalid onto: '%s'"), buf.buf);
 
        /*
         * We always write to orig-head, but interactive rebase used to write to
@@ -448,7 +438,8 @@ static int read_basic_state(struct rebase_options *opts)
        } else if (!read_oneliner(&buf, state_dir_path("head", opts),
                                  READ_ONELINER_WARN_MISSING))
                return -1;
-       if (get_oid(buf.buf, &opts->orig_head))
+       if (get_oid_hex(buf.buf, &oid) ||
+           !(opts->orig_head = lookup_commit_object(the_repository, &oid)))
                return error(_("invalid orig-head: '%s'"), buf.buf);
 
        if (file_exists(state_dir_path("quiet", opts)))
@@ -487,24 +478,6 @@ static int read_basic_state(struct rebase_options *opts)
                opts->gpg_sign_opt = xstrdup(buf.buf);
        }
 
-       if (file_exists(state_dir_path("strategy", opts))) {
-               strbuf_reset(&buf);
-               if (!read_oneliner(&buf, state_dir_path("strategy", opts),
-                                  READ_ONELINER_WARN_MISSING))
-                       return -1;
-               free(opts->strategy);
-               opts->strategy = xstrdup(buf.buf);
-       }
-
-       if (file_exists(state_dir_path("strategy_opts", opts))) {
-               strbuf_reset(&buf);
-               if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
-                                  READ_ONELINER_WARN_MISSING))
-                       return -1;
-               free(opts->strategy_opts);
-               opts->strategy_opts = xstrdup(buf.buf);
-       }
-
        strbuf_release(&buf);
 
        return 0;
@@ -517,17 +490,11 @@ static int rebase_write_basic_state(struct rebase_options *opts)
        write_file(state_dir_path("onto", opts), "%s",
                   opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
        write_file(state_dir_path("orig-head", opts), "%s",
-                  oid_to_hex(&opts->orig_head));
+                  oid_to_hex(&opts->orig_head->object.oid));
        if (!(opts->flags & REBASE_NO_QUIET))
                write_file(state_dir_path("quiet", opts), "%s", "");
        if (opts->flags & REBASE_VERBOSE)
                write_file(state_dir_path("verbose", opts), "%s", "");
-       if (opts->strategy)
-               write_file(state_dir_path("strategy", opts), "%s",
-                          opts->strategy);
-       if (opts->strategy_opts)
-               write_file(state_dir_path("strategy_opts", opts), "%s",
-                          opts->strategy_opts);
        if (opts->allow_rerere_autoupdate > 0)
                write_file(state_dir_path("allow_rerere_autoupdate", opts),
                           "-%s-rerere-autoupdate",
@@ -560,6 +527,7 @@ static int finish_rebase(struct rebase_options *opts)
 
                replay.action = REPLAY_INTERACTIVE_REBASE;
                ret = sequencer_remove_state(&replay);
+               replay_opts_release(&replay);
        } else {
                strbuf_addstr(&dir, opts->state_dir);
                if (remove_dir_recursively(&dir, 0))
@@ -583,10 +551,11 @@ static int move_to_original_branch(struct rebase_options *opts)
        if (!opts->onto)
                BUG("move_to_original_branch without onto");
 
-       strbuf_addf(&branch_reflog, "rebase finished: %s onto %s",
+       strbuf_addf(&branch_reflog, "%s (finish): %s onto %s",
+                   opts->reflog_action,
                    opts->head_name, oid_to_hex(&opts->onto->object.oid));
-       strbuf_addf(&head_reflog, "rebase finished: returning to %s",
-                   opts->head_name);
+       strbuf_addf(&head_reflog, "%s (finish): returning to %s",
+                   opts->reflog_action, opts->head_name);
        ropts.branch = opts->head_name;
        ropts.flags = RESET_HEAD_REFS_ONLY;
        ropts.branch_msg = branch_reflog.buf;
@@ -609,14 +578,14 @@ static int run_am(struct rebase_options *opts)
 {
        struct child_process am = CHILD_PROCESS_INIT;
        struct child_process format_patch = CHILD_PROCESS_INIT;
-       struct strbuf revisions = STRBUF_INIT;
        int status;
        char *rebased_patches;
 
        am.git_cmd = 1;
        strvec_push(&am.args, "am");
-
-       if (opts->action && !strcmp("continue", opts->action)) {
+       strvec_pushf(&am.env, GIT_REFLOG_ACTION_ENVIRONMENT "=%s (pick)",
+                    opts->reflog_action);
+       if (opts->action == ACTION_CONTINUE) {
                strvec_push(&am.args, "--resolved");
                strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
                if (opts->gpg_sign_opt)
@@ -627,7 +596,7 @@ static int run_am(struct rebase_options *opts)
 
                return move_to_original_branch(opts);
        }
-       if (opts->action && !strcmp("skip", opts->action)) {
+       if (opts->action == ACTION_SKIP) {
                strvec_push(&am.args, "--skip");
                strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
                status = run_command(&am);
@@ -636,18 +605,11 @@ static int run_am(struct rebase_options *opts)
 
                return move_to_original_branch(opts);
        }
-       if (opts->action && !strcmp("show-current-patch", opts->action)) {
+       if (opts->action == ACTION_SHOW_CURRENT_PATCH) {
                strvec_push(&am.args, "--show-current-patch");
                return run_command(&am);
        }
 
-       strbuf_addf(&revisions, "%s...%s",
-                   oid_to_hex(opts->root ?
-                              /* this is now equivalent to !opts->upstream */
-                              &opts->onto->object.oid :
-                              &opts->upstream->object.oid),
-                   oid_to_hex(&opts->orig_head));
-
        rebased_patches = xstrdup(git_path("rebased-patches"));
        format_patch.out = open(rebased_patches,
                                O_WRONLY | O_CREAT | O_TRUNC, 0666);
@@ -662,13 +624,18 @@ static int run_am(struct rebase_options *opts)
        format_patch.git_cmd = 1;
        strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
                     "--full-index", "--cherry-pick", "--right-only",
-                    "--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
+                    "--default-prefix", "--no-renames",
                     "--no-cover-letter", "--pretty=mboxrd", "--topo-order",
                     "--no-base", NULL);
        if (opts->git_format_patch_opt.len)
                strvec_split(&format_patch.args,
                             opts->git_format_patch_opt.buf);
-       strvec_push(&format_patch.args, revisions.buf);
+       strvec_pushf(&format_patch.args, "%s...%s",
+                    oid_to_hex(opts->root ?
+                               /* this is now equivalent to !opts->upstream */
+                               &opts->onto->object.oid :
+                               &opts->upstream->object.oid),
+                    oid_to_hex(&opts->orig_head->object.oid));
        if (opts->restrict_revision)
                strvec_pushf(&format_patch.args, "^%s",
                             oid_to_hex(&opts->restrict_revision->object.oid));
@@ -680,9 +647,9 @@ static int run_am(struct rebase_options *opts)
                free(rebased_patches);
                strvec_clear(&am.args);
 
-               ropts.oid = &opts->orig_head;
+               ropts.oid = &opts->orig_head->object.oid;
                ropts.branch = opts->head_name;
-               ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
+               ropts.default_reflog_action = opts->reflog_action;
                reset_head(the_repository, &ropts);
                error(_("\ngit encountered an error while preparing the "
                        "patches to replay\n"
@@ -691,10 +658,8 @@ static int run_am(struct rebase_options *opts)
                        "As a result, git cannot rebase them."),
                      opts->revisions);
 
-               strbuf_release(&revisions);
                return status;
        }
-       strbuf_release(&revisions);
 
        am.in = open(rebased_patches, O_RDONLY);
        if (am.in < 0) {
@@ -729,17 +694,15 @@ static int run_am(struct rebase_options *opts)
        return status;
 }
 
-static int run_specific_rebase(struct rebase_options *opts, enum action action)
+static int run_specific_rebase(struct rebase_options *opts)
 {
        int status;
 
        if (opts->type == REBASE_MERGE) {
                /* Run sequencer-based rebase */
                setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
-               if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
+               if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT))
                        setenv("GIT_SEQUENCE_EDITOR", ":", 1);
-                       opts->autosquash = 0;
-               }
                if (opts->gpg_sign_opt) {
                        /* remove the leading "-S" */
                        char *tmp = xstrdup(opts->gpg_sign_opt + 2);
@@ -747,7 +710,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
                        opts->gpg_sign_opt = tmp;
                }
 
-               status = run_sequencer_rebase(opts, action);
+               status = run_sequencer_rebase(opts);
        } else if (opts->type == REBASE_APPLY)
                status = run_am(opts);
        else
@@ -773,7 +736,18 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        return status ? -1 : 0;
 }
 
-static int rebase_config(const char *var, const char *value, void *data)
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+       if (!strcmp("no-rebase-cousins", value))
+               options->rebase_cousins = 0;
+       else if (!strcmp("rebase-cousins", value))
+               options->rebase_cousins = 1;
+       else
+               die(_("Unknown rebase-merges mode: %s"), value);
+}
+
+static int rebase_config(const char *var, const char *value,
+                        const struct config_context *ctx, void *data)
 {
        struct rebase_options *opts = data;
 
@@ -786,7 +760,7 @@ static int rebase_config(const char *var, const char *value, void *data)
        }
 
        if (!strcmp(var, "rebase.autosquash")) {
-               opts->autosquash = git_config_bool(var, value);
+               opts->config_autosquash = git_config_bool(var, value);
                return 0;
        }
 
@@ -802,8 +776,19 @@ static int rebase_config(const char *var, const char *value, void *data)
                return 0;
        }
 
+       if (!strcmp(var, "rebase.rebasemerges")) {
+               opts->config_rebase_merges = git_parse_maybe_bool(value);
+               if (opts->config_rebase_merges < 0) {
+                       opts->config_rebase_merges = 1;
+                       parse_rebase_merges_value(opts, value);
+               } else {
+                       opts->rebase_cousins = 0;
+               }
+               return 0;
+       }
+
        if (!strcmp(var, "rebase.updaterefs")) {
-               opts->update_refs = git_config_bool(var, value);
+               opts->config_update_refs = git_config_bool(var, value);
                return 0;
        }
 
@@ -821,7 +806,7 @@ static int rebase_config(const char *var, const char *value, void *data)
                return git_config_string(&opts->default_backend, var, value);
        }
 
-       return git_default_config(var, value, data);
+       return git_default_config(var, value, ctx, data);
 }
 
 static int checkout_up_to_date(struct rebase_options *options)
@@ -831,9 +816,8 @@ static int checkout_up_to_date(struct rebase_options *options)
        int ret = 0;
 
        strbuf_addf(&buf, "%s: checkout %s",
-                   getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
-                   options->switch_to);
-       ropts.oid = &options->orig_head;
+                   options->reflog_action, options->switch_to);
+       ropts.oid = &options->orig_head->object.oid;
        ropts.branch = options->head_name;
        ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
        if (!ropts.branch)
@@ -854,7 +838,7 @@ static int checkout_up_to_date(struct rebase_options *options)
 static int is_linear_history(struct commit *from, struct commit *to)
 {
        while (to && to != from) {
-               parse_commit(to);
+               repo_parse_commit(the_repository, to);
                if (!to->parents)
                        return 1;
                if (to->parents->next)
@@ -866,33 +850,24 @@ static int is_linear_history(struct commit *from, struct commit *to)
 
 static int can_fast_forward(struct commit *onto, struct commit *upstream,
                            struct commit *restrict_revision,
-                           struct object_id *head_oid, struct object_id *merge_base)
+                           struct commit *head, struct object_id *branch_base)
 {
-       struct commit *head = lookup_commit(the_repository, head_oid);
        struct commit_list *merge_bases = NULL;
        int res = 0;
 
-       if (!head)
-               goto done;
+       if (is_null_oid(branch_base))
+               goto done; /* fill_branch_base() found multiple merge bases */
 
-       merge_bases = get_merge_bases(onto, head);
-       if (!merge_bases || merge_bases->next) {
-               oidcpy(merge_base, null_oid());
+       if (!oideq(branch_base, &onto->object.oid))
                goto done;
-       }
 
-       oidcpy(merge_base, &merge_bases->item->object.oid);
-       if (!oideq(merge_base, &onto->object.oid))
-               goto done;
-
-       if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base))
+       if (restrict_revision && !oideq(&restrict_revision->object.oid, branch_base))
                goto done;
 
        if (!upstream)
                goto done;
 
-       free_commit_list(merge_bases);
-       merge_bases = get_merge_bases(upstream, head);
+       merge_bases = repo_get_merge_bases(the_repository, upstream, head);
        if (!merge_bases || merge_bases->next)
                goto done;
 
@@ -906,6 +881,21 @@ done:
        return res && is_linear_history(onto, head);
 }
 
+static void fill_branch_base(struct rebase_options *options,
+                           struct object_id *branch_base)
+{
+       struct commit_list *merge_bases = NULL;
+
+       merge_bases = repo_get_merge_bases(the_repository, options->onto,
+                                          options->orig_head);
+       if (!merge_bases || merge_bases->next)
+               oidcpy(branch_base, null_oid());
+       else
+               oidcpy(branch_base, &merge_bases->item->object.oid);
+
+       free_commit_list(merge_bases);
+}
+
 static int parse_opt_am(const struct option *opt, const char *arg, int unset)
 {
        struct rebase_options *opts = opt->value;
@@ -913,6 +903,9 @@ static int parse_opt_am(const struct option *opt, const char *arg, int unset)
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
 
+       if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_APPLY)
+           die(_("apply options and merge options cannot be used together"));
+
        opts->type = REBASE_APPLY;
 
        return 0;
@@ -926,8 +919,10 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
 
-       if (!is_merge(opts))
-               opts->type = REBASE_MERGE;
+       if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+           die(_("apply options and merge options cannot be used together"));
+
+       opts->type = REBASE_MERGE;
 
        return 0;
 }
@@ -941,6 +936,9 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
 
+       if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+           die(_("apply options and merge options cannot be used together"));
+
        opts->type = REBASE_MERGE;
        opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
 
@@ -959,6 +957,18 @@ static enum empty_type parse_empty_value(const char *value)
        die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
 }
 
+static int parse_opt_keep_empty(const struct option *opt, const char *arg,
+                               int unset)
+{
+       struct rebase_options *opts = opt->value;
+
+       BUG_ON_OPT_ARG(arg);
+
+       imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
+       opts->keep_empty = !unset;
+       return 0;
+}
+
 static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
 {
        struct rebase_options *options = opt->value;
@@ -970,6 +980,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+       struct rebase_options *options = opt->value;
+
+       options->rebase_merges = !unset;
+       options->rebase_cousins = 0;
+
+       if (arg) {
+               if (!*arg) {
+                       warning(_("--rebase-merges with an empty string "
+                                 "argument is deprecated and will stop "
+                                 "working in a future version of Git. Use "
+                                 "--rebase-merges without an argument "
+                                 "instead, which does the same thing."));
+                       return 0;
+               }
+               parse_rebase_merges_value(options, arg);
+       }
+
+       return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
        struct branch *current_branch = branch_get(NULL);
@@ -1000,23 +1032,6 @@ static void NORETURN error_on_missing_default_upstream(void)
        exit(1);
 }
 
-static void set_reflog_action(struct rebase_options *options)
-{
-       const char *env;
-       struct strbuf buf = STRBUF_INIT;
-
-       if (!is_merge(options))
-               return;
-
-       env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
-       if (env && strcmp("rebase", env))
-               return; /* only override it if it is "rebase" */
-
-       strbuf_addf(&buf, "rebase (%s)", options->action);
-       setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
-       strbuf_release(&buf);
-}
-
 static int check_exec_cmd(const char *cmd)
 {
        if (strchr(cmd, '\n'))
@@ -1039,15 +1054,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        struct strbuf msg = STRBUF_INIT;
        struct strbuf revisions = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
-       struct object_id merge_base;
+       struct object_id branch_base;
        int ignore_whitespace = 0;
-       enum action action = ACTION_NONE;
        const char *gpg_sign = NULL;
-       struct string_list exec = STRING_LIST_INIT_NODUP;
-       const char *rebase_merges = NULL;
-       struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
+       char *keep_base_onto_name = NULL;
        int reschedule_failed_exec = -1;
        int allow_preemptive_ff = 1;
        int preserve_merges_selected = 0;
@@ -1090,18 +1102,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                OPT_BIT(0, "no-ff", &options.flags,
                        N_("cherry-pick all commits, even if unchanged"),
                        REBASE_FORCE),
-               OPT_CMDMODE(0, "continue", &action, N_("continue"),
+               OPT_CMDMODE(0, "continue", &options.action, N_("continue"),
                            ACTION_CONTINUE),
-               OPT_CMDMODE(0, "skip", &action,
+               OPT_CMDMODE(0, "skip", &options.action,
                            N_("skip current patch and continue"), ACTION_SKIP),
-               OPT_CMDMODE(0, "abort", &action,
+               OPT_CMDMODE(0, "abort", &options.action,
                            N_("abort and check out the original branch"),
                            ACTION_ABORT),
-               OPT_CMDMODE(0, "quit", &action,
+               OPT_CMDMODE(0, "quit", &options.action,
                            N_("abort but keep HEAD where it is"), ACTION_QUIT),
-               OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list "
+               OPT_CMDMODE(0, "edit-todo", &options.action, N_("edit the todo list "
                            "during an interactive rebase"), ACTION_EDIT_TODO),
-               OPT_CMDMODE(0, "show-current-patch", &action,
+               OPT_CMDMODE(0, "show-current-patch", &options.action,
                            N_("show the patch file being applied or merged"),
                            ACTION_SHOW_CURRENT_PATCH),
                OPT_CALLBACK_F(0, "apply", &options, NULL,
@@ -1121,7 +1133,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                 "instead of ignoring them"),
                              1, PARSE_OPT_HIDDEN),
                OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-               OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
+               OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)",
                               N_("how to handle commits that become empty"),
                               PARSE_OPT_NONEG, parse_opt_empty),
                OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1138,22 +1150,21 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        N_("GPG-sign commits"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                OPT_AUTOSTASH(&options.autostash),
-               OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+               OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"),
                                N_("add exec lines after each commit of the "
                                   "editable list")),
                OPT_BOOL_F(0, "allow-empty-message",
                           &options.allow_empty_message,
                           N_("allow rebasing commits with empty messages"),
                           PARSE_OPT_HIDDEN),
-               {OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-                       N_("mode"),
+               OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
                        N_("try to rebase merges instead of skipping them"),
-                       PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+                       PARSE_OPT_OPTARG, parse_opt_rebase_merges),
                OPT_BOOL(0, "fork-point", &options.fork_point,
                         N_("use 'merge-base --fork-point' to refine upstream")),
                OPT_STRING('s', "strategy", &options.strategy,
                           N_("strategy"), N_("use the given merge strategy")),
-               OPT_STRING_LIST('X', "strategy-option", &strategy_options,
+               OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
                                N_("option"),
                                N_("pass the argument through to the merge "
                                   "strategy")),
@@ -1175,7 +1186,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
-       options.allow_empty_message = 1;
        git_config(rebase_config, &options);
        /* options.gpg_sign_opt will be either "-S" or NULL */
        gpg_sign = options.gpg_sign_opt ? "" : NULL;
@@ -1192,7 +1202,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        } else if (is_directory(merge_dir())) {
                strbuf_reset(&buf);
                strbuf_addf(&buf, "%s/rewritten", merge_dir());
-               if (!(action == ACTION_ABORT) && is_directory(buf.buf)) {
+               if (!(options.action == ACTION_ABORT) && is_directory(buf.buf)) {
                        die(_("`rebase --preserve-merges` (-p) is no longer supported.\n"
                        "Use `git rebase --abort` to terminate current rebase.\n"
                        "Or downgrade to v2.33, or earlier, to complete the rebase."));
@@ -1219,7 +1229,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        "Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
                        "which is no longer supported; use 'merges' instead"));
 
-       if (action != ACTION_NONE && total_argc != 2) {
+       if (options.action != ACTION_NONE && total_argc != 2) {
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
        }
@@ -1233,42 +1243,47 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto");
                if (options.root)
                        die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
+               /*
+                * --keep-base defaults to --no-fork-point to keep the
+                * base the same.
+                */
+               if (options.fork_point < 0)
+                       options.fork_point = 0;
        }
-
        if (options.root && options.fork_point > 0)
                die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
 
-       if (action != ACTION_NONE && !in_progress)
+       if (options.action != ACTION_NONE && !in_progress)
                die(_("No rebase in progress?"));
-       setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
 
-       if (action == ACTION_EDIT_TODO && !is_merge(&options))
+       if (options.action == ACTION_EDIT_TODO && !is_merge(&options))
                die(_("The --edit-todo action can only be used during "
                      "interactive rebase."));
 
        if (trace2_is_enabled()) {
                if (is_merge(&options))
                        trace2_cmd_mode("interactive");
-               else if (exec.nr)
+               else if (options.exec.nr)
                        trace2_cmd_mode("interactive-exec");
                else
-                       trace2_cmd_mode(action_names[action]);
+                       trace2_cmd_mode(action_names[options.action]);
        }
 
-       switch (action) {
+       options.reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+       options.reflog_action =
+               xstrdup(options.reflog_action ? options.reflog_action : "rebase");
+
+       switch (options.action) {
        case ACTION_CONTINUE: {
                struct object_id head;
                struct lock_file lock_file = LOCK_INIT;
                int fd;
 
-               options.action = "continue";
-               set_reflog_action(&options);
-
                /* Sanity check */
-               if (get_oid("HEAD", &head))
+               if (repo_get_oid(the_repository, "HEAD", &head))
                        die(_("Cannot read HEAD"));
 
-               fd = hold_locked_index(&lock_file, 0);
+               fd = repo_hold_locked_index(the_repository, &lock_file, 0);
                if (repo_read_index(the_repository) < 0)
                        die(_("could not read index"));
                refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
@@ -1289,9 +1304,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        case ACTION_SKIP: {
                struct string_list merge_rr = STRING_LIST_INIT_DUP;
 
-               options.action = "skip";
-               set_reflog_action(&options);
-
                rerere_clear(the_repository, &merge_rr);
                string_list_clear(&merge_rr, 1);
                ropts.flags = RESET_HEAD_HARD;
@@ -1304,21 +1316,26 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        }
        case ACTION_ABORT: {
                struct string_list merge_rr = STRING_LIST_INIT_DUP;
-               options.action = "abort";
-               set_reflog_action(&options);
+               struct strbuf head_msg = STRBUF_INIT;
 
                rerere_clear(the_repository, &merge_rr);
                string_list_clear(&merge_rr, 1);
 
                if (read_basic_state(&options))
                        exit(1);
-               ropts.oid = &options.orig_head;
+
+               strbuf_addf(&head_msg, "%s (abort): returning to %s",
+                           options.reflog_action,
+                           options.head_name ? options.head_name
+                                             : oid_to_hex(&options.orig_head->object.oid));
+               ropts.oid = &options.orig_head->object.oid;
+               ropts.head_msg = head_msg.buf;
                ropts.branch = options.head_name;
                ropts.flags = RESET_HEAD_HARD;
-               ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
                if (reset_head(the_repository, &ropts) < 0)
                        die(_("could not move back to %s"),
-                           oid_to_hex(&options.orig_head));
+                           oid_to_hex(&options.orig_head->object.oid));
+               strbuf_release(&head_msg);
                remove_branch_state(the_repository, 0);
                ret = finish_rebase(&options);
                goto cleanup;
@@ -1330,6 +1347,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
                        replay.action = REPLAY_INTERACTIVE_REBASE;
                        ret = sequencer_remove_state(&replay);
+                       replay_opts_release(&replay);
                } else {
                        strbuf_reset(&buf);
                        strbuf_addstr(&buf, options.state_dir);
@@ -1341,17 +1359,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                goto cleanup;
        }
        case ACTION_EDIT_TODO:
-               options.action = "edit-todo";
                options.dont_finish_rebase = 1;
                goto run_rebase;
        case ACTION_SHOW_CURRENT_PATCH:
-               options.action = "show-current-patch";
                options.dont_finish_rebase = 1;
                goto run_rebase;
        case ACTION_NONE:
                break;
        default:
-               BUG("action: %d", action);
+               BUG("action: %d", options.action);
        }
 
        /* Make sure no rebase is in progress */
@@ -1375,9 +1391,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        }
 
        if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
-           (action != ACTION_NONE) ||
-           (exec.nr > 0) ||
-           options.autosquash) {
+           (options.action != ACTION_NONE) ||
+           (options.exec.nr > 0) ||
+           options.autosquash == 1) {
                allow_preemptive_ff = 0;
        }
        if (options.committer_date_is_author_date || options.ignore_date)
@@ -1400,8 +1416,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                }
        }
 
-       for (i = 0; i < exec.nr; i++)
-               if (check_exec_cmd(exec.items[i].string))
+       for (i = 0; i < options.exec.nr; i++)
+               if (check_exec_cmd(options.exec.items[i].string))
                        exit(1);
 
        if (!(options.flags & REBASE_NO_QUIET))
@@ -1410,34 +1426,34 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (options.empty != EMPTY_UNSPECIFIED)
                imply_merge(&options, "--empty");
 
-       if (options.reapply_cherry_picks)
-               imply_merge(&options, "--reapply-cherry-picks");
+       if (options.reapply_cherry_picks < 0)
+               /*
+                * We default to --no-reapply-cherry-picks unless
+                * --keep-base is given; when --keep-base is given, we want
+                * to default to --reapply-cherry-picks.
+                */
+               options.reapply_cherry_picks = keep_base;
+       else if (!keep_base)
+               /*
+                * The apply backend always searches for and drops cherry
+                * picks.  This is often not wanted with --keep-base, so
+                * --keep-base allows --reapply-cherry-picks to be
+                * simulated by altering the upstream such that
+                * cherry-picks cannot be detected and thus all commits are
+                * reapplied.  Thus, --[no-]reapply-cherry-picks is
+                * supported when --keep-base is specified, but not when
+                * --keep-base is left out.
+                */
+               imply_merge(&options, options.reapply_cherry_picks ?
+                                         "--reapply-cherry-picks" :
+                                         "--no-reapply-cherry-picks");
 
        if (gpg_sign)
                options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
 
-       if (exec.nr) {
-               int i;
-
+       if (options.exec.nr)
                imply_merge(&options, "--exec");
 
-               strbuf_reset(&buf);
-               for (i = 0; i < exec.nr; i++)
-                       strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
-               options.cmd = xstrdup(buf.buf);
-       }
-
-       if (rebase_merges) {
-               if (!*rebase_merges)
-                       ; /* default mode; do nothing */
-               else if (!strcmp("rebase-cousins", rebase_merges))
-                       options.rebase_cousins = 1;
-               else if (strcmp("no-rebase-cousins", rebase_merges))
-                       die(_("Unknown mode: %s"), rebase_merges);
-               options.rebase_merges = 1;
-               imply_merge(&options, "--rebase-merges");
-       }
-
        if (options.type == REBASE_APPLY) {
                if (ignore_whitespace)
                        strvec_push(&options.git_am_opts,
@@ -1450,43 +1466,19 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        } else {
                /* REBASE_MERGE */
                if (ignore_whitespace) {
-                       string_list_append(&strategy_options,
+                       string_list_append(&options.strategy_opts,
                                           "ignore-space-change");
                }
        }
 
-       if (strategy_options.nr) {
-               int i;
-
-               if (!options.strategy)
-                       options.strategy = "ort";
-
-               strbuf_reset(&buf);
-               for (i = 0; i < strategy_options.nr; i++)
-                       strbuf_addf(&buf, " --%s",
-                                   strategy_options.items[i].string);
-               options.strategy_opts = xstrdup(buf.buf);
-       }
+       if (options.strategy_opts.nr && !options.strategy)
+               options.strategy = "ort";
 
        if (options.strategy) {
                options.strategy = xstrdup(options.strategy);
-               switch (options.type) {
-               case REBASE_APPLY:
-                       die(_("--strategy requires --merge or --interactive"));
-               case REBASE_MERGE:
-                       /* compatible */
-                       break;
-               case REBASE_UNSPECIFIED:
-                       options.type = REBASE_MERGE;
-                       break;
-               default:
-                       BUG("unhandled rebase type (%d)", options.type);
-               }
+               imply_merge(&options, "--strategy");
        }
 
-       if (options.type == REBASE_MERGE)
-               imply_merge(&options, "--merge");
-
        if (options.root && !options.onto_name)
                imply_merge(&options, "--root without --onto");
 
@@ -1499,18 +1491,40 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        if (strcmp(options.git_am_opts.v[i], "-q"))
                                break;
 
-               if (i >= 0) {
+               if (i >= 0 || options.type == REBASE_APPLY) {
                        if (is_merge(&options))
                                die(_("apply options and merge options "
                                          "cannot be used together"));
+                       else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+                               die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
+                       else if (options.update_refs == -1 && options.config_update_refs == 1)
+                               die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
                        else
                                options.type = REBASE_APPLY;
                }
        }
 
+       if (options.update_refs == 1)
+               imply_merge(&options, "--update-refs");
+       options.update_refs = (options.update_refs >= 0) ? options.update_refs :
+                            ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
+
+       if (options.rebase_merges == 1)
+               imply_merge(&options, "--rebase-merges");
+       options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+                               ((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
+       if (options.autosquash == 1) {
+               imply_merge(&options, "--autosquash");
+       } else if (options.autosquash == -1) {
+               options.autosquash =
+                       options.config_autosquash &&
+                       (options.flags & REBASE_INTERACTIVE_EXPLICIT);
+       }
+
        if (options.type == REBASE_UNSPECIFIED) {
                if (!strcmp(options.default_backend, "merge"))
-                       imply_merge(&options, "--merge");
+                       options.type = REBASE_MERGE;
                else if (!strcmp(options.default_backend, "apply"))
                        options.type = REBASE_APPLY;
                else
@@ -1537,7 +1551,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (options.empty == EMPTY_UNSPECIFIED) {
                if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
                        options.empty = EMPTY_ASK;
-               else if (exec.nr > 0)
+               else if (options.exec.nr > 0)
                        options.empty = EMPTY_KEEP;
                else
                        options.empty = EMPTY_DROP;
@@ -1604,25 +1618,27 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
         */
        if (argc == 1) {
                /* Is it "rebase other branchname" or "rebase other commit"? */
+               struct object_id branch_oid;
                branch_name = argv[0];
                options.switch_to = argv[0];
 
                /* Is it a local branch? */
                strbuf_reset(&buf);
                strbuf_addf(&buf, "refs/heads/%s", branch_name);
-               if (!read_ref(buf.buf, &options.orig_head)) {
+               if (!read_ref(buf.buf, &branch_oid)) {
                        die_if_checked_out(buf.buf, 1);
                        options.head_name = xstrdup(buf.buf);
+                       options.orig_head =
+                               lookup_commit_object(the_repository,
+                                                    &branch_oid);
                /* If not is it a valid ref (branch or commit)? */
                } else {
-                       struct commit *commit =
+                       options.orig_head =
                                lookup_commit_reference_by_name(branch_name);
-                       if (!commit)
-                               die(_("no such branch/commit '%s'"),
-                                   branch_name);
-                       oidcpy(&options.orig_head, &commit->object.oid);
                        options.head_name = NULL;
                }
+               if (!options.orig_head)
+                       die(_("no such branch/commit '%s'"), branch_name);
        } else if (argc == 0) {
                /* Do not need to switch branches, we are already on it. */
                options.head_name =
@@ -1639,8 +1655,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        FREE_AND_NULL(options.head_name);
                        branch_name = "HEAD";
                }
-               if (get_oid("HEAD", &options.orig_head))
-                       die(_("Could not resolve HEAD to a revision"));
+               options.orig_head = lookup_commit_reference_by_name("HEAD");
+               if (!options.orig_head)
+                       die(_("Could not resolve HEAD to a commit"));
        } else
                BUG("unexpected number of arguments left to parse");
 
@@ -1650,11 +1667,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                strbuf_addstr(&buf, options.upstream_name);
                strbuf_addstr(&buf, "...");
                strbuf_addstr(&buf, branch_name);
-               options.onto_name = xstrdup(buf.buf);
+               options.onto_name = keep_base_onto_name = xstrdup(buf.buf);
        } else if (!options.onto_name)
                options.onto_name = options.upstream_name;
        if (strstr(options.onto_name, "...")) {
-               if (get_oid_mb(options.onto_name, &merge_base) < 0) {
+               if (repo_get_oid_mb(the_repository, options.onto_name, &branch_base) < 0) {
                        if (keep_base)
                                die(_("'%s': need exactly one merge base with branch"),
                                    options.upstream_name);
@@ -1662,7 +1679,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                die(_("'%s': need exactly one merge base"),
                                    options.onto_name);
                }
-               options.onto = lookup_commit_or_die(&merge_base,
+               options.onto = lookup_commit_or_die(&branch_base,
                                                    options.onto_name);
        } else {
                options.onto =
@@ -1670,15 +1687,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                if (!options.onto)
                        die(_("Does not point to a valid commit '%s'"),
                                options.onto_name);
+               fill_branch_base(&options, &branch_base);
        }
 
-       if (options.fork_point > 0) {
-               struct commit *head =
-                       lookup_commit_reference(the_repository,
-                                               &options.orig_head);
+       if (keep_base && options.reapply_cherry_picks)
+               options.upstream = options.onto;
+
+       if (options.fork_point > 0)
                options.restrict_revision =
-                       get_fork_point(options.upstream_name, head);
-       }
+                       get_fork_point(options.upstream_name, options.orig_head);
 
        if (repo_read_index(the_repository) < 0)
                die(_("could not read index"));
@@ -1703,13 +1720,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
         * Check if we are already based on onto with linear history,
         * in which case we could fast-forward without replacing the commits
         * with new commits recreated by replaying their changes.
-        *
-        * Note that can_fast_forward() initializes merge_base, so we have to
-        * call it before checking allow_preemptive_ff.
         */
-       if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
-                   &options.orig_head, &merge_base) &&
-           allow_preemptive_ff) {
+       if (allow_preemptive_ff &&
+           can_fast_forward(options.onto, options.upstream, options.restrict_revision,
+                            options.orig_head, &branch_base)) {
                int flag;
 
                if (!(options.flags & REBASE_FORCE)) {
@@ -1750,25 +1764,24 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                struct diff_options opts;
 
                if (options.flags & REBASE_VERBOSE) {
-                       if (is_null_oid(&merge_base))
+                       if (is_null_oid(&branch_base))
                                printf(_("Changes to %s:\n"),
                                       oid_to_hex(&options.onto->object.oid));
                        else
                                printf(_("Changes from %s to %s:\n"),
-                                      oid_to_hex(&merge_base),
+                                      oid_to_hex(&branch_base),
                                       oid_to_hex(&options.onto->object.oid));
                }
 
                /* We want color (if set), but no pager */
-               diff_setup(&opts);
-               opts.stat_width = -1; /* use full terminal width */
-               opts.stat_graph_width = -1; /* respect statGraphWidth config */
+               repo_diff_setup(the_repository, &opts);
+               init_diffstat_widths(&opts);
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
                diff_setup_done(&opts);
-               diff_tree_oid(is_null_oid(&merge_base) ?
-                             the_hash_algo->empty_tree : &merge_base,
+               diff_tree_oid(is_null_oid(&branch_base) ?
+                             the_hash_algo->empty_tree : &branch_base,
                              &options.onto->object.oid, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
@@ -1782,14 +1795,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                printf(_("First, rewinding head to replay your work on top of "
                         "it...\n"));
 
-       strbuf_addf(&msg, "%s: checkout %s",
-                   getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
+       strbuf_addf(&msg, "%s (start): checkout %s",
+                   options.reflog_action, options.onto_name);
        ropts.oid = &options.onto->object.oid;
-       ropts.orig_head = &options.orig_head,
+       ropts.orig_head = &options.orig_head->object.oid,
        ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD |
                        RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
        ropts.head_msg = msg.buf;
-       ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
+       ropts.default_reflog_action = options.reflog_action;
        if (reset_head(the_repository, &ropts))
                die(_("Could not detach HEAD"));
        strbuf_release(&msg);
@@ -1798,19 +1811,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
         * If the onto is a proper descendant of the tip of the branch, then
         * we just fast-forwarded.
         */
-       strbuf_reset(&msg);
-       if (oideq(&merge_base, &options.orig_head)) {
+       if (oideq(&branch_base, &options.orig_head->object.oid)) {
                printf(_("Fast-forwarded %s to %s.\n"),
                        branch_name, options.onto_name);
-               strbuf_addf(&msg, "rebase finished: %s onto %s",
-                       options.head_name ? options.head_name : "detached HEAD",
-                       oid_to_hex(&options.onto->object.oid));
-               memset(&ropts, 0, sizeof(ropts));
-               ropts.branch = options.head_name;
-               ropts.flags = RESET_HEAD_REFS_ONLY;
-               ropts.head_msg = msg.buf;
-               reset_head(the_repository, &ropts);
-               strbuf_release(&msg);
+               move_to_original_branch(&options);
                ret = finish_rebase(&options);
                goto cleanup;
        }
@@ -1820,21 +1824,25 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                    (options.restrict_revision ?
                     oid_to_hex(&options.restrict_revision->object.oid) :
                     oid_to_hex(&options.upstream->object.oid)),
-                   oid_to_hex(&options.orig_head));
+                   oid_to_hex(&options.orig_head->object.oid));
 
        options.revisions = revisions.buf;
 
 run_rebase:
-       ret = run_specific_rebase(&options, action);
+       ret = run_specific_rebase(&options);
 
 cleanup:
        strbuf_release(&buf);
        strbuf_release(&revisions);
+       free(options.reflog_action);
        free(options.head_name);
+       strvec_clear(&options.git_am_opts);
        free(options.gpg_sign_opt);
-       free(options.cmd);
+       string_list_clear(&options.exec, 0);
        free(options.strategy);
+       string_list_clear(&options.strategy_opts, 0);
        strbuf_release(&options.git_format_patch_opt);
        free(squash_onto_name);
+       free(keep_base_onto_name);
        return !!ret;
 }
index 44bcea3a5b3add614c72531d112b7b4a9777751a..e36b1d619f5c075fc26617fb26972dc21d561e75 100644 (file)
@@ -1,6 +1,10 @@
 #include "builtin.h"
+#include "abspath.h"
 #include "repository.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "pack.h"
 #include "refs.h"
 #include "connected.h"
 #include "strvec.h"
 #include "version.h"
-#include "tag.h"
 #include "gpg-interface.h"
 #include "sigchain.h"
 #include "fsck.h"
 #include "tmp-objdir.h"
 #include "oidset.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "protocol.h"
 #include "commit-reach.h"
+#include "server-info.h"
+#include "trace.h"
+#include "trace2.h"
 #include "worktree.h"
 #include "shallow.h"
+#include "parse-options.h"
 
 static const char * const receive_pack_usage[] = {
        N_("git receive-pack <git-dir>"),
@@ -80,6 +89,7 @@ static struct object_id push_cert_oid;
 static struct signature_check sigcheck;
 static const char *push_cert_nonce;
 static const char *cert_nonce_seed;
+static struct strvec hidden_refs = STRVEC_INIT;
 
 static const char *NONCE_UNSOLICITED = "UNSOLICITED";
 static const char *NONCE_BAD = "BAD";
@@ -128,14 +138,12 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
        return DENY_IGNORE;
 }
 
-static int receive_pack_config(const char *var, const char *value, void *cb)
+static int receive_pack_config(const char *var, const char *value,
+                              const struct config_context *ctx, void *cb)
 {
-       int status = parse_hide_refs_config(var, value, "receive");
+       const char *msg_id;
+       int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
 
-       if (status)
-               return status;
-
-       status = git_gpg_config(var, value, NULL);
        if (status)
                return status;
 
@@ -150,12 +158,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
        }
 
        if (strcmp(var, "receive.unpacklimit") == 0) {
-               receive_unpack_limit = git_config_int(var, value);
+               receive_unpack_limit = git_config_int(var, value, ctx->kvi);
                return 0;
        }
 
        if (strcmp(var, "transfer.unpacklimit") == 0) {
-               transfer_unpack_limit = git_config_int(var, value);
+               transfer_unpack_limit = git_config_int(var, value, ctx->kvi);
                return 0;
        }
 
@@ -170,12 +178,14 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (skip_prefix(var, "receive.fsck.", &var)) {
-               if (is_valid_msg_type(var, value))
+       if (skip_prefix(var, "receive.fsck.", &msg_id)) {
+               if (!value)
+                       return config_error_nonbool(var);
+               if (is_valid_msg_type(msg_id, value))
                        strbuf_addf(&fsck_msg_types, "%c%s=%s",
-                               fsck_msg_types.len ? ',' : '=', var, value);
+                               fsck_msg_types.len ? ',' : '=', msg_id, value);
                else
-                       warning("skipping unknown msg id '%s'", var);
+                       warning("skipping unknown msg id '%s'", msg_id);
                return 0;
        }
 
@@ -223,7 +233,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return git_config_string(&cert_nonce_seed, var, value);
 
        if (strcmp(var, "receive.certnonceslop") == 0) {
-               nonce_stamp_slop_limit = git_config_ulong(var, value);
+               nonce_stamp_slop_limit = git_config_ulong(var, value, ctx->kvi);
                return 0;
        }
 
@@ -238,12 +248,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
        }
 
        if (strcmp(var, "receive.keepalive") == 0) {
-               keepalive_in_sec = git_config_int(var, value);
+               keepalive_in_sec = git_config_int(var, value, ctx->kvi);
                return 0;
        }
 
        if (strcmp(var, "receive.maxinputsize") == 0) {
-               max_input_size = git_config_int64(var, value);
+               max_input_size = git_config_int64(var, value, ctx->kvi);
                return 0;
        }
 
@@ -259,7 +269,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static void show_ref(const char *path, const struct object_id *oid)
@@ -296,7 +306,7 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
        struct oidset *seen = data;
        const char *path = strip_namespace(path_full);
 
-       if (ref_is_hidden(path, path_full))
+       if (ref_is_hidden(path, path_full, &hidden_refs))
                return 0;
 
        /*
@@ -330,7 +340,9 @@ static void write_head_info(void)
 {
        static struct oidset seen = OIDSET_INIT;
 
-       for_each_ref(show_ref_cb, &seen);
+       refs_for_each_fullref_in(get_main_ref_store(the_repository), "",
+                                hidden_refs_to_excludes(&hidden_refs),
+                                show_ref_cb, &seen);
        for_each_alternate_ref(show_one_alternate_ref, &seen);
        oidset_clear(&seen);
        if (!sent_capabilities)
@@ -1346,7 +1358,7 @@ static int head_has_history(void)
 {
        struct object_id oid;
 
-       return !get_oid("HEAD", &oid);
+       return !repo_get_oid(the_repository, "HEAD", &oid);
 }
 
 static const char *push_to_deploy(unsigned char *sha1,
@@ -1462,8 +1474,10 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                find_shared_symref(worktrees, "HEAD", name);
 
        /* only refs/... are allowed */
-       if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
-               rp_error("refusing to create funny ref '%s' remotely", name);
+       if (!starts_with(name, "refs/") ||
+           check_refname_format(name + 5, is_null_oid(new_oid) ?
+                                REFNAME_ALLOW_ONELEVEL : 0)) {
+               rp_error("refusing to update funny ref '%s' remotely", name);
                ret = "funny refname";
                goto out;
        }
@@ -1493,7 +1507,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                }
        }
 
-       if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
+       if (!is_null_oid(new_oid) && !repo_has_object_file(the_repository, new_oid)) {
                error("unpack should have generated %s, "
                      "but I can't find it!", oid_to_hex(new_oid));
                ret = "bad pack";
@@ -1547,7 +1561,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                }
                old_commit = (struct commit *)old_object;
                new_commit = (struct commit *)new_object;
-               if (!in_merge_bases(old_commit, new_commit)) {
+               if (!repo_in_merge_bases(the_repository, old_commit, new_commit)) {
                        rp_error("denying non-fast-forward %s"
                                 " (you should pull first)", name);
                        ret = "non-fast-forward";
@@ -1680,11 +1694,11 @@ static void check_aliased_update_internal(struct command *cmd,
        rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
                 " its target '%s' (%s..%s)",
                 cmd->ref_name,
-                find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV),
-                find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV),
+                repo_find_unique_abbrev(the_repository, &cmd->old_oid, DEFAULT_ABBREV),
+                repo_find_unique_abbrev(the_repository, &cmd->new_oid, DEFAULT_ABBREV),
                 dst_cmd->ref_name,
-                find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV),
-                find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV));
+                repo_find_unique_abbrev(the_repository, &dst_cmd->old_oid, DEFAULT_ABBREV),
+                repo_find_unique_abbrev(the_repository, &dst_cmd->new_oid, DEFAULT_ABBREV));
 
        cmd->error_string = dst_cmd->error_string =
                "inconsistent aliased update";
@@ -1794,7 +1808,7 @@ static void reject_updates_to_hidden(struct command *commands)
                strbuf_setlen(&refname_full, prefix_len);
                strbuf_addstr(&refname_full, cmd->ref_name);
 
-               if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
+               if (!ref_is_hidden(cmd->ref_name, refname_full.buf, &hidden_refs))
                        continue;
                if (is_null_oid(&cmd->new_oid))
                        cmd->error_string = "deny deleting a hidden ref";
@@ -1928,6 +1942,8 @@ static void execute_commands(struct command *commands,
        opt.err_fd = err_fd;
        opt.progress = err_fd && !quiet;
        opt.env = tmp_objdir_env(tmp_objdir);
+       opt.exclude_hidden_refs_section = "receive";
+
        if (check_connected(iterate_receive_command_list, &data, &opt))
                set_connectivity_errors(commands, si);
 
@@ -2029,6 +2045,16 @@ static struct command **queue_command(struct command **tail,
        return &cmd->next;
 }
 
+static void free_commands(struct command *commands)
+{
+       while (commands) {
+               struct command *next = commands->next;
+
+               free(commands);
+               commands = next;
+       }
+}
+
 static void queue_commands_from_cert(struct command **tail,
                                     struct strbuf *push_cert)
 {
@@ -2076,7 +2102,7 @@ static struct command *read_head_info(struct packet_reader *reader,
                        const char *feature_list = reader->line + linelen + 1;
                        const char *hash = NULL;
                        const char *client_sid;
-                       int len = 0;
+                       size_t len = 0;
                        if (parse_feature_request(feature_list, "report-status"))
                                report_status = 1;
                        if (parse_feature_request(feature_list, "report-status-v2"))
@@ -2171,7 +2197,7 @@ static const char *parse_pack_header(struct pack_header *hdr)
        }
 }
 
-static const char *pack_lockfile;
+static struct tempfile *pack_lockfile;
 
 static void push_header_arg(struct strvec *args, struct pack_header *hdr)
 {
@@ -2238,6 +2264,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                        return "unpack-objects abnormal exit";
        } else {
                char hostname[HOST_NAME_MAX + 1];
+               char *lockfile;
 
                strvec_pushl(&child.args, "index-pack", "--stdin", NULL);
                push_header_arg(&child.args, &hdr);
@@ -2267,8 +2294,14 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                status = start_command(&child);
                if (status)
                        return "index-pack fork failed";
-               pack_lockfile = index_pack_lockfile(child.out, NULL);
+
+               lockfile = index_pack_lockfile(child.out, NULL);
+               if (lockfile) {
+                       pack_lockfile = register_tempfile(lockfile);
+                       free(lockfile);
+               }
                close(child.out);
+
                status = finish_command(&child);
                if (status)
                        return "index-pack abnormal exit";
@@ -2496,10 +2529,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        if (cert_nonce_seed)
                push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL));
 
-       if (0 <= transfer_unpack_limit)
-               unpack_limit = transfer_unpack_limit;
-       else if (0 <= receive_unpack_limit)
+       if (0 <= receive_unpack_limit)
                unpack_limit = receive_unpack_limit;
+       else if (0 <= transfer_unpack_limit)
+               unpack_limit = transfer_unpack_limit;
 
        switch (determine_protocol_version_server()) {
        case protocol_v2:
@@ -2555,8 +2588,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                use_keepalive = KEEPALIVE_ALWAYS;
                execute_commands(commands, unpack_status, &si,
                                 &push_options);
-               if (pack_lockfile)
-                       unlink_or_warn(pack_lockfile);
+               delete_tempfile(&pack_lockfile);
                sigchain_push(SIGPIPE, SIG_IGN);
                if (report_status_v2)
                        report_v2(commands, unpack_status);
@@ -2566,6 +2598,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                run_receive_hook(commands, "post-receive", 1,
                                 &push_options);
                run_update_post_hook(commands);
+               free_commands(commands);
                string_list_clear(&push_options, 0);
                if (auto_gc) {
                        struct child_process proc = CHILD_PROCESS_INIT;
@@ -2591,6 +2624,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                packet_flush(1);
        oid_array_clear(&shallow);
        oid_array_clear(&ref);
+       strvec_clear(&hidden_refs);
        free((void *)push_cert_nonce);
        return 0;
 }
index 57c5c0d061c449a0cf782c2cc41688cc042e3c47..a5a4099f61a5f825d6e622572c9ee85ae563de7b 100644 (file)
@@ -1,9 +1,13 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
+#include "repository.h"
 #include "revision.h"
 #include "reachable.h"
+#include "wildmatch.h"
 #include "worktree.h"
 #include "reflog.h"
+#include "parse-options.h"
 
 #define BUILTIN_REFLOG_SHOW_USAGE \
        N_("git reflog [show] [<log-options>] [<ref>]")
@@ -67,7 +71,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
         * Avoid collecting the same shared ref multiple times because
         * they are available via all worktrees.
         */
-       if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL)
+       if (!worktree->is_current &&
+           parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED)
                return 0;
 
        strbuf_worktree_ref(worktree, &newref, ref);
@@ -105,7 +110,8 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 #define EXPIRE_TOTAL   01
 #define EXPIRE_UNREACH 02
 
-static int reflog_expire_config(const char *var, const char *value, void *cb)
+static int reflog_expire_config(const char *var, const char *value,
+                               const struct config_context *ctx, void *cb)
 {
        const char *pattern, *key;
        size_t pattern_len;
@@ -114,7 +120,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
        struct reflog_expire_cfg *ent;
 
        if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
-               return git_default_config(var, value, cb);
+               return git_default_config(var, value, ctx, cb);
 
        if (!strcmp(key, "reflogexpire")) {
                slot = EXPIRE_TOTAL;
@@ -125,7 +131,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
                if (git_config_expiry_date(&expire, var, value))
                        return -1;
        } else
-               return git_default_config(var, value, cb);
+               return git_default_config(var, value, ctx, cb);
 
        if (!pattern) {
                switch (slot) {
@@ -237,12 +243,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
        struct cmd_reflog_expire_cb cmd = { 0 };
        timestamp_t now = time(NULL);
-       int i, status, do_all, all_worktrees = 1;
+       int i, status, do_all, single_worktree = 0;
        unsigned int flags = 0;
        int verbose = 0;
        reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
        const struct option options[] = {
-               OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
+               OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
                        EXPIRE_REFLOGS_DRY_RUN),
                OPT_BIT(0, "rewrite", &flags,
                        N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
@@ -262,7 +268,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "stale-fix", &cmd.stalefix,
                         N_("prune any reflog entries that point to broken commits")),
                OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
-               OPT_BOOL(1, "single-worktree", &all_worktrees,
+               OPT_BOOL(0, "single-worktree", &single_worktree,
                         N_("limits processing to reflogs from the current worktree only")),
                OPT_END()
        };
@@ -292,7 +298,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                struct rev_info revs;
 
                repo_init_revisions(the_repository, &revs, prefix);
-               revs.do_not_die_on_missing_tree = 1;
+               revs.do_not_die_on_missing_objects = 1;
                revs.ignore_missing = 1;
                revs.ignore_missing_links = 1;
                if (verbose)
@@ -312,7 +318,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 
                worktrees = get_worktrees();
                for (p = worktrees; *p; p++) {
-                       if (!all_worktrees && !(*p)->is_current)
+                       if (single_worktree && !(*p)->is_current)
                                continue;
                        collected.worktree = *p;
                        refs_for_each_reflog(get_worktree_ref_store(*p),
@@ -362,7 +368,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
        int verbose = 0;
 
        const struct option options[] = {
-               OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
+               OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
                        EXPIRE_REFLOGS_DRY_RUN),
                OPT_BIT(0, "rewrite", &flags,
                        N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
index fd3538d4f0e6924214bedc36c132f631d17f4c8b..282782eccdd85691d238e6bd3f3e5b5350304927 100644 (file)
@@ -169,6 +169,8 @@ static int command_loop(const char *child)
 
        while (1) {
                size_t i;
+               const char *arg;
+
                if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
                        if (ferror(stdin))
                                die("Command input error");
@@ -182,10 +184,10 @@ static int command_loop(const char *child)
                if (!strcmp(buffer, "capabilities")) {
                        printf("*connect\n\n");
                        fflush(stdout);
-               } else if (!strncmp(buffer, "connect ", 8)) {
+               } else if (skip_prefix(buffer, "connect ", &arg)) {
                        printf("\n");
                        fflush(stdout);
-                       return run_child(child, buffer + 8);
+                       return run_child(child, arg);
                } else {
                        fprintf(stderr, "Bad command");
                        return 1;
@@ -195,6 +197,8 @@ static int command_loop(const char *child)
 
 int cmd_remote_ext(int argc, const char **argv, const char *prefix)
 {
+       BUG_ON_NON_EMPTY_PREFIX(prefix);
+
        if (argc != 3)
                usage(usage_msg);
 
index 91dfe07e06a1b9603dfd7a0d04f4b7866841d63f..9020fab9c580b304d5099e12d35094eb1d1f3874 100644 (file)
@@ -40,7 +40,7 @@ static void command_loop(int input_fd, int output_fd)
                if (!strcmp(buffer, "capabilities")) {
                        printf("*connect\n\n");
                        fflush(stdout);
-               } else if (!strncmp(buffer, "connect ", 8)) {
+               } else if (starts_with(buffer, "connect ")) {
                        printf("\n");
                        fflush(stdout);
                        if (bidirectional_transfer_loop(input_fd,
@@ -59,6 +59,8 @@ int cmd_remote_fd(int argc, const char **argv, const char *prefix)
        int output_fd = -1;
        char *end;
 
+       BUG_ON_NON_EMPTY_PREFIX(prefix);
+
        if (argc != 3)
                usage(usage_msg);
 
index 910f7b9316a4bdd036bd4203434c0bd6be0cd6cf..d91bbe728d739e074e5f4fa54f70c186f371c1b7 100644 (file)
@@ -1,6 +1,8 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "path.h"
 #include "transport.h"
 #include "remote.h"
 #include "string-list.h"
@@ -9,7 +11,7 @@
 #include "rebase.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "strvec.h"
 #include "commit-reach.h"
 #include "progress.h"
@@ -92,13 +94,15 @@ static int verbose;
 
 static int fetch_remote(const char *name)
 {
-       const char *argv[] = { "fetch", name, NULL, NULL };
-       if (verbose) {
-               argv[1] = "-v";
-               argv[2] = name;
-       }
+       struct child_process cmd = CHILD_PROCESS_INIT;
+
+       strvec_push(&cmd.args, "fetch");
+       if (verbose)
+               strvec_push(&cmd.args, "-v");
+       strvec_push(&cmd.args, name);
+       cmd.git_cmd = 1;
        printf_ln(_("Updating %s"), name);
-       if (run_command_v_opt(argv, RUN_GIT_CMD))
+       if (run_command(&cmd))
                return error(_("Could not fetch %s"), name);
        return 0;
 }
@@ -164,10 +168,9 @@ static int add(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")),
                OPT_SET_INT(0, "tags", &fetch_tags,
-                           N_("import all tags and associated objects when fetching"),
+                           N_("import all tags and associated objects when fetching\n"
+                              "or do not fetch any tag at all (--no-tags)"),
                            TAGS_SET),
-               OPT_SET_INT(0, NULL, &fetch_tags,
-                           N_("or do not fetch any tag at all (--no-tags)"), TAGS_UNSET),
                OPT_STRING_LIST('t', "track", &track, N_("branch"),
                                N_("branch(es) to track")),
                OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
@@ -265,6 +268,7 @@ static const char *abbrev_ref(const char *name, const char *prefix)
 #define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
 
 static int config_read_branches(const char *key, const char *value,
+                               const struct config_context *ctx UNUSED,
                                void *data UNUSED)
 {
        const char *orig_key = key;
@@ -441,7 +445,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
                        info->status = PUSH_STATUS_UPTODATE;
                else if (is_null_oid(&ref->old_oid))
                        info->status = PUSH_STATUS_CREATE;
-               else if (has_object_file(&ref->old_oid) &&
+               else if (repo_has_object_file(the_repository, &ref->old_oid) &&
                         ref_newer(&ref->new_oid, &ref->old_oid))
                        info->status = PUSH_STATUS_FASTFORWARD;
                else
@@ -642,17 +646,19 @@ struct push_default_info
 };
 
 static int config_read_push_default(const char *key, const char *value,
-       void *cb)
+       const struct config_context *ctx, void *cb)
 {
+       const struct key_value_info *kvi = ctx->kvi;
+
        struct push_default_info* info = cb;
        if (strcmp(key, "remote.pushdefault") ||
            !value || strcmp(value, info->old_name))
                return 0;
 
-       info->scope = current_config_scope();
+       info->scope = kvi->scope;
        strbuf_reset(&info->origin);
-       strbuf_addstr(&info->origin, current_config_name());
-       info->linenr = current_config_line();
+       strbuf_addstr(&info->origin, config_origin_type_name(kvi->origin_type));
+       info->linenr = kvi->linenr;
 
        return 0;
 }
@@ -942,7 +948,7 @@ static int rm(int argc, const char **argv, const char *prefix)
        return result;
 }
 
-static void clear_push_info(void *util, const char *string)
+static void clear_push_info(void *util, const char *string UNUSED)
 {
        struct push_info *info = util;
        free(info->dest);
@@ -1491,7 +1497,9 @@ static int prune(int argc, const char **argv, const char *prefix)
        return result;
 }
 
-static int get_remote_default(const char *key, const char *value UNUSED, void *priv)
+static int get_remote_default(const char *key, const char *value UNUSED,
+                             const struct config_context *ctx UNUSED,
+                             void *priv)
 {
        if (strcmp(key, "remotes.default") == 0) {
                int *found = priv;
@@ -1508,37 +1516,35 @@ static int update(int argc, const char **argv, const char *prefix)
                         N_("prune remotes after fetching")),
                OPT_END()
        };
-       struct strvec fetch_argv = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        int default_defined = 0;
-       int retval;
 
        argc = parse_options(argc, argv, prefix, options,
                             builtin_remote_update_usage,
                             PARSE_OPT_KEEP_ARGV0);
 
-       strvec_push(&fetch_argv, "fetch");
+       strvec_push(&cmd.args, "fetch");
 
        if (prune != -1)
-               strvec_push(&fetch_argv, prune ? "--prune" : "--no-prune");
+               strvec_push(&cmd.args, prune ? "--prune" : "--no-prune");
        if (verbose)
-               strvec_push(&fetch_argv, "-v");
-       strvec_push(&fetch_argv, "--multiple");
+               strvec_push(&cmd.args, "-v");
+       strvec_push(&cmd.args, "--multiple");
        if (argc < 2)
-               strvec_push(&fetch_argv, "default");
+               strvec_push(&cmd.args, "default");
        for (i = 1; i < argc; i++)
-               strvec_push(&fetch_argv, argv[i]);
+               strvec_push(&cmd.args, argv[i]);
 
-       if (strcmp(fetch_argv.v[fetch_argv.nr-1], "default") == 0) {
+       if (strcmp(cmd.args.v[cmd.args.nr-1], "default") == 0) {
                git_config(get_remote_default, &default_defined);
                if (!default_defined) {
-                       strvec_pop(&fetch_argv);
-                       strvec_push(&fetch_argv, "--all");
+                       strvec_pop(&cmd.args);
+                       strvec_push(&cmd.args, "--all");
                }
        }
 
-       retval = run_command_v_opt(fetch_argv.v, RUN_GIT_CMD);
-       strvec_clear(&fetch_argv);
-       return retval;
+       cmd.git_cmd = 1;
+       return run_command(&cmd);
 }
 
 static int remove_all_fetch_refspecs(const char *key)
index a5bacc7797435696cd6e23e73f847fc00d39bb7f..ede36328a3cab9170bc59cb98dda097ea6eb24f7 100644 (file)
@@ -1,29 +1,33 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
+#include "path.h"
 #include "run-command.h"
-#include "sigchain.h"
+#include "server-info.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "midx.h"
 #include "packfile.h"
 #include "prune-packed.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "promisor-remote.h"
 #include "shallow.h"
 #include "pack.h"
 #include "pack-bitmap.h"
 #include "refs.h"
+#include "list-objects-filter-options.h"
 
 #define ALL_INTO_ONE 1
 #define LOOSEN_UNREACHABLE 2
 #define PACK_CRUFT 4
 
 #define DELETE_PACK 1
-#define CRUFT_PACK 2
+#define RETAIN_PACK 2
 
 static int pack_everything;
 static int delta_base_offset = 1;
@@ -32,7 +36,6 @@ static int write_bitmaps = -1;
 static int use_delta_islands;
 static int run_update_server_info = 1;
 static char *packdir, *packtmp_name, *packtmp;
-static char *cruft_expiration;
 
 static const char *const git_repack_usage[] = {
        N_("git repack [<options>]"),
@@ -49,14 +52,16 @@ struct pack_objects_args {
        const char *window_memory;
        const char *depth;
        const char *threads;
-       const char *max_pack_size;
+       unsigned long max_pack_size;
        int no_reuse_delta;
        int no_reuse_object;
        int quiet;
        int local;
+       struct list_objects_filter_options filter_options;
 };
 
-static int repack_config(const char *var, const char *value, void *cb)
+static int repack_config(const char *var, const char *value,
+                        const struct config_context *ctx, void *cb)
 {
        struct pack_objects_args *cruft_po_args = cb;
        if (!strcmp(var, "repack.usedeltabaseoffset")) {
@@ -88,107 +93,185 @@ static int repack_config(const char *var, const char *value, void *cb)
                return git_config_string(&cruft_po_args->depth, var, value);
        if (!strcmp(var, "repack.cruftthreads"))
                return git_config_string(&cruft_po_args->threads, var, value);
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
-/*
- * Remove temporary $GIT_OBJECT_DIRECTORY/pack/.tmp-$$-pack-* files.
- */
-static void remove_temporary_files(void)
+struct existing_packs {
+       struct string_list kept_packs;
+       struct string_list non_kept_packs;
+       struct string_list cruft_packs;
+};
+
+#define EXISTING_PACKS_INIT { \
+       .kept_packs = STRING_LIST_INIT_DUP, \
+       .non_kept_packs = STRING_LIST_INIT_DUP, \
+       .cruft_packs = STRING_LIST_INIT_DUP, \
+}
+
+static int has_existing_non_kept_packs(const struct existing_packs *existing)
 {
-       struct strbuf buf = STRBUF_INIT;
-       size_t dirlen, prefixlen;
-       DIR *dir;
-       struct dirent *e;
+       return existing->non_kept_packs.nr || existing->cruft_packs.nr;
+}
 
-       dir = opendir(packdir);
-       if (!dir)
-               return;
+static void pack_mark_for_deletion(struct string_list_item *item)
+{
+       item->util = (void*)((uintptr_t)item->util | DELETE_PACK);
+}
+
+static void pack_unmark_for_deletion(struct string_list_item *item)
+{
+       item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK);
+}
+
+static int pack_is_marked_for_deletion(struct string_list_item *item)
+{
+       return (uintptr_t)item->util & DELETE_PACK;
+}
 
-       /* Point at the slash at the end of ".../objects/pack/" */
-       dirlen = strlen(packdir) + 1;
-       strbuf_addstr(&buf, packtmp);
-       /* Hold the length of  ".tmp-%d-pack-" */
-       prefixlen = buf.len - dirlen;
+static void pack_mark_retained(struct string_list_item *item)
+{
+       item->util = (void*)((uintptr_t)item->util | RETAIN_PACK);
+}
 
-       while ((e = readdir(dir))) {
-               if (strncmp(e->d_name, buf.buf + dirlen, prefixlen))
+static int pack_is_retained(struct string_list_item *item)
+{
+       return (uintptr_t)item->util & RETAIN_PACK;
+}
+
+static void mark_packs_for_deletion_1(struct string_list *names,
+                                     struct string_list *list)
+{
+       struct string_list_item *item;
+       const int hexsz = the_hash_algo->hexsz;
+
+       for_each_string_list_item(item, list) {
+               char *sha1;
+               size_t len = strlen(item->string);
+               if (len < hexsz)
                        continue;
-               strbuf_setlen(&buf, dirlen);
-               strbuf_addstr(&buf, e->d_name);
-               unlink(buf.buf);
+               sha1 = item->string + len - hexsz;
+
+               if (pack_is_retained(item)) {
+                       pack_unmark_for_deletion(item);
+               } else if (!string_list_has_string(names, sha1)) {
+                       /*
+                        * Mark this pack for deletion, which ensures
+                        * that this pack won't be included in a MIDX
+                        * (if `--write-midx` was given) and that we
+                        * will actually delete this pack (if `-d` was
+                        * given).
+                        */
+                       pack_mark_for_deletion(item);
+               }
        }
-       closedir(dir);
+}
+
+static void retain_cruft_pack(struct existing_packs *existing,
+                             struct packed_git *cruft)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct string_list_item *item;
+
+       strbuf_addstr(&buf, pack_basename(cruft));
+       strbuf_strip_suffix(&buf, ".pack");
+
+       item = string_list_lookup(&existing->cruft_packs, buf.buf);
+       if (!item)
+               BUG("could not find cruft pack '%s'", pack_basename(cruft));
+
+       pack_mark_retained(item);
        strbuf_release(&buf);
 }
 
-static void remove_pack_on_signal(int signo)
+static void mark_packs_for_deletion(struct existing_packs *existing,
+                                   struct string_list *names)
+
 {
-       remove_temporary_files();
-       sigchain_pop(signo);
-       raise(signo);
+       mark_packs_for_deletion_1(names, &existing->non_kept_packs);
+       mark_packs_for_deletion_1(names, &existing->cruft_packs);
+}
+
+static void remove_redundant_pack(const char *dir_name, const char *base_name)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct multi_pack_index *m = get_local_multi_pack_index(the_repository);
+       strbuf_addf(&buf, "%s.pack", base_name);
+       if (m && midx_contains_pack(m, buf.buf))
+               clear_midx_file(the_repository);
+       strbuf_insertf(&buf, 0, "%s/", dir_name);
+       unlink_pack_path(buf.buf, 1);
+       strbuf_release(&buf);
+}
+
+static void remove_redundant_packs_1(struct string_list *packs)
+{
+       struct string_list_item *item;
+       for_each_string_list_item(item, packs) {
+               if (!pack_is_marked_for_deletion(item))
+                       continue;
+               remove_redundant_pack(packdir, item->string);
+       }
+}
+
+static void remove_redundant_existing_packs(struct existing_packs *existing)
+{
+       remove_redundant_packs_1(&existing->non_kept_packs);
+       remove_redundant_packs_1(&existing->cruft_packs);
+}
+
+static void existing_packs_release(struct existing_packs *existing)
+{
+       string_list_clear(&existing->kept_packs, 0);
+       string_list_clear(&existing->non_kept_packs, 0);
+       string_list_clear(&existing->cruft_packs, 0);
 }
 
 /*
- * Adds all packs hex strings to either fname_nonkept_list or
- * fname_kept_list based on whether each pack has a corresponding
+ * Adds all packs hex strings (pack-$HASH) to either packs->non_kept
+ * or packs->kept based on whether each pack has a corresponding
  * .keep file or not.  Packs without a .keep file are not to be kept
  * if we are going to pack everything into one file.
  */
-static void collect_pack_filenames(struct string_list *fname_nonkept_list,
-                                  struct string_list *fname_kept_list,
+static void collect_pack_filenames(struct existing_packs *existing,
                                   const struct string_list *extra_keep)
 {
-       DIR *dir;
-       struct dirent *e;
-       char *fname;
-
-       if (!(dir = opendir(packdir)))
-               return;
+       struct packed_git *p;
+       struct strbuf buf = STRBUF_INIT;
 
-       while ((e = readdir(dir)) != NULL) {
-               size_t len;
+       for (p = get_all_packs(the_repository); p; p = p->next) {
                int i;
+               const char *base;
 
-               if (!strip_suffix(e->d_name, ".pack", &len))
+               if (!p->pack_local)
                        continue;
 
+               base = pack_basename(p);
+
                for (i = 0; i < extra_keep->nr; i++)
-                       if (!fspathcmp(e->d_name, extra_keep->items[i].string))
+                       if (!fspathcmp(base, extra_keep->items[i].string))
                                break;
 
-               fname = xmemdupz(e->d_name, len);
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, base);
+               strbuf_strip_suffix(&buf, ".pack");
 
-               if ((extra_keep->nr > 0 && i < extra_keep->nr) ||
-                   (file_exists(mkpath("%s/%s.keep", packdir, fname)))) {
-                       string_list_append_nodup(fname_kept_list, fname);
-               } else {
-                       struct string_list_item *item;
-                       item = string_list_append_nodup(fname_nonkept_list,
-                                                       fname);
-                       if (file_exists(mkpath("%s/%s.mtimes", packdir, fname)))
-                               item->util = (void*)(uintptr_t)CRUFT_PACK;
-               }
+               if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep)
+                       string_list_append(&existing->kept_packs, buf.buf);
+               else if (p->is_cruft)
+                       string_list_append(&existing->cruft_packs, buf.buf);
+               else
+                       string_list_append(&existing->non_kept_packs, buf.buf);
        }
-       closedir(dir);
-
-       string_list_sort(fname_kept_list);
-}
 
-static void remove_redundant_pack(const char *dir_name, const char *base_name)
-{
-       struct strbuf buf = STRBUF_INIT;
-       struct multi_pack_index *m = get_local_multi_pack_index(the_repository);
-       strbuf_addf(&buf, "%s.pack", base_name);
-       if (m && midx_contains_pack(m, buf.buf))
-               clear_midx_file(the_repository);
-       strbuf_insertf(&buf, 0, "%s/", dir_name);
-       unlink_pack_path(buf.buf, 1);
+       string_list_sort(&existing->kept_packs);
+       string_list_sort(&existing->non_kept_packs);
+       string_list_sort(&existing->cruft_packs);
        strbuf_release(&buf);
 }
 
 static void prepare_pack_objects(struct child_process *cmd,
-                                const struct pack_objects_args *args)
+                                const struct pack_objects_args *args,
+                                const char *out)
 {
        strvec_push(&cmd->args, "pack-objects");
        if (args->window)
@@ -200,7 +283,7 @@ static void prepare_pack_objects(struct child_process *cmd,
        if (args->threads)
                strvec_pushf(&cmd->args, "--threads=%s", args->threads);
        if (args->max_pack_size)
-               strvec_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size);
+               strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size);
        if (args->no_reuse_delta)
                strvec_pushf(&cmd->args, "--no-reuse-delta");
        if (args->no_reuse_object)
@@ -211,7 +294,7 @@ static void prepare_pack_objects(struct child_process *cmd,
                strvec_push(&cmd->args,  "--quiet");
        if (delta_base_offset)
                strvec_push(&cmd->args,  "--delta-base-offset");
-       strvec_push(&cmd->args, packtmp);
+       strvec_push(&cmd->args, out);
        cmd->git_cmd = 1;
        cmd->out = -1;
 }
@@ -220,8 +303,9 @@ static void prepare_pack_objects(struct child_process *cmd,
  * Write oid to the given struct child_process's stdin, starting it first if
  * necessary.
  */
-static int write_oid(const struct object_id *oid, struct packed_git *pack,
-                    uint32_t pos, void *data)
+static int write_oid(const struct object_id *oid,
+                    struct packed_git *pack UNUSED,
+                    uint32_t pos UNUSED, void *data)
 {
        struct child_process *cmd = data;
 
@@ -247,11 +331,15 @@ static struct {
        {".idx"},
 };
 
-static unsigned populate_pack_exts(char *name)
+struct generated_pack_data {
+       struct tempfile *tempfiles[ARRAY_SIZE(exts)];
+};
+
+static struct generated_pack_data *populate_pack_exts(const char *name)
 {
        struct stat statbuf;
        struct strbuf path = STRBUF_INIT;
-       unsigned ret = 0;
+       struct generated_pack_data *data = xcalloc(1, sizeof(*data));
        int i;
 
        for (i = 0; i < ARRAY_SIZE(exts); i++) {
@@ -261,11 +349,23 @@ static unsigned populate_pack_exts(char *name)
                if (stat(path.buf, &statbuf))
                        continue;
 
-               ret |= (1 << i);
+               data->tempfiles[i] = register_tempfile(path.buf);
        }
 
        strbuf_release(&path);
-       return ret;
+       return data;
+}
+
+static int has_pack_ext(const struct generated_pack_data *data,
+                       const char *ext)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(exts); i++) {
+               if (strcmp(exts[i].name, ext))
+                       continue;
+               return !!data->tempfiles[i];
+       }
+       BUG("unknown pack extension: '%s'", ext);
 }
 
 static void repack_promisor_objects(const struct pack_objects_args *args,
@@ -275,7 +375,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
        FILE *out;
        struct strbuf line = STRBUF_INIT;
 
-       prepare_pack_objects(&cmd, args);
+       prepare_pack_objects(&cmd, args, packtmp);
        cmd.in = -1;
 
        /*
@@ -320,7 +420,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
                                          line.buf);
                write_promisor_file(promisor_name, NULL, 0);
 
-               item->util = (void *)(uintptr_t)populate_pack_exts(item->string);
+               item->util = populate_pack_exts(item->string);
 
                free(promisor_name);
        }
@@ -333,6 +433,8 @@ struct pack_geometry {
        struct packed_git **pack;
        uint32_t pack_nr, pack_alloc;
        uint32_t split;
+
+       int split_factor;
 };
 
 static uint32_t geometry_pack_weight(struct packed_git *p)
@@ -354,36 +456,42 @@ static int geometry_cmp(const void *va, const void *vb)
        return 0;
 }
 
-static void init_pack_geometry(struct pack_geometry **geometry_p,
-                              struct string_list *existing_kept_packs)
+static void init_pack_geometry(struct pack_geometry *geometry,
+                              struct existing_packs *existing,
+                              const struct pack_objects_args *args)
 {
        struct packed_git *p;
-       struct pack_geometry *geometry;
        struct strbuf buf = STRBUF_INIT;
 
-       *geometry_p = xcalloc(1, sizeof(struct pack_geometry));
-       geometry = *geometry_p;
-
        for (p = get_all_packs(the_repository); p; p = p->next) {
+               if (args->local && !p->pack_local)
+                       /*
+                        * When asked to only repack local packfiles we skip
+                        * over any packfiles that are borrowed from alternate
+                        * object directories.
+                        */
+                       continue;
+
                if (!pack_kept_objects) {
                        /*
-                        * Any pack that has its pack_keep bit set will appear
-                        * in existing_kept_packs below, but this saves us from
-                        * doing a more expensive check.
+                        * Any pack that has its pack_keep bit set will
+                        * appear in existing->kept_packs below, but
+                        * this saves us from doing a more expensive
+                        * check.
                         */
                        if (p->pack_keep)
                                continue;
 
                        /*
-                        * The pack may be kept via the --keep-pack option;
-                        * check 'existing_kept_packs' to determine whether to
-                        * ignore it.
+                        * The pack may be kept via the --keep-pack
+                        * option; check 'existing->kept_packs' to
+                        * determine whether to ignore it.
                         */
                        strbuf_reset(&buf);
                        strbuf_addstr(&buf, pack_basename(p));
                        strbuf_strip_suffix(&buf, ".pack");
 
-                       if (string_list_has_string(existing_kept_packs, buf.buf))
+                       if (string_list_has_string(&existing->kept_packs, buf.buf))
                                continue;
                }
                if (p->is_cruft)
@@ -401,7 +509,7 @@ static void init_pack_geometry(struct pack_geometry **geometry_p,
        strbuf_release(&buf);
 }
 
-static void split_pack_geometry(struct pack_geometry *geometry, int factor)
+static void split_pack_geometry(struct pack_geometry *geometry)
 {
        uint32_t i;
        uint32_t split;
@@ -420,12 +528,14 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
                struct packed_git *ours = geometry->pack[i];
                struct packed_git *prev = geometry->pack[i - 1];
 
-               if (unsigned_mult_overflows(factor, geometry_pack_weight(prev)))
+               if (unsigned_mult_overflows(geometry->split_factor,
+                                           geometry_pack_weight(prev)))
                        die(_("pack %s too large to consider in geometric "
                              "progression"),
                            prev->pack_name);
 
-               if (geometry_pack_weight(ours) < factor * geometry_pack_weight(prev))
+               if (geometry_pack_weight(ours) <
+                   geometry->split_factor * geometry_pack_weight(prev))
                        break;
        }
 
@@ -460,10 +570,12 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
        for (i = split; i < geometry->pack_nr; i++) {
                struct packed_git *ours = geometry->pack[i];
 
-               if (unsigned_mult_overflows(factor, total_size))
+               if (unsigned_mult_overflows(geometry->split_factor,
+                                           total_size))
                        die(_("pack %s too large to roll up"), ours->pack_name);
 
-               if (geometry_pack_weight(ours) < factor * total_size) {
+               if (geometry_pack_weight(ours) <
+                   geometry->split_factor * total_size) {
                        if (unsigned_add_overflows(total_size,
                                                   geometry_pack_weight(ours)))
                                die(_("pack %s too large to roll up"),
@@ -478,8 +590,10 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
        geometry->split = split;
 }
 
-static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
+static struct packed_git *get_preferred_pack(struct pack_geometry *geometry)
 {
+       uint32_t i;
+
        if (!geometry) {
                /*
                 * No geometry means either an all-into-one repack (in which
@@ -494,18 +608,55 @@ static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry
        }
        if (geometry->split == geometry->pack_nr)
                return NULL;
-       return geometry->pack[geometry->pack_nr - 1];
+
+       /*
+        * The preferred pack is the largest pack above the split line. In
+        * other words, it is the largest pack that does not get rolled up in
+        * the geometric repack.
+        */
+       for (i = geometry->pack_nr; i > geometry->split; i--)
+               /*
+                * A pack that is not local would never be included in a
+                * multi-pack index. We thus skip over any non-local packs.
+                */
+               if (geometry->pack[i - 1]->pack_local)
+                       return geometry->pack[i - 1];
+
+       return NULL;
+}
+
+static void geometry_remove_redundant_packs(struct pack_geometry *geometry,
+                                           struct string_list *names,
+                                           struct existing_packs *existing)
+{
+       struct strbuf buf = STRBUF_INIT;
+       uint32_t i;
+
+       for (i = 0; i < geometry->split; i++) {
+               struct packed_git *p = geometry->pack[i];
+               if (string_list_has_string(names, hash_to_hex(p->hash)))
+                       continue;
+
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, pack_basename(p));
+               strbuf_strip_suffix(&buf, ".pack");
+
+               if ((p->pack_keep) ||
+                   (string_list_has_string(&existing->kept_packs, buf.buf)))
+                       continue;
+
+               remove_redundant_pack(packdir, buf.buf);
+       }
+
+       strbuf_release(&buf);
 }
 
-static void clear_pack_geometry(struct pack_geometry *geometry)
+static void free_pack_geometry(struct pack_geometry *geometry)
 {
        if (!geometry)
                return;
 
        free(geometry->pack);
-       geometry->pack_nr = 0;
-       geometry->pack_alloc = 0;
-       geometry->split = 0;
 }
 
 struct midx_snapshot_ref_data {
@@ -571,57 +722,76 @@ static void midx_snapshot_refs(struct tempfile *f)
 }
 
 static void midx_included_packs(struct string_list *include,
-                               struct string_list *existing_nonkept_packs,
-                               struct string_list *existing_kept_packs,
+                               struct existing_packs *existing,
                                struct string_list *names,
                                struct pack_geometry *geometry)
 {
        struct string_list_item *item;
 
-       for_each_string_list_item(item, existing_kept_packs)
+       for_each_string_list_item(item, &existing->kept_packs)
                string_list_insert(include, xstrfmt("%s.idx", item->string));
        for_each_string_list_item(item, names)
                string_list_insert(include, xstrfmt("pack-%s.idx", item->string));
-       if (geometry) {
+       if (geometry->split_factor) {
                struct strbuf buf = STRBUF_INIT;
                uint32_t i;
                for (i = geometry->split; i < geometry->pack_nr; i++) {
                        struct packed_git *p = geometry->pack[i];
 
+                       /*
+                        * The multi-pack index never refers to packfiles part
+                        * of an alternate object database, so we skip these.
+                        * While git-multi-pack-index(1) would silently ignore
+                        * them anyway, this allows us to skip executing the
+                        * command completely when we have only non-local
+                        * packfiles.
+                        */
+                       if (!p->pack_local)
+                               continue;
+
                        strbuf_addstr(&buf, pack_basename(p));
                        strbuf_strip_suffix(&buf, ".pack");
                        strbuf_addstr(&buf, ".idx");
 
                        string_list_insert(include, strbuf_detach(&buf, NULL));
                }
-
-               for_each_string_list_item(item, existing_nonkept_packs) {
-                       if (!((uintptr_t)item->util & CRUFT_PACK)) {
-                               /*
-                                * no need to check DELETE_PACK, since we're not
-                                * doing an ALL_INTO_ONE repack
-                                */
-                               continue;
-                       }
-                       string_list_insert(include, xstrfmt("%s.idx", item->string));
-               }
        } else {
-               for_each_string_list_item(item, existing_nonkept_packs) {
-                       if ((uintptr_t)item->util & DELETE_PACK)
+               for_each_string_list_item(item, &existing->non_kept_packs) {
+                       if (pack_is_marked_for_deletion(item))
                                continue;
                        string_list_insert(include, xstrfmt("%s.idx", item->string));
                }
        }
+
+       for_each_string_list_item(item, &existing->cruft_packs) {
+               /*
+                * When doing a --geometric repack, there is no need to check
+                * for deleted packs, since we're by definition not doing an
+                * ALL_INTO_ONE repack (hence no packs will be deleted).
+                * Otherwise we must check for and exclude any packs which are
+                * enqueued for deletion.
+                *
+                * So we could omit the conditional below in the --geometric
+                * case, but doing so is unnecessary since no packs are marked
+                * as pending deletion (since we only call
+                * `mark_packs_for_deletion()` when doing an all-into-one
+                * repack).
+                */
+               if (pack_is_marked_for_deletion(item))
+                       continue;
+               string_list_insert(include, xstrfmt("%s.idx", item->string));
+       }
 }
 
 static int write_midx_included_packs(struct string_list *include,
                                     struct pack_geometry *geometry,
+                                    struct string_list *names,
                                     const char *refs_snapshot,
                                     int show_progress, int write_bitmaps)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
        struct string_list_item *item;
-       struct packed_git *largest = get_largest_active_pack(geometry);
+       struct packed_git *preferred = get_preferred_pack(geometry);
        FILE *in;
        int ret;
 
@@ -642,9 +812,41 @@ static int write_midx_included_packs(struct string_list *include,
        if (write_bitmaps)
                strvec_push(&cmd.args, "--bitmap");
 
-       if (largest)
+       if (preferred)
                strvec_pushf(&cmd.args, "--preferred-pack=%s",
-                            pack_basename(largest));
+                            pack_basename(preferred));
+       else if (names->nr) {
+               /* The largest pack was repacked, meaning that either
+                * one or two packs exist depending on whether the
+                * repository has a cruft pack or not.
+                *
+                * Select the non-cruft one as preferred to encourage
+                * pack-reuse among packs containing reachable objects
+                * over unreachable ones.
+                *
+                * (Note we could write multiple packs here if
+                * `--max-pack-size` was given, but any one of them
+                * will suffice, so pick the first one.)
+                */
+               for_each_string_list_item(item, names) {
+                       struct generated_pack_data *data = item->util;
+                       if (has_pack_ext(data, ".mtimes"))
+                               continue;
+
+                       strvec_pushf(&cmd.args, "--preferred-pack=pack-%s.pack",
+                                    item->string);
+                       break;
+               }
+       } else {
+               /*
+                * No packs were kept, and no packs were written. The
+                * only thing remaining are .keep packs (unless
+                * --pack-kept-objects was given).
+                *
+                * Set the `--preferred-pack` arbitrarily here.
+                */
+               ;
+       }
 
        if (refs_snapshot)
                strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
@@ -661,19 +863,197 @@ static int write_midx_included_packs(struct string_list *include,
        return finish_command(&cmd);
 }
 
+static void remove_redundant_bitmaps(struct string_list *include,
+                                    const char *packdir)
+{
+       struct strbuf path = STRBUF_INIT;
+       struct string_list_item *item;
+       size_t packdir_len;
+
+       strbuf_addstr(&path, packdir);
+       strbuf_addch(&path, '/');
+       packdir_len = path.len;
+
+       /*
+        * Remove any pack bitmaps corresponding to packs which are now
+        * included in the MIDX.
+        */
+       for_each_string_list_item(item, include) {
+               strbuf_addstr(&path, item->string);
+               strbuf_strip_suffix(&path, ".idx");
+               strbuf_addstr(&path, ".bitmap");
+
+               if (unlink(path.buf) && errno != ENOENT)
+                       warning_errno(_("could not remove stale bitmap: %s"),
+                                     path.buf);
+
+               strbuf_setlen(&path, packdir_len);
+       }
+       strbuf_release(&path);
+}
+
+static int finish_pack_objects_cmd(struct child_process *cmd,
+                                  struct string_list *names,
+                                  int local)
+{
+       FILE *out;
+       struct strbuf line = STRBUF_INIT;
+
+       out = xfdopen(cmd->out, "r");
+       while (strbuf_getline_lf(&line, out) != EOF) {
+               struct string_list_item *item;
+
+               if (line.len != the_hash_algo->hexsz)
+                       die(_("repack: Expecting full hex object ID lines only "
+                             "from pack-objects."));
+               /*
+                * Avoid putting packs written outside of the repository in the
+                * list of names.
+                */
+               if (local) {
+                       item = string_list_append(names, line.buf);
+                       item->util = populate_pack_exts(line.buf);
+               }
+       }
+       fclose(out);
+
+       strbuf_release(&line);
+
+       return finish_command(cmd);
+}
+
+static int write_filtered_pack(const struct pack_objects_args *args,
+                              const char *destination,
+                              const char *pack_prefix,
+                              struct existing_packs *existing,
+                              struct string_list *names)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       struct string_list_item *item;
+       FILE *in;
+       int ret;
+       const char *caret;
+       const char *scratch;
+       int local = skip_prefix(destination, packdir, &scratch);
+
+       prepare_pack_objects(&cmd, args, destination);
+
+       strvec_push(&cmd.args, "--stdin-packs");
+
+       if (!pack_kept_objects)
+               strvec_push(&cmd.args, "--honor-pack-keep");
+       for_each_string_list_item(item, &existing->kept_packs)
+               strvec_pushf(&cmd.args, "--keep-pack=%s", item->string);
+
+       cmd.in = -1;
+
+       ret = start_command(&cmd);
+       if (ret)
+               return ret;
+
+       /*
+        * Here 'names' contains only the pack(s) that were just
+        * written, which is exactly the packs we want to keep. Also
+        * 'existing_kept_packs' already contains the packs in
+        * 'keep_pack_list'.
+        */
+       in = xfdopen(cmd.in, "w");
+       for_each_string_list_item(item, names)
+               fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string);
+       for_each_string_list_item(item, &existing->non_kept_packs)
+               fprintf(in, "%s.pack\n", item->string);
+       for_each_string_list_item(item, &existing->cruft_packs)
+               fprintf(in, "%s.pack\n", item->string);
+       caret = pack_kept_objects ? "" : "^";
+       for_each_string_list_item(item, &existing->kept_packs)
+               fprintf(in, "%s%s.pack\n", caret, item->string);
+       fclose(in);
+
+       return finish_pack_objects_cmd(&cmd, names, local);
+}
+
+static int existing_cruft_pack_cmp(const void *va, const void *vb)
+{
+       struct packed_git *a = *(struct packed_git **)va;
+       struct packed_git *b = *(struct packed_git **)vb;
+
+       if (a->pack_size < b->pack_size)
+               return -1;
+       if (a->pack_size > b->pack_size)
+               return 1;
+       return 0;
+}
+
+static void collapse_small_cruft_packs(FILE *in, size_t max_size,
+                                      struct existing_packs *existing)
+{
+       struct packed_git **existing_cruft, *p;
+       struct strbuf buf = STRBUF_INIT;
+       size_t total_size = 0;
+       size_t existing_cruft_nr = 0;
+       size_t i;
+
+       ALLOC_ARRAY(existing_cruft, existing->cruft_packs.nr);
+
+       for (p = get_all_packs(the_repository); p; p = p->next) {
+               if (!(p->is_cruft && p->pack_local))
+                       continue;
+
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, pack_basename(p));
+               strbuf_strip_suffix(&buf, ".pack");
+
+               if (!string_list_has_string(&existing->cruft_packs, buf.buf))
+                       continue;
+
+               if (existing_cruft_nr >= existing->cruft_packs.nr)
+                       BUG("too many cruft packs (found %"PRIuMAX", but knew "
+                           "of %"PRIuMAX")",
+                           (uintmax_t)existing_cruft_nr + 1,
+                           (uintmax_t)existing->cruft_packs.nr);
+               existing_cruft[existing_cruft_nr++] = p;
+       }
+
+       QSORT(existing_cruft, existing_cruft_nr, existing_cruft_pack_cmp);
+
+       for (i = 0; i < existing_cruft_nr; i++) {
+               size_t proposed;
+
+               p = existing_cruft[i];
+               proposed = st_add(total_size, p->pack_size);
+
+               if (proposed <= max_size) {
+                       total_size = proposed;
+                       fprintf(in, "-%s\n", pack_basename(p));
+               } else {
+                       retain_cruft_pack(existing, p);
+                       fprintf(in, "%s\n", pack_basename(p));
+               }
+       }
+
+       for (i = 0; i < existing->non_kept_packs.nr; i++)
+               fprintf(in, "-%s.pack\n",
+                       existing->non_kept_packs.items[i].string);
+
+       strbuf_release(&buf);
+       free(existing_cruft);
+}
+
 static int write_cruft_pack(const struct pack_objects_args *args,
+                           const char *destination,
                            const char *pack_prefix,
+                           const char *cruft_expiration,
                            struct string_list *names,
-                           struct string_list *existing_packs,
-                           struct string_list *existing_kept_packs)
+                           struct existing_packs *existing)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
-       struct strbuf line = STRBUF_INIT;
        struct string_list_item *item;
-       FILE *in, *out;
+       FILE *in;
        int ret;
+       const char *scratch;
+       int local = skip_prefix(destination, packdir, &scratch);
 
-       prepare_pack_objects(&cmd, args);
+       prepare_pack_objects(&cmd, args, destination);
 
        strvec_push(&cmd.args, "--cruft");
        if (cruft_expiration)
@@ -682,7 +1062,6 @@ static int write_cruft_pack(const struct pack_objects_args *args,
 
        strvec_push(&cmd.args, "--honor-pack-keep");
        strvec_push(&cmd.args, "--non-empty");
-       strvec_push(&cmd.args, "--max-pack-size=0");
 
        cmd.in = -1;
 
@@ -698,28 +1077,38 @@ static int write_cruft_pack(const struct pack_objects_args *args,
         * By the time it is read here, it contains only the pack(s)
         * that were just written, which is exactly the set of packs we
         * want to consider kept.
+        *
+        * If `--expire-to` is given, the double-use served by `names`
+        * ensures that the pack written to `--expire-to` excludes any
+        * objects contained in the cruft pack.
         */
        in = xfdopen(cmd.in, "w");
        for_each_string_list_item(item, names)
                fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
-       for_each_string_list_item(item, existing_packs)
-               fprintf(in, "-%s.pack\n", item->string);
-       for_each_string_list_item(item, existing_kept_packs)
+       if (args->max_pack_size && !cruft_expiration) {
+               collapse_small_cruft_packs(in, args->max_pack_size, existing);
+       } else {
+               for_each_string_list_item(item, &existing->non_kept_packs)
+                       fprintf(in, "-%s.pack\n", item->string);
+               for_each_string_list_item(item, &existing->cruft_packs)
+                       fprintf(in, "-%s.pack\n", item->string);
+       }
+       for_each_string_list_item(item, &existing->kept_packs)
                fprintf(in, "%s.pack\n", item->string);
        fclose(in);
 
-       out = xfdopen(cmd.out, "r");
-       while (strbuf_getline_lf(&line, out) != EOF) {
-               if (line.len != the_hash_algo->hexsz)
-                       die(_("repack: Expecting full hex object ID lines only "
-                             "from pack-objects."));
-               string_list_append(names, line.buf);
-       }
-       fclose(out);
-
-       strbuf_release(&line);
+       return finish_pack_objects_cmd(&cmd, names, local);
+}
 
-       return finish_command(&cmd);
+static const char *find_pack_prefix(const char *packdir, const char *packtmp)
+{
+       const char *pack_prefix;
+       if (!skip_prefix(packtmp, packdir, &pack_prefix))
+               die(_("pack prefix %s does not begin with objdir %s"),
+                   packtmp, packdir);
+       if (*pack_prefix == '/')
+               pack_prefix++;
+       return pack_prefix;
 }
 
 int cmd_repack(int argc, const char **argv, const char *prefix)
@@ -727,13 +1116,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        struct child_process cmd = CHILD_PROCESS_INIT;
        struct string_list_item *item;
        struct string_list names = STRING_LIST_INIT_DUP;
-       struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP;
-       struct string_list existing_kept_packs = STRING_LIST_INIT_DUP;
-       struct pack_geometry *geometry = NULL;
-       struct strbuf line = STRBUF_INIT;
+       struct existing_packs existing = EXISTING_PACKS_INIT;
+       struct pack_geometry geometry = { 0 };
        struct tempfile *refs_snapshot = NULL;
        int i, ext, ret;
-       FILE *out;
        int show_progress;
 
        /* variables to be filled by option parsing */
@@ -743,8 +1129,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
        struct pack_objects_args po_args = {NULL};
        struct pack_objects_args cruft_po_args = {NULL};
-       int geometric_factor = 0;
        int write_midx = 0;
+       const char *cruft_expiration = NULL;
+       const char *expire_to = NULL;
+       const char *filter_to = NULL;
 
        struct option builtin_repack_options[] = {
                OPT_BIT('a', NULL, &pack_everything,
@@ -756,7 +1144,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                N_("same as -a, pack unreachable cruft objects separately"),
                                   PACK_CRUFT),
                OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
-                               N_("with -C, expire objects older than this")),
+                               N_("with --cruft, expire objects older than this")),
+               OPT_MAGNITUDE(0, "max-cruft-size", &cruft_po_args.max_pack_size,
+                               N_("with --cruft, limit the size of new cruft packs")),
                OPT_BOOL('d', NULL, &delete_redundant,
                                N_("remove redundant packs, and run git-prune-packed")),
                OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
@@ -784,19 +1174,26 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                N_("limits the maximum delta depth")),
                OPT_STRING(0, "threads", &po_args.threads, N_("n"),
                                N_("limits the maximum number of threads")),
-               OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"),
+               OPT_MAGNITUDE(0, "max-pack-size", &po_args.max_pack_size,
                                N_("maximum size of each packfile")),
+               OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options),
                OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
                                N_("repack objects in packs marked with .keep")),
                OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
                                N_("do not repack this pack")),
-               OPT_INTEGER('g', "geometric", &geometric_factor,
+               OPT_INTEGER('g', "geometric", &geometry.split_factor,
                            N_("find a geometric progression with factor <N>")),
                OPT_BOOL('m', "write-midx", &write_midx,
                           N_("write a multi-pack index of the resulting packs")),
+               OPT_STRING(0, "expire-to", &expire_to, N_("dir"),
+                          N_("pack prefix to store a pack containing pruned objects")),
+               OPT_STRING(0, "filter-to", &filter_to, N_("dir"),
+                          N_("pack prefix to store a pack containing filtered out objects")),
                OPT_END()
        };
 
+       list_objects_filter_init(&po_args.filter_options);
+
        git_config(repack_config, &cruft_po_args);
 
        argc = parse_options(argc, argv, prefix, builtin_repack_options,
@@ -805,19 +1202,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (delete_redundant && repository_format_precious_objects)
                die(_("cannot delete packs in a precious-objects repo"));
 
-       if (keep_unreachable &&
-           (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
-               die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
+       die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A",
+                                 keep_unreachable, "-k/--keep-unreachable",
+                                 pack_everything & PACK_CRUFT, "--cruft");
 
-       if (pack_everything & PACK_CRUFT) {
+       if (pack_everything & PACK_CRUFT)
                pack_everything |= ALL_INTO_ONE;
 
-               if (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))
-                       die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-A");
-               if (keep_unreachable)
-                       die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-k");
-       }
-
        if (write_bitmaps < 0) {
                if (!write_midx &&
                    (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
@@ -833,6 +1224,18 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
                die(_(incremental_bitmap_conflict_error));
 
+       if (write_bitmaps && po_args.local && has_alt_odb(the_repository)) {
+               /*
+                * When asked to do a local repack, but we have
+                * packfiles that are inherited from an alternate, then
+                * we cannot guarantee that the multi-pack-index would
+                * have full coverage of all objects. We thus disable
+                * writing bitmaps in that case.
+                */
+               warning(_("disabling bitmap writing, as some objects are not being packed"));
+               write_bitmaps = 0;
+       }
+
        if (write_midx && write_bitmaps) {
                struct strbuf path = STRBUF_INIT;
 
@@ -849,19 +1252,16 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid());
        packtmp = mkpathdup("%s/%s", packdir, packtmp_name);
 
-       collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs,
-                              &keep_pack_list);
+       collect_pack_filenames(&existing, &keep_pack_list);
 
-       if (geometric_factor) {
+       if (geometry.split_factor) {
                if (pack_everything)
                        die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
-               init_pack_geometry(&geometry, &existing_kept_packs);
-               split_pack_geometry(geometry, geometric_factor);
+               init_pack_geometry(&geometry, &existing, &po_args);
+               split_pack_geometry(&geometry);
        }
 
-       sigchain_push_common(remove_pack_on_signal);
-
-       prepare_pack_objects(&cmd, &po_args);
+       prepare_pack_objects(&cmd, &po_args, packtmp);
 
        show_progress = !po_args.quiet && isatty(2);
 
@@ -872,7 +1272,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                strvec_pushf(&cmd.args, "--keep-pack=%s",
                             keep_pack_list.items[i].string);
        strvec_push(&cmd.args, "--non-empty");
-       if (!geometry) {
+       if (!geometry.split_factor) {
                /*
                 * We need to grab all reachable objects, including those that
                 * are reachable from reflogs and the index.
@@ -887,7 +1287,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                strvec_push(&cmd.args, "--reflog");
                strvec_push(&cmd.args, "--indexed-objects");
        }
-       if (has_promisor_remote())
+       if (repo_has_promisor_remote(the_repository))
                strvec_push(&cmd.args, "--exclude-promisor-objects");
        if (!write_midx) {
                if (write_bitmaps > 0)
@@ -901,7 +1301,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (pack_everything & ALL_INTO_ONE) {
                repack_promisor_objects(&po_args, &names);
 
-               if (existing_nonkept_packs.nr && delete_redundant &&
+               if (has_existing_non_kept_packs(&existing) &&
+                   delete_redundant &&
                    !(pack_everything & PACK_CRUFT)) {
                        for_each_string_list_item(item, &names) {
                                strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
@@ -919,7 +1320,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                strvec_push(&cmd.args, "--pack-loose-unreachable");
                        }
                }
-       } else if (geometry) {
+       } else if (geometry.split_factor) {
                strvec_push(&cmd.args, "--stdin-packs");
                strvec_push(&cmd.args, "--unpacked");
        } else {
@@ -927,50 +1328,44 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                strvec_push(&cmd.args, "--incremental");
        }
 
-       if (geometry)
+       if (po_args.filter_options.choice)
+               strvec_pushf(&cmd.args, "--filter=%s",
+                            expand_list_objects_filter_spec(&po_args.filter_options));
+       else if (filter_to)
+               die(_("option '%s' can only be used along with '%s'"), "--filter-to", "--filter");
+
+       if (geometry.split_factor)
                cmd.in = -1;
        else
                cmd.no_stdin = 1;
 
        ret = start_command(&cmd);
        if (ret)
-               return ret;
+               goto cleanup;
 
-       if (geometry) {
+       if (geometry.split_factor) {
                FILE *in = xfdopen(cmd.in, "w");
                /*
                 * The resulting pack should contain all objects in packs that
                 * are going to be rolled up, but exclude objects in packs which
                 * are being left alone.
                 */
-               for (i = 0; i < geometry->split; i++)
-                       fprintf(in, "%s\n", pack_basename(geometry->pack[i]));
-               for (i = geometry->split; i < geometry->pack_nr; i++)
-                       fprintf(in, "^%s\n", pack_basename(geometry->pack[i]));
+               for (i = 0; i < geometry.split; i++)
+                       fprintf(in, "%s\n", pack_basename(geometry.pack[i]));
+               for (i = geometry.split; i < geometry.pack_nr; i++)
+                       fprintf(in, "^%s\n", pack_basename(geometry.pack[i]));
                fclose(in);
        }
 
-       out = xfdopen(cmd.out, "r");
-       while (strbuf_getline_lf(&line, out) != EOF) {
-               if (line.len != the_hash_algo->hexsz)
-                       die(_("repack: Expecting full hex object ID lines only from pack-objects."));
-               string_list_append(&names, line.buf);
-       }
-       fclose(out);
-       ret = finish_command(&cmd);
+       ret = finish_pack_objects_cmd(&cmd, &names, 1);
        if (ret)
-               return ret;
+               goto cleanup;
 
        if (!names.nr && !po_args.quiet)
                printf_ln(_("Nothing new to pack."));
 
        if (pack_everything & PACK_CRUFT) {
-               const char *pack_prefix;
-               if (!skip_prefix(packtmp, packdir, &pack_prefix))
-                       die(_("pack prefix %s does not begin with objdir %s"),
-                           packtmp, packdir);
-               if (*pack_prefix == '/')
-                       pack_prefix++;
+               const char *pack_prefix = find_pack_prefix(packdir, packtmp);
 
                if (!cruft_po_args.window)
                        cruft_po_args.window = po_args.window;
@@ -980,119 +1375,131 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                        cruft_po_args.depth = po_args.depth;
                if (!cruft_po_args.threads)
                        cruft_po_args.threads = po_args.threads;
+               if (!cruft_po_args.max_pack_size)
+                       cruft_po_args.max_pack_size = po_args.max_pack_size;
 
                cruft_po_args.local = po_args.local;
                cruft_po_args.quiet = po_args.quiet;
 
-               ret = write_cruft_pack(&cruft_po_args, pack_prefix, &names,
-                                      &existing_nonkept_packs,
-                                      &existing_kept_packs);
+               ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix,
+                                      cruft_expiration, &names,
+                                      &existing);
                if (ret)
-                       return ret;
+                       goto cleanup;
+
+               if (delete_redundant && expire_to) {
+                       /*
+                        * If `--expire-to` is given with `-d`, it's possible
+                        * that we're about to prune some objects. With cruft
+                        * packs, pruning is implicit: any objects from existing
+                        * packs that weren't picked up by new packs are removed
+                        * when their packs are deleted.
+                        *
+                        * Generate an additional cruft pack, with one twist:
+                        * `names` now includes the name of the cruft pack
+                        * written in the previous step. So the contents of
+                        * _this_ cruft pack exclude everything contained in the
+                        * existing cruft pack (that is, all of the unreachable
+                        * objects which are no older than
+                        * `--cruft-expiration`).
+                        *
+                        * To make this work, cruft_expiration must become NULL
+                        * so that this cruft pack doesn't actually prune any
+                        * objects. If it were non-NULL, this call would always
+                        * generate an empty pack (since every object not in the
+                        * cruft pack generated above will have an mtime older
+                        * than the expiration).
+                        */
+                       ret = write_cruft_pack(&cruft_po_args, expire_to,
+                                              pack_prefix,
+                                              NULL,
+                                              &names,
+                                              &existing);
+                       if (ret)
+                               goto cleanup;
+               }
        }
 
-       string_list_sort(&names);
+       if (po_args.filter_options.choice) {
+               if (!filter_to)
+                       filter_to = packtmp;
 
-       for_each_string_list_item(item, &names) {
-               item->util = (void *)(uintptr_t)populate_pack_exts(item->string);
+               ret = write_filtered_pack(&po_args,
+                                         filter_to,
+                                         find_pack_prefix(packdir, packtmp),
+                                         &existing,
+                                         &names);
+               if (ret)
+                       goto cleanup;
        }
 
+       string_list_sort(&names);
+
        close_object_store(the_repository->objects);
 
        /*
         * Ok we have prepared all new packfiles.
         */
        for_each_string_list_item(item, &names) {
+               struct generated_pack_data *data = item->util;
+
                for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
-                       char *fname, *fname_old;
+                       char *fname;
 
                        fname = mkpathdup("%s/pack-%s%s",
                                        packdir, item->string, exts[ext].name);
-                       fname_old = mkpathdup("%s-%s%s",
-                                       packtmp, item->string, exts[ext].name);
 
-                       if (((uintptr_t)item->util) & ((uintptr_t)1 << ext)) {
+                       if (data->tempfiles[ext]) {
+                               const char *fname_old = get_tempfile_path(data->tempfiles[ext]);
                                struct stat statbuffer;
+
                                if (!stat(fname_old, &statbuffer)) {
                                        statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
                                        chmod(fname_old, statbuffer.st_mode);
                                }
 
-                               if (rename(fname_old, fname))
-                                       die_errno(_("renaming '%s' failed"), fname_old);
+                               if (rename_tempfile(&data->tempfiles[ext], fname))
+                                       die_errno(_("renaming pack to '%s' failed"), fname);
                        } else if (!exts[ext].optional)
-                               die(_("missing required file: %s"), fname_old);
+                               die(_("pack-objects did not write a '%s' file for pack %s-%s"),
+                                   exts[ext].name, packtmp, item->string);
                        else if (unlink(fname) < 0 && errno != ENOENT)
                                die_errno(_("could not unlink: %s"), fname);
 
                        free(fname);
-                       free(fname_old);
                }
        }
        /* End of pack replacement. */
 
-       if (delete_redundant && pack_everything & ALL_INTO_ONE) {
-               const int hexsz = the_hash_algo->hexsz;
-               for_each_string_list_item(item, &existing_nonkept_packs) {
-                       char *sha1;
-                       size_t len = strlen(item->string);
-                       if (len < hexsz)
-                               continue;
-                       sha1 = item->string + len - hexsz;
-                       /*
-                        * Mark this pack for deletion, which ensures that this
-                        * pack won't be included in a MIDX (if `--write-midx`
-                        * was given) and that we will actually delete this pack
-                        * (if `-d` was given).
-                        */
-                       if (!string_list_has_string(&names, sha1))
-                               item->util = (void*)(uintptr_t)((size_t)item->util | DELETE_PACK);
-               }
-       }
+       if (delete_redundant && pack_everything & ALL_INTO_ONE)
+               mark_packs_for_deletion(&existing, &names);
 
        if (write_midx) {
                struct string_list include = STRING_LIST_INIT_NODUP;
-               midx_included_packs(&include, &existing_nonkept_packs,
-                                   &existing_kept_packs, &names, geometry);
+               midx_included_packs(&include, &existing, &names, &geometry);
 
-               ret = write_midx_included_packs(&include, geometry,
+               ret = write_midx_included_packs(&include, &geometry, &names,
                                                refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
                                                show_progress, write_bitmaps > 0);
 
+               if (!ret && write_bitmaps)
+                       remove_redundant_bitmaps(&include, packdir);
+
                string_list_clear(&include, 0);
 
                if (ret)
-                       return ret;
+                       goto cleanup;
        }
 
        reprepare_packed_git(the_repository);
 
        if (delete_redundant) {
                int opts = 0;
-               for_each_string_list_item(item, &existing_nonkept_packs) {
-                       if (!((uintptr_t)item->util & DELETE_PACK))
-                               continue;
-                       remove_redundant_pack(packdir, item->string);
-               }
-
-               if (geometry) {
-                       struct strbuf buf = STRBUF_INIT;
-
-                       uint32_t i;
-                       for (i = 0; i < geometry->split; i++) {
-                               struct packed_git *p = geometry->pack[i];
-                               if (string_list_has_string(&names,
-                                                          hash_to_hex(p->hash)))
-                                       continue;
+               remove_redundant_existing_packs(&existing);
 
-                               strbuf_reset(&buf);
-                               strbuf_addstr(&buf, pack_basename(p));
-                               strbuf_strip_suffix(&buf, ".pack");
-
-                               remove_redundant_pack(packdir, buf.buf);
-                       }
-                       strbuf_release(&buf);
-               }
+               if (geometry.split_factor)
+                       geometry_remove_redundant_packs(&geometry, &names,
+                                                       &existing);
                if (show_progress)
                        opts |= PRUNE_PACKED_VERBOSE;
                prune_packed_objects(opts);
@@ -1106,7 +1513,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        if (run_update_server_info)
                update_server_info(0);
-       remove_temporary_files();
 
        if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) {
                unsigned flags = 0;
@@ -1115,11 +1521,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                write_midx_file(get_object_directory(), NULL, NULL, flags);
        }
 
-       string_list_clear(&names, 0);
-       string_list_clear(&existing_nonkept_packs, 0);
-       string_list_clear(&existing_kept_packs, 0);
-       clear_pack_geometry(geometry);
-       strbuf_release(&line);
+cleanup:
+       string_list_clear(&names, 1);
+       existing_packs_release(&existing);
+       free_pack_geometry(&geometry);
+       list_objects_filter_release(&po_args.filter_options);
 
-       return 0;
+       return ret;
 }
index a29e911d3099be9d327ecc030d68c56fe046a04a..da59600ad22fda16155a278a210478fb55d0d94e 100644 (file)
@@ -8,15 +8,23 @@
  * git-tag.sh and mktag.c by Linus Torvalds.
  */
 
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
 #include "parse-options.h"
+#include "path.h"
 #include "run-command.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
 #include "repository.h"
 #include "tag.h"
+#include "wildmatch.h"
 
 static const char * const git_replace_usage[] = {
        N_("git replace [-f] <object> <replacement>"),
@@ -41,7 +49,7 @@ struct show_data {
 
 static int show_reference(struct repository *r, const char *refname,
                          const struct object_id *oid,
-                         int flag, void *cb_data)
+                         int flag UNUSED, void *cb_data)
 {
        struct show_data *data = cb_data;
 
@@ -54,7 +62,7 @@ static int show_reference(struct repository *r, const char *refname,
                        struct object_id object;
                        enum object_type obj_type, repl_type;
 
-                       if (get_oid(refname, &object))
+                       if (repo_get_oid(r, refname, &object))
                                return error(_("failed to resolve '%s' as a valid ref"), refname);
 
                        obj_type = oid_object_info(r, &object, NULL);
@@ -112,7 +120,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
        base_len = ref.len;
 
        for (p = argv; *p; p++) {
-               if (get_oid(*p, &oid)) {
+               if (repo_get_oid(the_repository, *p, &oid)) {
                        error("failed to resolve '%s' as a valid ref", *p);
                        had_error = 1;
                        continue;
@@ -206,10 +214,10 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
 {
        struct object_id object, repl;
 
-       if (get_oid(object_ref, &object))
+       if (repo_get_oid(the_repository, object_ref, &object))
                return error(_("failed to resolve '%s' as a valid ref"),
                             object_ref);
-       if (get_oid(replace_ref, &repl))
+       if (repo_get_oid(the_repository, replace_ref, &repl))
                return error(_("failed to resolve '%s' as a valid ref"),
                             replace_ref);
 
@@ -320,7 +328,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
        struct object_id old_oid, new_oid, prev;
        struct strbuf ref = STRBUF_INIT;
 
-       if (get_oid(object_ref, &old_oid) < 0)
+       if (repo_get_oid(the_repository, object_ref, &old_oid) < 0)
                return error(_("not a valid object name: '%s'"), object_ref);
 
        type = oid_object_info(the_repository, &old_oid, NULL);
@@ -375,7 +383,7 @@ static int replace_parents(struct strbuf *buf, int argc, const char **argv)
                struct object_id oid;
                struct commit *commit;
 
-               if (get_oid(argv[i], &oid) < 0) {
+               if (repo_get_oid(the_repository, argv[i], &oid) < 0) {
                        strbuf_release(&new_parents);
                        return error(_("not a valid object name: '%s'"),
                                     argv[i]);
@@ -401,7 +409,7 @@ struct check_mergetag_data {
        const char **argv;
 };
 
-static int check_one_mergetag(struct commit *commit,
+static int check_one_mergetag(struct commit *commit UNUSED,
                               struct commit_extra_header *extra,
                               void *data)
 {
@@ -422,7 +430,7 @@ static int check_one_mergetag(struct commit *commit,
        /* iterate over new parents */
        for (i = 1; i < mergetag_data->argc; i++) {
                struct object_id oid;
-               if (get_oid(mergetag_data->argv[i], &oid) < 0)
+               if (repo_get_oid(the_repository, mergetag_data->argv[i], &oid) < 0)
                        return error(_("not a valid object name: '%s'"),
                                     mergetag_data->argv[i]);
                if (oideq(get_tagged_oid(tag), &oid))
@@ -452,15 +460,15 @@ static int create_graft(int argc, const char **argv, int force, int gentle)
        const char *buffer;
        unsigned long size;
 
-       if (get_oid(old_ref, &old_oid) < 0)
+       if (repo_get_oid(the_repository, old_ref, &old_oid) < 0)
                return error(_("not a valid object name: '%s'"), old_ref);
        commit = lookup_commit_reference(the_repository, &old_oid);
        if (!commit)
                return error(_("could not parse %s"), old_ref);
 
-       buffer = get_commit_buffer(commit, &size);
+       buffer = repo_get_commit_buffer(the_repository, commit, &size);
        strbuf_add(&buf, buffer, size);
-       unuse_commit_buffer(commit, buffer);
+       repo_unuse_commit_buffer(the_repository, commit, buffer);
 
        if (replace_parents(&buf, argc - 1, &argv[1]) < 0) {
                strbuf_release(&buf);
@@ -559,7 +567,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       read_replace_refs = 0;
+       disable_replace_refs();
        git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
diff --git a/builtin/replay.c b/builtin/replay.c
new file mode 100644 (file)
index 0000000..6bc4b47
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+ * "git replay" builtin command
+ */
+
+#define USE_THE_INDEX_VARIABLE
+#include "git-compat-util.h"
+
+#include "builtin.h"
+#include "environment.h"
+#include "hex.h"
+#include "lockfile.h"
+#include "merge-ort.h"
+#include "object-name.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "revision.h"
+#include "strmap.h"
+#include <oidset.h>
+#include <tree.h>
+
+static const char *short_commit_name(struct commit *commit)
+{
+       return repo_find_unique_abbrev(the_repository, &commit->object.oid,
+                                      DEFAULT_ABBREV);
+}
+
+static struct commit *peel_committish(const char *name)
+{
+       struct object *obj;
+       struct object_id oid;
+
+       if (repo_get_oid(the_repository, name, &oid))
+               return NULL;
+       obj = parse_object(the_repository, &oid);
+       return (struct commit *)repo_peel_to_type(the_repository, name, 0, obj,
+                                                 OBJ_COMMIT);
+}
+
+static char *get_author(const char *message)
+{
+       size_t len;
+       const char *a;
+
+       a = find_commit_header(message, "author", &len);
+       if (a)
+               return xmemdupz(a, len);
+
+       return NULL;
+}
+
+static struct commit *create_commit(struct tree *tree,
+                                   struct commit *based_on,
+                                   struct commit *parent)
+{
+       struct object_id ret;
+       struct object *obj;
+       struct commit_list *parents = NULL;
+       char *author;
+       char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
+       struct commit_extra_header *extra;
+       struct strbuf msg = STRBUF_INIT;
+       const char *out_enc = get_commit_output_encoding();
+       const char *message = repo_logmsg_reencode(the_repository, based_on,
+                                                  NULL, out_enc);
+       const char *orig_message = NULL;
+       const char *exclude_gpgsig[] = { "gpgsig", NULL };
+
+       commit_list_insert(parent, &parents);
+       extra = read_commit_extra_headers(based_on, exclude_gpgsig);
+       find_commit_subject(message, &orig_message);
+       strbuf_addstr(&msg, orig_message);
+       author = get_author(message);
+       reset_ident_date();
+       if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
+                                &ret, author, NULL, sign_commit, extra)) {
+               error(_("failed to write commit object"));
+               return NULL;
+       }
+       free(author);
+       strbuf_release(&msg);
+
+       obj = parse_object(the_repository, &ret);
+       return (struct commit *)obj;
+}
+
+struct ref_info {
+       struct commit *onto;
+       struct strset positive_refs;
+       struct strset negative_refs;
+       int positive_refexprs;
+       int negative_refexprs;
+};
+
+static void get_ref_information(struct rev_cmdline_info *cmd_info,
+                               struct ref_info *ref_info)
+{
+       int i;
+
+       ref_info->onto = NULL;
+       strset_init(&ref_info->positive_refs);
+       strset_init(&ref_info->negative_refs);
+       ref_info->positive_refexprs = 0;
+       ref_info->negative_refexprs = 0;
+
+       /*
+        * When the user specifies e.g.
+        *   git replay origin/main..mybranch
+        *   git replay ^origin/next mybranch1 mybranch2
+        * we want to be able to determine where to replay the commits.  In
+        * these examples, the branches are probably based on an old version
+        * of either origin/main or origin/next, so we want to replay on the
+        * newest version of that branch.  In contrast we would want to error
+        * out if they ran
+        *   git replay ^origin/master ^origin/next mybranch
+        *   git replay mybranch~2..mybranch
+        * the first of those because there's no unique base to choose, and
+        * the second because they'd likely just be replaying commits on top
+        * of the same commit and not making any difference.
+        */
+       for (i = 0; i < cmd_info->nr; i++) {
+               struct rev_cmdline_entry *e = cmd_info->rev + i;
+               struct object_id oid;
+               const char *refexpr = e->name;
+               char *fullname = NULL;
+               int can_uniquely_dwim = 1;
+
+               if (*refexpr == '^')
+                       refexpr++;
+               if (repo_dwim_ref(the_repository, refexpr, strlen(refexpr), &oid, &fullname, 0) != 1)
+                       can_uniquely_dwim = 0;
+
+               if (e->flags & BOTTOM) {
+                       if (can_uniquely_dwim)
+                               strset_add(&ref_info->negative_refs, fullname);
+                       if (!ref_info->negative_refexprs)
+                               ref_info->onto = lookup_commit_reference_gently(the_repository,
+                                                                               &e->item->oid, 1);
+                       ref_info->negative_refexprs++;
+               } else {
+                       if (can_uniquely_dwim)
+                               strset_add(&ref_info->positive_refs, fullname);
+                       ref_info->positive_refexprs++;
+               }
+
+               free(fullname);
+       }
+}
+
+static void determine_replay_mode(struct rev_cmdline_info *cmd_info,
+                                 const char *onto_name,
+                                 const char **advance_name,
+                                 struct commit **onto,
+                                 struct strset **update_refs)
+{
+       struct ref_info rinfo;
+
+       get_ref_information(cmd_info, &rinfo);
+       if (!rinfo.positive_refexprs)
+               die(_("need some commits to replay"));
+       if (onto_name && *advance_name)
+               die(_("--onto and --advance are incompatible"));
+       else if (onto_name) {
+               *onto = peel_committish(onto_name);
+               if (rinfo.positive_refexprs <
+                   strset_get_size(&rinfo.positive_refs))
+                       die(_("all positive revisions given must be references"));
+       } else if (*advance_name) {
+               struct object_id oid;
+               char *fullname = NULL;
+
+               *onto = peel_committish(*advance_name);
+               if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name),
+                            &oid, &fullname, 0) == 1) {
+                       *advance_name = fullname;
+               } else {
+                       die(_("argument to --advance must be a reference"));
+               }
+               if (rinfo.positive_refexprs > 1)
+                       die(_("cannot advance target with multiple sources because ordering would be ill-defined"));
+       } else {
+               int positive_refs_complete = (
+                       rinfo.positive_refexprs ==
+                       strset_get_size(&rinfo.positive_refs));
+               int negative_refs_complete = (
+                       rinfo.negative_refexprs ==
+                       strset_get_size(&rinfo.negative_refs));
+               /*
+                * We need either positive_refs_complete or
+                * negative_refs_complete, but not both.
+                */
+               if (rinfo.negative_refexprs > 0 &&
+                   positive_refs_complete == negative_refs_complete)
+                       die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
+               if (negative_refs_complete) {
+                       struct hashmap_iter iter;
+                       struct strmap_entry *entry;
+
+                       if (rinfo.negative_refexprs == 0)
+                               die(_("all positive revisions given must be references"));
+                       else if (rinfo.negative_refexprs > 1)
+                               die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
+                       else if (rinfo.positive_refexprs > 1)
+                               die(_("cannot advance target with multiple source branches because ordering would be ill-defined"));
+
+                       /* Only one entry, but we have to loop to get it */
+                       strset_for_each_entry(&rinfo.negative_refs,
+                                             &iter, entry) {
+                               *advance_name = entry->key;
+                       }
+               } else { /* positive_refs_complete */
+                       if (rinfo.negative_refexprs > 1)
+                               die(_("cannot implicitly determine correct base for --onto"));
+                       if (rinfo.negative_refexprs == 1)
+                               *onto = rinfo.onto;
+               }
+       }
+       if (!*advance_name) {
+               *update_refs = xcalloc(1, sizeof(**update_refs));
+               **update_refs = rinfo.positive_refs;
+               memset(&rinfo.positive_refs, 0, sizeof(**update_refs));
+       }
+       strset_clear(&rinfo.negative_refs);
+       strset_clear(&rinfo.positive_refs);
+}
+
+static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
+                                   struct commit *commit,
+                                   struct commit *fallback)
+{
+       khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid);
+       if (pos == kh_end(replayed_commits))
+               return fallback;
+       return kh_value(replayed_commits, pos);
+}
+
+static struct commit *pick_regular_commit(struct commit *pickme,
+                                         kh_oid_map_t *replayed_commits,
+                                         struct commit *onto,
+                                         struct merge_options *merge_opt,
+                                         struct merge_result *result)
+{
+       struct commit *base, *replayed_base;
+       struct tree *pickme_tree, *base_tree;
+
+       base = pickme->parents->item;
+       replayed_base = mapped_commit(replayed_commits, base, onto);
+
+       result->tree = repo_get_commit_tree(the_repository, replayed_base);
+       pickme_tree = repo_get_commit_tree(the_repository, pickme);
+       base_tree = repo_get_commit_tree(the_repository, base);
+
+       merge_opt->branch1 = short_commit_name(replayed_base);
+       merge_opt->branch2 = short_commit_name(pickme);
+       merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
+
+       merge_incore_nonrecursive(merge_opt,
+                                 base_tree,
+                                 result->tree,
+                                 pickme_tree,
+                                 result);
+
+       free((char*)merge_opt->ancestor);
+       merge_opt->ancestor = NULL;
+       if (!result->clean)
+               return NULL;
+       return create_commit(result->tree, pickme, replayed_base);
+}
+
+int cmd_replay(int argc, const char **argv, const char *prefix)
+{
+       const char *advance_name = NULL;
+       struct commit *onto = NULL;
+       const char *onto_name = NULL;
+       int contained = 0;
+
+       struct rev_info revs;
+       struct commit *last_commit = NULL;
+       struct commit *commit;
+       struct merge_options merge_opt;
+       struct merge_result result;
+       struct strset *update_refs = NULL;
+       kh_oid_map_t *replayed_commits;
+       int ret = 0;
+
+       const char * const replay_usage[] = {
+               N_("(EXPERIMENTAL!) git replay "
+                  "([--contained] --onto <newbase> | --advance <branch>) "
+                  "<revision-range>..."),
+               NULL
+       };
+       struct option replay_options[] = {
+               OPT_STRING(0, "advance", &advance_name,
+                          N_("branch"),
+                          N_("make replay advance given branch")),
+               OPT_STRING(0, "onto", &onto_name,
+                          N_("revision"),
+                          N_("replay onto given commit")),
+               OPT_BOOL(0, "contained", &contained,
+                        N_("advance all branches contained in revision-range")),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, replay_options, replay_usage,
+                            PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
+
+       if (!onto_name && !advance_name) {
+               error(_("option --onto or --advance is mandatory"));
+               usage_with_options(replay_usage, replay_options);
+       }
+
+       if (advance_name && contained)
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--advance", "--contained");
+
+       repo_init_revisions(the_repository, &revs, prefix);
+
+       /*
+        * Set desired values for rev walking options here. If they
+        * are changed by some user specified option in setup_revisions()
+        * below, we will detect that below and then warn.
+        *
+        * TODO: In the future we might want to either die(), or allow
+        * some options changing these values if we think they could
+        * be useful.
+        */
+       revs.reverse = 1;
+       revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+       revs.topo_order = 1;
+       revs.simplify_history = 0;
+
+       argc = setup_revisions(argc, argv, &revs, NULL);
+       if (argc > 1) {
+               ret = error(_("unrecognized argument: %s"), argv[1]);
+               goto cleanup;
+       }
+
+       /*
+        * Detect and warn if we override some user specified rev
+        * walking options.
+        */
+       if (revs.reverse != 1) {
+               warning(_("some rev walking options will be overridden as "
+                         "'%s' bit in 'struct rev_info' will be forced"),
+                       "reverse");
+               revs.reverse = 1;
+       }
+       if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) {
+               warning(_("some rev walking options will be overridden as "
+                         "'%s' bit in 'struct rev_info' will be forced"),
+                       "sort_order");
+               revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+       }
+       if (revs.topo_order != 1) {
+               warning(_("some rev walking options will be overridden as "
+                         "'%s' bit in 'struct rev_info' will be forced"),
+                       "topo_order");
+               revs.topo_order = 1;
+       }
+       if (revs.simplify_history != 0) {
+               warning(_("some rev walking options will be overridden as "
+                         "'%s' bit in 'struct rev_info' will be forced"),
+                       "simplify_history");
+               revs.simplify_history = 0;
+       }
+
+       determine_replay_mode(&revs.cmdline, onto_name, &advance_name,
+                             &onto, &update_refs);
+
+       if (!onto) /* FIXME: Should handle replaying down to root commit */
+               die("Replaying down to root commit is not supported yet!");
+
+       if (prepare_revision_walk(&revs) < 0) {
+               ret = error(_("error preparing revisions"));
+               goto cleanup;
+       }
+
+       init_merge_options(&merge_opt, the_repository);
+       memset(&result, 0, sizeof(result));
+       merge_opt.show_rename_progress = 0;
+       last_commit = onto;
+       replayed_commits = kh_init_oid_map();
+       while ((commit = get_revision(&revs))) {
+               const struct name_decoration *decoration;
+               khint_t pos;
+               int hr;
+
+               if (!commit->parents)
+                       die(_("replaying down to root commit is not supported yet!"));
+               if (commit->parents->next)
+                       die(_("replaying merge commits is not supported yet!"));
+
+               last_commit = pick_regular_commit(commit, replayed_commits, onto,
+                                                 &merge_opt, &result);
+               if (!last_commit)
+                       break;
+
+               /* Record commit -> last_commit mapping */
+               pos = kh_put_oid_map(replayed_commits, commit->object.oid, &hr);
+               if (hr == 0)
+                       BUG("Duplicate rewritten commit: %s\n",
+                           oid_to_hex(&commit->object.oid));
+               kh_value(replayed_commits, pos) = last_commit;
+
+               /* Update any necessary branches */
+               if (advance_name)
+                       continue;
+               decoration = get_name_decoration(&commit->object);
+               if (!decoration)
+                       continue;
+               while (decoration) {
+                       if (decoration->type == DECORATION_REF_LOCAL &&
+                           (contained || strset_contains(update_refs,
+                                                         decoration->name))) {
+                               printf("update %s %s %s\n",
+                                      decoration->name,
+                                      oid_to_hex(&last_commit->object.oid),
+                                      oid_to_hex(&commit->object.oid));
+                       }
+                       decoration = decoration->next;
+               }
+       }
+
+       /* In --advance mode, advance the target ref */
+       if (result.clean == 1 && advance_name) {
+               printf("update %s %s %s\n",
+                      advance_name,
+                      oid_to_hex(&last_commit->object.oid),
+                      oid_to_hex(&onto->object.oid));
+       }
+
+       merge_finalize(&merge_opt, &result);
+       kh_destroy_oid_map(replayed_commits);
+       if (update_refs) {
+               strset_clear(update_refs);
+               free(update_refs);
+       }
+       ret = result.clean;
+
+cleanup:
+       release_revisions(&revs);
+
+       /* Return */
+       if (ret < 0)
+               exit(128);
+       return ret ? 0 : 1;
+}
index 83d7a778e374be053f861145690c161b21d9801c..b2efc6f640e18a2f956cdc985ecc2c793120a267 100644 (file)
@@ -1,8 +1,8 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
-#include "dir.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "xdiff/xdiff.h"
 #include "pathspec.h"
 
 static const char * const rerere_usage[] = {
-       N_("git rerere [clear | forget <path>... | status | remaining | diff | gc]"),
+       N_("git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"),
        NULL,
 };
 
-static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
+static int outf(void *dummy UNUSED, mmbuffer_t *ptr, int nbuf)
 {
        int i;
        for (i = 0; i < nbuf; i++)
index fdce6f8c85670c8b2b0e20304336debba3190408..8390bfe4c487b0c3170045f0601234c1e8278122 100644 (file)
@@ -7,25 +7,34 @@
  *
  * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "lockfile.h"
-#include "tag.h"
 #include "object.h"
 #include "pretty.h"
-#include "run-command.h"
 #include "refs.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "tree.h"
 #include "branch.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
 #include "unpack-trees.h"
 #include "cache-tree.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
+#include "trace.h"
+#include "trace2.h"
 #include "dir.h"
+#include "add-interactive.h"
 
 #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
 
@@ -73,20 +82,22 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
        case HARD:
                opts.update = 1;
                opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+               opts.skip_cache_tree_update = 1;
                break;
        case MIXED:
                opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
+               opts.skip_cache_tree_update = 1;
                /* but opts.update=0, so working tree not updated */
                break;
        default:
                BUG("invalid reset_type passed to reset_index");
        }
 
-       read_cache_unmerged();
+       repo_read_index_unmerged(the_repository);
 
        if (reset_type == KEEP) {
                struct object_id head_oid;
-               if (get_oid("HEAD", &head_oid))
+               if (repo_get_oid(the_repository, "HEAD", &head_oid))
                        return error(_("You do not have a valid HEAD."));
                if (!fill_tree_descriptor(the_repository, desc + nr, &head_oid))
                        return error(_("Failed to find tree of HEAD."));
@@ -121,7 +132,7 @@ static void print_new_head_line(struct commit *commit)
        struct strbuf buf = STRBUF_INIT;
 
        printf(_("HEAD is now at %s"),
-               find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+               repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
 
        pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
        if (buf.len > 0)
@@ -131,7 +142,8 @@ static void print_new_head_line(struct commit *commit)
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
-               struct diff_options *opt, void *data)
+                                  struct diff_options *opt UNUSED,
+                                  void *data)
 {
        int i;
        int intent_to_add = *(int *)data;
@@ -143,7 +155,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
                struct cache_entry *ce;
 
                if (!is_in_reset_tree && !intent_to_add) {
-                       remove_file_from_cache(one->path);
+                       remove_file_from_index(&the_index, one->path);
                        continue;
                }
 
@@ -158,8 +170,8 @@ static void update_index_from_diff(struct diff_queue_struct *q,
                 * if this entry is outside the sparse cone - this is necessary
                 * to properly construct the reset sparse directory.
                 */
-               pos = cache_name_pos(one->path, strlen(one->path));
-               if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) ||
+               pos = index_name_pos(&the_index, one->path, strlen(one->path));
+               if ((pos >= 0 && ce_skip_worktree(the_index.cache[pos])) ||
                    (pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
                        ce->ce_flags |= CE_SKIP_WORKTREE;
 
@@ -170,7 +182,8 @@ static void update_index_from_diff(struct diff_queue_struct *q,
                        ce->ce_flags |= CE_INTENT_TO_ADD;
                        set_object_name_for_intent_to_add_entry(ce);
                }
-               add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+               add_index_entry(&the_index, ce,
+                               ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        }
 }
 
@@ -218,7 +231,7 @@ static void set_reflog_message(struct strbuf *sb, const char *action,
 
 static void die_if_unmerged_cache(int reset_type)
 {
-       if (is_merge() || unmerged_cache())
+       if (is_merge() || unmerged_index(&the_index))
                die(_("Cannot do a %s reset in the middle of a merge."),
                    _(reset_type_names[reset_type]));
 
@@ -255,8 +268,8 @@ static void parse_args(struct pathspec *pathspec,
                 * has to be unambiguous. If there is a single argument, it
                 * can not be a tree
                 */
-               else if ((!argv[1] && !get_oid_committish(argv[0], &unused)) ||
-                        (argv[1] && !get_oid_treeish(argv[0], &unused))) {
+               else if ((!argv[1] && !repo_get_oid_committish(the_repository, argv[0], &unused)) ||
+                        (argv[1] && !repo_get_oid_treeish(the_repository, argv[0], &unused))) {
                        /*
                         * Ok, argv[0] looks like a commit/tree; it should not
                         * be a filename.
@@ -283,9 +296,9 @@ static int reset_refs(const char *rev, const struct object_id *oid)
        struct object_id *orig = NULL, oid_orig,
                *old_orig = NULL, oid_old_orig;
 
-       if (!get_oid("ORIG_HEAD", &oid_old_orig))
+       if (!repo_get_oid(the_repository, "ORIG_HEAD", &oid_old_orig))
                old_orig = &oid_old_orig;
-       if (!get_oid("HEAD", &oid_orig)) {
+       if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) {
                orig = &oid_orig;
                set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
                update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
@@ -299,12 +312,13 @@ static int reset_refs(const char *rev, const struct object_id *oid)
        return update_ref_status;
 }
 
-static int git_reset_config(const char *var, const char *value, void *cb)
+static int git_reset_config(const char *var, const char *value,
+                           const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "submodule.recurse"))
                return git_default_submodule_config(var, value, cb);
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
@@ -312,7 +326,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        int reset_type = NONE, update_ref_status = 0, quiet = 0;
        int no_refresh = 0;
        int patch_mode = 0, pathspec_file_nul = 0, unborn;
-       const char *rev, *pathspec_from_file = NULL;
+       const char *rev;
+       char *pathspec_from_file = NULL;
        struct object_id oid;
        struct pathspec pathspec;
        int intent_to_add = 0;
@@ -320,18 +335,25 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                OPT__QUIET(&quiet, N_("be quiet, only report errors")),
                OPT_BOOL(0, "no-refresh", &no_refresh,
                                N_("skip refreshing the index after reset")),
-               OPT_SET_INT(0, "mixed", &reset_type,
-                                               N_("reset HEAD and index"), MIXED),
-               OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT),
-               OPT_SET_INT(0, "hard", &reset_type,
-                               N_("reset HEAD, index and working tree"), HARD),
-               OPT_SET_INT(0, "merge", &reset_type,
-                               N_("reset HEAD, index and working tree"), MERGE),
-               OPT_SET_INT(0, "keep", &reset_type,
-                               N_("reset HEAD but keep local changes"), KEEP),
+               OPT_SET_INT_F(0, "mixed", &reset_type,
+                             N_("reset HEAD and index"),
+                             MIXED, PARSE_OPT_NONEG),
+               OPT_SET_INT_F(0, "soft", &reset_type,
+                             N_("reset only HEAD"),
+                             SOFT, PARSE_OPT_NONEG),
+               OPT_SET_INT_F(0, "hard", &reset_type,
+                             N_("reset HEAD, index and working tree"),
+                             HARD, PARSE_OPT_NONEG),
+               OPT_SET_INT_F(0, "merge", &reset_type,
+                             N_("reset HEAD, index and working tree"),
+                             MERGE, PARSE_OPT_NONEG),
+               OPT_SET_INT_F(0, "keep", &reset_type,
+                             N_("reset HEAD but keep local changes"),
+                             KEEP, PARSE_OPT_NONEG),
                OPT_CALLBACK_F(0, "recurse-submodules", NULL,
-                           "reset", "control recursive updating of submodules",
-                           PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
+                              "reset", "control recursive updating of submodules",
+                              PARSE_OPT_OPTARG,
+                              option_parse_recurse_submodules_worktree_updater),
                OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
                OPT_BOOL('N', "intent-to-add", &intent_to_add,
                                N_("record only the fact that removed paths will be added later")),
@@ -360,13 +382,14 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
-       unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
+       unborn = !strcmp(rev, "HEAD") && repo_get_oid(the_repository, "HEAD",
+                                                     &oid);
        if (unborn) {
                /* reset on unborn branch: treat as reset to empty tree */
                oidcpy(&oid, the_hash_algo->empty_tree);
        } else if (!pathspec.nr && !patch_mode) {
                struct commit *commit;
-               if (get_oid_committish(rev, &oid))
+               if (repo_get_oid_committish(the_repository, rev, &oid))
                        die(_("Failed to resolve '%s' as a valid revision."), rev);
                commit = lookup_commit_reference(the_repository, &oid);
                if (!commit)
@@ -374,7 +397,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                oidcpy(&oid, &commit->object.oid);
        } else {
                struct tree *tree;
-               if (get_oid_treeish(rev, &oid))
+               if (repo_get_oid_treeish(the_repository, rev, &oid))
                        die(_("Failed to resolve '%s' as a valid tree."), rev);
                tree = parse_tree_indirect(&oid);
                if (!tree)
@@ -386,7 +409,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                if (reset_type != NONE)
                        die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
                trace2_cmd_mode("patch-interactive");
-               return run_add_interactive(rev, "--patch=reset", &pathspec);
+               update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev,
+                                  &pathspec);
+               goto cleanup;
        }
 
        /* git reset tree [--] paths... can be used to
@@ -420,7 +445,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                die(_("index file corrupt"));
 
        /* Soft reset does not touch the index file nor the working tree
@@ -431,11 +456,14 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        if (reset_type != SOFT) {
                struct lock_file lock = LOCK_INIT;
-               hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+               repo_hold_locked_index(the_repository, &lock,
+                                      LOCK_DIE_ON_ERROR);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
-                       if (read_from_tree(&pathspec, &oid, intent_to_add))
-                               return 1;
+                       if (read_from_tree(&pathspec, &oid, intent_to_add)) {
+                               update_ref_status = 1;
+                               goto cleanup;
+                       }
                        the_index.updated_skipworktree = 1;
                        if (!no_refresh && get_git_work_tree()) {
                                uint64_t t_begin, t_delta_in_ms;
@@ -454,7 +482,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                        char *ref = NULL;
                        int err;
 
-                       dwim_ref(rev, strlen(rev), &dummy, &ref, 0);
+                       repo_dwim_ref(the_repository, rev, strlen(rev),
+                                     &dummy, &ref, 0);
                        if (ref && !starts_with(ref, "refs/"))
                                FREE_AND_NULL(ref);
 
@@ -481,5 +510,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        if (!pathspec.nr)
                remove_branch_state(the_repository, 0);
 
+       discard_index(&the_index);
+
+cleanup:
+       clear_pathspec(&pathspec);
+       free(pathspec_from_file);
        return update_ref_status;
 }
index fba6f5d51f32d1217b89298e1361169b43feb38b..b3f47838580c9dd0ed233a3ac34c19aac6b7fa0f 100644 (file)
@@ -1,16 +1,18 @@
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "revision.h"
 #include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "object.h"
-#include "object-store.h"
-#include "pack.h"
+#include "object-name.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "pack-bitmap.h"
-#include "builtin.h"
 #include "log-tree.h"
 #include "graph.h"
 #include "bisect.h"
@@ -20,7 +22,8 @@
 #include "packfile.h"
 
 static const char rev_list_usage[] =
-"git rev-list [<options>] <commit-id>... [-- <path>...]\n"
+"git rev-list [<options>] <commit>... [--] [<path>...]\n"
+"\n"
 "  limiting output:\n"
 "    --max-count=<n>\n"
 "    --max-age=<epoch>\n"
@@ -37,6 +40,7 @@ static const char rev_list_usage[] =
 "    --tags\n"
 "    --remotes\n"
 "    --stdin\n"
+"    --exclude-hidden=[fetch|receive|uploadpack]\n"
 "    --quiet\n"
 "  ordering output:\n"
 "    --topo-order\n"
@@ -94,7 +98,48 @@ static off_t get_object_disk_usage(struct object *obj)
        return size;
 }
 
-static void finish_commit(struct commit *commit);
+static inline void finish_object__ma(struct object *obj)
+{
+       /*
+        * Whether or not we try to dynamically fetch missing objects
+        * from the server, we currently DO NOT have the object.  We
+        * can either print, allow (ignore), or conditionally allow
+        * (ignore) them.
+        */
+       switch (arg_missing_action) {
+       case MA_ERROR:
+               die("missing %s object '%s'",
+                   type_name(obj->type), oid_to_hex(&obj->oid));
+               return;
+
+       case MA_ALLOW_ANY:
+               return;
+
+       case MA_PRINT:
+               oidset_insert(&missing_objects, &obj->oid);
+               return;
+
+       case MA_ALLOW_PROMISOR:
+               if (is_promisor_object(&obj->oid))
+                       return;
+               die("unexpected missing %s object '%s'",
+                   type_name(obj->type), oid_to_hex(&obj->oid));
+               return;
+
+       default:
+               BUG("unhandled missing_action");
+               return;
+       }
+}
+
+static void finish_commit(struct commit *commit)
+{
+       free_commit_list(commit->parents);
+       commit->parents = NULL;
+       free_commit_buffer(the_repository->parsed_objects,
+                          commit);
+}
+
 static void show_commit(struct commit *commit, void *data)
 {
        struct rev_list_info *info = data;
@@ -102,6 +147,12 @@ static void show_commit(struct commit *commit, void *data)
 
        display_progress(progress, ++progress_counter);
 
+       if (revs->do_not_die_on_missing_objects &&
+           oidset_contains(&revs->missing_commits, &commit->object.oid)) {
+               finish_object__ma(&commit->object);
+               return;
+       }
+
        if (show_disk_usage)
                total_disk_usage += get_object_disk_usage(&commit->object);
 
@@ -132,7 +183,7 @@ static void show_commit(struct commit *commit, void *data)
                if (!revs->graph)
                        fputs(get_revision_mark(revs, commit), stdout);
                if (revs->abbrev_commit && revs->abbrev)
-                       fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
+                       fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid, revs->abbrev),
                              stdout);
                else
                        fputs(oid_to_hex(&commit->object.oid), stdout);
@@ -213,49 +264,8 @@ static void show_commit(struct commit *commit, void *data)
        finish_commit(commit);
 }
 
-static void finish_commit(struct commit *commit)
-{
-       free_commit_list(commit->parents);
-       commit->parents = NULL;
-       free_commit_buffer(the_repository->parsed_objects,
-                          commit);
-}
-
-static inline void finish_object__ma(struct object *obj)
-{
-       /*
-        * Whether or not we try to dynamically fetch missing objects
-        * from the server, we currently DO NOT have the object.  We
-        * can either print, allow (ignore), or conditionally allow
-        * (ignore) them.
-        */
-       switch (arg_missing_action) {
-       case MA_ERROR:
-               die("missing %s object '%s'",
-                   type_name(obj->type), oid_to_hex(&obj->oid));
-               return;
-
-       case MA_ALLOW_ANY:
-               return;
-
-       case MA_PRINT:
-               oidset_insert(&missing_objects, &obj->oid);
-               return;
-
-       case MA_ALLOW_PROMISOR:
-               if (is_promisor_object(&obj->oid))
-                       return;
-               die("unexpected missing %s object '%s'",
-                   type_name(obj->type), oid_to_hex(&obj->oid));
-               return;
-
-       default:
-               BUG("unhandled missing_action");
-               return;
-       }
-}
-
-static int finish_object(struct object *obj, const char *name, void *cb_data)
+static int finish_object(struct object *obj, const char *name UNUSED,
+                        void *cb_data)
 {
        struct rev_list_info *info = cb_data;
        if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) {
@@ -360,11 +370,11 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
 
 static int show_object_fast(
        const struct object_id *oid,
-       enum object_type type,
-       int exclude,
-       uint32_t name_hash,
-       struct packed_git *found_pack,
-       off_t found_offset)
+       enum object_type type UNUSED,
+       int exclude UNUSED,
+       uint32_t name_hash UNUSED,
+       struct packed_git *found_pack UNUSED,
+       off_t found_offset UNUSED)
 {
        fprintf(stdout, "%s\n", oid_to_hex(oid));
        return 1;
@@ -554,7 +564,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        }
 
        if (arg_missing_action)
-               revs.do_not_die_on_missing_tree = 1;
+               revs.do_not_die_on_missing_objects = 1;
 
        argc = setup_revisions(argc, argv, &revs, &s_r_opt);
 
index 8f61050bde884303fe1ac0fcb8162a99224bf5fd..d08987646a0a533ad81480ae4667b577a335f9f7 100644 (file)
@@ -3,16 +3,24 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
+#include "abspath.h"
 #include "config.h"
 #include "commit.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "refs.h"
 #include "quote.h"
-#include "builtin.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
 #include "diff.h"
+#include "read-cache-ll.h"
 #include "revision.h"
+#include "setup.h"
 #include "split-index.h"
 #include "submodule.h"
 #include "commit-reach.h"
@@ -39,7 +47,7 @@ static int abbrev_ref_strict;
 static int output_sq;
 
 static int stuck_long;
-static struct string_list *ref_excludes;
+static struct ref_exclusions ref_excludes = REF_EXCLUSIONS_INIT;
 
 /*
  * Some arguments are relevant "revision" arguments,
@@ -136,7 +144,9 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
                        struct object_id discard;
                        char *full;
 
-                       switch (dwim_ref(name, strlen(name), &discard, &full, 0)) {
+                       switch (repo_dwim_ref(the_repository, name,
+                                             strlen(name), &discard, &full,
+                                             0)) {
                        case 0:
                                /*
                                 * Not found -- not a ref.  We could
@@ -147,9 +157,12 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
                                 */
                                break;
                        case 1: /* happy */
-                               if (abbrev_ref)
+                               if (abbrev_ref) {
+                                       char *old = full;
                                        full = shorten_unambiguous_ref(full,
                                                abbrev_ref_strict);
+                                       free(old);
+                               }
                                show_with_type(type, full);
                                break;
                        default: /* ambiguous */
@@ -162,7 +175,8 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
                }
        }
        else if (abbrev)
-               show_with_type(type, find_unique_abbrev(oid, abbrev));
+               show_with_type(type,
+                              repo_find_unique_abbrev(the_repository, oid, abbrev));
        else
                show_with_type(type, oid_to_hex(oid));
 }
@@ -187,7 +201,7 @@ static int show_default(void)
                struct object_id oid;
 
                def = NULL;
-               if (!get_oid(s, &oid)) {
+               if (!repo_get_oid(the_repository, s, &oid)) {
                        show_rev(NORMAL, &oid, s);
                        return 1;
                }
@@ -198,7 +212,7 @@ static int show_default(void)
 static int show_reference(const char *refname, const struct object_id *oid,
                          int flag UNUSED, void *cb_data UNUSED)
 {
-       if (ref_excluded(ref_excludes, refname))
+       if (ref_excluded(&ref_excludes, refname))
                return 0;
        show_rev(NORMAL, oid, refname);
        return 0;
@@ -211,7 +225,7 @@ static int anti_reference(const char *refname, const struct object_id *oid,
        return 0;
 }
 
-static int show_abbrev(const struct object_id *oid, void *cb_data)
+static int show_abbrev(const struct object_id *oid, void *cb_data UNUSED)
 {
        show_rev(NORMAL, oid, NULL);
        return 0;
@@ -279,7 +293,7 @@ static int try_difference(const char *arg)
                return 0;
        }
 
-       if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) {
+       if (!repo_get_oid_committish(the_repository, start, &start_oid) && !repo_get_oid_committish(the_repository, end, &end_oid)) {
                show_rev(NORMAL, &end_oid, end);
                show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
                if (symmetric) {
@@ -291,7 +305,7 @@ static int try_difference(const char *arg)
                                *dotdot = '.';
                                return 0;
                        }
-                       exclude = get_merge_bases(a, b);
+                       exclude = repo_get_merge_bases(the_repository, a, b);
                        while (exclude) {
                                struct commit *commit = pop_commit(&exclude);
                                show_rev(REVERSED, &commit->object.oid, NULL);
@@ -337,7 +351,7 @@ static int try_parent_shorthands(const char *arg)
                return 0;
 
        *dotdot = 0;
-       if (get_oid_committish(arg, &oid) ||
+       if (repo_get_oid_committish(the_repository, arg, &oid) ||
            !(commit = lookup_commit_reference(the_repository, &oid))) {
                *dotdot = '^';
                return 0;
@@ -530,6 +544,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        strbuf_addstr(&parsed, " --");
        sq_quote_argv(&parsed, argv);
        puts(parsed.buf);
+       strbuf_release(&parsed);
        return 0;
 }
 
@@ -585,7 +600,7 @@ static void handle_ref_opt(const char *pattern, const char *prefix)
                for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
        else
                for_each_ref_in(prefix, show_reference, NULL);
-       clear_ref_exclusion(&ref_excludes);
+       clear_ref_exclusions(&ref_excludes);
 }
 
 enum format_type {
@@ -863,11 +878,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        }
                        if (!strcmp(arg, "--all")) {
                                for_each_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                               clear_ref_exclusions(&ref_excludes);
                                continue;
                        }
                        if (skip_prefix(arg, "--disambiguate=", &arg)) {
-                               for_each_abbrev(arg, show_abbrev, NULL);
+                               repo_for_each_abbrev(the_repository, arg,
+                                                    show_abbrev, NULL);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
@@ -876,10 +892,16 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (opt_with_value(arg, "--branches", &arg)) {
+                               if (ref_excludes.hidden_refs_configured)
+                                       return error(_("options '%s' and '%s' cannot be used together"),
+                                                    "--exclude-hidden", "--branches");
                                handle_ref_opt(arg, "refs/heads/");
                                continue;
                        }
                        if (opt_with_value(arg, "--tags", &arg)) {
+                               if (ref_excludes.hidden_refs_configured)
+                                       return error(_("options '%s' and '%s' cannot be used together"),
+                                                    "--exclude-hidden", "--tags");
                                handle_ref_opt(arg, "refs/tags/");
                                continue;
                        }
@@ -888,6 +910,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (opt_with_value(arg, "--remotes", &arg)) {
+                               if (ref_excludes.hidden_refs_configured)
+                                       return error(_("options '%s' and '%s' cannot be used together"),
+                                                    "--exclude-hidden", "--remotes");
                                handle_ref_opt(arg, "refs/remotes/");
                                continue;
                        }
@@ -895,6 +920,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                add_ref_exclusion(&ref_excludes, arg);
                                continue;
                        }
+                       if (skip_prefix(arg, "--exclude-hidden=", &arg)) {
+                               exclude_hidden_refs(&ref_excludes, arg);
+                               continue;
+                       }
                        if (!strcmp(arg, "--show-toplevel")) {
                                const char *work_tree = get_git_work_tree();
                                if (work_tree)
@@ -997,7 +1026,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--shared-index-path")) {
-                               if (read_cache() < 0)
+                               if (repo_read_index(the_repository) < 0)
                                        die(_("Could not read the index"));
                                if (the_index.split_index) {
                                        const struct object_id *oid = &the_index.split_index->base_oid;
@@ -1033,6 +1062,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                puts(the_hash_algo->name);
                                continue;
                        }
+                       if (!strcmp(arg, "--show-ref-format")) {
+                               puts(ref_storage_format_to_name(the_repository->ref_storage_format));
+                               continue;
+                       }
                        if (!strcmp(arg, "--end-of-options")) {
                                seen_end_of_options = 1;
                                if (filter & (DO_FLAGS | DO_REVS))
index ee2a0807f011e817801f1dc45e858b99ced613a3..89821bab95745a5fca294d00811804799ecd323d 100644 (file)
@@ -1,11 +1,11 @@
-#include "cache.h"
-#include "config.h"
+#include "git-compat-util.h"
 #include "builtin.h"
 #include "parse-options.h"
 #include "diff.h"
+#include "gettext.h"
+#include "repository.h"
 #include "revision.h"
 #include "rerere.h"
-#include "dir.h"
 #include "sequencer.h"
 #include "branch.h"
 
  */
 
 static const char * const revert_usage[] = {
-       N_("git revert [<options>] <commit-ish>..."),
-       N_("git revert <subcommand>"),
+       N_("git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] <commit>..."),
+       N_("git revert (--continue | --skip | --abort | --quit)"),
        NULL
 };
 
 static const char * const cherry_pick_usage[] = {
-       N_("git cherry-pick [<options>] <commit-ish>..."),
-       N_("git cherry-pick <subcommand>"),
+       N_("git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+          "                [-S[<keyid>]] <commit>..."),
+       N_("git cherry-pick (--continue | --skip | --abort | --quit)"),
        NULL
 };
 
@@ -42,20 +43,6 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
        return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
 }
 
-static int option_parse_x(const struct option *opt,
-                         const char *arg, int unset)
-{
-       struct replay_opts **opts_ptr = opt->value;
-       struct replay_opts *opts = *opts_ptr;
-
-       if (unset)
-               return 0;
-
-       ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
-       opts->xopts[opts->xopts_nr++] = xstrdup(arg);
-       return 0;
-}
-
 static int option_parse_m(const struct option *opt,
                          const char *arg, int unset)
 {
@@ -92,7 +79,8 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
                die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
 }
 
-static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
+static int run_sequencer(int argc, const char **argv, const char *prefix,
+                        struct replay_opts *opts)
 {
        const char * const * usage_str = revert_or_cherry_pick_usage(opts);
        const char *me = action_name(opts);
@@ -112,8 +100,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
                             N_("select mainline parent"), option_parse_m),
                OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
                OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
-               OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
-                       N_("option for merge strategy"), option_parse_x),
+               OPT_STRVEC('X', "strategy-option", &opts->xopts, N_("option"),
+                       N_("option for merge strategy")),
                { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
                  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                OPT_END()
@@ -139,7 +127,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
                options = parse_options_concat(options, cp_extra);
        }
 
-       argc = parse_options(argc, argv, NULL, options, usage_str,
+       argc = parse_options(argc, argv, prefix, options, usage_str,
                        PARSE_OPT_KEEP_ARGV0 |
                        PARSE_OPT_KEEP_UNKNOWN_OPT);
 
@@ -174,7 +162,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
                                "--signoff", opts->signoff,
                                "--mainline", opts->mainline,
                                "--strategy", opts->strategy ? 1 : 0,
-                               "--strategy-option", opts->xopts ? 1 : 0,
+                               "--strategy-option", opts->xopts.nr ? 1 : 0,
                                "-x", opts->record_origin,
                                "--ff", opts->allow_ff,
                                "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
@@ -220,6 +208,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
        opts->strategy = xstrdup_or_null(opts->strategy);
        if (!opts->strategy && getenv("GIT_TEST_MERGE_ALGORITHM"))
                opts->strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
+       free(options);
 
        if (cmd == 'q') {
                int ret = sequencer_remove_state(opts);
@@ -243,12 +232,10 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 
        opts.action = REPLAY_REVERT;
        sequencer_init_config(&opts);
-       res = run_sequencer(argc, argv, &opts);
+       res = run_sequencer(argc, argv, prefix, &opts);
        if (res < 0)
                die(_("revert failed"));
-       if (opts.revs)
-               release_revisions(opts.revs);
-       free(opts.revs);
+       replay_opts_release(&opts);
        return res;
 }
 
@@ -259,8 +246,9 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 
        opts.action = REPLAY_PICK;
        sequencer_init_config(&opts);
-       res = run_sequencer(argc, argv, &opts);
+       res = run_sequencer(argc, argv, prefix, &opts);
        if (res < 0)
                die(_("cherry-pick failed"));
+       replay_opts_release(&opts);
        return res;
 }
index b6ba859fe42571fd868697db1da8dac6ffd2c32e..fd130cea2d2592829e3abd991ca6caae4a119897 100644 (file)
@@ -3,21 +3,29 @@
  *
  * Copyright (C) Linus Torvalds 2006
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
 #include "lockfile.h"
 #include "dir.h"
-#include "cache-tree.h"
+#include "gettext.h"
+#include "hash.h"
 #include "tree-walk.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "read-cache.h"
+#include "repository.h"
 #include "string-list.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
 #include "pathspec.h"
 
 static const char * const builtin_rm_usage[] = {
-       N_("git rm [<options>] [--] <file>..."),
+       N_("git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+          "       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+          "       [--] [<pathspec>...]"),
        NULL
 };
 
@@ -33,8 +41,8 @@ static int get_ours_cache_pos(const char *path, int pos)
 {
        int i = -pos - 1;
 
-       while ((i < active_nr) && !strcmp(active_cache[i]->name, path)) {
-               if (ce_stage(active_cache[i]) == 2)
+       while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) {
+               if (ce_stage(the_index.cache[i]) == 2)
                        return i;
                i++;
        }
@@ -70,13 +78,13 @@ static void submodules_absorb_gitdir_if_needed(void)
                int pos;
                const struct cache_entry *ce;
 
-               pos = cache_name_pos(name, strlen(name));
+               pos = index_name_pos(&the_index, name, strlen(name));
                if (pos < 0) {
                        pos = get_ours_cache_pos(name, pos);
                        if (pos < 0)
                                continue;
                }
-               ce = active_cache[pos];
+               ce = the_index.cache[pos];
 
                if (!S_ISGITLINK(ce->ce_mode) ||
                    !file_exists(ce->name) ||
@@ -84,8 +92,7 @@ static void submodules_absorb_gitdir_if_needed(void)
                        continue;
 
                if (!submodule_uses_gitfile(name))
-                       absorb_git_dir_into_superproject(name,
-                               ABSORB_GITDIR_RECURSE_SUBMODULES);
+                       absorb_git_dir_into_superproject(name, NULL);
        }
 }
 
@@ -115,7 +122,7 @@ static int check_local_mod(struct object_id *head, int index_only)
                int local_changes = 0;
                int staged_changes = 0;
 
-               pos = cache_name_pos(name, strlen(name));
+               pos = index_name_pos(&the_index, name, strlen(name));
                if (pos < 0) {
                        /*
                         * Skip unmerged entries except for populated submodules
@@ -125,11 +132,11 @@ static int check_local_mod(struct object_id *head, int index_only)
                        if (pos < 0)
                                continue;
 
-                       if (!S_ISGITLINK(active_cache[pos]->ce_mode) ||
+                       if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) ||
                            is_empty_dir(name))
                                continue;
                }
-               ce = active_cache[pos];
+               ce = the_index.cache[pos];
 
                if (lstat(ce->name, &st) < 0) {
                        if (!is_missing_file_error(errno))
@@ -166,7 +173,7 @@ static int check_local_mod(struct object_id *head, int index_only)
                 * Is the index different from the file in the work tree?
                 * If it's a submodule, is its work tree modified?
                 */
-               if (ce_match_stat(ce, &st, 0) ||
+               if (ie_match_stat(&the_index, ce, &st, 0) ||
                    (S_ISGITLINK(ce->ce_mode) &&
                     bad_to_remove_submodule(ce->name,
                                SUBMODULE_REMOVAL_DIE_ON_ERROR |
@@ -289,9 +296,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                die(_("index file corrupt"));
 
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
@@ -301,8 +308,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        if (pathspec_needs_expanded_index(&the_index, &pathspec))
                ensure_full_index(&the_index);
 
-       for (i = 0; i < active_nr; i++) {
-               const struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < the_index.cache_nr; i++) {
+               const struct cache_entry *ce = the_index.cache[i];
 
                if (!include_sparse &&
                    (ce_skip_worktree(ce) ||
@@ -369,7 +376,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
         */
        if (!force) {
                struct object_id oid;
-               if (get_oid("HEAD", &oid))
+               if (repo_get_oid(the_repository, "HEAD", &oid))
                        oidclr(&oid);
                if (check_local_mod(&oid, index_only))
                        exit(1);
@@ -384,7 +391,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                if (!quiet)
                        printf("rm '%s'\n", path);
 
-               if (remove_file_from_cache(path))
+               if (remove_file_from_index(&the_index, path))
                        die(_("git rm: unable to remove %s"), path);
        }
 
index 7a750b18a7bd5db87ed69c09602c2af9ddd66ce1..3df9eaad092babdbbfc47f1c5441fc43a4ccd182 100644 (file)
@@ -1,25 +1,24 @@
 #include "builtin.h"
 #include "config.h"
-#include "commit.h"
-#include "refs.h"
+#include "hex.h"
 #include "pkt-line.h"
-#include "sideband.h"
 #include "run-command.h"
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
 #include "quote.h"
 #include "transport.h"
-#include "version.h"
 #include "oid-array.h"
-#include "gpg-interface.h"
 #include "gettext.h"
 #include "protocol.h"
+#include "parse-options.h"
+#include "write-or-die.h"
 
 static const char * const send_pack_usage[] = {
        N_("git send-pack [--mirror] [--dry-run] [--force]\n"
           "              [--receive-pack=<git-receive-pack>]\n"
           "              [--verbose] [--thin] [--atomic]\n"
+          "              [--[no-]signed | --signed=(true|false|if-asked)]\n"
           "              [<host>:]<directory> (--all | <ref>...)"),
        NULL,
 };
@@ -127,29 +126,25 @@ static void print_helper_status(struct ref *ref)
        strbuf_release(&buf);
 }
 
-static int send_pack_config(const char *k, const char *v, void *cb)
+static int send_pack_config(const char *k, const char *v,
+                           const struct config_context *ctx, void *cb)
 {
-       git_gpg_config(k, v, NULL);
-
        if (!strcmp(k, "push.gpgsign")) {
-               const char *value;
-               if (!git_config_get_value("push.gpgsign", &value)) {
-                       switch (git_parse_maybe_bool(value)) {
-                       case 0:
-                               args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
-                               break;
-                       case 1:
-                               args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
-                               break;
-                       default:
-                               if (value && !strcasecmp(value, "if-asked"))
-                                       args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
-                               else
-                                       return error(_("invalid value for '%s'"), k);
-                       }
+               switch (git_parse_maybe_bool(v)) {
+               case 0:
+                       args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
+                       break;
+               case 1:
+                       args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
+                       break;
+               default:
+                       if (!strcasecmp(v, "if-asked"))
+                               args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
+                       else
+                               return error(_("invalid value for '%s'"), k);
                }
        }
-       return git_default_config(k, v, cb);
+       return git_default_config(k, v, ctx, cb);
 }
 
 int cmd_send_pack(int argc, const char **argv, const char *prefix)
@@ -205,7 +200,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")),
                OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")),
                OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")),
-               OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+               OPT_CALLBACK_F(0, "force-with-lease", &cas, N_("<refname>:<expect>"),
                  N_("require old value of ref to be at this value"),
                  PARSE_OPT_OPTARG, parseopt_push_cas_option),
                OPT_BOOL(0, TRANS_OPT_FORCE_IF_INCLUDES, &force_if_includes,
@@ -274,7 +269,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                fd[0] = 0;
                fd[1] = 1;
        } else {
-               conn = git_connect(fd, dest, receivepack,
+               conn = git_connect(fd, dest, "git-receive-pack", receivepack,
                        args.verbose ? CONNECT_VERBOSE : 0);
        }
 
index 7a1e1fe7c0ed6d97789c352d37e4870d6f2e2b04..1307ed2b88a77dbb93d38528b8d39e2b87452d07 100644 (file)
@@ -1,12 +1,15 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
 #include "string-list.h"
+#include "repository.h"
 #include "revision.h"
 #include "utf8.h"
 #include "mailmap.h"
+#include "setup.h"
 #include "shortlog.h"
 #include "parse-options.h"
 #include "trailer.h"
@@ -132,7 +135,9 @@ static void read_from_stdin(struct shortlog *log)
                match = committer_match;
                break;
        case SHORTLOG_GROUP_TRAILER:
-               die(_("using --group=trailer with stdin is not supported"));
+               die(_("using %s with stdin is not supported"), "--group=trailer");
+       case SHORTLOG_GROUP_FORMAT:
+               die(_("using %s with stdin is not supported"), "--group=format");
        default:
                BUG("unhandled shortlog group");
        }
@@ -170,11 +175,15 @@ static void insert_records_from_trailers(struct shortlog *log,
        const char *commit_buffer, *body;
        struct strbuf ident = STRBUF_INIT;
 
+       if (!log->trailers.nr)
+               return;
+
        /*
-        * Using format_commit_message("%B") would be simpler here, but
+        * Using repo_format_commit_message("%B") would be simpler here, but
         * this saves us copying the message.
         */
-       commit_buffer = logmsg_reencode(commit, NULL, ctx->output_encoding);
+       commit_buffer = repo_logmsg_reencode(the_repository, commit, NULL,
+                                            ctx->output_encoding);
        body = strstr(commit_buffer, "\n\n");
        if (!body)
                return;
@@ -197,12 +206,38 @@ static void insert_records_from_trailers(struct shortlog *log,
        trailer_iterator_release(&iter);
 
        strbuf_release(&ident);
-       unuse_commit_buffer(commit, commit_buffer);
+       repo_unuse_commit_buffer(the_repository, commit, commit_buffer);
+}
+
+static int shortlog_needs_dedup(const struct shortlog *log)
+{
+       return HAS_MULTI_BITS(log->groups) || log->format.nr > 1 || log->trailers.nr;
+}
+
+static void insert_records_from_format(struct shortlog *log,
+                                      struct strset *dups,
+                                      struct commit *commit,
+                                      struct pretty_print_context *ctx,
+                                      const char *oneline)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct string_list_item *item;
+
+       for_each_string_list_item(item, &log->format) {
+               strbuf_reset(&buf);
+
+               repo_format_commit_message(the_repository, commit,
+                                          item->string, &buf, ctx);
+
+               if (!shortlog_needs_dedup(log) || strset_add(dups, buf.buf))
+                       insert_one_record(log, buf.buf, oneline);
+       }
+
+       strbuf_release(&buf);
 }
 
 void shortlog_add_commit(struct shortlog *log, struct commit *commit)
 {
-       struct strbuf ident = STRBUF_INIT;
        struct strbuf oneline = STRBUF_INIT;
        struct strset dups = STRSET_INIT;
        struct pretty_print_context ctx = {0};
@@ -211,41 +246,22 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
        ctx.fmt = CMIT_FMT_USERFORMAT;
        ctx.abbrev = log->abbrev;
        ctx.print_email_subject = 1;
-       ctx.date_mode.type = DATE_NORMAL;
+       ctx.date_mode = log->date_mode;
        ctx.output_encoding = get_log_output_encoding();
 
        if (!log->summary) {
                if (log->user_format)
                        pretty_print_commit(&ctx, commit, &oneline);
                else
-                       format_commit_message(commit, "%s", &oneline, &ctx);
+                       repo_format_commit_message(the_repository, commit,
+                                                  "%s", &oneline, &ctx);
        }
        oneline_str = oneline.len ? oneline.buf : "<none>";
 
-       if (log->groups & SHORTLOG_GROUP_AUTHOR) {
-               strbuf_reset(&ident);
-               format_commit_message(commit,
-                                     log->email ? "%aN <%aE>" : "%aN",
-                                     &ident, &ctx);
-               if (!HAS_MULTI_BITS(log->groups) ||
-                   strset_add(&dups, ident.buf))
-                       insert_one_record(log, ident.buf, oneline_str);
-       }
-       if (log->groups & SHORTLOG_GROUP_COMMITTER) {
-               strbuf_reset(&ident);
-               format_commit_message(commit,
-                                     log->email ? "%cN <%cE>" : "%cN",
-                                     &ident, &ctx);
-               if (!HAS_MULTI_BITS(log->groups) ||
-                   strset_add(&dups, ident.buf))
-                       insert_one_record(log, ident.buf, oneline_str);
-       }
-       if (log->groups & SHORTLOG_GROUP_TRAILER) {
-               insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str);
-       }
+       insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str);
+       insert_records_from_format(log, &dups, commit, &ctx, oneline_str);
 
        strset_clear(&dups);
-       strbuf_release(&ident);
        strbuf_release(&oneline);
 }
 
@@ -314,6 +330,7 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns
        if (unset) {
                log->groups = 0;
                string_list_clear(&log->trailers, 0);
+               string_list_clear(&log->format, 0);
        } else if (!strcasecmp(arg, "author"))
                log->groups |= SHORTLOG_GROUP_AUTHOR;
        else if (!strcasecmp(arg, "committer"))
@@ -321,8 +338,15 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns
        else if (skip_prefix(arg, "trailer:", &field)) {
                log->groups |= SHORTLOG_GROUP_TRAILER;
                string_list_append(&log->trailers, field);
-       } else
+       } else if (skip_prefix(arg, "format:", &field)) {
+               log->groups |= SHORTLOG_GROUP_FORMAT;
+               string_list_append(&log->format, field);
+       } else if (strchr(arg, '%')) {
+               log->groups |= SHORTLOG_GROUP_FORMAT;
+               string_list_append(&log->format, arg);
+       } else {
                return error(_("unknown group type: %s"), arg);
+       }
 
        return 0;
 }
@@ -340,6 +364,19 @@ void shortlog_init(struct shortlog *log)
        log->in2 = DEFAULT_INDENT2;
        log->trailers.strdup_strings = 1;
        log->trailers.cmp = strcasecmp;
+       log->format.strdup_strings = 1;
+}
+
+void shortlog_finish_setup(struct shortlog *log)
+{
+       if (log->groups & SHORTLOG_GROUP_AUTHOR)
+               string_list_append(&log->format,
+                                  log->email ? "%aN <%aE>" : "%aN");
+       if (log->groups & SHORTLOG_GROUP_COMMITTER)
+               string_list_append(&log->format,
+                                  log->email ? "%cN <%cE>" : "%cN");
+
+       string_list_sort(&log->trailers);
 }
 
 int cmd_shortlog(int argc, const char **argv, const char *prefix)
@@ -407,10 +444,11 @@ parse_done:
        log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
        log.abbrev = rev.abbrev;
        log.file = rev.diffopt.file;
+       log.date_mode = rev.date_mode;
 
        if (!log.groups)
                log.groups = SHORTLOG_GROUP_AUTHOR;
-       string_list_sort(&log.trailers);
+       shortlog_finish_setup(&log);
 
        /* assume HEAD if from a tty */
        if (!nongit && !rev.pending.nr && isatty(0))
@@ -479,4 +517,5 @@ void shortlog_output(struct shortlog *log)
        log->list.strdup_strings = 1;
        string_list_clear(&log->list, 1);
        clear_mailmap(&log->mailmap);
+       string_list_clear(&log->format, 0);
 }
index d3f5715e3e3af468fa7b1b444454ef30f6313f0c..b01ec761d2596c6a524994b99e1ef0aa719735d2 100644 (file)
@@ -1,20 +1,27 @@
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "pretty.h"
 #include "refs.h"
-#include "builtin.h"
 #include "color.h"
 #include "strvec.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "dir.h"
 #include "commit-slab.h"
 #include "date.h"
+#include "wildmatch.h"
 
 static const char* show_branch_usage[] = {
     N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
        "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
        "                [--more=<n> | --list | --independent | --merge-base]\n"
-       "                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
+       "                [--no-name | --sha1-name] [--topics]\n"
+       "                [(<rev> | <glob>)...]"),
     N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"),
     NULL
 };
@@ -239,7 +246,7 @@ static void join_revs(struct commit_list **list_p,
                        parents = parents->next;
                        if ((this_flag & flags) == flags)
                                continue;
-                       parse_commit(p);
+                       repo_parse_commit(the_repository, p);
                        if (mark_seen(p, seen_p) && !still_interesting)
                                extra--;
                        p->object.flags |= flags;
@@ -311,8 +318,8 @@ static void show_one_commit(struct commit *commit, int no_name)
                }
                else
                        printf("[%s] ",
-                              find_unique_abbrev(&commit->object.oid,
-                                                 DEFAULT_ABBREV));
+                              repo_find_unique_abbrev(the_repository, &commit->object.oid,
+                                                      DEFAULT_ABBREV));
        }
        puts(pretty_str);
        strbuf_release(&pretty);
@@ -413,7 +420,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid,
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
         */
-       if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid))
+       if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
                ofs = 5;
        return append_ref(refname + ofs, oid, 0);
 }
@@ -428,7 +435,7 @@ static int append_remote_ref(const char *refname, const struct object_id *oid,
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
         */
-       if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid))
+       if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
                ofs = 5;
        return append_ref(refname + ofs, oid, 0);
 }
@@ -532,7 +539,7 @@ static int show_independent(struct commit **rev,
 static void append_one_rev(const char *av)
 {
        struct object_id revkey;
-       if (!get_oid(av, &revkey)) {
+       if (!repo_get_oid(the_repository, av, &revkey)) {
                append_ref(av, &revkey, 0);
                return;
        }
@@ -552,7 +559,8 @@ static void append_one_rev(const char *av)
        die("bad sha1 reference %s", av);
 }
 
-static int git_show_branch_config(const char *var, const char *value, void *cb)
+static int git_show_branch_config(const char *var, const char *value,
+                                 const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "showbranch.default")) {
                if (!value)
@@ -572,7 +580,10 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       return git_color_default_config(var, value, cb);
+       if (git_color_config(var, value, cb) < 0)
+               return -1;
+
+       return git_default_config(var, value, ctx, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
@@ -638,7 +649,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        int with_current_branch = 0;
        int head_at = -1;
        int topics = 0;
-       int dense = 1;
+       int sparse = 0;
        const char *reflog_base = NULL;
        struct option builtin_show_branch_options[] = {
                OPT_BOOL('a', "all", &all_heads,
@@ -660,17 +671,17 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                         N_("show possible merge bases")),
                OPT_BOOL(0, "independent", &independent,
                            N_("show refs unreachable from any other ref")),
-               OPT_SET_INT(0, "topo-order", &sort_order,
-                           N_("show commits in topological order"),
-                           REV_SORT_IN_GRAPH_ORDER),
+               OPT_SET_INT_F(0, "topo-order", &sort_order,
+                             N_("show commits in topological order"),
+                             REV_SORT_IN_GRAPH_ORDER, PARSE_OPT_NONEG),
                OPT_BOOL(0, "topics", &topics,
                         N_("show only commits not on the first branch")),
-               OPT_SET_INT(0, "sparse", &dense,
-                           N_("show merges reachable from only one tip"), 0),
-               OPT_SET_INT(0, "date-order", &sort_order,
-                           N_("topologically sort, maintaining date order "
-                              "where possible"),
-                           REV_SORT_BY_COMMIT_DATE),
+               OPT_SET_INT(0, "sparse", &sparse,
+                           N_("show merges reachable from only one tip"), 1),
+               OPT_SET_INT_F(0, "date-order", &sort_order,
+                             N_("topologically sort, maintaining date order "
+                                "where possible"),
+                             REV_SORT_BY_COMMIT_DATE, PARSE_OPT_NONEG),
                OPT_CALLBACK_F('g', "reflog", &reflog_base, N_("<n>[,<base>]"),
                            N_("show <n> most recent ref-log entries starting at "
                               "base"),
@@ -745,7 +756,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        die(Q_("only %d entry can be shown at one time.",
                               "only %d entries can be shown at one time.",
                               MAX_REVS), MAX_REVS);
-               if (!dwim_ref(*av, strlen(*av), &oid, &ref, 0))
+               if (!repo_dwim_ref(the_repository, *av, strlen(*av), &oid,
+                                  &ref, 0))
                        die(_("no such ref %s"), *av);
 
                /* Has the base been specified? */
@@ -835,13 +847,13 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        die(Q_("cannot handle more than %d rev.",
                               "cannot handle more than %d revs.",
                               MAX_REVS), MAX_REVS);
-               if (get_oid(ref_name[num_rev], &revkey))
+               if (repo_get_oid(the_repository, ref_name[num_rev], &revkey))
                        die(_("'%s' is not a valid ref."), ref_name[num_rev]);
                commit = lookup_commit_reference(the_repository, &revkey);
                if (!commit)
                        die(_("cannot find commit %s (%s)"),
                            ref_name[num_rev], oid_to_hex(&revkey));
-               parse_commit(commit);
+               repo_parse_commit(the_repository, commit);
                mark_seen(commit, &seen);
 
                /* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
@@ -928,7 +940,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                            !is_merge_point &&
                            (this_flag & (1u << REV_SHIFT)))
                                continue;
-                       if (dense && is_merge &&
+                       if (!sparse && is_merge &&
                            omit_in_dense(commit, rev, num_rev))
                                continue;
                        for (i = 0; i < num_rev; i++) {
@@ -955,5 +967,6 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                if (shown_merge_point && --extra < 0)
                        break;
        }
+       free(head);
        return 0;
 }
index 0e0b9fb95bc11395fecb3718cbe85fd7a0bea903..540dc3dad1c1e3e4308c5058151dfb1dcaee6d21 100644 (file)
@@ -1,7 +1,10 @@
 #include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "pack.h"
 #include "parse-options.h"
+#include "repository.h"
 
 static const char *const show_index_usage[] = {
        "git show-index [--object-format=<hash-algorithm>]",
index 48569061087416ee0cc78f777094e8b1ed0660db..aaa2c39b2f636c6a031dc98f237c83d59b754f31 100644 (file)
@@ -1,60 +1,79 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
-#include "refs.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "refs/refs-internal.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "object.h"
-#include "tag.h"
 #include "string-list.h"
 #include "parse-options.h"
 
 static const char * const show_ref_usage[] = {
-       N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"),
+       N_("git show-ref [--head] [-d | --dereference]\n"
+          "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+          "             [--heads] [--] [<pattern>...]"),
+       N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+          "             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+          "             [--] [<ref>...]"),
        N_("git show-ref --exclude-existing[=<pattern>]"),
+       N_("git show-ref --exists <ref>"),
        NULL
 };
 
-static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
-          quiet, hash_only, abbrev, exclude_arg;
-static const char **pattern;
-static const char *exclude_existing_arg;
+struct show_one_options {
+       int quiet;
+       int hash_only;
+       int abbrev;
+       int deref_tags;
+};
 
-static void show_one(const char *refname, const struct object_id *oid)
+static void show_one(const struct show_one_options *opts,
+                    const char *refname, const struct object_id *oid)
 {
        const char *hex;
        struct object_id peeled;
 
-       if (!has_object_file(oid))
+       if (!repo_has_object_file(the_repository, oid))
                die("git show-ref: bad ref %s (%s)", refname,
                    oid_to_hex(oid));
 
-       if (quiet)
+       if (opts->quiet)
                return;
 
-       hex = find_unique_abbrev(oid, abbrev);
-       if (hash_only)
+       hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
+       if (opts->hash_only)
                printf("%s\n", hex);
        else
                printf("%s %s\n", hex, refname);
 
-       if (!deref_tags)
+       if (!opts->deref_tags)
                return;
 
        if (!peel_iterated_oid(oid, &peeled)) {
-               hex = find_unique_abbrev(&peeled, abbrev);
+               hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
                printf("%s %s^{}\n", hex, refname);
        }
 }
 
+struct show_ref_data {
+       const struct show_one_options *show_one_opts;
+       const char **patterns;
+       int found_match;
+       int show_head;
+};
+
 static int show_ref(const char *refname, const struct object_id *oid,
-                   int flag UNUSED, void *cbdata UNUSED)
+                   int flag UNUSED, void *cbdata)
 {
-       if (show_head && !strcmp(refname, "HEAD"))
+       struct show_ref_data *data = cbdata;
+
+       if (data->show_head && !strcmp(refname, "HEAD"))
                goto match;
 
-       if (pattern) {
+       if (data->patterns) {
                int reflen = strlen(refname);
-               const char **p = pattern, *m;
+               const char **p = data->patterns, *m;
                while ((m = *p++) != NULL) {
                        int len = strlen(m);
                        if (len > reflen)
@@ -70,9 +89,9 @@ static int show_ref(const char *refname, const struct object_id *oid,
        }
 
 match:
-       found_match++;
+       data->found_match++;
 
-       show_one(refname, oid);
+       show_one(data->show_one_opts, refname, oid);
 
        return 0;
 }
@@ -86,6 +105,15 @@ static int add_existing(const char *refname,
        return 0;
 }
 
+struct exclude_existing_options {
+       /*
+        * We need an explicit `enabled` field because it is perfectly valid
+        * for `pattern` to be `NULL` even if `--exclude-existing` was given.
+        */
+       int enabled;
+       const char *pattern;
+};
+
 /*
  * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
  * and
@@ -95,11 +123,11 @@ static int add_existing(const char *refname,
  * (4) ignore if refname is a ref that exists in the local repository;
  * (5) otherwise output the line.
  */
-static int exclude_existing(const char *match)
+static int cmd_show_ref__exclude_existing(const struct exclude_existing_options *opts)
 {
-       static struct string_list existing_refs = STRING_LIST_INIT_DUP;
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
        char buf[1024];
-       int matchlen = match ? strlen(match) : 0;
+       int patternlen = opts->pattern ? strlen(opts->pattern) : 0;
 
        for_each_ref(add_existing, &existing_refs);
        while (fgets(buf, sizeof(buf), stdin)) {
@@ -115,11 +143,11 @@ static int exclude_existing(const char *match)
                for (ref = buf + len; buf < ref; ref--)
                        if (isspace(ref[-1]))
                                break;
-               if (match) {
+               if (opts->pattern) {
                        int reflen = buf + len - ref;
-                       if (reflen < matchlen)
+                       if (reflen < patternlen)
                                continue;
-                       if (strncmp(ref, match, matchlen))
+                       if (strncmp(ref, opts->pattern, patternlen))
                                continue;
                }
                if (check_refname_format(ref, 0)) {
@@ -130,97 +158,172 @@ static int exclude_existing(const char *match)
                        printf("%s\n", buf);
                }
        }
+
+       string_list_clear(&existing_refs, 0);
+       return 0;
+}
+
+static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
+                               const char **refs)
+{
+       if (!refs || !*refs)
+               die("--verify requires a reference");
+
+       while (*refs) {
+               struct object_id oid;
+
+               if ((starts_with(*refs, "refs/") || !strcmp(*refs, "HEAD")) &&
+                   !read_ref(*refs, &oid)) {
+                       show_one(show_one_opts, *refs, &oid);
+               }
+               else if (!show_one_opts->quiet)
+                       die("'%s' - not a valid ref", *refs);
+               else
+                       return 1;
+               refs++;
+       }
+
        return 0;
 }
 
+struct patterns_options {
+       int show_head;
+       int heads_only;
+       int tags_only;
+};
+
+static int cmd_show_ref__patterns(const struct patterns_options *opts,
+                                 const struct show_one_options *show_one_opts,
+                                 const char **patterns)
+{
+       struct show_ref_data show_ref_data = {
+               .show_one_opts = show_one_opts,
+               .show_head = opts->show_head,
+       };
+
+       if (patterns && *patterns)
+               show_ref_data.patterns = patterns;
+
+       if (opts->show_head)
+               head_ref(show_ref, &show_ref_data);
+       if (opts->heads_only || opts->tags_only) {
+               if (opts->heads_only)
+                       for_each_fullref_in("refs/heads/", show_ref, &show_ref_data);
+               if (opts->tags_only)
+                       for_each_fullref_in("refs/tags/", show_ref, &show_ref_data);
+       } else {
+               for_each_ref(show_ref, &show_ref_data);
+       }
+       if (!show_ref_data.found_match)
+               return 1;
+
+       return 0;
+}
+
+static int cmd_show_ref__exists(const char **refs)
+{
+       struct strbuf unused_referent = STRBUF_INIT;
+       struct object_id unused_oid;
+       unsigned int unused_type;
+       int failure_errno = 0;
+       const char *ref;
+       int ret = 0;
+
+       if (!refs || !*refs)
+               die("--exists requires a reference");
+       ref = *refs++;
+       if (*refs)
+               die("--exists requires exactly one reference");
+
+       if (refs_read_raw_ref(get_main_ref_store(the_repository), ref,
+                             &unused_oid, &unused_referent, &unused_type,
+                             &failure_errno)) {
+               if (failure_errno == ENOENT) {
+                       error(_("reference does not exist"));
+                       ret = 2;
+               } else {
+                       errno = failure_errno;
+                       error_errno(_("failed to look up reference"));
+                       ret = 1;
+               }
+
+               goto out;
+       }
+
+out:
+       strbuf_release(&unused_referent);
+       return ret;
+}
+
 static int hash_callback(const struct option *opt, const char *arg, int unset)
 {
-       hash_only = 1;
+       struct show_one_options *opts = opt->value;
+       struct option abbrev_opt = *opt;
+
+       opts->hash_only = 1;
        /* Use full length SHA1 if no argument */
        if (!arg)
                return 0;
-       return parse_opt_abbrev_cb(opt, arg, unset);
+
+       abbrev_opt.value = &opts->abbrev;
+       return parse_opt_abbrev_cb(&abbrev_opt, arg, unset);
 }
 
 static int exclude_existing_callback(const struct option *opt, const char *arg,
                                     int unset)
 {
+       struct exclude_existing_options *opts = opt->value;
        BUG_ON_OPT_NEG(unset);
-       exclude_arg = 1;
-       *(const char **)opt->value = arg;
+       opts->enabled = 1;
+       opts->pattern = arg;
        return 0;
 }
 
-static const struct option show_ref_options[] = {
-       OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")),
-       OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")),
-       OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
-                   "requires exact ref path")),
-       OPT_HIDDEN_BOOL('h', NULL, &show_head,
-                       N_("show the HEAD reference, even if it would be filtered out")),
-       OPT_BOOL(0, "head", &show_head,
-         N_("show the HEAD reference, even if it would be filtered out")),
-       OPT_BOOL('d', "dereference", &deref_tags,
-                   N_("dereference tags into object IDs")),
-       OPT_CALLBACK_F('s', "hash", &abbrev, N_("n"),
-                      N_("only show SHA1 hash using <n> digits"),
-                      PARSE_OPT_OPTARG, &hash_callback),
-       OPT__ABBREV(&abbrev),
-       OPT__QUIET(&quiet,
-                  N_("do not print results to stdout (useful with --verify)")),
-       OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_arg,
-                      N_("pattern"), N_("show refs from stdin that aren't in local repository"),
-                      PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback),
-       OPT_END()
-};
-
 int cmd_show_ref(int argc, const char **argv, const char *prefix)
 {
+       struct exclude_existing_options exclude_existing_opts = {0};
+       struct patterns_options patterns_opts = {0};
+       struct show_one_options show_one_opts = {0};
+       int verify = 0, exists = 0;
+       const struct option show_ref_options[] = {
+               OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")),
+               OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")),
+               OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")),
+               OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
+                           "requires exact ref path")),
+               OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,
+                               N_("show the HEAD reference, even if it would be filtered out")),
+               OPT_BOOL(0, "head", &patterns_opts.show_head,
+                 N_("show the HEAD reference, even if it would be filtered out")),
+               OPT_BOOL('d', "dereference", &show_one_opts.deref_tags,
+                           N_("dereference tags into object IDs")),
+               OPT_CALLBACK_F('s', "hash", &show_one_opts, N_("n"),
+                              N_("only show SHA1 hash using <n> digits"),
+                              PARSE_OPT_OPTARG, &hash_callback),
+               OPT__ABBREV(&show_one_opts.abbrev),
+               OPT__QUIET(&show_one_opts.quiet,
+                          N_("do not print results to stdout (useful with --verify)")),
+               OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_opts,
+                              N_("pattern"), N_("show refs from stdin that aren't in local repository"),
+                              PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback),
+               OPT_END()
+       };
+
        git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, show_ref_options,
                             show_ref_usage, 0);
 
-       if (exclude_arg)
-               return exclude_existing(exclude_existing_arg);
-
-       pattern = argv;
-       if (!*pattern)
-               pattern = NULL;
-
-       if (verify) {
-               if (!pattern)
-                       die("--verify requires a reference");
-               while (*pattern) {
-                       struct object_id oid;
-
-                       if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) &&
-                           !read_ref(*pattern, &oid)) {
-                               show_one(*pattern, &oid);
-                       }
-                       else if (!quiet)
-                               die("'%s' - not a valid ref", *pattern);
-                       else
-                               return 1;
-                       pattern++;
-               }
-               return 0;
-       }
+       die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing",
+                                 verify, "--verify",
+                                 exists, "--exists");
 
-       if (show_head)
-               head_ref(show_ref, NULL);
-       if (heads_only || tags_only) {
-               if (heads_only)
-                       for_each_fullref_in("refs/heads/", show_ref, NULL);
-               if (tags_only)
-                       for_each_fullref_in("refs/tags/", show_ref, NULL);
-       } else {
-               for_each_ref(show_ref, NULL);
-       }
-       if (!found_match) {
-               if (verify && !quiet)
-                       die("No match");
-               return 1;
-       }
-       return 0;
+       if (exclude_existing_opts.enabled)
+               return cmd_show_ref__exclude_existing(&exclude_existing_opts);
+       else if (verify)
+               return cmd_show_ref__verify(&show_one_opts, argv);
+       else if (exists)
+               return cmd_show_ref__exists(argv);
+       else
+               return cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv);
 }
index 287716db68e4198bdec97517ff2cd9c053c9f0b8..0f52e252493d0d56fd0bcf5a7b9f92a416e86348 100644 (file)
@@ -1,26 +1,26 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-file.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "pathspec.h"
 #include "repository.h"
-#include "run-command.h"
 #include "strbuf.h"
 #include "string-list.h"
-#include "cache-tree.h"
 #include "lockfile.h"
-#include "resolve-undo.h"
 #include "unpack-trees.h"
-#include "wt-status.h"
 #include "quote.h"
+#include "setup.h"
 #include "sparse-index.h"
 #include "worktree.h"
 
 static const char *empty_base = "";
 
 static char const * const builtin_sparse_checkout_usage[] = {
-       N_("git sparse-checkout (init|list|set|add|reapply|disable) <options>"),
+       N_("git sparse-checkout (init | list | set | add | reapply | disable | check-rules) [<options>]"),
        NULL
 };
 
@@ -57,6 +57,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix)
        char *sparse_filename;
        int res;
 
+       setup_work_tree();
        if (!core_apply_sparse_checkout)
                die(_("this worktree is not sparse"));
 
@@ -218,14 +219,13 @@ static int update_working_directory(struct pattern_list *pl)
        o.src_index = r->index;
        o.dst_index = r->index;
        o.skip_sparse_checkout = 0;
-       o.pl = pl;
 
        setup_work_tree();
 
        repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
 
        setup_unpack_trees_porcelain(&o, "sparse-checkout");
-       result = update_sparsity(&o);
+       result = update_sparsity(&o, pl);
        clear_unpack_trees_porcelain(&o);
 
        if (result == UPDATE_SPARSITY_WARNINGS)
@@ -382,13 +382,7 @@ static int set_config(enum sparse_checkout_mode mode)
        return 0;
 }
 
-static int update_modes(int *cone_mode, int *sparse_index)
-{
-       int mode, record_mode;
-
-       /* Determine if we need to record the mode; ensure sparse checkout on */
-       record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
-
+static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
        /* If not specified, use previous definition of cone mode */
        if (*cone_mode == -1 && core_apply_sparse_checkout)
                *cone_mode = core_sparse_checkout_cone;
@@ -396,12 +390,21 @@ static int update_modes(int *cone_mode, int *sparse_index)
        /* Set cone/non-cone mode appropriately */
        core_apply_sparse_checkout = 1;
        if (*cone_mode == 1 || *cone_mode == -1) {
-               mode = MODE_CONE_PATTERNS;
                core_sparse_checkout_cone = 1;
-       } else {
-               mode = MODE_ALL_PATTERNS;
-               core_sparse_checkout_cone = 0;
+               return MODE_CONE_PATTERNS;
        }
+       core_sparse_checkout_cone = 0;
+       return MODE_ALL_PATTERNS;
+}
+
+static int update_modes(int *cone_mode, int *sparse_index)
+{
+       int mode, record_mode;
+
+       /* Determine if we need to record the mode; ensure sparse checkout on */
+       record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+
+       mode = update_cone_mode(cone_mode);
        if (record_mode && set_config(mode))
                return 1;
 
@@ -447,6 +450,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
+       setup_work_tree();
        repo_read_index(the_repository);
 
        init_opts.cone_mode = -1;
@@ -470,7 +474,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
                return update_working_directory(NULL);
        }
 
-       if (get_oid("HEAD", &oid)) {
+       if (repo_get_oid(the_repository, "HEAD", &oid)) {
                FILE *fp;
 
                /* assume we are in a fresh repo, but update the sparse-checkout file */
@@ -544,7 +548,7 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
 
 static void add_patterns_from_input(struct pattern_list *pl,
                                    int argc, const char **argv,
-                                   int use_stdin)
+                                   FILE *file)
 {
        int i;
        if (core_sparse_checkout_cone) {
@@ -554,9 +558,9 @@ static void add_patterns_from_input(struct pattern_list *pl,
                hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
                pl->use_cone_patterns = 1;
 
-               if (use_stdin) {
+               if (file) {
                        struct strbuf unquoted = STRBUF_INIT;
-                       while (!strbuf_getline(&line, stdin)) {
+                       while (!strbuf_getline(&line, file)) {
                                if (line.buf[0] == '"') {
                                        strbuf_reset(&unquoted);
                                        if (unquote_c_style(&unquoted, line.buf, NULL))
@@ -578,10 +582,10 @@ static void add_patterns_from_input(struct pattern_list *pl,
                        }
                }
        } else {
-               if (use_stdin) {
+               if (file) {
                        struct strbuf line = STRBUF_INIT;
 
-                       while (!strbuf_getline(&line, stdin)) {
+                       while (!strbuf_getline(&line, file)) {
                                size_t len;
                                char *buf = strbuf_detach(&line, &len);
                                add_pattern(buf, empty_base, 0, pl, 0);
@@ -608,7 +612,8 @@ static void add_patterns_cone_mode(int argc, const char **argv,
        struct pattern_list existing;
        char *sparse_filename = get_sparse_checkout_filename();
 
-       add_patterns_from_input(pl, argc, argv, use_stdin);
+       add_patterns_from_input(pl, argc, argv,
+                               use_stdin ? stdin : NULL);
 
        memset(&existing, 0, sizeof(existing));
        existing.use_cone_patterns = core_sparse_checkout_cone;
@@ -645,7 +650,7 @@ static void add_patterns_literal(int argc, const char **argv,
                                           pl, NULL, 0))
                die(_("unable to load existing sparse-checkout patterns"));
        free(sparse_filename);
-       add_patterns_from_input(pl, argc, argv, use_stdin);
+       add_patterns_from_input(pl, argc, argv, use_stdin ? stdin : NULL);
 }
 
 static int modify_pattern_list(int argc, const char **argv, int use_stdin,
@@ -664,7 +669,8 @@ static int modify_pattern_list(int argc, const char **argv, int use_stdin,
                break;
 
        case REPLACE:
-               add_patterns_from_input(pl, argc, argv, use_stdin);
+               add_patterns_from_input(pl, argc, argv,
+                                       use_stdin ? stdin : NULL);
                break;
        }
 
@@ -759,6 +765,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
+       setup_work_tree();
        if (!core_apply_sparse_checkout)
                die(_("no sparse-checkout to add to"));
 
@@ -766,8 +773,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix,
                             builtin_sparse_checkout_add_options,
-                            builtin_sparse_checkout_add_usage,
-                            PARSE_OPT_KEEP_UNKNOWN_OPT);
+                            builtin_sparse_checkout_add_usage, 0);
 
        sanitize_paths(argc, argv, prefix, add_opts.skip_checks);
 
@@ -805,6 +811,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
+       setup_work_tree();
        repo_read_index(the_repository);
 
        set_opts.cone_mode = -1;
@@ -812,8 +819,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix,
                             builtin_sparse_checkout_set_options,
-                            builtin_sparse_checkout_set_usage,
-                            PARSE_OPT_KEEP_UNKNOWN_OPT);
+                            builtin_sparse_checkout_set_usage, 0);
 
        if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
                return 1;
@@ -823,7 +829,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
         * non-cone mode, if nothing is specified, manually select just the
         * top-level directory (much as 'init' would do).
         */
-       if (!core_sparse_checkout_cone && argc == 0) {
+       if (!core_sparse_checkout_cone && !set_opts.use_stdin && argc == 0) {
                argv = default_patterns;
                argc = default_patterns_nr;
        } else {
@@ -854,6 +860,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
                OPT_END(),
        };
 
+       setup_work_tree();
        if (!core_apply_sparse_checkout)
                die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
@@ -897,6 +904,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
         * forcibly return to a dense checkout regardless of initial state.
         */
 
+       setup_work_tree();
        argc = parse_options(argc, argv, prefix,
                             builtin_sparse_checkout_disable_options,
                             builtin_sparse_checkout_disable_usage, 0);
@@ -922,6 +930,90 @@ static int sparse_checkout_disable(int argc, const char **argv,
        return set_config(MODE_NO_PATTERNS);
 }
 
+static char const * const builtin_sparse_checkout_check_rules_usage[] = {
+       N_("git sparse-checkout check-rules [-z] [--skip-checks]"
+          "[--[no-]cone] [--rules-file <file>]"),
+       NULL
+};
+
+static struct sparse_checkout_check_rules_opts {
+       int cone_mode;
+       int null_termination;
+       char *rules_file;
+} check_rules_opts;
+
+static int check_rules(struct pattern_list *pl, int null_terminated) {
+       struct strbuf line = STRBUF_INIT;
+       struct strbuf unquoted = STRBUF_INIT;
+       char *path;
+       int line_terminator = null_terminated ? 0 : '\n';
+       strbuf_getline_fn getline_fn = null_terminated ? strbuf_getline_nul
+               : strbuf_getline;
+       the_repository->index->sparse_checkout_patterns = pl;
+       while (!getline_fn(&line, stdin)) {
+               path = line.buf;
+               if (!null_terminated && line.buf[0] == '"') {
+                       strbuf_reset(&unquoted);
+                       if (unquote_c_style(&unquoted, line.buf, NULL))
+                               die(_("unable to unquote C-style string '%s'"),
+                                       line.buf);
+
+                       path = unquoted.buf;
+               }
+
+               if (path_in_sparse_checkout(path, the_repository->index))
+                       write_name_quoted(path, stdout, line_terminator);
+       }
+       strbuf_release(&line);
+       strbuf_release(&unquoted);
+
+       return 0;
+}
+
+static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix)
+{
+       static struct option builtin_sparse_checkout_check_rules_options[] = {
+               OPT_BOOL('z', NULL, &check_rules_opts.null_termination,
+                        N_("terminate input and output files by a NUL character")),
+               OPT_BOOL(0, "cone", &check_rules_opts.cone_mode,
+                        N_("when used with --rules-file interpret patterns as cone mode patterns")),
+               OPT_FILENAME(0, "rules-file", &check_rules_opts.rules_file,
+                        N_("use patterns in <file> instead of the current ones.")),
+               OPT_END(),
+       };
+
+       FILE *fp;
+       int ret;
+       struct pattern_list pl = {0};
+       char *sparse_filename;
+       check_rules_opts.cone_mode = -1;
+
+       argc = parse_options(argc, argv, prefix,
+                            builtin_sparse_checkout_check_rules_options,
+                            builtin_sparse_checkout_check_rules_usage, 0);
+
+       if (check_rules_opts.rules_file && check_rules_opts.cone_mode < 0)
+               check_rules_opts.cone_mode = 1;
+
+       update_cone_mode(&check_rules_opts.cone_mode);
+       pl.use_cone_patterns = core_sparse_checkout_cone;
+       if (check_rules_opts.rules_file) {
+               fp = xfopen(check_rules_opts.rules_file, "r");
+               add_patterns_from_input(&pl, argc, argv, fp);
+               fclose(fp);
+       } else {
+               sparse_filename = get_sparse_checkout_filename();
+               if (add_patterns_from_file_to_list(sparse_filename, "", 0, &pl,
+                                                  NULL, 0))
+                       die(_("unable to load existing sparse-checkout patterns"));
+               free(sparse_filename);
+       }
+
+       ret = check_rules(&pl, check_rules_opts.null_termination);
+       clear_pattern_list(&pl);
+       return ret;
+}
+
 int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
 {
        parse_opt_subcommand_fn *fn = NULL;
@@ -932,6 +1024,7 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
                OPT_SUBCOMMAND("add", &fn, sparse_checkout_add),
                OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply),
                OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable),
+               OPT_SUBCOMMAND("check-rules", &fn, sparse_checkout_check_rules),
                OPT_END(),
        };
 
index 2274aae2556cc1837a3c7036fb52be2e6653a0e7..b2813c614cb2cf084ab28b48c1099209865e36b7 100644 (file)
@@ -1,6 +1,12 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "refs.h"
 #include "lockfile.h"
 #include "run-command.h"
 #include "dir.h"
 #include "entry.h"
+#include "preload-index.h"
+#include "read-cache.h"
 #include "rerere.h"
 #include "revision.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "log-tree.h"
 #include "diffcore.h"
-#include "exec-cmd.h"
 #include "reflog.h"
+#include "add-interactive.h"
 
 #define INCLUDE_ALL_FILES 2
 
+#define BUILTIN_STASH_LIST_USAGE \
+       N_("git stash list [<log-options>]")
+#define BUILTIN_STASH_SHOW_USAGE \
+       N_("git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]")
+#define BUILTIN_STASH_DROP_USAGE \
+       N_("git stash drop [-q | --quiet] [<stash>]")
+#define BUILTIN_STASH_POP_USAGE \
+       N_("git stash pop [--index] [-q | --quiet] [<stash>]")
+#define BUILTIN_STASH_APPLY_USAGE \
+       N_("git stash apply [--index] [-q | --quiet] [<stash>]")
+#define BUILTIN_STASH_BRANCH_USAGE \
+       N_("git stash branch <branchname> [<stash>]")
+#define BUILTIN_STASH_STORE_USAGE \
+       N_("git stash store [(-m | --message) <message>] [-q | --quiet] <commit>")
+#define BUILTIN_STASH_PUSH_USAGE \
+       N_("git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
+          "          [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]\n" \
+          "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" \
+          "          [--] [<pathspec>...]]")
+#define BUILTIN_STASH_SAVE_USAGE \
+       N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
+          "          [-u | --include-untracked] [-a | --all] [<message>]")
+#define BUILTIN_STASH_CREATE_USAGE \
+       N_("git stash create [<message>]")
+#define BUILTIN_STASH_CLEAR_USAGE \
+       "git stash clear"
+
 static const char * const git_stash_usage[] = {
-       N_("git stash list [<options>]"),
-       N_("git stash show [<options>] [<stash>]"),
-       N_("git stash drop [-q|--quiet] [<stash>]"),
-       N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
-       N_("git stash branch <branchname> [<stash>]"),
-       "git stash clear",
-       N_("git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
-          "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-          "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-          "          [--] [<pathspec>...]]"),
-       N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
-          "          [-u|--include-untracked] [-a|--all] [<message>]"),
+       BUILTIN_STASH_LIST_USAGE,
+       BUILTIN_STASH_SHOW_USAGE,
+       BUILTIN_STASH_DROP_USAGE,
+       BUILTIN_STASH_POP_USAGE,
+       BUILTIN_STASH_APPLY_USAGE,
+       BUILTIN_STASH_BRANCH_USAGE,
+       BUILTIN_STASH_PUSH_USAGE,
+       BUILTIN_STASH_SAVE_USAGE,
+       BUILTIN_STASH_CLEAR_USAGE,
+       BUILTIN_STASH_CREATE_USAGE,
+       BUILTIN_STASH_STORE_USAGE,
        NULL
 };
 
 static const char * const git_stash_list_usage[] = {
-       N_("git stash list [<options>]"),
+       BUILTIN_STASH_LIST_USAGE,
        NULL
 };
 
 static const char * const git_stash_show_usage[] = {
-       N_("git stash show [<options>] [<stash>]"),
+       BUILTIN_STASH_SHOW_USAGE,
        NULL
 };
 
 static const char * const git_stash_drop_usage[] = {
-       N_("git stash drop [-q|--quiet] [<stash>]"),
+       BUILTIN_STASH_DROP_USAGE,
        NULL
 };
 
 static const char * const git_stash_pop_usage[] = {
-       N_("git stash pop [--index] [-q|--quiet] [<stash>]"),
+       BUILTIN_STASH_POP_USAGE,
        NULL
 };
 
 static const char * const git_stash_apply_usage[] = {
-       N_("git stash apply [--index] [-q|--quiet] [<stash>]"),
+       BUILTIN_STASH_APPLY_USAGE,
        NULL
 };
 
 static const char * const git_stash_branch_usage[] = {
-       N_("git stash branch <branchname> [<stash>]"),
+       BUILTIN_STASH_BRANCH_USAGE,
        NULL
 };
 
 static const char * const git_stash_clear_usage[] = {
-       "git stash clear",
+       BUILTIN_STASH_CLEAR_USAGE,
        NULL
 };
 
 static const char * const git_stash_store_usage[] = {
-       N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"),
+       BUILTIN_STASH_STORE_USAGE,
        NULL
 };
 
 static const char * const git_stash_push_usage[] = {
-       N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-          "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-          "          [--] [<pathspec>...]]"),
+       BUILTIN_STASH_PUSH_USAGE,
        NULL
 };
 
 static const char * const git_stash_save_usage[] = {
-       N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-          "               [-u|--include-untracked] [-a|--all] [<message>]"),
+       BUILTIN_STASH_SAVE_USAGE,
        NULL
 };
 
@@ -177,7 +210,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 
        revision = info->revision.buf;
 
-       if (get_oid(revision, &info->w_commit))
+       if (repo_get_oid(the_repository, revision, &info->w_commit))
                return error(_("%s is not a valid reference"), revision);
 
        assert_stash_like(info, revision);
@@ -187,7 +220,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
        end_of_rev = strchrnul(revision, '@');
        strbuf_add(&symbolic, revision, end_of_rev - revision);
 
-       ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref, 0);
+       ret = repo_dwim_ref(the_repository, symbolic.buf, symbolic.len,
+                           &dummy, &expanded_ref, 0);
        strbuf_release(&symbolic);
        switch (ret) {
        case 0: /* Not found, but valid ref */
@@ -207,7 +241,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 static int do_clear_stash(void)
 {
        struct object_id obj;
-       if (get_oid(ref_stash, &obj))
+       if (repo_get_oid(the_repository, ref_stash, &obj))
                return 0;
 
        return delete_ref(NULL, ref_stash, &obj, 0);
@@ -238,11 +272,11 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
        struct tree *tree;
        struct lock_file lock_file = LOCK_INIT;
 
-       read_cache_preload(NULL);
-       if (refresh_cache(REFRESH_QUIET))
+       repo_read_index_preload(the_repository, NULL, 0);
+       if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL))
                return -1;
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof(opts));
 
@@ -327,7 +361,7 @@ static int is_path_a_directory(const char *path)
 }
 
 static void add_diff_to_buf(struct diff_queue_struct *q,
-                           struct diff_options *options,
+                           struct diff_options *options UNUSED,
                            void *data)
 {
        int i;
@@ -403,7 +437,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
         * to the index before a merge was run) and the current index
         * (reflecting the changes brought in by the merge).
         */
-       diff_setup(&diff_opts);
+       repo_diff_setup(the_repository, &diff_opts);
        diff_opts.flags.recursive = 1;
        diff_opts.detect_rename = 0;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -431,10 +465,10 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
                 * path, but left it out of the working tree, then clear the
                 * SKIP_WORKTREE bit and write it to the working tree.
                 */
-               if (pos >= 0 && ce_skip_worktree(active_cache[pos])) {
+               if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) {
                        struct stat st;
 
-                       ce = active_cache[pos];
+                       ce = the_index.cache[pos];
                        if (!lstat(ce->name, &st)) {
                                /* Conflicting path present; relocate it */
                                struct strbuf new_path = STRBUF_INIT;
@@ -500,11 +534,13 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
        struct tree *head, *merge, *merge_base;
        struct lock_file lock = LOCK_INIT;
 
-       read_cache_preload(NULL);
-       if (refresh_and_write_cache(REFRESH_QUIET, 0, 0))
+       repo_read_index_preload(the_repository, NULL, 0);
+       if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
+                                        NULL, NULL, NULL))
                return -1;
 
-       if (write_cache_as_tree(&c_tree, 0, NULL))
+       if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+                               NULL))
                return error(_("cannot apply a stash in the middle of a merge"));
 
        if (index) {
@@ -526,14 +562,15 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
                                return error(_("conflicts in index. "
                                               "Try without --index."));
 
-                       discard_cache();
-                       read_cache();
-                       if (write_cache_as_tree(&index_tree, 0, NULL))
+                       discard_index(&the_index);
+                       repo_read_index(the_repository);
+                       if (write_index_as_tree(&index_tree, &the_index,
+                                               get_index_file(), 0, NULL))
                                return error(_("could not save index tree"));
 
                        reset_head();
-                       discard_cache();
-                       read_cache();
+                       discard_index(&the_index);
+                       repo_read_index(the_repository);
                }
        }
 
@@ -573,7 +610,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
                ret = error(_("could not write index"));
 
        if (ret) {
-               rerere(0);
+               repo_rerere(the_repository, 0);
 
                if (index)
                        fprintf_ln(stderr, _("Index was not unstashed."));
@@ -803,7 +840,8 @@ static int show_stat = 1;
 static int show_patch;
 static int show_include_untracked;
 
-static int git_stash_config(const char *var, const char *value, void *cb)
+static int git_stash_config(const char *var, const char *value,
+                           const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "stash.showstat")) {
                show_stat = git_config_bool(var, value);
@@ -817,7 +855,7 @@ static int git_stash_config(const char *var, const char *value, void *cb)
                show_include_untracked = git_config_bool(var, value);
                return 0;
        }
-       return git_diff_basic_config(var, value, cb);
+       return git_diff_basic_config(var, value, ctx, cb);
 }
 
 static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt)
@@ -873,7 +911,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 
        init_diff_ui_defaults();
        git_config(git_diff_ui_config, NULL);
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
 
        argc = parse_options(argc, argv, prefix, options, git_stash_show_usage,
                             PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
@@ -934,7 +972,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
        }
        log_tree_diff_flush(&rev);
 
-       ret = diff_result_code(&rev.diffopt, 0);
+       ret = diff_result_code(&rev.diffopt);
 cleanup:
        strvec_clear(&stash_args);
        free_stash_info(&info);
@@ -950,6 +988,12 @@ usage:
 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
                          int quiet)
 {
+       struct stash_info info;
+       char revision[GIT_MAX_HEXSZ];
+
+       oid_to_hex_r(revision, w_commit);
+       assert_stash_like(&info, revision);
+
        if (!stash_msg)
                stash_msg = "Created via \"git stash store\".";
 
@@ -1050,19 +1094,18 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked,
  */
 static int check_changes_tracked_files(const struct pathspec *ps)
 {
-       int result;
        struct rev_info rev;
        struct object_id dummy;
        int ret = 0;
 
        /* No initial commit. */
-       if (get_oid("HEAD", &dummy))
+       if (repo_get_oid(the_repository, "HEAD", &dummy))
                return -1;
 
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                return -1;
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        copy_pathspec(&rev.prune_data, ps);
 
        rev.diffopt.flags.quick = 1;
@@ -1072,14 +1115,14 @@ static int check_changes_tracked_files(const struct pathspec *ps)
        add_head_to_pending(&rev);
        diff_setup_done(&rev.diffopt);
 
-       result = run_diff_index(&rev, 1);
-       if (diff_result_code(&rev.diffopt, result)) {
+       run_diff_index(&rev, DIFF_INDEX_CACHED);
+       if (diff_result_code(&rev.diffopt)) {
                ret = 1;
                goto done;
        }
 
-       result = run_diff_files(&rev, 0);
-       if (diff_result_code(&rev.diffopt, result)) {
+       run_diff_files(&rev, 0);
+       if (diff_result_code(&rev.diffopt)) {
                ret = 1;
                goto done;
        }
@@ -1113,7 +1156,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
        int ret = 0;
        struct strbuf untracked_msg = STRBUF_INIT;
        struct child_process cp_upd_index = CHILD_PROCESS_INIT;
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
 
        cp_upd_index.git_cmd = 1;
        strvec_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
@@ -1141,7 +1184,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
        }
 
 done:
-       discard_index(&istate);
+       release_index(&istate);
        strbuf_release(&untracked_msg);
        remove_path(stash_index_path.buf);
        return ret;
@@ -1152,7 +1195,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
 {
        int ret = 0;
        struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
 
        if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
                                0, NULL)) {
@@ -1175,7 +1218,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
        }
 
 done:
-       discard_index(&istate);
+       release_index(&istate);
        return ret;
 }
 
@@ -1185,7 +1228,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
        int ret = 0;
        struct child_process cp_read_tree = CHILD_PROCESS_INIT;
        struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
        char *old_index_env = NULL, *old_repo_index_file;
 
        remove_path(stash_index_path.buf);
@@ -1205,7 +1248,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
        old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
        setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
 
-       ret = run_add_interactive(NULL, "--patch=stash", ps);
+       ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps);
 
        the_repository->index_file = old_repo_index_file;
        if (old_index_env && *old_index_env)
@@ -1236,7 +1279,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
        }
 
 done:
-       discard_index(&istate);
+       release_index(&istate);
        remove_path(stash_index_path.buf);
        return ret;
 }
@@ -1247,9 +1290,9 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
        struct rev_info rev;
        struct child_process cp_upd_index = CHILD_PROCESS_INIT;
        struct strbuf diff_output = STRBUF_INIT;
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        copy_pathspec(&rev.prune_data, ps);
 
        set_alternate_index_output(stash_index_path.buf);
@@ -1263,17 +1306,14 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
        rev.diffopt.format_callback = add_diff_to_buf;
        rev.diffopt.format_callback_data = &diff_output;
 
-       if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+       if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
                ret = -1;
                goto done;
        }
 
        add_pending_object(&rev, parse_object(the_repository, &info->b_commit),
                           "");
-       if (run_diff_index(&rev, 0)) {
-               ret = -1;
-               goto done;
-       }
+       run_diff_index(&rev, 0);
 
        cp_upd_index.git_cmd = 1;
        strvec_pushl(&cp_upd_index.args, "update-index",
@@ -1295,7 +1335,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
        }
 
 done:
-       discard_index(&istate);
+       release_index(&istate);
        release_revisions(&rev);
        strbuf_release(&diff_output);
        remove_path(stash_index_path.buf);
@@ -1321,13 +1361,14 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
        prepare_fallback_ident("git stash", "git@stash");
 
-       read_cache_preload(NULL);
-       if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) {
+       repo_read_index_preload(the_repository, NULL, 0);
+       if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
+                                        NULL, NULL, NULL) < 0) {
                ret = -1;
                goto done;
        }
 
-       if (get_oid("HEAD", &info->b_commit)) {
+       if (repo_get_oid(the_repository, "HEAD", &info->b_commit)) {
                if (!quiet)
                        fprintf_ln(stderr, _("You do not have "
                                             "the initial commit yet"));
@@ -1345,14 +1386,16 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
        branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
        if (flags & REF_ISSYMREF)
                skip_prefix(branch_ref, "refs/heads/", &branch_name);
-       head_short_sha1 = find_unique_abbrev(&head_commit->object.oid,
-                                            DEFAULT_ABBREV);
+       head_short_sha1 = repo_find_unique_abbrev(the_repository,
+                                                 &head_commit->object.oid,
+                                                 DEFAULT_ABBREV);
        strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1);
        pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);
 
        strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
        commit_list_insert(head_commit, &parents);
-       if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
+       if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+                               NULL) ||
            commit_tree(commit_tree_label.buf, commit_tree_label.len,
                        &info->i_tree, parents, &info->i_commit, NULL, NULL)) {
                if (!quiet)
@@ -1436,7 +1479,7 @@ done:
        return ret;
 }
 
-static int create_stash(int argc, const char **argv, const char *prefix)
+static int create_stash(int argc, const char **argv, const char *prefix UNUSED)
 {
        int ret;
        struct strbuf stash_msg_buf = STRBUF_INIT;
@@ -1490,15 +1533,15 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
                goto done;
        }
 
-       read_cache_preload(NULL);
+       repo_read_index_preload(the_repository, NULL, 0);
        if (!include_untracked && ps->nr) {
                int i;
                char *ps_matched = xcalloc(ps->nr, 1);
 
                /* TODO: audit for interaction with sparse-index. */
                ensure_full_index(&the_index);
-               for (i = 0; i < active_nr; i++)
-                       ce_path_match(&the_index, active_cache[i], ps,
+               for (i = 0; i < the_index.cache_nr; i++)
+                       ce_path_match(&the_index, the_index.cache[i], ps,
                                      ps_matched);
 
                if (report_path_error(ps_matched, ps)) {
@@ -1510,7 +1553,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
                free(ps_matched);
        }
 
-       if (refresh_and_write_cache(REFRESH_QUIET, 0, 0)) {
+       if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
+                                        NULL, NULL, NULL)) {
                ret = -1;
                goto done;
        }
@@ -1567,7 +1611,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
                                goto done;
                        }
                }
-               discard_cache();
+               discard_index(&the_index);
                if (ps->nr) {
                        struct child_process cp_add = CHILD_PROCESS_INIT;
                        struct child_process cp_diff = CHILD_PROCESS_INIT;
@@ -1663,8 +1707,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
        }
 
 done:
+       strbuf_release(&patch);
        free_stash_info(&info);
        strbuf_release(&stash_msg_buf);
+       strbuf_release(&untracked_files);
        return ret;
 }
 
@@ -1699,6 +1745,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
                OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
                OPT_END()
        };
+       int ret;
 
        if (argc) {
                force_assume = !strcmp(argv[0], "-p");
@@ -1738,8 +1785,10 @@ static int push_stash(int argc, const char **argv, const char *prefix,
                die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
-       return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
-                            include_untracked, only_staged);
+       ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
+                           include_untracked, only_staged);
+       clear_pathspec(&ps);
+       return ret;
 }
 
 static int push_stash_unassumed(int argc, const char **argv, const char *prefix)
index 1e34cf2bebdf5160f0236c1d41a080de5d1744e0..7b700a9fb1c2a355af6ee6935edbeec49f3f4135 100644 (file)
@@ -1,8 +1,11 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "setup.h"
 #include "strbuf.h"
+#include "write-or-die.h"
 
 static void comment_lines(struct strbuf *buf)
 {
@@ -10,7 +13,7 @@ static void comment_lines(struct strbuf *buf)
        size_t len;
 
        msg = strbuf_detach(buf, &len);
-       strbuf_add_commented_lines(buf, msg, len);
+       strbuf_add_commented_lines(buf, msg, len, comment_line_char);
        free(msg);
 }
 
@@ -55,7 +58,8 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
                die_errno("could not read the input");
 
        if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
-               strbuf_stripspace(&buf, mode == STRIP_COMMENTS);
+               strbuf_stripspace(&buf,
+                         mode == STRIP_COMMENTS ? comment_line_char : '\0');
        else
                comment_lines(&buf);
 
index 0b4acb442b209305949a2715ead54d9de518e10f..fda50f2af1e33277161f819f560b4d8164d284a0 100644 (file)
@@ -1,12 +1,20 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
-#include "cache.h"
 #include "config.h"
 #include "parse-options.h"
 #include "quote.h"
+#include "path.h"
 #include "pathspec.h"
+#include "preload-index.h"
 #include "dir.h"
+#include "read-cache.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
 #include "submodule-config.h"
 #include "string-list.h"
 #include "remote.h"
 #include "refs.h"
 #include "refspec.h"
-#include "connect.h"
 #include "revision.h"
 #include "diffcore.h"
 #include "diff.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "advice.h"
 #include "branch.h"
 #include "list-objects-filter-options.h"
@@ -113,10 +122,9 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int
 }
 
 /* the result should be freed by the caller. */
-static char *get_submodule_displaypath(const char *path, const char *prefix)
+static char *get_submodule_displaypath(const char *path, const char *prefix,
+                                      const char *super_prefix)
 {
-       const char *super_prefix = get_super_prefix();
-
        if (prefix && super_prefix) {
                BUG("cannot have prefix '%s' and superprefix '%s'",
                    prefix, super_prefix);
@@ -181,7 +189,7 @@ static void module_list_release(struct module_list *ml)
        free(ml->entries);
 }
 
-static int module_list_compute(int argc, const char **argv,
+static int module_list_compute(const char **argv,
                               const char *prefix,
                               struct pathspec *pathspec,
                               struct module_list *list)
@@ -196,11 +204,11 @@ static int module_list_compute(int argc, const char **argv,
        if (pathspec->nr)
                ps_matched = xcalloc(pathspec->nr, 1);
 
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                die(_("index file corrupt"));
 
-       for (i = 0; i < active_nr; i++) {
-               const struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < the_index.cache_nr; i++) {
+               const struct cache_entry *ce = the_index.cache[i];
 
                if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
                                    0, ps_matched, 1) ||
@@ -209,8 +217,8 @@ static int module_list_compute(int argc, const char **argv,
 
                ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
                list->entries[list->nr++] = ce;
-               while (i + 1 < active_nr &&
-                      !strcmp(ce->name, active_cache[i + 1]->name))
+               while (i + 1 < the_index.cache_nr &&
+                      !strcmp(ce->name, the_index.cache[i + 1]->name))
                        /*
                         * Skip entries with the same name in different stages
                         * to make sure an entry is returned only once.
@@ -279,6 +287,7 @@ struct foreach_cb {
        int argc;
        const char **argv;
        const char *prefix;
+       const char *super_prefix;
        int quiet;
        int recursive;
 };
@@ -294,7 +303,8 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
        struct child_process cp = CHILD_PROCESS_INIT;
        char *displaypath;
 
-       displaypath = get_submodule_displaypath(path, info->prefix);
+       displaypath = get_submodule_displaypath(path, info->prefix,
+                                               info->super_prefix);
 
        sub = submodule_from_path(the_repository, null_oid(), path);
 
@@ -364,10 +374,10 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
 
-               strvec_pushl(&cpr.args, "--super-prefix", NULL);
-               strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
                             NULL);
+               strvec_pushl(&cpr.args, "--super-prefix", NULL);
+               strvec_pushf(&cpr.args, "%s/", displaypath);
 
                if (info->quiet)
                        strvec_push(&cpr.args, "--quiet");
@@ -391,6 +401,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
        struct pathspec pathspec = { 0 };
        struct module_list list = MODULE_LIST_INIT;
        struct option module_foreach_options[] = {
+               OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
                OPT_BOOL(0, "recursive", &info.recursive,
                         N_("recurse into nested submodules")),
@@ -405,7 +416,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, module_foreach_options,
                             git_submodule_helper_usage, 0);
 
-       if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
+       if (module_list_compute(NULL, prefix, &pathspec, &list) < 0)
                goto cleanup;
 
        info.argc = argc;
@@ -435,11 +446,13 @@ static int starts_with_dot_dot_slash(const char *const path)
 
 struct init_cb {
        const char *prefix;
+       const char *super_prefix;
        unsigned int flags;
 };
 #define INIT_CB_INIT { 0 }
 
 static void init_submodule(const char *path, const char *prefix,
+                          const char *super_prefix,
                           unsigned int flags)
 {
        const struct submodule *sub;
@@ -447,7 +460,7 @@ static void init_submodule(const char *path, const char *prefix,
        const char *upd;
        char *url = NULL, *displaypath;
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       displaypath = get_submodule_displaypath(path, prefix, super_prefix);
 
        sub = submodule_from_path(the_repository, null_oid(), path);
 
@@ -523,7 +536,8 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 {
        struct init_cb *info = cb_data;
 
-       init_submodule(list_item->name, info->prefix, info->flags);
+       init_submodule(list_item->name, info->prefix, info->super_prefix,
+                      info->flags);
 }
 
 static int module_init(int argc, const char **argv, const char *prefix)
@@ -545,14 +559,14 @@ static int module_init(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, module_init_options,
                             git_submodule_helper_usage, 0);
 
-       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+       if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
                goto cleanup;
 
        /*
         * If there are no path args and submodule.active is set then,
         * by default, only initialize 'active' modules.
         */
-       if (!argc && git_config_get_value_multi("submodule.active"))
+       if (!argc && !git_config_get("submodule.active"))
                module_list_active(&list);
 
        info.prefix = prefix;
@@ -570,6 +584,7 @@ cleanup:
 
 struct status_cb {
        const char *prefix;
+       const char *super_prefix;
        unsigned int flags;
 };
 #define STATUS_CB_INIT { 0 }
@@ -608,20 +623,22 @@ static int handle_submodule_head_ref(const char *refname UNUSED,
 
 static void status_submodule(const char *path, const struct object_id *ce_oid,
                             unsigned int ce_flags, const char *prefix,
-                            unsigned int flags)
+                            const char *super_prefix, unsigned int flags)
 {
        char *displaypath;
        struct strvec diff_files_args = STRVEC_INIT;
        struct rev_info rev = REV_INFO_INIT;
-       int diff_files_result;
        struct strbuf buf = STRBUF_INIT;
        const char *git_dir;
+       struct setup_revision_opt opt = {
+               .free_removed_argv_elements = 1,
+       };
 
        if (!submodule_from_path(the_repository, null_oid(), path))
                die(_("no submodule mapping found in .gitmodules for path '%s'"),
                      path);
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       displaypath = get_submodule_displaypath(path, prefix, super_prefix);
 
        if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
                print_status(flags, 'U', path, null_oid(), displaypath);
@@ -649,12 +666,10 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 
        repo_init_revisions(the_repository, &rev, NULL);
        rev.abbrev = 0;
-       diff_files_args.nr = setup_revisions(diff_files_args.nr,
-                                            diff_files_args.v,
-                                            &rev, NULL);
-       diff_files_result = run_diff_files(&rev, 0);
+       setup_revisions(diff_files_args.nr, diff_files_args.v, &rev, &opt);
+       run_diff_files(&rev, 0);
 
-       if (!diff_result_code(&rev.diffopt, diff_files_result)) {
+       if (!diff_result_code(&rev.diffopt)) {
                print_status(flags, ' ', path, ce_oid,
                             displaypath);
        } else if (!(flags & OPT_CACHED)) {
@@ -681,10 +696,10 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
 
-               strvec_push(&cpr.args, "--super-prefix");
-               strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "status",
                             "--recursive", NULL);
+               strvec_push(&cpr.args, "--super-prefix");
+               strvec_pushf(&cpr.args, "%s/", displaypath);
 
                if (flags & OPT_CACHED)
                        strvec_push(&cpr.args, "--cached");
@@ -708,7 +723,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
        struct status_cb *info = cb_data;
 
        status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
-                        info->prefix, info->flags);
+                        info->prefix, info->super_prefix, info->flags);
 }
 
 static int module_status(int argc, const char **argv, const char *prefix)
@@ -718,6 +733,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
        struct module_list list = MODULE_LIST_INIT;
        int quiet = 0;
        struct option module_status_options[] = {
+               OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&quiet, N_("suppress submodule status output")),
                OPT_BIT(0, "cached", &info.flags, N_("use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
                OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
@@ -732,7 +748,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, module_status_options,
                             git_submodule_helper_usage, 0);
 
-       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+       if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
                goto cleanup;
 
        info.prefix = prefix;
@@ -786,6 +802,7 @@ struct summary_cb {
        int argc;
        const char **argv;
        const char *prefix;
+       const char *super_prefix;
        unsigned int cached: 1;
        unsigned int for_status: 1;
        unsigned int files: 1;
@@ -947,7 +964,8 @@ static void generate_submodule_summary(struct summary_cb *info,
                dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
        }
 
-       displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
+       displaypath = get_submodule_displaypath(p->sm_path, info->prefix,
+                                               info->super_prefix);
 
        if (!missing_src && !missing_dst) {
                struct child_process cp_rev_list = CHILD_PROCESS_INIT;
@@ -1042,7 +1060,7 @@ static void prepare_submodule_summary(struct summary_cb *info,
 }
 
 static void submodule_summary_callback(struct diff_queue_struct *q,
-                                      struct diff_options *options,
+                                      struct diff_options *options UNUSED,
                                       void *data)
 {
        int i;
@@ -1098,7 +1116,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
                strvec_pushv(&diff_args, info->argv);
 
        git_config(git_diff_basic_config, NULL);
-       init_revisions(&rev, info->prefix);
+       repo_init_revisions(the_repository, &rev, info->prefix);
        rev.abbrev = 0;
        precompose_argv_prefix(diff_args.nr, diff_args.v, NULL);
        setup_revisions(diff_args.nr, diff_args.v, &rev, &opt);
@@ -1109,19 +1127,19 @@ static int compute_summary_module_list(struct object_id *head_oid,
        if (!info->cached) {
                if (diff_cmd == DIFF_INDEX)
                        setup_work_tree();
-               if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
-                       perror("read_cache_preload");
+               if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
+                       perror("repo_read_index_preload");
                        ret = -1;
                        goto cleanup;
                }
-       } else if (read_cache() < 0) {
-               perror("read_cache");
+       } else if (repo_read_index(the_repository) < 0) {
+               perror("repo_read_cache");
                ret = -1;
                goto cleanup;
        }
 
        if (diff_cmd == DIFF_INDEX)
-               run_diff_index(&rev, info->cached);
+               run_diff_index(&rev, info->cached ? DIFF_INDEX_CACHED : 0);
        else
                run_diff_files(&rev, 0);
        prepare_submodule_summary(info, &list);
@@ -1164,7 +1182,7 @@ static int module_summary(int argc, const char **argv, const char *prefix)
        if (!summary_limit)
                return 0;
 
-       if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
+       if (!repo_get_oid(the_repository, argc ? argv[0] : "HEAD", &head_oid)) {
                if (argc) {
                        argv++;
                        argc--;
@@ -1177,7 +1195,7 @@ static int module_summary(int argc, const char **argv, const char *prefix)
                        argc--;
                }
        } else {
-               if (get_oid("HEAD", &head_oid))
+               if (repo_get_oid(the_repository, "HEAD", &head_oid))
                        die(_("could not fetch a revision for HEAD"));
        }
 
@@ -1202,12 +1220,13 @@ static int module_summary(int argc, const char **argv, const char *prefix)
 
 struct sync_cb {
        const char *prefix;
+       const char *super_prefix;
        unsigned int flags;
 };
 #define SYNC_CB_INIT { 0 }
 
 static void sync_submodule(const char *path, const char *prefix,
-                          unsigned int flags)
+                          const char *super_prefix, unsigned int flags)
 {
        const struct submodule *sub;
        char *remote_key = NULL;
@@ -1238,7 +1257,7 @@ static void sync_submodule(const char *path, const char *prefix,
                super_config_url = xstrdup("");
        }
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       displaypath = get_submodule_displaypath(path, prefix, super_prefix);
 
        if (!(flags & OPT_QUIET))
                printf(_("Synchronizing submodule url for '%s'\n"),
@@ -1275,10 +1294,11 @@ static void sync_submodule(const char *path, const char *prefix,
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
 
-               strvec_push(&cpr.args, "--super-prefix");
-               strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "sync",
                             "--recursive", NULL);
+               strvec_push(&cpr.args, "--super-prefix");
+               strvec_pushf(&cpr.args, "%s/", displaypath);
+
 
                if (flags & OPT_QUIET)
                        strvec_push(&cpr.args, "--quiet");
@@ -1301,7 +1321,8 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 {
        struct sync_cb *info = cb_data;
 
-       sync_submodule(list_item->name, info->prefix, info->flags);
+       sync_submodule(list_item->name, info->prefix, info->super_prefix,
+                      info->flags);
 }
 
 static int module_sync(int argc, const char **argv, const char *prefix)
@@ -1312,6 +1333,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int recursive = 0;
        struct option module_sync_options[] = {
+               OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
                OPT_BOOL(0, "recursive", &recursive,
                        N_("recurse into nested submodules")),
@@ -1326,7 +1348,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, module_sync_options,
                             git_submodule_helper_usage, 0);
 
-       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+       if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
                goto cleanup;
 
        info.prefix = prefix;
@@ -1364,7 +1386,7 @@ static void deinit_submodule(const char *path, const char *prefix,
        if (!sub || !sub->name)
                goto cleanup;
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       displaypath = get_submodule_displaypath(path, prefix, NULL);
 
        /* remove the submodule work tree (unless the user already did it) */
        if (is_directory(path)) {
@@ -1378,8 +1400,7 @@ static void deinit_submodule(const char *path, const char *prefix,
                                          ".git file by using absorbgitdirs."),
                                        displaypath);
 
-                       absorb_git_dir_into_superproject(path,
-                                                        ABSORB_GITDIR_RECURSE_SUBMODULES);
+                       absorb_git_dir_into_superproject(path, NULL);
 
                }
 
@@ -1479,7 +1500,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
        if (!argc && !all)
                die(_("Use '--all' if you really want to deinitialize all submodules"));
 
-       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+       if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
                goto cleanup;
 
        info.prefix = prefix;
@@ -1883,6 +1904,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
        const char *prefix;
+       const char *super_prefix;
        char *displaypath;
        enum submodule_update_type update_default;
        struct object_id suboid;
@@ -1958,7 +1980,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        enum submodule_update_type update_type;
        char *key;
        const struct update_data *ud = suc->update_data;
-       char *displaypath = get_submodule_displaypath(ce->name, ud->prefix);
+       char *displaypath = get_submodule_displaypath(ce->name, ud->prefix,
+                                                     ud->super_prefix);
        struct strbuf sb = STRBUF_INIT;
        int needs_cloning = 0;
        int need_free_url = 0;
@@ -2001,14 +2024,17 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
        if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
-               if (starts_with_dot_slash(sub->url) ||
-                   starts_with_dot_dot_slash(sub->url)) {
+               if (sub->url && (starts_with_dot_slash(sub->url) ||
+                                starts_with_dot_dot_slash(sub->url))) {
                        url = resolve_relative_url(sub->url, NULL, 0);
                        need_free_url = 1;
                } else
                        url = sub->url;
        }
 
+       if (!url)
+               die(_("cannot clone submodule '%s' without a URL"), sub->name);
+
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/.git", ce->name);
        needs_cloning = !file_exists(sb.buf);
@@ -2117,9 +2143,9 @@ static int update_clone_get_next_task(struct child_process *child,
        return 0;
 }
 
-static int update_clone_start_failure(struct strbuf *err,
+static int update_clone_start_failure(struct strbuf *err UNUSED,
                                      void *suc_cb,
-                                     void *idx_task_cb)
+                                     void *idx_task_cb UNUSED)
 {
        struct submodule_update_clone *suc = suc_cb;
 
@@ -2166,12 +2192,13 @@ static int update_clone_task_finished(int result,
 }
 
 static int git_update_clone_config(const char *var, const char *value,
+                                  const struct config_context *ctx,
                                   void *cb)
 {
        int *max_jobs = cb;
 
        if (!strcmp(var, "submodule.fetchjobs"))
-               *max_jobs = parse_submodule_fetchjobs(var, value);
+               *max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
        return 0;
 }
 
@@ -2438,11 +2465,11 @@ static void update_data_to_args(const struct update_data *update_data,
 {
        enum submodule_update_type update_type = update_data->update_default;
 
+       strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
        if (update_data->displaypath) {
                strvec_push(args, "--super-prefix");
                strvec_pushf(args, "%s/", update_data->displaypath);
        }
-       strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
        strvec_pushf(args, "--jobs=%d", update_data->max_jobs);
        if (update_data->quiet)
                strvec_push(args, "--quiet");
@@ -2567,12 +2594,20 @@ static int update_submodules(struct update_data *update_data)
 {
        int i, ret = 0;
        struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
+       const struct run_process_parallel_opts opts = {
+               .tr2_category = "submodule",
+               .tr2_label = "parallel/update",
+
+               .processes = update_data->max_jobs,
+
+               .get_next_task = update_clone_get_next_task,
+               .start_failure = update_clone_start_failure,
+               .task_finished = update_clone_task_finished,
+               .data = &suc,
+       };
 
        suc.update_data = update_data;
-       run_processes_parallel_tr2(suc.update_data->max_jobs, update_clone_get_next_task,
-                                  update_clone_start_failure,
-                                  update_clone_task_finished, &suc, "submodule",
-                                  "parallel/update");
+       run_processes_parallel(&opts);
 
        /*
         * We saved the output and put it out all at once now.
@@ -2600,7 +2635,8 @@ static int update_submodules(struct update_data *update_data)
                        goto fail;
 
                update_data->displaypath = get_submodule_displaypath(
-                       update_data->sm_path, update_data->prefix);
+                       update_data->sm_path, update_data->prefix,
+                       update_data->super_prefix);
                code = update_submodule(update_data);
                FREE_AND_NULL(update_data->displaypath);
 fail:
@@ -2626,6 +2662,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
                LIST_OBJECTS_FILTER_INIT;
        int ret;
        struct option module_update_options[] = {
+               OPT__SUPER_PREFIX(&opt.super_prefix),
                OPT__FORCE(&opt.force, N_("force checkout updates"), 0),
                OPT_BOOL(0, "init", &opt.init,
                         N_("initialize uninitialized submodules before update")),
@@ -2635,9 +2672,6 @@ static int module_update(int argc, const char **argv, const char *prefix)
                         N_("traverse submodules recursively")),
                OPT_BOOL('N', "no-fetch", &opt.nofetch,
                         N_("don't fetch new objects from the remote site")),
-               OPT_STRING(0, "prefix", &opt.prefix,
-                          N_("path"),
-                          N_("path into the working tree")),
                OPT_SET_INT(0, "checkout", &opt.update_default,
                        N_("use the 'checkout' update strategy (default)"),
                        SM_UPDATE_CHECKOUT),
@@ -2693,11 +2727,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
        }
 
        opt.filter_options = &filter_options;
+       opt.prefix = prefix;
 
        if (opt.update_default)
                opt.update_strategy.type = opt.update_default;
 
-       if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
+       if (module_list_compute(argv, prefix, &pathspec, &opt.list) < 0) {
                ret = 1;
                goto cleanup;
        }
@@ -2709,7 +2744,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
                struct module_list list = MODULE_LIST_INIT;
                struct init_cb info = INIT_CB_INIT;
 
-               if (module_list_compute(argc, argv, opt.prefix,
+               if (module_list_compute(argv, opt.prefix,
                                        &pathspec2, &list) < 0) {
                        module_list_release(&list);
                        ret = 1;
@@ -2720,10 +2755,11 @@ static int module_update(int argc, const char **argv, const char *prefix)
                 * If there are no path args and submodule.active is set then,
                 * by default, only initialize 'active' modules.
                 */
-               if (!argc && git_config_get_value_multi("submodule.active"))
+               if (!argc && !git_config_get("submodule.active"))
                        module_list_active(&list);
 
                info.prefix = opt.prefix;
+               info.super_prefix = opt.super_prefix;
                if (opt.quiet)
                        info.flags |= OPT_QUIET;
 
@@ -2740,7 +2776,7 @@ cleanup:
        return ret;
 }
 
-static int push_check(int argc, const char **argv, const char *prefix)
+static int push_check(int argc, const char **argv, const char *prefix UNUSED)
 {
        struct remote *remote;
        const char *superproject_head;
@@ -2822,13 +2858,9 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
        int i;
        struct pathspec pathspec = { 0 };
        struct module_list list = MODULE_LIST_INIT;
-       unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
+       const char *super_prefix = NULL;
        struct option embed_gitdir_options[] = {
-               OPT_STRING(0, "prefix", &prefix,
-                          N_("path"),
-                          N_("path into the working tree")),
-               OPT_BIT(0, "--recursive", &flags, N_("recurse into submodules"),
-                       ABSORB_GITDIR_RECURSE_SUBMODULES),
+               OPT__SUPER_PREFIX(&super_prefix),
                OPT_END()
        };
        const char *const git_submodule_helper_usage[] = {
@@ -2840,11 +2872,12 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, embed_gitdir_options,
                             git_submodule_helper_usage, 0);
 
-       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+       if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
                goto cleanup;
 
        for (i = 0; i < list.nr; i++)
-               absorb_git_dir_into_superproject(list.entries[i]->name, flags);
+               absorb_git_dir_into_superproject(list.entries[i]->name,
+                                                super_prefix);
 
        ret = 0;
 cleanup:
@@ -2853,54 +2886,9 @@ cleanup:
        return ret;
 }
 
-static int module_config(int argc, const char **argv, const char *prefix)
-{
-       enum {
-               CHECK_WRITEABLE = 1,
-               DO_UNSET = 2
-       } command = 0;
-       struct option module_config_options[] = {
-               OPT_CMDMODE(0, "check-writeable", &command,
-                           N_("check if it is safe to write to the .gitmodules file"),
-                           CHECK_WRITEABLE),
-               OPT_CMDMODE(0, "unset", &command,
-                           N_("unset the config in the .gitmodules file"),
-                           DO_UNSET),
-               OPT_END()
-       };
-       const char *const git_submodule_helper_usage[] = {
-               N_("git submodule--helper config <name> [<value>]"),
-               N_("git submodule--helper config --unset <name>"),
-               "git submodule--helper config --check-writeable",
-               NULL
-       };
-
-       argc = parse_options(argc, argv, prefix, module_config_options,
-                            git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
-
-       if (argc == 1 && command == CHECK_WRITEABLE)
-               return is_writing_gitmodules_ok() ? 0 : -1;
-
-       /* Equivalent to ACTION_GET in builtin/config.c */
-       if (argc == 2 && command != DO_UNSET)
-               return print_config_from_gitmodules(the_repository, argv[1]);
-
-       /* Equivalent to ACTION_SET in builtin/config.c */
-       if (argc == 3 || (argc == 2 && command == DO_UNSET)) {
-               const char *value = (argc == 3) ? argv[2] : NULL;
-
-               if (!is_writing_gitmodules_ok())
-                       die(_("please make sure that the .gitmodules file is in the working tree"));
-
-               return config_set_in_gitmodules_file_gently(argv[1], value);
-       }
-
-       usage_with_options(git_submodule_helper_usage, module_config_options);
-}
-
 static int module_set_url(int argc, const char **argv, const char *prefix)
 {
-       int quiet = 0;
+       int quiet = 0, ret;
        const char *newurl;
        const char *path;
        char *config_name;
@@ -2912,20 +2900,29 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
                N_("git submodule set-url [--quiet] <path> <newurl>"),
                NULL
        };
+       const struct submodule *sub;
 
        argc = parse_options(argc, argv, prefix, options, usage, 0);
 
        if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1]))
                usage_with_options(usage, options);
 
-       config_name = xstrfmt("submodule.%s.url", path);
+       sub = submodule_from_path(the_repository, null_oid(), path);
 
-       config_set_in_gitmodules_file_gently(config_name, newurl);
-       sync_submodule(path, prefix, quiet ? OPT_QUIET : 0);
+       if (!sub)
+               die(_("no submodule mapping found in .gitmodules for path '%s'"),
+                   path);
 
-       free(config_name);
+       config_name = xstrfmt("submodule.%s.url", sub->name);
+       ret = config_set_in_gitmodules_file_gently(config_name, newurl);
 
-       return 0;
+       if (!ret) {
+               repo_read_gitmodules(the_repository, 0);
+               sync_submodule(sub->path, prefix, NULL, quiet ? OPT_QUIET : 0);
+       }
+
+       free(config_name);
+       return !!ret;
 }
 
 static int module_set_branch(int argc, const char **argv, const char *prefix)
@@ -2952,6 +2949,7 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
                N_("git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"),
                NULL
        };
+       const struct submodule *sub;
 
        argc = parse_options(argc, argv, prefix, options, usage, 0);
 
@@ -2964,7 +2962,13 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
        if (argc != 1 || !(path = argv[0]))
                usage_with_options(usage, options);
 
-       config_name = xstrfmt("submodule.%s.branch", path);
+       sub = submodule_from_path(the_repository, null_oid(), path);
+
+       if (!sub)
+               die(_("no submodule mapping found in .gitmodules for path '%s'"),
+                   path);
+
+       config_name = xstrfmt("submodule.%s.branch", sub->name);
        ret = config_set_in_gitmodules_file_gently(config_name, opt_branch);
 
        free(config_name);
@@ -3164,7 +3168,6 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
 static void configure_added_submodule(struct add_data *add_data)
 {
        char *key;
-       const char *val;
        struct child_process add_submod = CHILD_PROCESS_INIT;
        struct child_process add_gitmodules = CHILD_PROCESS_INIT;
 
@@ -3209,7 +3212,7 @@ static void configure_added_submodule(struct add_data *add_data)
         * is_submodule_active(), since that function needs to find
         * out the value of "submodule.active" again anyway.
         */
-       if (!git_config_get_string_tmp("submodule.active", &val)) {
+       if (!git_config_get("submodule.active")) {
                /*
                 * If the submodule being added isn't already covered by the
                 * current configured pathspec, set the submodule's active flag
@@ -3232,7 +3235,7 @@ static void die_on_index_match(const char *path, int force)
        const char *args[] = { path, NULL };
        parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
 
-       if (read_cache_preload(NULL) < 0)
+       if (repo_read_index_preload(the_repository, NULL, 0) < 0)
                die(_("index file corrupt"));
 
        if (ps.nr) {
@@ -3243,19 +3246,18 @@ static void die_on_index_match(const char *path, int force)
                ensure_full_index(&the_index);
 
                /*
-                * Since there is only one pathspec, we just need
-                * need to check ps_matched[0] to know if a cache
-                * entry matched.
+                * Since there is only one pathspec, we just need to
+                * check ps_matched[0] to know if a cache entry matched.
                 */
-               for (i = 0; i < active_nr; i++) {
-                       ce_path_match(&the_index, active_cache[i], &ps,
+               for (i = 0; i < the_index.cache_nr; i++) {
+                       ce_path_match(&the_index, the_index.cache[i], &ps,
                                      ps_matched);
 
                        if (ps_matched[0]) {
                                if (!force)
                                        die(_("'%s' already exists in the index"),
                                            path);
-                               if (!S_ISGITLINK(active_cache[i]->ce_mode))
+                               if (!S_ISGITLINK(the_index.cache[i]->ce_mode))
                                        die(_("'%s' already exists in the index "
                                              "and is not a submodule"), path);
                                break;
@@ -3396,48 +3398,31 @@ cleanup:
        return ret;
 }
 
-#define SUPPORT_SUPER_PREFIX (1<<0)
-
-struct cmd_struct {
-       const char *cmd;
-       int (*fn)(int, const char **, const char *);
-       unsigned option;
-};
-
-static struct cmd_struct commands[] = {
-       {"clone", module_clone, SUPPORT_SUPER_PREFIX},
-       {"add", module_add, 0},
-       {"update", module_update, SUPPORT_SUPER_PREFIX},
-       {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
-       {"init", module_init, 0},
-       {"status", module_status, SUPPORT_SUPER_PREFIX},
-       {"sync", module_sync, SUPPORT_SUPER_PREFIX},
-       {"deinit", module_deinit, 0},
-       {"summary", module_summary, 0},
-       {"push-check", push_check, 0},
-       {"absorbgitdirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
-       {"config", module_config, 0},
-       {"set-url", module_set_url, 0},
-       {"set-branch", module_set_branch, 0},
-       {"create-branch", module_create_branch, 0},
-};
-
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
 {
-       int i;
-       if (argc < 2 || !strcmp(argv[1], "-h"))
-               usage("git submodule--helper <command>");
-
-       for (i = 0; i < ARRAY_SIZE(commands); i++) {
-               if (!strcmp(argv[1], commands[i].cmd)) {
-                       if (get_super_prefix() &&
-                           !(commands[i].option & SUPPORT_SUPER_PREFIX))
-                               die(_("%s doesn't support --super-prefix"),
-                                   commands[i].cmd);
-                       return commands[i].fn(argc - 1, argv + 1, prefix);
-               }
-       }
+       parse_opt_subcommand_fn *fn = NULL;
+       const char *const usage[] = {
+               N_("git submodule--helper <command>"),
+               NULL
+       };
+       struct option options[] = {
+               OPT_SUBCOMMAND("clone", &fn, module_clone),
+               OPT_SUBCOMMAND("add", &fn, module_add),
+               OPT_SUBCOMMAND("update", &fn, module_update),
+               OPT_SUBCOMMAND("foreach", &fn, module_foreach),
+               OPT_SUBCOMMAND("init", &fn, module_init),
+               OPT_SUBCOMMAND("status", &fn, module_status),
+               OPT_SUBCOMMAND("sync", &fn, module_sync),
+               OPT_SUBCOMMAND("deinit", &fn, module_deinit),
+               OPT_SUBCOMMAND("summary", &fn, module_summary),
+               OPT_SUBCOMMAND("push-check", &fn, push_check),
+               OPT_SUBCOMMAND("absorbgitdirs", &fn, absorb_git_dirs),
+               OPT_SUBCOMMAND("set-url", &fn, module_set_url),
+               OPT_SUBCOMMAND("set-branch", &fn, module_set_branch),
+               OPT_SUBCOMMAND("create-branch", &fn, module_create_branch),
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options, usage, 0);
 
-       die(_("'%s' is not a valid submodule--helper "
-             "subcommand"), argv[1]);
+       return fn(argc, argv, prefix);
 }
index 1b0f10225f0c2630fab0f67534e7135b30571c66..c9defe4d2e4ff057bce4dc31330ae1f964225ede 100644 (file)
@@ -1,19 +1,24 @@
 #include "builtin.h"
 #include "config.h"
-#include "cache.h"
+#include "gettext.h"
 #include "refs.h"
 #include "parse-options.h"
+#include "strbuf.h"
 
 static const char * const git_symbolic_ref_usage[] = {
-       N_("git symbolic-ref [<options>] <name> [<ref>]"),
-       N_("git symbolic-ref -d [-q] <name>"),
+       N_("git symbolic-ref [-m <reason>] <name> <ref>"),
+       N_("git symbolic-ref [-q] [--short] [--no-recurse] <name>"),
+       N_("git symbolic-ref --delete [-q] <name>"),
        NULL
 };
 
-static int check_symref(const char *HEAD, int quiet, int shorten, int print)
+static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, int print)
 {
-       int flag;
-       const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag);
+       int resolve_flags, flag;
+       const char *refname;
+
+       resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE);
+       refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag);
 
        if (!refname)
                die("No such ref: %s", HEAD);
@@ -35,13 +40,14 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print)
 
 int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
 {
-       int quiet = 0, delete = 0, shorten = 0, ret = 0;
+       int quiet = 0, delete = 0, shorten = 0, recurse = 1, ret = 0;
        const char *msg = NULL;
        struct option options[] = {
                OPT__QUIET(&quiet,
                        N_("suppress error message for non-symbolic (detached) refs")),
                OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")),
                OPT_BOOL(0, "short", &shorten, N_("shorten ref output")),
+               OPT_BOOL(0, "recurse", &recurse, N_("recursively dereference (default)")),
                OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")),
                OPT_END(),
        };
@@ -55,7 +61,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
        if (delete) {
                if (argc != 1)
                        usage_with_options(git_symbolic_ref_usage, options);
-               ret = check_symref(argv[0], 1, 0, 0);
+               ret = check_symref(argv[0], 1, 0, 0, 0);
                if (ret)
                        die("Cannot delete %s, not a symbolic ref", argv[0]);
                if (!strcmp(argv[0], "HEAD"))
@@ -65,7 +71,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
 
        switch (argc) {
        case 1:
-               ret = check_symref(argv[0], quiet, shorten, 1);
+               ret = check_symref(argv[0], quiet, shorten, recurse, 1);
                break;
        case 2:
                if (!strcmp(argv[0], "HEAD") &&
index 75dece0e4f1c90c592f86a4881a01e6553d87613..f036cf32f5fafffba426e7e050f93ca39efc0eb2 100644 (file)
@@ -6,13 +6,18 @@
  * Based on git-tag.sh and mktag.c by Linus Torvalds.
  */
 
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "advice.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "tag.h"
-#include "run-command.h"
 #include "parse-options.h"
 #include "diff.h"
 #include "revision.h"
 #include "column.h"
 #include "ref-filter.h"
 #include "date.h"
+#include "write-or-die.h"
 
 static const char * const git_tag_usage[] = {
-       N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-          "        <tagname> [<head>]"),
+       N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+          "        <tagname> [<commit> | <object>]"),
        N_("git tag -d <tagname>..."),
-       N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
-          "        [--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
+       N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+          "        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+          "        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+          "        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
        N_("git tag -v [--format=<format>] <tagname>..."),
        NULL
 };
@@ -39,13 +47,7 @@ static int config_sign_tag = -1; /* unspecified */
 static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
                     struct ref_format *format)
 {
-       struct ref_array array;
-       struct strbuf output = STRBUF_INIT;
-       struct strbuf err = STRBUF_INIT;
        char *to_free = NULL;
-       int i;
-
-       memset(&array, 0, sizeof(array));
 
        if (filter->lines == -1)
                filter->lines = 0;
@@ -63,21 +65,8 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
        if (verify_ref_format(format))
                die(_("unable to parse format string"));
        filter->with_commit_tag_algo = 1;
-       filter_refs(&array, filter, FILTER_REFS_TAGS);
-       ref_array_sort(sorting, &array);
-
-       for (i = 0; i < array.nr; i++) {
-               strbuf_reset(&output);
-               strbuf_reset(&err);
-               if (format_ref_array_item(array.items[i], format, &output, &err))
-                       die("%s", err.buf);
-               fwrite(output.buf, 1, output.len, stdout);
-               putchar('\n');
-       }
+       filter_and_format_refs(filter, FILTER_REFS_TAGS, sorting, format);
 
-       strbuf_release(&err);
-       strbuf_release(&output);
-       ref_array_clear(&array);
        free(to_free);
 
        return 0;
@@ -109,7 +98,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
        return had_error;
 }
 
-static int collect_tags(const char *name, const char *ref,
+static int collect_tags(const char *name UNUSED, const char *ref,
                        const struct object_id *oid, void *cb_data)
 {
        struct string_list *ref_list = cb_data;
@@ -135,7 +124,7 @@ static int delete_tags(const char **argv)
                if (!ref_exists(name))
                        printf(_("Deleted tag '%s' (was %s)\n"),
                                item->string + 10,
-                               find_unique_abbrev(oid, DEFAULT_ABBREV));
+                               repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
 
                free(oid);
        }
@@ -143,7 +132,7 @@ static int delete_tags(const char **argv)
        return result;
 }
 
-static int verify_tag(const char *name, const char *ref,
+static int verify_tag(const char *name, const char *ref UNUSED,
                      const struct object_id *oid, void *cb_data)
 {
        int flags;
@@ -176,10 +165,9 @@ static const char tag_template_nocleanup[] =
        "Lines starting with '%c' will be kept; you may remove them"
        " yourself if you want to.\n");
 
-static int git_tag_config(const char *var, const char *value, void *cb)
+static int git_tag_config(const char *var, const char *value,
+                         const struct config_context *ctx, void *cb)
 {
-       int status;
-
        if (!strcmp(var, "tag.gpgsign")) {
                config_sign_tag = git_config_bool(var, value);
                return 0;
@@ -192,9 +180,6 @@ static int git_tag_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       status = git_gpg_config(var, value, cb);
-       if (status)
-               return status;
        if (!strcmp(var, "tag.forcesignannotated")) {
                force_sign_annotate = git_config_bool(var, value);
                return 0;
@@ -202,7 +187,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
 
        if (starts_with(var, "column."))
                return git_column_config(var, value, "tag", &colopts);
-       return git_color_default_config(var, value, cb);
+
+       if (git_color_config(var, value, cb) < 0)
+               return -1;
+
+       return git_default_config(var, value, ctx, cb);
 }
 
 static void write_tag_body(int fd, const struct object_id *oid)
@@ -213,7 +202,7 @@ static void write_tag_body(int fd, const struct object_id *oid)
        struct strbuf payload = STRBUF_INIT;
        struct strbuf signature = STRBUF_INIT;
 
-       orig = buf = read_object_file(oid, &type, &size);
+       orig = buf = repo_read_object_file(the_repository, oid, &type, &size);
        if (!buf)
                return;
        if (parse_signature(buf, size, &payload, &signature)) {
@@ -264,11 +253,10 @@ static const char message_advice_nested_tag[] =
 static void create_tag(const struct object_id *object, const char *object_ref,
                       const char *tag,
                       struct strbuf *buf, struct create_tag_options *opt,
-                      struct object_id *prev, struct object_id *result)
+                      struct object_id *prev, struct object_id *result, char *path)
 {
        enum object_type type;
        struct strbuf header = STRBUF_INIT;
-       char *path = NULL;
 
        type = oid_object_info(the_repository, object, NULL);
        if (type <= OBJ_NONE)
@@ -292,7 +280,6 @@ static void create_tag(const struct object_id *object, const char *object_ref,
                int fd;
 
                /* write the template message before editing: */
-               path = git_pathdup("TAG_EDITMSG");
                fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
                if (opt->message_given) {
@@ -304,9 +291,11 @@ static void create_tag(const struct object_id *object, const char *object_ref,
                        struct strbuf buf = STRBUF_INIT;
                        strbuf_addch(&buf, '\n');
                        if (opt->cleanup_mode == CLEANUP_ALL)
-                               strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char);
+                               strbuf_commented_addf(&buf, comment_line_char,
+                                     _(tag_template), tag, comment_line_char);
                        else
-                               strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char);
+                               strbuf_commented_addf(&buf, comment_line_char,
+                                     _(tag_template_nocleanup), tag, comment_line_char);
                        write_or_die(fd, buf.buf, buf.len);
                        strbuf_release(&buf);
                }
@@ -320,7 +309,8 @@ static void create_tag(const struct object_id *object, const char *object_ref,
        }
 
        if (opt->cleanup_mode != CLEANUP_NONE)
-               strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
+               strbuf_stripspace(buf,
+                 opt->cleanup_mode == CLEANUP_ALL ? comment_line_char : '\0');
 
        if (!opt->message_given && !buf->len)
                die(_("no tag message?"));
@@ -334,10 +324,6 @@ static void create_tag(const struct object_id *object, const char *object_ref,
                                path);
                exit(128);
        }
-       if (path) {
-               unlink_or_warn(path);
-               free(path);
-       }
 }
 
 static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
@@ -364,7 +350,7 @@ static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
                strbuf_addstr(sb, "object of unknown type");
                break;
        case OBJ_COMMIT:
-               if ((buf = read_object_file(oid, &type, &size))) {
+               if ((buf = repo_read_object_file(the_repository, oid, &type, &size))) {
                        subject_len = find_commit_subject(buf, &subject_start);
                        strbuf_insert(sb, sb->len, subject_start, subject_len);
                } else {
@@ -431,11 +417,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        int create_reflog = 0;
        int annotate = 0, force = 0;
        int cmdmode = 0, create_tag_object = 0;
-       const char *msgfile = NULL, *keyid = NULL;
+       char *msgfile = NULL;
+       const char *keyid = NULL;
        struct msg_arg msg = { .buf = STRBUF_INIT };
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
-       struct ref_filter filter;
+       struct ref_filter filter = REF_FILTER_INIT;
        struct ref_sorting *sorting;
        struct string_list sorting_options = STRING_LIST_INIT_DUP;
        struct ref_format format = REF_FORMAT_INIT;
@@ -471,6 +458,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
                OPT_MERGED(&filter, N_("print only tags that are merged")),
                OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
+               OPT_BOOL(0, "omit-empty",  &format.array_opts.omit_empty,
+                       N_("do not output a newline after empty formatted refs")),
                OPT_REF_SORT(&sorting_options),
                {
                        OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
@@ -485,13 +474,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        };
        int ret = 0;
        const char *only_in_list = NULL;
+       char *path = NULL;
 
        setup_ref_filter_porcelain_msg();
 
+       /*
+        * Try to set sort keys from config. If config does not set any,
+        * fall back on default (refname) sorting.
+        */
        git_config(git_tag_config, &sorting_options);
+       if (!sorting_options.nr)
+               string_list_append(&sorting_options, "refname");
 
        memset(&opt, 0, sizeof(opt));
-       memset(&filter, 0, sizeof(filter));
        filter.lines = -1;
        opt.sign = -1;
 
@@ -591,7 +586,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (argc > 2)
                die(_("too many arguments"));
 
-       if (get_oid(object_ref, &object))
+       if (repo_get_oid(the_repository, object_ref, &object))
                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
 
        if (strbuf_check_tag_ref(&ref, tag))
@@ -619,7 +614,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (create_tag_object) {
                if (force_sign_annotate && !annotate)
                        opt.sign = 1;
-               create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
+               path = git_pathdup("TAG_EDITMSG");
+               create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
+                          path);
        }
 
        transaction = ref_transaction_begin(&err);
@@ -627,19 +624,30 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
            ref_transaction_update(transaction, ref.buf, &object, &prev,
                                   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
                                   reflog_msg.buf, &err) ||
-           ref_transaction_commit(transaction, &err))
+           ref_transaction_commit(transaction, &err)) {
+               if (path)
+                       fprintf(stderr,
+                               _("The tag message has been left in %s\n"),
+                               path);
                die("%s", err.buf);
+       }
+       if (path) {
+               unlink_or_warn(path);
+               free(path);
+       }
        ref_transaction_free(transaction);
        if (force && !is_null_oid(&prev) && !oideq(&prev, &object))
                printf(_("Updated tag '%s' (was %s)\n"), tag,
-                      find_unique_abbrev(&prev, DEFAULT_ABBREV));
+                      repo_find_unique_abbrev(the_repository, &prev, DEFAULT_ABBREV));
 
 cleanup:
        ref_sorting_release(sorting);
+       ref_filter_clear(&filter);
        strbuf_release(&buf);
        strbuf_release(&ref);
        strbuf_release(&reflog_msg);
        strbuf_release(&msg.buf);
        strbuf_release(&err);
+       free(msgfile);
        return ret;
 }
index 58652229f273bf93e55b0ff5eef1421096cfe719..c129e2bb6cbffaf80898c8a9c1c4e49fdeb001a4 100644 (file)
@@ -1,6 +1,8 @@
 #include "builtin.h"
 #include "config.h"
-#include "object-store.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 
 static char *create_temp_file(struct object_id *oid)
 {
@@ -10,7 +12,7 @@ static char *create_temp_file(struct object_id *oid)
        unsigned long size;
        int fd;
 
-       buf = read_object_file(oid, &type, &size);
+       buf = repo_read_object_file(the_repository, oid, &type, &size);
        if (!buf || type != OBJ_BLOB)
                die("unable to read blob object %s", oid_to_hex(oid));
 
@@ -19,16 +21,17 @@ static char *create_temp_file(struct object_id *oid)
        if (write_in_full(fd, buf, size) < 0)
                die_errno("unable to write temp-file");
        close(fd);
+       free(buf);
        return path;
 }
 
-int cmd_unpack_file(int argc, const char **argv, const char *prefix)
+int cmd_unpack_file(int argc, const char **argv, const char *prefix UNUSED)
 {
        struct object_id oid;
 
        if (argc != 2 || !strcmp(argv[1], "-h"))
-               usage("git unpack-file <sha1>");
-       if (get_oid(argv[1], &oid))
+               usage("git unpack-file <blob>");
+       if (repo_get_oid(the_repository, argv[1], &oid))
                die("Not a valid object name %s", argv[1]);
 
        git_config(git_default_config, NULL);
index 43789b8ef294d8aa3fc9517f65712ac90e1fa60c..e0a701f2b383bbbd95fc1e7fef3fec30c3783c37 100644 (file)
@@ -1,16 +1,17 @@
 #include "builtin.h"
-#include "cache.h"
 #include "bulk-checkin.h"
 #include "config.h"
-#include "object-store.h"
+#include "environment.h"
+#include "gettext.h"
+#include "git-zlib.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "object.h"
 #include "delta.h"
 #include "pack.h"
 #include "blob.h"
-#include "commit.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
+#include "replace-object.h"
+#include "strbuf.h"
 #include "progress.h"
 #include "decorate.h"
 #include "fsck.h"
@@ -210,7 +211,8 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
  * Verify its reachability and validity recursively and write it out.
  */
 static int check_object(struct object *obj, enum object_type type,
-                       void *data, struct fsck_options *options)
+                       void *data UNUSED,
+                       struct fsck_options *options UNUSED)
 {
        struct obj_buffer *obj_buf;
 
@@ -442,7 +444,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
                delta_data = get_data(delta_size);
                if (!delta_data)
                        return;
-               if (has_object_file(&base_oid))
+               if (repo_has_object_file(the_repository, &base_oid))
                        ; /* Ok we have this one */
                else if (resolve_against_held(nr, &base_oid,
                                              delta_data, delta_size))
@@ -508,7 +510,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
        if (resolve_against_held(nr, &base_oid, delta_data, delta_size))
                return;
 
-       base = read_object_file(&base_oid, &type, &base_size);
+       base = repo_read_object_file(the_repository, &base_oid, &type,
+                                    &base_size);
        if (!base) {
                error("failed to read delta-pack base object %s",
                      oid_to_hex(&base_oid));
@@ -598,12 +601,13 @@ static void unpack_all(void)
                die("unresolved deltas left after unpacking");
 }
 
-int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED)
 {
        int i;
        struct object_id oid;
+       git_hash_ctx tmp_ctx;
 
-       read_replace_refs = 0;
+       disable_replace_refs();
 
        git_config(git_default_config, NULL);
 
@@ -662,7 +666,9 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
        the_hash_algo->init_fn(&ctx);
        unpack_all();
        the_hash_algo->update_fn(&ctx, buffer, offset);
-       the_hash_algo->final_oid_fn(&oid, &ctx);
+       the_hash_algo->init_fn(&tmp_ctx);
+       the_hash_algo->clone_fn(&tmp_ctx, &ctx);
+       the_hash_algo->final_oid_fn(&oid, &tmp_ctx);
        if (strict) {
                write_rest();
                if (fsck_finish(&fsck_options))
index b62249905f1b808fe94a86494765b368836c7eec..7bcaa1476c0fd5233475cac56a22db8c7714a570 100644 (file)
@@ -3,22 +3,32 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "builtin.h"
 #include "bulk-checkin.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "quote.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
-#include "builtin.h"
+#include "object-file.h"
 #include "refs.h"
 #include "resolve-undo.h"
 #include "parse-options.h"
 #include "pathspec.h"
 #include "dir.h"
+#include "read-cache.h"
+#include "repository.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "split-index.h"
+#include "symlinks.h"
 #include "fsmonitor.h"
+#include "write-or-die.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -237,16 +247,16 @@ done:
 static int mark_ce_flags(const char *path, int flag, int mark)
 {
        int namelen = strlen(path);
-       int pos = cache_name_pos(path, namelen);
+       int pos = index_name_pos(&the_index, path, namelen);
        if (0 <= pos) {
-               mark_fsmonitor_invalid(&the_index, active_cache[pos]);
+               mark_fsmonitor_invalid(&the_index, the_index.cache[pos]);
                if (mark)
-                       active_cache[pos]->ce_flags |= flag;
+                       the_index.cache[pos]->ce_flags |= flag;
                else
-                       active_cache[pos]->ce_flags &= ~flag;
-               active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+                       the_index.cache[pos]->ce_flags &= ~flag;
+               the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
                cache_tree_invalidate_path(&the_index, path);
-               active_cache_changed |= CE_ENTRY_CHANGED;
+               the_index.cache_changed |= CE_ENTRY_CHANGED;
                return 0;
        }
        return -1;
@@ -256,7 +266,7 @@ static int remove_one_path(const char *path)
 {
        if (!allow_remove)
                return error("%s: does not exist and --remove not passed", path);
-       if (remove_file_from_cache(path))
+       if (remove_file_from_index(&the_index, path))
                return error("%s: cannot remove from the index", path);
        return 0;
 }
@@ -281,7 +291,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
        struct cache_entry *ce;
 
        /* Was the old index entry already up-to-date? */
-       if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
+       if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0))
                return 0;
 
        ce = make_empty_cache_entry(&the_index, len);
@@ -298,7 +308,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
        }
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-       if (add_cache_entry(ce, option)) {
+       if (add_index_entry(&the_index, ce, option)) {
                discard_cache_entry(ce);
                return error("%s: cannot add to the index - missing --add option?", path);
        }
@@ -331,11 +341,11 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
 static int process_directory(const char *path, int len, struct stat *st)
 {
        struct object_id oid;
-       int pos = cache_name_pos(path, len);
+       int pos = index_name_pos(&the_index, path, len);
 
        /* Exact match: file or existing gitlink */
        if (pos >= 0) {
-               const struct cache_entry *ce = active_cache[pos];
+               const struct cache_entry *ce = the_index.cache[pos];
                if (S_ISGITLINK(ce->ce_mode)) {
 
                        /* Do nothing to the index if there is no HEAD! */
@@ -350,8 +360,8 @@ static int process_directory(const char *path, int len, struct stat *st)
 
        /* Inexact match: is there perhaps a subdirectory match? */
        pos = -pos-1;
-       while (pos < active_nr) {
-               const struct cache_entry *ce = active_cache[pos++];
+       while (pos < the_index.cache_nr) {
+               const struct cache_entry *ce = the_index.cache[pos++];
 
                if (strncmp(ce->name, path, len))
                        break;
@@ -381,8 +391,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
        if (has_symlink_leading_path(path, len))
                return error("'%s' is beyond a symbolic link", path);
 
-       pos = cache_name_pos(path, len);
-       ce = pos < 0 ? NULL : active_cache[pos];
+       pos = index_name_pos(&the_index, path, len);
+       ce = pos < 0 ? NULL : the_index.cache[pos];
        if (ce && ce_skip_worktree(ce)) {
                /*
                 * working directory version is assumed "good"
@@ -390,7 +400,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
                 * On the other hand, removing it from index should work
                 */
                if (!ignore_skip_worktree_entries && allow_remove &&
-                   remove_file_from_cache(path))
+                   remove_file_from_index(&the_index, path))
                        return error("%s: cannot remove from the index", path);
                return 0;
        }
@@ -429,7 +439,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
                ce->ce_flags |= CE_VALID;
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-       if (add_cache_entry(ce, option))
+       if (add_index_entry(&the_index, ce, option))
                return error("%s: cannot add to the index - missing --add option?",
                             path);
        report("add '%s'", path);
@@ -441,11 +451,11 @@ static void chmod_path(char flip, const char *path)
        int pos;
        struct cache_entry *ce;
 
-       pos = cache_name_pos(path, strlen(path));
+       pos = index_name_pos(&the_index, path, strlen(path));
        if (pos < 0)
                goto fail;
-       ce = active_cache[pos];
-       if (chmod_cache_entry(ce, flip) < 0)
+       ce = the_index.cache[pos];
+       if (chmod_index_entry(&the_index, ce, flip) < 0)
                goto fail;
 
        report("chmod %cx '%s'", flip, path);
@@ -488,7 +498,7 @@ static void update_one(const char *path)
        }
 
        if (force_remove) {
-               if (remove_file_from_cache(path))
+               if (remove_file_from_index(&the_index, path))
                        die("git update-index: unable to remove %s", path);
                report("remove '%s'", path);
                return;
@@ -571,7 +581,7 @@ static void read_index_info(int nul_term_line)
 
                if (!mode) {
                        /* mode == 0 means there is no such path -- remove */
-                       if (remove_file_from_cache(path_name))
+                       if (remove_file_from_index(&the_index, path_name))
                                die("git update-index: unable to remove %s",
                                    ptr);
                }
@@ -599,9 +609,6 @@ static const char * const update_index_usage[] = {
        NULL
 };
 
-static struct object_id head_oid;
-static struct object_id merge_head_oid;
-
 static struct cache_entry *read_one_ent(const char *which,
                                        struct object_id *ent, const char *path,
                                        int namelen, int stage)
@@ -632,84 +639,17 @@ static struct cache_entry *read_one_ent(const char *which,
 
 static int unresolve_one(const char *path)
 {
-       int namelen = strlen(path);
-       int pos;
-       int ret = 0;
-       struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
-
-       /* See if there is such entry in the index. */
-       pos = cache_name_pos(path, namelen);
-       if (0 <= pos) {
-               /* already merged */
-               pos = unmerge_cache_entry_at(pos);
-               if (pos < active_nr) {
-                       const struct cache_entry *ce = active_cache[pos];
-                       if (ce_stage(ce) &&
-                           ce_namelen(ce) == namelen &&
-                           !memcmp(ce->name, path, namelen))
-                               return 0;
-               }
-               /* no resolve-undo information; fall back */
-       } else {
-               /* If there isn't, either it is unmerged, or
-                * resolved as "removed" by mistake.  We do not
-                * want to do anything in the former case.
-                */
-               pos = -pos-1;
-               if (pos < active_nr) {
-                       const struct cache_entry *ce = active_cache[pos];
-                       if (ce_namelen(ce) == namelen &&
-                           !memcmp(ce->name, path, namelen)) {
-                               fprintf(stderr,
-                                       "%s: skipping still unmerged path.\n",
-                                       path);
-                               goto free_return;
-                       }
-               }
-       }
-
-       /* Grab blobs from given path from HEAD and MERGE_HEAD,
-        * stuff HEAD version in stage #2,
-        * stuff MERGE_HEAD version in stage #3.
-        */
-       ce_2 = read_one_ent("our", &head_oid, path, namelen, 2);
-       ce_3 = read_one_ent("their", &merge_head_oid, path, namelen, 3);
-
-       if (!ce_2 || !ce_3) {
-               ret = -1;
-               goto free_return;
-       }
-       if (oideq(&ce_2->oid, &ce_3->oid) &&
-           ce_2->ce_mode == ce_3->ce_mode) {
-               fprintf(stderr, "%s: identical in both, skipping.\n",
-                       path);
-               goto free_return;
-       }
-
-       remove_file_from_cache(path);
-       if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
-               error("%s: cannot add our version to the index.", path);
-               ret = -1;
-               goto free_return;
-       }
-       if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
-               return 0;
-       error("%s: cannot add their version to the index.", path);
-       ret = -1;
- free_return:
-       discard_cache_entry(ce_2);
-       discard_cache_entry(ce_3);
-       return ret;
-}
-
-static void read_head_pointers(void)
-{
-       if (read_ref("HEAD", &head_oid))
-               die("No HEAD -- no initial commit yet?");
-       if (read_ref("MERGE_HEAD", &merge_head_oid)) {
-               fprintf(stderr, "Not in the middle of a merge.\n");
-               exit(0);
-       }
+       struct string_list_item *item;
+       int res = 0;
+
+       if (!the_index.resolve_undo)
+               return res;
+       item = string_list_lookup(the_index.resolve_undo, path);
+       if (!item)
+               return res; /* no resolve-undo record for the path */
+       res = unmerge_index_entry(&the_index, path, item->util, 0);
+       FREE_AND_NULL(item->util);
+       return res;
 }
 
 static int do_unresolve(int ac, const char **av,
@@ -718,11 +658,6 @@ static int do_unresolve(int ac, const char **av,
        int i;
        int err = 0;
 
-       /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
-        * are not doing a merge, so exit with success status.
-        */
-       read_head_pointers();
-
        for (i = 1; i < ac; i++) {
                const char *arg = av[i];
                char *p = prefix_path(prefix, prefix_length, arg);
@@ -732,7 +667,7 @@ static int do_unresolve(int ac, const char **av,
        return err;
 }
 
-static int do_reupdate(int ac, const char **av,
+static int do_reupdate(const char **paths,
                       const char *prefix)
 {
        /* Read HEAD and run update-index on paths that are
@@ -741,10 +676,11 @@ static int do_reupdate(int ac, const char **av,
        int pos;
        int has_head = 1;
        struct pathspec pathspec;
+       struct object_id head_oid;
 
        parse_pathspec(&pathspec, 0,
                       PATHSPEC_PREFER_CWD,
-                      prefix, av + 1);
+                      prefix, paths);
 
        if (read_ref("HEAD", &head_oid))
                /* If there is no HEAD, that means it is an initial
@@ -752,8 +688,8 @@ static int do_reupdate(int ac, const char **av,
                 */
                has_head = 0;
  redo:
-       for (pos = 0; pos < active_nr; pos++) {
-               const struct cache_entry *ce = active_cache[pos];
+       for (pos = 0; pos < the_index.cache_nr; pos++) {
+               const struct cache_entry *ce = the_index.cache[pos];
                struct cache_entry *old = NULL;
                int save_nr;
                char *path;
@@ -782,12 +718,12 @@ static int do_reupdate(int ac, const char **av,
                 * path anymore, in which case, under 'allow_remove',
                 * or worse yet 'allow_replace', active_nr may decrease.
                 */
-               save_nr = active_nr;
+               save_nr = the_index.cache_nr;
                path = xstrdup(ce->name);
                update_one(path);
                free(path);
                discard_cache_entry(old);
-               if (save_nr != active_nr)
+               if (save_nr != the_index.cache_nr)
                        goto redo;
        }
        clear_pathspec(&pathspec);
@@ -802,18 +738,19 @@ struct refresh_params {
 static int refresh(struct refresh_params *o, unsigned int flag)
 {
        setup_work_tree();
-       read_cache();
-       *o->has_errors |= refresh_cache(o->flags | flag);
+       repo_read_index(the_repository);
+       *o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL,
+                                       NULL, NULL);
        if (has_racy_timestamp(&the_index)) {
                /*
                 * Even if nothing else has changed, updating the file
                 * increases the chance that racy timestamps become
                 * non-racy, helping future run-time performance.
                 * We do that even in case of "errors" returned by
-                * refresh_cache() as these are no actual errors.
+                * refresh_index() as these are no actual errors.
                 * cmd_status() does the same.
                 */
-               active_cache_changed |= SOMETHING_CHANGED;
+               the_index.cache_changed |= SOMETHING_CHANGED;
        }
        return 0;
 }
@@ -845,12 +782,12 @@ static int chmod_callback(const struct option *opt,
        return 0;
 }
 
-static int resolve_undo_clear_callback(const struct option *opt,
+static int resolve_undo_clear_callback(const struct option *opt UNUSED,
                                const char *arg, int unset)
 {
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
-       resolve_undo_clear();
+       resolve_undo_clear_index(&the_index);
        return 0;
 }
 
@@ -879,7 +816,7 @@ static int parse_new_style_cacheinfo(const char *arg,
 }
 
 static enum parse_opt_result cacheinfo_callback(
-       struct parse_opt_ctx_t *ctx, const struct option *opt,
+       struct parse_opt_ctx_t *ctx, const struct option *opt UNUSED,
        const char *arg, int unset)
 {
        struct object_id oid;
@@ -951,7 +888,7 @@ static enum parse_opt_result unresolve_callback(
        *has_errors = do_unresolve(ctx->argc, ctx->argv,
                                prefix, prefix ? strlen(prefix) : 0);
        if (*has_errors)
-               active_cache_changed = 0;
+               the_index.cache_changed = 0;
 
        ctx->argv += ctx->argc - 1;
        ctx->argc = 1;
@@ -970,9 +907,9 @@ static enum parse_opt_result reupdate_callback(
 
        /* consume remaining arguments. */
        setup_work_tree();
-       *has_errors = do_reupdate(ctx->argc, ctx->argv, prefix);
+       *has_errors = do_reupdate(ctx->argv + 1, prefix);
        if (*has_errors)
-               active_cache_changed = 0;
+               the_index.cache_changed = 0;
 
        ctx->argv += ctx->argc - 1;
        ctx->argc = 1;
@@ -1079,6 +1016,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                        resolve_undo_clear_callback),
                OPT_INTEGER(0, "index-version", &preferred_index_format,
                        N_("write index in this format")),
+               OPT_SET_INT(0, "show-index-version", &preferred_index_format,
+                           N_("report on-disk index format version"), -1),
                OPT_BOOL(0, "split-index", &split_index,
                        N_("enable or disable split index")),
                OPT_BOOL(0, "untracked-cache", &untracked_cache,
@@ -1109,11 +1048,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        the_repository->settings.command_requires_full_index = 0;
 
        /* we will diagnose later if it turns out that we need to update it */
-       newfd = hold_locked_index(&lock_file, 0);
+       newfd = repo_hold_locked_index(the_repository, &lock_file, 0);
        if (newfd < 0)
                lock_error = errno;
 
-       entries = read_cache();
+       entries = repo_read_index(the_repository);
        if (entries < 0)
                die("cache corrupted");
 
@@ -1171,15 +1110,20 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 
        getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
        if (preferred_index_format) {
-               if (preferred_index_format < INDEX_FORMAT_LB ||
-                   INDEX_FORMAT_UB < preferred_index_format)
+               if (preferred_index_format < 0) {
+                       printf(_("%d\n"), the_index.version);
+               } else if (preferred_index_format < INDEX_FORMAT_LB ||
+                          INDEX_FORMAT_UB < preferred_index_format) {
                        die("index-version %d not in range: %d..%d",
                            preferred_index_format,
                            INDEX_FORMAT_LB, INDEX_FORMAT_UB);
-
-               if (the_index.version != preferred_index_format)
-                       active_cache_changed |= SOMETHING_CHANGED;
-               the_index.version = preferred_index_format;
+               } else {
+                       if (the_index.version != preferred_index_format)
+                               the_index.cache_changed |= SOMETHING_CHANGED;
+                       report(_("index-version: was %d, set to %d"),
+                              the_index.version, preferred_index_format);
+                       the_index.version = preferred_index_format;
+               }
        }
 
        if (read_from_stdin) {
@@ -1290,7 +1234,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                report(_("fsmonitor disabled"));
        }
 
-       if (active_cache_changed || force_write) {
+       if (the_index.cache_changed || force_write) {
                if (newfd < 0) {
                        if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);
index a84e7b47a20620b421b9ab3782cdad4e52bc9e90..61338a01ecfc0ee4f029215149651bb9ac03da1e 100644 (file)
@@ -1,10 +1,12 @@
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
+#include "gettext.h"
+#include "hash.h"
 #include "refs.h"
-#include "builtin.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "quote.h"
-#include "strvec.h"
+#include "repository.h"
 
 static const char * const git_update_ref_usage[] = {
        N_("git update-ref [<options>] -d <refname> [<old-val>]"),
@@ -116,7 +118,7 @@ static int parse_next_oid(const char **next, const char *end,
                (*next)++;
                *next = parse_arg(*next, &arg);
                if (arg.len) {
-                       if (get_oid(arg.buf, oid))
+                       if (repo_get_oid(the_repository, arg.buf, oid))
                                goto invalid;
                } else {
                        /* Without -z, an empty value means all zeros: */
@@ -134,7 +136,7 @@ static int parse_next_oid(const char **next, const char *end,
                *next += arg.len;
 
                if (arg.len) {
-                       if (get_oid(arg.buf, oid))
+                       if (repo_get_oid(the_repository, arg.buf, oid))
                                goto invalid;
                } else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
                        /* With -z, treat an empty value as all zeros: */
@@ -308,8 +310,8 @@ static void report_ok(const char *command)
        fflush(stdout);
 }
 
-static void parse_cmd_option(struct ref_transaction *transaction,
-                            const char *next, const char *end)
+static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
+                            const char *next, const char *end UNUSED)
 {
        const char *rest;
        if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
@@ -318,8 +320,8 @@ static void parse_cmd_option(struct ref_transaction *transaction,
                die("option unknown: %s", next);
 }
 
-static void parse_cmd_start(struct ref_transaction *transaction,
-                           const char *next, const char *end)
+static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
+                           const char *next, const char *end UNUSED)
 {
        if (*next != line_termination)
                die("start: extra input: %s", next);
@@ -327,7 +329,7 @@ static void parse_cmd_start(struct ref_transaction *transaction,
 }
 
 static void parse_cmd_prepare(struct ref_transaction *transaction,
-                             const char *next, const char *end)
+                             const char *next, const char *end UNUSED)
 {
        struct strbuf error = STRBUF_INIT;
        if (*next != line_termination)
@@ -338,7 +340,7 @@ static void parse_cmd_prepare(struct ref_transaction *transaction,
 }
 
 static void parse_cmd_abort(struct ref_transaction *transaction,
-                           const char *next, const char *end)
+                           const char *next, const char *end UNUSED)
 {
        struct strbuf error = STRBUF_INIT;
        if (*next != line_termination)
@@ -349,7 +351,7 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
 }
 
 static void parse_cmd_commit(struct ref_transaction *transaction,
-                            const char *next, const char *end)
+                            const char *next, const char *end UNUSED)
 {
        struct strbuf error = STRBUF_INIT;
        if (*next != line_termination)
@@ -549,7 +551,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                refname = argv[0];
                value = argv[1];
                oldval = argv[2];
-               if (get_oid(value, &oid))
+               if (repo_get_oid(the_repository, value, &oid))
                        die("%s: not a valid SHA1", value);
        }
 
@@ -560,7 +562,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                         * must not already exist:
                         */
                        oidclr(&oldoid);
-               else if (get_oid(oldval, &oldoid))
+               else if (repo_get_oid(the_repository, oldval, &oldoid))
                        die("%s: not a valid old SHA1", oldval);
        }
 
index 880fffec58750ea52b15618341f6189bd5a878db..1dc3971edeb3837a1479ce519197880d28ba7699 100644 (file)
@@ -1,10 +1,11 @@
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "server-info.h"
 
 static const char * const update_server_info_usage[] = {
-       "git update-server-info [--force]",
+       "git update-server-info [-f | --force]",
        NULL
 };
 
index 98d028dae679080af9785d57bc83525d5cd2872f..1b09e5e1aa3f08e91bf6de731b111864ef405573 100644 (file)
@@ -1,16 +1,17 @@
 /*
  * Copyright (c) 2006 Franck Bui-Huu
  */
-#include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "path.h"
 #include "pkt-line.h"
 #include "sideband.h"
+#include "repository.h"
 #include "run-command.h"
 #include "strvec.h"
 
 static const char upload_archive_usage[] =
-       "git upload-archive <repo>";
+       "git upload-archive <repository>";
 
 static const char deadchild[] =
 "git upload-archive: archiver died with error";
@@ -79,6 +80,8 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
 {
        struct child_process writer = CHILD_PROCESS_INIT;
 
+       BUG_ON_NON_EMPTY_PREFIX(prefix);
+
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(upload_archive_usage);
 
index 125af53885f89fbd4b691f5d9463774b12a66559..9b021ef026c28c97ece12b924833dff87c15a103 100644 (file)
@@ -1,14 +1,17 @@
-#include "cache.h"
 #include "builtin.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "pkt-line.h"
 #include "parse-options.h"
+#include "path.h"
 #include "protocol.h"
+#include "replace-object.h"
 #include "upload-pack.h"
 #include "serve.h"
 
 static const char * const upload_pack_usage[] = {
-       N_("git upload-pack [<options>] <dir>"),
+       N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+          "                [--advertise-refs] <directory>"),
        NULL
 };
 
@@ -33,7 +36,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
        };
 
        packet_trace_identity("upload-pack");
-       read_replace_refs = 0;
+       disable_replace_refs();
 
        argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
 
index 491db2742926dbd6fd08f681817821addcfafab7..8cf7dd9e2e5cef1d9238c22ed64ea07440d31528 100644 (file)
  * Copyright (C) Eric Biederman, 2005
  */
 #include "builtin.h"
+#include "attr.h"
 #include "config.h"
+#include "editor.h"
+#include "ident.h"
+#include "pager.h"
 #include "refs.h"
+#include "path.h"
+#include "strbuf.h"
 
 static const char var_usage[] = "git var (-l | <variable>)";
 
-static const char *editor(int flag)
+static char *committer(int ident_flag)
 {
-       const char *pgm = git_editor();
+       return xstrdup_or_null(git_committer_info(ident_flag));
+}
 
-       if (!pgm && flag & IDENT_STRICT)
-               die("Terminal is dumb, but EDITOR unset");
+static char *author(int ident_flag)
+{
+       return xstrdup_or_null(git_author_info(ident_flag));
+}
 
-       return pgm;
+static char *editor(int ident_flag UNUSED)
+{
+       return xstrdup_or_null(git_editor());
 }
 
-static const char *pager(int flag)
+static char *sequence_editor(int ident_flag UNUSED)
+{
+       return xstrdup_or_null(git_sequence_editor());
+}
+
+static char *pager(int ident_flag UNUSED)
 {
        const char *pgm = git_pager(1);
 
        if (!pgm)
                pgm = "cat";
-       return pgm;
+       return xstrdup(pgm);
+}
+
+static char *default_branch(int ident_flag UNUSED)
+{
+       return xstrdup_or_null(git_default_branch_name(1));
+}
+
+static char *shell_path(int ident_flag UNUSED)
+{
+       return xstrdup(SHELL_PATH);
+}
+
+static char *git_attr_val_system(int ident_flag UNUSED)
+{
+       if (git_attr_system_is_enabled()) {
+               char *file = xstrdup(git_attr_system_file());
+               normalize_path_copy(file, file);
+               return file;
+       }
+       return NULL;
 }
 
-static const char *default_branch(int flag)
+static char *git_attr_val_global(int ident_flag UNUSED)
 {
-       return git_default_branch_name(1);
+       char *file = xstrdup_or_null(git_attr_global_file());
+       if (file) {
+               normalize_path_copy(file, file);
+               return file;
+       }
+       return NULL;
+}
+
+static char *git_config_val_system(int ident_flag UNUSED)
+{
+       if (git_config_system()) {
+               char *file = git_system_config();
+               normalize_path_copy(file, file);
+               return file;
+       }
+       return NULL;
+}
+
+static char *git_config_val_global(int ident_flag UNUSED)
+{
+       struct strbuf buf = STRBUF_INIT;
+       char *user, *xdg;
+       size_t unused;
+
+       git_global_config(&user, &xdg);
+       if (xdg && *xdg) {
+               normalize_path_copy(xdg, xdg);
+               strbuf_addf(&buf, "%s\n", xdg);
+       }
+       if (user && *user) {
+               normalize_path_copy(user, user);
+               strbuf_addf(&buf, "%s\n", user);
+       }
+       free(xdg);
+       free(user);
+       strbuf_trim_trailing_newline(&buf);
+       if (buf.len == 0) {
+               strbuf_release(&buf);
+               return NULL;
+       }
+       return strbuf_detach(&buf, &unused);
 }
 
 struct git_var {
        const char *name;
-       const char *(*read)(int);
+       char *(*read)(int);
+       int multivalued;
 };
 static struct git_var git_vars[] = {
-       { "GIT_COMMITTER_IDENT", git_committer_info },
-       { "GIT_AUTHOR_IDENT",   git_author_info },
-       { "GIT_EDITOR", editor },
-       { "GIT_PAGER", pager },
-       { "GIT_DEFAULT_BRANCH", default_branch },
-       { "", NULL },
+       {
+               .name = "GIT_COMMITTER_IDENT",
+               .read = committer,
+       },
+       {
+               .name = "GIT_AUTHOR_IDENT",
+               .read = author,
+       },
+       {
+               .name = "GIT_EDITOR",
+               .read = editor,
+       },
+       {
+               .name = "GIT_SEQUENCE_EDITOR",
+               .read = sequence_editor,
+       },
+       {
+               .name = "GIT_PAGER",
+               .read = pager,
+       },
+       {
+               .name = "GIT_DEFAULT_BRANCH",
+               .read = default_branch,
+       },
+       {
+               .name = "GIT_SHELL_PATH",
+               .read = shell_path,
+       },
+       {
+               .name = "GIT_ATTR_SYSTEM",
+               .read = git_attr_val_system,
+       },
+       {
+               .name = "GIT_ATTR_GLOBAL",
+               .read = git_attr_val_global,
+       },
+       {
+               .name = "GIT_CONFIG_SYSTEM",
+               .read = git_config_val_system,
+       },
+       {
+               .name = "GIT_CONFIG_GLOBAL",
+               .read = git_config_val_global,
+               .multivalued = 1,
+       },
+       {
+               .name = "",
+               .read = NULL,
+       },
 };
 
 static void list_vars(void)
 {
        struct git_var *ptr;
-       const char *val;
+       char *val;
 
        for (ptr = git_vars; ptr->read; ptr++)
-               if ((val = ptr->read(0)))
-                       printf("%s=%s\n", ptr->name, val);
+               if ((val = ptr->read(0))) {
+                       if (ptr->multivalued && *val) {
+                               struct string_list list = STRING_LIST_INIT_DUP;
+                               int i;
+
+                               string_list_split(&list, val, '\n', -1);
+                               for (i = 0; i < list.nr; i++)
+                                       printf("%s=%s\n", ptr->name, list.items[i].string);
+                               string_list_clear(&list, 0);
+                       } else {
+                               printf("%s=%s\n", ptr->name, val);
+                       }
+                       free(val);
+               }
 }
 
-static const char *read_var(const char *var)
+static const struct git_var *get_git_var(const char *var)
 {
        struct git_var *ptr;
-       const char *val;
-       val = NULL;
        for (ptr = git_vars; ptr->read; ptr++) {
                if (strcmp(var, ptr->name) == 0) {
-                       val = ptr->read(IDENT_STRICT);
-                       break;
+                       return ptr;
                }
        }
-       return val;
+       return NULL;
 }
 
-static int show_config(const char *var, const char *value, void *cb)
+static int show_config(const char *var, const char *value,
+                      const struct config_context *ctx, void *cb)
 {
        if (value)
                printf("%s=%s\n", var, value);
        else
                printf("%s\n", var);
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
-int cmd_var(int argc, const char **argv, const char *prefix)
+int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
 {
-       const char *val = NULL;
+       const struct git_var *git_var;
+       char *val;
+
        if (argc != 2)
                usage(var_usage);
 
@@ -91,11 +223,17 @@ int cmd_var(int argc, const char **argv, const char *prefix)
                return 0;
        }
        git_config(git_default_config, NULL);
-       val = read_var(argv[1]);
-       if (!val)
+
+       git_var = get_git_var(argv[1]);
+       if (!git_var)
                usage(var_usage);
 
+       val = git_var->read(IDENT_STRICT);
+       if (!val)
+               return 1;
+
        printf("%s\n", val);
+       free(val);
 
        return 0;
 }
index 40c69a0bedde5be02af557fd0c92efc1d2c857e5..0d2b9aea2aedca39b18d15bc7a8719669d63f1d3 100644 (file)
@@ -5,18 +5,17 @@
  *
  * Based on git-verify-tag
  */
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
-#include "object-store.h"
+#include "config.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "repository.h"
 #include "commit.h"
-#include "run-command.h"
 #include "parse-options.h"
 #include "gpg-interface.h"
 
 static const char * const verify_commit_usage[] = {
-               N_("git verify-commit [-v | --verbose] <commit>..."),
+               N_("git verify-commit [-v | --verbose] [--raw] <commit>..."),
                NULL
 };
 
@@ -39,7 +38,7 @@ static int verify_commit(const char *name, unsigned flags)
        struct object_id oid;
        struct object *obj;
 
-       if (get_oid(name, &oid))
+       if (repo_get_oid(the_repository, name, &oid))
                return error("commit '%s' not found.", name);
 
        obj = parse_object(the_repository, &oid);
@@ -52,14 +51,6 @@ static int verify_commit(const char *name, unsigned flags)
        return run_gpg_verify((struct commit *)obj, flags);
 }
 
-static int git_verify_commit_config(const char *var, const char *value, void *cb)
-{
-       int status = git_gpg_config(var, value, cb);
-       if (status)
-               return status;
-       return git_default_config(var, value, cb);
-}
-
 int cmd_verify_commit(int argc, const char **argv, const char *prefix)
 {
        int i = 1, verbose = 0, had_error = 0;
@@ -70,7 +61,7 @@ int cmd_verify_commit(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       git_config(git_verify_commit_config, NULL);
+       git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, verify_commit_options,
                             verify_commit_usage, PARSE_OPT_KEEP_ARGV0);
index 05c52135946b77ba6014f69b203d3e979df94ffd..011dddd2dc329292c363fbcfb10b71c218ffa034 100644 (file)
@@ -1,8 +1,9 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "gettext.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "strbuf.h"
 
 #define VERIFY_PACK_VERBOSE 01
 #define VERIFY_PACK_STAT_ONLY 02
@@ -56,7 +57,7 @@ static int verify_one_pack(const char *path, unsigned int flags, const char *has
 }
 
 static const char * const verify_pack_usage[] = {
-       N_("git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."),
+       N_("git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."),
        NULL
 };
 
index f45136a06ba76077891d9e5fae545ee8c297ddff..c731e2f87b4ee35e4910e3a8c11e18b8e68e58c1 100644 (file)
@@ -5,28 +5,20 @@
  *
  * Based on git-verify-tag.sh
  */
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "gettext.h"
 #include "tag.h"
-#include "run-command.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "gpg-interface.h"
 #include "ref-filter.h"
 
 static const char * const verify_tag_usage[] = {
-               N_("git verify-tag [-v | --verbose] [--format=<format>] <tag>..."),
+               N_("git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."),
                NULL
 };
 
-static int git_verify_tag_config(const char *var, const char *value, void *cb)
-{
-       int status = git_gpg_config(var, value, cb);
-       if (status)
-               return status;
-       return git_default_config(var, value, cb);
-}
-
 int cmd_verify_tag(int argc, const char **argv, const char *prefix)
 {
        int i = 1, verbose = 0, had_error = 0;
@@ -39,7 +31,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       git_config(git_verify_tag_config, NULL);
+       git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, verify_tag_options,
                             verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
@@ -60,7 +52,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
                struct object_id oid;
                const char *name = argv[i++];
 
-               if (get_oid(name, &oid)) {
+               if (repo_get_oid(the_repository, name, &oid)) {
                        had_error = !!error("tag '%s' not found.", name);
                        continue;
                }
index c6710b2552006df0a3bc70fcddcde81fa2476783..cac83a94197dd60054d5266de8cc1b5186ea2b35 100644 (file)
@@ -1,12 +1,23 @@
-#include "cache.h"
+#include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
 #include "checkout.h"
 #include "config.h"
-#include "builtin.h"
+#include "copy.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
 #include "strvec.h"
 #include "branch.h"
+#include "read-cache-ll.h"
 #include "refs.h"
+#include "remote.h"
+#include "repository.h"
 #include "run-command.h"
 #include "hook.h"
 #include "sigchain.h"
 #include "worktree.h"
 #include "quote.h"
 
-static const char * const worktree_usage[] = {
-       N_("git worktree add [<options>] <path> [<commit-ish>]"),
-       N_("git worktree list [<options>]"),
-       N_("git worktree lock [<options>] <path>"),
-       N_("git worktree move <worktree> <new-path>"),
-       N_("git worktree prune [<options>]"),
-       N_("git worktree remove [<options>] <worktree>"),
-       N_("git worktree repair [<path>...]"),
-       N_("git worktree unlock <path>"),
+#define BUILTIN_WORKTREE_ADD_USAGE \
+       N_("git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n" \
+          "                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]")
+
+#define BUILTIN_WORKTREE_LIST_USAGE \
+       N_("git worktree list [-v | --porcelain [-z]]")
+#define BUILTIN_WORKTREE_LOCK_USAGE \
+       N_("git worktree lock [--reason <string>] <worktree>")
+#define BUILTIN_WORKTREE_MOVE_USAGE \
+       N_("git worktree move <worktree> <new-path>")
+#define BUILTIN_WORKTREE_PRUNE_USAGE \
+       N_("git worktree prune [-n] [-v] [--expire <expire>]")
+#define BUILTIN_WORKTREE_REMOVE_USAGE \
+       N_("git worktree remove [-f] <worktree>")
+#define BUILTIN_WORKTREE_REPAIR_USAGE \
+       N_("git worktree repair [<path>...]")
+#define BUILTIN_WORKTREE_UNLOCK_USAGE \
+       N_("git worktree unlock <worktree>")
+
+#define WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT \
+       _("No possible source branch, inferring '--orphan'")
+
+#define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \
+       _("If you meant to create a worktree containing a new unborn branch\n" \
+       "(branch with no commits) for this repository, you can do so\n" \
+       "using the --orphan flag:\n" \
+       "\n" \
+       "    git worktree add --orphan -b %s %s\n")
+
+#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \
+       _("If you meant to create a worktree containing a new unborn branch\n" \
+       "(branch with no commits) for this repository, you can do so\n" \
+       "using the --orphan flag:\n" \
+       "\n" \
+       "    git worktree add --orphan %s\n")
+
+static const char * const git_worktree_usage[] = {
+       BUILTIN_WORKTREE_ADD_USAGE,
+       BUILTIN_WORKTREE_LIST_USAGE,
+       BUILTIN_WORKTREE_LOCK_USAGE,
+       BUILTIN_WORKTREE_MOVE_USAGE,
+       BUILTIN_WORKTREE_PRUNE_USAGE,
+       BUILTIN_WORKTREE_REMOVE_USAGE,
+       BUILTIN_WORKTREE_REPAIR_USAGE,
+       BUILTIN_WORKTREE_UNLOCK_USAGE,
+       NULL
+};
+
+static const char * const git_worktree_add_usage[] = {
+       BUILTIN_WORKTREE_ADD_USAGE,
+       NULL,
+};
+
+static const char * const git_worktree_list_usage[] = {
+       BUILTIN_WORKTREE_LIST_USAGE,
+       NULL
+};
+
+static const char * const git_worktree_lock_usage[] = {
+       BUILTIN_WORKTREE_LOCK_USAGE,
+       NULL
+};
+
+static const char * const git_worktree_move_usage[] = {
+       BUILTIN_WORKTREE_MOVE_USAGE,
+       NULL
+};
+
+static const char * const git_worktree_prune_usage[] = {
+       BUILTIN_WORKTREE_PRUNE_USAGE,
+       NULL
+};
+
+static const char * const git_worktree_remove_usage[] = {
+       BUILTIN_WORKTREE_REMOVE_USAGE,
+       NULL
+};
+
+static const char * const git_worktree_repair_usage[] = {
+       BUILTIN_WORKTREE_REPAIR_USAGE,
+       NULL
+};
+
+static const char * const git_worktree_unlock_usage[] = {
+       BUILTIN_WORKTREE_UNLOCK_USAGE,
        NULL
 };
 
@@ -32,6 +119,7 @@ struct add_opts {
        int detach;
        int quiet;
        int checkout;
+       int orphan;
        const char *keep_locked;
 };
 
@@ -40,14 +128,15 @@ static int verbose;
 static int guess_remote;
 static timestamp_t expire;
 
-static int git_worktree_config(const char *var, const char *value, void *cb)
+static int git_worktree_config(const char *var, const char *value,
+                              const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "worktree.guessremote")) {
                guess_remote = git_config_bool(var, value);
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static int delete_git_dir(const char *id)
@@ -115,7 +204,7 @@ static void prune_worktrees(void)
 {
        struct strbuf reason = STRBUF_INIT;
        struct strbuf main_path = STRBUF_INIT;
-       struct string_list kept = STRING_LIST_INIT_NODUP;
+       struct string_list kept = STRING_LIST_INIT_DUP;
        DIR *dir = opendir(git_path("worktrees"));
        struct dirent *d;
        if (!dir)
@@ -126,14 +215,14 @@ static void prune_worktrees(void)
                if (should_prune_worktree(d->d_name, &reason, &path, expire))
                        prune_worktree(d->d_name, reason.buf);
                else if (path)
-                       string_list_append(&kept, path)->util = xstrdup(d->d_name);
+                       string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
        }
        closedir(dir);
 
        strbuf_add_absolute_path(&main_path, get_git_common_dir());
        /* massage main worktree absolute path to match 'gitdir' content */
        strbuf_strip_suffix(&main_path, "/.");
-       string_list_append(&kept, strbuf_detach(&main_path, NULL));
+       string_list_append_nodup(&kept, strbuf_detach(&main_path, NULL));
        prune_dups(&kept);
        string_list_clear(&kept, 1);
 
@@ -153,9 +242,10 @@ static int prune(int ac, const char **av, const char *prefix)
        };
 
        expire = TIME_MAX;
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_prune_usage,
+                          0);
        if (ac)
-               usage_with_options(worktree_usage, options);
+               usage_with_options(git_worktree_prune_usage, options);
        prune_worktrees();
        return 0;
 }
@@ -260,7 +350,6 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
 
        if (file_exists(from_file)) {
                struct config_set cs = { { 0 } };
-               const char *core_worktree;
                int bare;
 
                if (safe_create_leading_directories(to_file) ||
@@ -279,7 +368,7 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
                                to_file, "core.bare", NULL, "true", 0))
                        error(_("failed to unset '%s' in '%s'"),
                                "core.bare", to_file);
-               if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
+               if (!git_configset_get(&cs, "core.worktree") &&
                        git_config_set_in_file_gently(to_file,
                                                        "core.worktree", NULL))
                        error(_("failed to unset '%s' in '%s'"),
@@ -305,6 +394,22 @@ static int checkout_worktree(const struct add_opts *opts,
        return run_command(&cp);
 }
 
+static int make_worktree_orphan(const char * ref, const struct add_opts *opts,
+                               struct strvec *child_env)
+{
+       struct strbuf symref = STRBUF_INIT;
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       validate_new_branchname(ref, &symref, 0);
+       strvec_pushl(&cp.args, "symbolic-ref", "HEAD", symref.buf, NULL);
+       if (opts->quiet)
+               strvec_push(&cp.args, "--quiet");
+       strvec_pushv(&cp.env, child_env->v);
+       strbuf_release(&symref);
+       cp.git_cmd = 1;
+       return run_command(&cp);
+}
+
 static int add_worktree(const char *path, const char *refname,
                        const struct add_opts *opts)
 {
@@ -334,7 +439,7 @@ static int add_worktree(const char *path, const char *refname,
                        die_if_checked_out(symref.buf, 0);
        }
        commit = lookup_commit_reference_by_name(refname);
-       if (!commit)
+       if (!commit && !opts->orphan)
                die(_("invalid reference: %s"), refname);
 
        name = worktree_basename(path, &len);
@@ -416,17 +521,17 @@ static int add_worktree(const char *path, const char *refname,
         * values from the current worktree into the new one, that way the
         * new worktree behaves the same as this one.
         */
-       if (repository_format_worktree_config)
+       if (the_repository->repository_format_worktree_config)
                copy_filtered_worktree_config(sb_repo.buf);
 
        strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
        strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
        cp.git_cmd = 1;
 
-       if (!is_branch)
+       if (!is_branch && commit) {
                strvec_pushl(&cp.args, "update-ref", "HEAD",
                             oid_to_hex(&commit->object.oid), NULL);
-       else {
+       else {
                strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
                             symref.buf, NULL);
                if (opts->quiet)
@@ -438,6 +543,10 @@ static int add_worktree(const char *path, const char *refname,
        if (ret)
                goto done;
 
+       if (opts->orphan &&
+           (ret = make_worktree_orphan(refname, opts, &child_env)))
+               goto done;
+
        if (opts->checkout &&
            (ret = checkout_worktree(opts, &child_env)))
                goto done;
@@ -457,7 +566,7 @@ done:
         * Hook failure does not warrant worktree deletion, so run hook after
         * is_junk is cleared, but do return appropriate code when hook fails.
         */
-       if (!ret && opts->checkout) {
+       if (!ret && opts->checkout && !opts->orphan) {
                struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
 
                strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
@@ -493,7 +602,7 @@ static void print_preparing_worktree_line(int detach,
                else
                        fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"),
                                  new_branch,
-                                 find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+                                 repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
        } else if (new_branch) {
                fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
        } else {
@@ -505,14 +614,131 @@ static void print_preparing_worktree_line(int detach,
                else {
                        struct commit *commit = lookup_commit_reference_by_name(branch);
                        if (!commit)
-                               die(_("invalid reference: %s"), branch);
+                               BUG(_("unreachable: invalid reference: %s"), branch);
                        fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
-                                 find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+                                 repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
                }
                strbuf_release(&s);
        }
 }
 
+/**
+ * Callback to short circuit iteration over refs on the first reference
+ * corresponding to a valid oid.
+ *
+ * Returns 0 on failure and non-zero on success.
+ */
+static int first_valid_ref(const char *refname UNUSED,
+                          const struct object_id *oid UNUSED,
+                          int flags UNUSED,
+                          void *cb_data UNUSED)
+{
+       return 1;
+}
+
+/**
+ * Verifies HEAD and determines whether there exist any valid local references.
+ *
+ * - Checks whether HEAD points to a valid reference.
+ *
+ * - Checks whether any valid local branches exist.
+ *
+ * - Emits a warning if there exist any valid branches but HEAD does not point
+ *   to a valid reference.
+ *
+ * Returns 1 if any of the previous checks are true, otherwise returns 0.
+ */
+static int can_use_local_refs(const struct add_opts *opts)
+{
+       if (head_ref(first_valid_ref, NULL)) {
+               return 1;
+       } else if (for_each_branch_ref(first_valid_ref, NULL)) {
+               if (!opts->quiet) {
+                       struct strbuf path = STRBUF_INIT;
+                       struct strbuf contents = STRBUF_INIT;
+
+                       strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+                       strbuf_addstr(&path, "/HEAD");
+                       strbuf_read_file(&contents, path.buf, 64);
+                       strbuf_stripspace(&contents, 0);
+                       strbuf_strip_suffix(&contents, "\n");
+
+                       warning(_("HEAD points to an invalid (or orphaned) reference.\n"
+                                 "HEAD path: '%s'\n"
+                                 "HEAD contents: '%s'"),
+                                 path.buf, contents.buf);
+                       strbuf_release(&path);
+                       strbuf_release(&contents);
+               }
+               return 1;
+       }
+       return 0;
+}
+
+/**
+ * Reports whether the necessary flags were set and whether the repository has
+ * remote references to attempt DWIM tracking of upstream branches.
+ *
+ * 1. Checks that `--guess-remote` was used or `worktree.guessRemote = true`.
+ *
+ * 2. Checks whether any valid remote branches exist.
+ *
+ * 3. Checks that there exists at least one remote and emits a warning/error
+ *    if both checks 1. and 2. are false (can be bypassed with `--force`).
+ *
+ * Returns 1 if checks 1. and 2. are true, otherwise 0.
+ */
+static int can_use_remote_refs(const struct add_opts *opts)
+{
+       if (!guess_remote) {
+               return 0;
+       } else if (for_each_remote_ref(first_valid_ref, NULL)) {
+               return 1;
+       } else if (!opts->force && remote_get(NULL)) {
+               die(_("No local or remote refs exist despite at least one remote\n"
+                     "present, stopping; use 'add -f' to override or fetch a remote first"));
+       }
+       return 0;
+}
+
+/**
+ * Determines whether `--orphan` should be inferred in the evaluation of
+ * `worktree add path/` or `worktree add -b branch path/` and emits an error
+ * if the supplied arguments would produce an illegal combination when the
+ * `--orphan` flag is included.
+ *
+ * `opts` and `opt_track` contain the other options & flags supplied to the
+ * command.
+ *
+ * remote determines whether to check `can_use_remote_refs()` or not. This
+ * is primarily to differentiate between the basic `add` DWIM and `add -b`.
+ *
+ * Returns 1 when inferring `--orphan`, 0 otherwise, and emits an error when
+ * `--orphan` is inferred but doing so produces an illegal combination of
+ * options and flags. Additionally produces an error when remote refs are
+ * checked and the repo is in a state that looks like the user added a remote
+ * but forgot to fetch (and did not override the warning with -f).
+ */
+static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
+{
+       if (can_use_local_refs(opts)) {
+               return 0;
+       } else if (remote && can_use_remote_refs(opts)) {
+               return 0;
+       } else if (!opts->quiet) {
+               fprintf_ln(stderr, WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT);
+       }
+
+       if (opt_track) {
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--track");
+       } else if (!opts->checkout) {
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--no-checkout");
+       }
+       return 1;
+}
+
 static const char *dwim_branch(const char *path, const char **new_branch)
 {
        int n;
@@ -549,6 +775,7 @@ static int add(int ac, const char **av, const char *prefix)
        const char *opt_track = NULL;
        const char *lock_reason = NULL;
        int keep_locked = 0;
+       int used_new_branch_options;
        struct option options[] = {
                OPT__FORCE(&opts.force,
                           N_("checkout <branch> even if already checked out in other worktree"),
@@ -557,6 +784,7 @@ static int add(int ac, const char **av, const char *prefix)
                           N_("create a new branch")),
                OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
                           N_("create or reset a branch")),
+               OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn branch")),
                OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
                OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
                OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")),
@@ -570,12 +798,25 @@ static int add(int ac, const char **av, const char *prefix)
                         N_("try to match the new branch name with a remote-tracking branch")),
                OPT_END()
        };
+       int ret;
 
        memset(&opts, 0, sizeof(opts));
        opts.checkout = 1;
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0);
        if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
                die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
+       if (opts.detach && opts.orphan)
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--detach");
+       if (opts.orphan && opt_track)
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--track");
+       if (opts.orphan && !opts.checkout)
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--no-checkout");
+       if (opts.orphan && ac == 2)
+               die(_("option '%s' and commit-ish cannot be used together"),
+                   "--orphan");
        if (lock_reason && !keep_locked)
                die(_("the option '%s' requires '%s'"), "--reason", "--lock");
        if (lock_reason)
@@ -584,10 +825,11 @@ static int add(int ac, const char **av, const char *prefix)
                opts.keep_locked = _("added with --lock");
 
        if (ac < 1 || ac > 2)
-               usage_with_options(worktree_usage, options);
+               usage_with_options(git_worktree_add_usage, options);
 
        path = prefix_filename(prefix, av[0]);
        branch = ac < 2 ? "HEAD" : av[1];
+       used_new_branch_options = new_branch || new_branch_force;
 
        if (!strcmp(branch, "-"))
                branch = "@{-1}";
@@ -604,13 +846,28 @@ static int add(int ac, const char **av, const char *prefix)
                strbuf_release(&symref);
        }
 
-       if (ac < 2 && !new_branch && !opts.detach) {
+       if (opts.orphan && !new_branch) {
+               int n;
+               const char *s = worktree_basename(path, &n);
+               new_branch = xstrndup(s, n);
+       } else if (opts.orphan) {
+               // No-op
+       } else if (opts.detach) {
+               // Check HEAD
+               if (!strcmp(branch, "HEAD"))
+                       can_use_local_refs(&opts);
+       } else if (ac < 2 && new_branch) {
+               // DWIM: Infer --orphan when repo has no refs.
+               opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
+       } else if (ac < 2) {
+               // DWIM: Guess branch name from path.
                const char *s = dwim_branch(path, &new_branch);
                if (s)
                        branch = s;
-       }
 
-       if (ac == 2 && !new_branch && !opts.detach) {
+               // DWIM: Infer --orphan when repo has no refs.
+               opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);
+       } else if (ac == 2) {
                struct object_id oid;
                struct commit *commit;
                const char *remote;
@@ -623,11 +880,31 @@ static int add(int ac, const char **av, const char *prefix)
                                branch = remote;
                        }
                }
+
+               if (!strcmp(branch, "HEAD"))
+                       can_use_local_refs(&opts);
+
+       }
+
+       if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {
+               int attempt_hint = !opts.quiet && (ac < 2);
+               if (attempt_hint && used_new_branch_options) {
+                       advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+                               WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT,
+                               new_branch, path);
+               } else if (attempt_hint) {
+                       advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+                               WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT, path);
+               }
+               die(_("invalid reference: %s"), branch);
        }
+
        if (!opts.quiet)
                print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force);
 
-       if (new_branch) {
+       if (opts.orphan) {
+               branch = new_branch;
+       } else if (new_branch) {
                struct child_process cp = CHILD_PROCESS_INIT;
                cp.git_cmd = 1;
                strvec_push(&cp.args, "branch");
@@ -646,9 +923,9 @@ static int add(int ac, const char **av, const char *prefix)
                die(_("--[no-]track can only be used if a new branch is created"));
        }
 
-       UNLEAK(path);
-       UNLEAK(opts);
-       return add_worktree(path, branch, &opts);
+       ret = add_worktree(path, branch, &opts);
+       free(path);
+       return ret;
 }
 
 static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
@@ -696,7 +973,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
                strbuf_addstr(&sb, "(bare)");
        else {
                strbuf_addf(&sb, "%-*s ", abbrev_len,
-                               find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV));
+                               repo_find_unique_abbrev(the_repository, &wt->head_oid, DEFAULT_ABBREV));
                if (wt->is_detached)
                        strbuf_addstr(&sb, "(detached HEAD)");
                else if (wt->head_ref) {
@@ -733,7 +1010,7 @@ static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
 
                if (path_len > *maxlen)
                        *maxlen = path_len;
-               sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev));
+               sha1_len = strlen(repo_find_unique_abbrev(the_repository, &wt[i]->head_oid, *abbrev));
                if (sha1_len > *abbrev)
                        *abbrev = sha1_len;
        }
@@ -772,9 +1049,9 @@ static int list(int ac, const char **av, const char *prefix)
        };
 
        expire = TIME_MAX;
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_list_usage, 0);
        if (ac)
-               usage_with_options(worktree_usage, options);
+               usage_with_options(git_worktree_list_usage, options);
        else if (verbose && porcelain)
                die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
        else if (!line_terminator && !porcelain)
@@ -811,9 +1088,9 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
        };
        struct worktree **worktrees, *wt;
 
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
        if (ac != 1)
-               usage_with_options(worktree_usage, options);
+               usage_with_options(git_worktree_lock_usage, options);
 
        worktrees = get_worktrees();
        wt = find_worktree(worktrees, prefix, av[0]);
@@ -844,9 +1121,9 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
        struct worktree **worktrees, *wt;
        int ret;
 
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
        if (ac != 1)
-               usage_with_options(worktree_usage, options);
+               usage_with_options(git_worktree_unlock_usage, options);
 
        worktrees = get_worktrees();
        wt = find_worktree(worktrees, prefix, av[0]);
@@ -863,7 +1140,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
 
 static void validate_no_submodules(const struct worktree *wt)
 {
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
        struct strbuf path = STRBUF_INIT;
        int i, found_submodules = 0;
 
@@ -914,9 +1191,10 @@ static int move_worktree(int ac, const char **av, const char *prefix)
        const char *reason = NULL;
        char *path;
 
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_move_usage,
+                          0);
        if (ac != 2)
-               usage_with_options(worktree_usage, options);
+               usage_with_options(git_worktree_move_usage, options);
 
        path = prefix_filename(prefix, av[1]);
        strbuf_addstr(&dst, path);
@@ -1042,9 +1320,9 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
        const char *reason = NULL;
        int ret = 0;
 
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_remove_usage, 0);
        if (ac != 1)
-               usage_with_options(worktree_usage, options);
+               usage_with_options(git_worktree_remove_usage, options);
 
        worktrees = get_worktrees();
        wt = find_worktree(worktrees, prefix, av[0]);
@@ -1102,7 +1380,7 @@ static int repair(int ac, const char **av, const char *prefix)
        };
        int rc = 0;
 
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_repair_usage, 0);
        p = ac > 0 ? av : self;
        for (; *p; p++)
                repair_worktree_at_path(*p, report_repair, &rc);
@@ -1130,6 +1408,10 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
        if (!prefix)
                prefix = "";
 
-       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0);
+
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
+
        return fn(ac, av, prefix);
 }
index 45d61707e7d11e60e5ba2e6da90017fb38b30dc3..66e83d0ecba04c90d1e12a2529cc40210808035c 100644 (file)
@@ -3,13 +3,16 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tree.h"
 #include "cache-tree.h"
 #include "parse-options.h"
+#include "repository.h"
 
 static const char * const write_tree_usage[] = {
        N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"),
@@ -38,7 +41,11 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
        argc = parse_options(argc, argv, cmd_prefix, write_tree_options,
                             write_tree_usage, 0);
 
-       ret = write_cache_as_tree(&oid, flags, tree_prefix);
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
+
+       ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
+                                 tree_prefix);
        switch (ret) {
        case 0:
                printf("%s\n", oid_to_hex(&oid));
index 855b68ec23bdb1bdc85c1184a9c144c226f0245c..eb46b8863793e25b82ca6df27eb435476c4807a5 100644 (file)
@@ -1,17 +1,20 @@
 /*
  * Copyright (c) 2011, Google Inc.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "bulk-checkin.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "repository.h"
 #include "csum-file.h"
 #include "pack.h"
 #include "strbuf.h"
-#include "string-list.h"
 #include "tmp-objdir.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 
 static int odb_transaction_nesting;
 
@@ -124,7 +127,7 @@ static int already_written(struct bulk_checkin_packfile *state, struct object_id
        int i;
 
        /* The object may already exist in the repository */
-       if (has_object_file(oid))
+       if (repo_has_object_file(the_repository, oid))
                return 1;
 
        /* Might want to keep the list sorted */
@@ -151,10 +154,10 @@ static int already_written(struct bulk_checkin_packfile *state, struct object_id
  * status before calling us just in case we ask it to call us again
  * with a new pack.
  */
-static int stream_to_pack(struct bulk_checkin_packfile *state,
-                         git_hash_ctx *ctx, off_t *already_hashed_to,
-                         int fd, size_t size, enum object_type type,
-                         const char *path, unsigned flags)
+static int stream_blob_to_pack(struct bulk_checkin_packfile *state,
+                              git_hash_ctx *ctx, off_t *already_hashed_to,
+                              int fd, size_t size, const char *path,
+                              unsigned flags)
 {
        git_zstream s;
        unsigned char ibuf[16384];
@@ -166,7 +169,7 @@ static int stream_to_pack(struct bulk_checkin_packfile *state,
 
        git_deflate_init(&s, pack_compression_level);
 
-       hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), type, size);
+       hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), OBJ_BLOB, size);
        s.next_out = obuf + hdrlen;
        s.avail_out = sizeof(obuf) - hdrlen;
 
@@ -243,11 +246,10 @@ static void prepare_to_stream(struct bulk_checkin_packfile *state,
                die_errno("unable to write pack header");
 }
 
-static int deflate_to_pack(struct bulk_checkin_packfile *state,
-                          struct object_id *result_oid,
-                          int fd, size_t size,
-                          enum object_type type, const char *path,
-                          unsigned flags)
+static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,
+                               struct object_id *result_oid,
+                               int fd, size_t size,
+                               const char *path, unsigned flags)
 {
        off_t seekback, already_hashed_to;
        git_hash_ctx ctx;
@@ -261,9 +263,10 @@ static int deflate_to_pack(struct bulk_checkin_packfile *state,
                return error("cannot find the current offset");
 
        header_len = format_object_header((char *)obuf, sizeof(obuf),
-                                         type, size);
+                                         OBJ_BLOB, size);
        the_hash_algo->init_fn(&ctx);
        the_hash_algo->update_fn(&ctx, obuf, header_len);
+       the_hash_algo->init_fn(&checkpoint.ctx);
 
        /* Note: idx is non-NULL when we are writing */
        if ((flags & HASH_WRITE_OBJECT) != 0)
@@ -278,8 +281,8 @@ static int deflate_to_pack(struct bulk_checkin_packfile *state,
                        idx->offset = state->offset;
                        crc32_begin(state->f);
                }
-               if (!stream_to_pack(state, &ctx, &already_hashed_to,
-                                   fd, size, type, path, flags))
+               if (!stream_blob_to_pack(state, &ctx, &already_hashed_to,
+                                        fd, size, path, flags))
                        break;
                /*
                 * Writing this object to the current pack will make
@@ -346,12 +349,12 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename)
        }
 }
 
-int index_bulk_checkin(struct object_id *oid,
-                      int fd, size_t size, enum object_type type,
-                      const char *path, unsigned flags)
+int index_blob_bulk_checkin(struct object_id *oid,
+                           int fd, size_t size,
+                           const char *path, unsigned flags)
 {
-       int status = deflate_to_pack(&bulk_checkin_packfile, oid, fd, size, type,
-                                    path, flags);
+       int status = deflate_blob_to_pack(&bulk_checkin_packfile, oid, fd, size,
+                                         path, flags);
        if (!odb_transaction_nesting)
                flush_bulk_checkin_packfile(&bulk_checkin_packfile);
        return status;
index 8281b9cb159691530eb2f74b46daf535261f6c93..aa7286a7b3e127bbd4ee14cf99e3fd21f46df445 100644 (file)
@@ -4,14 +4,14 @@
 #ifndef BULK_CHECKIN_H
 #define BULK_CHECKIN_H
 
-#include "cache.h"
+#include "object.h"
 
 void prepare_loose_object_bulk_checkin(void);
 void fsync_loose_object_bulk_checkin(int fd, const char *filename);
 
-int index_bulk_checkin(struct object_id *oid,
-                      int fd, size_t size, enum object_type type,
-                      const char *path, unsigned flags);
+int index_blob_bulk_checkin(struct object_id *oid,
+                           int fd, size_t size,
+                           const char *path, unsigned flags);
 
 /*
  * Tell the object database to optimize for adding
index 4a8cc74ed0531eaf3fcfb0023d198cea4aa2188d..ca32050a78fd7570adea76db264c5600339caf82 100644 (file)
-#include "cache.h"
+#include "git-compat-util.h"
 #include "bundle-uri.h"
 #include "bundle.h"
-#include "object-store.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
 #include "refs.h"
 #include "run-command.h"
+#include "hashmap.h"
+#include "pkt-line.h"
+#include "config.h"
+#include "remote.h"
 
-static int find_temp_filename(struct strbuf *name)
+static struct {
+       enum bundle_list_heuristic heuristic;
+       const char *name;
+} heuristics[BUNDLE_HEURISTIC__COUNT] = {
+       { BUNDLE_HEURISTIC_NONE, ""},
+       { BUNDLE_HEURISTIC_CREATIONTOKEN, "creationToken" },
+};
+
+static int compare_bundles(const void *hashmap_cmp_fn_data UNUSED,
+                          const struct hashmap_entry *he1,
+                          const struct hashmap_entry *he2,
+                          const void *id)
+{
+       const struct remote_bundle_info *e1 =
+               container_of(he1, const struct remote_bundle_info, ent);
+       const struct remote_bundle_info *e2 =
+               container_of(he2, const struct remote_bundle_info, ent);
+
+       return strcmp(e1->id, id ? (const char *)id : e2->id);
+}
+
+void init_bundle_list(struct bundle_list *list)
+{
+       memset(list, 0, sizeof(*list));
+
+       /* Implied defaults. */
+       list->mode = BUNDLE_MODE_ALL;
+       list->version = 1;
+
+       hashmap_init(&list->bundles, compare_bundles, NULL, 0);
+}
+
+static int clear_remote_bundle_info(struct remote_bundle_info *bundle,
+                                   void *data UNUSED)
+{
+       FREE_AND_NULL(bundle->id);
+       FREE_AND_NULL(bundle->uri);
+       FREE_AND_NULL(bundle->file);
+       bundle->unbundled = 0;
+       return 0;
+}
+
+void clear_bundle_list(struct bundle_list *list)
+{
+       if (!list)
+               return;
+
+       for_all_bundles_in_list(list, clear_remote_bundle_info, NULL);
+       hashmap_clear_and_free(&list->bundles, struct remote_bundle_info, ent);
+       free(list->baseURI);
+}
+
+int for_all_bundles_in_list(struct bundle_list *list,
+                           bundle_iterator iter,
+                           void *data)
+{
+       struct remote_bundle_info *info;
+       struct hashmap_iter i;
+
+       hashmap_for_each_entry(&list->bundles, &i, info, ent) {
+               int result = iter(info, data);
+
+               if (result)
+                       return result;
+       }
+
+       return 0;
+}
+
+static int summarize_bundle(struct remote_bundle_info *info, void *data)
+{
+       FILE *fp = data;
+       fprintf(fp, "[bundle \"%s\"]\n", info->id);
+       fprintf(fp, "\turi = %s\n", info->uri);
+
+       if (info->creationToken)
+               fprintf(fp, "\tcreationToken = %"PRIu64"\n", info->creationToken);
+       return 0;
+}
+
+void print_bundle_list(FILE *fp, struct bundle_list *list)
+{
+       const char *mode;
+
+       switch (list->mode) {
+       case BUNDLE_MODE_ALL:
+               mode = "all";
+               break;
+
+       case BUNDLE_MODE_ANY:
+               mode = "any";
+               break;
+
+       case BUNDLE_MODE_NONE:
+       default:
+               mode = "<unknown>";
+       }
+
+       fprintf(fp, "[bundle]\n");
+       fprintf(fp, "\tversion = %d\n", list->version);
+       fprintf(fp, "\tmode = %s\n", mode);
+
+       if (list->heuristic) {
+               int i;
+               for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) {
+                       if (heuristics[i].heuristic == list->heuristic) {
+                               printf("\theuristic = %s\n",
+                                      heuristics[list->heuristic].name);
+                               break;
+                       }
+               }
+       }
+
+       for_all_bundles_in_list(list, summarize_bundle, fp);
+}
+
+/**
+ * Given a key-value pair, update the state of the given bundle list.
+ * Returns 0 if the key-value pair is understood. Returns -1 if the key
+ * is not understood or the value is malformed.
+ */
+static int bundle_list_update(const char *key, const char *value,
+                             struct bundle_list *list)
+{
+       struct strbuf id = STRBUF_INIT;
+       struct remote_bundle_info lookup = REMOTE_BUNDLE_INFO_INIT;
+       struct remote_bundle_info *bundle;
+       const char *subsection, *subkey;
+       size_t subsection_len;
+
+       if (parse_config_key(key, "bundle", &subsection, &subsection_len, &subkey))
+               return -1;
+
+       if (!subsection_len) {
+               if (!strcmp(subkey, "version")) {
+                       int version;
+                       if (!git_parse_int(value, &version))
+                               return -1;
+                       if (version != 1)
+                               return -1;
+
+                       list->version = version;
+                       return 0;
+               }
+
+               if (!strcmp(subkey, "mode")) {
+                       if (!strcmp(value, "all"))
+                               list->mode = BUNDLE_MODE_ALL;
+                       else if (!strcmp(value, "any"))
+                               list->mode = BUNDLE_MODE_ANY;
+                       else
+                               return -1;
+                       return 0;
+               }
+
+               if (!strcmp(subkey, "heuristic")) {
+                       int i;
+                       for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) {
+                               if (heuristics[i].heuristic &&
+                                   heuristics[i].name &&
+                                   !strcmp(value, heuristics[i].name)) {
+                                       list->heuristic = heuristics[i].heuristic;
+                                       return 0;
+                               }
+                       }
+
+                       /* Ignore unknown heuristics. */
+                       return 0;
+               }
+
+               /* Ignore other unknown global keys. */
+               return 0;
+       }
+
+       strbuf_add(&id, subsection, subsection_len);
+
+       /*
+        * Check for an existing bundle with this <id>, or create one
+        * if necessary.
+        */
+       lookup.id = id.buf;
+       hashmap_entry_init(&lookup.ent, strhash(lookup.id));
+       if (!(bundle = hashmap_get_entry(&list->bundles, &lookup, ent, NULL))) {
+               CALLOC_ARRAY(bundle, 1);
+               bundle->id = strbuf_detach(&id, NULL);
+               hashmap_entry_init(&bundle->ent, strhash(bundle->id));
+               hashmap_add(&list->bundles, &bundle->ent);
+       }
+       strbuf_release(&id);
+
+       if (!strcmp(subkey, "uri")) {
+               if (bundle->uri)
+                       return -1;
+               bundle->uri = relative_url(list->baseURI, value, NULL);
+               return 0;
+       }
+
+       if (!strcmp(subkey, "creationtoken")) {
+               if (sscanf(value, "%"PRIu64, &bundle->creationToken) != 1)
+                       warning(_("could not parse bundle list key %s with value '%s'"),
+                               "creationToken", value);
+               return 0;
+       }
+
+       /*
+        * At this point, we ignore any information that we don't
+        * understand, assuming it to be hints for a heuristic the client
+        * does not currently understand.
+        */
+       return 0;
+}
+
+static int config_to_bundle_list(const char *key, const char *value,
+                                const struct config_context *ctx UNUSED,
+                                void *data)
+{
+       struct bundle_list *list = data;
+       return bundle_list_update(key, value, list);
+}
+
+int bundle_uri_parse_config_format(const char *uri,
+                                  const char *filename,
+                                  struct bundle_list *list)
+{
+       int result;
+       struct config_options opts = {
+               .error_action = CONFIG_ERROR_ERROR,
+       };
+
+       if (!list->baseURI) {
+               struct strbuf baseURI = STRBUF_INIT;
+               strbuf_addstr(&baseURI, uri);
+
+               /*
+                * If the URI does not end with a trailing slash, then
+                * remove the filename portion of the path. This is
+                * important for relative URIs.
+                */
+               strbuf_strip_file_from_path(&baseURI);
+               list->baseURI = strbuf_detach(&baseURI, NULL);
+       }
+       result = git_config_from_file_with_options(config_to_bundle_list,
+                                                  filename, list,
+                                                  CONFIG_SCOPE_UNKNOWN,
+                                                  &opts);
+
+       if (!result && list->mode == BUNDLE_MODE_NONE) {
+               warning(_("bundle list at '%s' has no mode"), uri);
+               result = 1;
+       }
+
+       return result;
+}
+
+static char *find_temp_filename(void)
 {
        int fd;
+       struct strbuf name = STRBUF_INIT;
        /*
         * Find a temporary filename that is available. This is briefly
         * racy, but unlikely to collide.
         */
-       fd = odb_mkstemp(name, "bundles/tmp_uri_XXXXXX");
+       fd = odb_mkstemp(&name, "bundles/tmp_uri_XXXXXX");
        if (fd < 0) {
                warning(_("failed to create temporary file"));
-               return -1;
+               return NULL;
        }
 
        close(fd);
-       unlink(name->buf);
-       return 0;
+       unlink(name.buf);
+       return strbuf_detach(&name, NULL);
 }
 
 static int download_https_uri_to_file(const char *file, const char *uri)
@@ -32,6 +293,7 @@ static int download_https_uri_to_file(const char *file, const char *uri)
        int found_get = 0;
 
        strvec_pushl(&cp.args, "git-remote-https", uri, NULL);
+       cp.err = -1;
        cp.in = -1;
        cp.out = -1;
 
@@ -105,7 +367,13 @@ static int unbundle_from_file(struct repository *r, const char *file)
        if ((bundle_fd = read_bundle_header(file, &header)) < 0)
                return 1;
 
-       if ((result = unbundle(r, &header, bundle_fd, NULL)))
+       /*
+        * Skip the reachability walk here, since we will be adding
+        * a reachable ref pointing to the new tips, which will reach
+        * the prerequisite commits.
+        */
+       if ((result = unbundle(r, &header, bundle_fd, NULL,
+                              VERIFY_BUNDLE_QUIET)))
                return 1;
 
        /*
@@ -138,31 +406,529 @@ static int unbundle_from_file(struct repository *r, const char *file)
        return result;
 }
 
-int fetch_bundle_uri(struct repository *r, const char *uri)
+struct bundle_list_context {
+       struct repository *r;
+       struct bundle_list *list;
+       enum bundle_list_mode mode;
+       int count;
+       int depth;
+};
+
+/*
+ * This early definition is necessary because we use indirect recursion:
+ *
+ * While iterating through a bundle list that was downloaded as part
+ * of fetch_bundle_uri_internal(), iterator methods eventually call it
+ * again, but with depth + 1.
+ */
+static int fetch_bundle_uri_internal(struct repository *r,
+                                    struct remote_bundle_info *bundle,
+                                    int depth,
+                                    struct bundle_list *list);
+
+static int download_bundle_to_file(struct remote_bundle_info *bundle, void *data)
+{
+       int res;
+       struct bundle_list_context *ctx = data;
+
+       if (ctx->mode == BUNDLE_MODE_ANY && ctx->count)
+               return 0;
+
+       res = fetch_bundle_uri_internal(ctx->r, bundle, ctx->depth + 1, ctx->list);
+
+       /*
+        * Only increment count if the download succeeded. If our mode is
+        * BUNDLE_MODE_ANY, then we will want to try other URIs in the
+        * list in case they work instead.
+        */
+       if (!res)
+               ctx->count++;
+
+       /*
+        * To be opportunistic as possible, we continue iterating and
+        * download as many bundles as we can, so we can apply the ones
+        * that work, even in BUNDLE_MODE_ALL mode.
+        */
+       return 0;
+}
+
+struct bundles_for_sorting {
+       struct remote_bundle_info **items;
+       size_t alloc;
+       size_t nr;
+};
+
+static int append_bundle(struct remote_bundle_info *bundle, void *data)
+{
+       struct bundles_for_sorting *list = data;
+       list->items[list->nr++] = bundle;
+       return 0;
+}
+
+/**
+ * For use in QSORT() to get a list sorted by creationToken
+ * in decreasing order.
+ */
+static int compare_creation_token_decreasing(const void *va, const void *vb)
+{
+       const struct remote_bundle_info * const *a = va;
+       const struct remote_bundle_info * const *b = vb;
+
+       if ((*a)->creationToken > (*b)->creationToken)
+               return -1;
+       if ((*a)->creationToken < (*b)->creationToken)
+               return 1;
+       return 0;
+}
+
+static int fetch_bundles_by_token(struct repository *r,
+                                 struct bundle_list *list)
+{
+       int cur;
+       int move_direction = 0;
+       const char *creationTokenStr;
+       uint64_t maxCreationToken = 0, newMaxCreationToken = 0;
+       struct bundle_list_context ctx = {
+               .r = r,
+               .list = list,
+               .mode = list->mode,
+       };
+       struct bundles_for_sorting bundles = {
+               .alloc = hashmap_get_size(&list->bundles),
+       };
+
+       ALLOC_ARRAY(bundles.items, bundles.alloc);
+
+       for_all_bundles_in_list(list, append_bundle, &bundles);
+
+       if (!bundles.nr) {
+               free(bundles.items);
+               return 0;
+       }
+
+       QSORT(bundles.items, bundles.nr, compare_creation_token_decreasing);
+
+       /*
+        * If fetch.bundleCreationToken exists, parses to a uint64t, and
+        * is not strictly smaller than the maximum creation token in the
+        * bundle list, then do not download any bundles.
+        */
+       if (!repo_config_get_value(r,
+                                  "fetch.bundlecreationtoken",
+                                  &creationTokenStr) &&
+           sscanf(creationTokenStr, "%"PRIu64, &maxCreationToken) == 1 &&
+           bundles.items[0]->creationToken <= maxCreationToken) {
+               free(bundles.items);
+               return 0;
+       }
+
+       /*
+        * Attempt to download and unbundle the minimum number of bundles by
+        * creationToken in decreasing order. If we fail to unbundle (after
+        * a successful download) then move to the next non-downloaded bundle
+        * and attempt downloading. Once we succeed in applying a bundle,
+        * move to the previous unapplied bundle and attempt to unbundle it
+        * again.
+        *
+        * In the case of a fresh clone, we will likely download all of the
+        * bundles before successfully unbundling the oldest one, then the
+        * rest of the bundles unbundle successfully in increasing order
+        * of creationToken.
+        *
+        * If there are existing objects, then this process may terminate
+        * early when all required commits from "new" bundles exist in the
+        * repo's object store.
+        */
+       cur = 0;
+       while (cur >= 0 && cur < bundles.nr) {
+               struct remote_bundle_info *bundle = bundles.items[cur];
+
+               /*
+                * If we need to dig into bundles below the previous
+                * creation token value, then likely we are in an erroneous
+                * state due to missing or invalid bundles. Halt the process
+                * instead of continuing to download extra data.
+                */
+               if (bundle->creationToken <= maxCreationToken)
+                       break;
+
+               if (!bundle->file) {
+                       /*
+                        * Not downloaded yet. Try downloading.
+                        *
+                        * Note that bundle->file is non-NULL if a download
+                        * was attempted, even if it failed to download.
+                        */
+                       if (fetch_bundle_uri_internal(ctx.r, bundle, ctx.depth + 1, ctx.list)) {
+                               /* Mark as unbundled so we do not retry. */
+                               bundle->unbundled = 1;
+
+                               /* Try looking deeper in the list. */
+                               move_direction = 1;
+                               goto move;
+                       }
+
+                       /* We expect bundles when using creationTokens. */
+                       if (!is_bundle(bundle->file, 1)) {
+                               warning(_("file downloaded from '%s' is not a bundle"),
+                                       bundle->uri);
+                               break;
+                       }
+               }
+
+               if (bundle->file && !bundle->unbundled) {
+                       /*
+                        * This was downloaded, but not successfully
+                        * unbundled. Try unbundling again.
+                        */
+                       if (unbundle_from_file(ctx.r, bundle->file)) {
+                               /* Try looking deeper in the list. */
+                               move_direction = 1;
+                       } else {
+                               /*
+                                * Succeeded in unbundle. Retry bundles
+                                * that previously failed to unbundle.
+                                */
+                               move_direction = -1;
+                               bundle->unbundled = 1;
+
+                               if (bundle->creationToken > newMaxCreationToken)
+                                       newMaxCreationToken = bundle->creationToken;
+                       }
+               }
+
+               /*
+                * Else case: downloaded and unbundled successfully.
+                * Skip this by moving in the same direction as the
+                * previous step.
+                */
+
+move:
+               /* Move in the specified direction and repeat. */
+               cur += move_direction;
+       }
+
+       /*
+        * We succeed if the loop terminates because 'cur' drops below
+        * zero. The other case is that we terminate because 'cur'
+        * reaches the end of the list, so we have a failure no matter
+        * which bundles we apply from the list.
+        */
+       if (cur < 0) {
+               struct strbuf value = STRBUF_INIT;
+               strbuf_addf(&value, "%"PRIu64"", newMaxCreationToken);
+               if (repo_config_set_multivar_gently(ctx.r,
+                                                   "fetch.bundleCreationToken",
+                                                   value.buf, NULL, 0))
+                       warning(_("failed to store maximum creation token"));
+
+               strbuf_release(&value);
+       }
+
+       free(bundles.items);
+       return cur >= 0;
+}
+
+static int download_bundle_list(struct repository *r,
+                               struct bundle_list *local_list,
+                               struct bundle_list *global_list,
+                               int depth)
+{
+       struct bundle_list_context ctx = {
+               .r = r,
+               .list = global_list,
+               .depth = depth + 1,
+               .mode = local_list->mode,
+       };
+
+       return for_all_bundles_in_list(local_list, download_bundle_to_file, &ctx);
+}
+
+static int fetch_bundle_list_in_config_format(struct repository *r,
+                                             struct bundle_list *global_list,
+                                             struct remote_bundle_info *bundle,
+                                             int depth)
+{
+       int result;
+       struct bundle_list list_from_bundle;
+
+       init_bundle_list(&list_from_bundle);
+
+       if ((result = bundle_uri_parse_config_format(bundle->uri,
+                                                    bundle->file,
+                                                    &list_from_bundle)))
+               goto cleanup;
+
+       if (list_from_bundle.mode == BUNDLE_MODE_NONE) {
+               warning(_("unrecognized bundle mode from URI '%s'"),
+                       bundle->uri);
+               result = -1;
+               goto cleanup;
+       }
+
+       /*
+        * If this list uses the creationToken heuristic, then the URIs
+        * it advertises are expected to be bundles, not nested lists.
+        * We can drop 'global_list' and 'depth'.
+        */
+       if (list_from_bundle.heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN) {
+               result = fetch_bundles_by_token(r, &list_from_bundle);
+               global_list->heuristic = BUNDLE_HEURISTIC_CREATIONTOKEN;
+       } else if ((result = download_bundle_list(r, &list_from_bundle,
+                                          global_list, depth)))
+               goto cleanup;
+
+cleanup:
+       clear_bundle_list(&list_from_bundle);
+       return result;
+}
+
+/**
+ * This limits the recursion on fetch_bundle_uri_internal() when following
+ * bundle lists.
+ */
+static int max_bundle_uri_depth = 4;
+
+/**
+ * Recursively download all bundles advertised at the given URI
+ * to files. If the file is a bundle, then add it to the given
+ * 'list'. Otherwise, expect a bundle list and recurse on the
+ * URIs in that list according to the list mode (ANY or ALL).
+ */
+static int fetch_bundle_uri_internal(struct repository *r,
+                                    struct remote_bundle_info *bundle,
+                                    int depth,
+                                    struct bundle_list *list)
 {
        int result = 0;
-       struct strbuf filename = STRBUF_INIT;
+       struct remote_bundle_info *bcopy;
 
-       if ((result = find_temp_filename(&filename)))
+       if (depth >= max_bundle_uri_depth) {
+               warning(_("exceeded bundle URI recursion limit (%d)"),
+                       max_bundle_uri_depth);
+               return -1;
+       }
+
+       if (!bundle->file &&
+           !(bundle->file = find_temp_filename())) {
+               result = -1;
                goto cleanup;
+       }
 
-       if ((result = copy_uri_to_file(filename.buf, uri))) {
-               warning(_("failed to download bundle from URI '%s'"), uri);
+       if ((result = copy_uri_to_file(bundle->file, bundle->uri))) {
+               warning(_("failed to download bundle from URI '%s'"), bundle->uri);
                goto cleanup;
        }
 
-       if ((result = !is_bundle(filename.buf, 0))) {
-               warning(_("file at URI '%s' is not a bundle"), uri);
+       if ((result = !is_bundle(bundle->file, 1))) {
+               result = fetch_bundle_list_in_config_format(
+                               r, list, bundle, depth);
+               if (result)
+                       warning(_("file at URI '%s' is not a bundle or bundle list"),
+                               bundle->uri);
                goto cleanup;
        }
 
-       if ((result = unbundle_from_file(r, filename.buf))) {
-               warning(_("failed to unbundle bundle from URI '%s'"), uri);
+       /* Copy the bundle and insert it into the global list. */
+       CALLOC_ARRAY(bcopy, 1);
+       bcopy->id = xstrdup(bundle->id);
+       bcopy->file = xstrdup(bundle->file);
+       hashmap_entry_init(&bcopy->ent, strhash(bcopy->id));
+       hashmap_add(&list->bundles, &bcopy->ent);
+
+cleanup:
+       if (result && bundle->file)
+               unlink(bundle->file);
+       return result;
+}
+
+/**
+ * This loop iterator breaks the loop with nonzero return code on the
+ * first successful unbundling of a bundle.
+ */
+static int attempt_unbundle(struct remote_bundle_info *info, void *data)
+{
+       struct repository *r = data;
+
+       if (!info->file || info->unbundled)
+               return 0;
+
+       if (!unbundle_from_file(r, info->file)) {
+               info->unbundled = 1;
+               return 1;
+       }
+
+       return 0;
+}
+
+static int unbundle_all_bundles(struct repository *r,
+                               struct bundle_list *list)
+{
+       /*
+        * Iterate through all bundles looking for ones that can
+        * successfully unbundle. If any succeed, then perhaps another
+        * will succeed in the next attempt.
+        *
+        * Keep in mind that a non-zero result for the loop here means
+        * the loop terminated early on a successful unbundling, which
+        * signals that we can try again.
+        */
+       while (for_all_bundles_in_list(list, attempt_unbundle, r)) ;
+
+       return 0;
+}
+
+static int unlink_bundle(struct remote_bundle_info *info, void *data UNUSED)
+{
+       if (info->file)
+               unlink_or_warn(info->file);
+       return 0;
+}
+
+int fetch_bundle_uri(struct repository *r, const char *uri,
+                    int *has_heuristic)
+{
+       int result;
+       struct bundle_list list;
+       struct remote_bundle_info bundle = {
+               .uri = xstrdup(uri),
+               .id = xstrdup(""),
+       };
+
+       init_bundle_list(&list);
+
+       /*
+        * Do not fetch an empty bundle URI. An empty bundle URI
+        * could signal that a configured bundle URI has been disabled.
+        */
+       if (!*uri) {
+               result = 0;
                goto cleanup;
        }
 
+       /* If a bundle is added to this global list, then it is required. */
+       list.mode = BUNDLE_MODE_ALL;
+
+       if ((result = fetch_bundle_uri_internal(r, &bundle, 0, &list)))
+               goto cleanup;
+
+       result = unbundle_all_bundles(r, &list);
+
 cleanup:
-       unlink(filename.buf);
-       strbuf_release(&filename);
+       if (has_heuristic)
+               *has_heuristic = (list.heuristic != BUNDLE_HEURISTIC_NONE);
+       for_all_bundles_in_list(&list, unlink_bundle, NULL);
+       clear_bundle_list(&list);
+       clear_remote_bundle_info(&bundle, NULL);
+       return result;
+}
+
+int fetch_bundle_list(struct repository *r, struct bundle_list *list)
+{
+       int result;
+       struct bundle_list global_list;
+
+       /*
+        * If the creationToken heuristic is used, then the URIs
+        * advertised by 'list' are not nested lists and instead
+        * direct bundles. We do not need to use global_list.
+        */
+       if (list->heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN)
+               return fetch_bundles_by_token(r, list);
+
+       init_bundle_list(&global_list);
+
+       /* If a bundle is added to this global list, then it is required. */
+       global_list.mode = BUNDLE_MODE_ALL;
+
+       if ((result = download_bundle_list(r, list, &global_list, 0)))
+               goto cleanup;
+
+       if (list->heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN)
+               result = fetch_bundles_by_token(r, list);
+       else
+               result = unbundle_all_bundles(r, &global_list);
+
+cleanup:
+       for_all_bundles_in_list(&global_list, unlink_bundle, NULL);
+       clear_bundle_list(&global_list);
+       return result;
+}
+
+/**
+ * API for serve.c.
+ */
+
+int bundle_uri_advertise(struct repository *r, struct strbuf *value UNUSED)
+{
+       static int advertise_bundle_uri = -1;
+
+       if (advertise_bundle_uri != -1)
+               goto cached;
+
+       advertise_bundle_uri = 0;
+       repo_config_get_maybe_bool(r, "uploadpack.advertisebundleuris", &advertise_bundle_uri);
+
+cached:
+       return advertise_bundle_uri;
+}
+
+static int config_to_packet_line(const char *key, const char *value,
+                                const struct config_context *ctx UNUSED,
+                                void *data)
+{
+       struct packet_reader *writer = data;
+
+       if (starts_with(key, "bundle."))
+               packet_write_fmt(writer->fd, "%s=%s", key, value);
+
+       return 0;
+}
+
+int bundle_uri_command(struct repository *r,
+                      struct packet_reader *request)
+{
+       struct packet_writer writer;
+       packet_writer_init(&writer, 1);
+
+       while (packet_reader_read(request) == PACKET_READ_NORMAL)
+               die(_("bundle-uri: unexpected argument: '%s'"), request->line);
+       if (request->status != PACKET_READ_FLUSH)
+               die(_("bundle-uri: expected flush after arguments"));
+
+       /*
+        * Read all "bundle.*" config lines to the client as key=value
+        * packet lines.
+        */
+       repo_config(r, config_to_packet_line, &writer);
+
+       packet_writer_flush(&writer);
+
+       return 0;
+}
+
+/**
+ * General API for {transport,connect}.c etc.
+ */
+int bundle_uri_parse_line(struct bundle_list *list, const char *line)
+{
+       int result;
+       const char *equals;
+       struct strbuf key = STRBUF_INIT;
+
+       if (!strlen(line))
+               return error(_("bundle-uri: got an empty line"));
+
+       equals = strchr(line, '=');
+
+       if (!equals)
+               return error(_("bundle-uri: line is not of the form 'key=value'"));
+       if (line == equals || !*(equals + 1))
+               return error(_("bundle-uri: line has empty key or value"));
+
+       strbuf_add(&key, line, equals - line);
+       result = bundle_list_update(key.buf, equals + 1, list);
+       strbuf_release(&key);
+
        return result;
 }
index 8a152f1ef142f953ac760dc62e49b47074aae8ea..6dbc780f661bc06bbc9f0badddec91101814349c 100644 (file)
 #ifndef BUNDLE_URI_H
 #define BUNDLE_URI_H
 
+#include "hashmap.h"
+#include "strbuf.h"
+
+struct packet_reader;
 struct repository;
+struct string_list;
+
+/**
+ * The remote_bundle_info struct contains information for a single bundle
+ * URI. This may be initialized simply by a given URI or might have
+ * additional metadata associated with it if the bundle was advertised by
+ * a bundle list.
+ */
+struct remote_bundle_info {
+       struct hashmap_entry ent;
+
+       /**
+        * The 'id' is a name given to the bundle for reference
+        * by other bundle infos.
+        */
+       char *id;
+
+       /**
+        * The 'uri' is the location of the remote bundle so
+        * it can be downloaded on-demand. This will be NULL
+        * if there was no table of contents.
+        */
+       char *uri;
+
+       /**
+        * If the bundle has been downloaded, then 'file' is a
+        * filename storing its contents. Otherwise, 'file' is
+        * NULL.
+        */
+       char *file;
+
+       /**
+        * If the bundle has been unbundled successfully, then
+        * this boolean is true.
+        */
+       unsigned unbundled:1;
+
+       /**
+        * If the bundle is part of a list with the creationToken
+        * heuristic, then we use this member for sorting the bundles.
+        */
+       uint64_t creationToken;
+};
+
+#define REMOTE_BUNDLE_INFO_INIT { 0 }
+
+enum bundle_list_mode {
+       BUNDLE_MODE_NONE = 0,
+       BUNDLE_MODE_ALL,
+       BUNDLE_MODE_ANY
+};
+
+enum bundle_list_heuristic {
+       BUNDLE_HEURISTIC_NONE = 0,
+       BUNDLE_HEURISTIC_CREATIONTOKEN,
+
+       /* Must be last. */
+       BUNDLE_HEURISTIC__COUNT
+};
+
+/**
+ * A bundle_list contains an unordered set of remote_bundle_info structs,
+ * as well as information about the bundle listing, such as version and
+ * mode.
+ */
+struct bundle_list {
+       int version;
+       enum bundle_list_mode mode;
+       struct hashmap bundles;
+
+       /**
+        * The baseURI of a bundle_list is the URI that provided the list.
+        *
+        * In the case of the 'bundle-uri' protocol v2 command, the base
+        * URI is the URI of the Git remote.
+        *
+        * Otherwise, the bundle list was downloaded over HTTP from some
+        * known URI. 'baseURI' is set to that value.
+        *
+        * The baseURI is used as the base for any relative URIs
+        * advertised by the bundle list at that location.
+        */
+       char *baseURI;
+
+       /**
+        * A list can have a heuristic, which helps reduce the number of
+        * downloaded bundles.
+        */
+       enum bundle_list_heuristic heuristic;
+};
+
+void init_bundle_list(struct bundle_list *list);
+void clear_bundle_list(struct bundle_list *list);
+
+typedef int (*bundle_iterator)(struct remote_bundle_info *bundle,
+                              void *data);
+
+int for_all_bundles_in_list(struct bundle_list *list,
+                           bundle_iterator iter,
+                           void *data);
+
+struct FILE;
+void print_bundle_list(FILE *fp, struct bundle_list *list);
+
+/**
+ * A bundle URI may point to a bundle list where the key=value
+ * pairs are provided in config file format. This method is
+ * exposed publicly for testing purposes.
+ */
+int bundle_uri_parse_config_format(const char *uri,
+                                  const char *filename,
+                                  struct bundle_list *list);
 
 /**
  * Fetch data from the given 'uri' and unbundle the bundle data found
  * based on that information.
  *
  * Returns non-zero if no bundle information is found at the given 'uri'.
+ *
+ * If the pointer 'has_heuristic' is non-NULL, then the value it points to
+ * will be set to be non-zero if and only if the fetched list has a
+ * heuristic value. Such a value indicates that the list was designed for
+ * incremental fetches.
+ */
+int fetch_bundle_uri(struct repository *r, const char *uri,
+                    int *has_heuristic);
+
+/**
+ * Given a bundle list that was already advertised (likely by the
+ * bundle-uri protocol v2 verb) at the given uri, fetch and unbundle the
+ * bundles according to the bundle strategy of that list.
+ *
+ * It is expected that the given 'list' is initialized, including its
+ * 'baseURI' value.
+ *
+ * Returns non-zero if there was an error trying to download the list
+ * or any of its advertised bundles.
+ */
+int fetch_bundle_list(struct repository *r,
+                     struct bundle_list *list);
+
+/**
+ * API for serve.c.
+ */
+int bundle_uri_advertise(struct repository *r, struct strbuf *value);
+int bundle_uri_command(struct repository *r, struct packet_reader *request);
+
+/**
+ * General API for {transport,connect}.c etc.
+ */
+
+/**
+ * Parse a "key=value" packet line from the bundle-uri verb.
+ *
+ * Returns 0 on success and non-zero on error.
  */
-int fetch_bundle_uri(struct repository *r, const char *uri);
+int bundle_uri_parse_line(struct bundle_list *list,
+                         const char *line);
 
 #endif
index 0208e6d90d30f02828a9b6d668eb6a4feac9a971..a9744da255c6d527e53e65b71c1f4b9a9d6db4bd 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "lockfile.h"
 #include "bundle.h"
-#include "object-store.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "repository.h"
 #include "object.h"
 #include "commit.h"
@@ -12,6 +15,8 @@
 #include "refs.h"
 #include "strvec.h"
 #include "list-objects-filter-options.h"
+#include "connected.h"
+#include "write-or-die.h"
 
 static const char v2_bundle_signature[] = "# v2 git bundle\n";
 static const char v3_bundle_signature[] = "# v3 git bundle\n";
@@ -187,79 +192,65 @@ static int list_refs(struct string_list *r, int argc, const char **argv)
 /* Remember to update object flag allocation in object.h */
 #define PREREQ_MARK (1u<<16)
 
+struct string_list_iterator {
+       struct string_list *list;
+       size_t cur;
+};
+
+static const struct object_id *iterate_ref_map(void *cb_data)
+{
+       struct string_list_iterator *iter = cb_data;
+
+       if (iter->cur >= iter->list->nr)
+               return NULL;
+
+       return iter->list->items[iter->cur++].util;
+}
+
 int verify_bundle(struct repository *r,
                  struct bundle_header *header,
-                 int verbose)
+                 enum verify_bundle_flags flags)
 {
        /*
         * Do fast check, then if any prereqs are missing then go line by line
         * to be verbose about the errors
         */
        struct string_list *p = &header->prerequisites;
-       struct rev_info revs = REV_INFO_INIT;
-       const char *argv[] = {NULL, "--all", NULL};
-       struct commit *commit;
-       int i, ret = 0, req_nr;
+       int i, ret = 0;
        const char *message = _("Repository lacks these prerequisite commits:");
+       struct string_list_iterator iter = {
+               .list = p,
+       };
+       struct check_connected_options opts = {
+               .quiet = 1,
+       };
 
-       if (!r || !r->objects || !r->objects->odb) {
-               ret = error(_("need a repository to verify a bundle"));
-               goto cleanup;
-       }
+       if (!r || !r->objects || !r->objects->odb)
+               return error(_("need a repository to verify a bundle"));
 
-       repo_init_revisions(r, &revs, NULL);
        for (i = 0; i < p->nr; i++) {
                struct string_list_item *e = p->items + i;
                const char *name = e->string;
                struct object_id *oid = e->util;
                struct object *o = parse_object(r, oid);
-               if (o) {
-                       o->flags |= PREREQ_MARK;
-                       add_pending_object(&revs, o, name);
+               if (o)
                        continue;
-               }
-               if (++ret == 1)
-                       error("%s", message);
-               error("%s %s", oid_to_hex(oid), name);
-       }
-       if (revs.pending.nr != p->nr)
-               goto cleanup;
-       req_nr = revs.pending.nr;
-       setup_revisions(2, argv, &revs, NULL);
-
-       list_objects_filter_copy(&revs.filter, &header->filter);
-
-       if (prepare_revision_walk(&revs))
-               die(_("revision walk setup failed"));
-
-       i = req_nr;
-       while (i && (commit = get_revision(&revs)))
-               if (commit->object.flags & PREREQ_MARK)
-                       i--;
-
-       for (i = 0; i < p->nr; i++) {
-               struct string_list_item *e = p->items + i;
-               const char *name = e->string;
-               const struct object_id *oid = e->util;
-               struct object *o = parse_object(r, oid);
-               assert(o); /* otherwise we'd have returned early */
-               if (o->flags & SHOWN)
+               ret++;
+               if (flags & VERIFY_BUNDLE_QUIET)
                        continue;
-               if (++ret == 1)
+               if (ret == 1)
                        error("%s", message);
                error("%s %s", oid_to_hex(oid), name);
        }
+       if (ret)
+               goto cleanup;
 
-       /* Clean up objects used, as they will be reused. */
-       for (i = 0; i < p->nr; i++) {
-               struct string_list_item *e = p->items + i;
-               struct object_id *oid = e->util;
-               commit = lookup_commit_reference_gently(r, oid, 1);
-               if (commit)
-                       clear_commit_marks(commit, ALL_REV_FLAGS);
-       }
+       if ((ret = check_connected(iterate_ref_map, &iter, &opts)))
+               error(_("some prerequisite commits exist in the object store, "
+                       "but are not connected to the repository's history"));
 
-       if (verbose) {
+       /* TODO: preserve this verbose language. */
+       if (flags & VERIFY_BUNDLE_VERBOSE) {
                struct string_list *r;
 
                r = &header->references;
@@ -280,14 +271,13 @@ int verify_bundle(struct repository *r,
                        list_refs(r, 0, NULL);
                }
 
-               printf_ln("The bundle uses this hash algorithm: %s",
+               printf_ln(_("The bundle uses this hash algorithm: %s"),
                          header->hash_algo->name);
                if (header->filter.choice)
-                       printf_ln("The bundle uses this filter: %s",
+                       printf_ln(_("The bundle uses this filter: %s"),
                                  list_objects_filter_spec(&header->filter));
        }
 cleanup:
-       release_revisions(&revs);
        return ret;
 }
 
@@ -307,7 +297,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
        if (revs->max_age == -1 && revs->min_age == -1)
                goto out;
 
-       buf = read_object_file(&tag->oid, &type, &size);
+       buf = repo_read_object_file(the_repository, &tag->oid, &type, &size);
        if (!buf)
                goto out;
        line = memmem(buf, size, "\ntagger ", 8);
@@ -396,7 +386,8 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
 
                if (e->item->flags & UNINTERESTING)
                        continue;
-               if (dwim_ref(e->name, strlen(e->name), &oid, &ref, 0) != 1)
+               if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
+                                 &oid, &ref, 0) != 1)
                        goto skip_write_ref;
                if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
                        flag = 0;
@@ -620,9 +611,14 @@ err:
 }
 
 int unbundle(struct repository *r, struct bundle_header *header,
-            int bundle_fd, struct strvec *extra_index_pack_args)
+            int bundle_fd, struct strvec *extra_index_pack_args,
+            enum verify_bundle_flags flags)
 {
        struct child_process ip = CHILD_PROCESS_INIT;
+
+       if (verify_bundle(r, header, flags))
+               return -1;
+
        strvec_pushl(&ip.args, "index-pack", "--fix-thin", "--stdin", NULL);
 
        /* If there is a filter, then we need to create the promisor pack. */
@@ -634,8 +630,6 @@ int unbundle(struct repository *r, struct bundle_header *header,
                strvec_clear(extra_index_pack_args);
        }
 
-       if (verify_bundle(r, header, 0))
-               return -1;
        ip.in = bundle_fd;
        ip.no_stdout = 1;
        ip.git_cmd = 1;
index 68ff39a0a74085a218f16d07619fad0adad1972c..021adbdcbb3d9b482a08b304a80e62264e1a3630 100644 (file)
--- a/bundle.h
+++ b/bundle.h
@@ -2,7 +2,6 @@
 #define BUNDLE_H
 
 #include "strvec.h"
-#include "cache.h"
 #include "string-list.h"
 #include "list-objects-filter-options.h"
 
@@ -30,7 +29,14 @@ int read_bundle_header_fd(int fd, struct bundle_header *header,
 int create_bundle(struct repository *r, const char *path,
                  int argc, const char **argv, struct strvec *pack_options,
                  int version);
-int verify_bundle(struct repository *r, struct bundle_header *header, int verbose);
+
+enum verify_bundle_flags {
+       VERIFY_BUNDLE_VERBOSE = (1 << 0),
+       VERIFY_BUNDLE_QUIET = (1 << 1),
+};
+
+int verify_bundle(struct repository *r, struct bundle_header *header,
+                 enum verify_bundle_flags flags);
 
 /**
  * Unbundle after reading the header with read_bundle_header().
@@ -41,9 +47,13 @@ int verify_bundle(struct repository *r, struct bundle_header *header, int verbos
  * Provide "extra_index_pack_args" to pass any extra arguments
  * (e.g. "-v" for verbose/progress), NULL otherwise. The provided
  * "extra_index_pack_args" (if any) will be strvec_clear()'d for you.
+ *
+ * Before unbundling, this method will call verify_bundle() with the
+ * given 'flags'.
  */
 int unbundle(struct repository *r, struct bundle_header *header,
-            int bundle_fd, struct strvec *extra_index_pack_args);
+            int bundle_fd, struct strvec *extra_index_pack_args,
+            enum verify_bundle_flags flags);
 int list_bundle_refs(struct bundle_header *header,
                int argc, const char **argv);
 
index c97111cccf2eda3a53be9dfce637fa2c35464f4d..64678fe19930402b8400cb05d3ccaad5bb5b00a6 100644 (file)
@@ -1,13 +1,18 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
 #include "bulk-checkin.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "read-cache-ll.h"
 #include "replace-object.h"
 #include "promisor-remote.h"
-#include "sparse-index.h"
+#include "trace.h"
+#include "trace2.h"
 
 #ifndef DEBUG_CACHE_TREE
 #define DEBUG_CACHE_TREE 0
@@ -229,7 +234,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
        int i;
        if (!it)
                return 0;
-       if (it->entry_count < 0 || !has_object_file(&it->oid))
+       if (it->entry_count < 0 || !repo_has_object_file(the_repository, &it->oid))
                return 0;
        for (i = 0; i < it->subtree_nr; i++) {
                if (!cache_tree_fully_valid(it->down[i]->cache_tree))
@@ -240,7 +245,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
 
 static int must_check_existence(const struct cache_entry *ce)
 {
-       return !(has_promisor_remote() && ce_skip_worktree(ce));
+       return !(repo_has_promisor_remote(the_repository) && ce_skip_worktree(ce));
 }
 
 static int update_one(struct cache_tree *it,
@@ -280,7 +285,7 @@ static int update_one(struct cache_tree *it,
                }
        }
 
-       if (0 <= it->entry_count && has_object_file(&it->oid))
+       if (0 <= it->entry_count && repo_has_object_file(the_repository, &it->oid))
                return it->entry_count;
 
        /*
@@ -386,7 +391,7 @@ static int update_one(struct cache_tree *it,
                ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
                        !must_check_existence(ce);
                if (is_null_oid(oid) ||
-                   (!ce_missing_ok && !has_object_file(oid))) {
+                   (!ce_missing_ok && !repo_has_object_file(the_repository, oid))) {
                        strbuf_release(&buffer);
                        if (expected_missing)
                                return -1;
@@ -405,7 +410,7 @@ static int update_one(struct cache_tree *it,
                }
 
                /*
-                * CE_INTENT_TO_ADD entries exist on on-disk index but
+                * CE_INTENT_TO_ADD entries exist in on-disk index but
                 * they are not part of generated trees. Invalidate up
                 * to root to force cache-tree users to read elsewhere.
                 */
@@ -434,7 +439,7 @@ static int update_one(struct cache_tree *it,
                struct object_id oid;
                hash_object_file(the_hash_algo, buffer.buf, buffer.len,
                                 OBJ_TREE, &oid);
-               if (has_object_file_with_flags(&oid, OBJECT_INFO_SKIP_FETCH_OBJECT))
+               if (repo_has_object_file_with_flags(the_repository, &oid, OBJECT_INFO_SKIP_FETCH_OBJECT))
                        oidcpy(&it->oid, &oid);
                else
                        to_invalidate = 1;
@@ -470,7 +475,7 @@ int cache_tree_update(struct index_state *istate, int flags)
        if (!istate->cache_tree)
                istate->cache_tree = cache_tree();
 
-       if (!(flags & WRITE_TREE_MISSING_OK) && has_promisor_remote())
+       if (!(flags & WRITE_TREE_MISSING_OK) && repo_has_promisor_remote(the_repository))
                prefetch_cache_entries(istate, must_check_existence);
 
        trace_performance_enter();
@@ -760,7 +765,7 @@ static void prime_cache_tree_rec(struct repository *r,
        struct tree_desc desc;
        struct name_entry entry;
        int cnt;
-       int base_path_len = tree_path->len;
+       size_t base_path_len = tree_path->len;
 
        oidcpy(&it->oid, &tree->object.oid);
 
@@ -785,7 +790,6 @@ static void prime_cache_tree_rec(struct repository *r,
                         */
                        if (r->index->sparse_index) {
                                strbuf_setlen(tree_path, base_path_len);
-                               strbuf_grow(tree_path, base_path_len + entry.pathlen + 1);
                                strbuf_add(tree_path, entry.path, entry.pathlen);
                                strbuf_addch(tree_path, '/');
                        }
@@ -815,14 +819,14 @@ void prime_cache_tree(struct repository *r,
 {
        struct strbuf tree_path = STRBUF_INIT;
 
-       trace2_region_enter("cache-tree", "prime_cache_tree", the_repository);
+       trace2_region_enter("cache-tree", "prime_cache_tree", r);
        cache_tree_free(&istate->cache_tree);
        istate->cache_tree = cache_tree();
 
        prime_cache_tree_rec(r, istate->cache_tree, tree, &tree_path);
        strbuf_release(&tree_path);
        istate->cache_changed |= CACHE_TREE_CHANGED;
-       trace2_region_leave("cache-tree", "prime_cache_tree", the_repository);
+       trace2_region_leave("cache-tree", "prime_cache_tree", r);
 }
 
 /*
index 8efeccebfc9f0bf8c9cfda1aa4ac531108b52650..faae88be63c2ce012c700af3a88e02e165982a4e 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef CACHE_TREE_H
 #define CACHE_TREE_H
 
-#include "cache.h"
 #include "tree.h"
 #include "tree-walk.h"
 
@@ -53,19 +52,4 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state,
 void prime_cache_tree(struct repository *, struct index_state *, struct tree *);
 
 int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
-
-#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
-static inline int write_cache_as_tree(struct object_id *oid, int flags, const char *prefix)
-{
-       return write_index_as_tree(oid, &the_index, get_index_file(), flags, prefix);
-}
-
-static inline int update_main_cache_tree(int flags)
-{
-       if (!the_index.cache_tree)
-               the_index.cache_tree = cache_tree();
-       return cache_tree_update(&the_index, flags);
-}
-#endif
-
 #endif
diff --git a/cache.h b/cache.h
deleted file mode 100644 (file)
index 26ed03b..0000000
--- a/cache.h
+++ /dev/null
@@ -1,1968 +0,0 @@
-#ifndef CACHE_H
-#define CACHE_H
-
-#include "git-compat-util.h"
-#include "strbuf.h"
-#include "hashmap.h"
-#include "list.h"
-#include "advice.h"
-#include "gettext.h"
-#include "convert.h"
-#include "trace.h"
-#include "trace2.h"
-#include "string-list.h"
-#include "pack-revindex.h"
-#include "hash.h"
-#include "path.h"
-#include "oid-array.h"
-#include "repository.h"
-#include "mem-pool.h"
-
-typedef struct git_zstream {
-       z_stream z;
-       unsigned long avail_in;
-       unsigned long avail_out;
-       unsigned long total_in;
-       unsigned long total_out;
-       unsigned char *next_in;
-       unsigned char *next_out;
-} git_zstream;
-
-void git_inflate_init(git_zstream *);
-void git_inflate_init_gzip_only(git_zstream *);
-void git_inflate_end(git_zstream *);
-int git_inflate(git_zstream *, int flush);
-
-void git_deflate_init(git_zstream *, int level);
-void git_deflate_init_gzip(git_zstream *, int level);
-void git_deflate_init_raw(git_zstream *, int level);
-void git_deflate_end(git_zstream *);
-int git_deflate_abort(git_zstream *);
-int git_deflate_end_gently(git_zstream *);
-int git_deflate(git_zstream *, int flush);
-unsigned long git_deflate_bound(git_zstream *, unsigned long);
-
-#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
-#define DTYPE(de)      ((de)->d_type)
-#else
-#undef DT_UNKNOWN
-#undef DT_DIR
-#undef DT_REG
-#undef DT_LNK
-#define DT_UNKNOWN     0
-#define DT_DIR         1
-#define DT_REG         2
-#define DT_LNK         3
-#define DTYPE(de)      DT_UNKNOWN
-#endif
-
-/* unknown mode (impossible combination S_IFIFO|S_IFCHR) */
-#define S_IFINVALID     0030000
-
-/*
- * A "directory link" is a link to another git directory.
- *
- * The value 0160000 is not normally a valid mode, and
- * also just happens to be S_IFDIR + S_IFLNK
- */
-#define S_IFGITLINK    0160000
-#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
-
-/*
- * Some mode bits are also used internally for computations.
- *
- * They *must* not overlap with any valid modes, and they *must* not be emitted
- * to outside world - i.e. appear on disk or network. In other words, it's just
- * temporary fields, which we internally use, but they have to stay in-house.
- *
- * ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
- *   codebase mode is `unsigned int` which is assumed to be at least 32 bits )
- */
-
-/* used internally in tree-diff */
-#define S_DIFFTREE_IFXMIN_NEQ  0x80000000
-
-
-/*
- * Intensive research over the course of many years has shown that
- * port 9418 is totally unused by anything else. Or
- *
- *     Your search - "port 9418" - did not match any documents.
- *
- * as www.google.com puts it.
- *
- * This port has been properly assigned for git use by IANA:
- * git (Assigned-9418) [I06-050728-0001].
- *
- *     git  9418/tcp   git pack transfer service
- *     git  9418/udp   git pack transfer service
- *
- * with Linus Torvalds <torvalds@osdl.org> as the point of
- * contact. September 2005.
- *
- * See http://www.iana.org/assignments/port-numbers
- */
-#define DEFAULT_GIT_PORT 9418
-
-/*
- * Basic data structures for the directory cache
- */
-
-#define CACHE_SIGNATURE 0x44495243     /* "DIRC" */
-struct cache_header {
-       uint32_t hdr_signature;
-       uint32_t hdr_version;
-       uint32_t hdr_entries;
-};
-
-#define INDEX_FORMAT_LB 2
-#define INDEX_FORMAT_UB 4
-
-/*
- * The "cache_time" is just the low 32 bits of the
- * time. It doesn't matter if it overflows - we only
- * check it for equality in the 32 bits we save.
- */
-struct cache_time {
-       uint32_t sec;
-       uint32_t nsec;
-};
-
-struct stat_data {
-       struct cache_time sd_ctime;
-       struct cache_time sd_mtime;
-       unsigned int sd_dev;
-       unsigned int sd_ino;
-       unsigned int sd_uid;
-       unsigned int sd_gid;
-       unsigned int sd_size;
-};
-
-struct cache_entry {
-       struct hashmap_entry ent;
-       struct stat_data ce_stat_data;
-       unsigned int ce_mode;
-       unsigned int ce_flags;
-       unsigned int mem_pool_allocated;
-       unsigned int ce_namelen;
-       unsigned int index;     /* for link extension */
-       struct object_id oid;
-       char name[FLEX_ARRAY]; /* more */
-};
-
-#define CE_STAGEMASK (0x3000)
-#define CE_EXTENDED  (0x4000)
-#define CE_VALID     (0x8000)
-#define CE_STAGESHIFT 12
-
-/*
- * Range 0xFFFF0FFF in ce_flags is divided into
- * two parts: in-memory flags and on-disk ones.
- * Flags in CE_EXTENDED_FLAGS will get saved on-disk
- * if you want to save a new flag, add it in
- * CE_EXTENDED_FLAGS
- *
- * In-memory only flags
- */
-#define CE_UPDATE            (1 << 16)
-#define CE_REMOVE            (1 << 17)
-#define CE_UPTODATE          (1 << 18)
-#define CE_ADDED             (1 << 19)
-
-#define CE_HASHED            (1 << 20)
-#define CE_FSMONITOR_VALID   (1 << 21)
-#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
-#define CE_CONFLICTED        (1 << 23)
-
-#define CE_UNPACKED          (1 << 24)
-#define CE_NEW_SKIP_WORKTREE (1 << 25)
-
-/* used to temporarily mark paths matched by pathspecs */
-#define CE_MATCHED           (1 << 26)
-
-#define CE_UPDATE_IN_BASE    (1 << 27)
-#define CE_STRIP_NAME        (1 << 28)
-
-/*
- * Extended on-disk flags
- */
-#define CE_INTENT_TO_ADD     (1 << 29)
-#define CE_SKIP_WORKTREE     (1 << 30)
-/* CE_EXTENDED2 is for future extension */
-#define CE_EXTENDED2         (1U << 31)
-
-#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
-
-/*
- * Safeguard to avoid saving wrong flags:
- *  - CE_EXTENDED2 won't get saved until its semantic is known
- *  - Bits in 0x0000FFFF have been saved in ce_flags already
- *  - Bits in 0x003F0000 are currently in-memory flags
- */
-#if CE_EXTENDED_FLAGS & 0x803FFFFF
-#error "CE_EXTENDED_FLAGS out of range"
-#endif
-
-#define S_ISSPARSEDIR(m) ((m) == S_IFDIR)
-
-/* Forward structure decls */
-struct pathspec;
-struct child_process;
-struct tree;
-
-/*
- * Copy the sha1 and stat state of a cache entry from one to
- * another. But we never change the name, or the hash state!
- */
-static inline void copy_cache_entry(struct cache_entry *dst,
-                                   const struct cache_entry *src)
-{
-       unsigned int state = dst->ce_flags & CE_HASHED;
-       int mem_pool_allocated = dst->mem_pool_allocated;
-
-       /* Don't copy hash chain and name */
-       memcpy(&dst->ce_stat_data, &src->ce_stat_data,
-                       offsetof(struct cache_entry, name) -
-                       offsetof(struct cache_entry, ce_stat_data));
-
-       /* Restore the hash state */
-       dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
-
-       /* Restore the mem_pool_allocated flag */
-       dst->mem_pool_allocated = mem_pool_allocated;
-}
-
-static inline unsigned create_ce_flags(unsigned stage)
-{
-       return (stage << CE_STAGESHIFT);
-}
-
-#define ce_namelen(ce) ((ce)->ce_namelen)
-#define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
-#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
-#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
-#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
-#define ce_intent_to_add(ce) ((ce)->ce_flags & CE_INTENT_TO_ADD)
-
-#define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
-static inline unsigned int create_ce_mode(unsigned int mode)
-{
-       if (S_ISLNK(mode))
-               return S_IFLNK;
-       if (S_ISSPARSEDIR(mode))
-               return S_IFDIR;
-       if (S_ISDIR(mode) || S_ISGITLINK(mode))
-               return S_IFGITLINK;
-       return S_IFREG | ce_permissions(mode);
-}
-static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
-                                            unsigned int mode)
-{
-       extern int trust_executable_bit, has_symlinks;
-       if (!has_symlinks && S_ISREG(mode) &&
-           ce && S_ISLNK(ce->ce_mode))
-               return ce->ce_mode;
-       if (!trust_executable_bit && S_ISREG(mode)) {
-               if (ce && S_ISREG(ce->ce_mode))
-                       return ce->ce_mode;
-               return create_ce_mode(0666);
-       }
-       return create_ce_mode(mode);
-}
-static inline int ce_to_dtype(const struct cache_entry *ce)
-{
-       unsigned ce_mode = ntohl(ce->ce_mode);
-       if (S_ISREG(ce_mode))
-               return DT_REG;
-       else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
-               return DT_DIR;
-       else if (S_ISLNK(ce_mode))
-               return DT_LNK;
-       else
-               return DT_UNKNOWN;
-}
-static inline unsigned int canon_mode(unsigned int mode)
-{
-       if (S_ISREG(mode))
-               return S_IFREG | ce_permissions(mode);
-       if (S_ISLNK(mode))
-               return S_IFLNK;
-       if (S_ISDIR(mode))
-               return S_IFDIR;
-       return S_IFGITLINK;
-}
-
-#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
-
-#define SOMETHING_CHANGED      (1 << 0) /* unclassified changes go here */
-#define CE_ENTRY_CHANGED       (1 << 1)
-#define CE_ENTRY_REMOVED       (1 << 2)
-#define CE_ENTRY_ADDED         (1 << 3)
-#define RESOLVE_UNDO_CHANGED   (1 << 4)
-#define CACHE_TREE_CHANGED     (1 << 5)
-#define SPLIT_INDEX_ORDERED    (1 << 6)
-#define UNTRACKED_CHANGED      (1 << 7)
-#define FSMONITOR_CHANGED      (1 << 8)
-
-struct split_index;
-struct untracked_cache;
-struct progress;
-struct pattern_list;
-
-enum sparse_index_mode {
-       /*
-        * There are no sparse directories in the index at all.
-        *
-        * Repositories that don't use cone-mode sparse-checkout will
-        * always have their indexes in this mode.
-        */
-       INDEX_EXPANDED = 0,
-
-       /*
-        * The index has already been collapsed to sparse directories
-        * whereever possible.
-        */
-       INDEX_COLLAPSED,
-
-       /*
-        * The sparse directories that exist are outside the
-        * sparse-checkout boundary, but it is possible that some file
-        * entries could collapse to sparse directory entries.
-        */
-       INDEX_PARTIALLY_SPARSE,
-};
-
-struct index_state {
-       struct cache_entry **cache;
-       unsigned int version;
-       unsigned int cache_nr, cache_alloc, cache_changed;
-       struct string_list *resolve_undo;
-       struct cache_tree *cache_tree;
-       struct split_index *split_index;
-       struct cache_time timestamp;
-       unsigned name_hash_initialized : 1,
-                initialized : 1,
-                drop_cache_tree : 1,
-                updated_workdir : 1,
-                updated_skipworktree : 1,
-                fsmonitor_has_run_once : 1;
-       enum sparse_index_mode sparse_index;
-       struct hashmap name_hash;
-       struct hashmap dir_hash;
-       struct object_id oid;
-       struct untracked_cache *untracked;
-       char *fsmonitor_last_update;
-       struct ewah_bitmap *fsmonitor_dirty;
-       struct mem_pool *ce_mem_pool;
-       struct progress *progress;
-       struct repository *repo;
-       struct pattern_list *sparse_checkout_patterns;
-};
-
-/* Name hashing */
-int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
-void add_name_hash(struct index_state *istate, struct cache_entry *ce);
-void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
-void free_name_hash(struct index_state *istate);
-
-/* Cache entry creation and cleanup */
-
-/*
- * Create cache_entry intended for use in the specified index. Caller
- * is responsible for discarding the cache_entry with
- * `discard_cache_entry`.
- */
-struct cache_entry *make_cache_entry(struct index_state *istate,
-                                    unsigned int mode,
-                                    const struct object_id *oid,
-                                    const char *path,
-                                    int stage,
-                                    unsigned int refresh_options);
-
-struct cache_entry *make_empty_cache_entry(struct index_state *istate,
-                                          size_t name_len);
-
-/*
- * Create a cache_entry that is not intended to be added to an index. If
- * `ce_mem_pool` is not NULL, the entry is allocated within the given memory
- * pool. Caller is responsible for discarding "loose" entries with
- * `discard_cache_entry()` and the memory pool with
- * `mem_pool_discard(ce_mem_pool, should_validate_cache_entries())`.
- */
-struct cache_entry *make_transient_cache_entry(unsigned int mode,
-                                              const struct object_id *oid,
-                                              const char *path,
-                                              int stage,
-                                              struct mem_pool *ce_mem_pool);
-
-struct cache_entry *make_empty_transient_cache_entry(size_t len,
-                                                    struct mem_pool *ce_mem_pool);
-
-/*
- * Discard cache entry.
- */
-void discard_cache_entry(struct cache_entry *ce);
-
-/*
- * Check configuration if we should perform extra validation on cache
- * entries.
- */
-int should_validate_cache_entries(void);
-
-/*
- * Duplicate a cache_entry. Allocate memory for the new entry from a
- * memory_pool. Takes into account cache_entry fields that are meant
- * for managing the underlying memory allocation of the cache_entry.
- */
-struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
-
-/*
- * Validate the cache entries in the index.  This is an internal
- * consistency check that the cache_entry structs are allocated from
- * the expected memory pool.
- */
-void validate_cache_entries(const struct index_state *istate);
-
-/*
- * Bulk prefetch all missing cache entries that are not GITLINKs and that match
- * the given predicate. This function should only be called if
- * has_promisor_remote() returns true.
- */
-typedef int (*must_prefetch_predicate)(const struct cache_entry *);
-void prefetch_cache_entries(const struct index_state *istate,
-                           must_prefetch_predicate must_prefetch);
-
-#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
-extern struct index_state the_index;
-
-#define active_cache (the_index.cache)
-#define active_nr (the_index.cache_nr)
-#define active_alloc (the_index.cache_alloc)
-#define active_cache_changed (the_index.cache_changed)
-#define active_cache_tree (the_index.cache_tree)
-
-#define read_cache() repo_read_index(the_repository)
-#define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
-#define read_cache_preload(pathspec) repo_read_index_preload(the_repository, (pathspec), 0)
-#define is_cache_unborn() is_index_unborn(&the_index)
-#define read_cache_unmerged() repo_read_index_unmerged(the_repository)
-#define discard_cache() discard_index(&the_index)
-#define unmerged_cache() unmerged_index(&the_index)
-#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
-#define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
-#define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
-#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
-#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
-#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
-#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
-#define chmod_cache_entry(ce, flip) chmod_index_entry(&the_index, (ce), (flip))
-#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
-#define refresh_and_write_cache(refresh_flags, write_flags, gentle) repo_refresh_and_write_index(the_repository, (refresh_flags), (write_flags), (gentle), NULL, NULL, NULL)
-#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
-#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
-#define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
-#define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
-#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
-#define resolve_undo_clear() resolve_undo_clear_index(&the_index)
-#define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
-#define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
-#define read_blob_data_from_cache(path, sz) read_blob_data_from_index(&the_index, (path), (sz))
-#define hold_locked_index(lock_file, flags) repo_hold_locked_index(the_repository, (lock_file), (flags))
-#endif
-
-#define TYPE_BITS 3
-
-/*
- * Values in this enum (except those outside the 3 bit range) are part
- * of pack file format. See gitformat-pack(5) for more information.
- */
-enum object_type {
-       OBJ_BAD = -1,
-       OBJ_NONE = 0,
-       OBJ_COMMIT = 1,
-       OBJ_TREE = 2,
-       OBJ_BLOB = 3,
-       OBJ_TAG = 4,
-       /* 5 for future expansion */
-       OBJ_OFS_DELTA = 6,
-       OBJ_REF_DELTA = 7,
-       OBJ_ANY,
-       OBJ_MAX
-};
-
-static inline enum object_type object_type(unsigned int mode)
-{
-       return S_ISDIR(mode) ? OBJ_TREE :
-               S_ISGITLINK(mode) ? OBJ_COMMIT :
-               OBJ_BLOB;
-}
-
-/* Double-check local_repo_env below if you add to this list. */
-#define GIT_DIR_ENVIRONMENT "GIT_DIR"
-#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
-#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
-#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
-#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
-#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
-#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
-#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
-#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
-#define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
-#define GIT_SHALLOW_FILE_ENVIRONMENT "GIT_SHALLOW_FILE"
-#define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
-#define CONFIG_ENVIRONMENT "GIT_CONFIG"
-#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
-#define CONFIG_COUNT_ENVIRONMENT "GIT_CONFIG_COUNT"
-#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
-#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
-#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
-#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
-#define GITATTRIBUTES_FILE ".gitattributes"
-#define INFOATTRIBUTES_FILE "info/attributes"
-#define ATTRIBUTE_MACRO_PREFIX "[attr]"
-#define GITMODULES_FILE ".gitmodules"
-#define GITMODULES_INDEX ":.gitmodules"
-#define GITMODULES_HEAD "HEAD:.gitmodules"
-#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
-#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
-#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
-#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
-#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
-#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
-#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
-#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
-#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
-#define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
-#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
-#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
-
-/*
- * Environment variable used in handshaking the wire protocol.
- * Contains a colon ':' separated list of keys with optional values
- * 'key[=value]'.  Presence of unknown keys and values must be
- * ignored.
- */
-#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
-/* HTTP header used to handshake the wire protocol */
-#define GIT_PROTOCOL_HEADER "Git-Protocol"
-
-/*
- * This environment variable is expected to contain a boolean indicating
- * whether we should or should not treat:
- *
- *   GIT_DIR=foo.git git ...
- *
- * as if GIT_WORK_TREE=. was given. It's not expected that users will make use
- * of this, but we use it internally to communicate to sub-processes that we
- * are in a bare repo. If not set, defaults to true.
- */
-#define GIT_IMPLICIT_WORK_TREE_ENVIRONMENT "GIT_IMPLICIT_WORK_TREE"
-
-/*
- * Repository-local GIT_* environment variables; these will be cleared
- * when git spawns a sub-process that runs inside another repository.
- * The array is NULL-terminated, which makes it easy to pass in the "env"
- * parameter of a run-command invocation, or to do a simple walk.
- */
-extern const char * const local_repo_env[];
-
-void setup_git_env(const char *git_dir);
-
-/*
- * Returns true iff we have a configured git repository (either via
- * setup_git_directory, or in the environment via $GIT_DIR).
- */
-int have_git_dir(void);
-
-extern int is_bare_repository_cfg;
-int is_bare_repository(void);
-int is_inside_git_dir(void);
-extern char *git_work_tree_cfg;
-int is_inside_work_tree(void);
-const char *get_git_dir(void);
-const char *get_git_common_dir(void);
-const char *get_object_directory(void);
-char *get_index_file(void);
-char *get_graft_file(struct repository *r);
-void set_git_dir(const char *path, int make_realpath);
-int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
-int get_common_dir(struct strbuf *sb, const char *gitdir);
-const char *get_git_namespace(void);
-const char *strip_namespace(const char *namespaced_ref);
-const char *get_super_prefix(void);
-const char *get_git_work_tree(void);
-
-/*
- * Return true if the given path is a git directory; note that this _just_
- * looks at the directory itself. If you want to know whether "foo/.git"
- * is a repository, you must feed that path, not just "foo".
- */
-int is_git_directory(const char *path);
-
-/*
- * Return 1 if the given path is the root of a git repository or
- * submodule, else 0. Will not return 1 for bare repositories with the
- * exception of creating a bare repository in "foo/.git" and calling
- * is_git_repository("foo").
- *
- * If we run into read errors, we err on the side of saying "yes, it is",
- * as we usually consider sub-repos precious, and would prefer to err on the
- * side of not disrupting or deleting them.
- */
-int is_nonbare_repository_dir(struct strbuf *path);
-
-#define READ_GITFILE_ERR_STAT_FAILED 1
-#define READ_GITFILE_ERR_NOT_A_FILE 2
-#define READ_GITFILE_ERR_OPEN_FAILED 3
-#define READ_GITFILE_ERR_READ_FAILED 4
-#define READ_GITFILE_ERR_INVALID_FORMAT 5
-#define READ_GITFILE_ERR_NO_PATH 6
-#define READ_GITFILE_ERR_NOT_A_REPO 7
-#define READ_GITFILE_ERR_TOO_LARGE 8
-void read_gitfile_error_die(int error_code, const char *path, const char *dir);
-const char *read_gitfile_gently(const char *path, int *return_error_code);
-#define read_gitfile(path) read_gitfile_gently((path), NULL)
-const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
-#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
-
-void set_git_work_tree(const char *tree);
-
-#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
-
-void setup_work_tree(void);
-/*
- * Find the commondir and gitdir of the repository that contains the current
- * working directory, without changing the working directory or other global
- * state. The result is appended to commondir and gitdir.  If the discovered
- * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will
- * both have the same result appended to the buffer.  The return value is
- * either 0 upon success and non-zero if no repository was found.
- */
-int discover_git_directory(struct strbuf *commondir,
-                          struct strbuf *gitdir);
-const char *setup_git_directory_gently(int *);
-const char *setup_git_directory(void);
-char *prefix_path(const char *prefix, int len, const char *path);
-char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
-
-/*
- * Concatenate "prefix" (if len is non-zero) and "path", with no
- * connecting characters (so "prefix" should end with a "/").
- * Unlike prefix_path, this should be used if the named file does
- * not have to interact with index entry; i.e. name of a random file
- * on the filesystem.
- *
- * The return value is always a newly allocated string (even if the
- * prefix was empty).
- */
-char *prefix_filename(const char *prefix, const char *path);
-
-int check_filename(const char *prefix, const char *name);
-void verify_filename(const char *prefix,
-                    const char *name,
-                    int diagnose_misspelt_rev);
-void verify_non_filename(const char *prefix, const char *name);
-int path_inside_repo(const char *prefix, const char *path);
-
-#define INIT_DB_QUIET 0x0001
-#define INIT_DB_EXIST_OK 0x0002
-
-int init_db(const char *git_dir, const char *real_git_dir,
-           const char *template_dir, int hash_algo,
-           const char *initial_branch, unsigned int flags);
-void initialize_repository_version(int hash_algo, int reinit);
-
-void sanitize_stdfds(void);
-int daemonize(void);
-
-#define alloc_nr(x) (((x)+16)*3/2)
-
-/**
- * Dynamically growing an array using realloc() is error prone and boring.
- *
- * Define your array with:
- *
- * - a pointer (`item`) that points at the array, initialized to `NULL`
- *   (although please name the variable based on its contents, not on its
- *   type);
- *
- * - an integer variable (`alloc`) that keeps track of how big the current
- *   allocation is, initialized to `0`;
- *
- * - another integer variable (`nr`) to keep track of how many elements the
- *   array currently has, initialized to `0`.
- *
- * Then before adding `n`th element to the item, call `ALLOC_GROW(item, n,
- * alloc)`.  This ensures that the array can hold at least `n` elements by
- * calling `realloc(3)` and adjusting `alloc` variable.
- *
- * ------------
- * sometype *item;
- * size_t nr;
- * size_t alloc
- *
- * for (i = 0; i < nr; i++)
- *     if (we like item[i] already)
- *             return;
- *
- * // we did not like any existing one, so add one
- * ALLOC_GROW(item, nr + 1, alloc);
- * item[nr++] = value you like;
- * ------------
- *
- * You are responsible for updating the `nr` variable.
- *
- * If you need to specify the number of elements to allocate explicitly
- * then use the macro `REALLOC_ARRAY(item, alloc)` instead of `ALLOC_GROW`.
- *
- * Consider using ALLOC_GROW_BY instead of ALLOC_GROW as it has some
- * added niceties.
- *
- * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
- */
-#define ALLOC_GROW(x, nr, alloc) \
-       do { \
-               if ((nr) > alloc) { \
-                       if (alloc_nr(alloc) < (nr)) \
-                               alloc = (nr); \
-                       else \
-                               alloc = alloc_nr(alloc); \
-                       REALLOC_ARRAY(x, alloc); \
-               } \
-       } while (0)
-
-/*
- * Similar to ALLOC_GROW but handles updating of the nr value and
- * zeroing the bytes of the newly-grown array elements.
- *
- * DO NOT USE any expression with side-effect for any of the
- * arguments.
- */
-#define ALLOC_GROW_BY(x, nr, increase, alloc) \
-       do { \
-               if (increase) { \
-                       size_t new_nr = nr + (increase); \
-                       if (new_nr < nr) \
-                               BUG("negative growth in ALLOC_GROW_BY"); \
-                       ALLOC_GROW(x, new_nr, alloc); \
-                       memset((x) + nr, 0, sizeof(*(x)) * (increase)); \
-                       nr = new_nr; \
-               } \
-       } while (0)
-
-/* Initialize and use the cache information */
-struct lock_file;
-void preload_index(struct index_state *index,
-                  const struct pathspec *pathspec,
-                  unsigned int refresh_flags);
-int do_read_index(struct index_state *istate, const char *path,
-                 int must_exist); /* for testting only! */
-int read_index_from(struct index_state *, const char *path,
-                   const char *gitdir);
-int is_index_unborn(struct index_state *);
-
-void ensure_full_index(struct index_state *istate);
-
-/* For use with `write_locked_index()`. */
-#define COMMIT_LOCK            (1 << 0)
-#define SKIP_IF_UNCHANGED      (1 << 1)
-
-/*
- * Write the index while holding an already-taken lock. Close the lock,
- * and if `COMMIT_LOCK` is given, commit it.
- *
- * Unless a split index is in use, write the index into the lockfile.
- *
- * With a split index, write the shared index to a temporary file,
- * adjust its permissions and rename it into place, then write the
- * split index to the lockfile. If the temporary file for the shared
- * index cannot be created, fall back to the behavior described in
- * the previous paragraph.
- *
- * With `COMMIT_LOCK`, the lock is always committed or rolled back.
- * Without it, the lock is closed, but neither committed nor rolled
- * back.
- *
- * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
- * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
- */
-int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
-
-int discard_index(struct index_state *);
-void move_index_extensions(struct index_state *dst, struct index_state *src);
-int unmerged_index(const struct index_state *);
-
-/**
- * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
- * compares istate to HEAD.  If tree is NULL and on an unborn branch,
- * returns 1 if there are entries in istate, 0 otherwise.  If an strbuf is
- * provided, the space-separated list of files that differ will be appended
- * to it.
- */
-int repo_index_has_changes(struct repository *repo,
-                          struct tree *tree,
-                          struct strbuf *sb);
-
-int verify_path(const char *path, unsigned mode);
-int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
-int index_dir_exists(struct index_state *istate, const char *name, int namelen);
-void adjust_dirname_case(struct index_state *istate, char *name);
-struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
-
-/*
- * Searches for an entry defined by name and namelen in the given index.
- * If the return value is positive (including 0) it is the position of an
- * exact match. If the return value is negative, the negated value minus 1
- * is the position where the entry would be inserted.
- * Example: The current index consists of these files and its stages:
- *
- *   b#0, d#0, f#1, f#3
- *
- * index_name_pos(&index, "a", 1) -> -1
- * index_name_pos(&index, "b", 1) ->  0
- * index_name_pos(&index, "c", 1) -> -2
- * index_name_pos(&index, "d", 1) ->  1
- * index_name_pos(&index, "e", 1) -> -3
- * index_name_pos(&index, "f", 1) -> -3
- * index_name_pos(&index, "g", 1) -> -5
- */
-int index_name_pos(struct index_state *, const char *name, int namelen);
-
-/*
- * Like index_name_pos, returns the position of an entry of the given name in
- * the index if one exists, otherwise returns a negative value where the negated
- * value minus 1 is the position where the index entry would be inserted. Unlike
- * index_name_pos, however, a sparse index is not expanded to find an entry
- * inside a sparse directory.
- */
-int index_name_pos_sparse(struct index_state *, const char *name, int namelen);
-
-/*
- * Determines whether an entry with the given name exists within the
- * given index. The return value is 1 if an exact match is found, otherwise
- * it is 0. Note that, unlike index_name_pos, this function does not expand
- * the index if it is sparse. If an item exists within the full index but it
- * is contained within a sparse directory (and not in the sparse index), 0 is
- * returned.
- */
-int index_entry_exists(struct index_state *, const char *name, int namelen);
-
-/*
- * Some functions return the negative complement of an insert position when a
- * precise match was not found but a position was found where the entry would
- * need to be inserted. This helper protects that logic from any integer
- * underflow.
- */
-static inline int index_pos_to_insert_pos(uintmax_t pos)
-{
-       if (pos > INT_MAX)
-               die("overflow: -1 - %"PRIuMAX, pos);
-       return -1 - (int)pos;
-}
-
-#define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
-#define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
-#define ADD_CACHE_SKIP_DFCHECK 4       /* Ok to skip DF conflict checks */
-#define ADD_CACHE_JUST_APPEND 8                /* Append only */
-#define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
-#define ADD_CACHE_KEEP_CACHE_TREE 32   /* Do not invalidate cache-tree */
-#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
-int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
-void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
-
-/* Remove entry, return true if there are more entries to go. */
-int remove_index_entry_at(struct index_state *, int pos);
-
-void remove_marked_cache_entries(struct index_state *istate, int invalidate);
-int remove_file_from_index(struct index_state *, const char *path);
-#define ADD_CACHE_VERBOSE 1
-#define ADD_CACHE_PRETEND 2
-#define ADD_CACHE_IGNORE_ERRORS        4
-#define ADD_CACHE_IGNORE_REMOVAL 8
-#define ADD_CACHE_INTENT 16
-/*
- * These two are used to add the contents of the file at path
- * to the index, marking the working tree up-to-date by storing
- * the cached stat info in the resulting cache entry.  A caller
- * that has already run lstat(2) on the path can call
- * add_to_index(), and all others can call add_file_to_index();
- * the latter will do necessary lstat(2) internally before
- * calling the former.
- */
-int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
-int add_file_to_index(struct index_state *, const char *path, int flags);
-
-int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
-int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
-void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
-int index_name_is_other(struct index_state *, const char *, int);
-void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
-
-/* do stat comparison even if CE_VALID is true */
-#define CE_MATCH_IGNORE_VALID          01
-/* do not check the contents but report dirty on racily-clean entries */
-#define CE_MATCH_RACY_IS_DIRTY         02
-/* do stat comparison even if CE_SKIP_WORKTREE is true */
-#define CE_MATCH_IGNORE_SKIP_WORKTREE  04
-/* ignore non-existent files during stat update  */
-#define CE_MATCH_IGNORE_MISSING                0x08
-/* enable stat refresh */
-#define CE_MATCH_REFRESH               0x10
-/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
-#define CE_MATCH_IGNORE_FSMONITOR 0X20
-int is_racy_timestamp(const struct index_state *istate,
-                     const struct cache_entry *ce);
-int has_racy_timestamp(struct index_state *istate);
-int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-
-#define HASH_WRITE_OBJECT 1
-#define HASH_FORMAT_CHECK 2
-#define HASH_RENORMALIZE  4
-#define HASH_SILENT 8
-int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
-int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
-
-/*
- * Record to sd the data from st that we use to check whether a file
- * might have changed.
- */
-void fill_stat_data(struct stat_data *sd, struct stat *st);
-
-/*
- * Return 0 if st is consistent with a file not having been changed
- * since sd was filled.  If there are differences, return a
- * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
- * INODE_CHANGED, and DATA_CHANGED.
- */
-int match_stat_data(const struct stat_data *sd, struct stat *st);
-int match_stat_data_racy(const struct index_state *istate,
-                        const struct stat_data *sd, struct stat *st);
-
-void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st);
-
-#define REFRESH_REALLY                   (1 << 0) /* ignore_valid */
-#define REFRESH_UNMERGED                 (1 << 1) /* allow unmerged */
-#define REFRESH_QUIET                    (1 << 2) /* be quiet about it */
-#define REFRESH_IGNORE_MISSING           (1 << 3) /* ignore non-existent */
-#define REFRESH_IGNORE_SUBMODULES        (1 << 4) /* ignore submodules */
-#define REFRESH_IN_PORCELAIN             (1 << 5) /* user friendly output, not "needs update" */
-#define REFRESH_PROGRESS                 (1 << 6) /* show progress bar if stderr is tty */
-#define REFRESH_IGNORE_SKIP_WORKTREE     (1 << 7) /* ignore skip_worktree entries */
-int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
-/*
- * Refresh the index and write it to disk.
- *
- * 'refresh_flags' is passed directly to 'refresh_index()', while
- * 'COMMIT_LOCK | write_flags' is passed to 'write_locked_index()', so
- * the lockfile is always either committed or rolled back.
- *
- * If 'gentle' is passed, errors locking the index are ignored.
- *
- * Return 1 if refreshing the index returns an error, -1 if writing
- * the index to disk fails, 0 on success.
- *
- * Note that if refreshing the index returns an error, we still write
- * out the index (unless locking fails).
- */
-int repo_refresh_and_write_index(struct repository*, unsigned int refresh_flags, unsigned int write_flags, int gentle, const struct pathspec *, char *seen, const char *header_msg);
-
-struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
-
-void set_alternate_index_output(const char *);
-
-extern int verify_index_checksum;
-extern int verify_ce_order;
-
-/* Environment bits from configuration mechanism */
-extern int trust_executable_bit;
-extern int trust_ctime;
-extern int check_stat;
-extern int quote_path_fully;
-extern int has_symlinks;
-extern int minimum_abbrev, default_abbrev;
-extern int ignore_case;
-extern int assume_unchanged;
-extern int prefer_symlink_refs;
-extern int warn_ambiguous_refs;
-extern int warn_on_object_refname_ambiguity;
-extern char *apply_default_whitespace;
-extern char *apply_default_ignorewhitespace;
-extern const char *git_attributes_file;
-extern const char *git_hooks_path;
-extern int zlib_compression_level;
-extern int pack_compression_level;
-extern size_t packed_git_window_size;
-extern size_t packed_git_limit;
-extern size_t delta_base_cache_limit;
-extern unsigned long big_file_threshold;
-extern unsigned long pack_size_limit_cfg;
-
-/*
- * Accessors for the core.sharedrepository config which lazy-load the value
- * from the config (if not already set). The "reset" function can be
- * used to unset "set" or cached value, meaning that the value will be loaded
- * fresh from the config file on the next call to get_shared_repository().
- */
-void set_shared_repository(int value);
-int get_shared_repository(void);
-void reset_shared_repository(void);
-
-/*
- * Do replace refs need to be checked this run?  This variable is
- * initialized to true unless --no-replace-object is used or
- * $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some
- * commands that do not want replace references to be active.
- */
-extern int read_replace_refs;
-
-/*
- * These values are used to help identify parts of a repository to fsync.
- * FSYNC_COMPONENT_NONE identifies data that will not be a persistent part of the
- * repository and so shouldn't be fsynced.
- */
-enum fsync_component {
-       FSYNC_COMPONENT_NONE,
-       FSYNC_COMPONENT_LOOSE_OBJECT            = 1 << 0,
-       FSYNC_COMPONENT_PACK                    = 1 << 1,
-       FSYNC_COMPONENT_PACK_METADATA           = 1 << 2,
-       FSYNC_COMPONENT_COMMIT_GRAPH            = 1 << 3,
-       FSYNC_COMPONENT_INDEX                   = 1 << 4,
-       FSYNC_COMPONENT_REFERENCE               = 1 << 5,
-};
-
-#define FSYNC_COMPONENTS_OBJECTS (FSYNC_COMPONENT_LOOSE_OBJECT | \
-                                 FSYNC_COMPONENT_PACK)
-
-#define FSYNC_COMPONENTS_DERIVED_METADATA (FSYNC_COMPONENT_PACK_METADATA | \
-                                          FSYNC_COMPONENT_COMMIT_GRAPH)
-
-#define FSYNC_COMPONENTS_DEFAULT ((FSYNC_COMPONENTS_OBJECTS | \
-                                  FSYNC_COMPONENTS_DERIVED_METADATA) & \
-                                 ~FSYNC_COMPONENT_LOOSE_OBJECT)
-
-#define FSYNC_COMPONENTS_COMMITTED (FSYNC_COMPONENTS_OBJECTS | \
-                                   FSYNC_COMPONENT_REFERENCE)
-
-#define FSYNC_COMPONENTS_ADDED (FSYNC_COMPONENTS_COMMITTED | \
-                               FSYNC_COMPONENT_INDEX)
-
-#define FSYNC_COMPONENTS_ALL (FSYNC_COMPONENT_LOOSE_OBJECT | \
-                             FSYNC_COMPONENT_PACK | \
-                             FSYNC_COMPONENT_PACK_METADATA | \
-                             FSYNC_COMPONENT_COMMIT_GRAPH | \
-                             FSYNC_COMPONENT_INDEX | \
-                             FSYNC_COMPONENT_REFERENCE)
-
-#ifndef FSYNC_COMPONENTS_PLATFORM_DEFAULT
-#define FSYNC_COMPONENTS_PLATFORM_DEFAULT FSYNC_COMPONENTS_DEFAULT
-#endif
-
-/*
- * A bitmask indicating which components of the repo should be fsynced.
- */
-extern enum fsync_component fsync_components;
-extern int fsync_object_files;
-extern int use_fsync;
-
-enum fsync_method {
-       FSYNC_METHOD_FSYNC,
-       FSYNC_METHOD_WRITEOUT_ONLY,
-       FSYNC_METHOD_BATCH,
-};
-
-extern enum fsync_method fsync_method;
-extern int core_preload_index;
-extern int precomposed_unicode;
-extern int protect_hfs;
-extern int protect_ntfs;
-
-extern int core_apply_sparse_checkout;
-extern int core_sparse_checkout_cone;
-extern int sparse_expect_files_outside_of_patterns;
-
-/*
- * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
- */
-int use_optional_locks(void);
-
-/*
- * The character that begins a commented line in user-editable file
- * that is subject to stripspace.
- */
-extern char comment_line_char;
-extern int auto_comment_line_char;
-
-enum log_refs_config {
-       LOG_REFS_UNSET = -1,
-       LOG_REFS_NONE = 0,
-       LOG_REFS_NORMAL,
-       LOG_REFS_ALWAYS
-};
-extern enum log_refs_config log_all_ref_updates;
-
-enum rebase_setup_type {
-       AUTOREBASE_NEVER = 0,
-       AUTOREBASE_LOCAL,
-       AUTOREBASE_REMOTE,
-       AUTOREBASE_ALWAYS
-};
-
-enum push_default_type {
-       PUSH_DEFAULT_NOTHING = 0,
-       PUSH_DEFAULT_MATCHING,
-       PUSH_DEFAULT_SIMPLE,
-       PUSH_DEFAULT_UPSTREAM,
-       PUSH_DEFAULT_CURRENT,
-       PUSH_DEFAULT_UNSPECIFIED
-};
-
-extern enum rebase_setup_type autorebase;
-extern enum push_default_type push_default;
-
-enum object_creation_mode {
-       OBJECT_CREATION_USES_HARDLINKS = 0,
-       OBJECT_CREATION_USES_RENAMES = 1
-};
-
-extern enum object_creation_mode object_creation_mode;
-
-extern char *notes_ref_name;
-
-extern int grafts_replace_parents;
-
-/*
- * GIT_REPO_VERSION is the version we write by default. The
- * _READ variant is the highest number we know how to
- * handle.
- */
-#define GIT_REPO_VERSION 0
-#define GIT_REPO_VERSION_READ 1
-extern int repository_format_precious_objects;
-extern int repository_format_worktree_config;
-
-/*
- * You _have_ to initialize a `struct repository_format` using
- * `= REPOSITORY_FORMAT_INIT` before calling `read_repository_format()`.
- */
-struct repository_format {
-       int version;
-       int precious_objects;
-       char *partial_clone; /* value of extensions.partialclone */
-       int worktree_config;
-       int is_bare;
-       int hash_algo;
-       int sparse_index;
-       char *work_tree;
-       struct string_list unknown_extensions;
-       struct string_list v1_only_extensions;
-};
-
-/*
- * Always use this to initialize a `struct repository_format`
- * to a well-defined, default state before calling
- * `read_repository()`.
- */
-#define REPOSITORY_FORMAT_INIT \
-{ \
-       .version = -1, \
-       .is_bare = -1, \
-       .hash_algo = GIT_HASH_SHA1, \
-       .unknown_extensions = STRING_LIST_INIT_DUP, \
-       .v1_only_extensions = STRING_LIST_INIT_DUP, \
-}
-
-/*
- * Read the repository format characteristics from the config file "path" into
- * "format" struct. Returns the numeric version. On error, or if no version is
- * found in the configuration, -1 is returned, format->version is set to -1,
- * and all other fields in the struct are set to the default configuration
- * (REPOSITORY_FORMAT_INIT). Always initialize the struct using
- * REPOSITORY_FORMAT_INIT before calling this function.
- */
-int read_repository_format(struct repository_format *format, const char *path);
-
-/*
- * Free the memory held onto by `format`, but not the struct itself.
- * (No need to use this after `read_repository_format()` fails.)
- */
-void clear_repository_format(struct repository_format *format);
-
-/*
- * Verify that the repository described by repository_format is something we
- * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
- * any errors encountered.
- */
-int verify_repository_format(const struct repository_format *format,
-                            struct strbuf *err);
-
-/*
- * Check the repository format version in the path found in get_git_dir(),
- * and die if it is a version we don't understand. Generally one would
- * set_git_dir() before calling this, and use it only for "are we in a valid
- * repo?".
- *
- * If successful and fmt is not NULL, fill fmt with data.
- */
-void check_repository_format(struct repository_format *fmt);
-
-#define MTIME_CHANGED  0x0001
-#define CTIME_CHANGED  0x0002
-#define OWNER_CHANGED  0x0004
-#define MODE_CHANGED    0x0008
-#define INODE_CHANGED   0x0010
-#define DATA_CHANGED    0x0020
-#define TYPE_CHANGED    0x0040
-
-/*
- * Return an abbreviated sha1 unique within this repository's object database.
- * The result will be at least `len` characters long, and will be NUL
- * terminated.
- *
- * The non-`_r` version returns a static buffer which remains valid until 4
- * more calls to find_unique_abbrev are made.
- *
- * The `_r` variant writes to a buffer supplied by the caller, which must be at
- * least `GIT_MAX_HEXSZ + 1` bytes. The return value is the number of bytes
- * written (excluding the NUL terminator).
- *
- * Note that while this version avoids the static buffer, it is not fully
- * reentrant, as it calls into other non-reentrant git code.
- */
-const char *repo_find_unique_abbrev(struct repository *r, const struct object_id *oid, int len);
-#define find_unique_abbrev(oid, len) repo_find_unique_abbrev(the_repository, oid, len)
-int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len);
-#define find_unique_abbrev_r(hex, oid, len) repo_find_unique_abbrev_r(the_repository, hex, oid, len)
-
-/* set default permissions by passing mode arguments to open(2) */
-int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
-int git_mkstemp_mode(char *pattern, int mode);
-
-/*
- * NOTE NOTE NOTE!!
- *
- * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must
- * not be changed. Old repositories have core.sharedrepository written in
- * numeric format, and therefore these values are preserved for compatibility
- * reasons.
- */
-enum sharedrepo {
-       PERM_UMASK          = 0,
-       OLD_PERM_GROUP      = 1,
-       OLD_PERM_EVERYBODY  = 2,
-       PERM_GROUP          = 0660,
-       PERM_EVERYBODY      = 0664
-};
-int git_config_perm(const char *var, const char *value);
-int adjust_shared_perm(const char *path);
-
-/*
- * Create the directory containing the named path, using care to be
- * somewhat safe against races. Return one of the scld_error values to
- * indicate success/failure. On error, set errno to describe the
- * problem.
- *
- * SCLD_VANISHED indicates that one of the ancestor directories of the
- * path existed at one point during the function call and then
- * suddenly vanished, probably because another process pruned the
- * directory while we were working.  To be robust against this kind of
- * race, callers might want to try invoking the function again when it
- * returns SCLD_VANISHED.
- *
- * safe_create_leading_directories() temporarily changes path while it
- * is working but restores it before returning.
- * safe_create_leading_directories_const() doesn't modify path, even
- * temporarily. Both these variants adjust the permissions of the
- * created directories to honor core.sharedRepository, so they are best
- * suited for files inside the git dir. For working tree files, use
- * safe_create_leading_directories_no_share() instead, as it ignores
- * the core.sharedRepository setting.
- */
-enum scld_error {
-       SCLD_OK = 0,
-       SCLD_FAILED = -1,
-       SCLD_PERMS = -2,
-       SCLD_EXISTS = -3,
-       SCLD_VANISHED = -4
-};
-enum scld_error safe_create_leading_directories(char *path);
-enum scld_error safe_create_leading_directories_const(const char *path);
-enum scld_error safe_create_leading_directories_no_share(char *path);
-
-int mkdir_in_gitdir(const char *path);
-char *interpolate_path(const char *path, int real_home);
-/* NEEDSWORK: remove this synonym once in-flight topics have migrated */
-#define expand_user_path interpolate_path
-const char *enter_repo(const char *path, int strict);
-static inline int is_absolute_path(const char *path)
-{
-       return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
-}
-int is_directory(const char *);
-char *strbuf_realpath(struct strbuf *resolved, const char *path,
-                     int die_on_error);
-char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
-                               int die_on_error);
-char *real_pathdup(const char *path, int die_on_error);
-const char *absolute_path(const char *path);
-char *absolute_pathdup(const char *path);
-const char *remove_leading_path(const char *in, const char *prefix);
-const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
-int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
-int normalize_path_copy(char *dst, const char *src);
-int longest_ancestor_length(const char *path, struct string_list *prefixes);
-char *strip_path_suffix(const char *path, const char *suffix);
-int daemon_avoid_alias(const char *path);
-
-/*
- * These functions match their is_hfs_dotgit() counterparts; see utf8.h for
- * details.
- */
-int is_ntfs_dotgit(const char *name);
-int is_ntfs_dotgitmodules(const char *name);
-int is_ntfs_dotgitignore(const char *name);
-int is_ntfs_dotgitattributes(const char *name);
-int is_ntfs_dotmailmap(const char *name);
-
-/*
- * Returns true iff "str" could be confused as a command-line option when
- * passed to a sub-program like "ssh". Note that this has nothing to do with
- * shell-quoting, which should be handled separately; we're assuming here that
- * the string makes it verbatim to the sub-program.
- */
-int looks_like_command_line_option(const char *str);
-
-/**
- * Return a newly allocated string with the evaluation of
- * "$XDG_CONFIG_HOME/$subdir/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
- * "$HOME/.config/$subdir/$filename". Return NULL upon error.
- */
-char *xdg_config_home_for(const char *subdir, const char *filename);
-
-/**
- * Return a newly allocated string with the evaluation of
- * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
- * "$HOME/.config/git/$filename". Return NULL upon error.
- */
-char *xdg_config_home(const char *filename);
-
-/**
- * Return a newly allocated string with the evaluation of
- * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
- * "$HOME/.cache/git/$filename". Return NULL upon error.
- */
-char *xdg_cache_home(const char *filename);
-
-int git_open_cloexec(const char *name, int flags);
-#define git_open(name) git_open_cloexec(name, O_RDONLY)
-
-/**
- * unpack_loose_header() initializes the data stream needed to unpack
- * a loose object header.
- *
- * Returns:
- *
- * - ULHR_OK on success
- * - ULHR_BAD on error
- * - ULHR_TOO_LONG if the header was too long
- *
- * It will only parse up to MAX_HEADER_LEN bytes unless an optional
- * "hdrbuf" argument is non-NULL. This is intended for use with
- * OBJECT_INFO_ALLOW_UNKNOWN_TYPE to extract the bad type for (error)
- * reporting. The full header will be extracted to "hdrbuf" for use
- * with parse_loose_header(), ULHR_TOO_LONG will still be returned
- * from this function to indicate that the header was too long.
- */
-enum unpack_loose_header_result {
-       ULHR_OK,
-       ULHR_BAD,
-       ULHR_TOO_LONG,
-};
-enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
-                                                   unsigned char *map,
-                                                   unsigned long mapsize,
-                                                   void *buffer,
-                                                   unsigned long bufsiz,
-                                                   struct strbuf *hdrbuf);
-
-/**
- * parse_loose_header() parses the starting "<type> <len>\0" of an
- * object. If it doesn't follow that format -1 is returned. To check
- * the validity of the <type> populate the "typep" in the "struct
- * object_info". It will be OBJ_BAD if the object type is unknown. The
- * parsed <len> can be retrieved via "oi->sizep", and from there
- * passed to unpack_loose_rest().
- */
-struct object_info;
-int parse_loose_header(const char *hdr, struct object_info *oi);
-
-/**
- * With in-core object data in "buf", rehash it to make sure the
- * object name actually matches "oid" to detect object corruption.
- *
- * A negative value indicates an error, usually that the OID is not
- * what we expected, but it might also indicate another error.
- */
-int check_object_signature(struct repository *r, const struct object_id *oid,
-                          void *map, unsigned long size,
-                          enum object_type type);
-
-/**
- * A streaming version of check_object_signature().
- * Try reading the object named with "oid" using
- * the streaming interface and rehash it to do the same.
- */
-int stream_object_signature(struct repository *r, const struct object_id *oid);
-
-int finalize_object_file(const char *tmpfile, const char *filename);
-
-/* Helper to check and "touch" a file */
-int check_and_freshen_file(const char *fn, int freshen);
-
-extern const signed char hexval_table[256];
-static inline unsigned int hexval(unsigned char c)
-{
-       return hexval_table[c];
-}
-
-/*
- * Convert two consecutive hexadecimal digits into a char.  Return a
- * negative value on error.  Don't run over the end of short strings.
- */
-static inline int hex2chr(const char *s)
-{
-       unsigned int val = hexval(s[0]);
-       return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
-}
-
-/* Convert to/from hex/sha1 representation */
-#define MINIMUM_ABBREV minimum_abbrev
-#define DEFAULT_ABBREV default_abbrev
-
-/* used when the code does not know or care what the default abbrev is */
-#define FALLBACK_DEFAULT_ABBREV 7
-
-struct object_context {
-       unsigned short mode;
-       /*
-        * symlink_path is only used by get_tree_entry_follow_symlinks,
-        * and only for symlinks that point outside the repository.
-        */
-       struct strbuf symlink_path;
-       /*
-        * If GET_OID_RECORD_PATH is set, this will record path (if any)
-        * found when resolving the name. The caller is responsible for
-        * releasing the memory.
-        */
-       char *path;
-};
-
-#define GET_OID_QUIETLY           01
-#define GET_OID_COMMIT            02
-#define GET_OID_COMMITTISH        04
-#define GET_OID_TREE             010
-#define GET_OID_TREEISH          020
-#define GET_OID_BLOB             040
-#define GET_OID_FOLLOW_SYMLINKS 0100
-#define GET_OID_RECORD_PATH     0200
-#define GET_OID_ONLY_TO_DIE    04000
-#define GET_OID_REQUIRE_PATH  010000
-
-#define GET_OID_DISAMBIGUATORS \
-       (GET_OID_COMMIT | GET_OID_COMMITTISH | \
-       GET_OID_TREE | GET_OID_TREEISH | \
-       GET_OID_BLOB)
-
-enum get_oid_result {
-       FOUND = 0,
-       MISSING_OBJECT = -1, /* The requested object is missing */
-       SHORT_NAME_AMBIGUOUS = -2,
-       /* The following only apply when symlinks are followed */
-       DANGLING_SYMLINK = -4, /*
-                               * The initial symlink is there, but
-                               * (transitively) points to a missing
-                               * in-tree file
-                               */
-       SYMLINK_LOOP = -5,
-       NOT_DIR = -6, /*
-                      * Somewhere along the symlink chain, a path is
-                      * requested which contains a file as a
-                      * non-final element.
-                      */
-};
-
-int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
-__attribute__((format (printf, 2, 3)))
-int get_oidf(struct object_id *oid, const char *fmt, ...);
-int repo_get_oid_commit(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_committish(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_tree(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_treeish(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_blob(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_mb(struct repository *r, const char *str, struct object_id *oid);
-void maybe_die_on_misspelt_object_name(struct repository *repo,
-                                      const char *name,
-                                      const char *prefix);
-enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
-                                        unsigned flags, struct object_id *oid,
-                                        struct object_context *oc);
-
-#define get_oid(str, oid)              repo_get_oid(the_repository, str, oid)
-#define get_oid_commit(str, oid)       repo_get_oid_commit(the_repository, str, oid)
-#define get_oid_committish(str, oid)   repo_get_oid_committish(the_repository, str, oid)
-#define get_oid_tree(str, oid)         repo_get_oid_tree(the_repository, str, oid)
-#define get_oid_treeish(str, oid)      repo_get_oid_treeish(the_repository, str, oid)
-#define get_oid_blob(str, oid)         repo_get_oid_blob(the_repository, str, oid)
-#define get_oid_mb(str, oid)           repo_get_oid_mb(the_repository, str, oid)
-
-typedef int each_abbrev_fn(const struct object_id *oid, void *);
-int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *);
-#define for_each_abbrev(prefix, fn, data) repo_for_each_abbrev(the_repository, prefix, fn, data)
-
-int set_disambiguate_hint_config(const char *var, const char *value);
-
-/*
- * Try to read a SHA1 in hexadecimal format from the 40 characters
- * starting at hex.  Write the 20-byte result to sha1 in binary form.
- * Return 0 on success.  Reading stops if a NUL is encountered in the
- * input, so it is safe to pass this function an arbitrary
- * null-terminated string.
- */
-int get_sha1_hex(const char *hex, unsigned char *sha1);
-int get_oid_hex(const char *hex, struct object_id *sha1);
-
-/* Like get_oid_hex, but for an arbitrary hash algorithm. */
-int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
-
-/*
- * Read `len` pairs of hexadecimal digits from `hex` and write the
- * values to `binary` as `len` bytes. Return 0 on success, or -1 if
- * the input does not consist of hex digits).
- */
-int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
-
-/*
- * Convert a binary hash in "unsigned char []" or an object name in
- * "struct object_id *" to its hex equivalent. The `_r` variant is reentrant,
- * and writes the NUL-terminated output to the buffer `out`, which must be at
- * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
- * convenience.
- *
- * The non-`_r` variant returns a static buffer, but uses a ring of 4
- * buffers, making it safe to make multiple calls for a single statement, like:
- *
- *   printf("%s -> %s", hash_to_hex(one), hash_to_hex(two));
- *   printf("%s -> %s", oid_to_hex(one), oid_to_hex(two));
- */
-char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
-char *oid_to_hex_r(char *out, const struct object_id *oid);
-char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);      /* static buffer result! */
-char *hash_to_hex(const unsigned char *hash);                                          /* same static buffer */
-char *oid_to_hex(const struct object_id *oid);                                         /* same static buffer */
-
-/*
- * Parse a 40-character hexadecimal object ID starting from hex, updating the
- * pointer specified by end when parsing stops.  The resulting object ID is
- * stored in oid.  Returns 0 on success.  Parsing will stop on the first NUL or
- * other invalid character.  end is only updated on success; otherwise, it is
- * unmodified.
- */
-int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
-
-/* Like parse_oid_hex, but for an arbitrary hash algorithm. */
-int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end,
-                       const struct git_hash_algo *algo);
-
-
-/*
- * These functions work like get_oid_hex and parse_oid_hex, but they will parse
- * a hex value for any algorithm. The algorithm is detected based on the length
- * and the algorithm in use is returned. If this is not a hex object ID in any
- * algorithm, returns GIT_HASH_UNKNOWN.
- */
-int get_oid_hex_any(const char *hex, struct object_id *oid);
-int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
-
-/*
- * This reads short-hand syntax that not only evaluates to a commit
- * object name, but also can act as if the end user spelled the name
- * of the branch from the command line.
- *
- * - "@{-N}" finds the name of the Nth previous branch we were on, and
- *   places the name of the branch in the given buf and returns the
- *   number of characters parsed if successful.
- *
- * - "<branch>@{upstream}" finds the name of the other ref that
- *   <branch> is configured to merge with (missing <branch> defaults
- *   to the current branch), and places the name of the branch in the
- *   given buf and returns the number of characters parsed if
- *   successful.
- *
- * If the input is not of the accepted format, it returns a negative
- * number to signal an error.
- *
- * If the input was ok but there are not N branch switches in the
- * reflog, it returns 0.
- */
-#define INTERPRET_BRANCH_LOCAL (1<<0)
-#define INTERPRET_BRANCH_REMOTE (1<<1)
-#define INTERPRET_BRANCH_HEAD (1<<2)
-struct interpret_branch_name_options {
-       /*
-        * If "allowed" is non-zero, it is a treated as a bitfield of allowable
-        * expansions: local branches ("refs/heads/"), remote branches
-        * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
-        * allowed, even ones to refs outside of those namespaces.
-        */
-       unsigned allowed;
-
-       /*
-        * If ^{upstream} or ^{push} (or equivalent) is requested, and the
-        * branch in question does not have such a reference, return -1 instead
-        * of die()-ing.
-        */
-       unsigned nonfatal_dangling_mark : 1;
-};
-int repo_interpret_branch_name(struct repository *r,
-                              const char *str, int len,
-                              struct strbuf *buf,
-                              const struct interpret_branch_name_options *options);
-#define interpret_branch_name(str, len, buf, options) \
-       repo_interpret_branch_name(the_repository, str, len, buf, options)
-
-int validate_headref(const char *ref);
-
-int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
-int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
-
-void *read_object_with_reference(struct repository *r,
-                                const struct object_id *oid,
-                                enum object_type required_type,
-                                unsigned long *size,
-                                struct object_id *oid_ret);
-
-struct object *repo_peel_to_type(struct repository *r,
-                                const char *name, int namelen,
-                                struct object *o, enum object_type);
-#define peel_to_type(name, namelen, obj, type) \
-       repo_peel_to_type(the_repository, name, namelen, obj, type)
-
-#define IDENT_STRICT          1
-#define IDENT_NO_DATE         2
-#define IDENT_NO_NAME         4
-
-enum want_ident {
-       WANT_BLANK_IDENT,
-       WANT_AUTHOR_IDENT,
-       WANT_COMMITTER_IDENT
-};
-
-const char *git_author_info(int);
-const char *git_committer_info(int);
-const char *fmt_ident(const char *name, const char *email,
-                     enum want_ident whose_ident,
-                     const char *date_str, int);
-const char *fmt_name(enum want_ident);
-const char *ident_default_name(void);
-const char *ident_default_email(void);
-const char *git_editor(void);
-const char *git_sequence_editor(void);
-const char *git_pager(int stdout_is_tty);
-int is_terminal_dumb(void);
-int git_ident_config(const char *, const char *, void *);
-/*
- * Prepare an ident to fall back on if the user didn't configure it.
- */
-void prepare_fallback_ident(const char *name, const char *email);
-void reset_ident_date(void);
-
-struct ident_split {
-       const char *name_begin;
-       const char *name_end;
-       const char *mail_begin;
-       const char *mail_end;
-       const char *date_begin;
-       const char *date_end;
-       const char *tz_begin;
-       const char *tz_end;
-};
-/*
- * Signals an success with 0, but time part of the result may be NULL
- * if the input lacks timestamp and zone
- */
-int split_ident_line(struct ident_split *, const char *, int);
-
-/*
- * Given a commit or tag object buffer and the commit or tag headers, replaces
- * the idents in the headers with their canonical versions using the mailmap mechanism.
- */
-void apply_mailmap_to_header(struct strbuf *, const char **, struct string_list *);
-
-/*
- * Compare split idents for equality or strict ordering. Note that we
- * compare only the ident part of the line, ignoring any timestamp.
- *
- * Because there are two fields, we must choose one as the primary key; we
- * currently arbitrarily pick the email.
- */
-int ident_cmp(const struct ident_split *, const struct ident_split *);
-
-struct cache_def {
-       struct strbuf path;
-       int flags;
-       int track_flags;
-       int prefix_len_stat_func;
-};
-#define CACHE_DEF_INIT { \
-       .path = STRBUF_INIT, \
-}
-static inline void cache_def_clear(struct cache_def *cache)
-{
-       strbuf_release(&cache->path);
-}
-
-int has_symlink_leading_path(const char *name, int len);
-int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
-int check_leading_path(const char *name, int len, int warn_on_lstat_err);
-int has_dirs_only_path(const char *name, int len, int prefix_len);
-void invalidate_lstat_cache(void);
-void schedule_dir_for_removal(const char *name, int len);
-void remove_scheduled_dirs(void);
-
-struct pack_window {
-       struct pack_window *next;
-       unsigned char *base;
-       off_t offset;
-       size_t len;
-       unsigned int last_used;
-       unsigned int inuse_cnt;
-};
-
-struct pack_entry {
-       off_t offset;
-       struct packed_git *p;
-};
-
-/*
- * Create a temporary file rooted in the object database directory, or
- * die on failure. The filename is taken from "pattern", which should have the
- * usual "XXXXXX" trailer, and the resulting filename is written into the
- * "template" buffer. Returns the open descriptor.
- */
-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
-
-/*
- * Create a pack .keep file named "name" (which should generally be the output
- * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
- * error.
- */
-int odb_pack_keep(const char *name);
-
-/*
- * Set this to 0 to prevent oid_object_info_extended() from fetching missing
- * blobs. This has a difference only if extensions.partialClone is set.
- *
- * Its default value is 1.
- */
-extern int fetch_if_missing;
-
-/* Dumb servers support */
-int update_server_info(int);
-
-const char *get_log_output_encoding(void);
-const char *get_commit_output_encoding(void);
-
-int committer_ident_sufficiently_given(void);
-int author_ident_sufficiently_given(void);
-
-extern const char *git_commit_encoding;
-extern const char *git_log_output_encoding;
-extern const char *git_mailmap_file;
-extern const char *git_mailmap_blob;
-
-/* IO helper functions */
-void maybe_flush_or_die(FILE *, const char *);
-__attribute__((format (printf, 2, 3)))
-void fprintf_or_die(FILE *, const char *fmt, ...);
-void fwrite_or_die(FILE *f, const void *buf, size_t count);
-void fflush_or_die(FILE *f);
-
-#define COPY_READ_ERROR (-2)
-#define COPY_WRITE_ERROR (-3)
-int copy_fd(int ifd, int ofd);
-int copy_file(const char *dst, const char *src, int mode);
-int copy_file_with_time(const char *dst, const char *src, int mode);
-
-void write_or_die(int fd, const void *buf, size_t count);
-void fsync_or_die(int fd, const char *);
-int fsync_component(enum fsync_component component, int fd);
-void fsync_component_or_die(enum fsync_component component, int fd, const char *msg);
-
-static inline int batch_fsync_enabled(enum fsync_component component)
-{
-       return (fsync_components & component) && (fsync_method == FSYNC_METHOD_BATCH);
-}
-
-ssize_t read_in_full(int fd, void *buf, size_t count);
-ssize_t write_in_full(int fd, const void *buf, size_t count);
-ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
-
-static inline ssize_t write_str_in_full(int fd, const char *str)
-{
-       return write_in_full(fd, str, strlen(str));
-}
-
-/**
- * Open (and truncate) the file at path, write the contents of buf to it,
- * and close it. Dies if any errors are encountered.
- */
-void write_file_buf(const char *path, const char *buf, size_t len);
-
-/**
- * Like write_file_buf(), but format the contents into a buffer first.
- * Additionally, write_file() will append a newline if one is not already
- * present, making it convenient to write text files:
- *
- *   write_file(path, "counter: %d", ctr);
- */
-__attribute__((format (printf, 2, 3)))
-void write_file(const char *path, const char *fmt, ...);
-
-/* pager.c */
-void setup_pager(void);
-int pager_in_use(void);
-extern int pager_use_color;
-int term_columns(void);
-void term_clear_line(void);
-int decimal_width(uintmax_t);
-int check_pager_config(const char *cmd);
-void prepare_pager_args(struct child_process *, const char *pager);
-
-extern const char *editor_program;
-extern const char *askpass_program;
-extern const char *excludes_file;
-
-/* base85 */
-int decode_85(char *dst, const char *line, int linelen);
-void encode_85(char *buf, const unsigned char *data, int bytes);
-
-/* pkt-line.c */
-void packet_trace_identity(const char *prog);
-
-/* add */
-/*
- * return 0 if success, 1 - if addition of a file failed and
- * ADD_FILES_IGNORE_ERRORS was specified in flags
- */
-int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
-
-/* diff.c */
-extern int diff_auto_refresh_index;
-
-/* match-trees.c */
-void shift_tree(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, int);
-void shift_tree_by(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, const char *);
-
-/*
- * whitespace rules.
- * used by both diff and apply
- * last two digits are tab width
- */
-#define WS_BLANK_AT_EOL         0100
-#define WS_SPACE_BEFORE_TAB     0200
-#define WS_INDENT_WITH_NON_TAB  0400
-#define WS_CR_AT_EOL           01000
-#define WS_BLANK_AT_EOF        02000
-#define WS_TAB_IN_INDENT       04000
-#define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
-#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
-#define WS_TAB_WIDTH_MASK        077
-/* All WS_* -- when extended, adapt diff.c emit_symbol */
-#define WS_RULE_MASK           07777
-extern unsigned whitespace_rule_cfg;
-unsigned whitespace_rule(struct index_state *, const char *);
-unsigned parse_whitespace_rule(const char *);
-unsigned ws_check(const char *line, int len, unsigned ws_rule);
-void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
-char *whitespace_error_string(unsigned ws);
-void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
-int ws_blank_line(const char *line, int len, unsigned ws_rule);
-#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
-
-/* ls-files */
-void overlay_tree_on_index(struct index_state *istate,
-                          const char *tree_name, const char *prefix);
-
-/* setup.c */
-struct startup_info {
-       int have_repository;
-       const char *prefix;
-       const char *original_cwd;
-};
-extern struct startup_info *startup_info;
-extern const char *tmp_original_cwd;
-
-/* merge.c */
-struct commit_list;
-int try_merge_command(struct repository *r,
-               const char *strategy, size_t xopts_nr,
-               const char **xopts, struct commit_list *common,
-               const char *head_arg, struct commit_list *remotes);
-int checkout_fast_forward(struct repository *r,
-                         const struct object_id *from,
-                         const struct object_id *to,
-                         int overwrite_ignore);
-
-
-int sane_execvp(const char *file, char *const argv[]);
-
-/*
- * A struct to encapsulate the concept of whether a file has changed
- * since we last checked it. This uses criteria similar to those used
- * for the index.
- */
-struct stat_validity {
-       struct stat_data *sd;
-};
-
-void stat_validity_clear(struct stat_validity *sv);
-
-/*
- * Returns 1 if the path is a regular file (or a symlink to a regular
- * file) and matches the saved stat_validity, 0 otherwise.  A missing
- * or inaccessible file is considered a match if the struct was just
- * initialized, or if the previous update found an inaccessible file.
- */
-int stat_validity_check(struct stat_validity *sv, const char *path);
-
-/*
- * Update the stat_validity from a file opened at descriptor fd. If
- * the file is missing, inaccessible, or not a regular file, then
- * future calls to stat_validity_check will match iff one of those
- * conditions continues to be true.
- */
-void stat_validity_update(struct stat_validity *sv, int fd);
-
-int versioncmp(const char *s1, const char *s2);
-
-/*
- * Create a directory and (if share is nonzero) adjust its permissions
- * according to the shared_repository setting. Only use this for
- * directories under $GIT_DIR.  Don't use it for working tree
- * directories.
- */
-void safe_create_dir(const char *dir, int share);
-
-/*
- * Should we print an ellipsis after an abbreviated SHA-1 value
- * when doing diff-raw output or indicating a detached HEAD?
- */
-int print_sha1_ellipsis(void);
-
-/* Return 1 if the file is empty or does not exists, 0 otherwise. */
-int is_empty_or_missing_file(const char *filename);
-
-#endif /* CACHE_H */
index 336e46dbba5a06e7d9b5fdb5dd6f3c7bdd971186..c1cc30a5dc7edaadfa69877b4a8f7a6e3a92ffdb 100644 (file)
--- a/cbtree.c
+++ b/cbtree.c
@@ -4,6 +4,7 @@
  * Based on Adam Langley's adaptation of Dan Bernstein's public domain code
  * git clone https://github.com/agl/critbit.git
  */
+#include "git-compat-util.h"
 #include "cbtree.h"
 
 static struct cb_node *cb_node_of(const void *p)
index 0be14fb7ee4276dff338eb27b35c68973dd2c4b2..43193abdda23cef279f9e9cee99370509611f7c5 100644 (file)
--- a/cbtree.h
+++ b/cbtree.h
@@ -14,8 +14,6 @@
 #ifndef CBTREE_H
 #define CBTREE_H
 
-#include "git-compat-util.h"
-
 struct cb_node;
 struct cb_node {
        struct cb_node *child[2];
index 5f7f2c2ac236f7aab50f0bbdc46324fd56213be9..0d7bc0460747b2973f913b746c4b9ef0ba6b9028 100644 (file)
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "chdir-notify.h"
 #include "list.h"
+#include "path.h"
 #include "strbuf.h"
+#include "trace.h"
 
 struct chdir_notify_entry {
        const char *name;
index 2e39dae684f8f0a048d172f36b21740f92e7de57..4256e71a7c41cd4fdfbcd832727c21548b52b740 100644 (file)
@@ -1,8 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "object-name.h"
 #include "remote.h"
 #include "refspec.h"
+#include "repository.h"
 #include "checkout.h"
 #include "config.h"
+#include "strbuf.h"
 
 struct tracking_name_data {
        /* const */ char *src_ref;
@@ -23,7 +26,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
        memset(&query, 0, sizeof(struct refspec_item));
        query.src = cb->src_ref;
        if (remote_find_tracking(remote, &query) ||
-           get_oid(query.dst, cb->dst_oid)) {
+           repo_get_oid(the_repository, query.dst, cb->dst_oid)) {
                free(query.dst);
                return 0;
        }
index 1152133bd77a773f40afd6c3477224e051318e0c..3c514a5ab4f923db5e412fe5552960d8382ea9f5 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef CHECKOUT_H
 #define CHECKOUT_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 /*
  * Check if the branch name uniquely matches a branch name on a remote
index 0275b74a895a17bb62b7fe12299e6f12aa503d92..cdc7f39b7039b94a14a42ec74b0e27917c43fe4f 100644 (file)
@@ -1,6 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "chunk-format.h"
 #include "csum-file.h"
+#include "gettext.h"
+#include "hash.h"
+#include "trace2.h"
 
 /*
  * When writing a chunk-based file format, collect the chunks in
@@ -99,7 +102,8 @@ int read_table_of_contents(struct chunkfile *cf,
                           const unsigned char *mfile,
                           size_t mfile_size,
                           uint64_t toc_offset,
-                          int toc_length)
+                          int toc_length,
+                          unsigned expected_alignment)
 {
        int i;
        uint32_t chunk_id;
@@ -117,6 +121,11 @@ int read_table_of_contents(struct chunkfile *cf,
                        error(_("terminating chunk id appears earlier than expected"));
                        return 1;
                }
+               if (chunk_offset % expected_alignment != 0) {
+                       error(_("chunk id %"PRIx32" not %d-byte aligned"),
+                             chunk_id, expected_alignment);
+                       return 1;
+               }
 
                table_of_contents += CHUNK_TOC_ENTRY_SIZE;
                next_chunk_offset = get_be64(table_of_contents + 4);
@@ -151,20 +160,28 @@ int read_table_of_contents(struct chunkfile *cf,
        return 0;
 }
 
+struct pair_chunk_data {
+       const unsigned char **p;
+       size_t *size;
+};
+
 static int pair_chunk_fn(const unsigned char *chunk_start,
                         size_t chunk_size,
                         void *data)
 {
-       const unsigned char **p = data;
-       *p = chunk_start;
+       struct pair_chunk_data *pcd = data;
+       *pcd->p = chunk_start;
+       *pcd->size = chunk_size;
        return 0;
 }
 
 int pair_chunk(struct chunkfile *cf,
               uint32_t chunk_id,
-              const unsigned char **p)
+              const unsigned char **p,
+              size_t *size)
 {
-       return read_chunk(cf, chunk_id, pair_chunk_fn, p);
+       struct pair_chunk_data pcd = { .p = p, .size = size };
+       return read_chunk(cf, chunk_id, pair_chunk_fn, &pcd);
 }
 
 int read_chunk(struct chunkfile *cf,
index 7885aa084878dde2f2786545b7edc05e7d3fee24..14b76180ef768f6b5c38aa1cda128ab87d2327ae 100644 (file)
@@ -1,8 +1,7 @@
 #ifndef CHUNK_FORMAT_H
 #define CHUNK_FORMAT_H
 
-#include "git-compat-util.h"
-#include "hash.h"
+#include "hash-ll.h"
 
 struct hashfile;
 struct chunkfile;
@@ -37,20 +36,23 @@ int read_table_of_contents(struct chunkfile *cf,
                           const unsigned char *mfile,
                           size_t mfile_size,
                           uint64_t toc_offset,
-                          int toc_length);
+                          int toc_length,
+                          unsigned expected_alignment);
 
 #define CHUNK_NOT_FOUND (-2)
 
 /*
  * Find 'chunk_id' in the given chunkfile and assign the
  * given pointer to the position in the mmap'd file where
- * that chunk begins.
+ * that chunk begins. Likewise the "size" parameter is filled
+ * with the size of the chunk.
  *
  * Returns CHUNK_NOT_FOUND if the chunk does not exist.
  */
 int pair_chunk(struct chunkfile *cf,
               uint32_t chunk_id,
-              const unsigned char **p);
+              const unsigned char **p,
+              size_t *size);
 
 typedef int (*chunk_read_fn)(const unsigned char *chunk_start,
                             size_t chunk_size, void *data);
diff --git a/ci/config/README b/ci/config/README
new file mode 100644 (file)
index 0000000..8de3a04
--- /dev/null
@@ -0,0 +1,14 @@
+You can configure some aspects of the GitHub Actions-based CI on a
+per-repository basis by setting "variables" and "secrets" from with the
+GitHub web interface. These can be found at:
+
+  https://github.com/<user>/git/settings/secrets/actions
+
+The following variables can be used:
+
+ - CI_BRANCHES
+
+   By default, CI is run when any branch is pushed. If this variable is
+   non-empty, then only the branches it lists will run CI. Branch names
+   should be separated by spaces, and should use their shortened form
+   (e.g., "main", not "refs/heads/main").
diff --git a/ci/config/allow-ref.sample b/ci/config/allow-ref.sample
deleted file mode 100755 (executable)
index af0e076..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-#
-# Sample script for enabling/disabling GitHub Actions CI runs on
-# particular refs. By default, CI is run for all branches pushed to
-# GitHub. You can override this by dropping the ".sample" from the script,
-# editing it, committing, and pushing the result to the "ci-config" branch of
-# your repository:
-#
-#   git checkout -b ci-config
-#   cp allow-ref.sample allow-ref
-#   $EDITOR allow-ref
-#   git add allow-ref
-#   git commit -am "implement my ci preferences"
-#   git push
-#
-# This script will then be run when any refs are pushed to that repository. It
-# gets the fully qualified refname as the first argument, and should exit with
-# success only for refs for which you want to run CI.
-
-case "$1" in
-# allow one-off tests by pushing to "for-ci" or "for-ci/mybranch"
-refs/heads/for-ci*) true ;;
-# always build your integration branch
-refs/heads/my-integration-branch) true ;;
-# don't build any other branches or tags
-*) false ;;
-esac
index 78b7e326da6d8b6b3d1923007d3f22dea9814b8d..eb2c9e1eca73d3beb19ad0d20087f50af13fc62a 100755 (executable)
@@ -3,6 +3,10 @@
 # Install dependencies required to build and test Git inside container
 #
 
+. ${0%/*}/lib.sh
+
+begin_group "Install dependencies"
+
 case "$jobname" in
 linux32)
        linux32 --32bit i386 sh -c '
@@ -12,11 +16,31 @@ linux32)
        '
        ;;
 linux-musl)
-       apk add --update build-base curl-dev openssl-dev expat-dev gettext \
-               pcre2-dev python3 musl-libintl perl-utils ncurses >/dev/null
+       apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
+               pcre2-dev python3 musl-libintl perl-utils ncurses \
+               apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
+               bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
+       ;;
+linux-*|StaticAnalysis)
+       # Required so that apt doesn't wait for user input on certain packages.
+       export DEBIAN_FRONTEND=noninteractive
+
+       apt update -q &&
+       apt install -q -y sudo git make language-pack-is libsvn-perl apache2 libssl-dev \
+               libcurl4-openssl-dev libexpat-dev tcl tk gettext zlib1g-dev \
+               perl-modules liberror-perl libauthen-sasl-perl libemail-valid-perl \
+               libdbd-sqlite3-perl libio-socket-ssl-perl libnet-smtp-ssl-perl ${CC_PACKAGE:-${CC:-gcc}} \
+               apache2 cvs cvsps gnupg libcgi-pm-perl subversion
+
+       if test "$jobname" = StaticAnalysis
+       then
+               apt install -q -y coccinelle
+       fi
        ;;
 pedantic)
        dnf -yq update >/dev/null &&
        dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
        ;;
 esac
+
+end_group "Install dependencies"
index 6561216a65daec09f6fb4527450261ec47520d74..c749b21366b95092b144611dd9182b30bf3cb4a8 100755 (executable)
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -1,16 +1,7 @@
 # Library of functions shared by all CI scripts
 
-if test true != "$GITHUB_ACTIONS"
+if test true = "$GITHUB_ACTIONS"
 then
-       begin_group () { :; }
-       end_group () { :; }
-
-       group () {
-               shift
-               "$@"
-       }
-       set -x
-else
        begin_group () {
                need_to_end_group=t
                echo "::group::$1" >&2
@@ -23,27 +14,50 @@ else
                need_to_end_group=
                echo '::endgroup::' >&2
        }
-       trap end_group EXIT
+elif test true = "$GITLAB_CI"
+then
+       begin_group () {
+               need_to_end_group=t
+               printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K$1\n"
+               trap "end_group '$1'" EXIT
+               set -x
+       }
 
-       group () {
+       end_group () {
+               test -n "$need_to_end_group" || return 0
                set +x
-               begin_group "$1"
-               shift
-               # work around `dash` not supporting `set -o pipefail`
-               (
-                       "$@" 2>&1
-                       echo $? >exit.status
-               ) |
-               sed 's/^\(\([^ ]*\):\([0-9]*\):\([0-9]*:\) \)\(error\|warning\): /::\5 file=\2,line=\3::\1/'
-               res=$(cat exit.status)
-               rm exit.status
-               end_group
-               return $res
+               need_to_end_group=
+               printf "\e[0Ksection_end:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K\n"
+               trap - EXIT
        }
+else
+       begin_group () { :; }
+       end_group () { :; }
 
-       begin_group "CI setup"
+       set -x
 fi
 
+group () {
+       group="$1"
+       shift
+       begin_group "$group"
+
+       # work around `dash` not supporting `set -o pipefail`
+       (
+               "$@" 2>&1
+               echo $? >exit.status
+       ) |
+       sed 's/^\(\([^ ]*\):\([0-9]*\):\([0-9]*:\) \)\(error\|warning\): /::\5 file=\2,line=\3::\1/'
+       res=$(cat exit.status)
+       rm exit.status
+
+       end_group "$group"
+       return $res
+}
+
+begin_group "CI setup"
+trap "end_group 'CI setup'" EXIT
+
 # Set 'exit on error' for all CI scripts to let the caller know that
 # something went wrong.
 #
@@ -71,10 +85,32 @@ skip_branch_tip_with_tag () {
        fi
 }
 
+# Check whether we can use the path passed via the first argument as Git
+# repository.
+is_usable_git_repository () {
+       # We require Git in our PATH, otherwise we cannot access repositories
+       # at all.
+       if ! command -v git >/dev/null
+       then
+               return 1
+       fi
+
+       # And the target directory needs to be a proper Git repository.
+       if ! git -C "$1" rev-parse 2>/dev/null
+       then
+               return 1
+       fi
+}
+
 # Save some info about the current commit's tree, so we can skip the build
 # job if we encounter the same tree again and can provide a useful info
 # message.
 save_good_tree () {
+       if ! is_usable_git_repository .
+       then
+               return
+       fi
+
        echo "$(git rev-parse $CI_COMMIT^{tree}) $CI_COMMIT $CI_JOB_NUMBER $CI_JOB_ID" >>"$good_trees_file"
        # limit the file size
        tail -1000 "$good_trees_file" >"$good_trees_file".tmp
@@ -90,6 +126,11 @@ skip_good_tree () {
                return
        fi
 
+       if ! is_usable_git_repository .
+       then
+               return
+       fi
+
        if ! good_tree_info="$(grep "^$(git rev-parse $CI_COMMIT^{tree}) " "$good_trees_file")"
        then
                # Haven't seen this tree yet, or no cached good trees file yet.
@@ -121,6 +162,11 @@ skip_good_tree () {
 }
 
 check_unignored_build_artifacts () {
+       if ! is_usable_git_repository .
+       then
+               return
+       fi
+
        ! git ls-files --other --exclude-standard --error-unmatch \
                -- ':/*' 2>/dev/null ||
        {
@@ -133,6 +179,26 @@ handle_failed_tests () {
        return 1
 }
 
+create_failed_test_artifacts () {
+       mkdir -p t/failed-test-artifacts
+
+       for test_exit in t/test-results/*.exit
+       do
+               test 0 != "$(cat "$test_exit")" || continue
+
+               test_name="${test_exit%.exit}"
+               test_name="${test_name##*/}"
+               printf "\\e[33m\\e[1m=== Failed test: ${test_name} ===\\e[m\\n"
+               echo "The full logs are in the 'print test failures' step below."
+               echo "See also the 'failed-tests-*' artifacts attached to this run."
+               cat "t/test-results/$test_name.markup"
+
+               trash_dir="t/trash directory.$test_name"
+               cp "t/test-results/$test_name.out" t/failed-test-artifacts/
+               tar czf t/failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
+       done
+}
+
 # GitHub Action doesn't set TERM, which is required by tput
 export TERM=${TERM:-dumb}
 
@@ -156,11 +222,8 @@ then
        # among *all* phases)
        cache_dir="$HOME/test-cache/$SYSTEM_PHASENAME"
 
-       export GIT_PROVE_OPTS="--timer --jobs 10 --state=failed,slow,save"
-       export GIT_TEST_OPTS="--verbose-log -x --write-junit-xml"
-       MAKEFLAGS="$MAKEFLAGS --jobs=10"
-       test windows_nt != "$CI_OS_NAME" ||
-       GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
+       GIT_TEST_OPTS="--write-junit-xml"
+       JOBS=10
 elif test true = "$GITHUB_ACTIONS"
 then
        CI_TYPE=github-actions
@@ -173,40 +236,63 @@ then
        CC="${CC_PACKAGE:-${CC:-gcc}}"
        DONT_SKIP_TAGS=t
        handle_failed_tests () {
-               mkdir -p t/failed-test-artifacts
                echo "FAILED_TEST_ARTIFACTS=t/failed-test-artifacts" >>$GITHUB_ENV
+               create_failed_test_artifacts
+               return 1
+       }
+
+       cache_dir="$HOME/none"
 
-               for test_exit in t/test-results/*.exit
-               do
-                       test 0 != "$(cat "$test_exit")" || continue
-
-                       test_name="${test_exit%.exit}"
-                       test_name="${test_name##*/}"
-                       printf "\\e[33m\\e[1m=== Failed test: ${test_name} ===\\e[m\\n"
-                       echo "The full logs are in the 'print test failures' step below."
-                       echo "See also the 'failed-tests-*' artifacts attached to this run."
-                       cat "t/test-results/$test_name.markup"
-
-                       trash_dir="t/trash directory.$test_name"
-                       cp "t/test-results/$test_name.out" t/failed-test-artifacts/
-                       tar czf t/failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
-               done
+       GIT_TEST_OPTS="--github-workflow-markup"
+       JOBS=10
+elif test true = "$GITLAB_CI"
+then
+       CI_TYPE=gitlab-ci
+       CI_BRANCH="$CI_COMMIT_REF_NAME"
+       CI_COMMIT="$CI_COMMIT_SHA"
+       case "$CI_JOB_IMAGE" in
+       macos-*)
+               CI_OS_NAME=osx;;
+       alpine:*|fedora:*|ubuntu:*)
+               CI_OS_NAME=linux;;
+       *)
+               echo "Could not identify OS image" >&2
+               env >&2
+               exit 1
+               ;;
+       esac
+       CI_REPO_SLUG="$CI_PROJECT_PATH"
+       CI_JOB_ID="$CI_JOB_ID"
+       CC="${CC_PACKAGE:-${CC:-gcc}}"
+       DONT_SKIP_TAGS=t
+       handle_failed_tests () {
+               create_failed_test_artifacts
                return 1
        }
 
        cache_dir="$HOME/none"
 
-       export GIT_PROVE_OPTS="--timer --jobs 10"
-       export GIT_TEST_OPTS="--verbose-log -x --github-workflow-markup"
-       MAKEFLAGS="$MAKEFLAGS --jobs=10"
-       test windows != "$CI_OS_NAME" ||
-       GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
+       runs_on_pool=$(echo "$CI_JOB_IMAGE" | tr : -)
+       JOBS=$(nproc)
 else
        echo "Could not identify CI type" >&2
        env >&2
        exit 1
 fi
 
+MAKEFLAGS="$MAKEFLAGS --jobs=$JOBS"
+GIT_PROVE_OPTS="--timer --jobs $JOBS"
+
+GIT_TEST_OPTS="$GIT_TEST_OPTS --verbose-log -x"
+case "$CI_OS_NAME" in
+windows|windows_nt)
+       GIT_TEST_OPTS="$GIT_TEST_OPTS --no-chain-lint --no-bin-wrappers"
+       ;;
+esac
+
+export GIT_TEST_OPTS
+export GIT_PROVE_OPTS
+
 good_trees_file="$cache_dir/good-trees"
 
 mkdir -p "$cache_dir"
@@ -253,11 +339,10 @@ ubuntu-*)
        export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
        ;;
 macos-*)
-       if [ "$jobname" = osx-gcc ]
+       MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
+       if [ "$jobname" != osx-gcc ]
        then
-               MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
-       else
-               MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
+               MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
        fi
        ;;
 esac
@@ -277,9 +362,14 @@ linux-leaks)
        export GIT_TEST_PASSING_SANITIZE_LEAK=true
        export GIT_TEST_SANITIZE_LEAK_LOG=true
        ;;
+linux-asan-ubsan)
+       export SANITIZE=address,undefined
+       export NO_SVN_TESTS=LetsSaveSomeTime
+       MAKEFLAGS="$MAKEFLAGS NO_PYTHON=YepBecauseP4FlakesTooOften"
+       ;;
 esac
 
 MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}"
 
-end_group
+end_group "CI setup"
 set -x
index 57277eefcd0c8b6117fd71e9adb3a526baa05ef1..c33ad4e3a2220a97f6e0708afe915010f4f44561 100755 (executable)
@@ -51,6 +51,12 @@ do
                        tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
                        continue
                        ;;
+               gitlab-ci)
+                       mkdir -p failed-test-artifacts
+                       cp "${TEST_EXIT%.exit}.out" failed-test-artifacts/
+                       tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
+                       continue
+                       ;;
                *)
                        echo "Unhandled CI type: $CI_TYPE" >&2
                        exit 1
index 8ebff4259676e303b953385ba14cf17b78c1a4de..7a1466b8687f6b24ec370f6577c930cf86621cf8 100755 (executable)
@@ -26,10 +26,10 @@ linux-TEST-vars)
        export GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1
        export GIT_TEST_MULTI_PACK_INDEX=1
        export GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=1
-       export GIT_TEST_ADD_I_USE_BUILTIN=0
        export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-       export GIT_TEST_WRITE_REV_INDEX=1
+       export GIT_TEST_NO_WRITE_REV_INDEX=1
        export GIT_TEST_CHECKOUT_WORKERS=2
+       export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
        ;;
 linux-clang)
        export GIT_TEST_DEFAULT_HASH=sha1
@@ -50,6 +50,8 @@ if test -n "$run_tests"
 then
        group "Run tests" make test ||
        handle_failed_tests
+       group "Run unit tests" \
+               make DEFAULT_UNIT_TEST_TARGET=unit-tests-prove unit-tests
 fi
 check_unignored_build_artifacts
 
index a3c67956a8df8f803bc81eee1b86a8f5559db57b..ae8094382fe418dbd807dbc2156e16b814aa2adc 100755 (executable)
@@ -15,4 +15,9 @@ group "Run tests" make --quiet -C t T="$(cd t &&
        tr '\n' ' ')" ||
 handle_failed_tests
 
+# We only have one unit test at the moment, so run it in the first slice
+if [ "$1" == "0" ] ; then
+       group "Run unit tests" make --quiet -C t unit-tests-prove
+fi
+
 check_unignored_build_artifacts
diff --git a/color.c b/color.c
index f05d8a81d72115edbf47d34950a31615a15ea98e..f663c06ac4eddc7b8085739318ddfc99a3ee7583 100644 (file)
--- a/color.c
+++ b/color.c
@@ -1,6 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "color.h"
+#include "editor.h"
+#include "gettext.h"
+#include "hex-ll.h"
+#include "pager.h"
+#include "strbuf.h"
 
 static int git_use_color_default = GIT_COLOR_AUTO;
 int color_stdout_is_tty = -1;
@@ -425,14 +430,6 @@ int git_color_config(const char *var, const char *value, void *cb UNUSED)
        return 0;
 }
 
-int git_color_default_config(const char *var, const char *value, void *cb)
-{
-       if (git_color_config(var, value, cb) < 0)
-               return -1;
-
-       return git_default_config(var, value, cb);
-}
-
 void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
 {
        if (*color)
diff --git a/color.h b/color.h
index cfc8f841b237c72a0a2ea8b7e42d58dc1cab5f9f..bb28343be210643b57201c6a68ac4f718fa8ebc8 100644 (file)
--- a/color.h
+++ b/color.h
@@ -88,12 +88,8 @@ extern const int column_colors_ansi_max;
  */
 extern int color_stdout_is_tty;
 
-/*
- * Use the first one if you need only color config; the second is a convenience
- * if you are just going to change to git_default_config, too.
- */
+/* Parse color config. */
 int git_color_config(const char *var, const char *value, void *cb);
-int git_color_default_config(const char *var, const char *value, void *cb);
 
 /*
  * Parse a config option, which can be a boolean or one of
index fbf88639aaed931602b203f012468cdeefda284f..ff2f0abf39930c85a515283625a98152d85bc492 100644 (file)
--- a/column.c
+++ b/column.c
@@ -1,7 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "column.h"
 #include "string-list.h"
+#include "pager.h"
 #include "parse-options.h"
 #include "run-command.h"
 #include "utf8.h"
index b0ece9548082c17510b6abd17427434bbf66cae9..db94581f72443c28fd47535db78e39b5ad021a86 100644 (file)
@@ -1,14 +1,18 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "object-store-ll.h"
 #include "commit.h"
-#include "blob.h"
+#include "convert.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "environment.h"
+#include "hex.h"
+#include "object-name.h"
 #include "quote.h"
 #include "xdiff-interface.h"
 #include "xdiff/xmacros.h"
 #include "log-tree.h"
 #include "refs.h"
+#include "tree.h"
 #include "userdiff.h"
 #include "oid-array.h"
 #include "revision.h"
@@ -332,7 +336,7 @@ static char *grab_blob(struct repository *r,
                *size = fill_textconv(r, textconv, df, &blob);
                free_filespec(df);
        } else {
-               blob = read_object_file(oid, &type, size);
+               blob = repo_read_object_file(r, oid, &type, size);
                if (type != OBJ_BLOB)
                        die("object '%s' is not a blob!", oid_to_hex(oid));
        }
@@ -372,7 +376,7 @@ struct combine_diff_state {
 static void consume_hunk(void *state_,
                         long ob, long on,
                         long nb, long nn,
-                        const char *funcline, long funclen)
+                        const char *func UNUSED, long funclen UNUSED)
 {
        struct combine_diff_state *state = state_;
 
@@ -948,11 +952,11 @@ static void show_combined_header(struct combine_diff_path *elem,
                         "", elem->path, line_prefix, c_meta, c_reset);
        printf("%s%sindex ", line_prefix, c_meta);
        for (i = 0; i < num_parent; i++) {
-               abb = find_unique_abbrev(&elem->parent[i].oid,
-                                        abbrev);
+               abb = repo_find_unique_abbrev(the_repository,
+                                             &elem->parent[i].oid, abbrev);
                printf("%s%s", i ? "," : "", abb);
        }
-       abb = find_unique_abbrev(&elem->oid, abbrev);
+       abb = repo_find_unique_abbrev(the_repository, &elem->oid, abbrev);
        printf("..%s%s\n", abb, c_reset);
 
        if (mode_differs) {
index 54b2a50f5f1dfeb2121f73a43417a7855d6fe1f4..c4cd0f352b8833bc7362a5f685f220ec5dd888b0 100644 (file)
@@ -160,6 +160,7 @@ git-reflog                              ancillarymanipulators           complete
 git-remote                              ancillarymanipulators           complete
 git-repack                              ancillarymanipulators           complete
 git-replace                             ancillarymanipulators           complete
+git-replay                              plumbingmanipulators
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain           history
index a7d8755932884cfcb05fad4c539c3b8be8322897..f86c5e9f942f3bc5d2f0341a8ee8d2f6a6cd0dde 100644 (file)
@@ -1,15 +1,19 @@
 #include "git-compat-util.h"
 #include "config.h"
+#include "csum-file.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
-#include "pack.h"
 #include "packfile.h"
 #include "commit.h"
 #include "object.h"
 #include "refs.h"
-#include "revision.h"
 #include "hash-lookup.h"
 #include "commit-graph.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
+#include "path.h"
 #include "alloc.h"
 #include "hashmap.h"
 #include "replace-object.h"
@@ -19,6 +23,7 @@
 #include "shallow.h"
 #include "json-writer.h"
 #include "trace2.h"
+#include "tree.h"
 #include "chunk-format.h"
 
 void git_test_write_commit_graph_or_die(void)
@@ -116,11 +121,19 @@ timestamp_t commit_graph_generation(const struct commit *c)
        struct commit_graph_data *data =
                commit_graph_data_slab_peek(&commit_graph_data_slab, c);
 
-       if (!data)
-               return GENERATION_NUMBER_INFINITY;
-       else if (data->graph_pos == COMMIT_NOT_FROM_GRAPH)
-               return GENERATION_NUMBER_INFINITY;
+       if (data && data->generation)
+               return data->generation;
 
+       return GENERATION_NUMBER_INFINITY;
+}
+
+static timestamp_t commit_graph_generation_from_graph(const struct commit *c)
+{
+       struct commit_graph_data *data =
+               commit_graph_data_slab_peek(&commit_graph_data_slab, c);
+
+       if (!data || data->graph_pos == COMMIT_NOT_FROM_GRAPH)
+               return GENERATION_NUMBER_INFINITY;
        return data->generation;
 }
 
@@ -200,14 +213,12 @@ static struct commit_graph *alloc_commit_graph(void)
        return g;
 }
 
-extern int read_replace_refs;
-
 static int commit_graph_compatible(struct repository *r)
 {
        if (!r->gitdir)
                return 0;
 
-       if (read_replace_refs) {
+       if (replace_refs_enabled(r)) {
                prepare_replace_object(r);
                if (hashmap_get_size(&r->objects->replace_map->map))
                        return 0;
@@ -263,31 +274,25 @@ struct commit_graph *load_commit_graph_one_fd_st(struct repository *r,
        return ret;
 }
 
-static int verify_commit_graph_lite(struct commit_graph *g)
+static int graph_read_oid_fanout(const unsigned char *chunk_start,
+                                size_t chunk_size, void *data)
 {
-       /*
-        * Basic validation shared between parse_commit_graph()
-        * which'll be called every time the graph is used, and the
-        * much more expensive verify_commit_graph() used by
-        * "commit-graph verify".
-        *
-        * There should only be very basic checks here to ensure that
-        * we don't e.g. segfault in fill_commit_in_graph(), but
-        * because this is a very hot codepath nothing that e.g. loops
-        * over g->num_commits, or runs a checksum on the commit-graph
-        * itself.
-        */
-       if (!g->chunk_oid_fanout) {
-               error("commit-graph is missing the OID Fanout chunk");
-               return 1;
-       }
-       if (!g->chunk_oid_lookup) {
-               error("commit-graph is missing the OID Lookup chunk");
-               return 1;
-       }
-       if (!g->chunk_commit_data) {
-               error("commit-graph is missing the Commit Data chunk");
-               return 1;
+       struct commit_graph *g = data;
+       int i;
+
+       if (chunk_size != 256 * sizeof(uint32_t))
+               return error(_("commit-graph oid fanout chunk is wrong size"));
+       g->chunk_oid_fanout = (const uint32_t *)chunk_start;
+       g->num_commits = ntohl(g->chunk_oid_fanout[255]);
+
+       for (i = 0; i < 255; i++) {
+               uint32_t oid_fanout1 = ntohl(g->chunk_oid_fanout[i]);
+               uint32_t oid_fanout2 = ntohl(g->chunk_oid_fanout[i + 1]);
+
+               if (oid_fanout1 > oid_fanout2) {
+                       error(_("commit-graph fanout values out of order"));
+                       return 1;
+               }
        }
 
        return 0;
@@ -298,7 +303,40 @@ static int graph_read_oid_lookup(const unsigned char *chunk_start,
 {
        struct commit_graph *g = data;
        g->chunk_oid_lookup = chunk_start;
-       g->num_commits = chunk_size / g->hash_len;
+       if (chunk_size / g->hash_len != g->num_commits)
+               return error(_("commit-graph OID lookup chunk is the wrong size"));
+       return 0;
+}
+
+static int graph_read_commit_data(const unsigned char *chunk_start,
+                                 size_t chunk_size, void *data)
+{
+       struct commit_graph *g = data;
+       if (chunk_size / GRAPH_DATA_WIDTH != g->num_commits)
+               return error(_("commit-graph commit data chunk is wrong size"));
+       g->chunk_commit_data = chunk_start;
+       return 0;
+}
+
+static int graph_read_generation_data(const unsigned char *chunk_start,
+                                     size_t chunk_size, void *data)
+{
+       struct commit_graph *g = data;
+       if (chunk_size / sizeof(uint32_t) != g->num_commits)
+               return error(_("commit-graph generations chunk is wrong size"));
+       g->chunk_generation_data = chunk_start;
+       return 0;
+}
+
+static int graph_read_bloom_index(const unsigned char *chunk_start,
+                                 size_t chunk_size, void *data)
+{
+       struct commit_graph *g = data;
+       if (chunk_size / 4 != g->num_commits) {
+               warning(_("commit-graph changed-path index chunk is too small"));
+               return -1;
+       }
+       g->chunk_bloom_indexes = chunk_start;
        return 0;
 }
 
@@ -307,7 +345,17 @@ static int graph_read_bloom_data(const unsigned char *chunk_start,
 {
        struct commit_graph *g = data;
        uint32_t hash_version;
+
+       if (chunk_size < BLOOMDATA_CHUNK_HEADER_SIZE) {
+               warning(_("ignoring too-small changed-path chunk"
+                       " (%"PRIuMAX" < %"PRIuMAX") in commit-graph file"),
+                       (uintmax_t)chunk_size,
+                       (uintmax_t)BLOOMDATA_CHUNK_HEADER_SIZE);
+               return -1;
+       }
+
        g->chunk_bloom_data = chunk_start;
+       g->chunk_bloom_data_size = chunk_size;
        hash_version = get_be32(chunk_start);
 
        if (hash_version != 1)
@@ -379,29 +427,41 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
        cf = init_chunkfile(NULL);
 
        if (read_table_of_contents(cf, graph->data, graph_size,
-                                  GRAPH_HEADER_SIZE, graph->num_chunks))
+                                  GRAPH_HEADER_SIZE, graph->num_chunks, 1))
                goto free_and_return;
 
-       pair_chunk(cf, GRAPH_CHUNKID_OIDFANOUT,
-                  (const unsigned char **)&graph->chunk_oid_fanout);
-       read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph);
-       pair_chunk(cf, GRAPH_CHUNKID_DATA, &graph->chunk_commit_data);
-       pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges);
-       pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs);
+       if (read_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, graph_read_oid_fanout, graph)) {
+               error(_("commit-graph required OID fanout chunk missing or corrupted"));
+               goto free_and_return;
+       }
+       if (read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph)) {
+               error(_("commit-graph required OID lookup chunk missing or corrupted"));
+               goto free_and_return;
+       }
+       if (read_chunk(cf, GRAPH_CHUNKID_DATA, graph_read_commit_data, graph)) {
+               error(_("commit-graph required commit data chunk missing or corrupted"));
+               goto free_and_return;
+       }
+
+       pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges,
+                  &graph->chunk_extra_edges_size);
+       pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs,
+                  &graph->chunk_base_graphs_size);
 
        if (s->commit_graph_generation_version >= 2) {
-               pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
-                       &graph->chunk_generation_data);
+               read_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
+                          graph_read_generation_data, graph);
                pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
-                       &graph->chunk_generation_data_overflow);
+                          &graph->chunk_generation_data_overflow,
+                          &graph->chunk_generation_data_overflow_size);
 
                if (graph->chunk_generation_data)
                        graph->read_generation_data = 1;
        }
 
        if (s->commit_graph_read_changed_paths) {
-               pair_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
-                          &graph->chunk_bloom_indexes);
+               read_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
+                          graph_read_bloom_index, graph);
                read_chunk(cf, GRAPH_CHUNKID_BLOOMDATA,
                           graph_read_bloom_data, graph);
        }
@@ -417,9 +477,6 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
 
        oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len);
 
-       if (verify_commit_graph_lite(graph))
-               goto free_and_return;
-
        free_chunkfile(cf);
        return graph;
 
@@ -461,6 +518,31 @@ static struct commit_graph *load_commit_graph_v1(struct repository *r,
        return g;
 }
 
+/*
+ * returns 1 if and only if all graphs in the chain have
+ * corrected commit dates stored in the generation_data chunk.
+ */
+static int validate_mixed_generation_chain(struct commit_graph *g)
+{
+       int read_generation_data = 1;
+       struct commit_graph *p = g;
+
+       while (read_generation_data && p) {
+               read_generation_data = p->read_generation_data;
+               p = p->base_graph;
+       }
+
+       if (read_generation_data)
+               return 1;
+
+       while (g) {
+               g->read_generation_data = 0;
+               g = g->base_graph;
+       }
+
+       return 0;
+}
+
 static int add_graph_to_chain(struct commit_graph *g,
                              struct commit_graph *chain,
                              struct object_id *oids,
@@ -473,12 +555,17 @@ static int add_graph_to_chain(struct commit_graph *g,
                return 0;
        }
 
+       if (g->chunk_base_graphs_size / g->hash_len < n) {
+               warning(_("commit-graph base graphs chunk is too small"));
+               return 0;
+       }
+
        while (n) {
                n--;
 
                if (!cur_g ||
                    !oideq(&oids[n], &cur_g->oid) ||
-                   !hasheq(oids[n].hash, g->chunk_base_graphs + g->hash_len * n)) {
+                   !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_len, n))) {
                        warning(_("commit-graph chain does not match"));
                        return 0;
                }
@@ -486,39 +573,56 @@ static int add_graph_to_chain(struct commit_graph *g,
                cur_g = cur_g->base_graph;
        }
 
+       if (chain) {
+               if (unsigned_add_overflows(chain->num_commits,
+                                          chain->num_commits_in_base)) {
+                       warning(_("commit count in base graph too high: %"PRIuMAX),
+                               (uintmax_t)chain->num_commits_in_base);
+                       return 0;
+               }
+               g->num_commits_in_base = chain->num_commits + chain->num_commits_in_base;
+       }
+
        g->base_graph = chain;
 
-       if (chain)
-               g->num_commits_in_base = chain->num_commits + chain->num_commits_in_base;
+       return 1;
+}
 
+int open_commit_graph_chain(const char *chain_file,
+                           int *fd, struct stat *st)
+{
+       *fd = git_open(chain_file);
+       if (*fd < 0)
+               return 0;
+       if (fstat(*fd, st)) {
+               close(*fd);
+               return 0;
+       }
+       if (st->st_size < the_hash_algo->hexsz) {
+               close(*fd);
+               if (!st->st_size) {
+                       /* treat empty files the same as missing */
+                       errno = ENOENT;
+               } else {
+                       warning(_("commit-graph chain file too small"));
+                       errno = EINVAL;
+               }
+               return 0;
+       }
        return 1;
 }
 
-static struct commit_graph *load_commit_graph_chain(struct repository *r,
-                                                   struct object_directory *odb)
+struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
+                                                  int fd, struct stat *st,
+                                                  int *incomplete_chain)
 {
        struct commit_graph *graph_chain = NULL;
        struct strbuf line = STRBUF_INIT;
-       struct stat st;
        struct object_id *oids;
        int i = 0, valid = 1, count;
-       char *chain_name = get_commit_graph_chain_filename(odb);
-       FILE *fp;
-       int stat_res;
+       FILE *fp = xfdopen(fd, "r");
 
-       fp = fopen(chain_name, "r");
-       stat_res = stat(chain_name, &st);
-       free(chain_name);
-
-       if (!fp)
-               return NULL;
-       if (stat_res ||
-           st.st_size <= the_hash_algo->hexsz) {
-               fclose(fp);
-               return NULL;
-       }
-
-       count = st.st_size / (the_hash_algo->hexsz + 1);
+       count = st->st_size / (the_hash_algo->hexsz + 1);
        CALLOC_ARRAY(oids, count);
 
        prepare_alt_odb(r);
@@ -547,6 +651,8 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r,
                                if (add_graph_to_chain(g, graph_chain, oids, i)) {
                                        graph_chain = g;
                                        valid = 1;
+                               } else {
+                                       free_commit_graph(g);
                                }
 
                                break;
@@ -559,36 +665,32 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r,
                }
        }
 
+       validate_mixed_generation_chain(graph_chain);
+
        free(oids);
        fclose(fp);
        strbuf_release(&line);
 
+       *incomplete_chain = !valid;
        return graph_chain;
 }
 
-/*
- * returns 1 if and only if all graphs in the chain have
- * corrected commit dates stored in the generation_data chunk.
- */
-static int validate_mixed_generation_chain(struct commit_graph *g)
+static struct commit_graph *load_commit_graph_chain(struct repository *r,
+                                                   struct object_directory *odb)
 {
-       int read_generation_data = 1;
-       struct commit_graph *p = g;
-
-       while (read_generation_data && p) {
-               read_generation_data = p->read_generation_data;
-               p = p->base_graph;
-       }
-
-       if (read_generation_data)
-               return 1;
+       char *chain_file = get_commit_graph_chain_filename(odb);
+       struct stat st;
+       int fd;
+       struct commit_graph *g = NULL;
 
-       while (g) {
-               g->read_generation_data = 0;
-               g = g->base_graph;
+       if (open_commit_graph_chain(chain_file, &fd, &st)) {
+               int incomplete;
+               /* ownership of fd is taken over by load function */
+               g = load_commit_graph_chain_fd_st(r, fd, &st, &incomplete);
        }
 
-       return 0;
+       free(chain_file);
+       return g;
 }
 
 struct commit_graph *read_commit_graph_one(struct repository *r,
@@ -599,8 +701,6 @@ struct commit_graph *read_commit_graph_one(struct repository *r,
        if (!g)
                g = load_commit_graph_chain(r, odb);
 
-       validate_mixed_generation_chain(g);
-
        return g;
 }
 
@@ -704,19 +804,13 @@ struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
        return NULL;
 }
 
-static void close_commit_graph_one(struct commit_graph *g)
+void close_commit_graph(struct raw_object_store *o)
 {
-       if (!g)
+       if (!o->commit_graph)
                return;
 
        clear_commit_graph_data_slab(&commit_graph_data_slab);
-       close_commit_graph_one(g->base_graph);
-       free_commit_graph(g);
-}
-
-void close_commit_graph(struct raw_object_store *o)
-{
-       close_commit_graph_one(o->commit_graph);
+       free_commit_graph(o->commit_graph);
        o->commit_graph = NULL;
 }
 
@@ -743,7 +837,7 @@ static void load_oid_from_graph(struct commit_graph *g,
 
        lex_index = pos - g->num_commits_in_base;
 
-       oidread(oid, g->chunk_oid_lookup + g->hash_len * lex_index);
+       oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_len, lex_index));
 }
 
 static struct commit_list **insert_parent_or_die(struct repository *r,
@@ -779,7 +873,7 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
                die(_("invalid commit position. commit-graph is likely corrupt"));
 
        lex_index = pos - g->num_commits_in_base;
-       commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index;
+       commit_data = g->chunk_commit_data + st_mult(GRAPH_DATA_WIDTH, lex_index);
 
        graph_data = commit_graph_data_at(item);
        graph_data->graph_pos = pos;
@@ -789,14 +883,17 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
        item->date = (timestamp_t)((date_high << 32) | date_low);
 
        if (g->read_generation_data) {
-               offset = (timestamp_t)get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index);
+               offset = (timestamp_t)get_be32(g->chunk_generation_data + st_mult(sizeof(uint32_t), lex_index));
 
                if (offset & CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW) {
                        if (!g->chunk_generation_data_overflow)
                                die(_("commit-graph requires overflow generation data but has none"));
 
                        offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
-                       graph_data->generation = item->date + get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
+                       if (g->chunk_generation_data_overflow_size / sizeof(uint64_t) <= offset_pos)
+                               die(_("commit-graph overflow generation data is too small"));
+                       graph_data->generation = item->date +
+                               get_be64(g->chunk_generation_data_overflow + sizeof(uint64_t) * offset_pos);
                } else
                        graph_data->generation = item->date + offset;
        } else
@@ -816,7 +913,7 @@ static int fill_commit_in_graph(struct repository *r,
                                struct commit_graph *g, uint32_t pos)
 {
        uint32_t edge_value;
-       uint32_t *parent_data_ptr;
+       uint32_t parent_data_pos;
        struct commit_list **pptr;
        const unsigned char *commit_data;
        uint32_t lex_index;
@@ -827,7 +924,7 @@ static int fill_commit_in_graph(struct repository *r,
        fill_commit_graph_info(item, g, pos);
 
        lex_index = pos - g->num_commits_in_base;
-       commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index;
+       commit_data = g->chunk_commit_data + st_mult(g->hash_len + 16, lex_index);
 
        item->object.parsed = 1;
 
@@ -848,14 +945,21 @@ static int fill_commit_in_graph(struct repository *r,
                return 1;
        }
 
-       parent_data_ptr = (uint32_t*)(g->chunk_extra_edges +
-                         4 * (uint64_t)(edge_value & GRAPH_EDGE_LAST_MASK));
+       parent_data_pos = edge_value & GRAPH_EDGE_LAST_MASK;
        do {
-               edge_value = get_be32(parent_data_ptr);
+               if (g->chunk_extra_edges_size / sizeof(uint32_t) <= parent_data_pos) {
+                       error(_("commit-graph extra-edges pointer out of bounds"));
+                       free_commit_list(item->parents);
+                       item->parents = NULL;
+                       item->object.parsed = 0;
+                       return 0;
+               }
+               edge_value = get_be32(g->chunk_extra_edges +
+                                     sizeof(uint32_t) * parent_data_pos);
                pptr = insert_parent_or_die(r, g,
                                            edge_value & GRAPH_EDGE_LAST_MASK,
                                            pptr);
-               parent_data_ptr++;
+               parent_data_pos++;
        } while (!(edge_value & GRAPH_LAST_EDGE));
 
        return 1;
@@ -898,14 +1002,18 @@ int repo_find_commit_pos_in_graph(struct repository *r, struct commit *c,
 
 struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id)
 {
+       static int commit_graph_paranoia = -1;
        struct commit *commit;
        uint32_t pos;
 
+       if (commit_graph_paranoia == -1)
+               commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 0);
+
        if (!prepare_commit_graph(repo))
                return NULL;
        if (!search_commit_pos_in_graph(id, repo->objects->commit_graph, &pos))
                return NULL;
-       if (!has_object(repo, id, 0))
+       if (commit_graph_paranoia && !has_object(repo, id, 0))
                return NULL;
 
        commit = lookup_commit(repo, id);
@@ -969,7 +1077,7 @@ static struct tree *load_tree_for_commit(struct repository *r,
                g = g->base_graph;
 
        commit_data = g->chunk_commit_data +
-                       GRAPH_DATA_WIDTH * (graph_pos - g->num_commits_in_base);
+                       st_mult(GRAPH_DATA_WIDTH, graph_pos - g->num_commits_in_base);
 
        oidread(&oid, commit_data);
        set_commit_tree(c, lookup_tree(r, &oid));
@@ -1446,24 +1554,52 @@ static void close_reachable(struct write_commit_graph_context *ctx)
        stop_progress(&ctx->progress);
 }
 
-static void compute_topological_levels(struct write_commit_graph_context *ctx)
+struct compute_generation_info {
+       struct repository *r;
+       struct packed_commit_list *commits;
+       struct progress *progress;
+       int progress_cnt;
+
+       timestamp_t (*get_generation)(struct commit *c, void *data);
+       void (*set_generation)(struct commit *c, timestamp_t gen, void *data);
+       void *data;
+};
+
+static timestamp_t compute_generation_from_max(struct commit *c,
+                                              timestamp_t max_gen,
+                                              int generation_version)
+{
+       switch (generation_version) {
+       case 1: /* topological levels */
+               if (max_gen > GENERATION_NUMBER_V1_MAX - 1)
+                       max_gen = GENERATION_NUMBER_V1_MAX - 1;
+               return max_gen + 1;
+
+       case 2: /* corrected commit date */
+               if (c->date && c->date > max_gen)
+                       max_gen = c->date - 1;
+               return max_gen + 1;
+
+       default:
+               BUG("attempting unimplemented version");
+       }
+}
+
+static void compute_reachable_generation_numbers(
+                       struct compute_generation_info *info,
+                       int generation_version)
 {
        int i;
        struct commit_list *list = NULL;
 
-       if (ctx->report_progress)
-               ctx->progress = start_delayed_progress(
-                                       _("Computing commit graph topological levels"),
-                                       ctx->commits.nr);
-       for (i = 0; i < ctx->commits.nr; i++) {
-               struct commit *c = ctx->commits.list[i];
-               uint32_t level;
+       for (i = 0; i < info->commits->nr; i++) {
+               struct commit *c = info->commits->list[i];
+               timestamp_t gen;
+               repo_parse_commit(info->r, c);
+               gen = info->get_generation(c, info->data);
+               display_progress(info->progress, info->progress_cnt + 1);
 
-               repo_parse_commit(ctx->r, c);
-               level = *topo_level_slab_at(ctx->topo_levels, c);
-
-               display_progress(ctx->progress, i + 1);
-               if (level != GENERATION_NUMBER_ZERO)
+               if (gen != GENERATION_NUMBER_ZERO && gen != GENERATION_NUMBER_INFINITY)
                        continue;
 
                commit_list_insert(c, &list);
@@ -1471,41 +1607,92 @@ static void compute_topological_levels(struct write_commit_graph_context *ctx)
                        struct commit *current = list->item;
                        struct commit_list *parent;
                        int all_parents_computed = 1;
-                       uint32_t max_level = 0;
+                       uint32_t max_gen = 0;
 
                        for (parent = current->parents; parent; parent = parent->next) {
-                               repo_parse_commit(ctx->r, parent->item);
-                               level = *topo_level_slab_at(ctx->topo_levels, parent->item);
+                               repo_parse_commit(info->r, parent->item);
+                               gen = info->get_generation(parent->item, info->data);
 
-                               if (level == GENERATION_NUMBER_ZERO) {
+                               if (gen == GENERATION_NUMBER_ZERO) {
                                        all_parents_computed = 0;
                                        commit_list_insert(parent->item, &list);
                                        break;
                                }
 
-                               if (level > max_level)
-                                       max_level = level;
+                               if (gen > max_gen)
+                                       max_gen = gen;
                        }
 
                        if (all_parents_computed) {
                                pop_commit(&list);
-
-                               if (max_level > GENERATION_NUMBER_V1_MAX - 1)
-                                       max_level = GENERATION_NUMBER_V1_MAX - 1;
-                               *topo_level_slab_at(ctx->topo_levels, current) = max_level + 1;
+                               gen = compute_generation_from_max(
+                                               current, max_gen,
+                                               generation_version);
+                               info->set_generation(current, gen, info->data);
                        }
                }
        }
+}
+
+static timestamp_t get_topo_level(struct commit *c, void *data)
+{
+       struct write_commit_graph_context *ctx = data;
+       return *topo_level_slab_at(ctx->topo_levels, c);
+}
+
+static void set_topo_level(struct commit *c, timestamp_t t, void *data)
+{
+       struct write_commit_graph_context *ctx = data;
+       *topo_level_slab_at(ctx->topo_levels, c) = (uint32_t)t;
+}
+
+static void compute_topological_levels(struct write_commit_graph_context *ctx)
+{
+       struct compute_generation_info info = {
+               .r = ctx->r,
+               .commits = &ctx->commits,
+               .get_generation = get_topo_level,
+               .set_generation = set_topo_level,
+               .data = ctx,
+       };
+
+       if (ctx->report_progress)
+               info.progress = ctx->progress
+                             = start_delayed_progress(
+                                       _("Computing commit graph topological levels"),
+                                       ctx->commits.nr);
+
+       compute_reachable_generation_numbers(&info, 1);
+
        stop_progress(&ctx->progress);
 }
 
+static timestamp_t get_generation_from_graph_data(struct commit *c,
+                                                 void *data UNUSED)
+{
+       return commit_graph_data_at(c)->generation;
+}
+
+static void set_generation_v2(struct commit *c, timestamp_t t,
+                             void *data UNUSED)
+{
+       struct commit_graph_data *g = commit_graph_data_at(c);
+       g->generation = t;
+}
+
 static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 {
        int i;
-       struct commit_list *list = NULL;
+       struct compute_generation_info info = {
+               .r = ctx->r,
+               .commits = &ctx->commits,
+               .get_generation = get_generation_from_graph_data,
+               .set_generation = set_generation_v2,
+       };
 
        if (ctx->report_progress)
-               ctx->progress = start_delayed_progress(
+               info.progress = ctx->progress
+                             = start_delayed_progress(
                                        _("Computing commit graph generation numbers"),
                                        ctx->commits.nr);
 
@@ -1517,47 +1704,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
                }
        }
 
-       for (i = 0; i < ctx->commits.nr; i++) {
-               struct commit *c = ctx->commits.list[i];
-               timestamp_t corrected_commit_date;
-
-               repo_parse_commit(ctx->r, c);
-               corrected_commit_date = commit_graph_data_at(c)->generation;
-
-               display_progress(ctx->progress, i + 1);
-               if (corrected_commit_date != GENERATION_NUMBER_ZERO)
-                       continue;
-
-               commit_list_insert(c, &list);
-               while (list) {
-                       struct commit *current = list->item;
-                       struct commit_list *parent;
-                       int all_parents_computed = 1;
-                       timestamp_t max_corrected_commit_date = 0;
-
-                       for (parent = current->parents; parent; parent = parent->next) {
-                               repo_parse_commit(ctx->r, parent->item);
-                               corrected_commit_date = commit_graph_data_at(parent->item)->generation;
-
-                               if (corrected_commit_date == GENERATION_NUMBER_ZERO) {
-                                       all_parents_computed = 0;
-                                       commit_list_insert(parent->item, &list);
-                                       break;
-                               }
-
-                               if (corrected_commit_date > max_corrected_commit_date)
-                                       max_corrected_commit_date = corrected_commit_date;
-                       }
-
-                       if (all_parents_computed) {
-                               pop_commit(&list);
-
-                               if (current->date && current->date > max_corrected_commit_date)
-                                       max_corrected_commit_date = current->date - 1;
-                               commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
-                       }
-               }
-       }
+       compute_reachable_generation_numbers(&info, 2);
 
        for (i = 0; i < ctx->commits.nr; i++) {
                struct commit *c = ctx->commits.list[i];
@@ -1568,6 +1715,35 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
        stop_progress(&ctx->progress);
 }
 
+static void set_generation_in_graph_data(struct commit *c, timestamp_t t,
+                                        void *data UNUSED)
+{
+       commit_graph_data_at(c)->generation = t;
+}
+
+/*
+ * After this method, all commits reachable from those in the given
+ * list will have non-zero, non-infinite generation numbers.
+ */
+void ensure_generations_valid(struct repository *r,
+                             struct commit **commits, size_t nr)
+{
+       int generation_version = get_configured_generation_version(r);
+       struct packed_commit_list list = {
+               .list = commits,
+               .alloc = nr,
+               .nr = nr,
+       };
+       struct compute_generation_info info = {
+               .r = r,
+               .commits = &list,
+               .get_generation = get_generation_from_graph_data,
+               .set_generation = set_generation_in_graph_data,
+       };
+
+       compute_reachable_generation_numbers(&info, generation_version);
+}
+
 static void trace2_bloom_filter_write_statistics(struct write_commit_graph_context *ctx)
 {
        trace2_data_intmax("commit-graph", ctx->r, "filter-computed",
@@ -1594,8 +1770,7 @@ static void compute_bloom_filters(struct write_commit_graph_context *ctx)
                        _("Computing commit changed paths Bloom filters"),
                        ctx->commits.nr);
 
-       ALLOC_ARRAY(sorted_commits, ctx->commits.nr);
-       COPY_ARRAY(sorted_commits, ctx->commits.list, ctx->commits.nr);
+       DUP_ARRAY(sorted_commits, ctx->commits.list, ctx->commits.nr);
 
        if (ctx->order_by_pack)
                QSORT(sorted_commits, ctx->commits.nr, commit_pos_cmp);
@@ -1883,35 +2058,35 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 
        add_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, GRAPH_FANOUT_SIZE,
                  write_graph_chunk_fanout);
-       add_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, hashsz * ctx->commits.nr,
+       add_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, st_mult(hashsz, ctx->commits.nr),
                  write_graph_chunk_oids);
-       add_chunk(cf, GRAPH_CHUNKID_DATA, (hashsz + 16) * ctx->commits.nr,
+       add_chunk(cf, GRAPH_CHUNKID_DATA, st_mult(hashsz + 16, ctx->commits.nr),
                  write_graph_chunk_data);
 
        if (ctx->write_generation_data)
                add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
-                         sizeof(uint32_t) * ctx->commits.nr,
+                         st_mult(sizeof(uint32_t), ctx->commits.nr),
                          write_graph_chunk_generation_data);
        if (ctx->num_generation_data_overflows)
                add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
-                         sizeof(timestamp_t) * ctx->num_generation_data_overflows,
+                         st_mult(sizeof(timestamp_t), ctx->num_generation_data_overflows),
                          write_graph_chunk_generation_data_overflow);
        if (ctx->num_extra_edges)
                add_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES,
-                         4 * ctx->num_extra_edges,
+                         st_mult(4, ctx->num_extra_edges),
                          write_graph_chunk_extra_edges);
        if (ctx->changed_paths) {
                add_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
-                         sizeof(uint32_t) * ctx->commits.nr,
+                         st_mult(sizeof(uint32_t), ctx->commits.nr),
                          write_graph_chunk_bloom_indexes);
                add_chunk(cf, GRAPH_CHUNKID_BLOOMDATA,
-                         sizeof(uint32_t) * 3
-                               + ctx->total_bloom_filter_data_size,
+                         st_add(sizeof(uint32_t) * 3,
+                                ctx->total_bloom_filter_data_size),
                          write_graph_chunk_bloom_data);
        }
        if (ctx->num_commit_graphs_after > 1)
                add_chunk(cf, GRAPH_CHUNKID_BASE,
-                         hashsz * (ctx->num_commit_graphs_after - 1),
+                         st_mult(hashsz, ctx->num_commit_graphs_after - 1),
                          write_graph_chunk_base);
 
        hashwrite_be32(f, GRAPH_SIGNATURE);
@@ -1929,7 +2104,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
                            get_num_chunks(cf));
                ctx->progress = start_delayed_progress(
                        progress_title.buf,
-                       get_num_chunks(cf) * ctx->commits.nr);
+                       st_mult(get_num_chunks(cf), ctx->commits.nr));
        }
 
        write_chunkfile(cf, ctx);
@@ -1986,9 +2161,11 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
                        free(graph_name);
                }
 
+               free(ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]);
                ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(hash_to_hex(file_hash));
                final_graph_name = get_split_graph_filename(ctx->odb,
                                        ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]);
+               free(ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1]);
                ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1] = final_graph_name;
 
                result = rename(ctx->graph_name, final_graph_name);
@@ -2035,11 +2212,16 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
 
        if (flags != COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED &&
            flags != COMMIT_GRAPH_SPLIT_REPLACE) {
-               while (g && (g->num_commits <= size_mult * num_commits ||
+               while (g && (g->num_commits <= st_mult(size_mult, num_commits) ||
                            (max_commits && num_commits > max_commits))) {
                        if (g->odb != ctx->odb)
                                break;
 
+                       if (unsigned_add_overflows(num_commits, g->num_commits))
+                               die(_("cannot merge graphs with %"PRIuMAX", "
+                                     "%"PRIuMAX" commits"),
+                                   (uintmax_t)num_commits,
+                                   (uintmax_t)g->num_commits);
                        num_commits += g->num_commits;
                        g = g->base_graph;
 
@@ -2097,6 +2279,11 @@ static void merge_commit_graph(struct write_commit_graph_context *ctx,
        uint32_t i;
        uint32_t offset = g->num_commits_in_base;
 
+       if (unsigned_add_overflows(ctx->commits.nr, g->num_commits))
+               die(_("cannot merge graph %s, too many commits: %"PRIuMAX),
+                   oid_to_hex(&g->oid),
+                   (uintmax_t)st_add(ctx->commits.nr, g->num_commits));
+
        ALLOC_GROW(ctx->commits.list, ctx->commits.nr + g->num_commits, ctx->commits.alloc);
 
        for (i = 0; i < g->num_commits; i++) {
@@ -2361,13 +2548,13 @@ int write_commit_graph(struct object_directory *odb,
                        replace = ctx->opts->split_flags & COMMIT_GRAPH_SPLIT_REPLACE;
        }
 
-       ctx->approx_nr_objects = approximate_object_count();
+       ctx->approx_nr_objects = repo_approximate_object_count(the_repository);
 
        if (ctx->append && ctx->r->objects->commit_graph) {
                struct commit_graph *g = ctx->r->objects->commit_graph;
                for (i = 0; i < g->num_commits; i++) {
                        struct object_id oid;
-                       oidread(&oid, g->chunk_oid_lookup + g->hash_len * i);
+                       oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
                        oid_array_append(&ctx->oids, &oid);
                }
        }
@@ -2427,6 +2614,7 @@ int write_commit_graph(struct object_directory *odb,
 
 cleanup:
        free(ctx->graph_name);
+       free(ctx->base_graph_name);
        free(ctx->commits.list);
        oid_array_clear(&ctx->oids);
        clear_topo_level_slab(&topo_levels);
@@ -2465,30 +2653,20 @@ static void graph_report(const char *fmt, ...)
        va_end(ap);
 }
 
-#define GENERATION_ZERO_EXISTS 1
-#define GENERATION_NUMBER_EXISTS 2
-
 static int commit_graph_checksum_valid(struct commit_graph *g)
 {
        return hashfile_checksum_valid(g->data, g->data_len);
 }
 
-int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
+static int verify_one_commit_graph(struct repository *r,
+                                  struct commit_graph *g,
+                                  struct progress *progress,
+                                  uint64_t *seen)
 {
        uint32_t i, cur_fanout_pos = 0;
        struct object_id prev_oid, cur_oid;
-       int generation_zero = 0;
-       struct progress *progress = NULL;
-       int local_error = 0;
-
-       if (!g) {
-               graph_report("no commit-graph file loaded");
-               return 1;
-       }
-
-       verify_commit_graph_error = verify_commit_graph_lite(g);
-       if (verify_commit_graph_error)
-               return verify_commit_graph_error;
+       struct commit *seen_gen_zero = NULL;
+       struct commit *seen_gen_non_zero = NULL;
 
        if (!commit_graph_checksum_valid(g)) {
                graph_report(_("the commit-graph file has incorrect checksum and is likely corrupt"));
@@ -2498,7 +2676,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
        for (i = 0; i < g->num_commits; i++) {
                struct commit *graph_commit;
 
-               oidread(&cur_oid, g->chunk_oid_lookup + g->hash_len * i);
+               oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
 
                if (i && oidcmp(&prev_oid, &cur_oid) >= 0)
                        graph_report(_("commit-graph has incorrect OID order: %s then %s"),
@@ -2535,22 +2713,18 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
        if (verify_commit_graph_error & ~VERIFY_COMMIT_GRAPH_ERROR_HASH)
                return verify_commit_graph_error;
 
-       if (flags & COMMIT_GRAPH_WRITE_PROGRESS)
-               progress = start_progress(_("Verifying commits in commit graph"),
-                                       g->num_commits);
-
        for (i = 0; i < g->num_commits; i++) {
                struct commit *graph_commit, *odb_commit;
                struct commit_list *graph_parents, *odb_parents;
                timestamp_t max_generation = 0;
                timestamp_t generation;
 
-               display_progress(progress, i + 1);
-               oidread(&cur_oid, g->chunk_oid_lookup + g->hash_len * i);
+               display_progress(progress, ++(*seen));
+               oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
 
                graph_commit = lookup_commit(r, &cur_oid);
                odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r));
-               if (parse_commit_internal(odb_commit, 0, 0)) {
+               if (repo_parse_commit_internal(r, odb_commit, 0, 0)) {
                        graph_report(_("failed to parse commit %s from object database for commit-graph"),
                                     oid_to_hex(&cur_oid));
                        continue;
@@ -2582,7 +2756,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
                                             oid_to_hex(&graph_parents->item->object.oid),
                                             oid_to_hex(&odb_parents->item->object.oid));
 
-                       generation = commit_graph_generation(graph_parents->item);
+                       generation = commit_graph_generation_from_graph(graph_parents->item);
                        if (generation > max_generation)
                                max_generation = generation;
 
@@ -2594,16 +2768,12 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
                        graph_report(_("commit-graph parent list for commit %s terminates early"),
                                     oid_to_hex(&cur_oid));
 
-               if (!commit_graph_generation(graph_commit)) {
-                       if (generation_zero == GENERATION_NUMBER_EXISTS)
-                               graph_report(_("commit-graph has generation number zero for commit %s, but non-zero elsewhere"),
-                                            oid_to_hex(&cur_oid));
-                       generation_zero = GENERATION_ZERO_EXISTS;
-               } else if (generation_zero == GENERATION_ZERO_EXISTS)
-                       graph_report(_("commit-graph has non-zero generation number for commit %s, but zero elsewhere"),
-                                    oid_to_hex(&cur_oid));
+               if (commit_graph_generation_from_graph(graph_commit))
+                       seen_gen_non_zero = graph_commit;
+               else
+                       seen_gen_zero = graph_commit;
 
-               if (generation_zero == GENERATION_ZERO_EXISTS)
+               if (seen_gen_zero)
                        continue;
 
                /*
@@ -2628,27 +2798,60 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
                                     graph_commit->date,
                                     odb_commit->date);
        }
-       stop_progress(&progress);
 
-       local_error = verify_commit_graph_error;
+       if (seen_gen_zero && seen_gen_non_zero)
+               graph_report(_("commit-graph has both zero and non-zero "
+                              "generations (e.g., commits '%s' and '%s')"),
+                            oid_to_hex(&seen_gen_zero->object.oid),
+                            oid_to_hex(&seen_gen_non_zero->object.oid));
+
+       return verify_commit_graph_error;
+}
+
+int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
+{
+       struct progress *progress = NULL;
+       int local_error = 0;
+       uint64_t seen = 0;
+
+       if (!g) {
+               graph_report("no commit-graph file loaded");
+               return 1;
+       }
 
-       if (!(flags & COMMIT_GRAPH_VERIFY_SHALLOW) && g->base_graph)
-               local_error |= verify_commit_graph(r, g->base_graph, flags);
+       if (flags & COMMIT_GRAPH_WRITE_PROGRESS) {
+               uint64_t total = g->num_commits;
+               if (!(flags & COMMIT_GRAPH_VERIFY_SHALLOW))
+                       total += g->num_commits_in_base;
+
+               progress = start_progress(_("Verifying commits in commit graph"),
+                                         total);
+       }
+
+       for (; g; g = g->base_graph) {
+               local_error |= verify_one_commit_graph(r, g, progress, &seen);
+               if (flags & COMMIT_GRAPH_VERIFY_SHALLOW)
+                       break;
+       }
+
+       stop_progress(&progress);
 
        return local_error;
 }
 
 void free_commit_graph(struct commit_graph *g)
 {
-       if (!g)
-               return;
-       if (g->data) {
-               munmap((void *)g->data, g->data_len);
-               g->data = NULL;
+       while (g) {
+               struct commit_graph *next = g->base_graph;
+
+               if (g->data)
+                       munmap((void *)g->data, g->data_len);
+               free(g->filename);
+               free(g->bloom_filter_settings);
+               free(g);
+
+               g = next;
        }
-       free(g->filename);
-       free(g->bloom_filter_settings);
-       free(g);
 }
 
 void disable_commit_graph(struct repository *r)
index 37faee6b66d59c693dc9ee829dbd5ad61654fe3c..e519cb81cb649dd016fdb5f26dbfd3623ead13e9 100644 (file)
@@ -1,14 +1,19 @@
 #ifndef COMMIT_GRAPH_H
 #define COMMIT_GRAPH_H
 
-#include "git-compat-util.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "oidset.h"
 
 #define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
 #define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
 #define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
 
+/*
+ * This environment variable controls whether commits looked up via the
+ * commit graph will be double checked to exist in the object database.
+ */
+#define GIT_COMMIT_GRAPH_PARANOIA "GIT_COMMIT_GRAPH_PARANOIA"
+
 /*
  * This method is only used to enhance coverage of the commit-graph
  * feature in the test suite with the GIT_TEST_COMMIT_GRAPH and
@@ -27,6 +32,7 @@ struct string_list;
 char *get_commit_graph_filename(struct object_directory *odb);
 char *get_commit_graph_chain_filename(struct object_directory *odb);
 int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
+int open_commit_graph_chain(const char *chain_file, int *fd, struct stat *st);
 
 /*
  * Given a commit struct, try to fill the commit struct info, including:
@@ -94,10 +100,14 @@ struct commit_graph {
        const unsigned char *chunk_commit_data;
        const unsigned char *chunk_generation_data;
        const unsigned char *chunk_generation_data_overflow;
+       size_t chunk_generation_data_overflow_size;
        const unsigned char *chunk_extra_edges;
+       size_t chunk_extra_edges_size;
        const unsigned char *chunk_base_graphs;
+       size_t chunk_base_graphs_size;
        const unsigned char *chunk_bloom_indexes;
        const unsigned char *chunk_bloom_data;
+       size_t chunk_bloom_data_size;
 
        struct topo_level_slab *topo_levels;
        struct bloom_filter_settings *bloom_filter_settings;
@@ -106,6 +116,9 @@ struct commit_graph {
 struct commit_graph *load_commit_graph_one_fd_st(struct repository *r,
                                                 int fd, struct stat *st,
                                                 struct object_directory *odb);
+struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
+                                                  int fd, struct stat *st,
+                                                  int *incomplete_chain);
 struct commit_graph *read_commit_graph_one(struct repository *r,
                                           struct object_directory *odb);
 
@@ -190,4 +203,12 @@ struct commit_graph_data {
  */
 timestamp_t commit_graph_generation(const struct commit *);
 uint32_t commit_graph_position(const struct commit *);
+
+/*
+ * After this method, all commits reachable from those in the given
+ * list will have non-zero, non-infinite generation numbers.
+ */
+void ensure_generations_valid(struct repository *r,
+                             struct commit **commits, size_t nr);
+
 #endif
index c226ee3da469c50c82dea58804ad3819230ed112..ecc913fc99ba9e6b88df675462a2b8f912cbd0ce 100644 (file)
@@ -1,13 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
 #include "commit-graph.h"
 #include "decorate.h"
+#include "hex.h"
 #include "prio-queue.h"
-#include "tree.h"
 #include "ref-filter.h"
 #include "revision.h"
 #include "tag.h"
 #include "commit-reach.h"
+#include "ewah/ewok.h"
 
 /* Remember to update object flag allocation in object.h */
 #define PARENT1                (1u<<16)
@@ -162,7 +163,8 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
 
                for (j = ret; j; j = j->next) {
                        struct commit_list *bases;
-                       bases = get_merge_bases(i->item, j->item);
+                       bases = repo_get_merge_bases(the_repository, i->item,
+                                                    j->item);
                        if (!new_commits)
                                new_commits = bases;
                        else
@@ -170,6 +172,7 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
                        for (k = bases; k; k = k->next)
                                end = k;
                }
+               free_commit_list(ret);
                ret = new_commits;
        }
        return ret;
@@ -245,8 +248,7 @@ static int remove_redundant_with_gen(struct repository *r,
         * min_gen_pos points to the current position within 'array'
         * that is not yet known to be STALE.
         */
-       ALLOC_ARRAY(sorted, cnt);
-       COPY_ARRAY(sorted, array, cnt);
+       DUP_ARRAY(sorted, array, cnt);
        QSORT(sorted, cnt, compare_commits_by_gen);
        min_generation = commit_graph_generation(sorted[0]);
 
@@ -448,7 +450,7 @@ int repo_is_descendant_of(struct repository *r,
        if (!with_commit)
                return 1;
 
-       if (generation_numbers_enabled(the_repository)) {
+       if (generation_numbers_enabled(r)) {
                struct commit_list *from_list = NULL;
                int result;
                commit_list_insert(commit, &from_list);
@@ -585,7 +587,7 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
                return 0;
        new_commit = (struct commit *) o;
 
-       if (parse_commit(new_commit) < 0)
+       if (repo_parse_commit(the_repository, new_commit) < 0)
                return 0;
 
        commit_list_insert(old_commit, &old_commit_list);
@@ -749,7 +751,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
                }
 
                list[nr_commits] = (struct commit *)from_one;
-               if (parse_commit(list[nr_commits]) ||
+               if (repo_parse_commit(the_repository, list[nr_commits]) ||
                    commit_graph_generation(list[nr_commits]) < min_generation) {
                        result = 0;
                        goto cleanup;
@@ -784,7 +786,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
                                if (!(parent->item->object.flags & assign_flag)) {
                                        parent->item->object.flags |= assign_flag;
 
-                                       if (parse_commit(parent->item) ||
+                                       if (repo_parse_commit(the_repository, parent->item) ||
                                            parent->item->date < min_commit_date ||
                                            commit_graph_generation(parent->item) < min_generation)
                                                continue;
@@ -808,8 +810,12 @@ cleanup:
        clear_commit_marks_many(nr_commits, list, RESULT | assign_flag);
        free(list);
 
-       for (i = 0; i < from->nr; i++)
-               from->objects[i].item->flags &= ~assign_flag;
+       for (i = 0; i < from->nr; i++) {
+               struct object *from_one = from->objects[i].item;
+
+               if (from_one)
+                       from_one->flags &= ~assign_flag;
+       }
 
        return result;
 }
@@ -826,7 +832,7 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
        while (from_iter) {
                add_object_array(&from_iter->item->object, NULL, &from_objs);
 
-               if (!parse_commit(from_iter->item)) {
+               if (!repo_parse_commit(the_repository, from_iter->item)) {
                        timestamp_t generation;
                        if (from_iter->item->date < min_commit_date)
                                min_commit_date = from_iter->item->date;
@@ -840,7 +846,7 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
        }
 
        while (to_iter) {
-               if (!parse_commit(to_iter->item)) {
+               if (!repo_parse_commit(the_repository, to_iter->item)) {
                        timestamp_t generation;
                        if (to_iter->item->date < min_commit_date)
                                min_commit_date = to_iter->item->date;
@@ -890,7 +896,7 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
                timestamp_t generation;
                struct commit *c = *item;
 
-               parse_commit(c);
+               repo_parse_commit(the_repository, c);
                generation = commit_graph_generation(c);
                if (generation < min_generation)
                        min_generation = generation;
@@ -905,7 +911,7 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
                struct commit *c = *item;
                if (!(c->object.flags & PARENT2)) {
                        c->object.flags |= PARENT2;
-                       parse_commit(c);
+                       repo_parse_commit(the_repository, c);
 
                        prio_queue_put(&queue, *item);
                }
@@ -924,7 +930,7 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
                for (parents = current->parents; parents; parents = parents->next) {
                        struct commit *p = parents->item;
 
-                       parse_commit(p);
+                       repo_parse_commit(the_repository, p);
 
                        if (commit_graph_generation(p) < min_generation)
                                continue;
@@ -937,8 +943,225 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
                }
        }
 
+       clear_prio_queue(&queue);
+
        clear_commit_marks_many(nr_to, to, PARENT1);
        clear_commit_marks_many(nr_from, from, PARENT2);
 
        return found_commits;
 }
+
+define_commit_slab(bit_arrays, struct bitmap *);
+static struct bit_arrays bit_arrays;
+
+static void insert_no_dup(struct prio_queue *queue, struct commit *c)
+{
+       if (c->object.flags & PARENT2)
+               return;
+       prio_queue_put(queue, c);
+       c->object.flags |= PARENT2;
+}
+
+static struct bitmap *get_bit_array(struct commit *c, int width)
+{
+       struct bitmap **bitmap = bit_arrays_at(&bit_arrays, c);
+       if (!*bitmap)
+               *bitmap = bitmap_word_alloc(width);
+       return *bitmap;
+}
+
+static void free_bit_array(struct commit *c)
+{
+       struct bitmap **bitmap = bit_arrays_at(&bit_arrays, c);
+       if (!*bitmap)
+               return;
+       bitmap_free(*bitmap);
+       *bitmap = NULL;
+}
+
+void ahead_behind(struct repository *r,
+                 struct commit **commits, size_t commits_nr,
+                 struct ahead_behind_count *counts, size_t counts_nr)
+{
+       struct prio_queue queue = { .compare = compare_commits_by_gen_then_commit_date };
+       size_t width = DIV_ROUND_UP(commits_nr, BITS_IN_EWORD);
+
+       if (!commits_nr || !counts_nr)
+               return;
+
+       for (size_t i = 0; i < counts_nr; i++) {
+               counts[i].ahead = 0;
+               counts[i].behind = 0;
+       }
+
+       ensure_generations_valid(r, commits, commits_nr);
+
+       init_bit_arrays(&bit_arrays);
+
+       for (size_t i = 0; i < commits_nr; i++) {
+               struct commit *c = commits[i];
+               struct bitmap *bitmap = get_bit_array(c, width);
+
+               bitmap_set(bitmap, i);
+               insert_no_dup(&queue, c);
+       }
+
+       while (queue_has_nonstale(&queue)) {
+               struct commit *c = prio_queue_get(&queue);
+               struct commit_list *p;
+               struct bitmap *bitmap_c = get_bit_array(c, width);
+
+               for (size_t i = 0; i < counts_nr; i++) {
+                       int reach_from_tip = !!bitmap_get(bitmap_c, counts[i].tip_index);
+                       int reach_from_base = !!bitmap_get(bitmap_c, counts[i].base_index);
+
+                       if (reach_from_tip ^ reach_from_base) {
+                               if (reach_from_base)
+                                       counts[i].behind++;
+                               else
+                                       counts[i].ahead++;
+                       }
+               }
+
+               for (p = c->parents; p; p = p->next) {
+                       struct bitmap *bitmap_p;
+
+                       repo_parse_commit(r, p->item);
+
+                       bitmap_p = get_bit_array(p->item, width);
+                       bitmap_or(bitmap_p, bitmap_c);
+
+                       /*
+                        * If this parent is reachable from every starting
+                        * commit, then none of its ancestors can contribute
+                        * to the ahead/behind count. Mark it as STALE, so
+                        * we can stop the walk when every commit in the
+                        * queue is STALE.
+                        */
+                       if (bitmap_popcount(bitmap_p) == commits_nr)
+                               p->item->object.flags |= STALE;
+
+                       insert_no_dup(&queue, p->item);
+               }
+
+               free_bit_array(c);
+       }
+
+       /* STALE is used here, PARENT2 is used by insert_no_dup(). */
+       repo_clear_commit_marks(r, PARENT2 | STALE);
+       clear_bit_arrays(&bit_arrays);
+       clear_prio_queue(&queue);
+}
+
+struct commit_and_index {
+       struct commit *commit;
+       unsigned int index;
+       timestamp_t generation;
+};
+
+static int compare_commit_and_index_by_generation(const void *va, const void *vb)
+{
+       const struct commit_and_index *a = (const struct commit_and_index *)va;
+       const struct commit_and_index *b = (const struct commit_and_index *)vb;
+
+       if (a->generation > b->generation)
+               return 1;
+       if (a->generation < b->generation)
+               return -1;
+       return 0;
+}
+
+void tips_reachable_from_bases(struct repository *r,
+                              struct commit_list *bases,
+                              struct commit **tips, size_t tips_nr,
+                              int mark)
+{
+       struct commit_and_index *commits;
+       size_t min_generation_index = 0;
+       timestamp_t min_generation;
+       struct commit_list *stack = NULL;
+
+       if (!bases || !tips || !tips_nr)
+               return;
+
+       /*
+        * Do a depth-first search starting at 'bases' to search for the
+        * tips. Stop at the lowest (un-found) generation number. When
+        * finding the lowest commit, increase the minimum generation
+        * number to the next lowest (un-found) generation number.
+        */
+
+       CALLOC_ARRAY(commits, tips_nr);
+
+       for (size_t i = 0; i < tips_nr; i++) {
+               commits[i].commit = tips[i];
+               commits[i].index = i;
+               commits[i].generation = commit_graph_generation(tips[i]);
+       }
+
+       /* Sort with generation number ascending. */
+       QSORT(commits, tips_nr, compare_commit_and_index_by_generation);
+       min_generation = commits[0].generation;
+
+       while (bases) {
+               repo_parse_commit(r, bases->item);
+               commit_list_insert(bases->item, &stack);
+               bases = bases->next;
+       }
+
+       while (stack) {
+               int explored_all_parents = 1;
+               struct commit_list *p;
+               struct commit *c = stack->item;
+               timestamp_t c_gen = commit_graph_generation(c);
+
+               /* Does it match any of our tips? */
+               for (size_t j = min_generation_index; j < tips_nr; j++) {
+                       if (c_gen < commits[j].generation)
+                               break;
+
+                       if (commits[j].commit == c) {
+                               tips[commits[j].index]->object.flags |= mark;
+
+                               if (j == min_generation_index) {
+                                       unsigned int k = j + 1;
+                                       while (k < tips_nr &&
+                                              (tips[commits[k].index]->object.flags & mark))
+                                               k++;
+
+                                       /* Terminate early if all found. */
+                                       if (k >= tips_nr)
+                                               goto done;
+
+                                       min_generation_index = k;
+                                       min_generation = commits[k].generation;
+                               }
+                       }
+               }
+
+               for (p = c->parents; p; p = p->next) {
+                       repo_parse_commit(r, p->item);
+
+                       /* Have we already explored this parent? */
+                       if (p->item->object.flags & SEEN)
+                               continue;
+
+                       /* Is it below the current minimum generation? */
+                       if (commit_graph_generation(p->item) < min_generation)
+                               continue;
+
+                       /* Ok, we will explore from here on. */
+                       p->item->object.flags |= SEEN;
+                       explored_all_parents = 0;
+                       commit_list_insert(p->item, &stack);
+                       break;
+               }
+
+               if (explored_all_parents)
+                       pop_commit(&stack);
+       }
+
+done:
+       free(commits);
+       repo_clear_commit_marks(r, SEEN);
+}
index 148b56fea50629607d2f0dd196cbd4dec08fdcfd..35c4da4948122a6caea3a1757484b487db16b0fd 100644 (file)
@@ -19,11 +19,6 @@ struct commit_list *repo_get_merge_bases_many(struct repository *r,
 struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
                                                    struct commit *one, int n,
                                                    struct commit **twos);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define get_merge_bases(r1, r2)           repo_get_merge_bases(the_repository, r1, r2)
-#define get_merge_bases_many(one, n, two) repo_get_merge_bases_many(the_repository, one, n, two)
-#define get_merge_bases_many_dirty(one, n, twos) repo_get_merge_bases_many_dirty(the_repository, one, n, twos)
-#endif
 
 struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 
@@ -36,10 +31,6 @@ int repo_in_merge_bases(struct repository *r,
 int repo_in_merge_bases_many(struct repository *r,
                             struct commit *commit,
                             int nr_reference, struct commit **reference);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define in_merge_bases(c1, c2) repo_in_merge_bases(the_repository, c1, c2)
-#define in_merge_bases_many(c1, n, cs) repo_in_merge_bases_many(the_repository, c1, n, cs)
-#endif
 
 /*
  * Takes a list of commits and returns a new list where those
@@ -104,4 +95,44 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
                                         struct commit **to, int nr_to,
                                         unsigned int reachable_flag);
 
+struct ahead_behind_count {
+       /**
+        * As input, the *_index members indicate which positions in
+        * the 'tips' array correspond to the tip and base of this
+        * comparison.
+        */
+       size_t tip_index;
+       size_t base_index;
+
+       /**
+        * These values store the computed counts for each side of the
+        * symmetric difference:
+        *
+        * 'ahead' stores the number of commits reachable from the tip
+        * and not reachable from the base.
+        *
+        * 'behind' stores the number of commits reachable from the base
+        * and not reachable from the tip.
+        */
+       unsigned int ahead;
+       unsigned int behind;
+};
+
+/*
+ * Given an array of commits and an array of ahead_behind_count pairs,
+ * compute the ahead/behind counts for each pair.
+ */
+void ahead_behind(struct repository *r,
+                 struct commit **commits, size_t commits_nr,
+                 struct ahead_behind_count *counts, size_t counts_nr);
+
+/*
+ * For all tip commits, add 'mark' to their flags if and only if they
+ * are reachable from one of the commits in 'bases'.
+ */
+void tips_reachable_from_bases(struct repository *r,
+                              struct commit_list *bases,
+                              struct commit **tips, size_t tips_nr,
+                              int mark);
+
 #endif
index 557738df271c7dd78c5ddcc65d6fe6e47c665758..4a414ee905d5de4498d1fcca2da53e7c6a07b50e 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef COMMIT_SLAB_IMPL_H
 #define COMMIT_SLAB_IMPL_H
 
-#include "git-compat-util.h"
-
 #define implement_static_commit_slab(slabname, elemtype) \
        implement_commit_slab(slabname, elemtype, MAYBE_UNUSED static)
 
index 89b8efc6116883d032912cdc38429b5a8ba5f2f6..ef679a0b939046c4aac15567cdf3c0ae8c079d29 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1,10 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tag.h"
 #include "commit.h"
 #include "commit-graph.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
-#include "object-store.h"
-#include "pkt-line.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
 #include "advice.h"
 #include "refs.h"
 #include "commit-reach.h"
-#include "run-command.h"
+#include "setup.h"
 #include "shallow.h"
+#include "tree.h"
 #include "hook.h"
+#include "parse.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -59,6 +64,14 @@ struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref
        return c;
 }
 
+struct commit *lookup_commit_object(struct repository *r,
+                                   const struct object_id *oid)
+{
+       struct object *obj = parse_object(r, oid);
+       return obj ? object_as_type(obj, OBJ_COMMIT, 0) : NULL;
+
+}
+
 struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
 {
        struct object *obj = lookup_object(r, oid);
@@ -72,10 +85,10 @@ struct commit *lookup_commit_reference_by_name(const char *name)
        struct object_id oid;
        struct commit *commit;
 
-       if (get_oid_committish(name, &oid))
+       if (repo_get_oid_committish(the_repository, name, &oid))
                return NULL;
        commit = lookup_commit_reference(the_repository, &oid);
-       if (parse_commit(commit))
+       if (repo_parse_commit(the_repository, commit))
                return NULL;
        return commit;
 }
@@ -83,6 +96,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
 static timestamp_t parse_commit_date(const char *buf, const char *tail)
 {
        const char *dateptr;
+       const char *eol;
 
        if (buf + 6 >= tail)
                return 0;
@@ -94,16 +108,56 @@ static timestamp_t parse_commit_date(const char *buf, const char *tail)
                return 0;
        if (memcmp(buf, "committer", 9))
                return 0;
-       while (buf < tail && *buf++ != '>')
-               /* nada */;
-       if (buf >= tail)
+
+       /*
+        * Jump to end-of-line so that we can walk backwards to find the
+        * end-of-email ">". This is more forgiving of malformed cases
+        * because unexpected characters tend to be in the name and email
+        * fields.
+        */
+       eol = memchr(buf, '\n', tail - buf);
+       if (!eol)
                return 0;
-       dateptr = buf;
-       while (buf < tail && *buf++ != '\n')
-               /* nada */;
-       if (buf >= tail)
+       dateptr = eol;
+       while (dateptr > buf && dateptr[-1] != '>')
+               dateptr--;
+       if (dateptr == buf)
                return 0;
-       /* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+
+       /*
+        * Trim leading whitespace, but make sure we have at least one
+        * non-whitespace character, as parse_timestamp() will otherwise walk
+        * right past the newline we found in "eol" when skipping whitespace
+        * itself.
+        *
+        * In theory it would be sufficient to allow any character not matched
+        * by isspace(), but there's a catch: our isspace() does not
+        * necessarily match the behavior of parse_timestamp(), as the latter
+        * is implemented by system routines which match more exotic control
+        * codes, or even locale-dependent sequences.
+        *
+        * Since we expect the timestamp to be a number, we can check for that.
+        * Anything else (e.g., a non-numeric token like "foo") would just
+        * cause parse_timestamp() to return 0 anyway.
+        */
+       while (dateptr < eol && isspace(*dateptr))
+               dateptr++;
+       if (!isdigit(*dateptr) && *dateptr != '-')
+               return 0;
+
+       /*
+        * We know there is at least one digit (or dash), so we'll begin
+        * parsing there and stop at worst case at eol.
+        *
+        * Note that we may feed parse_timestamp() extra characters here if the
+        * commit is malformed, and it will parse as far as it can. For
+        * example, "123foo456" would return "123". That might be questionable
+        * (versus returning "0"), but it would help in a hypothetical case
+        * like "123456+0100", where the whitespace from the timezone is
+        * missing. Since such syntactic errors may be baked into history and
+        * hard to correct now, let's err on trying to make our best guess
+        * here, rather than insist on perfect syntax.
+        */
        return parse_timestamp(dateptr, NULL, 10);
 }
 
@@ -374,7 +428,7 @@ struct tree *repo_get_commit_tree(struct repository *r,
 
 struct object_id *get_commit_tree_oid(const struct commit *commit)
 {
-       struct tree *tree = get_commit_tree(commit);
+       struct tree *tree = repo_get_commit_tree(the_repository, commit);
        return tree ? &tree->object.oid : NULL;
 }
 
@@ -461,7 +515,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
                 * The clone is shallow if nr_parent < 0, and we must
                 * not traverse its real parents even when we unhide them.
                 */
-               if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
+               if (graft && (graft->nr_parent < 0 || !grafts_keep_true_parents))
                        continue;
                new_parent = lookup_commit(r, &parent);
                if (!new_parent)
@@ -500,16 +554,40 @@ int repo_parse_commit_internal(struct repository *r,
        enum object_type type;
        void *buffer;
        unsigned long size;
+       struct object_info oi = {
+               .typep = &type,
+               .sizep = &size,
+               .contentp = &buffer,
+       };
+       /*
+        * Git does not support partial clones that exclude commits, so set
+        * OBJECT_INFO_SKIP_FETCH_OBJECT to fail fast when an object is missing.
+        */
+       int flags = OBJECT_INFO_LOOKUP_REPLACE | OBJECT_INFO_SKIP_FETCH_OBJECT |
+               OBJECT_INFO_DIE_IF_CORRUPT;
        int ret;
 
        if (!item)
                return -1;
        if (item->object.parsed)
                return 0;
-       if (use_commit_graph && parse_commit_in_graph(r, item))
+       if (use_commit_graph && parse_commit_in_graph(r, item)) {
+               static int commit_graph_paranoia = -1;
+
+               if (commit_graph_paranoia == -1)
+                       commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 0);
+
+               if (commit_graph_paranoia && !has_object(r, &item->object.oid, 0)) {
+                       unparse_commit(r, &item->object.oid);
+                       return quiet_on_missing ? -1 :
+                               error(_("commit %s exists in commit-graph but not in the object database"),
+                                     oid_to_hex(&item->object.oid));
+               }
+
                return 0;
-       buffer = repo_read_object_file(r, &item->object.oid, &type, &size);
-       if (!buffer)
+       }
+
+       if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
                             oid_to_hex(&item->object.oid));
@@ -536,7 +614,7 @@ int repo_parse_commit_gently(struct repository *r,
 
 void parse_commit_or_die(struct commit *item)
 {
-       if (parse_commit(item))
+       if (repo_parse_commit(the_repository, item))
                die("unable to parse commit %s",
                    item ? oid_to_hex(&item->object.oid) : "(null)");
 }
@@ -669,7 +747,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list,
 
        while (parents) {
                struct commit *commit = parents->item;
-               if (!parse_commit(commit) && !(commit->object.flags & mark)) {
+               if (!repo_parse_commit(the_repository, commit) && !(commit->object.flags & mark)) {
                        commit->object.flags |= mark;
                        commit_list_insert_by_date(commit, list);
                }
@@ -693,8 +771,10 @@ static void clear_commit_marks_1(struct commit_list **plist,
                if (!parents)
                        return;
 
-               while ((parents = parents->next))
-                       commit_list_insert(parents->item, plist);
+               while ((parents = parents->next)) {
+                       if (parents->item->object.flags & mark)
+                               commit_list_insert(parents->item, plist);
+               }
 
                commit = commit->parents->item;
        }
@@ -741,7 +821,8 @@ define_commit_slab(author_date_slab, timestamp_t);
 void record_author_date(struct author_date_slab *author_date,
                        struct commit *commit)
 {
-       const char *buffer = get_commit_buffer(commit, NULL);
+       const char *buffer = repo_get_commit_buffer(the_repository, commit,
+                                                   NULL);
        struct ident_split ident;
        const char *ident_line;
        size_t ident_len;
@@ -761,7 +842,7 @@ void record_author_date(struct author_date_slab *author_date,
        *(author_date_slab_at(author_date, commit)) = date;
 
 fail_exit:
-       unuse_commit_buffer(commit, buffer);
+       repo_unuse_commit_buffer(the_repository, commit, buffer);
 }
 
 int compare_commits_by_author_date(const void *a_, const void *b_,
@@ -780,7 +861,8 @@ int compare_commits_by_author_date(const void *a_, const void *b_,
        return 0;
 }
 
-int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
+int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_,
+                                           void *unused UNUSED)
 {
        const struct commit *a = a_, *b = b_;
        const timestamp_t generation_a = commit_graph_generation(a),
@@ -800,7 +882,8 @@ int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void
        return 0;
 }
 
-int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
+int compare_commits_by_commit_date(const void *a_, const void *b_,
+                                  void *unused UNUSED)
 {
        const struct commit *a = a_, *b = b_;
        /* newer commits with larger date first */
@@ -942,7 +1025,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
        commit = lookup_commit(the_repository, oid);
        if (!commit ||
            (commit->object.flags & TMP_MARK) ||
-           parse_commit(commit))
+           repo_parse_commit(the_repository, commit))
                return;
 
        ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
@@ -974,7 +1057,8 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
        struct commit *ret = NULL;
        char *full_refname;
 
-       switch (dwim_ref(refname, strlen(refname), &oid, &full_refname, 0)) {
+       switch (repo_dwim_ref(the_repository, refname, strlen(refname), &oid,
+                             &full_refname, 0)) {
        case 0:
                die("No such ref: '%s'", refname);
        case 1:
@@ -993,7 +1077,8 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
        for (i = 0; i < revs.nr; i++)
                revs.commit[i]->object.flags &= ~TMP_MARK;
 
-       bases = get_merge_bases_many(commit, revs.nr, revs.commit);
+       bases = repo_get_merge_bases_many(the_repository, commit, revs.nr,
+                                         revs.commit);
 
        /*
         * There should be one and only one merge base, when we found
@@ -1012,6 +1097,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
        ret = bases->item;
 
 cleanup_return:
+       free(revs.commit);
        free_commit_list(bases);
        free(full_refname);
        return ret;
@@ -1073,10 +1159,11 @@ int parse_signed_commit(const struct commit *commit,
                        const struct git_hash_algo *algop)
 {
        unsigned long size;
-       const char *buffer = get_commit_buffer(commit, &size);
+       const char *buffer = repo_get_commit_buffer(the_repository, commit,
+                                                   &size);
        int ret = parse_buffer_signed_by_header(buffer, size, payload, signature, algop);
 
-       unuse_commit_buffer(commit, buffer);
+       repo_unuse_commit_buffer(the_repository, commit, buffer);
        return ret;
 }
 
@@ -1187,7 +1274,8 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
        desc = merge_remote_util(parent);
        if (!desc || !desc->obj)
                return;
-       buf = read_object_file(&desc->obj->oid, &type, &size);
+       buf = repo_read_object_file(the_repository, &desc->obj->oid, &type,
+                                   &size);
        if (!buf || type != OBJ_TAG)
                goto free_return;
        if (!parse_signature(buf, size, &payload, &signature))
@@ -1249,7 +1337,8 @@ void verify_merge_signature(struct commit *commit, int verbosity,
 
        ret = check_commit_signature(commit, &signature_check);
 
-       find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
+       repo_find_unique_abbrev_r(the_repository, hex, &commit->object.oid,
+                                 DEFAULT_ABBREV);
        switch (signature_check.result) {
        case 'G':
                if (ret || (check_trust && signature_check.trust_level < TRUST_MARGINAL))
@@ -1294,9 +1383,10 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
 {
        struct commit_extra_header *extra = NULL;
        unsigned long size;
-       const char *buffer = get_commit_buffer(commit, &size);
+       const char *buffer = repo_get_commit_buffer(the_repository, commit,
+                                                   &size);
        extra = read_commit_extra_header_lines(buffer, size, exclude);
-       unuse_commit_buffer(commit, buffer);
+       repo_unuse_commit_buffer(the_repository, commit, buffer);
        return extra;
 }
 
@@ -1610,10 +1700,11 @@ struct commit *get_merge_parent(const char *name)
        struct object *obj;
        struct commit *commit;
        struct object_id oid;
-       if (get_oid(name, &oid))
+       if (repo_get_oid(the_repository, name, &oid))
                return NULL;
        obj = parse_object(the_repository, &oid);
-       commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+       commit = (struct commit *)repo_peel_to_type(the_repository, name, 0,
+                                                   obj, OBJ_COMMIT);
        if (commit && !merge_remote_util(commit))
                set_merge_remote_desc(commit, name, obj);
        return commit;
@@ -1690,7 +1781,7 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
  * Returns the number of bytes from the tail to ignore, to be fed as
  * the second parameter to append_signoff().
  */
-size_t ignore_non_trailer(const char *buf, size_t len)
+size_t ignored_log_message_bytes(const char *buf, size_t len)
 {
        size_t boc = 0;
        size_t bol = 0;
index 21e4d25ce7878ab6463110b6b3104c7b99d159cb..1cc872f225f438be7dc03a9b4dc8f207da0deb5b 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -2,13 +2,10 @@
 #define COMMIT_H
 
 #include "object.h"
-#include "tree.h"
-#include "strbuf.h"
-#include "decorate.h"
-#include "gpg-interface.h"
-#include "string-list.h"
-#include "pretty.h"
-#include "commit-slab.h"
+
+struct signature_check;
+struct strbuf;
+struct tree;
 
 #define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
 #define GENERATION_NUMBER_INFINITY ((1ULL << 63) - 1)
@@ -64,6 +61,19 @@ enum decoration_type {
 void add_name_decoration(enum decoration_type type, const char *name, struct object *obj);
 const struct name_decoration *get_name_decoration(const struct object *obj);
 
+/*
+ * Look up commit named by "oid" respecting replacement objects.
+ * Returns NULL if "oid" is not a commit or does not exist.
+ */
+struct commit *lookup_commit_object(struct repository *r, const struct object_id *oid);
+
+/*
+ * Look up commit named by "oid" without replacement objects or
+ * checking for object existence. Returns the requested commit if it
+ * is found in the object cache, NULL if "oid" is in the object cache
+ * but is not a commit and a newly allocated unparsed commit object if
+ * "oid" is not in the object cache.
+ */
 struct commit *lookup_commit(struct repository *r, const struct object_id *oid);
 struct commit *lookup_commit_reference(struct repository *r,
                                       const struct object_id *oid);
@@ -96,11 +106,6 @@ static inline int repo_parse_commit_no_graph(struct repository *r,
        return repo_parse_commit_internal(r, commit, 0, 0);
 }
 
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define parse_commit_internal(item, quiet, use) repo_parse_commit_internal(the_repository, item, quiet, use)
-#define parse_commit(item) repo_parse_commit(the_repository, item)
-#endif
-
 void parse_commit_or_die(struct commit *item);
 
 struct buffer_slab;
@@ -122,27 +127,21 @@ const void *get_cached_commit_buffer(struct repository *, const struct commit *,
 /*
  * Get the commit's object contents, either from cache or by reading the object
  * from disk. The resulting memory should not be modified, and must be given
- * to unuse_commit_buffer when the caller is done.
+ * to repo_unuse_commit_buffer when the caller is done.
  */
 const void *repo_get_commit_buffer(struct repository *r,
                                   const struct commit *,
                                   unsigned long *size);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define get_commit_buffer(c, s) repo_get_commit_buffer(the_repository, c, s)
-#endif
 
 /*
  * Tell the commit subsystem that we are done with a particular commit buffer.
  * The commit and buffer should be the input and return value, respectively,
- * from an earlier call to get_commit_buffer.  The buffer may or may not be
+ * from an earlier call to repo_get_commit_buffer.  The buffer may or may not be
  * freed by this call; callers should not access the memory afterwards.
  */
 void repo_unuse_commit_buffer(struct repository *r,
                              const struct commit *,
                              const void *buffer);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define unuse_commit_buffer(c, b) repo_unuse_commit_buffer(the_repository, c, b)
-#endif
 
 /*
  * Free any cached object buffer associated with the commit.
@@ -150,7 +149,6 @@ void repo_unuse_commit_buffer(struct repository *r,
 void free_commit_buffer(struct parsed_object_pool *pool, struct commit *);
 
 struct tree *repo_get_commit_tree(struct repository *, const struct commit *);
-#define get_commit_tree(c) repo_get_commit_tree(the_repository, c)
 struct object_id *get_commit_tree_oid(const struct commit *);
 
 /*
@@ -192,17 +190,10 @@ void free_commit_list(struct commit_list *list);
 
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 
-int has_non_ascii(const char *text);
-const char *logmsg_reencode(const struct commit *commit,
-                           char **commit_encoding,
-                           const char *output_encoding);
 const char *repo_logmsg_reencode(struct repository *r,
                                 const struct commit *commit,
                                 char **commit_encoding,
                                 const char *output_encoding);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define logmsg_reencode(c, enc, out) repo_logmsg_reencode(the_repository, c, enc, out)
-#endif
 
 const char *skip_blank_lines(const char *msg);
 
@@ -261,8 +252,6 @@ struct ref;
 int for_each_commit_graft(each_commit_graft_fn, void *);
 
 int interactive_add(const char **argv, const char *prefix, int patch);
-int run_add_interactive(const char *revision, const char *patch_mode,
-                       const struct pathspec *pathspec);
 
 struct commit_extra_header {
        struct commit_extra_header *next;
@@ -305,8 +294,8 @@ const char *find_header_mem(const char *msg, size_t len,
 const char *find_commit_header(const char *msg, const char *key,
                               size_t *out_len);
 
-/* Find the end of the log message, the right place for a new trailer. */
-size_t ignore_non_trailer(const char *buf, size_t len);
+/* Find the number of bytes to ignore from the end of a log message. */
+size_t ignored_log_message_bytes(const char *buf, size_t len);
 
 typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
                                void *cb_data);
index c531372f3ff0f13839a2056568cbaa7fd37f98a8..033778b3c56a3557e191d3bb74592fd00497853b 100644 (file)
@@ -1,6 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "attr.h"
+#include "repository.h"
+#include "setup.h"
+#include "strbuf.h"
+#include "trace2.h"
 
 /*
  * Many parts of Git have subprograms communicate via pipe, expect the
@@ -40,6 +45,7 @@ int main(int argc, const char **argv)
 
        git_resolve_executable_dir(argv[0]);
 
+       setlocale(LC_CTYPE, "");
        git_setup_gettext();
 
        initialize_the_repository();
index 50a32e3d8a43bc2bc45c8c8afa2c59e79518fc07..6c979c27d89ec858b849f87e64505c2554cb121b 100644 (file)
@@ -2,6 +2,8 @@
 #define COMPAT_DISK_H
 
 #include "git-compat-util.h"
+#include "abspath.h"
+#include "gettext.h"
 
 static int get_disk_info(struct strbuf *out)
 {
index 1c75c3d48e7d7c3d960e4cf7ff3bf672f4fe1432..3496e29b3a1f1b8ea04224b63b65539edda602df 100644 (file)
@@ -80,9 +80,7 @@ void CFRunLoopRun(void);
 void CFRunLoopStop(CFRunLoopRef run_loop);
 CFRunLoopRef CFRunLoopGetCurrent(void);
 extern CFStringRef kCFRunLoopDefaultMode;
-void FSEventStreamScheduleWithRunLoop(FSEventStreamRef stream,
-                                     CFRunLoopRef run_loop,
-                                     CFStringRef run_loop_mode);
+void FSEventStreamSetDispatchQueue(FSEventStreamRef stream, dispatch_queue_t q);
 unsigned char FSEventStreamStart(FSEventStreamRef stream);
 void FSEventStreamStop(FSEventStreamRef stream);
 void FSEventStreamInvalidate(FSEventStreamRef stream);
index b9f709e854833f67b45725e1684abe2ef06e16ca..c2afcbe6c89d9ab2f279b5ac7cb4c9a3e6ae01ea 100644 (file)
@@ -1,24 +1,24 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsm-health.h"
 #include "fsmonitor--daemon.h"
 
-int fsm_health__ctor(struct fsmonitor_daemon_state *state)
+int fsm_health__ctor(struct fsmonitor_daemon_state *state UNUSED)
 {
        return 0;
 }
 
-void fsm_health__dtor(struct fsmonitor_daemon_state *state)
+void fsm_health__dtor(struct fsmonitor_daemon_state *state UNUSED)
 {
        return;
 }
 
-void fsm_health__loop(struct fsmonitor_daemon_state *state)
+void fsm_health__loop(struct fsmonitor_daemon_state *state UNUSED)
 {
        return;
 }
 
-void fsm_health__stop_async(struct fsmonitor_daemon_state *state)
+void fsm_health__stop_async(struct fsmonitor_daemon_state *state UNUSED)
 {
 }
index 2ea08c1d4e81ecec5c6cada8f0e9fff76303fcc4..2aa8c219acee4def1711e5f84d45571e34be4e16 100644 (file)
@@ -1,8 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsm-health.h"
 #include "fsmonitor--daemon.h"
+#include "gettext.h"
+#include "simple-ipc.h"
 
 /*
  * Every minute wake up and test our health.
diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c
new file mode 100644 (file)
index 0000000..6f3a954
--- /dev/null
@@ -0,0 +1,56 @@
+#include "git-compat-util.h"
+#include "config.h"
+#include "gettext.h"
+#include "hex.h"
+#include "path.h"
+#include "repository.h"
+#include "strbuf.h"
+#include "fsmonitor-ll.h"
+#include "fsmonitor-ipc.h"
+#include "fsmonitor-path-utils.h"
+
+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, "fsmonitor--daemon.ipc")
+
+const char *fsmonitor_ipc__get_path(struct repository *r)
+{
+       static const char *ipc_path = NULL;
+       git_SHA_CTX sha1ctx;
+       char *sock_dir = NULL;
+       struct strbuf ipc_file = STRBUF_INIT;
+       unsigned char hash[GIT_MAX_RAWSZ];
+
+       if (!r)
+               BUG("No repository passed into fsmonitor_ipc__get_path");
+
+       if (ipc_path)
+               return ipc_path;
+
+
+       /* By default the socket file is created in the .git directory */
+       if (fsmonitor__is_fs_remote(r->gitdir) < 1) {
+               ipc_path = fsmonitor_ipc__get_default_path();
+               return ipc_path;
+       }
+
+       git_SHA1_Init(&sha1ctx);
+       git_SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));
+       git_SHA1_Final(hash, &sha1ctx);
+
+       repo_config_get_string(r, "fsmonitor.socketdir", &sock_dir);
+
+       /* Create the socket file in either socketDir or $HOME */
+       if (sock_dir && *sock_dir) {
+               strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
+                                       sock_dir, hash_to_hex(hash));
+       } else {
+               strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
+       }
+       free(sock_dir);
+
+       ipc_path = interpolate_path(ipc_file.buf, 1);
+       if (!ipc_path)
+               die(_("Invalid path: %s"), ipc_file.buf);
+
+       strbuf_release(&ipc_file);
+       return ipc_path;
+}
diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c
new file mode 100644 (file)
index 0000000..41984ea
--- /dev/null
@@ -0,0 +1,11 @@
+#include "git-compat-util.h"
+#include "config.h"
+#include "fsmonitor-ipc.h"
+#include "path.h"
+
+const char *fsmonitor_ipc__get_path(struct repository *r) {
+       static char *ret;
+       if (!ret)
+               ret = repo_git_path(r, "fsmonitor--daemon.ipc");
+       return ret;
+}
index 8e208e8289e0caef1d12425bc913b7069fe63a60..2fc67442eb5e87e0b006f997fd6b597f9a919b99 100644 (file)
@@ -1,4 +1,5 @@
 #ifndef __clang__
+#include <dispatch/dispatch.h>
 #include "fsm-darwin-gcc.h"
 #else
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 #endif
 
-#include "cache.h"
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
 #include "fsm-listen.h"
 #include "fsmonitor--daemon.h"
+#include "fsmonitor-path-utils.h"
+#include "gettext.h"
+#include "simple-ipc.h"
+#include "string-list.h"
+#include "trace.h"
 
 struct fsm_listen_data
 {
@@ -37,7 +43,9 @@ struct fsm_listen_data
 
        FSEventStreamRef stream;
 
-       CFRunLoopRef rl;
+       dispatch_queue_t dq;
+       pthread_cond_t dq_finished;
+       pthread_mutex_t dq_lock;
 
        enum shutdown_style {
                SHUTDOWN_EVENT = 0,
@@ -184,12 +192,12 @@ static void my_add_path(struct fsmonitor_batch *batch, const char *path)
 }
 
 
-static void fsevent_callback(ConstFSEventStreamRef streamRef,
+static void fsevent_callback(ConstFSEventStreamRef streamRef UNUSED,
                             void *ctx,
                             size_t num_of_events,
                             void *event_paths,
                             const FSEventStreamEventFlags event_flags[],
-                            const FSEventStreamEventId event_ids[])
+                            const FSEventStreamEventId event_ids[] UNUSED)
 {
        struct fsmonitor_daemon_state *state = ctx;
        struct fsm_listen_data *data = state->listen_data;
@@ -198,8 +206,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
        struct string_list cookie_list = STRING_LIST_INIT_DUP;
        const char *path_k;
        const char *slash;
-       int k;
+       char *resolved = NULL;
        struct strbuf tmp = STRBUF_INIT;
+       int k;
 
        /*
         * Build a list of all filesystem changes into a private/local
@@ -209,7 +218,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
                /*
                 * On Mac, we receive an array of absolute paths.
                 */
-               path_k = paths[k];
+               free(resolved);
+               resolved = fsmonitor__resolve_alias(paths[k], &state->alias);
+               if (resolved)
+                       path_k = resolved;
+               else
+                       path_k = paths[k];
 
                /*
                 * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.
@@ -238,6 +252,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
                        fsmonitor_force_resync(state);
                        fsmonitor_batch__free_list(batch);
                        string_list_clear(&cookie_list, 0);
+                       batch = NULL;
 
                        /*
                         * We assume that any events that we received
@@ -328,7 +343,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
                         * know how much to invalidate/refresh.
                         */
 
-                       if (event_flags[k] & kFSEventStreamEventFlagItemIsFile) {
+                       if (event_flags[k] & (kFSEventStreamEventFlagItemIsFile | kFSEventStreamEventFlagItemIsSymlink)) {
                                const char *rel = path_k +
                                        state->path_worktree_watch.len + 1;
 
@@ -360,17 +375,22 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
                }
        }
 
+       free(resolved);
        fsmonitor_publish(state, batch, &cookie_list);
        string_list_clear(&cookie_list, 0);
        strbuf_release(&tmp);
        return;
 
 force_shutdown:
+       free(resolved);
        fsmonitor_batch__free_list(batch);
        string_list_clear(&cookie_list, 0);
 
+       pthread_mutex_lock(&data->dq_lock);
        data->shutdown_style = FORCE_SHUTDOWN;
-       CFRunLoopStop(data->rl);
+       pthread_cond_broadcast(&data->dq_finished);
+       pthread_mutex_unlock(&data->dq_lock);
+
        strbuf_release(&tmp);
        return;
 }
@@ -431,10 +451,6 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
        if (!data->stream)
                goto failed;
 
-       /*
-        * `data->rl` needs to be set inside the listener thread.
-        */
-
        return 0;
 
 failed:
@@ -461,6 +477,11 @@ void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
                FSEventStreamRelease(data->stream);
        }
 
+       if (data->dq)
+               dispatch_release(data->dq);
+       pthread_cond_destroy(&data->dq_finished);
+       pthread_mutex_destroy(&data->dq_lock);
+
        FREE_AND_NULL(state->listen_data);
 }
 
@@ -469,9 +490,11 @@ void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
        struct fsm_listen_data *data;
 
        data = state->listen_data;
-       data->shutdown_style = SHUTDOWN_EVENT;
 
-       CFRunLoopStop(data->rl);
+       pthread_mutex_lock(&data->dq_lock);
+       data->shutdown_style = SHUTDOWN_EVENT;
+       pthread_cond_broadcast(&data->dq_finished);
+       pthread_mutex_unlock(&data->dq_lock);
 }
 
 void fsm_listen__loop(struct fsmonitor_daemon_state *state)
@@ -480,9 +503,11 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
 
        data = state->listen_data;
 
-       data->rl = CFRunLoopGetCurrent();
+       pthread_mutex_init(&data->dq_lock, NULL);
+       pthread_cond_init(&data->dq_finished, NULL);
+       data->dq = dispatch_queue_create("FSMonitor", NULL);
 
-       FSEventStreamScheduleWithRunLoop(data->stream, data->rl, kCFRunLoopDefaultMode);
+       FSEventStreamSetDispatchQueue(data->stream, data->dq);
        data->stream_scheduled = 1;
 
        if (!FSEventStreamStart(data->stream)) {
@@ -491,7 +516,9 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
        }
        data->stream_started = 1;
 
-       CFRunLoopRun();
+       pthread_mutex_lock(&data->dq_lock);
+       pthread_cond_wait(&data->dq_finished, &data->dq_lock);
+       pthread_mutex_unlock(&data->dq_lock);
 
        switch (data->shutdown_style) {
        case FORCE_ERROR_STOP:
index 03df8d951b871f80da8b66dda8582df2eb4b9d91..5a21dade7b8659aa4d6e9e3c697a7e124ef31ccb 100644 (file)
@@ -1,8 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsm-listen.h"
 #include "fsmonitor--daemon.h"
+#include "gettext.h"
+#include "simple-ipc.h"
+#include "trace2.h"
 
 /*
  * The documentation of ReadDirectoryChangesW() states that the maximum
@@ -287,8 +290,7 @@ void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
        SetEvent(state->listen_data->hListener[LISTENER_SHUTDOWN]);
 }
 
-static struct one_watch *create_watch(struct fsmonitor_daemon_state *state,
-                                     const char *path)
+static struct one_watch *create_watch(const char *path)
 {
        struct one_watch *watch = NULL;
        DWORD desired_access = FILE_LIST_DIRECTORY;
@@ -359,8 +361,7 @@ static void destroy_watch(struct one_watch *watch)
        free(watch);
 }
 
-static int start_rdcw_watch(struct fsm_listen_data *data,
-                           struct one_watch *watch)
+static int start_rdcw_watch(struct one_watch *watch)
 {
        DWORD dwNotifyFilter =
                FILE_NOTIFY_CHANGE_FILE_NAME |
@@ -733,11 +734,11 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
 
        state->listen_error_code = 0;
 
-       if (start_rdcw_watch(data, data->watch_worktree) == -1)
+       if (start_rdcw_watch(data->watch_worktree) == -1)
                goto force_error_stop;
 
        if (data->watch_gitdir &&
-           start_rdcw_watch(data, data->watch_gitdir) == -1)
+           start_rdcw_watch(data->watch_gitdir) == -1)
                goto force_error_stop;
 
        for (;;) {
@@ -753,7 +754,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
                        }
                        if (result == -2) {
                                /* retryable error */
-                               if (start_rdcw_watch(data, data->watch_worktree) == -1)
+                               if (start_rdcw_watch(data->watch_worktree) == -1)
                                        goto force_error_stop;
                                continue;
                        }
@@ -761,7 +762,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
                        /* have data */
                        if (process_worktree_events(state) == LISTENER_SHUTDOWN)
                                goto force_shutdown;
-                       if (start_rdcw_watch(data, data->watch_worktree) == -1)
+                       if (start_rdcw_watch(data->watch_worktree) == -1)
                                goto force_error_stop;
                        continue;
                }
@@ -774,7 +775,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
                        }
                        if (result == -2) {
                                /* retryable error */
-                               if (start_rdcw_watch(data, data->watch_gitdir) == -1)
+                               if (start_rdcw_watch(data->watch_gitdir) == -1)
                                        goto force_error_stop;
                                continue;
                        }
@@ -782,7 +783,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
                        /* have data */
                        if (process_gitdir_events(state) == LISTENER_SHUTDOWN)
                                goto force_shutdown;
-                       if (start_rdcw_watch(data, data->watch_gitdir) == -1)
+                       if (start_rdcw_watch(data->watch_gitdir) == -1)
                                goto force_error_stop;
                        continue;
                }
@@ -819,16 +820,14 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
 
        data->hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
 
-       data->watch_worktree = create_watch(state,
-                                           state->path_worktree_watch.buf);
+       data->watch_worktree = create_watch(state->path_worktree_watch.buf);
        if (!data->watch_worktree)
                goto failed;
 
        check_for_shortnames(data->watch_worktree);
 
        if (state->nr_paths_watching > 1) {
-               data->watch_gitdir = create_watch(state,
-                                                 state->path_gitdir_watch.buf);
+               data->watch_gitdir = create_watch(state->path_gitdir_watch.buf);
                if (!data->watch_gitdir)
                        goto failed;
        }
diff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c
new file mode 100644 (file)
index 0000000..049f97e
--- /dev/null
@@ -0,0 +1,138 @@
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
+#include "fsmonitor-path-utils.h"
+#include "gettext.h"
+#include "trace.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
+{
+       struct statfs fs;
+       if (statfs(path, &fs) == -1) {
+               int saved_errno = errno;
+               trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
+                                path, strerror(saved_errno));
+               errno = saved_errno;
+               return -1;
+       }
+
+       trace_printf_key(&trace_fsmonitor,
+                        "statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
+                        path, fs.f_type, fs.f_flags, fs.f_fstypename);
+
+       if (!(fs.f_flags & MNT_LOCAL))
+               fs_info->is_remote = 1;
+       else
+               fs_info->is_remote = 0;
+
+       fs_info->typename = xstrdup(fs.f_fstypename);
+
+       trace_printf_key(&trace_fsmonitor,
+                               "'%s' is_remote: %d",
+                               path, fs_info->is_remote);
+       return 0;
+}
+
+int fsmonitor__is_fs_remote(const char *path)
+{
+       struct fs_info fs;
+       if (fsmonitor__get_fs_info(path, &fs))
+               return -1;
+
+       free(fs.typename);
+
+       return fs.is_remote;
+}
+
+/*
+ * Scan the root directory for synthetic firmlinks that when resolved
+ * are a prefix of the path, stopping at the first one found.
+ *
+ * Some information about firmlinks and synthetic firmlinks:
+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/
+ *
+ * macOS no longer allows symlinks in the root directory; any link found
+ * there is therefore a synthetic firmlink.
+ *
+ * If this function gets called often, will want to cache all the firmlink
+ * information, but for now there is only one caller of this function.
+ *
+ * If there is more than one alias for the path, that is another
+ * matter altogether.
+ */
+int fsmonitor__get_alias(const char *path, struct alias_info *info)
+{
+       DIR *dir;
+       int retval = -1;
+       const char *const root = "/";
+       struct stat st;
+       struct dirent *de;
+       struct strbuf alias;
+       struct strbuf points_to = STRBUF_INIT;
+
+       dir = opendir(root);
+       if (!dir)
+               return error_errno(_("opendir('%s') failed"), root);
+
+       strbuf_init(&alias, 256);
+
+       while ((de = readdir(dir)) != NULL) {
+               strbuf_reset(&alias);
+               strbuf_addf(&alias, "%s%s", root, de->d_name);
+
+               if (lstat(alias.buf, &st) < 0) {
+                       error_errno(_("lstat('%s') failed"), alias.buf);
+                       goto done;
+               }
+
+               if (!S_ISLNK(st.st_mode))
+                       continue;
+
+               if (strbuf_readlink(&points_to, alias.buf, st.st_size) < 0) {
+                       error_errno(_("strbuf_readlink('%s') failed"), alias.buf);
+                       goto done;
+               }
+
+               if (!strncmp(points_to.buf, path, points_to.len) &&
+                       (path[points_to.len] == '/')) {
+                       strbuf_addbuf(&info->alias, &alias);
+                       strbuf_addbuf(&info->points_to, &points_to);
+                       trace_printf_key(&trace_fsmonitor,
+                               "Found alias for '%s' : '%s' -> '%s'",
+                               path, info->alias.buf, info->points_to.buf);
+                       retval = 0;
+                       goto done;
+               }
+       }
+       retval = 0; /* no alias */
+
+done:
+       strbuf_release(&alias);
+       strbuf_release(&points_to);
+       if (closedir(dir) < 0)
+               return error_errno(_("closedir('%s') failed"), root);
+       return retval;
+}
+
+char *fsmonitor__resolve_alias(const char *path,
+       const struct alias_info *info)
+{
+       if (!info->alias.len)
+               return NULL;
+
+       if ((!strncmp(info->alias.buf, path, info->alias.len))
+               && path[info->alias.len] == '/') {
+               struct strbuf tmp = STRBUF_INIT;
+               const char *remainder = path + info->alias.len;
+
+               strbuf_addbuf(&tmp, &info->points_to);
+               strbuf_add(&tmp, remainder, strlen(remainder));
+               return strbuf_detach(&tmp, NULL);
+       }
+
+       return NULL;
+}
diff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c
new file mode 100644 (file)
index 0000000..f4f9cc1
--- /dev/null
@@ -0,0 +1,148 @@
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
+#include "fsmonitor-path-utils.h"
+#include "gettext.h"
+#include "trace.h"
+
+/*
+ * Check remote working directory protocol.
+ *
+ * Return -1 if client machine cannot get remote protocol information.
+ */
+static int check_remote_protocol(wchar_t *wpath)
+{
+       HANDLE h;
+       FILE_REMOTE_PROTOCOL_INFO proto_info;
+
+       h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+                       FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+       if (h == INVALID_HANDLE_VALUE) {
+               error(_("[GLE %ld] unable to open for read '%ls'"),
+                     GetLastError(), wpath);
+               return -1;
+       }
+
+       if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,
+               &proto_info, sizeof(proto_info))) {
+               error(_("[GLE %ld] unable to get protocol information for '%ls'"),
+                     GetLastError(), wpath);
+               CloseHandle(h);
+               return -1;
+       }
+
+       CloseHandle(h);
+
+       trace_printf_key(&trace_fsmonitor,
+                               "check_remote_protocol('%ls') remote protocol %#8.8lx",
+                               wpath, proto_info.Protocol);
+
+       return 0;
+}
+
+/*
+ * Notes for testing:
+ *
+ * (a) Windows allows a network share to be mapped to a drive letter.
+ *     (This is the normal method to access it.)
+ *
+ *     $ NET USE Z: \\server\share
+ *     $ git -C Z:/repo status
+ *
+ * (b) Windows allows a network share to be referenced WITHOUT mapping
+ *     it to drive letter.
+ *
+ *     $ NET USE \\server\share\dir
+ *     $ git -C //server/share/repo status
+ *
+ * (c) Windows allows "SUBST" to create a fake drive mapping to an
+ *     arbitrary path (which may be remote)
+ *
+ *     $ SUBST Q: Z:\repo
+ *     $ git -C Q:/ status
+ *
+ * (d) Windows allows a directory symlink to be created on a local
+ *     file system that points to a remote repo.
+ *
+ *     $ mklink /d ./link //server/share/repo
+ *     $ git -C ./link status
+ */
+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
+{
+       wchar_t wpath[MAX_PATH];
+       wchar_t wfullpath[MAX_PATH];
+       size_t wlen;
+       UINT driveType;
+
+       /*
+        * Do everything in wide chars because the drive letter might be
+        * a multi-byte sequence.  See win32_has_dos_drive_prefix().
+        */
+       if (xutftowcs_path(wpath, path) < 0) {
+               return -1;
+       }
+
+       /*
+        * GetDriveTypeW() requires a final slash.  We assume that the
+        * worktree pathname points to an actual directory.
+        */
+       wlen = wcslen(wpath);
+       if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
+               wpath[wlen++] = L'\\';
+               wpath[wlen] = 0;
+       }
+
+       /*
+        * Normalize the path.  If nothing else, this converts forward
+        * slashes to backslashes.  This is essential to get GetDriveTypeW()
+        * correctly handle some UNC "\\server\share\..." paths.
+        */
+       if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {
+               return -1;
+       }
+
+       driveType = GetDriveTypeW(wfullpath);
+       trace_printf_key(&trace_fsmonitor,
+                        "DriveType '%s' L'%ls' (%u)",
+                        path, wfullpath, driveType);
+
+       if (driveType == DRIVE_REMOTE) {
+               fs_info->is_remote = 1;
+               if (check_remote_protocol(wfullpath) < 0)
+                       return -1;
+       } else {
+               fs_info->is_remote = 0;
+       }
+
+       trace_printf_key(&trace_fsmonitor,
+                               "'%s' is_remote: %d",
+                               path, fs_info->is_remote);
+
+       return 0;
+}
+
+int fsmonitor__is_fs_remote(const char *path)
+{
+       struct fs_info fs;
+       if (fsmonitor__get_fs_info(path, &fs))
+               return -1;
+       return fs.is_remote;
+}
+
+/*
+ * No-op for now.
+ */
+int fsmonitor__get_alias(const char *path UNUSED,
+                        struct alias_info *info UNUSED)
+{
+       return 0;
+}
+
+/*
+ * No-op for now.
+ */
+char *fsmonitor__resolve_alias(const char *path UNUSED,
+                              const struct alias_info *info UNUSED)
+{
+       return NULL;
+}
index efc732c0f317fc63c75d5878c82695748bcc1aa4..a38259063517144690122454970f399ae504708e 100644 (file)
@@ -1,32 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "repository.h"
+#include "fsmonitor-ll.h"
+#include "fsmonitor-ipc.h"
 #include "fsmonitor-settings.h"
-#include "fsmonitor.h"
-#include <sys/param.h>
-#include <sys/mount.h>
+#include "fsmonitor-path-utils.h"
 
-/*
- * [1] Remote working directories are problematic for FSMonitor.
- *
- * The underlying file system on the server machine and/or the remote
- * mount type (NFS, SAMBA, etc.) dictates whether notification events
- * are available at all to remote client machines.
- *
- * Kernel differences between the server and client machines also
- * dictate the how (buffering, frequency, de-dup) the events are
- * delivered to client machine processes.
- *
- * A client machine (such as a laptop) may choose to suspend/resume
- * and it is unclear (without lots of testing) whether the watcher can
- * resync after a resume.  We might be able to treat this as a normal
- * "events were dropped by the kernel" event and do our normal "flush
- * and resync" --or-- we might need to close the existing (zombie?)
- * notification fd and create a new one.
- *
- * In theory, the above issues need to be addressed whether we are
- * using the Hook or IPC API.
- *
+ /*
  * For the builtin FSMonitor, we create the Unix domain socket for the
  * IPC in the .git directory.  If the working directory is remote,
  * then the socket will be created on the remote file system.  This
  * be taken to ensure that $HOME is actually local and not a managed
  * file share.)
  *
- * So (for now at least), mark remote working directories as
- * incompatible.
- *
- *
- * [2] FAT32 and NTFS working directories are problematic too.
+ * FAT32 and NTFS working directories are problematic too.
  *
  * The builtin FSMonitor uses a Unix domain socket in the .git
  * directory for IPC.  These Windows drive formats do not support
  * Unix domain sockets, so mark them as incompatible for the daemon.
  *
  */
-static enum fsmonitor_reason check_volume(struct repository *r)
+static enum fsmonitor_reason check_uds_volume(struct repository *r)
 {
-       struct statfs fs;
+       struct fs_info fs;
+       const char *ipc_path = fsmonitor_ipc__get_path(r);
+       struct strbuf path = STRBUF_INIT;
+       strbuf_add(&path, ipc_path, strlen(ipc_path));
 
-       if (statfs(r->worktree, &fs) == -1) {
-               int saved_errno = errno;
-               trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
-                                r->worktree, strerror(saved_errno));
-               errno = saved_errno;
+       if (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {
+               strbuf_release(&path);
                return FSMONITOR_REASON_ERROR;
        }
 
-       trace_printf_key(&trace_fsmonitor,
-                        "statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
-                        r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);
-
-       if (!(fs.f_flags & MNT_LOCAL))
-               return FSMONITOR_REASON_REMOTE;
-
-       if (!strcmp(fs.f_fstypename, "msdos")) /* aka FAT32 */
-               return FSMONITOR_REASON_NOSOCKETS;
+       strbuf_release(&path);
 
-       if (!strcmp(fs.f_fstypename, "ntfs"))
+       if (fs.is_remote ||
+               !strcmp(fs.typename, "msdos") ||
+               !strcmp(fs.typename, "ntfs")) {
+               free(fs.typename);
                return FSMONITOR_REASON_NOSOCKETS;
+       }
 
+       free(fs.typename);
        return FSMONITOR_REASON_OK;
 }
 
-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)
 {
        enum fsmonitor_reason reason;
 
-       reason = check_volume(r);
-       if (reason != FSMONITOR_REASON_OK)
-               return reason;
+       if (ipc) {
+               reason = check_uds_volume(r);
+               if (reason != FSMONITOR_REASON_OK)
+                       return reason;
+       }
 
        return FSMONITOR_REASON_OK;
 }
index e5ec5b0a9f73bcf04085b5d62d7b123690ce937e..0f2aa321f6e15f729aef67d4012fea6513cacfa4 100644 (file)
@@ -1,8 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "repository.h"
+#include "fsmonitor-ll.h"
 #include "fsmonitor-settings.h"
-#include "fsmonitor.h"
+#include "fsmonitor-path-utils.h"
 
 /*
  * VFS for Git is incompatible with FSMonitor.
@@ -24,172 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)
        return FSMONITOR_REASON_OK;
 }
 
-/*
- * Check if monitoring remote working directories is allowed.
- *
- * By default, monitoring remote working directories is
- * disabled.  Users may override this behavior in enviroments where
- * they have proper support.
- */
-static int check_config_allowremote(struct repository *r)
-{
-       int allow;
-
-       if (!repo_config_get_bool(r, "fsmonitor.allowremote", &allow))
-               return allow;
-
-       return -1; /* fsmonitor.allowremote not set */
-}
-
-/*
- * Check remote working directory protocol.
- *
- * Error if client machine cannot get remote protocol information.
- */
-static int check_remote_protocol(wchar_t *wpath)
-{
-       HANDLE h;
-       FILE_REMOTE_PROTOCOL_INFO proto_info;
-
-       h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
-                       FILE_FLAG_BACKUP_SEMANTICS, NULL);
-
-       if (h == INVALID_HANDLE_VALUE) {
-               error(_("[GLE %ld] unable to open for read '%ls'"),
-                     GetLastError(), wpath);
-               return -1;
-       }
-
-       if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,
-               &proto_info, sizeof(proto_info))) {
-               error(_("[GLE %ld] unable to get protocol information for '%ls'"),
-                     GetLastError(), wpath);
-               CloseHandle(h);
-               return -1;
-       }
-
-       CloseHandle(h);
-
-       trace_printf_key(&trace_fsmonitor,
-                               "check_remote_protocol('%ls') remote protocol %#8.8lx",
-                               wpath, proto_info.Protocol);
-
-       return 0;
-}
-
-/*
- * Remote working directories are problematic for FSMonitor.
- *
- * The underlying file system on the server machine and/or the remote
- * mount type dictates whether notification events are available at
- * all to remote client machines.
- *
- * Kernel differences between the server and client machines also
- * dictate the how (buffering, frequency, de-dup) the events are
- * delivered to client machine processes.
- *
- * A client machine (such as a laptop) may choose to suspend/resume
- * and it is unclear (without lots of testing) whether the watcher can
- * resync after a resume.  We might be able to treat this as a normal
- * "events were dropped by the kernel" event and do our normal "flush
- * and resync" --or-- we might need to close the existing (zombie?)
- * notification fd and create a new one.
- *
- * In theory, the above issues need to be addressed whether we are
- * using the Hook or IPC API.
- *
- * So (for now at least), mark remote working directories as
- * incompatible.
- *
- * Notes for testing:
- *
- * (a) Windows allows a network share to be mapped to a drive letter.
- *     (This is the normal method to access it.)
- *
- *     $ NET USE Z: \\server\share
- *     $ git -C Z:/repo status
- *
- * (b) Windows allows a network share to be referenced WITHOUT mapping
- *     it to drive letter.
- *
- *     $ NET USE \\server\share\dir
- *     $ git -C //server/share/repo status
- *
- * (c) Windows allows "SUBST" to create a fake drive mapping to an
- *     arbitrary path (which may be remote)
- *
- *     $ SUBST Q: Z:\repo
- *     $ git -C Q:/ status
- *
- * (d) Windows allows a directory symlink to be created on a local
- *     file system that points to a remote repo.
- *
- *     $ mklink /d ./link //server/share/repo
- *     $ git -C ./link status
- */
-static enum fsmonitor_reason check_remote(struct repository *r)
-{
-       int ret;
-       wchar_t wpath[MAX_PATH];
-       wchar_t wfullpath[MAX_PATH];
-       size_t wlen;
-       UINT driveType;
-
-       /*
-        * Do everything in wide chars because the drive letter might be
-        * a multi-byte sequence.  See win32_has_dos_drive_prefix().
-        */
-       if (xutftowcs_path(wpath, r->worktree) < 0)
-               return FSMONITOR_REASON_ERROR;
-
-       /*
-        * GetDriveTypeW() requires a final slash.  We assume that the
-        * worktree pathname points to an actual directory.
-        */
-       wlen = wcslen(wpath);
-       if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
-               wpath[wlen++] = L'\\';
-               wpath[wlen] = 0;
-       }
-
-       /*
-        * Normalize the path.  If nothing else, this converts forward
-        * slashes to backslashes.  This is essential to get GetDriveTypeW()
-        * correctly handle some UNC "\\server\share\..." paths.
-        */
-       if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))
-               return FSMONITOR_REASON_ERROR;
-
-       driveType = GetDriveTypeW(wfullpath);
-       trace_printf_key(&trace_fsmonitor,
-                        "DriveType '%s' L'%ls' (%u)",
-                        r->worktree, wfullpath, driveType);
-
-       if (driveType == DRIVE_REMOTE) {
-               trace_printf_key(&trace_fsmonitor,
-                                "check_remote('%s') true",
-                                r->worktree);
-
-               ret = check_remote_protocol(wfullpath);
-               if (ret < 0)
-                       return FSMONITOR_REASON_ERROR;
-
-               switch (check_config_allowremote(r)) {
-               case 0: /* config overrides and disables */
-                       return FSMONITOR_REASON_REMOTE;
-               case 1: /* config overrides and enables */
-                       return FSMONITOR_REASON_OK;
-               default:
-                       break; /* config has no opinion */
-               }
-
-               return FSMONITOR_REASON_REMOTE;
-       }
-
-       return FSMONITOR_REASON_OK;
-}
-
-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc UNUSED)
 {
        enum fsmonitor_reason reason;
 
@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
        if (reason != FSMONITOR_REASON_OK)
                return reason;
 
-       reason = check_remote(r);
-       if (reason != FSMONITOR_REASON_OK)
-               return reason;
-
        return FSMONITOR_REASON_OK;
 }
index bc2f9382a17f8f63a783680770a46358964e8f81..4bb2d66227bd5e0197855416957c1499de8c5565 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 
 #include "strbuf.h"
 #include "strvec.h"
index 901375d58415a3ae21f03a15e7b78da6f8b08efa..238a84ddbaa1b4c7563f6950eca469949d77fa68 100644 (file)
@@ -6,10 +6,16 @@
 #include <wchar.h>
 #include "../strbuf.h"
 #include "../run-command.h"
-#include "../cache.h"
+#include "../abspath.h"
+#include "../alloc.h"
 #include "win32/lazyload.h"
 #include "../config.h"
+#include "../environment.h"
+#include "../trace2.h"
+#include "../symlinks.h"
+#include "../wrapper.h"
 #include "dir.h"
+#include "gettext.h"
 #define SECURITY_WIN32
 #include <sspi.h>
 
@@ -196,16 +202,19 @@ static int read_yes_no_answer(void)
 static int ask_yes_no_if_possible(const char *format, ...)
 {
        char question[4096];
-       const char *retry_hook[] = { NULL, NULL, NULL };
+       const char *retry_hook;
        va_list args;
 
        va_start(args, format);
        vsnprintf(question, sizeof(question), format, args);
        va_end(args);
 
-       if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
-               retry_hook[1] = question;
-               return !run_command_v_opt(retry_hook, 0);
+       retry_hook = mingw_getenv("GIT_ASK_YESNO");
+       if (retry_hook) {
+               struct child_process cmd = CHILD_PROCESS_INIT;
+
+               strvec_pushl(&cmd.args, retry_hook, question, NULL);
+               return !run_command(&cmd);
        }
 
        if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))
@@ -234,7 +243,8 @@ static int core_restrict_inherited_handles = -1;
 static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 static char *unset_environment_variables;
 
-int mingw_core_config(const char *var, const char *value, void *cb)
+int mingw_core_config(const char *var, const char *value,
+                     const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "core.hidedotfiles")) {
                if (value && !strcasecmp(value, "dotgitonly"))
@@ -245,6 +255,8 @@ int mingw_core_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, "core.unsetenvvars")) {
+               if (!value)
+                       return config_error_nonbool(var);
                free(unset_environment_variables);
                unset_environment_variables = xstrdup(value);
                return 0;
@@ -1337,6 +1349,11 @@ static char *path_lookup(const char *cmd, int exe_only)
        return prog;
 }
 
+char *mingw_locate_in_PATH(const char *cmd)
+{
+       return path_lookup(cmd, 0);
+}
+
 static const wchar_t *wcschrnul(const wchar_t *s, wchar_t c)
 {
        while (*s && *s != c)
@@ -1393,8 +1410,7 @@ static wchar_t *make_environment_block(char **deltaenv)
                        p += s;
                }
 
-               ALLOC_ARRAY(result, size);
-               COPY_ARRAY(result, wenv, size);
+               DUP_ARRAY(result, wenv, size);
                FreeEnvironmentStringsW(wenv);
                return result;
        }
@@ -1836,16 +1852,13 @@ static int try_shell_exec(const char *cmd, char *const *argv)
        if (prog) {
                int exec_id;
                int argc = 0;
-#ifndef _MSC_VER
-               const
-#endif
                char **argv2;
                while (argv[argc]) argc++;
                ALLOC_ARRAY(argv2, argc + 1);
                argv2[0] = (char *)cmd; /* full path to the script file */
                COPY_ARRAY(&argv2[1], &argv[1], argc);
-               exec_id = trace2_exec(prog, argv2);
-               pid = mingw_spawnv(prog, argv2, 1);
+               exec_id = trace2_exec(prog, (const char **)argv2);
+               pid = mingw_spawnv(prog, (const char **)argv2, 1);
                if (pid >= 0) {
                        int status;
                        if (waitpid(pid, &status, 0) < 0)
@@ -2671,6 +2684,30 @@ static PSID get_current_user_sid(void)
        return result;
 }
 
+static BOOL user_sid_to_user_name(PSID sid, LPSTR *str)
+{
+       SID_NAME_USE pe_use;
+       DWORD len_user = 0, len_domain = 0;
+       BOOL translate_sid_to_user;
+
+       /*
+        * returns only FALSE, because the string pointers are NULL
+        */
+       LookupAccountSidA(NULL, sid, NULL, &len_user, NULL, &len_domain,
+                         &pe_use);
+       /*
+        * Alloc needed space of the strings
+        */
+       ALLOC_ARRAY((*str), (size_t)len_domain + (size_t)len_user);
+       translate_sid_to_user = LookupAccountSidA(NULL, sid,
+           (*str) + len_domain, &len_user, *str, &len_domain, &pe_use);
+       if (!translate_sid_to_user)
+               FREE_AND_NULL(*str);
+       else
+               (*str)[len_domain] = '/';
+       return translate_sid_to_user;
+}
+
 static int acls_supported(const char *path)
 {
        size_t offset = offset_1st_component(path);
@@ -2749,30 +2786,50 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
                        /*
                         * On FAT32 volumes, ownership is not actually recorded.
                         */
-                       strbuf_addf(report, "'%s' is on a file system that does"
+                       strbuf_addf(report, "'%s' is on a file system that does "
                                    "not record ownership\n", path);
                } else if (report) {
-                       LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;
+                       LPSTR str1, str2, str3, str4, to_free1 = NULL,
+                           to_free3 = NULL, to_local_free2 = NULL,
+                           to_local_free4 = NULL;
 
-                       if (ConvertSidToStringSidA(sid, &str1))
+                       if (user_sid_to_user_name(sid, &str1))
                                to_free1 = str1;
                        else
                                str1 = "(inconvertible)";
-
-                       if (!current_user_sid)
-                               str2 = "(none)";
-                       else if (!IsValidSid(current_user_sid))
-                               str2 = "(invalid)";
-                       else if (ConvertSidToStringSidA(current_user_sid, &str2))
-                               to_free2 = str2;
+                       if (ConvertSidToStringSidA(sid, &str2))
+                               to_local_free2 = str2;
                        else
                                str2 = "(inconvertible)";
+
+                       if (!current_user_sid) {
+                               str3 = "(none)";
+                               str4 = "(none)";
+                       }
+                       else if (!IsValidSid(current_user_sid)) {
+                               str3 = "(invalid)";
+                               str4 = "(invalid)";
+                       } else {
+                               if (user_sid_to_user_name(current_user_sid,
+                                                         &str3))
+                                       to_free3 = str3;
+                               else
+                                       str3 = "(inconvertible)";
+                               if (ConvertSidToStringSidA(current_user_sid,
+                                                          &str4))
+                                       to_local_free4 = str4;
+                               else
+                                       str4 = "(inconvertible)";
+                       }
                        strbuf_addf(report,
                                    "'%s' is owned by:\n"
-                                   "\t'%s'\nbut the current user is:\n"
-                                   "\t'%s'\n", path, str1, str2);
-                       LocalFree(to_free1);
-                       LocalFree(to_free2);
+                                   "\t%s (%s)\nbut the current user is:\n"
+                                   "\t%s (%s)\n",
+                                   path, str1, str2, str3, str4);
+                       free(to_free1);
+                       LocalFree(to_local_free2);
+                       free(to_free3);
+                       LocalFree(to_local_free4);
                }
        }
 
index 209cf7cebadd1746c8b79e1105bac103b8a72910..6aec50e4124e145d6d43f584418288d3fc29f481 100644 (file)
@@ -11,7 +11,9 @@ typedef _sigset_t sigset_t;
 #undef _POSIX_THREAD_SAFE_FUNCTIONS
 #endif
 
-int mingw_core_config(const char *var, const char *value, void *cb);
+struct config_context;
+int mingw_core_config(const char *var, const char *value,
+                     const struct config_context *ctx, void *cb);
 #define platform_core_config mingw_core_config
 
 /*
@@ -175,6 +177,9 @@ 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)
index 9694ebdb1d7af1cd272883a2378d6c22771e34c9..5b51195c32f847ffb058f421fede64439a0d9712 100644 (file)
@@ -41,7 +41,7 @@ int enable_pipe_nonblock(int fd)
 
 #else
 
-int enable_pipe_nonblock(int fd)
+int enable_pipe_nonblock(int fd UNUSED)
 {
        errno = ENOSYS;
        return -1;
index 978cac4ec91e6bb2f81539d85422bb37e4941a51..484e6d4c716ef6b6c3e5b24ff5a1879db3039d5d 100644 (file)
@@ -1,4 +1,5 @@
 #include "../git-compat-util.h"
+#include "../wrapper.h"
 
 ssize_t git_pread(int fd, void *buf, size_t count, off_t offset)
 {
index cce1d57a46471c9465d6a55c176a063fdfef96eb..0bd5c24250a4fb717f61db6e03e40326ca1fad07 100644 (file)
@@ -5,8 +5,12 @@
 
 #define PRECOMPOSE_UNICODE_C
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "path.h"
+#include "strbuf.h"
 #include "utf8.h"
 #include "precompose_utf8.h"
 
diff --git a/compat/regcomp_enhanced.c b/compat/regcomp_enhanced.c
new file mode 100644 (file)
index 0000000..84193ce
--- /dev/null
@@ -0,0 +1,9 @@
+#include "../git-compat-util.h"
+#undef regcomp
+
+int git_regcomp(regex_t *preg, const char *pattern, int cflags)
+{
+       if (!(cflags & REG_EXTENDED))
+               cflags |= REG_ENHANCED;
+       return regcomp(preg, pattern, cflags);
+}
index 6adfcfd54051617fd4cbdd0e5d8a904c2a839152..a4a6f930d7b14a473d5e47a9a90b84a7557054af 100644 (file)
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hash-ll.h"
 
 int git_SHA1_Update_Chunked(platform_SHA_CTX *c, const void *data, size_t len)
 {
index 1b9d359ab68185036c40a40bb2a44b8e5fbfb913..cb176d966f287d98e44495db1797061c8c1102af 100644 (file)
@@ -1,8 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "simple-ipc.h"
-#include "strbuf.h"
-#include "pkt-line.h"
-#include "thread-utils.h"
 
 #ifndef SUPPORTS_SIMPLE_IPC
 /*
index 28a79289d4fd0a9d4e185cd5c9d8b19afdde70e7..9b3f2cdf8c9608a5aae4dc3ebfd8fa208c5617de 100644 (file)
@@ -1,8 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "simple-ipc.h"
 #include "strbuf.h"
-#include "pkt-line.h"
 #include "thread-utils.h"
+#include "trace2.h"
 #include "unix-socket.h"
 #include "unix-stream-server.h"
 
index 20ea7b65e0ba6311b22678fba5201e4a7ebeb8ce..8bfe51248e552e6915acec34abea31a3d3dc279e 100644 (file)
@@ -1,8 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "gettext.h"
 #include "simple-ipc.h"
 #include "strbuf.h"
 #include "pkt-line.h"
 #include "thread-utils.h"
+#include "trace.h"
+#include "trace2.h"
 #include "accctrl.h"
 #include "aclapi.h"
 
index ea490a7ced431a798629ca86ac904052d99f839a..0afda730f278cc776e430ca6b5f44712d5b27f7b 100644 (file)
@@ -1,5 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "compat/terminal.h"
+#include "gettext.h"
 #include "sigchain.h"
 #include "strbuf.h"
 #include "run-command.h"
@@ -478,10 +479,13 @@ struct escape_sequence_entry {
 };
 
 static int sequence_entry_cmp(const void *hashmap_cmp_fn_data UNUSED,
-                             const struct escape_sequence_entry *e1,
-                             const struct escape_sequence_entry *e2,
+                             const struct hashmap_entry *he1,
+                             const struct hashmap_entry *he2,
                              const void *keydata)
 {
+       const struct escape_sequence_entry
+               *e1 = container_of(he1, const struct escape_sequence_entry, entry),
+               *e2 = container_of(he2, const struct escape_sequence_entry, entry);
        return strcmp(e1->sequence, keydata ? keydata : e2->sequence);
 }
 
@@ -495,8 +499,7 @@ static int is_known_escape_sequence(const char *sequence)
                struct strbuf buf = STRBUF_INIT;
                char *p, *eol;
 
-               hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp,
-                            NULL, 0);
+               hashmap_init(&sequences, sequence_entry_cmp, NULL, 0);
 
                strvec_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
                if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0))
diff --git a/compat/win32/headless.c b/compat/win32/headless.c
new file mode 100644 (file)
index 0000000..8b00dfe
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * headless Git - run Git without opening a console window on Windows
+ */
+
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#define UNICODE
+#define _UNICODE
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+/*
+ * If `dir` contains the path to a Git exec directory, extend `PATH` to
+ * include the corresponding `bin/` directory (which is where all those
+ * `.dll` files needed by `git.exe` are, on Windows).
+ */
+static int extend_path(wchar_t *dir, size_t dir_len)
+{
+       const wchar_t *suffix = L"\\libexec\\git-core";
+       size_t suffix_len = wcslen(suffix);
+       wchar_t *env;
+       DWORD len;
+
+       if (dir_len < suffix_len)
+               return 0;
+
+       dir_len -= suffix_len;
+       if (memcmp(dir + dir_len, suffix, suffix_len * sizeof(wchar_t)))
+               return 0;
+
+       len = GetEnvironmentVariableW(L"PATH", NULL, 0);
+       if (!len)
+               return 0;
+
+       env = _alloca((dir_len + 5 + len) * sizeof(wchar_t));
+       wcsncpy(env, dir, dir_len);
+       wcscpy(env + dir_len, L"\\bin;");
+       if (!GetEnvironmentVariableW(L"PATH", env + dir_len + 5, len))
+               return 0;
+
+       SetEnvironmentVariableW(L"PATH", env);
+       return 1;
+}
+
+int WINAPI wWinMain(_In_ HINSTANCE instance,
+                   _In_opt_ HINSTANCE previous_instance,
+                   _In_ LPWSTR command_line, _In_ int show)
+{
+       wchar_t git_command_line[32768];
+       size_t size = sizeof(git_command_line) / sizeof(wchar_t);
+       const wchar_t *needs_quotes = L"";
+       int slash = 0, i;
+
+       STARTUPINFO startup_info = {
+               .cb = sizeof(STARTUPINFO),
+               .dwFlags = STARTF_USESHOWWINDOW,
+               .wShowWindow = SW_HIDE,
+       };
+       PROCESS_INFORMATION process_info = { 0 };
+       DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT |
+               CREATE_NEW_CONSOLE | CREATE_NO_WINDOW;
+       DWORD exit_code;
+
+       /* First, determine the full path of argv[0] */
+       for (i = 0; _wpgmptr[i]; i++)
+               if (_wpgmptr[i] == L' ')
+                       needs_quotes = L"\"";
+               else if (_wpgmptr[i] == L'\\')
+                       slash = i;
+
+       if (slash >= size - 11)
+               return 127; /* Too long path */
+
+       /* If it is in Git's exec path, add the bin/ directory to the PATH */
+       extend_path(_wpgmptr, slash);
+
+       /* Then, add the full path of `git.exe` as argv[0] */
+       i = swprintf_s(git_command_line, size, L"%ls%.*ls\\git.exe%ls",
+                      needs_quotes, slash, _wpgmptr, needs_quotes);
+       if (i < 0)
+               return 127; /* Too long path */
+
+       if (*command_line) {
+               /* Now, append the command-line arguments */
+               i = swprintf_s(git_command_line + i, size - i,
+                              L" %ls", command_line);
+               if (i < 0)
+                       return 127;
+       }
+
+       startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+       startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+       startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+       if (!CreateProcess(NULL, /* infer argv[0] from the command line */
+                          git_command_line, /* modified command line */
+                          NULL, /* inherit process handles? */
+                          NULL, /* inherit thread handles? */
+                          FALSE, /* handles inheritable? */
+                          creation_flags,
+                          NULL, /* use this process' environment */
+                          NULL, /* use this process' working directory */
+                          &startup_info, &process_info))
+               return 129; /* could not start */
+       WaitForSingleObject(process_info.hProcess, INFINITE);
+       if (!GetExitCodeProcess(process_info.hProcess, &exit_code))
+               exit_code = 130; /* Could not determine exit code? */
+
+       CloseHandle(process_info.hProcess);
+       CloseHandle(process_info.hThread);
+
+       return (int)exit_code;
+}
index 2e7eead42cb008980c7a3b4de63050897c2996da..85f8f7920ce48d418f4c77896004d17715582840 100644 (file)
@@ -22,12 +22,12 @@ static unsigned __stdcall win32_start_routine(void *arg)
 }
 
 int pthread_create(pthread_t *thread, const void *unused,
-                  void *(*start_routine)(void*), void *arg)
+                  void *(*start_routine)(void *), void *arg)
 {
        thread->arg = arg;
        thread->start_routine = start_routine;
-       thread->handle = (HANDLE)
-               _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
+       thread->handle = (HANDLE)_beginthreadex(NULL, 0, win32_start_routine,
+                                               thread, 0, NULL);
 
        if (!thread->handle)
                return errno;
@@ -39,14 +39,17 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
 {
        DWORD result = WaitForSingleObject(thread->handle, INFINITE);
        switch (result) {
-               case WAIT_OBJECT_0:
-                       if (value_ptr)
-                               *value_ptr = thread->arg;
-                       return 0;
-               case WAIT_ABANDONED:
-                       return EINVAL;
-               default:
-                       return err_win_to_posix(GetLastError());
+       case WAIT_OBJECT_0:
+               if (value_ptr)
+                       *value_ptr = thread->arg;
+               CloseHandle(thread->handle);
+               return 0;
+       case WAIT_ABANDONED:
+               CloseHandle(thread->handle);
+               return EINVAL;
+       default:
+               /* the wait failed, so do not detach */
+               return err_win_to_posix(GetLastError());
        }
 }
 
index 737983d00bae9190e6a328ccf9a2b1a4914fa109..cc3221cb2c8a84a454784c7fa0dc287dbad83983 100644 (file)
@@ -66,7 +66,7 @@ pthread_t pthread_self(void);
 
 static inline void NORETURN pthread_exit(void *ret)
 {
-       ExitThread((DWORD)(intptr_t)ret);
+       _endthreadex((unsigned)(uintptr_t)ret);
 }
 
 typedef DWORD pthread_key_t;
index a53fd924340f7d6da04e5197363fe831b9d19bf3..3ef0936f6ff9711caed8c22c8aebd92eb4be6e30 100644 (file)
@@ -1,8 +1,10 @@
-#include "../../cache.h"
+#include "../../git-compat-util.h"
 #include "../../json-writer.h"
+#include "../../repository.h"
+#include "../../trace2.h"
 #include "lazyload.h"
-#include <Psapi.h>
-#include <tlHelp32.h>
+#include <psapi.h>
+#include <tlhelp32.h>
 
 /*
  * An arbitrarily chosen value to limit the size of the ancestor
index 3abe8dd5a2711b5268cb5efe623d8c027f0d3d83..f83610f684d031f8660542b108a940207cad89df 100644 (file)
@@ -644,7 +644,7 @@ void winansi_init(void)
 
        /* start console spool thread on the pipe's read end */
        hthread = CreateThread(NULL, 0, console_thread, NULL, 0, NULL);
-       if (hthread == INVALID_HANDLE_VALUE)
+       if (!hthread)
                die_lasterr("CreateThread(console_thread) failed");
 
        /* schedule cleanup routine */
index 9eb9ed5fa4652cba9813dec9a89561da915b66f7..9ff6ae1cb903a0690295a7d5bb452ef5f35c12e1 100644 (file)
--- a/config.c
+++ b/config.c
@@ -5,24 +5,39 @@
  * Copyright (C) Johannes Schindelin, 2005
  *
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
 #include "date.h"
 #include "branch.h"
 #include "config.h"
+#include "parse.h"
+#include "convert.h"
 #include "environment.h"
+#include "gettext.h"
+#include "ident.h"
 #include "repository.h"
 #include "lockfile.h"
+#include "mailmap.h"
+#include "attr.h"
 #include "exec-cmd.h"
 #include "strbuf.h"
 #include "quote.h"
 #include "hashmap.h"
 #include "string-list.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
+#include "path.h"
 #include "utf8.h"
-#include "dir.h"
 #include "color.h"
 #include "refs.h"
-#include "worktree.h"
+#include "setup.h"
+#include "strvec.h"
+#include "trace2.h"
+#include "wildmatch.h"
+#include "ws.h"
+#include "write-or-die.h"
 
 struct config_source {
        struct config_source *prev;
@@ -49,34 +64,7 @@ struct config_source {
        int (*do_ungetc)(int c, struct config_source *conf);
        long (*do_ftell)(struct config_source *c);
 };
-
-/*
- * These variables record the "current" config source, which
- * can be accessed by parsing callbacks.
- *
- * The "cf" variable will be non-NULL only when we are actually parsing a real
- * config source (file, blob, cmdline, etc).
- *
- * The "current_config_kvi" variable will be non-NULL only when we are feeding
- * cached config from a configset into a callback.
- *
- * They should generally never be non-NULL at the same time. If they are both
- * NULL, then we aren't parsing anything (and depending on the function looking
- * at the variables, it's either a bug for it to be called in the first place,
- * or it's a function which can be reused for non-config purposes, and should
- * fall back to some sane behavior).
- */
-static struct config_source *cf;
-static struct key_value_info *current_config_kvi;
-
-/*
- * Similar to the variables above, this gives access to the "scope" of the
- * current value (repo, global, etc). For cached values, it can be found via
- * the current_config_kvi as above. During parsing, the current value can be
- * found in this variable. It's not part of "cf" because it transcends a single
- * file (i.e., a file included from .git/config is still in "repo" scope).
- */
-static enum config_scope current_parsing_scope;
+#define CONFIG_SOURCE_INIT { 0 }
 
 static int pack_compression_seen;
 static int zlib_compression_seen;
@@ -139,6 +127,7 @@ struct config_include_data {
        void *data;
        const struct config_options *opts;
        struct git_config_source *config_source;
+       struct repository *repo;
 
        /*
         * All remote URLs discovered when reading all config files.
@@ -147,7 +136,8 @@ struct config_include_data {
 };
 #define CONFIG_INCLUDE_INIT { 0 }
 
-static int git_config_include(const char *var, const char *value, void *data);
+static int git_config_include(const char *var, const char *value,
+                             const struct config_context *ctx, void *data);
 
 #define MAX_INCLUDE_DEPTH 10
 static const char include_depth_advice[] = N_(
@@ -156,7 +146,9 @@ static const char include_depth_advice[] = N_(
 "from\n"
 "      %s\n"
 "This might be due to circular includes.");
-static int handle_path_include(const char *path, struct config_include_data *inc)
+static int handle_path_include(const struct key_value_info *kvi,
+                              const char *path,
+                              struct config_include_data *inc)
 {
        int ret = 0;
        struct strbuf buf = STRBUF_INIT;
@@ -177,14 +169,14 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        if (!is_absolute_path(path)) {
                char *slash;
 
-               if (!cf || !cf->path) {
+               if (!kvi || !kvi->path) {
                        ret = error(_("relative config includes must come from files"));
                        goto cleanup;
                }
 
-               slash = find_last_dir_sep(cf->path);
+               slash = find_last_dir_sep(kvi->path);
                if (slash)
-                       strbuf_add(&buf, cf->path, slash - cf->path + 1);
+                       strbuf_add(&buf, kvi->path, slash - kvi->path + 1);
                strbuf_addstr(&buf, path);
                path = buf.buf;
        }
@@ -192,10 +184,11 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        if (!access_or_die(path, R_OK, 0)) {
                if (++inc->depth > MAX_INCLUDE_DEPTH)
                        die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path,
-                           !cf ? "<unknown>" :
-                           cf->name ? cf->name :
+                           !kvi ? "<unknown>" :
+                           kvi->filename ? kvi->filename :
                            "the command line");
-               ret = git_config_from_file(git_config_include, path, inc);
+               ret = git_config_from_file_with_options(git_config_include, path, inc,
+                                                       kvi->scope, NULL);
                inc->depth--;
        }
 cleanup:
@@ -210,7 +203,8 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
                strbuf_addstr(pat, "**");
 }
 
-static int prepare_include_condition_pattern(struct strbuf *pat)
+static int prepare_include_condition_pattern(const struct key_value_info *kvi,
+                                            struct strbuf *pat)
 {
        struct strbuf path = STRBUF_INIT;
        char *expanded;
@@ -226,11 +220,11 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
        if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
                const char *slash;
 
-               if (!cf || !cf->path)
+               if (!kvi || !kvi->path)
                        return error(_("relative config include "
                                       "conditionals must come from files"));
 
-               strbuf_realpath(&path, cf->path, 1);
+               strbuf_realpath(&path, kvi->path, 1);
                slash = find_last_dir_sep(path.buf);
                if (!slash)
                        BUG("how is this possible?");
@@ -245,7 +239,8 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
        return prefix;
 }
 
-static int include_by_gitdir(const struct config_options *opts,
+static int include_by_gitdir(const struct key_value_info *kvi,
+                            const struct config_options *opts,
                             const char *cond, size_t cond_len, int icase)
 {
        struct strbuf text = STRBUF_INIT;
@@ -261,7 +256,7 @@ static int include_by_gitdir(const struct config_options *opts,
 
        strbuf_realpath(&text, git_dir, 1);
        strbuf_add(&pattern, cond, cond_len);
-       prefix = prepare_include_condition_pattern(&pattern);
+       prefix = prepare_include_condition_pattern(kvi, &pattern);
 
 again:
        if (prefix < 0)
@@ -323,7 +318,8 @@ static int include_by_branch(const char *cond, size_t cond_len)
        return ret;
 }
 
-static int add_remote_url(const char *var, const char *value, void *data)
+static int add_remote_url(const char *var, const char *value,
+                         const struct config_context *ctx UNUSED, void *data)
 {
        struct string_list *remote_urls = data;
        const char *remote_name;
@@ -342,27 +338,17 @@ static void populate_remote_urls(struct config_include_data *inc)
 {
        struct config_options opts;
 
-       struct config_source *store_cf = cf;
-       struct key_value_info *store_kvi = current_config_kvi;
-       enum config_scope store_scope = current_parsing_scope;
-
        opts = *inc->opts;
        opts.unconditional_remote_url = 1;
 
-       cf = NULL;
-       current_config_kvi = NULL;
-       current_parsing_scope = 0;
-
        inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
        string_list_init_dup(inc->remote_urls);
-       config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
-
-       cf = store_cf;
-       current_config_kvi = store_kvi;
-       current_parsing_scope = store_scope;
+       config_with_options(add_remote_url, inc->remote_urls,
+                           inc->config_source, inc->repo, &opts);
 }
 
 static int forbid_remote_url(const char *var, const char *value UNUSED,
+                            const struct config_context *ctx UNUSED,
                             void *data UNUSED)
 {
        const char *remote_name;
@@ -406,15 +392,16 @@ static int include_by_remote_url(struct config_include_data *inc,
                                             inc->remote_urls);
 }
 
-static int include_condition_is_true(struct config_include_data *inc,
+static int include_condition_is_true(const struct key_value_info *kvi,
+                                    struct config_include_data *inc,
                                     const char *cond, size_t cond_len)
 {
        const struct config_options *opts = inc->opts;
 
        if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
-               return include_by_gitdir(opts, cond, cond_len, 0);
+               return include_by_gitdir(kvi, opts, cond, cond_len, 0);
        else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
-               return include_by_gitdir(opts, cond, cond_len, 1);
+               return include_by_gitdir(kvi, opts, cond, cond_len, 1);
        else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
                return include_by_branch(cond, cond_len);
        else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -425,7 +412,9 @@ static int include_condition_is_true(struct config_include_data *inc,
        return 0;
 }
 
-static int git_config_include(const char *var, const char *value, void *data)
+static int git_config_include(const char *var, const char *value,
+                             const struct config_context *ctx,
+                             void *data)
 {
        struct config_include_data *inc = data;
        const char *cond, *key;
@@ -436,21 +425,21 @@ static int git_config_include(const char *var, const char *value, void *data)
         * Pass along all values, including "include" directives; this makes it
         * possible to query information on the includes themselves.
         */
-       ret = inc->fn(var, value, inc->data);
+       ret = inc->fn(var, value, ctx, inc->data);
        if (ret < 0)
                return ret;
 
        if (!strcmp(var, "include.path"))
-               ret = handle_path_include(value, inc);
+               ret = handle_path_include(ctx->kvi, value, inc);
 
        if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
-           cond && include_condition_is_true(inc, cond, cond_len) &&
+           cond && include_condition_is_true(ctx->kvi, inc, cond, cond_len) &&
            !strcmp(key, "path")) {
                config_fn_t old_fn = inc->fn;
 
                if (inc->opts->unconditional_remote_url)
                        inc->fn = forbid_remote_url;
-               ret = handle_path_include(value, inc);
+               ret = handle_path_include(ctx->kvi, value, inc);
                inc->fn = old_fn;
        }
 
@@ -608,27 +597,45 @@ out_free_ret_1:
 }
 
 static int config_parse_pair(const char *key, const char *value,
-                         config_fn_t fn, void *data)
+                            struct key_value_info *kvi,
+                            config_fn_t fn, void *data)
 {
        char *canonical_name;
        int ret;
+       struct config_context ctx = {
+               .kvi = kvi,
+       };
 
        if (!strlen(key))
                return error(_("empty config key"));
        if (git_config_parse_key(key, &canonical_name, NULL))
                return -1;
 
-       ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
+       ret = (fn(canonical_name, value, &ctx, data) < 0) ? -1 : 0;
        free(canonical_name);
        return ret;
 }
 
+
+/* for values read from `git_config_from_parameters()` */
+void kvi_from_param(struct key_value_info *out)
+{
+       out->filename = NULL;
+       out->linenr = -1;
+       out->origin_type = CONFIG_ORIGIN_CMDLINE;
+       out->scope = CONFIG_SCOPE_COMMAND;
+       out->path = NULL;
+}
+
 int git_config_parse_parameter(const char *text,
                               config_fn_t fn, void *data)
 {
        const char *value;
        struct strbuf **pair;
        int ret;
+       struct key_value_info kvi = KVI_INIT;
+
+       kvi_from_param(&kvi);
 
        pair = strbuf_split_str(text, '=', 2);
        if (!pair[0])
@@ -647,12 +654,13 @@ int git_config_parse_parameter(const char *text,
                return error(_("bogus config parameter: %s"), text);
        }
 
-       ret = config_parse_pair(pair[0]->buf, value, fn, data);
+       ret = config_parse_pair(pair[0]->buf, value, &kvi, fn, data);
        strbuf_list_free(pair);
        return ret;
 }
 
-static int parse_config_env_list(char *env, config_fn_t fn, void *data)
+static int parse_config_env_list(char *env, struct key_value_info *kvi,
+                                config_fn_t fn, void *data)
 {
        char *cur = env;
        while (cur && *cur) {
@@ -686,7 +694,7 @@ static int parse_config_env_list(char *env, config_fn_t fn, void *data)
                                             CONFIG_DATA_ENVIRONMENT);
                        }
 
-                       if (config_parse_pair(key, value, fn, data) < 0)
+                       if (config_parse_pair(key, value, kvi, fn, data) < 0)
                                return -1;
                }
                else {
@@ -710,13 +718,9 @@ int git_config_from_parameters(config_fn_t fn, void *data)
        struct strvec to_free = STRVEC_INIT;
        int ret = 0;
        char *envw = NULL;
-       struct config_source source;
-
-       memset(&source, 0, sizeof(source));
-       source.prev = cf;
-       source.origin_type = CONFIG_ORIGIN_CMDLINE;
-       cf = &source;
+       struct key_value_info kvi = KVI_INIT;
 
+       kvi_from_param(&kvi);
        env = getenv(CONFIG_COUNT_ENVIRONMENT);
        if (env) {
                unsigned long count;
@@ -752,7 +756,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
                        }
                        strbuf_reset(&envvar);
 
-                       if (config_parse_pair(key, value, fn, data) < 0) {
+                       if (config_parse_pair(key, value, &kvi, fn, data) < 0) {
                                ret = -1;
                                goto out;
                        }
@@ -763,7 +767,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
        if (env) {
                /* sq_dequote will write over it */
                envw = xstrdup(env);
-               if (parse_config_env_list(envw, fn, data) < 0) {
+               if (parse_config_env_list(envw, &kvi, fn, data) < 0) {
                        ret = -1;
                        goto out;
                }
@@ -773,25 +777,24 @@ out:
        strbuf_release(&envvar);
        strvec_clear(&to_free);
        free(envw);
-       cf = source.prev;
        return ret;
 }
 
-static int get_next_char(void)
+static int get_next_char(struct config_source *cs)
 {
-       int c = cf->do_fgetc(cf);
+       int c = cs->do_fgetc(cs);
 
        if (c == '\r') {
                /* DOS like systems */
-               c = cf->do_fgetc(cf);
+               c = cs->do_fgetc(cs);
                if (c != '\n') {
                        if (c != EOF)
-                               cf->do_ungetc(c, cf);
+                               cs->do_ungetc(c, cs);
                        c = '\r';
                }
        }
 
-       if (c != EOF && ++cf->total_len > INT_MAX) {
+       if (c != EOF && ++cs->total_len > INT_MAX) {
                /*
                 * This is an absurdly long config file; refuse to parse
                 * further in order to protect downstream code from integer
@@ -799,38 +802,38 @@ static int get_next_char(void)
                 * but we can mark EOF and put trash in the return value,
                 * which will trigger a parse error.
                 */
-               cf->eof = 1;
+               cs->eof = 1;
                return 0;
        }
 
        if (c == '\n')
-               cf->linenr++;
+               cs->linenr++;
        if (c == EOF) {
-               cf->eof = 1;
-               cf->linenr++;
+               cs->eof = 1;
+               cs->linenr++;
                c = '\n';
        }
        return c;
 }
 
-static char *parse_value(void)
+static char *parse_value(struct config_source *cs)
 {
        int quote = 0, comment = 0, space = 0;
 
-       strbuf_reset(&cf->value);
+       strbuf_reset(&cs->value);
        for (;;) {
-               int c = get_next_char();
+               int c = get_next_char(cs);
                if (c == '\n') {
                        if (quote) {
-                               cf->linenr--;
+                               cs->linenr--;
                                return NULL;
                        }
-                       return cf->value.buf;
+                       return cs->value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
-                       if (cf->value.len)
+                       if (cs->value.len)
                                space++;
                        continue;
                }
@@ -841,9 +844,9 @@ static char *parse_value(void)
                        }
                }
                for (; space; space--)
-                       strbuf_addch(&cf->value, ' ');
+                       strbuf_addch(&cs->value, ' ');
                if (c == '\\') {
-                       c = get_next_char();
+                       c = get_next_char(cs);
                        switch (c) {
                        case '\n':
                                continue;
@@ -863,27 +866,31 @@ static char *parse_value(void)
                        default:
                                return NULL;
                        }
-                       strbuf_addch(&cf->value, c);
+                       strbuf_addch(&cs->value, c);
                        continue;
                }
                if (c == '"') {
                        quote = 1-quote;
                        continue;
                }
-               strbuf_addch(&cf->value, c);
+               strbuf_addch(&cs->value, c);
        }
 }
 
-static int get_value(config_fn_t fn, void *data, struct strbuf *name)
+static int get_value(struct config_source *cs, struct key_value_info *kvi,
+                    config_fn_t fn, void *data, struct strbuf *name)
 {
        int c;
        char *value;
        int ret;
+       struct config_context ctx = {
+               .kvi = kvi,
+       };
 
        /* Get the full name */
        for (;;) {
-               c = get_next_char();
-               if (cf->eof)
+               c = get_next_char(cs);
+               if (cs->eof)
                        break;
                if (!iskeychar(c))
                        break;
@@ -891,13 +898,13 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
        }
 
        while (c == ' ' || c == '\t')
-               c = get_next_char();
+               c = get_next_char(cs);
 
        value = NULL;
        if (c != '\n') {
                if (c != '=')
                        return -1;
-               value = parse_value();
+               value = parse_value(cs);
                if (!value)
                        return -1;
        }
@@ -906,20 +913,22 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
         * the line we just parsed during the call to fn to get
         * accurate line number in error messages.
         */
-       cf->linenr--;
-       ret = fn(name->buf, value, data);
+       cs->linenr--;
+       kvi->linenr = cs->linenr;
+       ret = fn(name->buf, value, &ctx, data);
        if (ret >= 0)
-               cf->linenr++;
+               cs->linenr++;
        return ret;
 }
 
-static int get_extended_base_var(struct strbuf *name, int c)
+static int get_extended_base_var(struct config_source *cs, struct strbuf *name,
+                                int c)
 {
-       cf->subsection_case_sensitive = 0;
+       cs->subsection_case_sensitive = 0;
        do {
                if (c == '\n')
                        goto error_incomplete_line;
-               c = get_next_char();
+               c = get_next_char(cs);
        } while (isspace(c));
 
        /* We require the format to be '[base "extension"]' */
@@ -928,13 +937,13 @@ static int get_extended_base_var(struct strbuf *name, int c)
        strbuf_addch(name, '.');
 
        for (;;) {
-               int c = get_next_char();
+               int c = get_next_char(cs);
                if (c == '\n')
                        goto error_incomplete_line;
                if (c == '"')
                        break;
                if (c == '\\') {
-                       c = get_next_char();
+                       c = get_next_char(cs);
                        if (c == '\n')
                                goto error_incomplete_line;
                }
@@ -942,25 +951,25 @@ static int get_extended_base_var(struct strbuf *name, int c)
        }
 
        /* Final ']' */
-       if (get_next_char() != ']')
+       if (get_next_char(cs) != ']')
                return -1;
        return 0;
 error_incomplete_line:
-       cf->linenr--;
+       cs->linenr--;
        return -1;
 }
 
-static int get_base_var(struct strbuf *name)
+static int get_base_var(struct config_source *cs, struct strbuf *name)
 {
-       cf->subsection_case_sensitive = 1;
+       cs->subsection_case_sensitive = 1;
        for (;;) {
-               int c = get_next_char();
-               if (cf->eof)
+               int c = get_next_char(cs);
+               if (cs->eof)
                        return -1;
                if (c == ']')
                        return 0;
                if (isspace(c))
-                       return get_extended_base_var(name, c);
+                       return get_extended_base_var(cs, name, c);
                if (!iskeychar(c) && c != '.')
                        return -1;
                strbuf_addch(name, tolower(c));
@@ -973,7 +982,8 @@ struct parse_event_data {
        const struct config_options *opts;
 };
 
-static int do_event(enum config_event_t type, struct parse_event_data *data)
+static int do_event(struct config_source *cs, enum config_event_t type,
+                   struct parse_event_data *data)
 {
        size_t offset;
 
@@ -984,7 +994,7 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
            data->previous_type == type)
                return 0;
 
-       offset = cf->do_ftell(cf);
+       offset = cs->do_ftell(cs);
        /*
         * At EOF, the parser always "inserts" an extra '\n', therefore
         * the end offset of the event is the current file position, otherwise
@@ -995,7 +1005,7 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
 
        if (data->previous_type != CONFIG_EVENT_EOF &&
            data->opts->event_fn(data->previous_type, data->previous_offset,
-                                offset, data->opts->event_fn_data) < 0)
+                                offset, cs, data->opts->event_fn_data) < 0)
                return -1;
 
        data->previous_type = type;
@@ -1004,12 +1014,24 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
        return 0;
 }
 
-static int git_parse_source(config_fn_t fn, void *data,
+static void kvi_from_source(struct config_source *cs,
+                           enum config_scope scope,
+                           struct key_value_info *out)
+{
+       out->filename = strintern(cs->name);
+       out->origin_type = cs->origin_type;
+       out->linenr = cs->linenr;
+       out->scope = scope;
+       out->path = cs->path;
+}
+
+static int git_parse_source(struct config_source *cs, config_fn_t fn,
+                           struct key_value_info *kvi, void *data,
                            const struct config_options *opts)
 {
        int comment = 0;
        size_t baselen = 0;
-       struct strbuf *var = &cf->var;
+       struct strbuf *var = &cs->var;
        int error_return = 0;
        char *error_msg = NULL;
 
@@ -1024,7 +1046,7 @@ static int git_parse_source(config_fn_t fn, void *data,
        for (;;) {
                int c;
 
-               c = get_next_char();
+               c = get_next_char(cs);
                if (bomptr && *bomptr) {
                        /* We are at the file beginning; skip UTF8-encoded BOM
                         * if present. Sane editors won't put this in on their
@@ -1041,12 +1063,12 @@ static int git_parse_source(config_fn_t fn, void *data,
                        }
                }
                if (c == '\n') {
-                       if (cf->eof) {
-                               if (do_event(CONFIG_EVENT_EOF, &event_data) < 0)
+                       if (cs->eof) {
+                               if (do_event(cs, CONFIG_EVENT_EOF, &event_data) < 0)
                                        return -1;
                                return 0;
                        }
-                       if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+                       if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
                                return -1;
                        comment = 0;
                        continue;
@@ -1054,23 +1076,23 @@ static int git_parse_source(config_fn_t fn, void *data,
                if (comment)
                        continue;
                if (isspace(c)) {
-                       if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+                       if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
                                        return -1;
                        continue;
                }
                if (c == '#' || c == ';') {
-                       if (do_event(CONFIG_EVENT_COMMENT, &event_data) < 0)
+                       if (do_event(cs, CONFIG_EVENT_COMMENT, &event_data) < 0)
                                        return -1;
                        comment = 1;
                        continue;
                }
                if (c == '[') {
-                       if (do_event(CONFIG_EVENT_SECTION, &event_data) < 0)
+                       if (do_event(cs, CONFIG_EVENT_SECTION, &event_data) < 0)
                                        return -1;
 
                        /* Reset prior to determining a new stem */
                        strbuf_reset(var);
-                       if (get_base_var(var) < 0 || var->len < 1)
+                       if (get_base_var(cs, var) < 0 || var->len < 1)
                                break;
                        strbuf_addch(var, '.');
                        baselen = var->len;
@@ -1079,7 +1101,7 @@ static int git_parse_source(config_fn_t fn, void *data,
                if (!isalpha(c))
                        break;
 
-               if (do_event(CONFIG_EVENT_ENTRY, &event_data) < 0)
+               if (do_event(cs, CONFIG_EVENT_ENTRY, &event_data) < 0)
                        return -1;
 
                /*
@@ -1089,42 +1111,42 @@ static int git_parse_source(config_fn_t fn, void *data,
                 */
                strbuf_setlen(var, baselen);
                strbuf_addch(var, tolower(c));
-               if (get_value(fn, data, var) < 0)
+               if (get_value(cs, kvi, fn, data, var) < 0)
                        break;
        }
 
-       if (do_event(CONFIG_EVENT_ERROR, &event_data) < 0)
+       if (do_event(cs, CONFIG_EVENT_ERROR, &event_data) < 0)
                return -1;
 
-       switch (cf->origin_type) {
+       switch (cs->origin_type) {
        case CONFIG_ORIGIN_BLOB:
                error_msg = xstrfmt(_("bad config line %d in blob %s"),
-                                     cf->linenr, cf->name);
+                                     cs->linenr, cs->name);
                break;
        case CONFIG_ORIGIN_FILE:
                error_msg = xstrfmt(_("bad config line %d in file %s"),
-                                     cf->linenr, cf->name);
+                                     cs->linenr, cs->name);
                break;
        case CONFIG_ORIGIN_STDIN:
                error_msg = xstrfmt(_("bad config line %d in standard input"),
-                                     cf->linenr);
+                                     cs->linenr);
                break;
        case CONFIG_ORIGIN_SUBMODULE_BLOB:
                error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
-                                      cf->linenr, cf->name);
+                                      cs->linenr, cs->name);
                break;
        case CONFIG_ORIGIN_CMDLINE:
                error_msg = xstrfmt(_("bad config line %d in command line %s"),
-                                      cf->linenr, cf->name);
+                                      cs->linenr, cs->name);
                break;
        default:
                error_msg = xstrfmt(_("bad config line %d in %s"),
-                                     cf->linenr, cf->name);
+                                     cs->linenr, cs->name);
        }
 
        switch (opts && opts->error_action ?
                opts->error_action :
-               cf->default_error_action) {
+               cs->default_error_action) {
        case CONFIG_ERROR_DIE:
                die("%s", error_msg);
                break;
@@ -1142,199 +1164,81 @@ static int git_parse_source(config_fn_t fn, void *data,
        return error_return;
 }
 
-static uintmax_t get_unit_factor(const char *end)
-{
-       if (!*end)
-               return 1;
-       else if (!strcasecmp(end, "k"))
-               return 1024;
-       else if (!strcasecmp(end, "m"))
-               return 1024 * 1024;
-       else if (!strcasecmp(end, "g"))
-               return 1024 * 1024 * 1024;
-       return 0;
-}
-
-static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
-{
-       if (value && *value) {
-               char *end;
-               intmax_t val;
-               uintmax_t uval;
-               uintmax_t factor;
-
-               errno = 0;
-               val = strtoimax(value, &end, 0);
-               if (errno == ERANGE)
-                       return 0;
-               factor = get_unit_factor(end);
-               if (!factor) {
-                       errno = EINVAL;
-                       return 0;
-               }
-               uval = val < 0 ? -val : val;
-               if (unsigned_mult_overflows(factor, uval) ||
-                   factor * uval > max) {
-                       errno = ERANGE;
-                       return 0;
-               }
-               val *= factor;
-               *ret = val;
-               return 1;
-       }
-       errno = EINVAL;
-       return 0;
-}
-
-static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
-{
-       if (value && *value) {
-               char *end;
-               uintmax_t val;
-               uintmax_t factor;
-
-               errno = 0;
-               val = strtoumax(value, &end, 0);
-               if (errno == ERANGE)
-                       return 0;
-               factor = get_unit_factor(end);
-               if (!factor) {
-                       errno = EINVAL;
-                       return 0;
-               }
-               if (unsigned_mult_overflows(factor, val) ||
-                   factor * val > max) {
-                       errno = ERANGE;
-                       return 0;
-               }
-               val *= factor;
-               *ret = val;
-               return 1;
-       }
-       errno = EINVAL;
-       return 0;
-}
-
-static int git_parse_int(const char *value, int *ret)
-{
-       intmax_t tmp;
-       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
-               return 0;
-       *ret = tmp;
-       return 1;
-}
-
-static int git_parse_int64(const char *value, int64_t *ret)
-{
-       intmax_t tmp;
-       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
-               return 0;
-       *ret = tmp;
-       return 1;
-}
-
-int git_parse_ulong(const char *value, unsigned long *ret)
-{
-       uintmax_t tmp;
-       if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
-               return 0;
-       *ret = tmp;
-       return 1;
-}
-
-int git_parse_ssize_t(const char *value, ssize_t *ret)
-{
-       intmax_t tmp;
-       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
-               return 0;
-       *ret = tmp;
-       return 1;
-}
-
 NORETURN
-static void die_bad_number(const char *name, const char *value)
+static void die_bad_number(const char *name, const char *value,
+                          const struct key_value_info *kvi)
 {
        const char *error_type = (errno == ERANGE) ?
                N_("out of range") : N_("invalid unit");
        const char *bad_numeric = N_("bad numeric config value '%s' for '%s': %s");
 
+       if (!kvi)
+               BUG("kvi should not be NULL");
+
        if (!value)
                value = "";
 
-       if (!(cf && cf->name))
+       if (!kvi->filename)
                die(_(bad_numeric), value, name, _(error_type));
 
-       switch (cf->origin_type) {
+       switch (kvi->origin_type) {
        case CONFIG_ORIGIN_BLOB:
                die(_("bad numeric config value '%s' for '%s' in blob %s: %s"),
-                   value, name, cf->name, _(error_type));
+                   value, name, kvi->filename, _(error_type));
        case CONFIG_ORIGIN_FILE:
                die(_("bad numeric config value '%s' for '%s' in file %s: %s"),
-                   value, name, cf->name, _(error_type));
+                   value, name, kvi->filename, _(error_type));
        case CONFIG_ORIGIN_STDIN:
                die(_("bad numeric config value '%s' for '%s' in standard input: %s"),
                    value, name, _(error_type));
        case CONFIG_ORIGIN_SUBMODULE_BLOB:
                die(_("bad numeric config value '%s' for '%s' in submodule-blob %s: %s"),
-                   value, name, cf->name, _(error_type));
+                   value, name, kvi->filename, _(error_type));
        case CONFIG_ORIGIN_CMDLINE:
                die(_("bad numeric config value '%s' for '%s' in command line %s: %s"),
-                   value, name, cf->name, _(error_type));
+                   value, name, kvi->filename, _(error_type));
        default:
                die(_("bad numeric config value '%s' for '%s' in %s: %s"),
-                   value, name, cf->name, _(error_type));
+                   value, name, kvi->filename, _(error_type));
        }
 }
 
-int git_config_int(const char *name, const char *value)
+int git_config_int(const char *name, const char *value,
+                  const struct key_value_info *kvi)
 {
        int ret;
        if (!git_parse_int(value, &ret))
-               die_bad_number(name, value);
+               die_bad_number(name, value, kvi);
        return ret;
 }
 
-int64_t git_config_int64(const char *name, const char *value)
+int64_t git_config_int64(const char *name, const char *value,
+                        const struct key_value_info *kvi)
 {
        int64_t ret;
        if (!git_parse_int64(value, &ret))
-               die_bad_number(name, value);
+               die_bad_number(name, value, kvi);
        return ret;
 }
 
-unsigned long git_config_ulong(const char *name, const char *value)
+unsigned long git_config_ulong(const char *name, const char *value,
+                              const struct key_value_info *kvi)
 {
        unsigned long ret;
        if (!git_parse_ulong(value, &ret))
-               die_bad_number(name, value);
+               die_bad_number(name, value, kvi);
        return ret;
 }
 
-ssize_t git_config_ssize_t(const char *name, const char *value)
+ssize_t git_config_ssize_t(const char *name, const char *value,
+                          const struct key_value_info *kvi)
 {
        ssize_t ret;
        if (!git_parse_ssize_t(value, &ret))
-               die_bad_number(name, value);
+               die_bad_number(name, value, kvi);
        return ret;
 }
 
-static int git_parse_maybe_bool_text(const char *value)
-{
-       if (!value)
-               return 1;
-       if (!*value)
-               return 0;
-       if (!strcasecmp(value, "true")
-           || !strcasecmp(value, "yes")
-           || !strcasecmp(value, "on"))
-               return 1;
-       if (!strcasecmp(value, "false")
-           || !strcasecmp(value, "no")
-           || !strcasecmp(value, "off"))
-               return 0;
-       return -1;
-}
-
 static const struct fsync_component_name {
        const char *name;
        enum fsync_component component_bits;
@@ -1409,17 +1313,8 @@ next_name:
        return (current & ~negative) | positive;
 }
 
-int git_parse_maybe_bool(const char *value)
-{
-       int v = git_parse_maybe_bool_text(value);
-       if (0 <= v)
-               return v;
-       if (git_parse_int(value, &v))
-               return !!v;
-       return -1;
-}
-
-int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+int git_config_bool_or_int(const char *name, const char *value,
+                          const struct key_value_info *kvi, int *is_bool)
 {
        int v = git_parse_maybe_bool_text(value);
        if (0 <= v) {
@@ -1427,7 +1322,7 @@ int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
                return v;
        }
        *is_bool = 0;
-       return git_config_int(name, value);
+       return git_config_int(name, value, kvi);
 }
 
 int git_config_bool(const char *name, const char *value)
@@ -1475,7 +1370,8 @@ int git_config_color(char *dest, const char *var, const char *value)
        return 0;
 }
 
-static int git_default_core_config(const char *var, const char *value, void *cb)
+static int git_default_core_config(const char *var, const char *value,
+                                  const struct config_context *ctx, void *cb)
 {
        /* This needs a better name */
        if (!strcmp(var, "core.filemode")) {
@@ -1487,10 +1383,15 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
                return 0;
        }
        if (!strcmp(var, "core.checkstat")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (!strcasecmp(value, "default"))
                        check_stat = 1;
                else if (!strcasecmp(value, "minimal"))
                        check_stat = 0;
+               else
+                       return error(_("invalid value for '%s': '%s'"),
+                                    var, value);
        }
 
        if (!strcmp(var, "core.quotepath")) {
@@ -1552,7 +1453,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
                else if (!git_parse_maybe_bool_text(value))
                        default_abbrev = the_hash_algo->hexsz;
                else {
-                       int abbrev = git_config_int(var, value);
+                       int abbrev = git_config_int(var, value, ctx->kvi);
                        if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
                                return error(_("abbrev length out of range: %d"), abbrev);
                        default_abbrev = abbrev;
@@ -1564,7 +1465,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
                return set_disambiguate_hint_config(var, value);
 
        if (!strcmp(var, "core.loosecompression")) {
-               int level = git_config_int(var, value);
+               int level = git_config_int(var, value, ctx->kvi);
                if (level == -1)
                        level = Z_DEFAULT_COMPRESSION;
                else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1575,7 +1476,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, "core.compression")) {
-               int level = git_config_int(var, value);
+               int level = git_config_int(var, value, ctx->kvi);
                if (level == -1)
                        level = Z_DEFAULT_COMPRESSION;
                else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1589,7 +1490,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(var, "core.packedgitwindowsize")) {
                int pgsz_x2 = getpagesize() * 2;
-               packed_git_window_size = git_config_ulong(var, value);
+               packed_git_window_size = git_config_ulong(var, value, ctx->kvi);
 
                /* This value must be multiple of (pagesize * 2) */
                packed_git_window_size /= pgsz_x2;
@@ -1600,17 +1501,17 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, "core.bigfilethreshold")) {
-               big_file_threshold = git_config_ulong(var, value);
+               big_file_threshold = git_config_ulong(var, value, ctx->kvi);
                return 0;
        }
 
        if (!strcmp(var, "core.packedgitlimit")) {
-               packed_git_limit = git_config_ulong(var, value);
+               packed_git_limit = git_config_ulong(var, value, ctx->kvi);
                return 0;
        }
 
        if (!strcmp(var, "core.deltabasecachelimit")) {
-               delta_base_cache_limit = git_config_ulong(var, value);
+               delta_base_cache_limit = git_config_ulong(var, value, ctx->kvi);
                return 0;
        }
 
@@ -1647,12 +1548,12 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!strcmp(var, "core.checkroundtripencoding")) {
-               check_roundtrip_encoding = xstrdup(value);
-               return 0;
-       }
+       if (!strcmp(var, "core.checkroundtripencoding"))
+               return git_config_string(&check_roundtrip_encoding, var, value);
 
        if (!strcmp(var, "core.notesref")) {
+               if (!value)
+                       return config_error_nonbool(var);
                notes_ref_name = xstrdup(value);
                return 0;
        }
@@ -1669,7 +1570,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
                        comment_line_char = value[0];
                        auto_comment_line_char = 0;
                } else
-                       return error(_("core.commentChar should only be one character"));
+                       return error(_("core.commentChar should only be one ASCII character"));
                return 0;
        }
 
@@ -1720,6 +1621,8 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, "core.createobject")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (!strcmp(value, "rename"))
                        object_creation_mode = OBJECT_CREATION_USES_RENAMES;
                else if (!strcmp(value, "link"))
@@ -1754,13 +1657,13 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!strcmp(var, "core.usereplacerefs")) {
-               read_replace_refs = git_config_bool(var, value);
+       if (!strcmp(var, "core.maxtreedepth")) {
+               max_allowed_tree_depth = git_config_int(var, value, ctx->kvi);
                return 0;
        }
 
        /* Add other config variables here and to Documentation/config.txt. */
-       return platform_core_config(var, value, cb);
+       return platform_core_config(var, value, ctx, cb);
 }
 
 static int git_default_sparse_config(const char *var, const char *value)
@@ -1862,15 +1765,28 @@ static int git_default_mailmap_config(const char *var, const char *value)
        return 0;
 }
 
-int git_default_config(const char *var, const char *value, void *cb)
+static int git_default_attr_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "attr.tree"))
+               return git_config_string(&git_attr_tree, var, value);
+
+       /*
+        * Add other attribute related config variables here and to
+        * Documentation/config/attr.txt.
+        */
+       return 0;
+}
+
+int git_default_config(const char *var, const char *value,
+                      const struct config_context *ctx, void *cb)
 {
        if (starts_with(var, "core."))
-               return git_default_core_config(var, value, cb);
+               return git_default_core_config(var, value, ctx, cb);
 
        if (starts_with(var, "user.") ||
            starts_with(var, "author.") ||
            starts_with(var, "committer."))
-               return git_ident_config(var, value, cb);
+               return git_ident_config(var, value, ctx, cb);
 
        if (starts_with(var, "i18n."))
                return git_default_i18n_config(var, value);
@@ -1884,6 +1800,9 @@ int git_default_config(const char *var, const char *value, void *cb)
        if (starts_with(var, "mailmap."))
                return git_default_mailmap_config(var, value);
 
+       if (starts_with(var, "attr."))
+               return git_default_attr_config(var, value);
+
        if (starts_with(var, "advice.") || starts_with(var, "color.advice"))
                return git_default_advice_config(var, value);
 
@@ -1893,12 +1812,12 @@ int git_default_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, "pack.packsizelimit")) {
-               pack_size_limit_cfg = git_config_ulong(var, value);
+               pack_size_limit_cfg = git_config_ulong(var, value, ctx->kvi);
                return 0;
        }
 
        if (!strcmp(var, "pack.compression")) {
-               int level = git_config_int(var, value);
+               int level = git_config_int(var, value, ctx->kvi);
                if (level == -1)
                        level = Z_DEFAULT_COMPRESSION;
                else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1920,36 +1839,36 @@ int git_default_config(const char *var, const char *value, void *cb)
  * fgetc, ungetc, ftell of top need to be initialized before calling
  * this function.
  */
-static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
+static int do_config_from(struct config_source *top, config_fn_t fn,
+                         void *data, enum config_scope scope,
                          const struct config_options *opts)
 {
+       struct key_value_info kvi = KVI_INIT;
        int ret;
 
        /* push config-file parsing state stack */
-       top->prev = cf;
        top->linenr = 1;
        top->eof = 0;
        top->total_len = 0;
        strbuf_init(&top->value, 1024);
        strbuf_init(&top->var, 1024);
-       cf = top;
+       kvi_from_source(top, scope, &kvi);
 
-       ret = git_parse_source(fn, data, opts);
+       ret = git_parse_source(top, fn, &kvi, data, opts);
 
-       /* pop config-file parsing state stack */
        strbuf_release(&top->value);
        strbuf_release(&top->var);
-       cf = top->prev;
 
        return ret;
 }
 
 static int do_config_from_file(config_fn_t fn,
-               const enum config_origin_type origin_type,
-               const char *name, const char *path, FILE *f,
-               void *data, const struct config_options *opts)
+                              const enum config_origin_type origin_type,
+                              const char *name, const char *path, FILE *f,
+                              void *data, enum config_scope scope,
+                              const struct config_options *opts)
 {
-       struct config_source top;
+       struct config_source top = CONFIG_SOURCE_INIT;
        int ret;
 
        top.u.file = f;
@@ -1962,19 +1881,20 @@ static int do_config_from_file(config_fn_t fn,
        top.do_ftell = config_file_ftell;
 
        flockfile(f);
-       ret = do_config_from(&top, fn, data, opts);
+       ret = do_config_from(&top, fn, data, scope, opts);
        funlockfile(f);
        return ret;
 }
 
-static int git_config_from_stdin(config_fn_t fn, void *data)
+static int git_config_from_stdin(config_fn_t fn, void *data,
+                                enum config_scope scope)
 {
        return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
-                                  data, NULL);
+                                  data, scope, NULL);
 }
 
 int git_config_from_file_with_options(config_fn_t fn, const char *filename,
-                                     void *data,
+                                     void *data, enum config_scope scope,
                                      const struct config_options *opts)
 {
        int ret = -1;
@@ -1985,7 +1905,7 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
        f = fopen_or_warn(filename, "r");
        if (f) {
                ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
-                                         filename, f, data, opts);
+                                         filename, f, data, scope, opts);
                fclose(f);
        }
        return ret;
@@ -1993,15 +1913,17 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
 
 int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 {
-       return git_config_from_file_with_options(fn, filename, data, NULL);
+       return git_config_from_file_with_options(fn, filename, data,
+                                                CONFIG_SCOPE_UNKNOWN, NULL);
 }
 
 int git_config_from_mem(config_fn_t fn,
                        const enum config_origin_type origin_type,
                        const char *name, const char *buf, size_t len,
-                       void *data, const struct config_options *opts)
+                       void *data, enum config_scope scope,
+                       const struct config_options *opts)
 {
-       struct config_source top;
+       struct config_source top = CONFIG_SOURCE_INIT;
 
        top.u.buf.buf = buf;
        top.u.buf.len = len;
@@ -2014,14 +1936,15 @@ int git_config_from_mem(config_fn_t fn,
        top.do_ungetc = config_buf_ungetc;
        top.do_ftell = config_buf_ftell;
 
-       return do_config_from(&top, fn, data, opts);
+       return do_config_from(&top, fn, data, scope, opts);
 }
 
 int git_config_from_blob_oid(config_fn_t fn,
                              const char *name,
                              struct repository *repo,
                              const struct object_id *oid,
-                             void *data)
+                             void *data,
+                             enum config_scope scope)
 {
        enum object_type type;
        char *buf;
@@ -2037,7 +1960,7 @@ int git_config_from_blob_oid(config_fn_t fn,
        }
 
        ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size,
-                                 data, NULL);
+                                 data, scope, NULL);
        free(buf);
 
        return ret;
@@ -2046,13 +1969,14 @@ int git_config_from_blob_oid(config_fn_t fn,
 static int git_config_from_blob_ref(config_fn_t fn,
                                    struct repository *repo,
                                    const char *name,
-                                   void *data)
+                                   void *data,
+                                   enum config_scope scope)
 {
        struct object_id oid;
 
        if (repo_get_oid(repo, name, &oid) < 0)
                return error(_("unable to resolve config blob '%s'"), name);
-       return git_config_from_blob_oid(fn, name, repo, &oid, data);
+       return git_config_from_blob_oid(fn, name, repo, &oid, data, scope);
 }
 
 char *git_system_config(void)
@@ -2078,34 +2002,13 @@ void git_global_config(char **user_out, char **xdg_out)
        *xdg_out = xdg_config;
 }
 
-/*
- * Parse environment variable 'k' as a boolean (in various
- * possible spellings); if missing, use the default value 'def'.
- */
-int git_env_bool(const char *k, int def)
-{
-       const char *v = getenv(k);
-       return v ? git_config_bool(k, v) : def;
-}
-
-/*
- * Parse environment variable 'k' as ulong with possibly a unit
- * suffix; if missing, use the default value 'val'.
- */
-unsigned long git_env_ulong(const char *k, unsigned long val)
-{
-       const char *v = getenv(k);
-       if (v && !git_parse_ulong(v, &val))
-               die(_("failed to parse %s"), k);
-       return val;
-}
-
 int git_config_system(void)
 {
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
 static int do_git_config_sequence(const struct config_options *opts,
+                                 const struct repository *repo,
                                  config_fn_t fn, void *data)
 {
        int ret = 0;
@@ -2113,57 +2016,68 @@ static int do_git_config_sequence(const struct config_options *opts,
        char *xdg_config = NULL;
        char *user_config = NULL;
        char *repo_config;
-       enum config_scope prev_parsing_scope = current_parsing_scope;
+       char *worktree_config;
 
-       if (opts->commondir)
+       /*
+        * Ensure that either:
+        * - the git_dir and commondir are both set, or
+        * - the git_dir and commondir are both NULL
+        */
+       if (!opts->git_dir != !opts->commondir)
+               BUG("only one of commondir and git_dir is non-NULL");
+
+       if (opts->commondir) {
                repo_config = mkpathdup("%s/config", opts->commondir);
-       else if (opts->git_dir)
-               BUG("git_dir without commondir");
-       else
+               worktree_config = mkpathdup("%s/config.worktree", opts->git_dir);
+       } else {
                repo_config = NULL;
+               worktree_config = NULL;
+       }
 
-       current_parsing_scope = CONFIG_SCOPE_SYSTEM;
        if (git_config_system() && system_config &&
            !access_or_die(system_config, R_OK,
                           opts->system_gently ? ACCESS_EACCES_OK : 0))
-               ret += git_config_from_file(fn, system_config, data);
+               ret += git_config_from_file_with_options(fn, system_config,
+                                                        data, CONFIG_SCOPE_SYSTEM,
+                                                        NULL);
 
-       current_parsing_scope = CONFIG_SCOPE_GLOBAL;
        git_global_config(&user_config, &xdg_config);
 
        if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
-               ret += git_config_from_file(fn, xdg_config, data);
+               ret += git_config_from_file_with_options(fn, xdg_config, data,
+                                                        CONFIG_SCOPE_GLOBAL, NULL);
 
        if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
-               ret += git_config_from_file(fn, user_config, data);
+               ret += git_config_from_file_with_options(fn, user_config, data,
+                                                        CONFIG_SCOPE_GLOBAL, NULL);
 
-       current_parsing_scope = CONFIG_SCOPE_LOCAL;
        if (!opts->ignore_repo && repo_config &&
            !access_or_die(repo_config, R_OK, 0))
-               ret += git_config_from_file(fn, repo_config, data);
+               ret += git_config_from_file_with_options(fn, repo_config, data,
+                                                        CONFIG_SCOPE_LOCAL, NULL);
 
-       current_parsing_scope = CONFIG_SCOPE_WORKTREE;
-       if (!opts->ignore_worktree && repository_format_worktree_config) {
-               char *path = git_pathdup("config.worktree");
-               if (!access_or_die(path, R_OK, 0))
-                       ret += git_config_from_file(fn, path, data);
-               free(path);
+       if (!opts->ignore_worktree && worktree_config &&
+           repo && repo->repository_format_worktree_config &&
+           !access_or_die(worktree_config, R_OK, 0)) {
+                       ret += git_config_from_file_with_options(fn, worktree_config, data,
+                                                                CONFIG_SCOPE_WORKTREE,
+                                                                NULL);
        }
 
-       current_parsing_scope = CONFIG_SCOPE_COMMAND;
        if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
                die(_("unable to parse command-line config"));
 
-       current_parsing_scope = prev_parsing_scope;
        free(system_config);
        free(xdg_config);
        free(user_config);
        free(repo_config);
+       free(worktree_config);
        return ret;
 }
 
 int config_with_options(config_fn_t fn, void *data,
                        struct git_config_source *config_source,
+                       struct repository *repo,
                        const struct config_options *opts)
 {
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
@@ -2173,29 +2087,27 @@ int config_with_options(config_fn_t fn, void *data,
                inc.fn = fn;
                inc.data = data;
                inc.opts = opts;
+               inc.repo = repo;
                inc.config_source = config_source;
                fn = git_config_include;
                data = &inc;
        }
 
-       if (config_source)
-               current_parsing_scope = config_source->scope;
-
        /*
         * If we have a specific filename, use it. Otherwise, follow the
         * regular lookup sequence.
         */
        if (config_source && config_source->use_stdin) {
-               ret = git_config_from_stdin(fn, data);
+               ret = git_config_from_stdin(fn, data, config_source->scope);
        } else if (config_source && config_source->file) {
-               ret = git_config_from_file(fn, config_source->file, data);
+               ret = git_config_from_file_with_options(fn, config_source->file,
+                                                       data, config_source->scope,
+                                                       NULL);
        } else if (config_source && config_source->blob) {
-               struct repository *repo = config_source->repo ?
-                       config_source->repo : the_repository;
                ret = git_config_from_blob_ref(fn, repo, config_source->blob,
-                                               data);
+                                              data, config_source->scope);
        } else {
-               ret = do_git_config_sequence(opts, fn, data);
+               ret = do_git_config_sequence(opts, repo, fn, data);
        }
 
        if (inc.remote_urls) {
@@ -2205,26 +2117,24 @@ int config_with_options(config_fn_t fn, void *data,
        return ret;
 }
 
-static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
+static void configset_iter(struct config_set *set, config_fn_t fn, void *data)
 {
        int i, value_index;
        struct string_list *values;
        struct config_set_element *entry;
-       struct configset_list *list = &cs->list;
+       struct configset_list *list = &set->list;
+       struct config_context ctx = CONFIG_CONTEXT_INIT;
 
        for (i = 0; i < list->nr; i++) {
                entry = list->items[i].e;
                value_index = list->items[i].value_index;
                values = &entry->value_list;
 
-               current_config_kvi = values->items[value_index].util;
-
-               if (fn(entry->key, values->items[value_index].string, data) < 0)
+               ctx.kvi = values->items[value_index].util;
+               if (fn(entry->key, values->items[value_index].string, &ctx, data) < 0)
                        git_die_config_linenr(entry->key,
-                                             current_config_kvi->filename,
-                                             current_config_kvi->linenr);
-
-               current_config_kvi = NULL;
+                                             ctx.kvi->filename,
+                                             ctx.kvi->linenr);
        }
 }
 
@@ -2252,7 +2162,7 @@ void read_early_config(config_fn_t cb, void *data)
                opts.git_dir = gitdir.buf;
        }
 
-       config_with_options(cb, data, NULL, &opts);
+       config_with_options(cb, data, NULL, NULL, &opts);
 
        strbuf_release(&commondir);
        strbuf_release(&gitdir);
@@ -2272,36 +2182,47 @@ void read_very_early_config(config_fn_t cb, void *data)
        opts.ignore_cmdline = 1;
        opts.system_gently = 1;
 
-       config_with_options(cb, data, NULL, &opts);
+       config_with_options(cb, data, NULL, NULL, &opts);
 }
 
-static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+RESULT_MUST_BE_USED
+static int configset_find_element(struct config_set *set, const char *key,
+                                 struct config_set_element **dest)
 {
        struct config_set_element k;
        struct config_set_element *found_entry;
        char *normalized_key;
+       int ret;
+
        /*
         * `key` may come from the user, so normalize it before using it
         * for querying entries from the hashmap.
         */
-       if (git_config_parse_key(key, &normalized_key, NULL))
-               return NULL;
+       ret = git_config_parse_key(key, &normalized_key, NULL);
+       if (ret)
+               return ret;
 
        hashmap_entry_init(&k.ent, strhash(normalized_key));
        k.key = normalized_key;
-       found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
+       found_entry = hashmap_get_entry(&set->config_hash, &k, ent, NULL);
        free(normalized_key);
-       return found_entry;
+       *dest = found_entry;
+       return 0;
 }
 
-static int configset_add_value(struct config_set *cs, const char *key, const char *value)
+static int configset_add_value(const struct key_value_info *kvi_p,
+                              struct config_set *set, const char *key,
+                              const char *value)
 {
        struct config_set_element *e;
        struct string_list_item *si;
        struct configset_list_item *l_item;
        struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
+       int ret;
 
-       e = configset_find_element(cs, key);
+       ret = configset_find_element(set, key, &e);
+       if (ret)
+               return ret;
        /*
         * Since the keys are being fed by git_config*() callback mechanism, they
         * are already normalized. So simply add them without any further munging.
@@ -2311,28 +2232,16 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
                hashmap_entry_init(&e->ent, strhash(key));
                e->key = xstrdup(key);
                string_list_init_dup(&e->value_list);
-               hashmap_add(&cs->config_hash, &e->ent);
+               hashmap_add(&set->config_hash, &e->ent);
        }
        si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
 
-       ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
-       l_item = &cs->list.items[cs->list.nr++];
+       ALLOC_GROW(set->list.items, set->list.nr + 1, set->list.alloc);
+       l_item = &set->list.items[set->list.nr++];
        l_item->e = e;
        l_item->value_index = e->value_list.nr - 1;
 
-       if (!cf)
-               BUG("configset_add_value has no source");
-       if (cf->name) {
-               kv_info->filename = strintern(cf->name);
-               kv_info->linenr = cf->linenr;
-               kv_info->origin_type = cf->origin_type;
-       } else {
-               /* for values read from `git_config_from_parameters()` */
-               kv_info->filename = NULL;
-               kv_info->linenr = -1;
-               kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
-       }
-       kv_info->scope = current_parsing_scope;
+       *kv_info = *kvi_p;
        si->util = kv_info;
 
        return 0;
@@ -2351,89 +2260,131 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,
        return strcmp(e1->key, e2->key);
 }
 
-void git_configset_init(struct config_set *cs)
+void git_configset_init(struct config_set *set)
 {
-       hashmap_init(&cs->config_hash, config_set_element_cmp, NULL, 0);
-       cs->hash_initialized = 1;
-       cs->list.nr = 0;
-       cs->list.alloc = 0;
-       cs->list.items = NULL;
+       hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
+       set->hash_initialized = 1;
+       set->list.nr = 0;
+       set->list.alloc = 0;
+       set->list.items = NULL;
 }
 
-void git_configset_clear(struct config_set *cs)
+void git_configset_clear(struct config_set *set)
 {
        struct config_set_element *entry;
        struct hashmap_iter iter;
-       if (!cs->hash_initialized)
+       if (!set->hash_initialized)
                return;
 
-       hashmap_for_each_entry(&cs->config_hash, &iter, entry,
+       hashmap_for_each_entry(&set->config_hash, &iter, entry,
                                ent /* member name */) {
                free(entry->key);
                string_list_clear(&entry->value_list, 1);
        }
-       hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
-       cs->hash_initialized = 0;
-       free(cs->list.items);
-       cs->list.nr = 0;
-       cs->list.alloc = 0;
-       cs->list.items = NULL;
+       hashmap_clear_and_free(&set->config_hash, struct config_set_element, ent);
+       set->hash_initialized = 0;
+       free(set->list.items);
+       set->list.nr = 0;
+       set->list.alloc = 0;
+       set->list.items = NULL;
 }
 
-static int config_set_callback(const char *key, const char *value, void *cb)
+static int config_set_callback(const char *key, const char *value,
+                              const struct config_context *ctx,
+                              void *cb)
 {
-       struct config_set *cs = cb;
-       configset_add_value(cs, key, value);
+       struct config_set *set = cb;
+       configset_add_value(ctx->kvi, set, key, value);
        return 0;
 }
 
-int git_configset_add_file(struct config_set *cs, const char *filename)
+int git_configset_add_file(struct config_set *set, const char *filename)
 {
-       return git_config_from_file(config_set_callback, filename, cs);
+       return git_config_from_file(config_set_callback, filename, set);
 }
 
-int git_configset_add_parameters(struct config_set *cs)
-{
-       return git_config_from_parameters(config_set_callback, cs);
-}
-
-int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
+int git_configset_get_value(struct config_set *set, const char *key,
+                           const char **value, struct key_value_info *kvi)
 {
        const struct string_list *values = NULL;
+       int ret;
+       struct string_list_item item;
        /*
         * Follows "last one wins" semantic, i.e., if there are multiple matches for the
         * queried key in the files of the configset, the value returned will be the last
         * value in the value list for that key.
         */
-       values = git_configset_get_value_multi(cs, key);
+       if ((ret = git_configset_get_value_multi(set, key, &values)))
+               return ret;
 
-       if (!values)
-               return 1;
        assert(values->nr > 0);
-       *value = values->items[values->nr - 1].string;
+       item = values->items[values->nr - 1];
+       *value = item.string;
+       if (kvi)
+               *kvi = *((struct key_value_info *)item.util);
        return 0;
 }
 
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+int git_configset_get_value_multi(struct config_set *set, const char *key,
+                                 const struct string_list **dest)
 {
-       struct config_set_element *e = configset_find_element(cs, key);
-       return e ? &e->value_list : NULL;
+       struct config_set_element *e;
+       int ret;
+
+       if ((ret = configset_find_element(set, key, &e)))
+               return ret;
+       else if (!e)
+               return 1;
+       *dest = &e->value_list;
+
+       return 0;
+}
+
+static int check_multi_string(struct string_list_item *item, void *util)
+{
+       return item->string ? 0 : config_error_nonbool(util);
+}
+
+int git_configset_get_string_multi(struct config_set *cs, const char *key,
+                                  const struct string_list **dest)
+{
+       int ret;
+
+       if ((ret = git_configset_get_value_multi(cs, key, dest)))
+               return ret;
+       if ((ret = for_each_string_list((struct string_list *)*dest,
+                                       check_multi_string, (void *)key)))
+               return ret;
+
+       return 0;
 }
 
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+int git_configset_get(struct config_set *set, const char *key)
+{
+       struct config_set_element *e;
+       int ret;
+
+       if ((ret = configset_find_element(set, key, &e)))
+               return ret;
+       else if (!e)
+               return 1;
+       return 0;
+}
+
+int git_configset_get_string(struct config_set *set, const char *key, char **dest)
 {
        const char *value;
-       if (!git_configset_get_value(cs, key, &value))
+       if (!git_configset_get_value(set, key, &value, NULL))
                return git_config_string((const char **)dest, key, value);
        else
                return 1;
 }
 
-static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+static int git_configset_get_string_tmp(struct config_set *set, const char *key,
                                        const char **dest)
 {
        const char *value;
-       if (!git_configset_get_value(cs, key, &value)) {
+       if (!git_configset_get_value(set, key, &value, NULL)) {
                if (!value)
                        return config_error_nonbool(key);
                *dest = value;
@@ -2443,51 +2394,57 @@ static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
        }
 }
 
-int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_int(struct config_set *set, const char *key, int *dest)
 {
        const char *value;
-       if (!git_configset_get_value(cs, key, &value)) {
-               *dest = git_config_int(key, value);
+       struct key_value_info kvi;
+
+       if (!git_configset_get_value(set, key, &value, &kvi)) {
+               *dest = git_config_int(key, value, &kvi);
                return 0;
        } else
                return 1;
 }
 
-int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
+int git_configset_get_ulong(struct config_set *set, const char *key, unsigned long *dest)
 {
        const char *value;
-       if (!git_configset_get_value(cs, key, &value)) {
-               *dest = git_config_ulong(key, value);
+       struct key_value_info kvi;
+
+       if (!git_configset_get_value(set, key, &value, &kvi)) {
+               *dest = git_config_ulong(key, value, &kvi);
                return 0;
        } else
                return 1;
 }
 
-int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_bool(struct config_set *set, const char *key, int *dest)
 {
        const char *value;
-       if (!git_configset_get_value(cs, key, &value)) {
+       if (!git_configset_get_value(set, key, &value, NULL)) {
                *dest = git_config_bool(key, value);
                return 0;
        } else
                return 1;
 }
 
-int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
+int git_configset_get_bool_or_int(struct config_set *set, const char *key,
                                int *is_bool, int *dest)
 {
        const char *value;
-       if (!git_configset_get_value(cs, key, &value)) {
-               *dest = git_config_bool_or_int(key, value, is_bool);
+       struct key_value_info kvi;
+
+       if (!git_configset_get_value(set, key, &value, &kvi)) {
+               *dest = git_config_bool_or_int(key, value, &kvi, is_bool);
                return 0;
        } else
                return 1;
 }
 
-int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *dest)
 {
        const char *value;
-       if (!git_configset_get_value(cs, key, &value)) {
+       if (!git_configset_get_value(set, key, &value, NULL)) {
                *dest = git_parse_maybe_bool(value);
                if (*dest == -1)
                        return -1;
@@ -2496,10 +2453,10 @@ int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *de
                return 1;
 }
 
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
 {
        const char *value;
-       if (!git_configset_get_value(cs, key, &value))
+       if (!git_configset_get_value(set, key, &value, NULL))
                return git_config_pathname(dest, key, value);
        else
                return 1;
@@ -2520,8 +2477,8 @@ static void repo_read_config(struct repository *repo)
                git_configset_clear(repo->config);
 
        git_configset_init(repo->config);
-
-       if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
+       if (config_with_options(config_set_callback, repo->config, NULL,
+                               repo, &opts) < 0)
                /*
                 * config_with_options() normally returns only
                 * zero, as most errors are fatal, and
@@ -2556,18 +2513,31 @@ void repo_config(struct repository *repo, config_fn_t fn, void *data)
        configset_iter(repo->config, fn, data);
 }
 
+int repo_config_get(struct repository *repo, const char *key)
+{
+       git_config_check_init(repo);
+       return git_configset_get(repo->config, key);
+}
+
 int repo_config_get_value(struct repository *repo,
                          const char *key, const char **value)
 {
        git_config_check_init(repo);
-       return git_configset_get_value(repo->config, key, value);
+       return git_configset_get_value(repo->config, key, value, NULL);
+}
+
+int repo_config_get_value_multi(struct repository *repo, const char *key,
+                               const struct string_list **dest)
+{
+       git_config_check_init(repo);
+       return git_configset_get_value_multi(repo->config, key, dest);
 }
 
-const struct string_list *repo_config_get_value_multi(struct repository *repo,
-                                                     const char *key)
+int repo_config_get_string_multi(struct repository *repo, const char *key,
+                                const struct string_list **dest)
 {
        git_config_check_init(repo);
-       return git_configset_get_value_multi(repo->config, key);
+       return git_configset_get_string_multi(repo->config, key, dest);
 }
 
 int repo_config_get_string(struct repository *repo,
@@ -2641,24 +2611,16 @@ int repo_config_get_pathname(struct repository *repo,
 /* Read values into protected_config. */
 static void read_protected_config(void)
 {
-       char *xdg_config = NULL, *user_config = NULL, *system_config = NULL;
+       struct config_options opts = {
+               .respect_includes = 1,
+               .ignore_repo = 1,
+               .ignore_worktree = 1,
+               .system_gently = 1,
+       };
 
        git_configset_init(&protected_config);
-
-       system_config = git_system_config();
-       git_global_config(&user_config, &xdg_config);
-
-       if (system_config)
-               git_configset_add_file(&protected_config, system_config);
-       if (xdg_config)
-               git_configset_add_file(&protected_config, xdg_config);
-       if (user_config)
-               git_configset_add_file(&protected_config, user_config);
-       git_configset_add_parameters(&protected_config);
-
-       free(system_config);
-       free(xdg_config);
-       free(user_config);
+       config_with_options(config_set_callback, &protected_config, NULL,
+                           NULL, &opts);
 }
 
 void git_protected_config(config_fn_t fn, void *data)
@@ -2679,14 +2641,25 @@ void git_config_clear(void)
        repo_config_clear(the_repository);
 }
 
+int git_config_get(const char *key)
+{
+       return repo_config_get(the_repository, key);
+}
+
 int git_config_get_value(const char *key, const char **value)
 {
        return repo_config_get_value(the_repository, key, value);
 }
 
-const struct string_list *git_config_get_value_multi(const char *key)
+int git_config_get_value_multi(const char *key, const struct string_list **dest)
 {
-       return repo_config_get_value_multi(the_repository, key);
+       return repo_config_get_value_multi(the_repository, key, dest);
+}
+
+int git_config_get_string_multi(const char *key,
+                               const struct string_list **dest)
+{
+       return repo_config_get_string_multi(the_repository, key, dest);
 }
 
 int git_config_get_string(const char *key, char **dest)
@@ -2833,7 +2806,8 @@ void git_die_config(const char *key, const char *err, ...)
                error_fn(err, params);
                va_end(params);
        }
-       values = git_config_get_value_multi(key);
+       if (git_config_get_value_multi(key, &values))
+               BUG("for key '%s' we must have a value to report on", key);
        kv_info = values->items[values->nr - 1].util;
        git_die_config_linenr(key, kv_info->filename, kv_info->linenr);
 }
@@ -2857,6 +2831,7 @@ struct config_store_data {
        unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc;
        unsigned int key_seen:1, section_seen:1, is_keys_section:1;
 };
+#define CONFIG_STORE_INIT { 0 }
 
 static void config_store_data_clear(struct config_store_data *store)
 {
@@ -2887,8 +2862,8 @@ static int matches(const char *key, const char *value,
                (value && !regexec(store->value_pattern, value, 0, NULL, 0));
 }
 
-static int store_aux_event(enum config_event_t type,
-                          size_t begin, size_t end, void *data)
+static int store_aux_event(enum config_event_t type, size_t begin, size_t end,
+                          struct config_source *cs, void *data)
 {
        struct config_store_data *store = data;
 
@@ -2900,10 +2875,10 @@ static int store_aux_event(enum config_event_t type,
        if (type == CONFIG_EVENT_SECTION) {
                int (*cmpfn)(const char *, const char *, size_t);
 
-               if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.')
-                       return error(_("invalid section name '%s'"), cf->var.buf);
+               if (cs->var.len < 2 || cs->var.buf[cs->var.len - 1] != '.')
+                       return error(_("invalid section name '%s'"), cs->var.buf);
 
-               if (cf->subsection_case_sensitive)
+               if (cs->subsection_case_sensitive)
                        cmpfn = strncasecmp;
                else
                        cmpfn = strncmp;
@@ -2911,8 +2886,8 @@ static int store_aux_event(enum config_event_t type,
                /* Is this the section we were looking for? */
                store->is_keys_section =
                        store->parsed[store->parsed_nr].is_keys_section =
-                       cf->var.len - 1 == store->baselen &&
-                       !cmpfn(cf->var.buf, store->key, store->baselen);
+                       cs->var.len - 1 == store->baselen &&
+                       !cmpfn(cs->var.buf, store->key, store->baselen);
                if (store->is_keys_section) {
                        store->section_seen = 1;
                        ALLOC_GROW(store->seen, store->seen_nr + 1,
@@ -2926,7 +2901,8 @@ static int store_aux_event(enum config_event_t type,
        return 0;
 }
 
-static int store_aux(const char *key, const char *value, void *cb)
+static int store_aux(const char *key, const char *value,
+                    const struct config_context *ctx UNUSED, void *cb)
 {
        struct config_store_data *store = cb;
 
@@ -3154,8 +3130,8 @@ int git_config_set_gently(const char *key, const char *value)
 int repo_config_set_worktree_gently(struct repository *r,
                                    const char *key, const char *value)
 {
-       /* Only use worktree-specific config if it is is already enabled. */
-       if (repository_format_worktree_config) {
+       /* Only use worktree-specific config if it is already enabled. */
+       if (r->repository_format_worktree_config) {
                char *file = repo_git_path(r, "config.worktree");
                int ret = git_config_set_multivar_in_file_gently(
                                        file, key, value, NULL, 0);
@@ -3208,9 +3184,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        char *filename_buf = NULL;
        char *contents = NULL;
        size_t contents_sz;
-       struct config_store_data store;
-
-       memset(&store, 0, sizeof(store));
+       struct config_store_data store = CONFIG_STORE_INIT;
 
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
@@ -3300,7 +3274,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                 */
                if (git_config_from_file_with_options(store_aux,
                                                      config_filename,
-                                                     &store, &opts)) {
+                                                     &store, CONFIG_SCOPE_UNKNOWN,
+                                                     &opts)) {
                        error(_("invalid config file %s"), config_filename);
                        ret = CONFIG_INVALID_FILE;
                        goto out_free;
@@ -3661,6 +3636,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
                                                output[0] = '\t';
                                        }
                                } else {
+                                       strbuf_release(&copystr);
                                        copystr = store_create_section(new_name, &store);
                                }
                        }
@@ -3707,6 +3683,7 @@ out_no_rollback:
        free(filename_buf);
        config_store_data_clear(&store);
        strbuf_release(&buf);
+       strbuf_release(&copystr);
        return ret;
 }
 
@@ -3780,16 +3757,8 @@ int parse_config_key(const char *var,
        return 0;
 }
 
-const char *current_config_origin_type(void)
+const char *config_origin_type_name(enum config_origin_type type)
 {
-       int type;
-       if (current_config_kvi)
-               type = current_config_kvi->origin_type;
-       else if(cf)
-               type = cf->origin_type;
-       else
-               BUG("current_config_origin_type called outside config callback");
-
        switch (type) {
        case CONFIG_ORIGIN_BLOB:
                return "blob";
@@ -3826,34 +3795,6 @@ const char *config_scope_name(enum config_scope scope)
        }
 }
 
-const char *current_config_name(void)
-{
-       const char *name;
-       if (current_config_kvi)
-               name = current_config_kvi->filename;
-       else if (cf)
-               name = cf->name;
-       else
-               BUG("current_config_name called outside config callback");
-       return name ? name : "";
-}
-
-enum config_scope current_config_scope(void)
-{
-       if (current_config_kvi)
-               return current_config_kvi->scope;
-       else
-               return current_parsing_scope;
-}
-
-int current_config_line(void)
-{
-       if (current_config_kvi)
-               return current_config_kvi->linenr;
-       else
-               return cf->linenr;
-}
-
 int lookup_config(const char **mapping, int nr_mapping, const char *var)
 {
        int i;
index ca994d771475a961da458454beabbb66bf63b949..14f881ecfaf6b264b6d120205ab509f2ee5930f2 100644 (file)
--- a/config.h
+++ b/config.h
@@ -3,7 +3,8 @@
 
 #include "hashmap.h"
 #include "string-list.h"
-
+#include "repository.h"
+#include "parse.h"
 
 /**
  * The config API gives callers a way to access Git configuration files
@@ -49,13 +50,12 @@ const char *config_scope_name(enum config_scope scope);
 struct git_config_source {
        unsigned int use_stdin:1;
        const char *file;
-       /* The repository if blob is not NULL; leave blank for the_repository */
-       struct repository *repo;
        const char *blob;
        enum config_scope scope;
 };
 
 enum config_origin_type {
+       CONFIG_ORIGIN_UNKNOWN = 0,
        CONFIG_ORIGIN_BLOB,
        CONFIG_ORIGIN_FILE,
        CONFIG_ORIGIN_STDIN,
@@ -72,6 +72,7 @@ enum config_event_t {
        CONFIG_EVENT_ERROR
 };
 
+struct config_source;
 /*
  * The parser event function (if not NULL) is called with the event type and
  * the begin/end offsets of the parsed elements.
@@ -81,6 +82,7 @@ enum config_event_t {
  */
 typedef int (*config_parser_event_fn_t)(enum config_event_t type,
                                        size_t begin_offset, size_t end_offset,
+                                       struct config_source *cs,
                                        void *event_fn_data);
 
 struct config_options {
@@ -100,6 +102,10 @@ struct config_options {
 
        const char *commondir;
        const char *git_dir;
+       /*
+        * event_fn and event_fn_data are for internal use only. Handles events
+        * emitted by the config parser.
+        */
        config_parser_event_fn_t event_fn;
        void *event_fn_data;
        enum config_error_action {
@@ -110,8 +116,31 @@ struct config_options {
        } error_action;
 };
 
+/* Config source metadata for a given config key-value pair */
+struct key_value_info {
+       const char *filename;
+       int linenr;
+       enum config_origin_type origin_type;
+       enum config_scope scope;
+       const char *path;
+};
+#define KVI_INIT { \
+       .filename = NULL, \
+       .linenr = -1, \
+       .origin_type = CONFIG_ORIGIN_UNKNOWN, \
+       .scope = CONFIG_SCOPE_UNKNOWN, \
+       .path = NULL, \
+}
+
+/* Captures additional information that a config callback can use. */
+struct config_context {
+       /* Config source metadata for key and value. */
+       const struct key_value_info *kvi;
+};
+#define CONFIG_CONTEXT_INIT { 0 }
+
 /**
- * A config callback function takes three parameters:
+ * A config callback function takes four parameters:
  *
  * - the name of the parsed variable. This is in canonical "flat" form: the
  *   section, subsection, and variable segments will be separated by dots,
@@ -122,15 +151,22 @@ struct config_options {
  *   value specified, the value will be NULL (typically this means it
  *   should be interpreted as boolean true).
  *
+ * - the 'config context', that is, additional information about the config
+ *   iteration operation provided by the config machinery. For example, this
+ *   includes information about the config source being parsed (e.g. the
+ *   filename).
+ *
  * - a void pointer passed in by the caller of the config API; this can
  *   contain callback-specific data
  *
  * A config callback should return 0 for success, or -1 if the variable
  * could not be parsed properly.
  */
-typedef int (*config_fn_t)(const char *, const char *, void *);
+typedef int (*config_fn_t)(const char *, const char *,
+                          const struct config_context *, void *);
 
-int git_default_config(const char *, const char *, void *);
+int git_default_config(const char *, const char *,
+                      const struct config_context *, void *);
 
 /**
  * Read a specific file in git-config format.
@@ -141,16 +177,18 @@ int git_default_config(const char *, const char *, void *);
 int git_config_from_file(config_fn_t fn, const char *, void *);
 
 int git_config_from_file_with_options(config_fn_t fn, const char *,
-                                     void *,
+                                     void *, enum config_scope,
                                      const struct config_options *);
 int git_config_from_mem(config_fn_t fn,
                        const enum config_origin_type,
                        const char *name,
                        const char *buf, size_t len,
-                       void *data, const struct config_options *opts);
+                       void *data, enum config_scope scope,
+                       const struct config_options *opts);
 int git_config_from_blob_oid(config_fn_t fn, const char *name,
                             struct repository *repo,
-                            const struct object_id *oid, void *data);
+                            const struct object_id *oid, void *data,
+                            enum config_scope scope);
 void git_config_push_parameter(const char *text);
 void git_config_push_env(const char *spec);
 int git_config_from_parameters(config_fn_t fn, void *data);
@@ -195,6 +233,7 @@ void git_config(config_fn_t fn, void *);
  */
 int config_with_options(config_fn_t fn, void *,
                        struct git_config_source *config_source,
+                       struct repository *repo,
                        const struct config_options *opts);
 
 /**
@@ -204,35 +243,30 @@ int config_with_options(config_fn_t fn, void *,
  * The following helper functions aid in parsing string values
  */
 
-int git_parse_ssize_t(const char *, ssize_t *);
-int git_parse_ulong(const char *, unsigned long *);
-
-/**
- * Same as `git_config_bool`, except that it returns -1 on error rather
- * than dying.
- */
-int git_parse_maybe_bool(const char *);
-
 /**
  * Parse the string to an integer, including unit factors. Dies on error;
  * otherwise, returns the parsed result.
  */
-int git_config_int(const char *, const char *);
+int git_config_int(const char *, const char *, const struct key_value_info *);
 
-int64_t git_config_int64(const char *, const char *);
+int64_t git_config_int64(const char *, const char *,
+                        const struct key_value_info *);
 
 /**
  * Identical to `git_config_int`, but for unsigned longs.
  */
-unsigned long git_config_ulong(const char *, const char *);
+unsigned long git_config_ulong(const char *, const char *,
+                              const struct key_value_info *);
 
-ssize_t git_config_ssize_t(const char *, const char *);
+ssize_t git_config_ssize_t(const char *, const char *,
+                          const struct key_value_info *);
 
 /**
  * Same as `git_config_bool`, except that integers are returned as-is, and
  * an `is_bool` flag is unset.
  */
-int git_config_bool_or_int(const char *, const char *, int *);
+int git_config_bool_or_int(const char *, const char *,
+                          const struct key_value_info *, int *);
 
 /**
  * Parse a string into a boolean value, respecting keywords like "true" and
@@ -341,8 +375,6 @@ int git_config_rename_section(const char *, const char *);
 int git_config_rename_section_in_file(const char *, const char *, const char *);
 int git_config_copy_section(const char *, const char *);
 int git_config_copy_section_in_file(const char *, const char *, const char *);
-int git_env_bool(const char *, int);
-unsigned long git_env_ulong(const char *, unsigned long);
 int git_config_system(void);
 int config_error_nonbool(const char *);
 #if defined(__GNUC__)
@@ -354,10 +386,8 @@ void git_global_config(char **user, char **xdg);
 
 int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
-enum config_scope current_config_scope(void);
-const char *current_config_origin_type(void);
-const char *current_config_name(void);
-int current_config_line(void);
+const char *config_origin_type_name(enum config_origin_type type);
+void kvi_from_param(struct key_value_info *out);
 
 /*
  * Match and parse a config key of the form:
@@ -447,21 +477,33 @@ void git_configset_init(struct config_set *cs);
 int git_configset_add_file(struct config_set *cs, const char *filename);
 
 /**
- * Parses command line options and environment variables, and adds the
- * variable-value pairs to the `config_set`. Returns 0 on success, or -1
- * if there is an error in parsing. The caller decides whether to free
- * the incomplete configset or continue using it when the function
- * returns -1.
+ * Finds and returns the value list, sorted in order of increasing priority
+ * for the configuration variable `key` and config set `cs`. When the
+ * configuration variable `key` is not found, returns 1 without touching
+ * `value`.
+ *
+ * The key will be parsed for validity with git_config_parse_key(), on
+ * error a negative value will be returned.
+ *
+ * The caller should not free or modify the returned pointer, as it is
+ * owned by the cache.
  */
-int git_configset_add_parameters(struct config_set *cs);
+RESULT_MUST_BE_USED
+int git_configset_get_value_multi(struct config_set *cs, const char *key,
+                                 const struct string_list **dest);
 
 /**
- * Finds and returns the value list, sorted in order of increasing priority
- * for the configuration variable `key` and config set `cs`. When the
- * configuration variable `key` is not found, returns NULL. The caller
- * should not free or modify the returned pointer, as it is owned by the cache.
+ * A validation wrapper for git_configset_get_value_multi() which does
+ * for it what git_configset_get_string() does for
+ * git_configset_get_value().
+ *
+ * The configuration syntax allows for "[section] key", which will
+ * give us a NULL entry in the "struct string_list", as opposed to
+ * "[section] key =" which is the empty string. Most users of the API
+ * are not prepared to handle NULL in a "struct string_list".
  */
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+int git_configset_get_string_multi(struct config_set *cs, const char *key,
+                                  const struct string_list **dest);
 
 /**
  * Clears `config_set` structure, removes all saved variable-value pairs.
@@ -473,6 +515,13 @@ void git_configset_clear(struct config_set *cs);
  * value in the 'dest' pointer.
  */
 
+/**
+ * git_configset_get() returns negative values on error, see
+ * repo_config_get() below.
+ */
+RESULT_MUST_BE_USED
+int git_configset_get(struct config_set *cs, const char *key);
+
 /*
  * Finds the highest-priority value for the configuration variable `key`
  * and config set `cs`, stores the pointer to it in `value` and returns 0.
@@ -480,7 +529,8 @@ void git_configset_clear(struct config_set *cs);
  * touching `value`. The caller should not free or modify `value`, as it
  * is owned by the cache.
  */
-int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_value(struct config_set *cs, const char *key,
+                           const char **dest, struct key_value_info *kvi);
 
 int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
 int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
@@ -493,10 +543,22 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
 /* Functions for reading a repository's config */
 struct repository;
 void repo_config(struct repository *repo, config_fn_t fn, void *data);
+
+/**
+ * Run only the discover part of the repo_config_get_*() functions
+ * below, in addition to 1 if not found, returns negative values on
+ * error (e.g. if the key itself is invalid).
+ */
+RESULT_MUST_BE_USED
+int repo_config_get(struct repository *repo, const char *key);
 int repo_config_get_value(struct repository *repo,
                          const char *key, const char **value);
-const struct string_list *repo_config_get_value_multi(struct repository *repo,
-                                                     const char *key);
+RESULT_MUST_BE_USED
+int repo_config_get_value_multi(struct repository *repo, const char *key,
+                               const struct string_list **dest);
+RESULT_MUST_BE_USED
+int repo_config_get_string_multi(struct repository *repo, const char *key,
+                                const struct string_list **dest);
 int repo_config_get_string(struct repository *repo,
                           const char *key, char **dest);
 int repo_config_get_string_tmp(struct repository *repo,
@@ -529,8 +591,15 @@ void git_protected_config(config_fn_t fn, void *data);
  * manner, the config API provides two functions `git_config_get_value`
  * and `git_config_get_value_multi`. They both read values from an internal
  * cache generated previously from reading the config files.
+ *
+ * For those git_config_get*() functions that aren't documented,
+ * consult the corresponding repo_config_get*() function's
+ * documentation.
  */
 
+RESULT_MUST_BE_USED
+int git_config_get(const char *key);
+
 /**
  * Finds the highest-priority value for the configuration variable `key`,
  * stores the pointer to it in `value` and returns 0. When the
@@ -543,10 +612,17 @@ int git_config_get_value(const char *key, const char **value);
 /**
  * Finds and returns the value list, sorted in order of increasing priority
  * for the configuration variable `key`. When the configuration variable
- * `key` is not found, returns NULL. The caller should not free or modify
- * the returned pointer, as it is owned by the cache.
+ * `key` is not found, returns 1 without touching `value`.
+ *
+ * The caller should not free or modify the returned pointer, as it is
+ * owned by the cache.
  */
-const struct string_list *git_config_get_value_multi(const char *key);
+RESULT_MUST_BE_USED
+int git_config_get_value_multi(const char *key,
+                              const struct string_list **dest);
+RESULT_MUST_BE_USED
+int git_config_get_string_multi(const char *key,
+                               const struct string_list **dest);
 
 /**
  * Resets and invalidates the config cache.
@@ -620,13 +696,6 @@ int git_config_get_expiry(const char *key, const char **output);
 /* parse either "this many days" integer, or "5.days.ago" approxidate */
 int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
 
-struct key_value_info {
-       const char *filename;
-       int linenr;
-       enum config_origin_type origin_type;
-       enum config_scope scope;
-};
-
 /**
  * First prints the error message specified by the caller in `err` and then
  * dies printing the line number and the file name of the highest priority
index d63629fe807f59deda80ed780c1915df011bf862..3bb03f423a08102f837c52dcbe7a3b581104e0cb 100644 (file)
@@ -147,6 +147,7 @@ ifeq ($(uname_S),Darwin)
        FREAD_READS_DIRECTORIES = UnfortunatelyYes
        HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
        CSPRNG_METHOD = arc4random
+       USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS = YesPlease
 
        # Workaround for `gettext` being keg-only and not even being linked via
        # `brew link --force gettext`, should be obsolete as of
@@ -525,6 +526,8 @@ else
 endif
        X = .exe
 
+       EXTRA_PROGRAMS += headless-git$X
+
 compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS
 endif
 ifeq ($(uname_S),Interix)
@@ -623,6 +626,9 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        SHELL_PATH = /usr/coreutils/bin/bash
 endif
 ifeq ($(uname_S),MINGW)
+       ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
+               $(error "Building with MSys is no longer supported")
+       endif
        pathsep = ;
        HAVE_ALLOCA_H = YesPlease
        NO_PREAD = YesPlease
@@ -652,7 +658,6 @@ ifeq ($(uname_S),MINGW)
        USE_WIN32_IPC = YesPlease
        USE_WIN32_MMAP = YesPlease
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
-       USE_NED_ALLOCATOR = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
@@ -677,61 +682,44 @@ ifeq ($(uname_S),MINGW)
        RC = windres -O coff
        NATIVE_CRLF = YesPlease
        X = .exe
-ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
-       htmldir = doc/git/html/
-       prefix =
+       # MSys2
+       prefix = /usr/
+       # Enable DEP
+       BASIC_LDFLAGS += -Wl,--nxcompat
+       # Enable ASLR (unless debugging)
+       ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
+               BASIC_LDFLAGS += -Wl,--dynamicbase
+       endif
+       ifeq (MINGW32,$(MSYSTEM))
+               prefix = /mingw32
+               HOST_CPU = i686
+               BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
+       endif
+       ifeq (MINGW64,$(MSYSTEM))
+               prefix = /mingw64
+               HOST_CPU = x86_64
+               BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
+       else
+               COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
+               BASIC_LDFLAGS += -Wl,--large-address-aware
+       endif
+       CC = gcc
+       COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
+               -fstack-protector-strong
+       EXTLIBS += -lntdll
+       EXTRA_PROGRAMS += headless-git$X
        INSTALL = /bin/install
-       EXTLIBS += /mingw/lib/libz.a
        INTERNAL_QSORT = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
-       NO_GETTEXT = YesPlease
-       NO_PYTHON = YesPlease
-       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS
-else
-       ifneq ($(shell expr "$(uname_R)" : '1\.'),2)
-               # MSys2
-               prefix = /usr/
-               # Enable DEP
-               BASIC_LDFLAGS += -Wl,--nxcompat
-               # Enable ASLR (unless debugging)
-               ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
-                       BASIC_LDFLAGS += -Wl,--dynamicbase
-               endif
-               ifeq (MINGW32,$(MSYSTEM))
-                       prefix = /mingw32
-                       HOST_CPU = i686
-                       BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
-               endif
-               ifeq (MINGW64,$(MSYSTEM))
-                       prefix = /mingw64
-                       HOST_CPU = x86_64
-                       BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
-               else
-                       COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
-                       BASIC_LDFLAGS += -Wl,--large-address-aware
-               endif
-               CC = gcc
-               COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
-                       -fstack-protector-strong
-               EXTLIBS += -lntdll
-               INSTALL = /bin/install
-               INTERNAL_QSORT = YesPlease
-               HAVE_LIBCHARSET_H = YesPlease
-               USE_GETTEXT_SCHEME = fallthrough
-               USE_LIBPCRE = YesPlease
-               USE_NED_ALLOCATOR = YesPlease
-               ifeq (/mingw64,$(subst 32,64,$(prefix)))
-                       # Move system config into top-level /etc/
-                       ETC_GITCONFIG = ../etc/gitconfig
-                       ETC_GITATTRIBUTES = ../etc/gitattributes
-               endif
-       else
-               COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO
-               NO_CURL = YesPlease
-               NO_PYTHON = YesPlease
+       USE_GETTEXT_SCHEME = fallthrough
+       USE_LIBPCRE = YesPlease
+       USE_NED_ALLOCATOR = YesPlease
+       ifeq (/mingw64,$(subst 32,64,$(prefix)))
+               # Move system config into top-level /etc/
+               ETC_GITCONFIG = ../etc/gitconfig
+               ETC_GITATTRIBUTES = ../etc/gitattributes
        endif
 endif
-endif
 ifeq ($(uname_S),QNX)
        COMPAT_CFLAGS += -DSA_RESTART=0
        EXPAT_NEEDS_XMLPARSE_H = YesPlease
index 38ff86678a03a96dffa57507364a6f706ddb44d0..d1a96da14eb56766e1538da80ab97ed8a99b24f6 100644 (file)
@@ -94,7 +94,7 @@ AC_DEFUN([GIT_PARSE_WITH_SET_MAKE_VAR],
 [AC_ARG_WITH([$1],
  [AS_HELP_STRING([--with-$1=VALUE], $3)],
  if test -n "$withval"; then
-  if test "$withval" = "yes" -o "$withval" = "no"; then
+  if test "$withval" = "yes" || test "$withval" = "no"; then
     AC_MSG_WARN([You likely do not want either 'yes' or 'no' as]
                     [a value for $1 ($2).  Maybe you do...?])
   fi
@@ -546,6 +546,8 @@ fi
 # git-http-push are not built, and you cannot use http:// and https://
 # transports.
 
+if test -z "$NO_CURL"; then
+
 GIT_STASH_FLAGS($CURLDIR)
 
 AC_CHECK_LIB([curl], [curl_global_init],
@@ -554,6 +556,8 @@ AC_CHECK_LIB([curl], [curl_global_init],
 
 GIT_UNSTASH_FLAGS($CURLDIR)
 
+fi
+
 GIT_CONF_SUBST([NO_CURL])
 
 if test -z "$NO_CURL"; then
@@ -581,6 +585,8 @@ fi
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 # not built, and you cannot push using http:// and https:// transports.
 
+if test -z "$NO_EXPAT"; then
+
 GIT_STASH_FLAGS($EXPATDIR)
 
 AC_CHECK_LIB([expat], [XML_ParserCreate],
@@ -589,6 +595,8 @@ AC_CHECK_LIB([expat], [XML_ParserCreate],
 
 GIT_UNSTASH_FLAGS($EXPATDIR)
 
+fi
+
 GIT_CONF_SUBST([NO_EXPAT])
 
 #
@@ -636,7 +644,6 @@ LIBS="$old_LIBS"
 GIT_UNSTASH_FLAGS($ICONVDIR)
 
 GIT_CONF_SUBST([NEEDS_LIBICONV])
-GIT_CONF_SUBST([NO_ICONV])
 
 if test -n "$NO_ICONV"; then
     NEEDS_LIBICONV=
@@ -644,6 +651,8 @@ fi
 
 fi
 
+GIT_CONF_SUBST([NO_ICONV])
+
 #
 # Define NO_DEFLATE_BOUND if deflateBound is missing from zlib.
 
index 5ea53deda23527ded642449fa9d803cc12e34477..0d77737a5363c39fd71376c0f483bc8808538754 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -1,6 +1,8 @@
 #include "git-compat-util.h"
-#include "cache.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "pkt-line.h"
 #include "quote.h"
 #include "refs.h"
 #include "url.h"
 #include "string-list.h"
 #include "oid-array.h"
+#include "path.h"
 #include "transport.h"
+#include "trace2.h"
 #include "strbuf.h"
 #include "version.h"
 #include "protocol.h"
 #include "alias.h"
+#include "bundle-uri.h"
 
 static char *server_capabilities_v1;
 static struct strvec server_capabilities_v2 = STRVEC_INIT;
-static const char *next_server_feature_value(const char *feature, int *len, int *offset);
+static const char *next_server_feature_value(const char *feature, size_t *len, size_t *offset);
 
 static int check_ref(const char *name, unsigned int flags)
 {
@@ -29,7 +34,8 @@ static int check_ref(const char *name, unsigned int flags)
                return 0;
 
        /* REF_NORMAL means that we don't want the magic fake tag refs */
-       if ((flags & REF_NORMAL) && check_refname_format(name, 0))
+       if ((flags & REF_NORMAL) && check_refname_format(name,
+                                                        REFNAME_ALLOW_ONELEVEL))
                return 0;
 
        /* REF_HEADS means that we want regular branch heads */
@@ -66,7 +72,7 @@ static NORETURN void die_initial_contact(int unexpected)
 }
 
 /* Checks if the server supports the capability 'c' */
-int server_supports_v2(const char *c, int die_on_error)
+int server_supports_v2(const char *c)
 {
        int i;
 
@@ -76,11 +82,13 @@ int server_supports_v2(const char *c, int die_on_error)
                    (!*out || *out == '='))
                        return 1;
        }
+       return 0;
+}
 
-       if (die_on_error)
+void ensure_server_supports_v2(const char *c)
+{
+       if (!server_supports_v2(c))
                die(_("server doesn't support '%s'"), c);
-
-       return 0;
 }
 
 int server_feature_v2(const char *c, const char **v)
@@ -198,10 +206,10 @@ reject:
 static void annotate_refs_with_symref_info(struct ref *ref)
 {
        struct string_list symref = STRING_LIST_INIT_DUP;
-       int offset = 0;
+       size_t offset = 0;
 
        while (1) {
-               int len;
+               size_t len;
                const char *val;
 
                val = next_server_feature_value("symref", &len, &offset);
@@ -224,7 +232,7 @@ static void annotate_refs_with_symref_info(struct ref *ref)
 static void process_capabilities(struct packet_reader *reader, int *linelen)
 {
        const char *feat_val;
-       int feat_len;
+       size_t feat_len;
        const char *line = reader->line;
        int nul_location = strlen(line);
        if (nul_location == *linelen)
@@ -256,7 +264,8 @@ static int process_dummy_ref(const struct packet_reader *reader)
                return 0;
        name++;
 
-       return oideq(null_oid(), &oid) && !strcmp(name, "capabilities^{}");
+       return oideq(reader->hash_algo->null_oid, &oid) &&
+               !strcmp(name, "capabilities^{}");
 }
 
 static void check_no_capabilities(const char *line, int len)
@@ -477,7 +486,7 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)
 {
        const char *hash_name;
 
-       if (server_supports_v2("agent", 0))
+       if (server_supports_v2("agent"))
                packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
 
        if (server_feature_v2("object-format", &hash_name)) {
@@ -491,6 +500,49 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)
        }
 }
 
+int get_remote_bundle_uri(int fd_out, struct packet_reader *reader,
+                         struct bundle_list *bundles, int stateless_rpc)
+{
+       int line_nr = 1;
+
+       /* Assert bundle-uri support */
+       ensure_server_supports_v2("bundle-uri");
+
+       /* (Re-)send capabilities */
+       send_capabilities(fd_out, reader);
+
+       /* Send command */
+       packet_write_fmt(fd_out, "command=bundle-uri\n");
+       packet_delim(fd_out);
+
+       packet_flush(fd_out);
+
+       /* Process response from server */
+       while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+               const char *line = reader->line;
+               line_nr++;
+
+               if (!bundle_uri_parse_line(bundles, line))
+                       continue;
+
+               return error(_("error on bundle-uri response line %d: %s"),
+                            line_nr, line);
+       }
+
+       if (reader->status != PACKET_READ_FLUSH)
+               return error(_("expected flush after bundle-uri listing"));
+
+       /*
+        * Might die(), but obscure enough that that's OK, e.g. in
+        * serve.c we'll call BUG() on its equivalent (the
+        * PACKET_READ_RESPONSE_END check).
+        */
+       check_stateless_delimiter(stateless_rpc, reader,
+                                 _("expected response end packet after ref listing"));
+
+       return 0;
+}
+
 struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
                             struct ref **list, int for_push,
                             struct transport_ls_refs_options *transport_options,
@@ -504,17 +556,18 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
                &transport_options->unborn_head_target : NULL;
        *list = NULL;
 
-       if (server_supports_v2("ls-refs", 1))
-               packet_write_fmt(fd_out, "command=ls-refs\n");
+       ensure_server_supports_v2("ls-refs");
+       packet_write_fmt(fd_out, "command=ls-refs\n");
 
        /* Send capabilities */
        send_capabilities(fd_out, reader);
 
-       if (server_options && server_options->nr &&
-           server_supports_v2("server-option", 1))
+       if (server_options && server_options->nr) {
+               ensure_server_supports_v2("server-option");
                for (i = 0; i < server_options->nr; i++)
                        packet_write_fmt(fd_out, "server-option=%s",
                                         server_options->items[i].string);
+       }
 
        packet_delim(fd_out);
        /* When pushing we don't want to request the peeled tags */
@@ -544,9 +597,10 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
        return list;
 }
 
-const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset)
+const char *parse_feature_value(const char *feature_list, const char *feature, size_t *lenp, size_t *offset)
 {
-       int len;
+       const char *orig_start = feature_list;
+       size_t len;
 
        if (!feature_list)
                return NULL;
@@ -565,19 +619,19 @@ const char *parse_feature_value(const char *feature_list, const char *feature, i
                                if (lenp)
                                        *lenp = 0;
                                if (offset)
-                                       *offset = found + len - feature_list;
+                                       *offset = found + len - orig_start;
                                return value;
                        }
                        /* feature with a value (e.g., "agent=git/1.2.3") */
                        else if (*value == '=') {
-                               int end;
+                               size_t end;
 
                                value++;
                                end = strcspn(value, " \t\n");
                                if (lenp)
                                        *lenp = end;
                                if (offset)
-                                       *offset = value + end - feature_list;
+                                       *offset = value + end - orig_start;
                                return value;
                        }
                        /*
@@ -592,8 +646,8 @@ const char *parse_feature_value(const char *feature_list, const char *feature, i
 
 int server_supports_hash(const char *desired, int *feature_supported)
 {
-       int offset = 0;
-       int len;
+       size_t offset = 0;
+       size_t len;
        const char *hash;
 
        hash = next_server_feature_value("object-format", &len, &offset);
@@ -617,12 +671,12 @@ int parse_feature_request(const char *feature_list, const char *feature)
        return !!parse_feature_value(feature_list, feature, NULL, NULL);
 }
 
-static const char *next_server_feature_value(const char *feature, int *len, int *offset)
+static const char *next_server_feature_value(const char *feature, size_t *len, size_t *offset)
 {
        return parse_feature_value(server_capabilities_v1, feature, len, offset);
 }
 
-const char *server_feature_value(const char *feature, int *len)
+const char *server_feature_value(const char *feature, size_t *len)
 {
        return parse_feature_value(server_capabilities_v1, feature, len, NULL);
 }
@@ -911,7 +965,7 @@ static struct child_process *git_tcp_connect(int fd[2], char *host, int flags)
 static char *git_proxy_command;
 
 static int git_proxy_command_options(const char *var, const char *value,
-               void *cb)
+               const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "core.gitproxy")) {
                const char *for_pos;
@@ -957,7 +1011,7 @@ static int git_proxy_command_options(const char *var, const char *value,
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static int git_use_proxy(const char *host)
@@ -1359,6 +1413,7 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
  * the connection failed).
  */
 struct child_process *git_connect(int fd[2], const char *url,
+                                 const char *name,
                                  const char *prog, int flags)
 {
        char *hostandport, *path;
@@ -1368,10 +1423,11 @@ struct child_process *git_connect(int fd[2], const char *url,
 
        /*
         * NEEDSWORK: If we are trying to use protocol v2 and we are planning
-        * to perform a push, then fallback to v0 since the client doesn't know
-        * how to push yet using v2.
+        * to perform any operation that doesn't involve upload-pack (i.e., a
+        * fetch, ls-remote, etc), then fallback to v0 since we don't know how
+        * to do anything else (like push or remote archive) via v2.
         */
-       if (version == protocol_v2 && !strcmp("git-receive-pack", prog))
+       if (version == protocol_v2 && strcmp("git-upload-pack", name))
                version = protocol_v0;
 
        /* Without this we cannot rely on waitpid() to tell
index c53586e929e8b6f11194d9aeb9934e0fbecd5983..1645126c17f889659d84174210a43939ef67507f 100644 (file)
--- a/connect.h
+++ b/connect.h
@@ -7,20 +7,21 @@
 #define CONNECT_DIAG_URL      (1u << 1)
 #define CONNECT_IPV4          (1u << 2)
 #define CONNECT_IPV6          (1u << 3)
-struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
+struct child_process *git_connect(int fd[2], const char *url, const char *name, const char *prog, int flags);
 int finish_connect(struct child_process *conn);
 int git_connection_is_socket(struct child_process *conn);
 int server_supports(const char *feature);
 int parse_feature_request(const char *features, const char *feature);
-const char *server_feature_value(const char *feature, int *len_ret);
+const char *server_feature_value(const char *feature, size_t *len_ret);
 int url_is_local_not_ssh(const char *url);
 
 struct packet_reader;
 enum protocol_version discover_version(struct packet_reader *reader);
 
 int server_supports_hash(const char *desired, int *feature_supported);
-const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset);
-int server_supports_v2(const char *c, int die_on_error);
+const char *parse_feature_value(const char *feature_list, const char *feature, size_t *lenp, size_t *offset);
+int server_supports_v2(const char *c);
+void ensure_server_supports_v2(const char *c);
 int server_feature_v2(const char *c, const char **v);
 int server_supports_feature(const char *c, const char *feature,
                            int die_on_error);
index 74a20cb32e7c7bfa4527addf0e83c2284b111745..8f89376dbcf30cd2cf69c3d2eaa446e47c9f4ae8 100644 (file)
@@ -1,5 +1,7 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "connected.h"
@@ -54,7 +56,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
                strbuf_release(&idx_file);
        }
 
-       if (has_promisor_remote()) {
+       if (repo_has_promisor_remote(the_repository)) {
                /*
                 * For partial clones, we don't want to have to do a regular
                 * connectivity check because we have to enumerate and exclude
@@ -85,6 +87,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
 promisor_pack_found:
                        ;
                } while ((oid = fn(cb_data)) != NULL);
+               free(new_pack);
                return 0;
        }
 
@@ -96,10 +99,13 @@ no_promisor_pack_found:
        strvec_push(&rev_list.args,"rev-list");
        strvec_push(&rev_list.args, "--objects");
        strvec_push(&rev_list.args, "--stdin");
-       if (has_promisor_remote())
+       if (repo_has_promisor_remote(the_repository))
                strvec_push(&rev_list.args, "--exclude-promisor-objects");
        if (!opt->is_deepening_fetch) {
                strvec_push(&rev_list.args, "--not");
+               if (opt->exclude_hidden_refs_section)
+                       strvec_pushf(&rev_list.args, "--exclude-hidden=%s",
+                                    opt->exclude_hidden_refs_section);
                strvec_push(&rev_list.args, "--all");
        }
        strvec_push(&rev_list.args, "--quiet");
@@ -118,8 +124,10 @@ no_promisor_pack_found:
        else
                rev_list.no_stderr = opt->quiet;
 
-       if (start_command(&rev_list))
+       if (start_command(&rev_list)) {
+               free(new_pack);
                return error(_("Could not run 'git rev-list'"));
+       }
 
        sigchain_push(SIGPIPE, SIG_IGN);
 
@@ -151,5 +159,6 @@ no_promisor_pack_found:
                err = error_errno(_("failed to close rev-list's stdin"));
 
        sigchain_pop(SIGPIPE);
+       free(new_pack);
        return finish_command(&rev_list) || err;
 }
index 6e59c92aa33c0c10067c0ed1c095c5ea9fa4f731..16b2c84f2e35fc931507477e4bc1d31573c82524 100644 (file)
@@ -46,6 +46,13 @@ struct check_connected_options {
         * during a fetch.
         */
        unsigned is_deepening_fetch : 1;
+
+       /*
+        * If not NULL, use `--exclude-hidden=$section` to exclude all refs
+        * hidden via the `$section.hideRefs` config from the set of
+        * already-reachable refs.
+        */
+       const char *exclude_hidden_refs_section;
 };
 
 #define CHECK_CONNECTED_INIT { 0 }
index 05f291c1f1d3d1018f390618816f94d0cd58951b..21d3d0e7dee46de8ee07ae69d9735b76643693db 100644 (file)
@@ -23,7 +23,7 @@ This is the same way as how I have been treating gitk, and to a
 lesser degree various foreign SCM interfaces, so you know the
 drill.
 
-I expect that things that start their life in the contrib/ area
+I expect things that start their life in the contrib/ area
 to graduate out of contrib/ once they mature, either by becoming
 projects on their own, or moving to the toplevel directory.  On
 the other hand, I expect I'll be proposing removal of disused
@@ -31,7 +31,7 @@ and inactive ones from time to time.
 
 If you have new things to add to this area, please first propose
 it on the git mailing list, and after a list discussion proves
-there are some general interests (it does not have to be a
+there is general interest (it does not have to be a
 list-wide consensus for a tool targeted to a relatively narrow
 audience -- for example I do not work with projects whose
 upstream is svn, so I have no use for git-svn myself, but it is
index ea2a531be87494d9db48fc5dd72aa5c6ffb606b2..804629c525b1b1978512c9d15b710bfebeef185e 100644 (file)
@@ -227,7 +227,7 @@ add_compile_definitions(GIT_HOST_CPU="${CMAKE_SYSTEM_PROCESSOR}")
 add_compile_definitions(SHA256_BLK INTERNAL_QSORT RUNTIME_PREFIX)
 add_compile_definitions(NO_OPENSSL SHA1_DC SHA1DC_NO_STANDARD_INCLUDES
                        SHA1DC_INIT_SAFE_HASH_DEFAULT=0
-                       SHA1DC_CUSTOM_INCLUDE_SHA1_C="cache.h"
+                       SHA1DC_CUSTOM_INCLUDE_SHA1_C="git-compat-util.h"
                        SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="git-compat-util.h" )
 list(APPEND compat_SOURCES sha1dc_git.c sha1dc/sha1.c sha1dc/ubc_check.c block-sha1/sha1.c sha256/block/sha256.c compat/qsort_s.c)
 
@@ -308,6 +308,8 @@ if(SUPPORTS_SIMPLE_IPC)
                add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)
+               list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)
+               list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)
 
                add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)
@@ -315,6 +317,8 @@ if(SUPPORTS_SIMPLE_IPC)
                add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)
+               list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)
+               list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)
 
                add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)
@@ -734,6 +738,15 @@ if(WIN32)
        else()
                message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}")
        endif()
+
+       add_executable(headless-git ${CMAKE_SOURCE_DIR}/compat/win32/headless.c)
+       if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+               target_link_options(headless-git PUBLIC -municode -Wl,-subsystem,windows)
+       elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+               target_link_options(headless-git PUBLIC /NOLOGO /ENTRY:wWinMainCRTStartup /SUBSYSTEM:WINDOWS)
+       else()
+               message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}")
+       endif()
 elseif(UNIX)
        target_link_libraries(common-main pthread rt)
 endif()
@@ -961,6 +974,35 @@ target_link_libraries(test-fake-ssh common-main)
 parse_makefile_for_sources(test-reftable_SOURCES "REFTABLE_TEST_OBJS")
 list(TRANSFORM test-reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 
+#unit-tests
+add_library(unit-test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
+
+parse_makefile_for_scripts(unit_test_PROGRAMS "UNIT_TEST_PROGRAMS" "")
+foreach(unit_test ${unit_test_PROGRAMS})
+       add_executable("${unit_test}" "${CMAKE_SOURCE_DIR}/t/unit-tests/${unit_test}.c")
+       target_link_libraries("${unit_test}" unit-test-lib common-main)
+       set_target_properties("${unit_test}"
+               PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+       if(MSVC)
+               set_target_properties("${unit_test}"
+                       PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+               set_target_properties("${unit_test}"
+                       PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+       endif()
+       list(APPEND PROGRAMS_BUILT "${unit_test}")
+
+       # t-basic intentionally fails tests, to validate the unit-test infrastructure.
+       # Therefore, it should only be run as part of t0080, which verifies that it
+       # fails only in the expected ways.
+       #
+       # All other unit tests should be run.
+       if(NOT ${unit_test} STREQUAL "t-basic")
+               add_test(NAME "t.unit-tests.${unit_test}"
+                       COMMAND "./${unit_test}"
+                       WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/t/unit-tests/bin)
+       endif()
+endforeach()
+
 #test-tool
 parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
 
@@ -1021,7 +1063,6 @@ set(NO_PERL )
 set(NO_PTHREADS )
 set(NO_PYTHON )
 set(PAGER_ENV "LESS=FRX LV=-c")
-set(DC_SHA1 YesPlease)
 set(RUNTIME_PREFIX true)
 set(NO_GETTEXT )
 
@@ -1057,7 +1098,6 @@ file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PERL='${NO_PERL}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PTHREADS='${NO_PTHREADS}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_UNIX_SOCKETS='${NO_UNIX_SOCKETS}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PAGER_ENV='${PAGER_ENV}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "DC_SHA1='${DC_SHA1}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "X='${EXE_EXTENSION}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_GETTEXT='${NO_GETTEXT}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "RUNTIME_PREFIX='${RUNTIME_PREFIX}'\n")
@@ -1070,29 +1110,30 @@ endif()
 #Make the tests work when building out of the source tree
 get_filename_component(CACHE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../CMakeCache.txt ABSOLUTE)
 if(NOT ${CMAKE_BINARY_DIR}/CMakeCache.txt STREQUAL ${CACHE_PATH})
-       file(RELATIVE_PATH BUILD_DIR_RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}/CMakeCache.txt)
-       string(REPLACE "/CMakeCache.txt" "" BUILD_DIR_RELATIVE ${BUILD_DIR_RELATIVE})
        #Setting the build directory in test-lib.sh before running tests
        file(WRITE ${CMAKE_BINARY_DIR}/CTestCustom.cmake
-               "file(STRINGS ${CMAKE_SOURCE_DIR}/t/test-lib.sh GIT_BUILD_DIR_REPL REGEX \"GIT_BUILD_DIR=(.*)\")\n"
-               "file(STRINGS ${CMAKE_SOURCE_DIR}/t/test-lib.sh content NEWLINE_CONSUME)\n"
-               "string(REPLACE \"\${GIT_BUILD_DIR_REPL}\" \"GIT_BUILD_DIR=\\\"$TEST_DIRECTORY/../${BUILD_DIR_RELATIVE}\\\"\" content \"\${content}\")\n"
-               "file(WRITE ${CMAKE_SOURCE_DIR}/t/test-lib.sh \${content})")
+               "file(WRITE ${CMAKE_SOURCE_DIR}/GIT-BUILD-DIR \"${CMAKE_BINARY_DIR}\")")
        #misc copies
        file(COPY ${CMAKE_SOURCE_DIR}/t/chainlint.pl DESTINATION ${CMAKE_BINARY_DIR}/t/)
        file(COPY ${CMAKE_SOURCE_DIR}/po/is.po DESTINATION ${CMAKE_BINARY_DIR}/po/)
-       file(COPY ${CMAKE_SOURCE_DIR}/mergetools/tkdiff DESTINATION ${CMAKE_BINARY_DIR}/mergetools/)
+       file(GLOB mergetools "${CMAKE_SOURCE_DIR}/mergetools/*")
+       file(COPY ${mergetools} DESTINATION ${CMAKE_BINARY_DIR}/mergetools/)
        file(COPY ${CMAKE_SOURCE_DIR}/contrib/completion/git-prompt.sh DESTINATION ${CMAKE_BINARY_DIR}/contrib/completion/)
        file(COPY ${CMAKE_SOURCE_DIR}/contrib/completion/git-completion.bash DESTINATION ${CMAKE_BINARY_DIR}/contrib/completion/)
 endif()
 
-file(GLOB test_scipts "${CMAKE_SOURCE_DIR}/t/t[0-9]*.sh")
+file(GLOB test_scripts "${CMAKE_SOURCE_DIR}/t/t[0-9]*.sh")
 
 #test
-foreach(tsh ${test_scipts})
-       add_test(NAME ${tsh}
-               COMMAND ${SH_EXE} ${tsh}
+foreach(tsh ${test_scripts})
+       string(REGEX REPLACE ".*/(.*)\\.sh" "\\1" test_name ${tsh})
+       add_test(NAME "t.suite.${test_name}"
+               COMMAND ${SH_EXE} ${tsh} --no-bin-wrappers --no-chain-lint -vx
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/t)
 endforeach()
 
+# This test script takes an extremely long time and is known to time out even
+# on fast machines because it requires in excess of one hour to run
+set_tests_properties("t.suite.t7112-reset-submodule" PROPERTIES TIMEOUT 4000)
+
 endif()#BUILD_TESTING
index 1a25789d28513bb5f9dc9b0a625d75fac6e89092..b2e68a16715e3987f7bb05d69cad39e6b457a30c 100644 (file)
@@ -76,7 +76,7 @@ sub createProject {
 
     my $libs_release = "\n    ";
     my $libs_debug = "\n    ";
-    if (!$static_library) {
+    if (!$static_library && $name ne 'headless-git') {
       $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}}));
       $libs_debug = $libs_release;
       $libs_debug =~ s/zlib\.lib/zlibd\.lib/g;
@@ -230,7 +230,7 @@ EOM
     print F << "EOM";
   </ItemGroup>
 EOM
-    if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') {
+    if ((!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') && !($name =~ /headless-git/)) {
       my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"};
       my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"};
       my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"};
index ed6c45988a38b0f45a2b67a15cb18a221ad911c7..069be7e4befcd793430b338f930d103e41c4802a 100755 (executable)
@@ -371,6 +371,7 @@ sub handleLinkLine
 #    exit(1);
     foreach (@objfiles) {
         my $sourcefile = $_;
+        $sourcefile =~ s/^headless-git\.o$/compat\/win32\/headless.c/;
         $sourcefile =~ s/\.o$/.c/;
         push(@sources, $sourcefile);
         push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}});
index d3f29646dc3afa52cb960f190fe2f62e9887a501..1d45c0a40c81ee6f8501cac7a204a0b9cdca376b 100644 (file)
@@ -1 +1 @@
-*.patch*
+*.patch
index f0e80bd7f037731530e7099dc2b27985dd880be3..055ad0e06a70e2a23c88c1cf34782d679d42426a 100644 (file)
@@ -1,7 +1,9 @@
-This directory provides examples of Coccinelle (http://coccinelle.lip6.fr/)
-semantic patches that might be useful to developers.
+= coccinelle
 
-There are two types of semantic patches:
+This directory provides Coccinelle (http://coccinelle.lip6.fr/) semantic patches
+that might be useful to developers.
+
+==  Types of semantic patches
 
  * Using the semantic transformation to check for bad patterns in the code;
    The target 'make coccicheck' is designed to check for these patterns and
@@ -41,3 +43,82 @@ There are two types of semantic patches:
 
    This allows to expose plans of pending large scale refactorings without
    impacting the bad pattern checks.
+
+== Git-specific tips & things to know about how we run "spatch":
+
+ * The "make coccicheck" will piggy-back on
+   "COMPUTE_HEADER_DEPENDENCIES". If you've built a given object file
+   the "coccicheck" target will consider its depednency to decide if
+   it needs to re-run on the corresponding source file.
+
+   This means that a "make coccicheck" will re-compile object files
+   before running. This might be unexpected, but speeds up the run in
+   the common case, as e.g. a change to "column.h" won't require all
+   coccinelle rules to be re-run against "grep.c" (or another file
+   that happens not to use "column.h").
+
+   To disable this behavior use the "SPATCH_USE_O_DEPENDENCIES=NoThanks"
+   flag.
+
+ * To speed up our rules the "make coccicheck" target will by default
+   concatenate all of the *.cocci files here into an "ALL.cocci", and
+   apply it to each source file.
+
+   This makes the run faster, as we don't need to run each rule
+   against each source file. See the Makefile for further discussion,
+   this behavior can be disabled with "SPATCH_CONCAT_COCCI=".
+
+   But since they're concatenated any <id> in the <rulname> (e.g. "@
+   my_name", v.s. anonymous "@@") needs to be unique across all our
+   *.cocci files. You should only need to name rules if other rules
+   depend on them (currently only one rule is named).
+
+ * To speed up incremental runs even more use the "spatchcache" tool
+   in this directory as your "SPATCH". It aimns to be a "ccache" for
+   coccinelle, and piggy-backs on "COMPUTE_HEADER_DEPENDENCIES".
+
+   It caches in Redis by default, see it source for a how-to.
+
+   In one setup with a primed cache "make coccicheck" followed by a
+   "make clean && make" takes around 10s to run, but 2m30s with the
+   default of "SPATCH_CONCAT_COCCI=Y".
+
+   With "SPATCH_CONCAT_COCCI=" the total runtime is around ~6m, sped
+   up to ~1m with "spatchcache".
+
+   Most of the 10s (or ~1m) being spent on re-running "spatch" on
+   files we couldn't cache, as we didn't compile them (in contrib/*
+   and compat/* mostly).
+
+   The absolute times will differ for you, but the relative speedup
+   from caching should be on that order.
+
+== Authoring and reviewing coccinelle changes
+
+* When a .cocci is made, both the Git changes and .cocci file should be
+  reviewed. When reviewing such a change, do your best to understand the .cocci
+  changes (e.g. by asking the author to explain the change) and be explicit
+  about your understanding of the changes. This helps us decide whether input
+  from coccinelle experts is needed or not. If you aren't sure of the cocci
+  changes, indicate what changes you actively endorse and leave an Acked-by
+  (instead of Reviewed-by).
+
+* Authors should consider that reviewers may not be coccinelle experts, thus the
+  the .cocci changes may not be self-evident. A plain text description of the
+  changes is strongly encouraged, especially when using more esoteric features
+  of the language.
+
+* .cocci rules should target only the problem it is trying to solve; "collateral
+  damage" is not allowed. Reviewers should look out and flag overly-broad rules.
+
+* Consider the cost-benefit ratio of .cocci changes. In particular, consider the
+  effect on the runtime of "make coccicheck", and how often your .cocci check
+  will catch something valuable. As a rule of thumb, rules that can bail early
+  if a file doesn't have a particular token will have a small impact on runtime,
+  and vice-versa.
+
+* .cocci files used for refactoring should be temporarily kept in-tree to aid
+  the refactoring of out-of-tree code (e.g. in-flight topics). Periodically
+  evaluate the cost-benefit ratio to determine when the file should be removed.
+  For example, consider how many out-of-tree users are left and how much this
+  slows down "make coccicheck".
index aa759379508895ba002c531a13bde3f8995f9251..27a3b479c94e5cb42817c3d2bd1fff6f270ef583 100644 (file)
@@ -94,3 +94,10 @@ expression n != 1;
 @@
 - ptr = xcalloc(n, \( sizeof(*ptr) \| sizeof(T) \) )
 + CALLOC_ARRAY(ptr, n)
+
+@@
+expression dst, src, n;
+@@
+-ALLOC_ARRAY(dst, n);
+-COPY_ARRAY(dst, src, n);
++DUP_ARRAY(dst, src, n);
diff --git a/contrib/coccinelle/config_fn_ctx.pending.cocci b/contrib/coccinelle/config_fn_ctx.pending.cocci
new file mode 100644 (file)
index 0000000..6d3d100
--- /dev/null
@@ -0,0 +1,144 @@
+@ get_fn @
+identifier fn, R;
+@@
+(
+(
+git_config_from_file
+|
+git_config_from_file_with_options
+|
+git_config_from_mem
+|
+git_config_from_blob_oid
+|
+read_early_config
+|
+read_very_early_config
+|
+config_with_options
+|
+git_config
+|
+git_protected_config
+|
+config_from_gitmodules
+)
+  (fn, ...)
+|
+repo_config(R, fn, ...)
+)
+
+@ extends get_fn @
+identifier C1, C2, D;
+@@
+int fn(const char *C1, const char *C2,
++ const struct config_context *ctx,
+  void *D);
+
+@ extends get_fn @
+@@
+int fn(const char *, const char *,
++ const struct config_context *,
+  void *);
+
+@ extends get_fn @
+// Don't change fns that look like callback fns but aren't
+identifier fn2 != tar_filter_config && != git_diff_heuristic_config &&
+  != git_default_submodule_config && != git_color_config &&
+  != bundle_list_update && != parse_object_filter_config;
+identifier C1, C2, D1, D2, S;
+attribute name UNUSED;
+@@
+int fn(const char *C1, const char *C2,
++ const struct config_context *ctx,
+  void *D1) {
+<+...
+(
+fn2(C1, C2
++ , ctx
+, D2);
+|
+if(fn2(C1, C2
++ , ctx
+, D2) < 0) { ... }
+|
+return fn2(C1, C2
++ , ctx
+, D2);
+|
+S = fn2(C1, C2
++ , ctx
+, D2);
+)
+...+>
+  }
+
+@ extends get_fn@
+identifier C1, C2, D;
+attribute name UNUSED;
+@@
+int fn(const char *C1, const char *C2,
++ const struct config_context *ctx UNUSED,
+  void *D) {...}
+
+
+// The previous rules don't catch all callbacks, especially if they're defined
+// in a separate file from the git_config() call. Fix these manually.
+@@
+identifier C1, C2, D;
+attribute name UNUSED;
+@@
+int
+(
+git_ident_config
+|
+urlmatch_collect_fn
+|
+write_one_config
+|
+forbid_remote_url
+|
+credential_config_callback
+)
+  (const char *C1, const char *C2,
++ const struct config_context *ctx UNUSED,
+  void *D) {...}
+
+@@
+identifier C1, C2, D, D2, S, fn2;
+@@
+int
+(
+http_options
+|
+git_status_config
+|
+git_commit_config
+|
+git_default_core_config
+|
+grep_config
+)
+  (const char *C1, const char *C2,
++ const struct config_context *ctx,
+  void *D) {
+<+...
+(
+fn2(C1, C2
++ , ctx
+, D2);
+|
+if(fn2(C1, C2
++ , ctx
+, D2) < 0) { ... }
+|
+return fn2(C1, C2
++ , ctx
+, D2);
+|
+S = fn2(C1, C2
++ , ctx
+, D2);
+)
+...+>
+  }
diff --git a/contrib/coccinelle/git_config_number.cocci b/contrib/coccinelle/git_config_number.cocci
new file mode 100644 (file)
index 0000000..7b57dce
--- /dev/null
@@ -0,0 +1,27 @@
+@@
+identifier C1, C2, C3;
+@@
+(
+(
+git_config_int
+|
+git_config_int64
+|
+git_config_ulong
+|
+git_config_ssize_t
+)
+  (C1, C2
++ , ctx->kvi
+  )
+|
+(
+git_configset_get_value
+|
+git_config_bool_or_int
+)
+  (C1, C2
++ , ctx->kvi
+ , C3
+  )
+)
index d69e120ccffc04b29e7e6f83ac382f3492b82037..c5dbb4557b56b130d1ed518af1ccdff6c112d7a6 100644 (file)
@@ -1,4 +1,4 @@
-@ hashmap_entry_init_usage @
+@@
 expression E;
 struct hashmap_entry HME;
 @@
diff --git a/contrib/coccinelle/index-compatibility.cocci b/contrib/coccinelle/index-compatibility.cocci
new file mode 100644 (file)
index 0000000..31e36cf
--- /dev/null
@@ -0,0 +1,157 @@
+// the_index.* variables
+@@
+identifier AC = active_cache;
+identifier AN = active_nr;
+identifier ACC = active_cache_changed;
+identifier ACT = active_cache_tree;
+@@
+(
+- AC
++ the_index.cache
+|
+- AN
++ the_index.cache_nr
+|
+- ACC
++ the_index.cache_changed
+|
+- ACT
++ the_index.cache_tree
+)
+
+// "the_repository" simple cases
+@@
+@@
+(
+- read_cache
++ repo_read_index
+|
+- read_cache_unmerged
++ repo_read_index_unmerged
+|
+- hold_locked_index
++ repo_hold_locked_index
+)
+  (
++ the_repository,
+  ...)
+
+// "the_repository" special-cases
+@@
+@@
+(
+- read_cache_preload
++ repo_read_index_preload
+)
+  (
++ the_repository,
+  ...
++ , 0
+  )
+
+// "the_index" simple cases
+@@
+@@
+(
+- is_cache_unborn
++ is_index_unborn
+|
+- unmerged_cache
++ unmerged_index
+|
+- rename_cache_entry_at
++ rename_index_entry_at
+|
+- chmod_cache_entry
++ chmod_index_entry
+|
+- cache_file_exists
++ index_file_exists
+|
+- cache_name_is_other
++ index_name_is_other
+|
+- unmerge_cache_entry_at
++ unmerge_index_entry_at
+|
+- add_to_cache
++ add_to_index
+|
+- add_file_to_cache
++ add_file_to_index
+|
+- add_cache_entry
++ add_index_entry
+|
+- remove_file_from_cache
++ remove_file_from_index
+|
+- ce_match_stat
++ ie_match_stat
+|
+- ce_modified
++ ie_modified
+|
+- resolve_undo_clear
++ resolve_undo_clear_index
+|
+- cache_name_pos
++ index_name_pos
+|
+- update_main_cache_tree
++ cache_tree_update
+|
+- discard_cache
++ discard_index
+)
+  (
++ &the_index,
+  ...)
+
+@@
+@@
+(
+- refresh_and_write_cache
++ repo_refresh_and_write_index
+)
+  (
++ the_repository,
+  ...
++ , NULL, NULL, NULL
+  )
+
+// "the_index" special-cases
+@@
+@@
+(
+- read_cache_from
++ read_index_from
+)
+  (
++ &the_index,
+  ...
++ , get_git_dir()
+  )
+
+@@
+@@
+(
+- refresh_cache
++ refresh_index
+)
+  (
++ &the_index,
+  ...
++ , NULL, NULL, NULL
+  )
+
+@@
+expression O;
+@@
+- write_cache_as_tree
++ write_index_as_tree
+  (
+- O,
++ O, &the_index, get_index_file(),
+  ...
+  )
index 7fe1e8d2d9a0167bec1289da81bbe4b4cae6fe4f..ae42cb07302d5e4e480f85b1a1bac9e05474966f 100644 (file)
@@ -1,4 +1,4 @@
-@ preincrement @
+@@
 identifier i;
 @@
 - ++i > 1
diff --git a/contrib/coccinelle/spatchcache b/contrib/coccinelle/spatchcache
new file mode 100755 (executable)
index 0000000..29e9352
--- /dev/null
@@ -0,0 +1,304 @@
+#!/bin/sh
+#
+# spatchcache: a poor-man's "ccache"-alike for "spatch" in git.git
+#
+# This caching command relies on the peculiarities of the Makefile
+# driving "spatch" in git.git, in particular if we invoke:
+#
+#      make
+#      # See "spatchCache.cacheWhenStderr" for why "--very-quiet" is
+#      # used
+#      make coccicheck SPATCH_FLAGS=--very-quiet
+#
+# We can with COMPUTE_HEADER_DEPENDENCIES (auto-detected as true with
+# "gcc" and "clang") write e.g. a .depend/grep.o.d for grep.c, when we
+# compile grep.o.
+#
+# The .depend/grep.o.d will have the full header dependency tree of
+# grep.c, and we can thus cache the output of "spatch" by:
+#
+#      1. Hashing all of those files
+#      2. Hashing our source file, and the *.cocci rule we're
+#         applying
+#      3. Running spatch, if suggests no changes (by far the common
+#         case) we invoke "spatchCache.getCmd" and
+#         "spatchCache.setCmd" with a hash SHA-256 to ask "does this
+#         ID have no changes" or "say that ID had no changes>
+#      4. If no "spatchCache.{set,get}Cmd" is specified we'll use
+#         "redis-cli" and maintain a SET called "spatch-cache". Set
+#         appropriate redis memory policies to keep it from growing
+#         out of control.
+#
+# This along with the general incremental "make" support for
+# "contrib/coccinelle" makes it viable to (re-)run coccicheck
+# e.g. when merging integration branches.
+#
+# Note that the "--very-quiet" flag is currently critical. The cache
+# will refuse to cache anything that has output on STDERR (which might
+# be errors from spatch), but see spatchCache.cacheWhenStderr below.
+#
+# The STDERR (and exit code) could in principle be cached (as with
+# ccache), but then the simple structure in the Redis cache would need
+# to change, so just supply "--very-quiet" for now.
+#
+# To use this, simply set SPATCH to
+# contrib/coccinelle/spatchcache. Then optionally set:
+#
+#      [spatchCache]
+#              # Optional: path to a custom spatch
+#              spatch = ~/g/coccicheck/spatch.opt
+#
+# As well as this trace config (debug implies trace):
+#
+#              cacheWhenStderr = true
+#              trace = false
+#              debug = false
+#
+# The ".depend/grep.o.d" can also be customized, as a string that will
+# be eval'd, it has access to a "$dirname" and "$basename":
+#
+#      [spatchCache]
+#              dependFormat = "$dirname/.depend/${basename%.c}.o.d"
+#
+# Setting "trace" to "true" allows for seeing when we have a cache HIT
+# or MISS. To debug whether the cache is working do that, and run e.g.:
+#
+#      redis-cli FLUSHALL
+#      <make && make coccicheck, as above>
+#      grep -hore HIT -e MISS -e SET -e NOCACHE -e CANTCACHE .build/contrib/coccinelle | sort | uniq -c
+#          600 CANTCACHE
+#         7365 MISS
+#         7365 SET
+#
+# A subsequent "make cocciclean && make coccicheck" should then have
+# all "HIT"'s and "CANTCACHE"'s.
+#
+# The "spatchCache.cacheWhenStderr" option is critical when using
+# spatchCache.{trace,debug} to debug whether something is set in the
+# cache, as we'll write to the spatch logs in .build/* we'd otherwise
+# always emit a NOCACHE.
+#
+# Reading the config can make the command much slower, to work around
+# this the config can be set in the environment, with environment
+# variable name corresponding to the config key. "default" can be used
+# to use whatever's the script default, e.g. setting
+# spatchCache.cacheWhenStderr=true and deferring to the defaults for
+# the rest is:
+#
+#      export GIT_CONTRIB_SPATCHCACHE_DEBUG=default
+#      export GIT_CONTRIB_SPATCHCACHE_TRACE=default
+#      export GIT_CONTRIB_SPATCHCACHE_CACHEWHENSTDERR=true
+#      export GIT_CONTRIB_SPATCHCACHE_SPATCH=default
+#      export GIT_CONTRIB_SPATCHCACHE_DEPENDFORMAT=default
+#      export GIT_CONTRIB_SPATCHCACHE_SETCMD=default
+#      export GIT_CONTRIB_SPATCHCACHE_GETCMD=default
+
+set -e
+
+env_or_config () {
+       env="$1"
+       shift
+       if test "$env" = "default"
+       then
+               # Avoid expensive "git config" invocation
+               return
+       elif test -n "$env"
+       then
+               echo "$env"
+       else
+               git config $@ || :
+       fi
+}
+
+## Our own configuration & options
+debug=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_DEBUG" --bool "spatchCache.debug")
+if test "$debug" != "true"
+then
+       debug=
+fi
+if test -n "$debug"
+then
+       set -x
+fi
+
+trace=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_TRACE" --bool "spatchCache.trace")
+if test "$trace" != "true"
+then
+       trace=
+fi
+if test -n "$debug"
+then
+       # debug implies trace
+       trace=true
+fi
+
+cacheWhenStderr=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_CACHEWHENSTDERR" --bool "spatchCache.cacheWhenStderr")
+if test "$cacheWhenStderr" != "true"
+then
+       cacheWhenStderr=
+fi
+
+trace_it () {
+       if test -z "$trace"
+       then
+               return
+       fi
+       echo "$@" >&2
+}
+
+spatch=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_SPATCH" --path "spatchCache.spatch")
+if test -n "$spatch"
+then
+       if test -n "$debug"
+       then
+               trace_it "custom spatchCache.spatch='$spatch'"
+       fi
+else
+       spatch=spatch
+fi
+
+dependFormat='$dirname/.depend/${basename%.c}.o.d'
+dependFormatCfg=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_DEPENDFORMAT" "spatchCache.dependFormat")
+if test -n "$dependFormatCfg"
+then
+       dependFormat="$dependFormatCfg"
+fi
+
+set=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_SETCMD" "spatchCache.setCmd")
+get=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_GETCMD" "spatchCache.getCmd")
+
+## Parse spatch()-like command-line for caching info
+arg_sp=
+arg_file=
+args="$@"
+spatch_opts() {
+       while test $# != 0
+       do
+               arg_file="$1"
+               case "$1" in
+               --sp-file)
+                       arg_sp="$2"
+                       ;;
+               esac
+               shift
+       done
+}
+spatch_opts "$@"
+if ! test -f "$arg_file"
+then
+       arg_file=
+fi
+
+hash_for_cache() {
+       # Parameters that should affect the cache
+       echo "args=$args"
+       echo "config spatchCache.spatch=$spatch"
+       echo "config spatchCache.debug=$debug"
+       echo "config spatchCache.trace=$trace"
+       echo "config spatchCache.cacheWhenStderr=$cacheWhenStderr"
+       echo
+
+       # Our target file and its dependencies
+       git hash-object "$1" "$2" $(grep -E -o '^[^:]+:$' "$3" | tr -d ':')
+}
+
+# Sanity checks
+if ! test -f "$arg_sp" && ! test -f "$arg_file"
+then
+       echo $0: no idea how to cache "$@" >&2
+       exit 128
+fi
+
+# Main logic
+dirname=$(dirname "$arg_file")
+basename=$(basename "$arg_file")
+eval "dep=$dependFormat"
+
+if ! test -f "$dep"
+then
+       trace_it "$0: CANTCACHE have no '$dep' for '$arg_file'!"
+       exec "$spatch" "$@"
+fi
+
+if test -n "$debug"
+then
+       trace_it "$0: The full cache input for '$arg_sp' '$arg_file' '$dep'"
+       hash_for_cache "$arg_sp" "$arg_file" "$dep" >&2
+fi
+sum=$(hash_for_cache "$arg_sp" "$arg_file" "$dep" | git hash-object --stdin)
+
+trace_it "$0: processing '$arg_file' with '$arg_sp' rule, and got hash '$sum' for it + '$dep'"
+
+getret=
+if test -z "$get"
+then
+       if test $(redis-cli SISMEMBER spatch-cache "$sum") = 1
+       then
+               getret=0
+       else
+               getret=1
+       fi
+else
+       $set "$sum"
+       getret=$?
+fi
+
+if test "$getret" = 0
+then
+       trace_it "$0: HIT for '$arg_file' with '$arg_sp'"
+       exit 0
+else
+       trace_it "$0: MISS: for '$arg_file' with '$arg_sp'"
+fi
+
+out="$(mktemp)"
+err="$(mktemp)"
+
+set +e
+"$spatch" "$@" >"$out" 2>>"$err"
+ret=$?
+cat "$out"
+cat "$err" >&2
+set -e
+
+nocache=
+if test $ret != 0
+then
+       nocache="exited non-zero: $ret"
+elif test -s "$out"
+then
+       nocache="had patch output"
+elif test -z "$cacheWhenStderr" && test -s "$err"
+then
+       nocache="had stderr (use --very-quiet or spatchCache.cacheWhenStderr=true?)"
+fi
+
+if test -n "$nocache"
+then
+       trace_it "$0: NOCACHE ($nocache): for '$arg_file' with '$arg_sp'"
+       exit "$ret"
+fi
+
+trace_it "$0: SET: for '$arg_file' with '$arg_sp'"
+
+setret=
+if test -z "$set"
+then
+       if test $(redis-cli SADD spatch-cache "$sum") = 1
+       then
+               setret=0
+       else
+               setret=1
+       fi
+else
+       "$set" "$sum"
+       setret=$?
+fi
+
+if test "$setret" != 0
+then
+       echo "FAILED to set '$sum' in cache!" >&2
+       exit 128
+fi
+
+exit "$ret"
index 0970d98ad72f82bab1fc2b6ea6238c48526fa112..5f06105df6db7b790a89d3af8633c511d68bc85b 100644 (file)
@@ -1,4 +1,4 @@
-@ strbuf_addf_with_format_only @
+@@
 expression E;
 constant fmt !~ "%";
 @@
index a0934d1fdaf07e9748fb2055e4a47b029bcf8948..522177afb66354110b6a63a044d183c512ed1662 100644 (file)
@@ -1,4 +1,4 @@
-@ swap_with_declaration @
+@@
 type T;
 identifier tmp;
 T a, b;
diff --git a/contrib/coccinelle/tests/unused.c b/contrib/coccinelle/tests/unused.c
deleted file mode 100644 (file)
index 8294d73..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-void test_strbuf(void)
-{
-       struct strbuf sb1 = STRBUF_INIT;
-       struct strbuf sb2 = STRBUF_INIT;
-       struct strbuf sb3 = STRBUF_INIT;
-       struct strbuf sb4 = STRBUF_INIT;
-       struct strbuf sb5;
-       struct strbuf sb6 = { 0 };
-       struct strbuf sb7 = STRBUF_INIT;
-       struct strbuf sb8 = STRBUF_INIT;
-       struct strbuf *sp1;
-       struct strbuf *sp2;
-       struct strbuf *sp3;
-       struct strbuf *sp4 = xmalloc(sizeof(struct strbuf));
-       struct strbuf *sp5 = xmalloc(sizeof(struct strbuf));
-       struct strbuf *sp6 = xmalloc(sizeof(struct strbuf));
-       struct strbuf *sp7;
-
-       strbuf_init(&sb5, 0);
-       strbuf_init(sp1, 0);
-       strbuf_init(sp2, 0);
-       strbuf_init(sp3, 0);
-       strbuf_init(sp4, 0);
-       strbuf_init(sp5, 0);
-       strbuf_init(sp6, 0);
-       strbuf_init(sp7, 0);
-       sp7 = xmalloc(sizeof(struct strbuf));
-
-       use_before(&sb3);
-       use_as_str("%s", sb7.buf);
-       use_as_str("%s", sp1->buf);
-       use_as_str("%s", sp6->buf);
-       pass_pp(&sp3);
-
-       strbuf_release(&sb1);
-       strbuf_reset(&sb2);
-       strbuf_release(&sb3);
-       strbuf_release(&sb4);
-       strbuf_release(&sb5);
-       strbuf_release(&sb6);
-       strbuf_release(&sb7);
-       strbuf_release(sp1);
-       strbuf_release(sp2);
-       strbuf_release(sp3);
-       strbuf_release(sp4);
-       strbuf_release(sp5);
-       strbuf_release(sp6);
-       strbuf_release(sp7);
-
-       use_after(&sb4);
-
-       if (when_strict())
-               return;
-       strbuf_release(&sb8);
-}
-
-void test_other(void)
-{
-       struct string_list l = STRING_LIST_INIT_DUP;
-       struct strbuf sb = STRBUF_INIT;
-
-       string_list_clear(&l, 0);
-       string_list_clear(&sb, 0);
-}
-
-void test_worktrees(void)
-{
-       struct worktree **w1 = get_worktrees();
-       struct worktree **w2 = get_worktrees();
-       struct worktree **w3;
-       struct worktree **w4;
-
-       w3 = get_worktrees();
-       w4 = get_worktrees();
-
-       use_it(w4);
-
-       free_worktrees(w1);
-       free_worktrees(w2);
-       free_worktrees(w3);
-       free_worktrees(w4);
-}
diff --git a/contrib/coccinelle/tests/unused.res b/contrib/coccinelle/tests/unused.res
deleted file mode 100644 (file)
index 6d3e745..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-void test_strbuf(void)
-{
-       struct strbuf sb3 = STRBUF_INIT;
-       struct strbuf sb4 = STRBUF_INIT;
-       struct strbuf sb7 = STRBUF_INIT;
-       struct strbuf *sp1;
-       struct strbuf *sp3;
-       struct strbuf *sp6 = xmalloc(sizeof(struct strbuf));
-       strbuf_init(sp1, 0);
-       strbuf_init(sp3, 0);
-       strbuf_init(sp6, 0);
-
-       use_before(&sb3);
-       use_as_str("%s", sb7.buf);
-       use_as_str("%s", sp1->buf);
-       use_as_str("%s", sp6->buf);
-       pass_pp(&sp3);
-
-       strbuf_release(&sb3);
-       strbuf_release(&sb4);
-       strbuf_release(&sb7);
-       strbuf_release(sp1);
-       strbuf_release(sp3);
-       strbuf_release(sp6);
-
-       use_after(&sb4);
-
-       if (when_strict())
-               return;
-}
-
-void test_other(void)
-{
-}
-
-void test_worktrees(void)
-{
-       struct worktree **w4;
-
-       w4 = get_worktrees();
-
-       use_it(w4);
-
-       free_worktrees(w4);
-}
diff --git a/contrib/coccinelle/the_repository.cocci b/contrib/coccinelle/the_repository.cocci
new file mode 100644 (file)
index 0000000..765ad68
--- /dev/null
@@ -0,0 +1,123 @@
+// Fully migrated "the_repository" additions
+@@
+@@
+(
+// cache.h
+- get_oid
++ repo_get_oid
+|
+- get_oid_commit
++ repo_get_oid_commit
+|
+- get_oid_committish
++ repo_get_oid_committish
+|
+- get_oid_tree
++ repo_get_oid_tree
+|
+- get_oid_treeish
++ repo_get_oid_treeish
+|
+- get_oid_blob
++ repo_get_oid_blob
+|
+- get_oid_mb
++ repo_get_oid_mb
+|
+- find_unique_abbrev
++ repo_find_unique_abbrev
+|
+- find_unique_abbrev_r
++ repo_find_unique_abbrev_r
+|
+- for_each_abbrev
++ repo_for_each_abbrev
+|
+- interpret_branch_name
++ repo_interpret_branch_name
+|
+- peel_to_type
++ repo_peel_to_type
+// commit-reach.h
+|
+- get_merge_bases
++ repo_get_merge_bases
+|
+- get_merge_bases_many
++ repo_get_merge_bases_many
+|
+- get_merge_bases_many_dirty
++ repo_get_merge_bases_many_dirty
+|
+- in_merge_bases
++ repo_in_merge_bases
+|
+- in_merge_bases_many
++ repo_in_merge_bases_many
+// commit.h
+|
+- parse_commit_internal
++ repo_parse_commit_internal
+|
+- parse_commit
++ repo_parse_commit
+|
+- get_commit_buffer
++ repo_get_commit_buffer
+|
+- unuse_commit_buffer
++ repo_unuse_commit_buffer
+|
+- logmsg_reencode
++ repo_logmsg_reencode
+|
+- get_commit_tree
++ repo_get_commit_tree
+// diff.h
+|
+- diff_setup
++ repo_diff_setup
+// object-store.h
+|
+- read_object_file
++ repo_read_object_file
+|
+- has_object_file
++ repo_has_object_file
+|
+- has_object_file_with_flags
++ repo_has_object_file_with_flags
+// pretty.h
+|
+- format_commit_message
++ repo_format_commit_message
+// packfile.h
+|
+- approximate_object_count
++ repo_approximate_object_count
+// promisor-remote.h
+|
+- promisor_remote_reinit
++ repo_promisor_remote_reinit
+|
+- promisor_remote_find
++ repo_promisor_remote_find
+|
+- has_promisor_remote
++ repo_has_promisor_remote
+// refs.h
+|
+- dwim_ref
++ repo_dwim_ref
+// rerere.h
+|
+- rerere
++ repo_rerere
+// revision.h
+|
+- init_revisions
++ repo_init_revisions
+)
+  (
++ the_repository,
+  ...)
diff --git a/contrib/coccinelle/the_repository.pending.cocci b/contrib/coccinelle/the_repository.pending.cocci
deleted file mode 100644 (file)
index 072ea0d..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-// This file is used for the ongoing refactoring of
-// bringing the index or repository struct in all of
-// our code base.
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- read_object_file(
-+ repo_read_object_file(the_repository,
-  E, F, G)
-
-@@
-expression E;
-@@
-- has_object_file(
-+ repo_has_object_file(the_repository,
-  E)
-
-@@
-expression E;
-expression F;
-@@
-- has_object_file_with_flags(
-+ repo_has_object_file_with_flags(the_repository,
-  E)
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- parse_commit_internal(
-+ repo_parse_commit_internal(the_repository,
-  E, F, G)
-
-@@
-expression E;
-expression F;
-@@
-- parse_commit_gently(
-+ repo_parse_commit_gently(the_repository,
-  E, F)
-
-@@
-expression E;
-@@
-- parse_commit(
-+ repo_parse_commit(the_repository,
-  E)
-
-@@
-expression E;
-expression F;
-@@
-- get_merge_bases(
-+ repo_get_merge_bases(the_repository,
-  E, F);
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- get_merge_bases_many(
-+ repo_get_merge_bases_many(the_repository,
-  E, F, G);
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- get_merge_bases_many_dirty(
-+ repo_get_merge_bases_many_dirty(the_repository,
-  E, F, G);
-
-@@
-expression E;
-expression F;
-@@
-- in_merge_bases(
-+ repo_in_merge_bases(the_repository,
-  E, F);
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- in_merge_bases_many(
-+ repo_in_merge_bases_many(the_repository,
-  E, F, G);
-
-@@
-expression E;
-expression F;
-@@
-- get_commit_buffer(
-+ repo_get_commit_buffer(the_repository,
-  E, F);
-
-@@
-expression E;
-expression F;
-@@
-- unuse_commit_buffer(
-+ repo_unuse_commit_buffer(the_repository,
-  E, F);
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- logmsg_reencode(
-+ repo_logmsg_reencode(the_repository,
-  E, F, G);
-
-@@
-expression E;
-expression F;
-expression G;
-expression H;
-@@
-- format_commit_message(
-+ repo_format_commit_message(the_repository,
-  E, F, G, H);
diff --git a/contrib/coccinelle/unused.cocci b/contrib/coccinelle/unused.cocci
deleted file mode 100644 (file)
index d84046f..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-// This rule finds sequences of "unused" declerations and uses of a
-// variable, where "unused" is defined to include only calling the
-// equivalent of alloc, init & free functions on the variable.
-@@
-type T;
-identifier I;
-// STRBUF_INIT, but also e.g. STRING_LIST_INIT_DUP (so no anchoring)
-constant INIT_MACRO =~ "_INIT";
-identifier MALLOC1 =~ "^x?[mc]alloc$";
-identifier INIT_ASSIGN1 =~ "^get_worktrees$";
-identifier INIT_CALL1 =~ "^[a-z_]*_init$";
-identifier REL1 =~ "^[a-z_]*_(release|reset|clear|free)$";
-identifier REL2 =~ "^(release|clear|free)_[a-z_]*$";
-@@
-
-(
-- T I;
-|
-- T I = { 0 };
-|
-- T I = INIT_MACRO;
-|
-- T I = MALLOC1(...);
-|
-- T I = INIT_ASSIGN1(...);
-)
-
-<... when != \( I \| &I \)
-(
-- \( INIT_CALL1 \)( \( I \| &I \), ...);
-|
-- I = \( INIT_ASSIGN1 \)(...);
-|
-- I = MALLOC1(...);
-)
-...>
-
-(
-- \( REL1 \| REL2 \)( \( I \| &I \), ...);
-|
-- \( REL1 \| REL2 \)( \( &I \| I \) );
-)
-  ... when != \( I \| &I \)
index ba5c395d2d804f522d5730f7bca2fa6e17d63a96..8c40ade49411bb883799d79983ad33a696651aa8 100644 (file)
@@ -28,6 +28,8 @@
 # completion style.  For example '!f() { : git commit ; ... }; f' will
 # tell the completion to use commit completion.  This also works with aliases
 # of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
+# Note that "git" is optional --- '!f() { : commit; ...}; f' would complete
+# just like the 'git commit' command.
 #
 # If you have a command that is not part of git, but you would still
 # like completion, you can use __git_complete:
 #
 #     When set to "1" suggest all options, including options which are
 #     typically hidden (e.g. '--allow-empty' for 'git commit').
+#
+#   GIT_COMPLETION_IGNORE_CASE
+#
+#     When set, uses for-each-ref '--ignore-case' to find refs that match
+#     case insensitively, even on systems with case sensitive file systems
+#     (e.g., completing tag name "FOO" on "git checkout f<TAB>").
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -114,6 +122,38 @@ __git ()
                ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null
 }
 
+# Helper function to read the first line of a file into a variable.
+# __git_eread requires 2 arguments, the file path and the name of the
+# variable, in that order.
+#
+# This is taken from git-prompt.sh.
+__git_eread ()
+{
+       test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
+}
+
+# Runs git in $__git_repo_path to determine whether a pseudoref exists.
+# 1: The pseudo-ref to search
+__git_pseudoref_exists ()
+{
+       local ref=$1
+
+       # If the reftable is in use, we have to shell out to 'git rev-parse'
+       # to determine whether the ref exists instead of looking directly in
+       # the filesystem to determine whether the ref exists. Otherwise, use
+       # Bash builtins since executing Git commands are expensive on some
+       # platforms.
+       if __git_eread "$__git_repo_path/HEAD" head; then
+               b="${head#ref: }"
+               if [ "$b" == "refs/heads/.invalid" ]; then
+                       __git -C "$__git_repo_path" rev-parse --verify --quiet "$ref" 2>/dev/null
+                       return $?
+               fi
+       fi
+
+       [ -f "$__git_repo_path/$ref" ]
+}
+
 # Removes backslash escaping, single quotes and double quotes from a word,
 # stores the result in the variable $dequoted_word.
 # 1: The word to dequote.
@@ -646,6 +686,7 @@ __git_heads ()
        local pfx="${1-}" cur_="${2-}" sfx="${3-}"
 
        __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                        "refs/heads/$cur_*" "refs/heads/$cur_*/**"
 }
 
@@ -659,6 +700,7 @@ __git_remote_heads ()
        local pfx="${1-}" cur_="${2-}" sfx="${3-}"
 
        __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                        "refs/remotes/$cur_*" "refs/remotes/$cur_*/**"
 }
 
@@ -669,6 +711,7 @@ __git_tags ()
        local pfx="${1-}" cur_="${2-}" sfx="${3-}"
 
        __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                        "refs/tags/$cur_*" "refs/tags/$cur_*/**"
 }
 
@@ -688,6 +731,7 @@ __git_dwim_remote_heads ()
        # 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
 }
@@ -712,6 +756,7 @@ __git_refs ()
        local format refs
        local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}"
        local match="${4-}"
+       local umatch="${4-}"
        local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
 
        __git_find_repo_path
@@ -735,12 +780,19 @@ __git_refs ()
                fi
        fi
 
+       if test "${GIT_COMPLETION_IGNORE_CASE:+1}" = "1"
+       then
+               # uppercase with tr instead of ${match,^^} for bash 3.2 compatibility
+               umatch=$(echo "$match" | tr a-z A-Z 2>/dev/null || echo "$match")
+       fi
+
        if [ "$list_refs_from" = path ]; then
                if [[ "$cur_" == ^* ]]; then
                        pfx="$pfx^"
                        fer_pfx="$fer_pfx^"
                        cur_=${cur_#^}
                        match=${match#^}
+                       umatch=${umatch#^}
                fi
                case "$cur_" in
                refs|refs/*)
@@ -749,9 +801,9 @@ __git_refs ()
                        track=""
                        ;;
                *)
-                       for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD; do
+                       for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD REVERT_HEAD BISECT_HEAD AUTO_MERGE; do
                                case "$i" in
-                               $match*)
+                               $match*|$umatch*)
                                        if [ -e "$dir/$i" ]; then
                                                echo "$pfx$i$sfx"
                                        fi
@@ -765,6 +817,7 @@ __git_refs ()
                        ;;
                esac
                __git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \
+                       ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                        "${refs[@]}"
                if [ -n "$track" ]; then
                        __git_dwim_remote_heads "$pfx" "$match" "$sfx"
@@ -784,15 +837,16 @@ __git_refs ()
        *)
                if [ "$list_refs_from" = remote ]; then
                        case "HEAD" in
-                       $match*       echo "${pfx}HEAD$sfx" ;;
+                       $match*|$umatch*)       echo "${pfx}HEAD$sfx" ;;
                        esac
                        __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+                               ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                                "refs/remotes/$remote/$match*" \
                                "refs/remotes/$remote/$match*/**"
                else
                        local query_symref
                        case "HEAD" in
-                       $match*       query_symref="HEAD" ;;
+                       $match*|$umatch*)       query_symref="HEAD" ;;
                        esac
                        __git ls-remote "$remote" $query_symref \
                                "refs/tags/$match*" "refs/heads/$match*" \
@@ -1162,7 +1216,7 @@ __git_aliased_command ()
                        :)      : skip null command ;;
                        \'*)    : skip opening quote after sh -c ;;
                        *)
-                               cur="$word"
+                               cur="${word%;}"
                                break
                        esac
                done
@@ -1587,7 +1641,7 @@ _git_checkout ()
 
                if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then
                        __git_complete_refs --mode="refs"
-               elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
+               elif [ -n "$(__git_find_on_cmdline "-t --track")" ]; then
                        __git_complete_refs --mode="remote-heads"
                else
                        __git_complete_refs $dwim_opt --mode="refs"
@@ -1603,7 +1657,7 @@ __git_cherry_pick_inprogress_options=$__git_sequencer_inprogress_options
 _git_cherry_pick ()
 {
        __git_find_repo_path
-       if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then
+       if __git_pseudoref_exists CHERRY_PICK_HEAD; then
                __gitcomp "$__git_cherry_pick_inprogress_options"
                return
        fi
@@ -1657,6 +1711,11 @@ _git_clone ()
 
 __git_untracked_file_modes="all no normal"
 
+__git_trailer_tokens ()
+{
+       __git config --name-only --get-regexp '^trailer\..*\.key$' | cut -d. -f 2- | rev | cut -d. -f2- | rev
+}
+
 _git_commit ()
 {
        case "$prev" in
@@ -1681,6 +1740,10 @@ _git_commit ()
                __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
                return
                ;;
+       --trailer=*)
+               __gitcomp_nl "$(__git_trailer_tokens)" "" "${cur##--trailer=}" ":"
+               return
+               ;;
        --*)
                __gitcomp_builtin commit
                return
@@ -1713,32 +1776,44 @@ __git_color_moved_opts="no default plain blocks zebra dimmed-zebra"
 __git_color_moved_ws_opts="no ignore-space-at-eol ignore-space-change
                        ignore-all-space allow-indentation-change"
 
+__git_ws_error_highlight_opts="context old new all default"
+
+# Options for the diff machinery (diff, log, show, stash, range-diff, ...)
 __git_diff_common_options="--stat --numstat --shortstat --summary
                        --patch-with-stat --name-only --name-status --color
                        --no-color --color-words --no-renames --check
                        --color-moved --color-moved= --no-color-moved
                        --color-moved-ws= --no-color-moved-ws
                        --full-index --binary --abbrev --diff-filter=
+                       --find-copies --find-object --find-renames
+                       --no-relative --relative
                        --find-copies-harder --ignore-cr-at-eol
                        --text --ignore-space-at-eol --ignore-space-change
                        --ignore-all-space --ignore-blank-lines --exit-code
-                       --quiet --ext-diff --no-ext-diff
+                       --quiet --ext-diff --no-ext-diff --unified=
                        --no-prefix --src-prefix= --dst-prefix=
-                       --inter-hunk-context=
+                       --inter-hunk-context= --function-context
                        --patience --histogram --minimal
                        --raw --word-diff --word-diff-regex=
                        --dirstat --dirstat= --dirstat-by-file
                        --dirstat-by-file= --cumulative
-                       --diff-algorithm=
+                       --diff-algorithm= --default-prefix
                        --submodule --submodule= --ignore-submodules
                        --indent-heuristic --no-indent-heuristic
-                       --textconv --no-textconv
-                       --patch --no-patch
-                       --anchored=
+                       --textconv --no-textconv --break-rewrites
+                       --patch --no-patch --cc --combined-all-paths
+                       --anchored= --compact-summary --ignore-matching-lines=
+                       --irreversible-delete --line-prefix --no-stat
+                       --output= --output-indicator-context=
+                       --output-indicator-new= --output-indicator-old=
+                       --ws-error-highlight=
+                       --pickaxe-all --pickaxe-regex
 "
 
-__git_diff_difftool_options="--cached --staged --pickaxe-all --pickaxe-regex
-                       --base --ours --theirs --no-index --relative --merge-base
+# Options for diff/difftool
+__git_diff_difftool_options="--cached --staged
+                       --base --ours --theirs --no-index --merge-base
+                       --ita-invisible-in-index --ita-visible-in-index
                        $__git_diff_common_options"
 
 _git_diff ()
@@ -1762,6 +1837,10 @@ _git_diff ()
                __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
                return
                ;;
+       --ws-error-highlight=*)
+               __gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
+               return
+               ;;
        --*)
                __gitcomp "$__git_diff_difftool_options"
                return
@@ -2004,6 +2083,12 @@ __git_log_shortlog_options="
        --author= --committer= --grep=
        --all-match --invert-grep
 "
+# Options accepted by log and show
+__git_log_show_options="
+       --diff-merges --diff-merges= --no-diff-merges --dd --remerge-diff
+"
+
+__git_diff_merges_opts="off none on first-parent 1 separate m combined c dense-combined cc remerge r"
 
 __git_log_pretty_formats="oneline short medium full fuller reference email raw format: tformat: mboxrd"
 __git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default human raw unix auto: format:"
@@ -2014,7 +2099,7 @@ _git_log ()
        __git_find_repo_path
 
        local merge=""
-       if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
+       if __git_pseudoref_exists MERGE_HEAD; then
                merge="--merge"
        fi
        case "$prev,$cur" in
@@ -2052,15 +2137,24 @@ _git_log ()
                __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
                return
                ;;
+       --ws-error-highlight=*)
+               __gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
+               return
+               ;;
        --no-walk=*)
                __gitcomp "sorted unsorted" "" "${cur##--no-walk=}"
                return
                ;;
+       --diff-merges=*)
+                __gitcomp "$__git_diff_merges_opts" "" "${cur##--diff-merges=}"
+                return
+                ;;
        --*)
                __gitcomp "
                        $__git_log_common_options
                        $__git_log_shortlog_options
                        $__git_log_gitk_options
+                       $__git_log_show_options
                        --root --topo-order --date-order --reverse
                        --follow --full-diff
                        --abbrev-commit --no-abbrev-commit --abbrev=
@@ -2077,7 +2171,6 @@ _git_log ()
                        --expand-tabs --expand-tabs= --no-expand-tabs
                        $merge
                        $__git_diff_common_options
-                       --pickaxe-all --pickaxe-regex
                        "
                return
                ;;
@@ -2464,7 +2557,7 @@ _git_switch ()
 
                if [ -n "$(__git_find_on_cmdline "-c -C -d --detach")" ]; then
                        __git_complete_refs --mode="refs"
-               elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
+               elif [ -n "$(__git_find_on_cmdline "-t --track")" ]; then
                        __git_complete_refs --mode="remote-heads"
                else
                        __git_complete_refs $dwim_opt --mode="heads"
@@ -2873,6 +2966,7 @@ _git_reset ()
 
 _git_restore ()
 {
+       __git_find_repo_path
        case "$prev" in
        -s)
                __git_complete_refs
@@ -2891,7 +2985,7 @@ _git_restore ()
                __gitcomp_builtin restore
                ;;
        *)
-               if __git rev-parse --verify --quiet HEAD >/dev/null; then
+               if __git_pseudoref_exists HEAD; then
                        __git_complete_index_file "--modified"
                fi
        esac
@@ -2902,7 +2996,7 @@ __git_revert_inprogress_options=$__git_sequencer_inprogress_options
 _git_revert ()
 {
        __git_find_repo_path
-       if [ -f "$__git_repo_path"/REVERT_HEAD ]; then
+       if __git_pseudoref_exists REVERT_HEAD; then
                __gitcomp "$__git_revert_inprogress_options"
                return
        fi
@@ -2972,10 +3066,19 @@ _git_show ()
                __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
                return
                ;;
+       --ws-error-highlight=*)
+               __gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
+               return
+               ;;
+       --diff-merges=*)
+                __gitcomp "$__git_diff_merges_opts" "" "${cur##--diff-merges=}"
+                return
+                ;;
        --*)
                __gitcomp "--pretty= --format= --abbrev-commit --no-abbrev-commit
                        --oneline --show-signature
                        --expand-tabs --expand-tabs= --no-expand-tabs
+                       $__git_log_show_options
                        $__git_diff_common_options
                        "
                return
@@ -3014,12 +3117,119 @@ __gitcomp_directories ()
                        COMPREPLY+=("$c/")
                        _found=1
                fi
-       done < <(git ls-tree -z -d --name-only HEAD $_tmp_dir)
+       done < <(__git ls-tree -z -d --name-only HEAD $_tmp_dir)
 
        if [[ $_found == 0 ]] && [[ "$cur" =~ /$ ]]; then
                # No possible further completions any deeper, so assume we're at
                # a leaf directory and just consider it complete
                __gitcomp_direct_append "$cur "
+       elif [[ $_found == 0 ]]; then
+               # No possible completions found.  Avoid falling back to
+               # bash's default file and directory completion, because all
+               # valid completions have already been searched and the
+               # fallbacks can do nothing but mislead.  In fact, they can
+               # mislead in three different ways:
+               #    1) Fallback file completion makes no sense when asking
+               #       for directory completions, as this function does.
+               #    2) Fallback directory completion is bad because
+               #       e.g. "/pro" is invalid and should NOT complete to
+               #       "/proc".
+               #    3) Fallback file/directory completion only completes
+               #       on paths that exist in the current working tree,
+               #       i.e. which are *already* part of their
+               #       sparse-checkout.  Thus, normal file and directory
+               #       completion is always useless for "git
+               #       sparse-checkout add" and is also probelmatic for
+               #       "git sparse-checkout set" unless using it to
+               #       strictly narrow the checkout.
+               COMPREPLY=( "" )
+       fi
+}
+
+# In non-cone mode, the arguments to {set,add} are supposed to be
+# patterns, relative to the toplevel directory.  These can be any kind
+# of general pattern, like 'subdir/*.c' and we can't complete on all
+# of those.  However, if the user presses Tab to get tab completion, we
+# presume that they are trying to provide a pattern that names a specific
+# path.
+__gitcomp_slash_leading_paths ()
+{
+       local dequoted_word pfx="" cur_ toplevel
+
+       # Since we are dealing with a sparse-checkout, subdirectories may not
+       # exist in the local working copy.  Therefore, we want to run all
+       # ls-files commands relative to the repository toplevel.
+       toplevel="$(git rev-parse --show-toplevel)/"
+
+       __git_dequote "$cur"
+
+       # If the paths provided by the user already start with '/', then
+       # they are considered relative to the toplevel of the repository
+       # already.  If they do not start with /, then we need to adjust
+       # them to start with the appropriate prefix.
+       case "$cur" in
+       /*)
+               cur="${cur:1}"
+               ;;
+       *)
+               pfx="$(__git rev-parse --show-prefix)"
+       esac
+
+       # Since sparse-index is limited to cone-mode, in non-cone-mode the
+       # list of valid paths is precisely the cached files in the index.
+       #
+       # NEEDSWORK:
+       #   1) We probably need to take care of cases where ls-files
+       #      responds with special quoting.
+       #   2) We probably need to take care of cases where ${cur} has
+       #      some kind of special quoting.
+       #   3) On top of any quoting from 1 & 2, we have to provide an extra
+       #      level of quoting for any paths that contain a '*', '?', '\',
+       #      '[', ']', or leading '#' or '!' since those will be
+       #      interpreted by sparse-checkout as something other than a
+       #      literal path character.
+       # Since there are two types of quoting here, this might get really
+       # complex.  For now, just punt on all of this...
+       completions="$(__git -C "${toplevel}" -c core.quotePath=false \
+                        ls-files --cached -- "${pfx}${cur}*" \
+                        | sed -e s%^%/% -e 's%$% %')"
+       # Note, above, though that we needed all of the completions to be
+       # prefixed with a '/', and we want to add a space so that bash
+       # completion will actually complete an entry and let us move on to
+       # the next one.
+
+       # Return what we've found.
+       if test -n "$completions"; then
+               # We found some completions; return them
+               local IFS=$'\n'
+               COMPREPLY=($completions)
+       else
+               # Do NOT fall back to bash-style all-local-files-and-dirs
+               # when we find no match.  Such options are worse than
+               # useless:
+               #     1. "git sparse-checkout add" needs paths that are NOT
+               #        currently in the working copy.  "git
+               #        sparse-checkout set" does as well, except in the
+               #        special cases when users are only trying to narrow
+               #        their sparse checkout to a subset of what they
+               #        already have.
+               #
+               #     2. A path like '.config' is ambiguous as to whether
+               #        the user wants all '.config' files throughout the
+               #        tree, or just the one under the current directory.
+               #        It would result in a warning from the
+               #        sparse-checkout command due to this.  As such, all
+               #        completions of paths should be prefixed with a
+               #        '/'.
+               #
+               #     3. We don't want paths prefixed with a '/' to
+               #        complete files in the system root directory, we
+               #        want it to complete on files relative to the
+               #        repository root.
+               #
+               # As such, make sure that NO completions are offered rather
+               # than falling back to bash's default completions.
+               COMPREPLY=( "" )
        fi
 }
 
@@ -3027,6 +3237,7 @@ _git_sparse_checkout ()
 {
        local subcommands="list init set disable add reapply"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
+       local using_cone=true
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
                return
@@ -3037,9 +3248,18 @@ _git_sparse_checkout ()
                __gitcomp_builtin sparse-checkout_$subcommand "" "--"
                ;;
        set,*|add,*)
-               if [ "$(__git config core.sparseCheckoutCone)" == "true" ] ||
-               [ -n "$(__git_find_on_cmdline --cone)" ]; then
+               if [[ "$(__git config core.sparseCheckout)" == "true" &&
+                     "$(__git config core.sparseCheckoutCone)" == "false" &&
+                     -z "$(__git_find_on_cmdline --cone)" ]]; then
+                       using_cone=false
+               fi
+               if [[ -n "$(__git_find_on_cmdline --no-cone)" ]]; then
+                       using_cone=false
+               fi
+               if [[ "$using_cone" == "true" ]]; then
                        __gitcomp_directories
+               else
+                        __gitcomp_slash_leading_paths
                fi
        esac
 }
@@ -3522,7 +3742,7 @@ __gitk_main ()
        __git_find_repo_path
 
        local merge=""
-       if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
+       if __git_pseudoref_exists MERGE_HEAD; then
                merge="--merge"
        fi
        case "$cur" in
index 57972c2845c135dc45aeb560d289aade4caa5e5d..71f179cba3fbda3bc93de461649cf75bb08c6653 100644 (file)
 #
 # If you would like a colored hint about the current dirty state, set
 # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb" and are available only when
-# using __git_ps1 for PROMPT_COMMAND or precmd in Bash,
-# but always available in Zsh.
+# the colored output of "git status -sb".
 #
 # If you would like __git_ps1 to do nothing in the case when the current
 # directory is set up to be ignored by git, then set
@@ -259,12 +257,12 @@ __git_ps1_colorize_gitstring ()
                local c_lblue='%F{blue}'
                local c_clear='%f'
        else
-               # Using \[ and \] around colors is necessary to prevent
+               # Using \001 and \002 around colors is necessary to prevent
                # issues with command line editing/browsing/completion!
-               local c_red='\[\e[31m\]'
-               local c_green='\[\e[32m\]'
-               local c_lblue='\[\e[1;34m\]'
-               local c_clear='\[\e[0m\]'
+               local c_red=$'\001\e[31m\002'
+               local c_green=$'\001\e[32m\002'
+               local c_lblue=$'\001\e[1;34m\002'
+               local c_clear=$'\001\e[0m\002'
        fi
        local bad_color=$c_red
        local ok_color=$c_green
@@ -300,7 +298,7 @@ __git_ps1_colorize_gitstring ()
 # variable, in that order.
 __git_eread ()
 {
-       test -r "$1" && IFS=$'\r\n' read "$2" <"$1"
+       test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
 }
 
 # see if a cherry-pick or revert is in progress, if the user has committed a
@@ -410,7 +408,7 @@ __git_ps1 ()
 
        local repo_info rev_parse_exit_code
        repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
-               --is-bare-repository --is-inside-work-tree \
+               --is-bare-repository --is-inside-work-tree --show-ref-format \
                --short HEAD 2>/dev/null)"
        rev_parse_exit_code="$?"
 
@@ -423,6 +421,8 @@ __git_ps1 ()
                short_sha="${repo_info##*$'\n'}"
                repo_info="${repo_info%$'\n'*}"
        fi
+       local ref_format="${repo_info##*$'\n'}"
+       repo_info="${repo_info%$'\n'*}"
        local inside_worktree="${repo_info##*$'\n'}"
        repo_info="${repo_info%$'\n'*}"
        local bare_repo="${repo_info##*$'\n'}"
@@ -481,12 +481,25 @@ __git_ps1 ()
                        b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
                        local head=""
-                       if ! __git_eread "$g/HEAD" head; then
-                               return $exit
-                       fi
-                       # is it a symbolic ref?
-                       b="${head#ref: }"
-                       if [ "$head" = "$b" ]; then
+
+                       case "$ref_format" in
+                       files)
+                               if ! __git_eread "$g/HEAD" head; then
+                                       return $exit
+                               fi
+
+                               if [[ $head == "ref: "* ]]; then
+                                       head="${head#ref: }"
+                               else
+                                       head=""
+                               fi
+                               ;;
+                       *)
+                               head="$(git symbolic-ref HEAD 2>/dev/null)"
+                               ;;
+                       esac
+
+                       if test -z "$head"; then
                                detached=yes
                                b="$(
                                case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -504,6 +517,8 @@ __git_ps1 ()
 
                                b="$short_sha..."
                                b="($b)"
+                       else
+                               b="$head"
                        fi
                fi
        fi
@@ -574,11 +589,8 @@ __git_ps1 ()
                b="\${__git_ps1_branch_name}"
        fi
 
-       # NO color option unless in PROMPT_COMMAND mode or it's Zsh
        if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
-               if [ $pcmode = yes ] || [ -n "${ZSH_VERSION-}" ]; then
-                       __git_ps1_colorize_gitstring
-               fi
+               __git_ps1_colorize_gitstring
        fi
 
        local f="$h$w$i$s$u$p"
diff --git a/contrib/credential/gnome-keyring/.gitignore b/contrib/credential/gnome-keyring/.gitignore
deleted file mode 100644 (file)
index 88d8fcd..0000000
+++ /dev/null
@@ -1 +0,0 @@
-git-credential-gnome-keyring
diff --git a/contrib/credential/gnome-keyring/Makefile b/contrib/credential/gnome-keyring/Makefile
deleted file mode 100644 (file)
index 22c19df..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-MAIN:=git-credential-gnome-keyring
-all:: $(MAIN)
-
-CC = gcc
-RM = rm -f
-CFLAGS = -g -O2 -Wall
-PKG_CONFIG = pkg-config
-
--include ../../../config.mak.autogen
--include ../../../config.mak
-
-INCS:=$(shell $(PKG_CONFIG) --cflags gnome-keyring-1 glib-2.0)
-LIBS:=$(shell $(PKG_CONFIG) --libs gnome-keyring-1 glib-2.0)
-
-SRCS:=$(MAIN).c
-OBJS:=$(SRCS:.c=.o)
-
-%.o: %.c
-       $(CC) $(CFLAGS) $(CPPFLAGS) $(INCS) -o $@ -c $<
-
-$(MAIN): $(OBJS)
-       $(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
-
-clean:
-       @$(RM) $(MAIN) $(OBJS)
diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
deleted file mode 100644 (file)
index 5927e27..0000000
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
- *               2012 Philipp A. Hartmann <pah@qo.cx>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Credits:
- * - GNOME Keyring API handling originally written by John Szakmeister
- * - ported to credential helper API by Philipp A. Hartmann
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <gnome-keyring.h>
-
-#ifdef GNOME_KEYRING_DEFAULT
-
-   /* Modern gnome-keyring */
-
-#include <gnome-keyring-memory.h>
-
-#else
-
-   /*
-    * Support ancient gnome-keyring, circ. RHEL 5.X.
-    * GNOME_KEYRING_DEFAULT seems to have been introduced with Gnome 2.22,
-    * and the other features roughly around Gnome 2.20, 6 months before.
-    * Ubuntu 8.04 used Gnome 2.22 (I think).  Not sure any distro used 2.20.
-    * So the existence/non-existence of GNOME_KEYRING_DEFAULT seems like
-    * a decent thing to use as an indicator.
-    */
-
-#define GNOME_KEYRING_DEFAULT NULL
-
-/*
- * ancient gnome-keyring returns DENIED when an entry is not found.
- * Setting NO_MATCH to DENIED will prevent us from reporting DENIED
- * errors during get and erase operations, but we will still report
- * DENIED errors during a store.
- */
-#define GNOME_KEYRING_RESULT_NO_MATCH GNOME_KEYRING_RESULT_DENIED
-
-#define gnome_keyring_memory_alloc g_malloc
-#define gnome_keyring_memory_free gnome_keyring_free_password
-#define gnome_keyring_memory_strdup g_strdup
-
-static const char *gnome_keyring_result_to_message(GnomeKeyringResult result)
-{
-       switch (result) {
-       case GNOME_KEYRING_RESULT_OK:
-               return "OK";
-       case GNOME_KEYRING_RESULT_DENIED:
-               return "Denied";
-       case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
-               return "No Keyring Daemon";
-       case GNOME_KEYRING_RESULT_ALREADY_UNLOCKED:
-               return "Already UnLocked";
-       case GNOME_KEYRING_RESULT_NO_SUCH_KEYRING:
-               return "No Such Keyring";
-       case GNOME_KEYRING_RESULT_BAD_ARGUMENTS:
-               return "Bad Arguments";
-       case GNOME_KEYRING_RESULT_IO_ERROR:
-               return "IO Error";
-       case GNOME_KEYRING_RESULT_CANCELLED:
-               return "Cancelled";
-       case GNOME_KEYRING_RESULT_ALREADY_EXISTS:
-               return "Already Exists";
-       default:
-               return "Unknown Error";
-       }
-}
-
-/*
- * Support really ancient gnome-keyring, circ. RHEL 4.X.
- * Just a guess for the Glib version.  Glib 2.8 was roughly Gnome 2.12 ?
- * Which was released with gnome-keyring 0.4.3 ??
- */
-#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 8
-
-static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data)
-{
-       gpointer *data = (gpointer *)user_data;
-       int *done = (int *)data[0];
-       GnomeKeyringResult *r = (GnomeKeyringResult *)data[1];
-
-       *r = result;
-       *done = 1;
-}
-
-static void wait_for_request_completion(int *done)
-{
-       GMainContext *mc = g_main_context_default();
-       while (!*done)
-               g_main_context_iteration(mc, TRUE);
-}
-
-static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, guint32 id)
-{
-       int done = 0;
-       GnomeKeyringResult result;
-       gpointer data[] = { &done, &result };
-
-       gnome_keyring_item_delete(keyring, id, gnome_keyring_done_cb, data,
-               NULL);
-
-       wait_for_request_completion(&done);
-
-       return result;
-}
-
-#endif
-#endif
-
-/*
- * This credential struct and API is simplified from git's credential.{h,c}
- */
-struct credential {
-       char *protocol;
-       char *host;
-       unsigned short port;
-       char *path;
-       char *username;
-       char *password;
-};
-
-#define CREDENTIAL_INIT { 0 }
-
-typedef int (*credential_op_cb)(struct credential *);
-
-struct credential_operation {
-       char *name;
-       credential_op_cb op;
-};
-
-#define CREDENTIAL_OP_END { NULL, NULL }
-
-/* ----------------- GNOME Keyring functions ----------------- */
-
-/* create a special keyring option string, if path is given */
-static char *keyring_object(struct credential *c)
-{
-       if (!c->path)
-               return NULL;
-
-       if (c->port)
-               return g_strdup_printf("%s:%hd/%s", c->host, c->port, c->path);
-
-       return g_strdup_printf("%s/%s", c->host, c->path);
-}
-
-static int keyring_get(struct credential *c)
-{
-       char *object = NULL;
-       GList *entries;
-       GnomeKeyringNetworkPasswordData *password_data;
-       GnomeKeyringResult result;
-
-       if (!c->protocol || !(c->host || c->path))
-               return EXIT_FAILURE;
-
-       object = keyring_object(c);
-
-       result = gnome_keyring_find_network_password_sync(
-                               c->username,
-                               NULL /* domain */,
-                               c->host,
-                               object,
-                               c->protocol,
-                               NULL /* authtype */,
-                               c->port,
-                               &entries);
-
-       g_free(object);
-
-       if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-               return EXIT_SUCCESS;
-
-       if (result == GNOME_KEYRING_RESULT_CANCELLED)
-               return EXIT_SUCCESS;
-
-       if (result != GNOME_KEYRING_RESULT_OK) {
-               g_critical("%s", gnome_keyring_result_to_message(result));
-               return EXIT_FAILURE;
-       }
-
-       /* pick the first one from the list */
-       password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
-
-       gnome_keyring_memory_free(c->password);
-       c->password = gnome_keyring_memory_strdup(password_data->password);
-
-       if (!c->username)
-               c->username = g_strdup(password_data->user);
-
-       gnome_keyring_network_password_list_free(entries);
-
-       return EXIT_SUCCESS;
-}
-
-
-static int keyring_store(struct credential *c)
-{
-       guint32 item_id;
-       char *object = NULL;
-       GnomeKeyringResult result;
-
-       /*
-        * Sanity check that what we are storing is actually sensible.
-        * In particular, we can't make a URL without a protocol field.
-        * Without either a host or pathname (depending on the scheme),
-        * we have no primary key. And without a username and password,
-        * we are not actually storing a credential.
-        */
-       if (!c->protocol || !(c->host || c->path) ||
-           !c->username || !c->password)
-               return EXIT_FAILURE;
-
-       object = keyring_object(c);
-
-       result = gnome_keyring_set_network_password_sync(
-                               GNOME_KEYRING_DEFAULT,
-                               c->username,
-                               NULL /* domain */,
-                               c->host,
-                               object,
-                               c->protocol,
-                               NULL /* authtype */,
-                               c->port,
-                               c->password,
-                               &item_id);
-
-       g_free(object);
-
-       if (result != GNOME_KEYRING_RESULT_OK &&
-           result != GNOME_KEYRING_RESULT_CANCELLED) {
-               g_critical("%s", gnome_keyring_result_to_message(result));
-               return EXIT_FAILURE;
-       }
-
-       return EXIT_SUCCESS;
-}
-
-static int keyring_erase(struct credential *c)
-{
-       char *object = NULL;
-       GList *entries;
-       GnomeKeyringNetworkPasswordData *password_data;
-       GnomeKeyringResult result;
-
-       /*
-        * Sanity check that we actually have something to match
-        * against. The input we get is a restrictive pattern,
-        * so technically a blank credential means "erase everything".
-        * But it is too easy to accidentally send this, since it is equivalent
-        * to empty input. So explicitly disallow it, and require that the
-        * pattern have some actual content to match.
-        */
-       if (!c->protocol && !c->host && !c->path && !c->username)
-               return EXIT_FAILURE;
-
-       object = keyring_object(c);
-
-       result = gnome_keyring_find_network_password_sync(
-                               c->username,
-                               NULL /* domain */,
-                               c->host,
-                               object,
-                               c->protocol,
-                               NULL /* authtype */,
-                               c->port,
-                               &entries);
-
-       g_free(object);
-
-       if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-               return EXIT_SUCCESS;
-
-       if (result == GNOME_KEYRING_RESULT_CANCELLED)
-               return EXIT_SUCCESS;
-
-       if (result != GNOME_KEYRING_RESULT_OK) {
-               g_critical("%s", gnome_keyring_result_to_message(result));
-               return EXIT_FAILURE;
-       }
-
-       /* pick the first one from the list (delete all matches?) */
-       password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
-
-       result = gnome_keyring_item_delete_sync(
-               password_data->keyring, password_data->item_id);
-
-       gnome_keyring_network_password_list_free(entries);
-
-       if (result != GNOME_KEYRING_RESULT_OK) {
-               g_critical("%s", gnome_keyring_result_to_message(result));
-               return EXIT_FAILURE;
-       }
-
-       return EXIT_SUCCESS;
-}
-
-/*
- * Table with helper operation callbacks, used by generic
- * credential helper main function.
- */
-static struct credential_operation const credential_helper_ops[] = {
-       { "get",   keyring_get },
-       { "store", keyring_store },
-       { "erase", keyring_erase },
-       CREDENTIAL_OP_END
-};
-
-/* ------------------ credential functions ------------------ */
-
-static void credential_init(struct credential *c)
-{
-       memset(c, 0, sizeof(*c));
-}
-
-static void credential_clear(struct credential *c)
-{
-       g_free(c->protocol);
-       g_free(c->host);
-       g_free(c->path);
-       g_free(c->username);
-       gnome_keyring_memory_free(c->password);
-
-       credential_init(c);
-}
-
-static int credential_read(struct credential *c)
-{
-       char *buf;
-       size_t line_len;
-       char *key;
-       char *value;
-
-       key = buf = gnome_keyring_memory_alloc(1024);
-
-       while (fgets(buf, 1024, stdin)) {
-               line_len = strlen(buf);
-
-               if (line_len && buf[line_len-1] == '\n')
-                       buf[--line_len] = '\0';
-
-               if (!line_len)
-                       break;
-
-               value = strchr(buf, '=');
-               if (!value) {
-                       g_warning("invalid credential line: %s", key);
-                       gnome_keyring_memory_free(buf);
-                       return -1;
-               }
-               *value++ = '\0';
-
-               if (!strcmp(key, "protocol")) {
-                       g_free(c->protocol);
-                       c->protocol = g_strdup(value);
-               } else if (!strcmp(key, "host")) {
-                       g_free(c->host);
-                       c->host = g_strdup(value);
-                       value = strrchr(c->host, ':');
-                       if (value) {
-                               *value++ = '\0';
-                               c->port = atoi(value);
-                       }
-               } else if (!strcmp(key, "path")) {
-                       g_free(c->path);
-                       c->path = g_strdup(value);
-               } else if (!strcmp(key, "username")) {
-                       g_free(c->username);
-                       c->username = g_strdup(value);
-               } else if (!strcmp(key, "password")) {
-                       gnome_keyring_memory_free(c->password);
-                       c->password = gnome_keyring_memory_strdup(value);
-                       while (*value)
-                               *value++ = '\0';
-               }
-               /*
-                * Ignore other lines; we don't know what they mean, but
-                * this future-proofs us when later versions of git do
-                * learn new lines, and the helpers are updated to match.
-                */
-       }
-
-       gnome_keyring_memory_free(buf);
-
-       return 0;
-}
-
-static void credential_write_item(FILE *fp, const char *key, const char *value)
-{
-       if (!value)
-               return;
-       fprintf(fp, "%s=%s\n", key, value);
-}
-
-static void credential_write(const struct credential *c)
-{
-       /* only write username/password, if set */
-       credential_write_item(stdout, "username", c->username);
-       credential_write_item(stdout, "password", c->password);
-}
-
-static void usage(const char *name)
-{
-       struct credential_operation const *try_op = credential_helper_ops;
-       const char *basename = strrchr(name, '/');
-
-       basename = (basename) ? basename + 1 : name;
-       fprintf(stderr, "usage: %s <", basename);
-       while (try_op->name) {
-               fprintf(stderr, "%s", (try_op++)->name);
-               if (try_op->name)
-                       fprintf(stderr, "%s", "|");
-       }
-       fprintf(stderr, "%s", ">\n");
-}
-
-int main(int argc, char *argv[])
-{
-       int ret = EXIT_SUCCESS;
-
-       struct credential_operation const *try_op = credential_helper_ops;
-       struct credential cred = CREDENTIAL_INIT;
-
-       if (!argv[1]) {
-               usage(argv[0]);
-               exit(EXIT_FAILURE);
-       }
-
-       g_set_application_name("Git Credential Helper");
-
-       /* lookup operation callback */
-       while (try_op->name && strcmp(argv[1], try_op->name))
-               try_op++;
-
-       /* unsupported operation given -- ignore silently */
-       if (!try_op->name || !try_op->op)
-               goto out;
-
-       ret = credential_read(&cred);
-       if (ret)
-               goto out;
-
-       /* perform credential operation */
-       ret = (*try_op->op)(&cred);
-
-       credential_write(&cred);
-
-out:
-       credential_clear(&cred);
-       return ret;
-}
diff --git a/contrib/credential/libsecret/.gitignore b/contrib/credential/libsecret/.gitignore
new file mode 100644 (file)
index 0000000..4fa2235
--- /dev/null
@@ -0,0 +1 @@
+git-credential-libsecret
index 2c5d76d789f0fabbdcd30d2684a4021b647731db..215a81d8bae59ca2364eb762c72089ff8b6b51d7 100644 (file)
@@ -39,6 +39,8 @@ struct credential {
        char *path;
        char *username;
        char *password;
+       char *password_expiry_utc;
+       char *oauth_refresh_token;
 };
 
 #define CREDENTIAL_INIT { 0 }
@@ -52,8 +54,29 @@ struct credential_operation {
 
 #define CREDENTIAL_OP_END { NULL, NULL }
 
+static void credential_clear(struct credential *c);
+
 /* ----------------- Secret Service functions ----------------- */
 
+static const SecretSchema schema = {
+       "org.git.Password",
+       /* Ignore schema name during search for backwards compatibility */
+       SECRET_SCHEMA_DONT_MATCH_NAME,
+       {
+               /*
+                * libsecret assumes attribute values are non-confidential and
+                * unchanging, so we can't include oauth_refresh_token or
+                * password_expiry_utc.
+                */
+               {  "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
+               {  "object", SECRET_SCHEMA_ATTRIBUTE_STRING },
+               {  "protocol", SECRET_SCHEMA_ATTRIBUTE_STRING },
+               {  "port", SECRET_SCHEMA_ATTRIBUTE_INTEGER },
+               {  "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
+               {  NULL, 0 },
+       }
+};
+
 static char *make_label(struct credential *c)
 {
        if (c->port)
@@ -101,7 +124,7 @@ static int keyring_get(struct credential *c)
 
        attributes = make_attr_list(c);
        items = secret_service_search_sync(service,
-                                          SECRET_SCHEMA_COMPAT_NETWORK,
+                                          &schema,
                                           attributes,
                                           SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_UNLOCK,
                                           NULL,
@@ -117,6 +140,7 @@ static int keyring_get(struct credential *c)
                SecretItem *item;
                SecretValue *secret;
                const char *s;
+               gchar **parts;
 
                item = items->data;
                secret = secret_item_get_secret(item);
@@ -130,8 +154,27 @@ static int keyring_get(struct credential *c)
 
                s = secret_value_get_text(secret);
                if (s) {
-                       g_free(c->password);
-                       c->password = g_strdup(s);
+                       /*
+                        * Passwords and other attributes encoded in following format:
+                        *   hunter2
+                        *   password_expiry_utc=1684189401
+                        *   oauth_refresh_token=xyzzy
+                        */
+                       parts = g_strsplit(s, "\n", 0);
+                       if (g_strv_length(parts) >= 1) {
+                               g_free(c->password);
+                               c->password = g_strdup(parts[0]);
+                       }
+                       for (int i = 1; i < g_strv_length(parts); i++) {
+                               if (g_str_has_prefix(parts[i], "password_expiry_utc=")) {
+                                       g_free(c->password_expiry_utc);
+                                       c->password_expiry_utc = g_strdup(&parts[i][20]);
+                               } else if (g_str_has_prefix(parts[i], "oauth_refresh_token=")) {
+                                       g_free(c->oauth_refresh_token);
+                                       c->oauth_refresh_token = g_strdup(&parts[i][20]);
+                               }
+                       }
+                       g_strfreev(parts);
                }
 
                g_hash_table_unref(attributes);
@@ -148,6 +191,7 @@ static int keyring_store(struct credential *c)
        char *label = NULL;
        GHashTable *attributes = NULL;
        GError *error = NULL;
+       GString *secret = NULL;
 
        /*
         * Sanity check that what we are storing is actually sensible.
@@ -162,13 +206,23 @@ static int keyring_store(struct credential *c)
 
        label = make_label(c);
        attributes = make_attr_list(c);
-       secret_password_storev_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+       secret = g_string_new(c->password);
+       if (c->password_expiry_utc) {
+               g_string_append_printf(secret, "\npassword_expiry_utc=%s",
+                       c->password_expiry_utc);
+       }
+       if (c->oauth_refresh_token) {
+               g_string_append_printf(secret, "\noauth_refresh_token=%s",
+                       c->oauth_refresh_token);
+       }
+       secret_password_storev_sync(&schema,
                                    attributes,
                                    NULL,
                                    label,
-                                   c->password,
+                                   secret->str,
                                    NULL,
                                    &error);
+       g_string_free(secret, TRUE);
        g_free(label);
        g_hash_table_unref(attributes);
 
@@ -185,6 +239,7 @@ static int keyring_erase(struct credential *c)
 {
        GHashTable *attributes = NULL;
        GError *error = NULL;
+       struct credential existing = CREDENTIAL_INIT;
 
        /*
         * Sanity check that we actually have something to match
@@ -197,8 +252,22 @@ static int keyring_erase(struct credential *c)
        if (!c->protocol && !c->host && !c->path && !c->username)
                return EXIT_FAILURE;
 
+       if (c->password) {
+               existing.host = g_strdup(c->host);
+               existing.path = g_strdup(c->path);
+               existing.port = c->port;
+               existing.protocol = g_strdup(c->protocol);
+               existing.username = g_strdup(c->username);
+               keyring_get(&existing);
+               if (existing.password && strcmp(c->password, existing.password)) {
+                       credential_clear(&existing);
+                       return EXIT_SUCCESS;
+               }
+               credential_clear(&existing);
+       }
+
        attributes = make_attr_list(c);
-       secret_password_clearv_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+       secret_password_clearv_sync(&schema,
                                    attributes,
                                    NULL,
                                    &error);
@@ -238,23 +307,24 @@ static void credential_clear(struct credential *c)
        g_free(c->path);
        g_free(c->username);
        g_free(c->password);
+       g_free(c->password_expiry_utc);
+       g_free(c->oauth_refresh_token);
 
        credential_init(c);
 }
 
 static int credential_read(struct credential *c)
 {
-       char *buf;
-       size_t line_len;
+       char *buf = NULL;
+       size_t alloc;
+       ssize_t line_len;
        char *key;
        char *value;
 
-       key = buf = g_malloc(1024);
-
-       while (fgets(buf, 1024, stdin)) {
-               line_len = strlen(buf);
+       while ((line_len = getline(&buf, &alloc, stdin)) > 0) {
+               key = buf;
 
-               if (line_len && buf[line_len-1] == '\n')
+               if (buf[line_len-1] == '\n')
                        buf[--line_len] = '\0';
 
                if (!line_len)
@@ -285,11 +355,19 @@ static int credential_read(struct credential *c)
                } else if (!strcmp(key, "username")) {
                        g_free(c->username);
                        c->username = g_strdup(value);
+               } else if (!strcmp(key, "password_expiry_utc")) {
+                       g_free(c->password_expiry_utc);
+                       c->password_expiry_utc = g_strdup(value);
                } else if (!strcmp(key, "password")) {
                        g_free(c->password);
                        c->password = g_strdup(value);
                        while (*value)
                                *value++ = '\0';
+               } else if (!strcmp(key, "oauth_refresh_token")) {
+                       g_free(c->oauth_refresh_token);
+                       c->oauth_refresh_token = g_strdup(value);
+                       while (*value)
+                               *value++ = '\0';
                }
                /*
                 * Ignore other lines; we don't know what they mean, but
@@ -298,7 +376,7 @@ static int credential_read(struct credential *c)
                 */
        }
 
-       g_free(buf);
+       free(buf);
 
        return 0;
 }
@@ -315,6 +393,10 @@ static void credential_write(const struct credential *c)
        /* only write username/password, if set */
        credential_write_item(stdout, "username", c->username);
        credential_write_item(stdout, "password", c->password);
+       credential_write_item(stdout, "password_expiry_utc",
+               c->password_expiry_utc);
+       credential_write_item(stdout, "oauth_refresh_token",
+               c->oauth_refresh_token);
 }
 
 static void usage(const char *name)
index bc57cc65884b97dc6a0d33a067f00dac35ee19a8..9fb998ae090325ba0e0080d31798b7b01870b30e 100755 (executable)
@@ -356,7 +356,10 @@ sub read_credential_data_from_stdin {
                next unless m/^([^=]+)=(.+)/;
 
                my ($token, $value) = ($1, $2);
-               die "Unknown search token $token" unless exists $q{$token};
+
+               # skip any unknown tokens
+               next unless exists $q{$token};
+
                $q{$token} = $value;
                log_debug("We were given search token $token and value $value");
        }
index bf77748d602fec651661075cbd6dd32951d065d2..5f2e5f16c889ff278bc51553816c54acb41be21e 100644 (file)
@@ -113,14 +113,16 @@ static void add_internet_password(void)
 
 static void read_credential(void)
 {
-       char buf[1024];
+       char *buf = NULL;
+       size_t alloc;
+       ssize_t line_len;
 
-       while (fgets(buf, sizeof(buf), stdin)) {
+       while ((line_len = getline(&buf, &alloc, stdin)) > 0) {
                char *v;
 
                if (!strcmp(buf, "\n"))
                        break;
-               buf[strlen(buf)-1] = '\0';
+               buf[line_len-1] = '\0';
 
                v = strchr(buf, '=');
                if (!v)
@@ -159,7 +161,14 @@ static void read_credential(void)
                        username = xstrdup(v);
                else if (!strcmp(buf, "password"))
                        password = xstrdup(v);
+               /*
+                * Ignore other lines; we don't know what they mean, but
+                * this future-proofs us when later versions of git do
+                * learn new lines, and the helpers are updated to match.
+                */
        }
+
+       free(buf);
 }
 
 int main(int argc, const char **argv)
index 5091048f9c649f31f0c11c49f33a7e757043023a..4cd56c42e24469a48a40e7b02de4b4921ff8662c 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <io.h>
 #include <fcntl.h>
+#include <wincred.h>
 
 /* common helpers */
 
@@ -33,65 +34,8 @@ static void *xmalloc(size_t size)
        return ret;
 }
 
-/* MinGW doesn't have wincred.h, so we need to define stuff */
-
-typedef struct _CREDENTIAL_ATTRIBUTEW {
-       LPWSTR Keyword;
-       DWORD  Flags;
-       DWORD  ValueSize;
-       LPBYTE Value;
-} CREDENTIAL_ATTRIBUTEW, *PCREDENTIAL_ATTRIBUTEW;
-
-typedef struct _CREDENTIALW {
-       DWORD                  Flags;
-       DWORD                  Type;
-       LPWSTR                 TargetName;
-       LPWSTR                 Comment;
-       FILETIME               LastWritten;
-       DWORD                  CredentialBlobSize;
-       LPBYTE                 CredentialBlob;
-       DWORD                  Persist;
-       DWORD                  AttributeCount;
-       PCREDENTIAL_ATTRIBUTEW Attributes;
-       LPWSTR                 TargetAlias;
-       LPWSTR                 UserName;
-} CREDENTIALW, *PCREDENTIALW;
-
-#define CRED_TYPE_GENERIC 1
-#define CRED_PERSIST_LOCAL_MACHINE 2
-#define CRED_MAX_ATTRIBUTES 64
-
-typedef BOOL (WINAPI *CredWriteWT)(PCREDENTIALW, DWORD);
-typedef BOOL (WINAPI *CredEnumerateWT)(LPCWSTR, DWORD, DWORD *,
-    PCREDENTIALW **);
-typedef VOID (WINAPI *CredFreeT)(PVOID);
-typedef BOOL (WINAPI *CredDeleteWT)(LPCWSTR, DWORD, DWORD);
-
-static HMODULE advapi;
-static CredWriteWT CredWriteW;
-static CredEnumerateWT CredEnumerateW;
-static CredFreeT CredFree;
-static CredDeleteWT CredDeleteW;
-
-static void load_cred_funcs(void)
-{
-       /* load DLLs */
-       advapi = LoadLibraryExA("advapi32.dll", NULL,
-                               LOAD_LIBRARY_SEARCH_SYSTEM32);
-       if (!advapi)
-               die("failed to load advapi32.dll");
-
-       /* get function pointers */
-       CredWriteW = (CredWriteWT)GetProcAddress(advapi, "CredWriteW");
-       CredEnumerateW = (CredEnumerateWT)GetProcAddress(advapi,
-           "CredEnumerateW");
-       CredFree = (CredFreeT)GetProcAddress(advapi, "CredFree");
-       CredDeleteW = (CredDeleteWT)GetProcAddress(advapi, "CredDeleteW");
-       if (!CredWriteW || !CredEnumerateW || !CredFree || !CredDeleteW)
-               die("failed to load functions");
-}
-
-static WCHAR *wusername, *password, *protocol, *host, *path, target[1024];
+static WCHAR *wusername, *password, *protocol, *host, *path, target[1024],
+       *password_expiry_utc;
 
 static void write_item(const char *what, LPCWSTR wbuf, int wlen)
 {
@@ -165,7 +109,18 @@ static int match_part_last(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
        return match_part_with_last(ptarget, want, delim, 1);
 }
 
-static int match_cred(const CREDENTIALW *cred)
+static int match_cred_password(const CREDENTIALW *cred) {
+       int ret;
+       WCHAR *cred_password = xmalloc(cred->CredentialBlobSize);
+       wcsncpy_s(cred_password, cred->CredentialBlobSize,
+               (LPCWSTR)cred->CredentialBlob,
+               cred->CredentialBlobSize / sizeof(WCHAR));
+       ret = !wcscmp(cred_password, password);
+       free(cred_password);
+       return ret;
+}
+
+static int match_cred(const CREDENTIALW *cred, int match_password)
 {
        LPCWSTR target = cred->TargetName;
        if (wusername && wcscmp(wusername, cred->UserName ? cred->UserName : L""))
@@ -175,7 +130,8 @@ static int match_cred(const CREDENTIALW *cred)
                match_part(&target, protocol, L"://") &&
                match_part_last(&target, wusername, L"@") &&
                match_part(&target, host, L"/") &&
-               match_part(&target, path, L"");
+               match_part(&target, path, L"") &&
+               (!match_password || match_cred_password(cred));
 }
 
 static void get_credential(void)
@@ -183,18 +139,27 @@ static void get_credential(void)
        CREDENTIALW **creds;
        DWORD num_creds;
        int i;
+       CREDENTIAL_ATTRIBUTEW *attr;
 
        if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds))
                return;
 
        /* search for the first credential that matches username */
        for (i = 0; i < num_creds; ++i)
-               if (match_cred(creds[i])) {
+               if (match_cred(creds[i], 0)) {
                        write_item("username", creds[i]->UserName,
                                creds[i]->UserName ? wcslen(creds[i]->UserName) : 0);
                        write_item("password",
                                (LPCWSTR)creds[i]->CredentialBlob,
                                creds[i]->CredentialBlobSize / sizeof(WCHAR));
+                       for (int j = 0; j < creds[i]->AttributeCount; j++) {
+                               attr = creds[i]->Attributes + j;
+                               if (!wcscmp(attr->Keyword, L"git_password_expiry_utc")) {
+                                       write_item("password_expiry_utc", (LPCWSTR)attr->Value,
+                                       attr->ValueSize / sizeof(WCHAR));
+                                       break;
+                               }
+                       }
                        break;
                }
 
@@ -204,6 +169,7 @@ static void get_credential(void)
 static void store_credential(void)
 {
        CREDENTIALW cred;
+       CREDENTIAL_ATTRIBUTEW expiry_attr;
 
        if (!wusername || !password)
                return;
@@ -217,6 +183,14 @@ static void store_credential(void)
        cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
        cred.AttributeCount = 0;
        cred.Attributes = NULL;
+       if (password_expiry_utc != NULL) {
+               expiry_attr.Keyword = L"git_password_expiry_utc";
+               expiry_attr.Value = (LPVOID)password_expiry_utc;
+               expiry_attr.ValueSize = (wcslen(password_expiry_utc)) * sizeof(WCHAR);
+               expiry_attr.Flags = 0;
+               cred.Attributes = &expiry_attr;
+               cred.AttributeCount = 1;
+       }
        cred.TargetAlias = NULL;
        cred.UserName = wusername;
 
@@ -234,7 +208,7 @@ static void erase_credential(void)
                return;
 
        for (i = 0; i < num_creds; ++i) {
-               if (match_cred(creds[i]))
+               if (match_cred(creds[i], password != NULL))
                        CredDeleteW(creds[i]->TargetName, creds[i]->Type, 0);
        }
 
@@ -249,16 +223,27 @@ static WCHAR *utf8_to_utf16_dup(const char *str)
        return wstr;
 }
 
+#define KB (1024)
+
 static void read_credential(void)
 {
-       char buf[1024];
+       size_t alloc = 100 * KB;
+       char *buf = calloc(alloc, sizeof(*buf));
 
-       while (fgets(buf, sizeof(buf), stdin)) {
+       while (fgets(buf, alloc, stdin)) {
                char *v;
-               int len = strlen(buf);
+               size_t len = strlen(buf);
+               int ends_in_newline = 0;
                /* strip trailing CR / LF */
-               while (len && strchr("\r\n", buf[len - 1]))
+               if (len && buf[len - 1] == '\n') {
                        buf[--len] = 0;
+                       ends_in_newline = 1;
+               }
+               if (len && buf[len - 1] == '\r')
+                       buf[--len] = 0;
+
+               if (!ends_in_newline)
+                       die("bad input: %s", buf);
 
                if (!*buf)
                        break;
@@ -278,9 +263,16 @@ static void read_credential(void)
                        wusername = utf8_to_utf16_dup(v);
                } else if (!strcmp(buf, "password"))
                        password = utf8_to_utf16_dup(v);
-               else
-                       die("unrecognized input");
+               else if (!strcmp(buf, "password_expiry_utc"))
+                       password_expiry_utc = utf8_to_utf16_dup(v);
+               /*
+                * Ignore other lines; we don't know what they mean, but
+                * this future-proofs us when later versions of git do
+                * learn new lines, and the helpers are updated to match.
+                */
        }
+
+       free(buf);
 }
 
 int main(int argc, char *argv[])
@@ -289,7 +281,7 @@ int main(int argc, char *argv[])
            "usage: git credential-wincred <get|store|erase>\n";
 
        if (!argv[1])
-               die(usage);
+               die("%s", usage);
 
        /* git use binary pipes to avoid CRLF-issues */
        _setmode(_fileno(stdin), _O_BINARY);
@@ -297,8 +289,6 @@ int main(int argc, char *argv[])
 
        read_credential();
 
-       load_cred_funcs();
-
        if (!protocol || !(host || path))
                return 0;
 
index 376f577737591e26a118718a87945500ea621c95..636add6968067537a34e46b92957fbc35acd18e9 100644 (file)
@@ -1,6 +1,6 @@
 package DiffHighlight;
 
-use 5.008;
+use 5.008001;
 use warnings FATAL => 'all';
 use strict;
 
index 8bcace29d21eea15e224d9ef2a841b98fdf36fac..3211841305fcb3cc9cce22bcd6c445cf327e274f 100644 (file)
@@ -79,6 +79,14 @@ git jump grep -i foo_bar
 git config jump.grepCmd "ag --column"
 --------------------------------------------------
 
+You can use the optional argument '--stdout' to print the listing to
+standard output instead of feeding it to the editor. You can use the
+argument with M-x grep on Emacs:
+
+--------------------------------------------------
+# In Emacs, M-x grep and invoke "git jump --stdout <mode>"
+M-x grep<RET>git jump --stdout diff<RET>
+--------------------------------------------------
 
 Related Programs
 ----------------
@@ -100,7 +108,7 @@ Limitations
 -----------
 
 This script was written and tested with vim. Given that the quickfix
-format is the same as what gcc produces, I expect emacs users have a
+format is the same as what gcc produces, I expect other tools have a
 similar feature for iterating through the list, but I know nothing about
 how to activate it.
 
index 92dbd4cde18ea18f6933ed79648a61d0c7493796..47e0c557e63f1b236a3ad763e03ac086f1842d23 100755 (executable)
@@ -2,25 +2,43 @@
 
 usage() {
        cat <<\EOF
-usage: git jump <mode> [<args>]
+usage: git jump [--stdout] <mode> [<args>]
 
 Jump to interesting elements in an editor.
 The <mode> parameter is one of:
 
 diff: elements are diff hunks. Arguments are given to diff.
 
-merge: elements are merge conflicts. Arguments are ignored.
+merge: elements are merge conflicts. Arguments are given to ls-files -u.
 
 grep: elements are grep hits. Arguments are given to git grep or, if
       configured, to the command in `jump.grepCmd`.
 
 ws: elements are whitespace errors. Arguments are given to diff --check.
+
+If the optional argument `--stdout` is given, print the quickfix
+lines to standard output instead of feeding it to the editor.
 EOF
 }
 
 open_editor() {
        editor=`git var GIT_EDITOR`
-       eval "$editor -q \$1"
+       case "$editor" in
+       *emacs*)
+               # Supported editor values are:
+               # - emacs
+               # - emacsclient
+               # - emacsclient -t
+               #
+               # Wait for completion of the asynchronously executed process
+               # to avoid race conditions in case of "emacsclient".
+               eval "$editor --eval \"(let ((buf (grep \\\"cat \$1\\\"))) (pop-to-buffer buf) (select-frame-set-input-focus (selected-frame)) (while (get-buffer-process buf) (sleep-for 0.1)))\""
+               ;;
+       *)
+               # assume anything else is vi-compatible
+               eval "$editor -q \$1"
+               ;;
+       esac
 }
 
 mode_diff() {
@@ -64,15 +82,36 @@ mode_ws() {
        git diff --check "$@"
 }
 
+use_stdout=
+while test $# -gt 0; do
+       case "$1" in
+       --stdout)
+               use_stdout=t
+               ;;
+       --*)
+               usage >&2
+               exit 1
+               ;;
+       *)
+               break
+               ;;
+       esac
+       shift
+done
 if test $# -lt 1; then
        usage >&2
        exit 1
 fi
 mode=$1; shift
+type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }
+
+if test "$use_stdout" = "t"; then
+       "mode_$mode" "$@"
+       exit 0
+fi
 
 trap 'rm -f "$tmp"' 0 1 2 3 15
 tmp=`mktemp -t git-jump.XXXXXX` || exit 1
-type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }
 "mode_$mode" "$@" >"$tmp"
 test -s "$tmp" || exit 0
 open_editor "$tmp"
index 917d9e2d3222c12f2d0dc043a1434acae2b398ae..ff7811225ee67166d8e997ce99def0344971f904 100644 (file)
@@ -1,6 +1,6 @@
 package Git::Mediawiki;
 
-use 5.008;
+use 5.008001;
 use strict;
 use POSIX;
 use Git;
index 6187ec67faaa23c59d358bc3150b72fc82bd7087..7139995a405cee9881f690ff892464c875984e27 100755 (executable)
@@ -161,7 +161,7 @@ test_expect_success 'git push properly warns about insufficient permissions' '
                git add foo.forbidden &&
                git commit -m "add a file" &&
                git push 2>actual &&
-               test_i18ngrep "foo.forbidden is not a permitted file" actual
+               test_grep "foo.forbidden is not a permitted file" actual
        )
 '
 
index 7562a395c2456bf4588a26e6fa847cced5bb71c2..3028029ac2ddf67608dad1f6538b37a347aebe29 100755 (executable)
@@ -33,19 +33,19 @@ git subtree split --prefix=<prefix> [<commit>]
 git subtree pull  --prefix=<prefix> <repository> <ref>
 git subtree push  --prefix=<prefix> <repository> <refspec>
 --
-h,help        show the help
-q             quiet
-d             show debug messages
+h,help!       show the help
+q,quiet!      quiet
+d,debug!      show debug messages
 P,prefix=     the name of the subdir to split out
  options for 'split' (also: 'push')
 annotate=     add a prefix to commit message of new commits
-b,branch    create a new branch from the split subtree
+b,branch!=    create a new branch from the split subtree
 ignore-joins  ignore prior --rejoin commits
 onto=         try connecting new tree to an existing one
 rejoin        merge the new branch back into HEAD
  options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin')
 squash        merge subtree changes as a single commit
-m,message   use the given message as the commit message for the merge commit
+m,message!=   use the given message as the commit message for the merge commit
 "
 
 indent=0
@@ -98,10 +98,18 @@ progress () {
 assert () {
        if ! "$@"
        then
-               die "assertion failed: $*"
+               die "fatal: assertion failed: $*"
        fi
 }
 
+# Usage: die_incompatible_opt OPTION COMMAND
+die_incompatible_opt () {
+       assert test "$#" = 2
+       opt="$1"
+       arg_command="$2"
+       die "fatal: the '$opt' flag does not make sense with 'git subtree $arg_command'."
+}
+
 main () {
        if test $# -eq 0
        then
@@ -147,7 +155,7 @@ main () {
                allow_addmerge=$arg_split_rejoin
                ;;
        *)
-               die "Unknown command '$arg_command'"
+               die "fatal: unknown command '$arg_command'"
                ;;
        esac
        # Reset the arguments array for "real" flag parsing.
@@ -176,16 +184,16 @@ main () {
                        arg_debug=1
                        ;;
                --annotate)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        arg_split_annotate="$1"
                        shift
                        ;;
                --no-annotate)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        arg_split_annotate=
                        ;;
                -b)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        arg_split_branch="$1"
                        shift
                        ;;
@@ -194,7 +202,7 @@ main () {
                        shift
                        ;;
                -m)
-                       test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command"
                        arg_addmerge_message="$1"
                        shift
                        ;;
@@ -202,41 +210,41 @@ main () {
                        arg_prefix=
                        ;;
                --onto)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        arg_split_onto="$1"
                        shift
                        ;;
                --no-onto)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        arg_split_onto=
                        ;;
                --rejoin)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        ;;
                --no-rejoin)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        ;;
                --ignore-joins)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        arg_split_ignore_joins=1
                        ;;
                --no-ignore-joins)
-                       test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
                        arg_split_ignore_joins=
                        ;;
                --squash)
-                       test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command"
                        arg_addmerge_squash=1
                        ;;
                --no-squash)
-                       test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
+                       test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command"
                        arg_addmerge_squash=
                        ;;
                --)
                        break
                        ;;
                *)
-                       die "Unexpected option: $opt"
+                       die "fatal: unexpected option: $opt"
                        ;;
                esac
        done
@@ -244,17 +252,17 @@ main () {
 
        if test -z "$arg_prefix"
        then
-               die "You must provide the --prefix option."
+               die "fatal: you must provide the --prefix option."
        fi
 
        case "$arg_command" in
        add)
                test -e "$arg_prefix" &&
-                       die "prefix '$arg_prefix' already exists."
+                       die "fatal: prefix '$arg_prefix' already exists."
                ;;
        *)
                test -e "$arg_prefix" ||
-                       die "'$arg_prefix' does not exist; use 'git subtree add'"
+                       die "fatal: '$arg_prefix' does not exist; use 'git subtree add'"
                ;;
        esac
 
@@ -274,11 +282,11 @@ cache_setup () {
        assert test $# = 0
        cachedir="$GIT_DIR/subtree-cache/$$"
        rm -rf "$cachedir" ||
-               die "Can't delete old cachedir: $cachedir"
+               die "fatal: can't delete old cachedir: $cachedir"
        mkdir -p "$cachedir" ||
-               die "Can't create new cachedir: $cachedir"
+               die "fatal: can't create new cachedir: $cachedir"
        mkdir -p "$cachedir/notree" ||
-               die "Can't create new cachedir: $cachedir/notree"
+               die "fatal: can't create new cachedir: $cachedir/notree"
        debug "Using cachedir: $cachedir" >&2
 }
 
@@ -334,7 +342,7 @@ cache_set () {
                test "$oldrev" != "latest_new" &&
                test -e "$cachedir/$oldrev"
        then
-               die "cache for $oldrev already exists!"
+               die "fatal: cache for $oldrev already exists!"
        fi
        echo "$newrev" >"$cachedir/$oldrev"
 }
@@ -363,13 +371,49 @@ try_remove_previous () {
        fi
 }
 
-# Usage: find_latest_squash DIR
+# Usage: process_subtree_split_trailer SPLIT_HASH MAIN_HASH [REPOSITORY]
+process_subtree_split_trailer () {
+       assert test $# -ge 2
+       assert test $# -le 3
+       b="$1"
+       sq="$2"
+       repository=""
+       if test "$#" = 3
+       then
+               repository="$3"
+       fi
+       fail_msg="fatal: could not rev-parse split hash $b from commit $sq"
+       if ! sub="$(git rev-parse --verify --quiet "$b^{commit}")"
+       then
+               # if 'repository' was given, try to fetch the 'git-subtree-split' hash
+               # before 'rev-parse'-ing it again, as it might be a tag that we do not have locally
+               if test -n "${repository}"
+               then
+                       git fetch "$repository" "$b"
+                       sub="$(git rev-parse --verify --quiet "$b^{commit}")" ||
+                               die "$fail_msg"
+               else
+                       hint1=$(printf "hint: hash might be a tag, try fetching it from the subtree repository:")
+                       hint2=$(printf "hint:    git fetch <subtree-repository> $b")
+                       fail_msg=$(printf "$fail_msg\n$hint1\n$hint2")
+                       die "$fail_msg"
+               fi
+       fi
+}
+
+# Usage: find_latest_squash DIR [REPOSITORY]
 find_latest_squash () {
-       assert test $# = 1
-       debug "Looking for latest squash ($dir)..."
+       assert test $# -ge 1
+       assert test $# -le 2
+       dir="$1"
+       repository=""
+       if test "$#" = 2
+       then
+               repository="$2"
+       fi
+       debug "Looking for latest squash (dir=$dir, repository=$repository)..."
        local indent=$(($indent + 1))
 
-       dir="$1"
        sq=
        main=
        sub=
@@ -387,8 +431,7 @@ find_latest_squash () {
                        main="$b"
                        ;;
                git-subtree-split:)
-                       sub="$(git rev-parse "$b^{commit}")" ||
-                       die "could not rev-parse split hash $b from commit $sq"
+                       process_subtree_split_trailer "$b" "$sq" "$repository"
                        ;;
                END)
                        if test -n "$sub"
@@ -412,14 +455,20 @@ find_latest_squash () {
        done || exit $?
 }
 
-# Usage: find_existing_splits DIR REV
+# Usage: find_existing_splits DIR REV [REPOSITORY]
 find_existing_splits () {
-       assert test $# = 2
+       assert test $# -ge 2
+       assert test $# -le 3
        debug "Looking for prior splits..."
        local indent=$(($indent + 1))
 
        dir="$1"
        rev="$2"
+       repository=""
+       if test "$#" = 3
+       then
+               repository="$3"
+       fi
        main=
        sub=
        local grep_format="^git-subtree-dir: $dir/*\$"
@@ -439,18 +488,17 @@ find_existing_splits () {
                        main="$b"
                        ;;
                git-subtree-split:)
-                       sub="$(git rev-parse "$b^{commit}")" ||
-                       die "could not rev-parse split hash $b from commit $sq"
+                       process_subtree_split_trailer "$b" "$sq" "$repository"
                        ;;
                END)
                        debug "Main is: '$main'"
-                       if test -z "$main" -a -n "$sub"
+                       if test -z "$main" && test -n "$sub"
                        then
                                # squash commits refer to a subtree
                                debug "  Squash: $sq from $sub"
                                cache_set "$sq" "$sub"
                        fi
-                       if test -n "$main" -a -n "$sub"
+                       if test -n "$main" && test -n "$sub"
                        then
                                debug "  Prior: $main -> $sub"
                                cache_set $main $sub
@@ -490,7 +538,7 @@ copy_commit () {
                        cat
                ) |
                git commit-tree "$2" $3  # reads the rest of stdin
-       ) || die "Can't copy commit $1"
+       ) || die "fatal: can't copy commit $1"
 }
 
 # Usage: add_msg DIR LATEST_OLD LATEST_NEW
@@ -593,10 +641,16 @@ subtree_for_commit () {
        while read mode type tree name
        do
                assert test "$name" = "$dir"
-               assert test "$type" = "tree" -o "$type" = "commit"
-               test "$type" = "commit" && continue  # ignore submodules
-               echo $tree
-               break
+
+               case "$type" in
+               commit)
+                       continue;; # ignore submodules
+               tree)
+                       echo $tree
+                       break;;
+               *)
+                       die "fatal: tree entry is of type ${type}, expected tree or commit";;
+               esac
        done || exit $?
 }
 
@@ -718,11 +772,11 @@ ensure_clean () {
        assert test $# = 0
        if ! git diff-index HEAD --exit-code --quiet 2>&1
        then
-               die "Working tree has modifications.  Cannot add."
+               die "fatal: working tree has modifications.  Cannot add."
        fi
        if ! git diff-index --cached HEAD --exit-code --quiet 2>&1
        then
-               die "Index has modifications.  Cannot add."
+               die "fatal: index has modifications.  Cannot add."
        fi
 }
 
@@ -730,7 +784,7 @@ ensure_clean () {
 ensure_valid_ref_format () {
        assert test $# = 1
        git check-ref-format "refs/heads/$1" ||
-               die "'$1' does not look like a ref"
+               die "fatal: '$1' does not look like a ref"
 }
 
 # Usage: process_split_commit REV PARENTS
@@ -796,7 +850,7 @@ cmd_add () {
        if test $# -eq 1
        then
                git rev-parse -q --verify "$1^{commit}" >/dev/null ||
-                       die "'$1' does not refer to a commit"
+                       die "fatal: '$1' does not refer to a commit"
 
                cmd_add_commit "$@"
 
@@ -811,7 +865,7 @@ cmd_add () {
 
                cmd_add_repository "$@"
        else
-               say >&2 "error: parameters were '$*'"
+               say >&2 "fatal: parameters were '$*'"
                die "Provide either a commit or a repository and commit."
        fi
 }
@@ -843,7 +897,7 @@ cmd_add_commit () {
        git checkout -- "$dir" || exit $?
        tree=$(git write-tree) || exit $?
 
-       headrev=$(git rev-parse HEAD) || exit $?
+       headrev=$(git rev-parse --verify HEAD) || exit $?
        if test -n "$headrev" && test "$headrev" != "$rev"
        then
                headp="-p $headrev"
@@ -866,17 +920,22 @@ cmd_add_commit () {
        say >&2 "Added dir '$dir'"
 }
 
-# Usage: cmd_split [REV]
+# Usage: cmd_split [REV] [REPOSITORY]
 cmd_split () {
        if test $# -eq 0
        then
                rev=$(git rev-parse HEAD)
-       elif test $# -eq 1
+       elif test $# -eq 1 || test $# -eq 2
        then
                rev=$(git rev-parse -q --verify "$1^{commit}") ||
-                       die "'$1' does not refer to a commit"
+                       die "fatal: '$1' does not refer to a commit"
        else
-               die "You must provide exactly one revision.  Got: '$*'"
+               die "fatal: you must provide exactly one revision, and optionnally a repository.  Got: '$*'"
+       fi
+       repository=""
+       if test "$#" = 2
+       then
+               repository="$2"
        fi
 
        if test -n "$arg_split_rejoin"
@@ -900,7 +959,7 @@ cmd_split () {
                done || exit $?
        fi
 
-       unrevs="$(find_existing_splits "$dir" "$rev")" || exit $?
+       unrevs="$(find_existing_splits "$dir" "$rev" "$repository")" || exit $?
 
        # We can't restrict rev-list to only $dir here, because some of our
        # parents have the $dir contents the root, and those won't match.
@@ -919,7 +978,7 @@ cmd_split () {
        latest_new=$(cache_get latest_new) || exit $?
        if test -z "$latest_new"
        then
-               die "No new revisions were found"
+               die "fatal: no new revisions were found"
        fi
 
        if test -n "$arg_split_rejoin"
@@ -940,7 +999,7 @@ cmd_split () {
                then
                        if ! git merge-base --is-ancestor "$arg_split_branch" "$latest_new"
                        then
-                               die "Branch '$arg_split_branch' is not an ancestor of commit '$latest_new'."
+                               die "fatal: branch '$arg_split_branch' is not an ancestor of commit '$latest_new'."
                        fi
                        action='Updated'
                else
@@ -954,20 +1013,28 @@ cmd_split () {
        exit 0
 }
 
-# Usage: cmd_merge REV
+# Usage: cmd_merge REV [REPOSITORY]
 cmd_merge () {
-       test $# -eq 1 ||
-               die "You must provide exactly one revision.  Got: '$*'"
+       if test $# -lt 1 || test $# -gt 2
+       then
+               die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'"
+       fi
+
        rev=$(git rev-parse -q --verify "$1^{commit}") ||
-               die "'$1' does not refer to a commit"
+               die "fatal: '$1' does not refer to a commit"
+       repository=""
+       if test "$#" = 2
+       then
+               repository="$2"
+       fi
        ensure_clean
 
        if test -n "$arg_addmerge_squash"
        then
-               first_split="$(find_latest_squash "$dir")" || exit $?
+               first_split="$(find_latest_squash "$dir" "$repository")" || exit $?
                if test -z "$first_split"
                then
-                       die "Can't squash-merge: '$dir' was never added."
+                       die "fatal: can't squash-merge: '$dir' was never added."
                fi
                set $first_split
                old=$1
@@ -995,19 +1062,21 @@ cmd_merge () {
 cmd_pull () {
        if test $# -ne 2
        then
-               die "You must provide <repository> <ref>"
+               die "fatal: you must provide <repository> <ref>"
        fi
+       repository="$1"
+       ref="$2"
        ensure_clean
-       ensure_valid_ref_format "$2"
-       git fetch "$@" || exit $?
-       cmd_merge FETCH_HEAD
+       ensure_valid_ref_format "$ref"
+       git fetch "$repository" "$ref" || exit $?
+       cmd_merge FETCH_HEAD "$repository"
 }
 
 # Usage: cmd_push REPOSITORY [+][LOCALREV:]REMOTEREF
 cmd_push () {
        if test $# -ne 2
        then
-               die "You must provide <repository> <refspec>"
+               die "fatal: you must provide <repository> <refspec>"
        fi
        if test -e "$dir"
        then
@@ -1022,13 +1091,13 @@ cmd_push () {
                fi
                ensure_valid_ref_format "$remoteref"
                localrev_presplit=$(git rev-parse -q --verify "$localrevname_presplit^{commit}") ||
-                       die "'$localrevname_presplit' does not refer to a commit"
+                       die "fatal: '$localrevname_presplit' does not refer to a commit"
 
                echo "git push using: " "$repository" "$refspec"
-               localrev=$(cmd_split "$localrev_presplit") || die
+               localrev=$(cmd_split "$localrev_presplit" "$repository") || die
                git push "$repository" "$localrev":"refs/heads/$remoteref"
        else
-               die "'$dir' must already exist. Try 'git subtree add'."
+               die "fatal: '$dir' must already exist. Try 'git subtree add'."
        fi
 }
 
index 9cddfa26540a241d88c8ab049632797b6038e377..004abf415b8e5dd93eba0a9b0929587272565d37 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git subtree' [<options>] -P <prefix> add <local-commit>
 'git subtree' [<options>] -P <prefix> add <repository> <remote-ref>
-'git subtree' [<options>] -P <prefix> merge <local-commit>
+'git subtree' [<options>] -P <prefix> merge <local-commit> [<repository>]
 'git subtree' [<options>] -P <prefix> split [<local-commit>]
 
 [verse]
@@ -76,7 +76,7 @@ add <repository> <remote-ref>::
        only a single commit from the subproject, rather than its
        entire history.
 
-merge <local-commit>::
+merge <local-commit> [<repository>]::
        Merge recent changes up to <local-commit> into the <prefix>
        subtree.  As with normal 'git merge', this doesn't
        remove your own local changes; it just merges those
@@ -88,8 +88,13 @@ If you use '--squash', the merge direction doesn't always have to be
 forward; you can use this command to go back in time from v2.5 to v2.4,
 for example.  If your merge introduces a conflict, you can resolve it in
 the usual ways.
++
+When using '--squash', and the previous merge with '--squash' merged an
+annotated tag of the subtree repository, that tag needs to be available locally.
+If <repository> is given, a missing tag will automatically be fetched from that
+repository.
 
-split [<local-commit>]::
+split [<local-commit>] [<repository>]::
        Extract a new, synthetic project history from the
        history of the <prefix> subtree of <local-commit>, or of
        HEAD if no <local-commit> is given.  The new history
@@ -109,6 +114,11 @@ settings passed to 'split' (such as '--annotate') are the same.
 Because of this, if you add new commits and then re-split, the new
 commits will be attached as commits on top of the history you
 generated last time, so 'git merge' and friends will work as expected.
++
+When a previous merge with '--squash' merged an annotated tag of the
+subtree repository, that tag needs to be available locally.
+If <repository> is given, a missing tag will automatically be fetched from that
+repository.
 
 pull <repository> <remote-ref>::
        Exactly like 'merge', but parallels 'git pull' in that
index 4655e0987b32a837e966832eaebf89ed3ea829e9..093399c788178f3d4f005fc9d580e7c030e2d40a 100644 (file)
@@ -74,9 +74,7 @@ aggregate-results-and-cleanup: $(T)
        $(MAKE) clean
 
 aggregate-results:
-       for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
-               echo "$$f"; \
-       done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
+       @'$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh '$(TEST_RESULTS_DIRECTORY_SQ)'
 
 valgrind:
        $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
index 1c1f76f04aaed3f15fec9d750762f46ee86c4cab..49a21dd7c9c553f80a905ca8333b93fbe05fcc67 100755 (executable)
@@ -43,11 +43,35 @@ last_commit_subject () {
        git log --pretty=format:%s -1
 }
 
+# Upon 'git subtree add|merge --squash' of an annotated tag,
+# pre-2.32.0 versions of 'git subtree' would write the hash of the tag
+# (sub1 below), instead of the commit (sub1^{commit}) in the
+# "git-subtree-split" trailer.
+# We immitate this behaviour below using a replace ref.
+# This function creates 3 repositories:
+# - $1
+# - $1-sub (added as subtree "sub" in $1)
+# - $1-clone (clone of $1)
+test_create_pre2_32_repo () {
+       subtree_test_create_repo "$1" &&
+       subtree_test_create_repo "$1-sub" &&
+       test_commit -C "$1" main1 &&
+       test_commit -C "$1-sub" --annotate sub1 &&
+       git -C "$1" subtree add --prefix="sub" --squash "../$1-sub" sub1 &&
+       tag=$(git -C "$1" rev-parse FETCH_HEAD) &&
+       commit=$(git -C "$1" rev-parse FETCH_HEAD^{commit}) &&
+       git -C "$1" log -1 --format=%B HEAD^2 >msg &&
+       test_commit -C "$1-sub" --annotate sub2 &&
+       git clone --no-local "$1" "$1-clone" &&
+       new_commit=$(cat msg | sed -e "s/$commit/$tag/" | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
+       git -C "$1-clone" replace HEAD^2 $new_commit
+}
+
 test_expect_success 'shows short help text for -h' '
        test_expect_code 129 git subtree -h >out 2>err &&
        test_must_be_empty err &&
        grep -e "^ *or: git subtree pull" out &&
-       grep -e --annotate out
+       grep -F -e "--[no-]annotate" out
 '
 
 #
@@ -264,6 +288,13 @@ test_expect_success 'merge new subproj history into subdir/ with a slash appende
        )
 '
 
+test_expect_success 'merge with --squash after annotated tag was added/merged with --squash pre-v2.32.0 ' '
+       test_create_pre2_32_repo "$test_count" &&
+       git -C "$test_count-clone" fetch "../$test_count-sub" sub2  &&
+       test_must_fail git -C "$test_count-clone" subtree merge --prefix="sub" --squash FETCH_HEAD &&
+       git -C "$test_count-clone" subtree merge --prefix="sub" --squash FETCH_HEAD  "../$test_count-sub"
+'
+
 #
 # Tests for 'git subtree split'
 #
@@ -277,7 +308,7 @@ test_expect_success 'split requires option --prefix' '
                cd "$test_count" &&
                git fetch ./"sub proj" HEAD &&
                git subtree add --prefix="sub dir" FETCH_HEAD &&
-               echo "You must provide the --prefix option." >expected &&
+               echo "fatal: you must provide the --prefix option." >expected &&
                test_must_fail git subtree split >actual 2>&1 &&
                test_debug "printf '"expected: "'" &&
                test_debug "cat expected" &&
@@ -296,7 +327,7 @@ test_expect_success 'split requires path given by option --prefix must exist' '
                cd "$test_count" &&
                git fetch ./"sub proj" HEAD &&
                git subtree add --prefix="sub dir" FETCH_HEAD &&
-               echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" >expected &&
+               echo "fatal: '\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" >expected &&
                test_must_fail git subtree split --prefix=non-existent-directory >actual 2>&1 &&
                test_debug "printf '"expected: "'" &&
                test_debug "cat expected" &&
@@ -551,6 +582,12 @@ test_expect_success 'split "sub dir"/ with --branch for an incompatible branch'
        )
 '
 
+test_expect_success 'split after annotated tag was added/merged with --squash pre-v2.32.0' '
+       test_create_pre2_32_repo "$test_count" &&
+       test_must_fail git -C "$test_count-clone" subtree split --prefix="sub" HEAD &&
+       git -C "$test_count-clone" subtree split --prefix="sub" HEAD "../$test_count-sub"
+'
+
 #
 # Tests for 'git subtree pull'
 #
@@ -570,7 +607,7 @@ test_expect_success 'pull requires option --prefix' '
                cd "$test_count" &&
                test_must_fail git subtree pull ./"sub proj" HEAD >out 2>err &&
 
-               echo "You must provide the --prefix option." >expected &&
+               echo "fatal: you must provide the --prefix option." >expected &&
                test_must_be_empty out &&
                test_cmp expected err
        )
@@ -584,7 +621,7 @@ test_expect_success 'pull requires path given by option --prefix must exist' '
        (
                test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" HEAD >out 2>err &&
 
-               echo "'\''sub dir'\'' does not exist; use '\''git subtree add'\''" >expected &&
+               echo "fatal: '\''sub dir'\'' does not exist; use '\''git subtree add'\''" >expected &&
                test_must_be_empty out &&
                test_cmp expected err
        )
@@ -630,6 +667,11 @@ test_expect_success 'pull rejects flags for split' '
        )
 '
 
+test_expect_success 'pull with --squash after annotated tag was added/merged with --squash pre-v2.32.0 ' '
+       test_create_pre2_32_repo "$test_count" &&
+       git -C "$test_count-clone" subtree -d pull --prefix="sub" --squash "../$test_count-sub" sub2
+'
+
 #
 # Tests for 'git subtree push'
 #
@@ -643,7 +685,7 @@ test_expect_success 'push requires option --prefix' '
                cd "$test_count" &&
                git fetch ./"sub proj" HEAD &&
                git subtree add --prefix="sub dir" FETCH_HEAD &&
-               echo "You must provide the --prefix option." >expected &&
+               echo "fatal: you must provide the --prefix option." >expected &&
                test_must_fail git subtree push "./sub proj" from-mainline >actual 2>&1 &&
                test_debug "printf '"expected: "'" &&
                test_debug "cat expected" &&
@@ -662,7 +704,7 @@ test_expect_success 'push requires path given by option --prefix must exist' '
                cd "$test_count" &&
                git fetch ./"sub proj" HEAD &&
                git subtree add --prefix="sub dir" FETCH_HEAD &&
-               echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" >expected &&
+               echo "fatal: '\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" >expected &&
                test_must_fail git subtree push --prefix=non-existent-directory "./sub proj" from-mainline >actual 2>&1 &&
                test_debug "printf '"expected: "'" &&
                test_debug "cat expected" &&
@@ -953,6 +995,12 @@ test_expect_success 'push "sub dir"/ with a local rev' '
        )
 '
 
+test_expect_success 'push after annotated tag was added/merged with --squash pre-v2.32.0' '
+       test_create_pre2_32_repo "$test_count" &&
+       test_create_commit "$test_count-clone" sub/main-sub1 &&
+       git -C "$test_count-clone" subtree push --prefix="sub" "../$test_count-sub" from-mainline
+'
+
 #
 # Validity checking
 #
index 95e6a5244fc26c029abff85bb37a4e9fae71acba..a8870baff36a4a3042baf31c730793b45f6b6442 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1,14 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "config.h"
-#include "object-store.h"
+#include "convert.h"
+#include "copy.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "attr.h"
 #include "run-command.h"
 #include "quote.h"
+#include "read-cache-ll.h"
 #include "sigchain.h"
 #include "pkt-line.h"
 #include "sub-process.h"
+#include "trace.h"
 #include "utf8.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 
 /*
  * convert.c - convert a file when checking it out and checking it in.
@@ -626,23 +633,21 @@ static int filter_buffer_or_fd(int in UNUSED, int out, void *data)
         */
        struct child_process child_process = CHILD_PROCESS_INIT;
        struct filter_params *params = (struct filter_params *)data;
+       const char *format = params->cmd;
        int write_err, status;
 
        /* apply % substitution to cmd */
        struct strbuf cmd = STRBUF_INIT;
-       struct strbuf path = STRBUF_INIT;
-       struct strbuf_expand_dict_entry dict[] = {
-               { "f", NULL, },
-               { NULL, NULL, },
-       };
-
-       /* quote the path to preserve spaces, etc. */
-       sq_quote_buf(&path, params->path);
-       dict[0].value = path.buf;
 
-       /* expand all %f with the quoted path */
-       strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict);
-       strbuf_release(&path);
+       /* expand all %f with the quoted path; quote to preserve space, etc. */
+       while (strbuf_expand_step(&cmd, &format)) {
+               if (skip_prefix(format, "%", &format))
+                       strbuf_addch(&cmd, '%');
+               else if (skip_prefix(format, "f", &format))
+                       sq_quote_buf(&cmd, params->path);
+               else
+                       strbuf_addch(&cmd, '%');
+       }
 
        strvec_push(&child_process.args, cmd.buf);
        child_process.use_shell = 1;
@@ -1008,7 +1013,9 @@ static int apply_filter(const char *path, const char *src, size_t len,
        return 0;
 }
 
-static int read_convert_config(const char *var, const char *value, void *cb UNUSED)
+static int read_convert_config(const char *var, const char *value,
+                              const struct config_context *ctx UNUSED,
+                              void *cb UNUSED)
 {
        const char *key, *name;
        size_t namelen;
@@ -1549,7 +1556,7 @@ struct stream_filter {
        struct stream_filter_vtbl *vtbl;
 };
 
-static int null_filter_fn(struct stream_filter *filter,
+static int null_filter_fn(struct stream_filter *filter UNUSED,
                          const char *input, size_t *isize_p,
                          char *output, size_t *osize_p)
 {
@@ -1568,7 +1575,7 @@ static int null_filter_fn(struct stream_filter *filter,
        return 0;
 }
 
-static void null_free_fn(struct stream_filter *filter)
+static void null_free_fn(struct stream_filter *filter UNUSED)
 {
        ; /* nothing -- null instances are shared */
 }
index 0a6e4086b8f93257df513a20a4cc55062d218212..ab8b4fa68d67ba3da1fc7900b616dc70a9967eba 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -4,7 +4,7 @@
 #ifndef CONVERT_H
 #define CONVERT_H
 
-#include "hash.h"
+#include "hash-ll.h"
 #include "string-list.h"
 
 struct index_state;
@@ -92,7 +92,7 @@ void convert_attrs(struct index_state *istate,
                   struct conv_attrs *ca, const char *path);
 
 extern enum eol core_eol;
-extern char *check_roundtrip_encoding;
+extern const char *check_roundtrip_encoding;
 const char *get_cached_convert_stats_ascii(struct index_state *istate,
                                           const char *path);
 const char *get_wt_convert_stats_ascii(const char *path);
diff --git a/copy.c b/copy.c
index 4de6a110f0912d81def3f2dd4ffd6ddc9bc30d2f..23d84c6c1db554bcc7a33804d58821dfb3fb7634 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "copy.h"
+#include "path.h"
 
 int copy_fd(int ifd, int ofd)
 {
diff --git a/copy.h b/copy.h
new file mode 100644 (file)
index 0000000..2af77cb
--- /dev/null
+++ b/copy.h
@@ -0,0 +1,10 @@
+#ifndef COPY_H
+#define COPY_H
+
+#define COPY_READ_ERROR (-2)
+#define COPY_WRITE_ERROR (-3)
+int copy_fd(int ifd, int ofd);
+int copy_file(const char *dst, const char *src, int mode);
+int copy_file_with_time(const char *dst, const char *src, int mode);
+
+#endif /* COPY_H */
index f6389a50684a6e99bad1fe39b04a96fb99abc726..18098bd35ebab9683ae0046c1712b111eaeb3fe1 100644 (file)
@@ -1,12 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
 #include "credential.h"
+#include "gettext.h"
 #include "string-list.h"
 #include "run-command.h"
 #include "url.h"
 #include "prompt.h"
 #include "sigchain.h"
+#include "strbuf.h"
 #include "urlmatch.h"
+#include "git-compat-util.h"
 
 void credential_init(struct credential *c)
 {
@@ -21,19 +25,22 @@ void credential_clear(struct credential *c)
        free(c->path);
        free(c->username);
        free(c->password);
+       free(c->oauth_refresh_token);
        string_list_clear(&c->helpers, 0);
+       strvec_clear(&c->wwwauth_headers);
 
        credential_init(c);
 }
 
 int credential_match(const struct credential *want,
-                    const struct credential *have)
+                    const struct credential *have, int match_password)
 {
 #define CHECK(x) (!want->x || (have->x && !strcmp(want->x, have->x)))
        return CHECK(protocol) &&
               CHECK(host) &&
               CHECK(path) &&
-              CHECK(username);
+              CHECK(username) &&
+              (!match_password || CHECK(password));
 #undef CHECK
 }
 
@@ -42,6 +49,7 @@ static int credential_from_potentially_partial_url(struct credential *c,
                                                   const char *url);
 
 static int credential_config_callback(const char *var, const char *value,
+                                     const struct config_context *ctx UNUSED,
                                      void *data)
 {
        struct credential *c = data;
@@ -80,8 +88,8 @@ static int proto_is_http(const char *s)
 static void credential_describe(struct credential *c, struct strbuf *out);
 static void credential_format(struct credential *c, struct strbuf *out);
 
-static int select_all(const struct urlmatch_item *a,
-                     const struct urlmatch_item *b)
+static int select_all(const struct urlmatch_item *a UNUSED,
+                     const struct urlmatch_item *b UNUSED)
 {
        return 0;
 }
@@ -96,7 +104,7 @@ static int match_partial_url(const char *url, void *cb)
                warning(_("skipping credential lookup for key: credential.%s"),
                        url);
        else
-               matches = credential_match(&want, c);
+               matches = credential_match(&want, c, 0);
        credential_clear(&want);
 
        return matches;
@@ -234,6 +242,16 @@ int credential_read(struct credential *c, FILE *fp)
                } else if (!strcmp(key, "path")) {
                        free(c->path);
                        c->path = xstrdup(value);
+               } else if (!strcmp(key, "wwwauth[]")) {
+                       strvec_push(&c->wwwauth_headers, value);
+               } else if (!strcmp(key, "password_expiry_utc")) {
+                       errno = 0;
+                       c->password_expiry_utc = parse_timestamp(value, NULL, 10);
+                       if (c->password_expiry_utc == 0 || errno == ERANGE)
+                               c->password_expiry_utc = TIME_MAX;
+               } else if (!strcmp(key, "oauth_refresh_token")) {
+                       free(c->oauth_refresh_token);
+                       c->oauth_refresh_token = xstrdup(value);
                } else if (!strcmp(key, "url")) {
                        credential_from_url(c, value);
                } else if (!strcmp(key, "quit")) {
@@ -269,6 +287,14 @@ void credential_write(const struct credential *c, FILE *fp)
        credential_write_item(fp, "path", c->path, 0);
        credential_write_item(fp, "username", c->username, 0);
        credential_write_item(fp, "password", c->password, 0);
+       credential_write_item(fp, "oauth_refresh_token", c->oauth_refresh_token, 0);
+       if (c->password_expiry_utc != TIME_MAX) {
+               char *s = xstrfmt("%"PRItime, c->password_expiry_utc);
+               credential_write_item(fp, "password_expiry_utc", s, 0);
+               free(s);
+       }
+       for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
+               credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
 }
 
 static int run_credential_helper(struct credential *c,
@@ -342,6 +368,12 @@ void credential_fill(struct credential *c)
 
        for (i = 0; i < c->helpers.nr; i++) {
                credential_do(c, c->helpers.items[i].string, "get");
+               if (c->password_expiry_utc < time(NULL)) {
+                       /* Discard expired password */
+                       FREE_AND_NULL(c->password);
+                       /* Reset expiry to maintain consistency */
+                       c->password_expiry_utc = TIME_MAX;
+               }
                if (c->username && c->password)
                        return;
                if (c->quit)
@@ -360,7 +392,7 @@ void credential_approve(struct credential *c)
 
        if (c->approved)
                return;
-       if (!c->username || !c->password)
+       if (!c->username || !c->password || c->password_expiry_utc < time(NULL))
                return;
 
        credential_apply_config(c);
@@ -381,6 +413,8 @@ void credential_reject(struct credential *c)
 
        FREE_AND_NULL(c->username);
        FREE_AND_NULL(c->password);
+       FREE_AND_NULL(c->oauth_refresh_token);
+       c->password_expiry_utc = TIME_MAX;
        c->approved = 0;
 }
 
index f430e77fea4869ea98156add3956e15a9fc94755..acc41adf5481a21148a6b4797f9a8267feb5d8b6 100644 (file)
@@ -2,6 +2,7 @@
 #define CREDENTIAL_H
 
 #include "string-list.h"
+#include "strvec.h"
 
 /**
  * The credentials API provides an abstracted way of gathering username and
@@ -115,6 +116,20 @@ struct credential {
         */
        struct string_list helpers;
 
+       /**
+        * A `strvec` of WWW-Authenticate header values. Each string
+        * is the value of a WWW-Authenticate header in an HTTP response,
+        * in the order they were received in the response.
+        */
+       struct strvec wwwauth_headers;
+
+       /**
+        * Internal use only. Keeps track of if we previously matched against a
+        * WWW-Authenticate header line in order to re-fold future continuation
+        * lines into one value.
+        */
+       unsigned header_is_last_match:1;
+
        unsigned approved:1,
                 configured:1,
                 quit:1,
@@ -126,10 +141,14 @@ struct credential {
        char *protocol;
        char *host;
        char *path;
+       char *oauth_refresh_token;
+       timestamp_t password_expiry_utc;
 };
 
 #define CREDENTIAL_INIT { \
        .helpers = STRING_LIST_INIT_DUP, \
+       .password_expiry_utc = TIME_MAX, \
+       .wwwauth_headers = STRVEC_INIT, \
 }
 
 /* Initialize a credential structure, setting all fields to empty. */
@@ -192,6 +211,6 @@ void credential_from_url(struct credential *, const char *url);
 int credential_from_url_gently(struct credential *, const char *url, int quiet);
 
 int credential_match(const struct credential *want,
-                    const struct credential *have);
+                    const struct credential *have, int match_password);
 
 #endif /* CREDENTIAL_H */
index 59ef3398ca2b01676055fff40b80ac7896f87ffc..870748e01695f865e1e4d587cdb38f769367a5c6 100644 (file)
@@ -7,9 +7,10 @@
  * files. Useful when you write a file that you want to be
  * able to verify hasn't been messed with afterwards.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "progress.h"
 #include "csum-file.h"
+#include "hash.h"
 
 static void verify_buffer_or_die(struct hashfile *f,
                                 const void *buf,
@@ -45,7 +46,8 @@ void hashflush(struct hashfile *f)
        unsigned offset = f->offset;
 
        if (offset) {
-               the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
+               if (!f->skip_hash)
+                       the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
                flush(f, f->buffer, offset);
                f->offset = 0;
        }
@@ -64,7 +66,12 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
        int fd;
 
        hashflush(f);
-       the_hash_algo->final_fn(f->buffer, &f->ctx);
+
+       if (f->skip_hash)
+               hashclr(f->buffer);
+       else
+               the_hash_algo->final_fn(f->buffer, &f->ctx);
+
        if (result)
                hashcpy(result, f->buffer);
        if (flags & CSUM_HASH_IN_STREAM)
@@ -108,7 +115,8 @@ void hashwrite(struct hashfile *f, const void *buf, unsigned int count)
                         * the hashfile's buffer. In this block,
                         * f->offset is necessarily zero.
                         */
-                       the_hash_algo->update_fn(&f->ctx, buf, nr);
+                       if (!f->skip_hash)
+                               the_hash_algo->update_fn(&f->ctx, buf, nr);
                        flush(f, buf, nr);
                } else {
                        /*
@@ -153,6 +161,7 @@ static struct hashfile *hashfd_internal(int fd, const char *name,
        f->tp = tp;
        f->name = name;
        f->do_crc = 0;
+       f->skip_hash = 0;
        the_hash_algo->init_fn(&f->ctx);
 
        f->buffer_len = buffer_len;
@@ -198,7 +207,7 @@ int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint
            lseek(f->fd, offset, SEEK_SET) != offset)
                return -1;
        f->total = offset;
-       f->ctx = checkpoint->ctx;
+       the_hash_algo->clone_fn(&f->ctx, &checkpoint->ctx);
        f->offset = 0; /* hashflush() was called in checkpoint */
        return 0;
 }
index 0d29f528fbcb51df13eac225c0e2cc120198bd3f..bc5bec27acbab4a3303f4ea11575b0c1accda806 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef CSUM_FILE_H
 #define CSUM_FILE_H
 
-#include "cache.h"
-#include "hash.h"
+#include "hash-ll.h"
+#include "write-or-die.h"
 
 struct progress;
 
@@ -20,6 +20,13 @@ struct hashfile {
        size_t buffer_len;
        unsigned char *buffer;
        unsigned char *check_buffer;
+
+       /**
+        * If non-zero, skip_hash indicates that we should
+        * not actually compute the hash for this hashfile and
+        * instead only use it as a buffered write.
+        */
+       int skip_hash;
 };
 
 /* Checkpoint */
diff --git a/ctype.c b/ctype.c
index fc0225cebd1a4b43654772270826e06fd941a128..345174555077984f695dba702593e08fbed95031 100644 (file)
--- a/ctype.c
+++ b/ctype.c
@@ -28,39 +28,3 @@ const unsigned char sane_ctype[256] = {
        A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X,         /* 112..127 */
        /* Nothing in the 128.. range */
 };
-
-/* For case-insensitive kwset */
-const unsigned char tolower_trans_tbl[256] = {
-       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-        ' ',  '!',  '"',  '#',  '$',  '%',  '&', 0x27,
-        '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
-        '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
-        '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
-        '@',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
-        'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
-        'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
-        'x',  'y',  'z',  '[', 0x5c,  ']',  '^',  '_',
-        '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
-        'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
-        'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
-        'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7f,
-       0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
-       0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
-       0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
-       0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
-       0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
-       0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
-       0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
-       0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
-       0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
-       0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
-       0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
-       0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
-       0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
-       0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
-       0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
-       0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
-};
index 0ae7d12b5c132883907192d16c3e2f04d56a80fb..17d331b2f38465e295c019e534eff371ef3e6e49 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1,7 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "path.h"
 #include "pkt-line.h"
+#include "protocol.h"
 #include "run-command.h"
+#include "setup.h"
 #include "strbuf.h"
 #include "string-list.h"
 
@@ -137,42 +142,6 @@ static void NORETURN daemon_die(const char *err, va_list params)
        exit(1);
 }
 
-struct expand_path_context {
-       const char *directory;
-       struct hostinfo *hostinfo;
-};
-
-static size_t expand_path(struct strbuf *sb, const char *placeholder, void *ctx)
-{
-       struct expand_path_context *context = ctx;
-       struct hostinfo *hi = context->hostinfo;
-
-       switch (placeholder[0]) {
-       case 'H':
-               strbuf_addbuf(sb, &hi->hostname);
-               return 1;
-       case 'C':
-               if (placeholder[1] == 'H') {
-                       strbuf_addstr(sb, get_canon_hostname(hi));
-                       return 2;
-               }
-               break;
-       case 'I':
-               if (placeholder[1] == 'P') {
-                       strbuf_addstr(sb, get_ip_address(hi));
-                       return 2;
-               }
-               break;
-       case 'P':
-               strbuf_addbuf(sb, &hi->tcp_port);
-               return 1;
-       case 'D':
-               strbuf_addstr(sb, context->directory);
-               return 1;
-       }
-       return 0;
-}
-
 static const char *path_ok(const char *directory, struct hostinfo *hi)
 {
        static char rpath[PATH_MAX];
@@ -216,10 +185,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
        }
        else if (interpolated_path && hi->saw_extended_args) {
                struct strbuf expanded_path = STRBUF_INIT;
-               struct expand_path_context context;
-
-               context.directory = directory;
-               context.hostinfo = hi;
+               const char *format = interpolated_path;
 
                if (*dir != '/') {
                        /* Allow only absolute */
@@ -227,8 +193,24 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
                        return NULL;
                }
 
-               strbuf_expand(&expanded_path, interpolated_path,
-                             expand_path, &context);
+               while (strbuf_expand_step(&expanded_path, &format)) {
+                       if (skip_prefix(format, "%", &format))
+                               strbuf_addch(&expanded_path, '%');
+                       else if (skip_prefix(format, "H", &format))
+                               strbuf_addbuf(&expanded_path, &hi->hostname);
+                       else if (skip_prefix(format, "CH", &format))
+                               strbuf_addstr(&expanded_path,
+                                             get_canon_hostname(hi));
+                       else if (skip_prefix(format, "IP", &format))
+                               strbuf_addstr(&expanded_path,
+                                             get_ip_address(hi));
+                       else if (skip_prefix(format, "P", &format))
+                               strbuf_addbuf(&expanded_path, &hi->tcp_port);
+                       else if (skip_prefix(format, "D", &format))
+                               strbuf_addstr(&expanded_path, directory);
+                       else
+                               strbuf_addch(&expanded_path, '%');
+               }
 
                rlen = strlcpy(interp_path, expanded_path.buf,
                               sizeof(interp_path));
@@ -928,7 +910,7 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
                add_child(&cld, addr, addrlen);
 }
 
-static void child_handler(int signo)
+static void child_handler(int signo UNUSED)
 {
        /*
         * Otherwise empty handler because systemcalls will get interrupted
@@ -1261,19 +1243,20 @@ static int serve(struct string_list *listen_addr, int listen_port,
 int cmd_main(int argc, const char **argv)
 {
        int listen_port = 0;
-       struct string_list listen_addr = STRING_LIST_INIT_NODUP;
+       struct string_list listen_addr = STRING_LIST_INIT_DUP;
        int serve_mode = 0, inetd_mode = 0;
        const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
        int detach = 0;
        struct credentials *cred = NULL;
        int i;
+       int ret;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                const char *v;
 
                if (skip_prefix(arg, "--listen=", &v)) {
-                       string_list_append(&listen_addr, xstrdup_tolower(v));
+                       string_list_append_nodup(&listen_addr, xstrdup_tolower(v));
                        continue;
                }
                if (skip_prefix(arg, "--port=", &v)) {
@@ -1455,22 +1438,26 @@ int cmd_main(int argc, const char **argv)
                        die_errno("failed to redirect stderr to /dev/null");
        }
 
-       if (inetd_mode || serve_mode)
-               return execute();
+       if (inetd_mode || serve_mode) {
+               ret = execute();
+       } else {
+               if (detach) {
+                       if (daemonize())
+                               die("--detach not supported on this platform");
+               }
 
-       if (detach) {
-               if (daemonize())
-                       die("--detach not supported on this platform");
-       }
+               if (pid_file)
+                       write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
 
-       if (pid_file)
-               write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
+               /* prepare argv for serving-processes */
+               strvec_push(&cld_argv, argv[0]); /* git-daemon */
+               strvec_push(&cld_argv, "--serve");
+               for (i = 1; i < argc; ++i)
+                       strvec_push(&cld_argv, argv[i]);
 
-       /* prepare argv for serving-processes */
-       strvec_push(&cld_argv, argv[0]); /* git-daemon */
-       strvec_push(&cld_argv, "--serve");
-       for (i = 1; i < argc; ++i)
-               strvec_push(&cld_argv, argv[i]);
+               ret = serve(&listen_addr, listen_port, cred);
+       }
 
-       return serve(&listen_addr, listen_port, cred);
+       string_list_clear(&listen_addr, 0);
+       return ret;
 }
diff --git a/date.c b/date.c
index 68a260c214d333f61bf1c9156405520e8fc9c361..619ada5b20442046ef50a38d4e88136b14419a42 100644 (file)
--- a/date.c
+++ b/date.c
@@ -4,8 +4,11 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "date.h"
+#include "gettext.h"
+#include "pager.h"
+#include "strbuf.h"
 
 /*
  * This is like mktime, but without normalization of tm_wday and tm_yday.
@@ -493,6 +496,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)
                return 2;
        }
 
+       /* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */
+       if (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {
+               tm->tm_min = tm->tm_sec = 0;
+               return 1;
+       }
+
        /* BAD CRAP */
        return skip_alpha(date);
 }
@@ -638,6 +647,18 @@ static inline int nodate(struct tm *tm)
                tm->tm_sec) < 0;
 }
 
+/*
+ * Have we seen an ISO-8601-alike date, i.e. 20220101T0,
+ * In which, hour is still unset,
+ * and minutes and second has been set to 0.
+ */
+static inline int maybeiso8601(struct tm *tm)
+{
+       return tm->tm_hour == -1 &&
+               tm->tm_min == 0 &&
+               tm->tm_sec == 0;
+}
+
 /*
  * We've seen a digit. Time? Year? Date?
  */
@@ -701,6 +722,25 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
                return end - date;
        }
 
+       /* reduced precision of ISO-8601's time: HHMM or HH */
+       if (maybeiso8601(tm)) {
+               unsigned int num1 = num;
+               unsigned int num2 = 0;
+               if (n == 4) {
+                       num1 = num / 100;
+                       num2 = num % 100;
+               }
+               if ((n == 4 || n == 2) && !nodate(tm) &&
+                   set_time(num1, num2, 0, tm) == 0)
+                       return n;
+               /*
+                * We thought this is an ISO-8601 time string,
+                * we set minutes and seconds to 0,
+                * turn out it isn't, rollback the change.
+                */
+               tm->tm_min = tm->tm_sec = -1;
+       }
+
        /* Four-digit year or a timezone? */
        if (n == 4) {
                if (num <= 1400 && *offset == -1) {
@@ -1101,7 +1141,7 @@ static void date_tea(struct tm *tm, struct tm *now, int *num)
        date_time(tm, now, 17);
 }
 
-static void date_pm(struct tm *tm, struct tm *now, int *num)
+static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)
 {
        int hour, n = *num;
        *num = 0;
@@ -1115,7 +1155,7 @@ static void date_pm(struct tm *tm, struct tm *now, int *num)
        tm->tm_hour = (hour % 12) + 12;
 }
 
-static void date_am(struct tm *tm, struct tm *now, int *num)
+static void date_am(struct tm *tm, struct tm *now UNUSED, int *num)
 {
        int hour, n = *num;
        *num = 0;
@@ -1129,7 +1169,7 @@ static void date_am(struct tm *tm, struct tm *now, int *num)
        tm->tm_hour = (hour % 12);
 }
 
-static void date_never(struct tm *tm, struct tm *now, int *num)
+static void date_never(struct tm *tm, struct tm *now UNUSED, int *num)
 {
        time_t n = 0;
        localtime_r(&n, tm);
@@ -1328,20 +1368,6 @@ static timestamp_t approxidate_str(const char *date,
        return (timestamp_t)update_tm(&tm, &now, 0);
 }
 
-timestamp_t approxidate_relative(const char *date)
-{
-       struct timeval tv;
-       timestamp_t timestamp;
-       int offset;
-       int errors = 0;
-
-       if (!parse_date_basic(date, &timestamp, &offset))
-               return timestamp;
-
-       get_time(&tv);
-       return approxidate_str(date, (const struct timeval *) &tv, &errors);
-}
-
 timestamp_t approxidate_careful(const char *date, int *error_ret)
 {
        struct timeval tv;
diff --git a/date.h b/date.h
index 5d4eaba0a90e39f8684bb5500695ba07b5ea5521..6136212a19004497382e5bae8d6675ebaac62011 100644 (file)
--- a/date.h
+++ b/date.h
@@ -68,7 +68,6 @@ int parse_expiry_date(const char *date, timestamp_t *timestamp);
 void datestamp(struct strbuf *out);
 #define approxidate(s) approxidate_careful((s), NULL)
 timestamp_t approxidate_careful(const char *, int *);
-timestamp_t approxidate_relative(const char *date);
 int date_overflows(timestamp_t date);
 time_t tm_to_time_t(const struct tm *tm);
 #endif
index 2036d15967125303effff870a45066710ef99e3e..69aeb142b45e9fee08d61dace73f6dc2bce1c2d0 100644 (file)
@@ -2,7 +2,7 @@
  * decorate.c - decorate a git object with some arbitrary
  * data.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "object.h"
 #include "decorate.h"
 
@@ -81,3 +81,18 @@ void *lookup_decoration(struct decoration *n, const struct object *obj)
                        j = 0;
        }
 }
+
+void clear_decoration(struct decoration *n, void (*free_cb)(void *))
+{
+       if (free_cb) {
+               unsigned int i;
+               for (i = 0; i < n->size; i++) {
+                       void *d = n->entries[i].decoration;
+                       if (d)
+                               free_cb(d);
+               }
+       }
+
+       FREE_AND_NULL(n->entries);
+       n->size = n->nr = 0;
+}
index ee43dee1f008882094ca9c85f6b28b86ec88fdf6..cdeb17c9df2eb680d85e73e8ed2f48b52f44569f 100644 (file)
@@ -58,4 +58,14 @@ void *add_decoration(struct decoration *n, const struct object *obj, void *decor
  */
 void *lookup_decoration(struct decoration *n, const struct object *obj);
 
+/*
+ * Clear all decoration entries, releasing any memory used by the structure.
+ * If free_cb is not NULL, it is called for every decoration value currently
+ * stored.
+ *
+ * After clearing, the decoration struct can be used again. The "name" field is
+ * retained.
+ */
+void clear_decoration(struct decoration *n, void (*free_cb)(void *));
+
 #endif
index 26f9e99e1a978921d9ec19725c09a88247548d95..ee2318d45a1c7898b39999b1f5fc81be8ec49f00 100644 (file)
@@ -1,16 +1,13 @@
-#include "cache.h"
-#include "attr.h"
+#include "git-compat-util.h"
 #include "object.h"
-#include "blob.h"
 #include "commit.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tag.h"
 #include "tree.h"
-#include "delta.h"
 #include "pack.h"
 #include "tree-walk.h"
 #include "diff.h"
-#include "revision.h"
-#include "list-objects.h"
 #include "progress.h"
 #include "refs.h"
 #include "khash.h"
@@ -26,8 +23,6 @@ static kh_oid_map_t *island_marks;
 static unsigned island_counter;
 static unsigned island_counter_core;
 
-static kh_str_t *remote_islands;
-
 struct remote_island {
        uint64_t hash;
        struct oid_array oids;
@@ -312,29 +307,57 @@ void resolve_tree_islands(struct repository *r,
        free(todo);
 }
 
-static regex_t *island_regexes;
-static unsigned int island_regexes_alloc, island_regexes_nr;
+struct island_load_data {
+       kh_str_t *remote_islands;
+       regex_t *rx;
+       size_t nr;
+       size_t alloc;
+};
 static const char *core_island_name;
 
-static int island_config_callback(const char *k, const char *v, void *cb UNUSED)
+static void free_config_regexes(struct island_load_data *ild)
+{
+       for (size_t i = 0; i < ild->nr; i++)
+               regfree(&ild->rx[i]);
+       free(ild->rx);
+}
+
+static void free_remote_islands(kh_str_t *remote_islands)
+{
+       const char *island_name;
+       struct remote_island *rl;
+
+       kh_foreach(remote_islands, island_name, rl, {
+               free((void *)island_name);
+               oid_array_clear(&rl->oids);
+               free(rl);
+       });
+       kh_destroy_str(remote_islands);
+}
+
+static int island_config_callback(const char *k, const char *v,
+                                 const struct config_context *ctx UNUSED,
+                                 void *cb)
 {
+       struct island_load_data *ild = cb;
+
        if (!strcmp(k, "pack.island")) {
                struct strbuf re = STRBUF_INIT;
 
                if (!v)
                        return config_error_nonbool(k);
 
-               ALLOC_GROW(island_regexes, island_regexes_nr + 1, island_regexes_alloc);
+               ALLOC_GROW(ild->rx, ild->nr + 1, ild->alloc);
 
                if (*v != '^')
                        strbuf_addch(&re, '^');
                strbuf_addstr(&re, v);
 
-               if (regcomp(&island_regexes[island_regexes_nr], re.buf, REG_EXTENDED))
+               if (regcomp(&ild->rx[ild->nr], re.buf, REG_EXTENDED))
                        die(_("failed to load island regex for '%s': %s"), k, re.buf);
 
                strbuf_release(&re);
-               island_regexes_nr++;
+               ild->nr++;
                return 0;
        }
 
@@ -344,7 +367,8 @@ static int island_config_callback(const char *k, const char *v, void *cb UNUSED)
        return 0;
 }
 
-static void add_ref_to_island(const char *island_name, const struct object_id *oid)
+static void add_ref_to_island(kh_str_t *remote_islands, const char *island_name,
+                               const struct object_id *oid)
 {
        uint64_t sha_core;
        struct remote_island *rl = NULL;
@@ -365,8 +389,10 @@ static void add_ref_to_island(const char *island_name, const struct object_id *o
 }
 
 static int find_island_for_ref(const char *refname, const struct object_id *oid,
-                              int flags UNUSED, void *data UNUSED)
+                              int flags UNUSED, void *cb)
 {
+       struct island_load_data *ild = cb;
+
        /*
         * We should advertise 'ARRAY_SIZE(matches) - 2' as the max,
         * so we can diagnose below a config with more capture groups
@@ -377,8 +403,8 @@ static int find_island_for_ref(const char *refname, const struct object_id *oid,
        struct strbuf island_name = STRBUF_INIT;
 
        /* walk backwards to get last-one-wins ordering */
-       for (i = island_regexes_nr - 1; i >= 0; i--) {
-               if (!regexec(&island_regexes[i], refname,
+       for (i = ild->nr - 1; i >= 0; i--) {
+               if (!regexec(&ild->rx[i], refname,
                             ARRAY_SIZE(matches), matches, 0))
                        break;
        }
@@ -403,12 +429,12 @@ static int find_island_for_ref(const char *refname, const struct object_id *oid,
                strbuf_add(&island_name, refname + match->rm_so, match->rm_eo - match->rm_so);
        }
 
-       add_ref_to_island(island_name.buf, oid);
+       add_ref_to_island(ild->remote_islands, island_name.buf, oid);
        strbuf_release(&island_name);
        return 0;
 }
 
-static struct remote_island *get_core_island(void)
+static struct remote_island *get_core_island(kh_str_t *remote_islands)
 {
        if (core_island_name) {
                khiter_t pos = kh_get_str(remote_islands, core_island_name);
@@ -419,7 +445,7 @@ static struct remote_island *get_core_island(void)
        return NULL;
 }
 
-static void deduplicate_islands(struct repository *r)
+static void deduplicate_islands(kh_str_t *remote_islands, struct repository *r)
 {
        struct remote_island *island, *core = NULL, **list;
        unsigned int island_count, dst, src, ref, i = 0;
@@ -445,7 +471,7 @@ static void deduplicate_islands(struct repository *r)
        }
 
        island_bitmap_size = (island_count / 32) + 1;
-       core = get_core_island();
+       core = get_core_island(remote_islands);
 
        for (i = 0; i < island_count; ++i) {
                mark_remote_island_1(r, list[i], core && list[i]->hash == core->hash);
@@ -456,12 +482,16 @@ static void deduplicate_islands(struct repository *r)
 
 void load_delta_islands(struct repository *r, int progress)
 {
+       struct island_load_data ild = { 0 };
+
        island_marks = kh_init_oid_map();
-       remote_islands = kh_init_str();
 
-       git_config(island_config_callback, NULL);
-       for_each_ref(find_island_for_ref, NULL);
-       deduplicate_islands(r);
+       git_config(island_config_callback, &ild);
+       ild.remote_islands = kh_init_str();
+       for_each_ref(find_island_for_ref, &ild);
+       free_config_regexes(&ild);
+       deduplicate_islands(ild.remote_islands, r);
+       free_remote_islands(ild.remote_islands);
 
        if (progress)
                fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
@@ -475,13 +505,30 @@ void propagate_island_marks(struct commit *commit)
                struct commit_list *p;
                struct island_bitmap *root_marks = kh_value(island_marks, pos);
 
-               parse_commit(commit);
-               set_island_marks(&get_commit_tree(commit)->object, root_marks);
+               repo_parse_commit(the_repository, commit);
+               set_island_marks(&repo_get_commit_tree(the_repository, commit)->object,
+                                root_marks);
                for (p = commit->parents; p; p = p->next)
                        set_island_marks(&p->item->object, root_marks);
        }
 }
 
+void free_island_marks(void)
+{
+       struct island_bitmap *bitmap;
+
+       if (island_marks) {
+               kh_foreach_value(island_marks, bitmap, {
+                       if (!--bitmap->refcount)
+                               free(bitmap);
+               });
+               kh_destroy_oid_map(island_marks);
+       }
+
+       /* detect use-after-free with a an address which is never valid: */
+       island_marks = (void *)-1;
+}
+
 int compute_pack_layers(struct packing_data *to_pack)
 {
        uint32_t i;
index eb0f952629fc0a6cdcc113e19a04ddaa54bb32dd..8d1591ae28be302d2bb56fe05ad6c3465bf7668a 100644 (file)
@@ -14,5 +14,6 @@ void resolve_tree_islands(struct repository *r,
 void load_delta_islands(struct repository *r, int progress);
 void propagate_island_marks(struct commit *commit);
 int compute_pack_layers(struct packing_data *to_pack);
+void free_island_marks(void);
 
 #endif /* DELTA_ISLANDS_H */
index 50087f567062c4ff858bd6cae83fb3c59a0e4038..a87650b71bb095d403993486a7f2af6967be4d55 100755 (executable)
@@ -17,7 +17,15 @@ get_family() {
 }
 
 get_version() {
-       get_version_line | sed 's/^.* version \([0-9][^ ]*\).*/\1/'
+       # A string that begins with a digit up to the next SP
+       ver=$(get_version_line | sed 's/^.* version \([0-9][^ ]*\).*/\1/')
+
+       # There are known -variant suffixes that do not affect the
+       # meaning of the main version number.  Strip them.
+       ver=${ver%-win32}
+       ver=${ver%-posix}
+
+       echo "$ver"
 }
 
 print_flags() {
index 8f2656989666b67a3281d29a83f73ff82a006a1d..4d096c857f1e669a44bc2e43375889ce2952083c 100644 (file)
@@ -1,12 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diagnose.h"
 #include "compat/disk.h"
 #include "archive.h"
 #include "dir.h"
 #include "help.h"
+#include "gettext.h"
+#include "hex.h"
 #include "strvec.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "packfile.h"
+#include "parse-options.h"
+#include "write-or-die.h"
 
 struct archive_dir {
        const char *path;
@@ -43,7 +47,8 @@ int option_parse_diagnose(const struct option *opt, const char *arg, int unset)
        return error(_("invalid --%s value '%s'"), opt->long_name, arg);
 }
 
-static void dir_file_stats_objects(const char *full_path, size_t full_path_len,
+static void dir_file_stats_objects(const char *full_path,
+                                  size_t full_path_len UNUSED,
                                   const char *file_name, void *data)
 {
        struct strbuf *buf = data;
@@ -66,42 +71,6 @@ static int dir_file_stats(struct object_directory *object_dir, void *data)
        return 0;
 }
 
-/*
- * Get the d_type of a dirent. If the d_type is unknown, derive it from
- * stat.st_mode.
- *
- * Note that 'path' is assumed to have a trailing slash. It is also modified
- * in-place during the execution of the function, but is then reverted to its
- * original value before returning.
- */
-static unsigned char get_dtype(struct dirent *e, struct strbuf *path)
-{
-       struct stat st;
-       unsigned char dtype = DTYPE(e);
-       size_t base_path_len;
-
-       if (dtype != DT_UNKNOWN)
-               return dtype;
-
-       /* d_type unknown in dirent, try to fall back on lstat results */
-       base_path_len = path->len;
-       strbuf_addstr(path, e->d_name);
-       if (lstat(path->buf, &st))
-               goto cleanup;
-
-       /* determine d_type from st_mode */
-       if (S_ISREG(st.st_mode))
-               dtype = DT_REG;
-       else if (S_ISDIR(st.st_mode))
-               dtype = DT_DIR;
-       else if (S_ISLNK(st.st_mode))
-               dtype = DT_LNK;
-
-cleanup:
-       strbuf_setlen(path, base_path_len);
-       return dtype;
-}
-
 static int count_files(struct strbuf *path)
 {
        DIR *dir = opendir(path->buf);
@@ -112,7 +81,7 @@ static int count_files(struct strbuf *path)
                return 0;
 
        while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL)
-               if (get_dtype(e, path) == DT_REG)
+               if (get_dtype(e, path, 0) == DT_REG)
                        count++;
 
        closedir(dir);
@@ -141,7 +110,7 @@ static void loose_objs_stats(struct strbuf *buf, const char *path)
        base_path_len = count_path.len;
 
        while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL)
-               if (get_dtype(e, &count_path) == DT_DIR &&
+               if (get_dtype(e, &count_path, 0) == DT_DIR &&
                    strlen(e->d_name) == 2 &&
                    !hex_to_bytes(&c, e->d_name, 1)) {
                        strbuf_setlen(&count_path, base_path_len);
@@ -186,7 +155,7 @@ static int add_directory_to_archiver(struct strvec *archiver_args,
 
                strbuf_add_absolute_path(&abspath, at_root ? "." : path);
                strbuf_addch(&abspath, '/');
-               dtype = get_dtype(e, &abspath);
+               dtype = get_dtype(e, &abspath, 0);
 
                strbuf_setlen(&buf, len);
                strbuf_addstr(&buf, e->d_name);
index 7a4951a7863bcea8ac37e2d40d656b794f9bc925..f525219ab0cf9b36249892c847e731b952b081af 100644 (file)
@@ -2,7 +2,8 @@
 #define DIAGNOSE_H
 
 #include "strbuf.h"
-#include "parse-options.h"
+
+struct option;
 
 enum diagnose_mode {
        DIAGNOSE_NONE,
index 2edea41a2345af623017d43e14a74d71a32bb873..6c8df04273702fa47215dffa263477df347900ee 100644 (file)
@@ -1,16 +1,23 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
-#include "quote.h"
+#include "git-compat-util.h"
 #include "commit.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "object-name.h"
+#include "read-cache.h"
 #include "revision.h"
 #include "cache-tree.h"
 #include "unpack-trees.h"
 #include "refs.h"
+#include "repository.h"
 #include "submodule.h"
+#include "symlinks.h"
+#include "trace.h"
 #include "dir.h"
 #include "fsmonitor.h"
 #include "commit-reach.h"
  * exists for ce that is a submodule -- it is a submodule that is not
  * checked out).  Return negative for an error.
  */
-static int check_removed(const struct index_state *istate, const struct cache_entry *ce, struct stat *st)
+static int check_removed(const struct cache_entry *ce, struct stat *st)
 {
-       assert(is_fsmonitor_refreshed(istate));
-       if (!(ce->ce_flags & CE_FSMONITOR_VALID) && lstat(ce->name, st) < 0) {
+       int stat_err;
+
+       if (!(ce->ce_flags & CE_FSMONITOR_VALID))
+               stat_err = lstat(ce->name, st);
+       else
+               stat_err = fake_lstat(ce, st);
+       if (stat_err < 0) {
                if (!is_missing_file_error(errno))
                        return -1;
                return 1;
        }
+
        if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
                return 1;
        if (S_ISDIR(st->st_mode)) {
@@ -88,7 +101,7 @@ static int match_stat_with_submodule(struct diff_options *diffopt,
        return changed;
 }
 
-int run_diff_files(struct rev_info *revs, unsigned int option)
+void run_diff_files(struct rev_info *revs, unsigned int option)
 {
        int entries, i;
        int diff_unmerged_stage = revs->max_count;
@@ -141,7 +154,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        memset(&(dpath->parent[0]), 0,
                               sizeof(struct combine_diff_parent)*5);
 
-                       changed = check_removed(istate, ce, &st);
+                       changed = check_removed(ce, &st);
                        if (!changed)
                                wt_mode = ce_mode_from_stat(ce, st.st_mode);
                        else {
@@ -221,7 +234,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                } else {
                        struct stat st;
 
-                       changed = check_removed(istate, ce, &st);
+                       changed = check_removed(ce, &st);
                        if (changed) {
                                if (changed < 0) {
                                        perror(ce->name);
@@ -264,7 +277,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        trace_performance_since(start, "diff-files");
-       return 0;
 }
 
 /*
@@ -296,7 +308,7 @@ static int get_stat_data(const struct index_state *istate,
        if (!cached && !ce_uptodate(ce)) {
                int changed;
                struct stat st;
-               changed = check_removed(istate, ce, &st);
+               changed = check_removed(ce, &st);
                if (changed < 0)
                        return -1;
                else if (changed) {
@@ -564,8 +576,6 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
                struct object *obj = revs->pending.objects[i].item;
                if (obj->flags)
                        die(_("--merge-base does not work with ranges"));
-               if (obj->type != OBJ_COMMIT)
-                       die(_("--merge-base only works with commits"));
        }
 
        /*
@@ -581,7 +591,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
        if (revs->pending.nr == 1) {
                struct object_id oid;
 
-               if (get_oid("HEAD", &oid))
+               if (repo_get_oid(the_repository, "HEAD", &oid))
                        die(_("unable to get HEAD"));
 
                mb_child[1] = lookup_commit_reference(the_repository, &oid);
@@ -598,7 +608,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
        free_commit_list(merge_bases);
 }
 
-int run_diff_index(struct rev_info *revs, unsigned int option)
+void run_diff_index(struct rev_info *revs, unsigned int option)
 {
        struct object_array_entry *ent;
        int cached = !!(option & DIFF_INDEX_CACHED);
@@ -632,7 +642,6 @@ int run_diff_index(struct rev_info *revs, unsigned int option)
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        trace_performance_leave("diff-index");
-       return 0;
 }
 
 int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
@@ -664,16 +673,23 @@ int index_differs_from(struct repository *r,
        setup_revisions(0, NULL, &rev, &opt);
        rev.diffopt.flags.quick = 1;
        rev.diffopt.flags.exit_with_status = 1;
-       if (flags)
+       if (flags) {
                diff_flags_or(&rev.diffopt.flags, flags);
+               /*
+                * Now that flags are merged, honor override_submodule_config
+                * and ignore_submodules from passed flags.
+                */
+               if (flags->override_submodule_config)
+                       rev.diffopt.flags.ignore_submodules = flags->ignore_submodules;
+       }
        rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
-       run_diff_index(&rev, 1);
+       run_diff_index(&rev, DIFF_INDEX_CACHED);
        has_changes = rev.diffopt.flags.has_changes;
        release_revisions(&rev);
        return (has_changes != 0);
 }
 
-static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
+static struct strbuf *idiff_prefix_cb(struct diff_options *opt UNUSED, void *data)
 {
        return data;
 }
index 85cbefa5afd7c00e903232011ef3d2ff7aca4a8f..45507588a2797b8d3618e3a19a2854d12051157b 100644 (file)
@@ -1,5 +1,7 @@
+#include "git-compat-util.h"
 #include "diff-merges.h"
 
+#include "gettext.h"
 #include "revision.h"
 
 typedef void (*diff_merges_setup_func_t)(struct rev_info *);
@@ -129,6 +131,9 @@ int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
        } else if (!strcmp(arg, "--cc")) {
                set_dense_combined(revs);
                revs->merges_imply_patch = 1;
+       } else if (!strcmp(arg, "--dd")) {
+               set_first_parent(revs);
+               revs->merges_imply_patch = 1;
        } else if (!strcmp(arg, "--remerge-diff")) {
                set_remerge_diff(revs);
                revs->merges_imply_patch = 1;
index 18edbdf4b59ec020cc58e3d16d01d92edfdc302e..3a8965672c5e90093c08a9fde12981b922ae8a76 100644 (file)
@@ -4,16 +4,14 @@
  * Copyright (c) 2008 by Junio C Hamano
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "color.h"
 #include "commit.h"
-#include "blob.h"
-#include "tag.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "gettext.h"
 #include "revision.h"
-#include "log-tree.h"
-#include "builtin.h"
 #include "parse-options.h"
 #include "string-list.h"
 #include "dir.h"
@@ -40,42 +38,81 @@ static int read_directory_contents(const char *path, struct string_list *list)
  */
 static const char file_from_standard_input[] = "-";
 
-static int get_mode(const char *path, int *mode)
+/*
+ * For paths given on the command-line we treat "-" as stdin and named
+ * pipes and symbolic links to named pipes specially.
+ */
+enum special {
+       SPECIAL_NONE,
+       SPECIAL_STDIN,
+       SPECIAL_PIPE,
+};
+
+static int get_mode(const char *path, int *mode, enum special *special)
 {
        struct stat st;
 
-       if (!path || !strcmp(path, "/dev/null"))
+       if (!path || !strcmp(path, "/dev/null")) {
                *mode = 0;
 #ifdef GIT_WINDOWS_NATIVE
-       else if (!strcasecmp(path, "nul"))
+       } else if (!strcasecmp(path, "nul")) {
                *mode = 0;
 #endif
-       else if (path == file_from_standard_input)
+       } else if (path == file_from_standard_input) {
                *mode = create_ce_mode(0666);
-       else if (lstat(path, &st))
+               *special = SPECIAL_STDIN;
+       } else if (lstat(path, &st)) {
                return error("Could not access '%s'", path);
-       else
+       } else {
                *mode = st.st_mode;
+       }
+       /*
+        * For paths on the command-line treat named pipes and symbolic
+        * links that resolve to a named pipe specially.
+        */
+       if (special &&
+           (S_ISFIFO(*mode) ||
+            (S_ISLNK(*mode) && !stat(path, &st) && S_ISFIFO(st.st_mode)))) {
+               *mode = create_ce_mode(0666);
+               *special = SPECIAL_PIPE;
+       }
+
        return 0;
 }
 
-static int populate_from_stdin(struct diff_filespec *s)
+static void populate_common(struct diff_filespec *s, struct strbuf *buf)
 {
-       struct strbuf buf = STRBUF_INIT;
        size_t size = 0;
 
-       if (strbuf_read(&buf, 0, 0) < 0)
-               return error_errno("error while reading from stdin");
-
        s->should_munmap = 0;
-       s->data = strbuf_detach(&buf, &size);
+       s->data = strbuf_detach(buf, &size);
        s->size = size;
        s->should_free = 1;
        s->is_stdin = 1;
-       return 0;
 }
 
-static struct diff_filespec *noindex_filespec(const char *name, int mode)
+static void populate_from_pipe(struct diff_filespec *s)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int fd = xopen(s->path, O_RDONLY);
+
+       if (strbuf_read(&buf, fd, 0) < 0)
+               die_errno("error while reading from '%s'", s->path);
+       close(fd);
+       populate_common(s, &buf);
+}
+
+static void populate_from_stdin(struct diff_filespec *s)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       if (strbuf_read(&buf, 0, 0) < 0)
+               die_errno("error while reading from stdin");
+       populate_common(s, &buf);
+}
+
+static struct diff_filespec *noindex_filespec(const char *name, int mode,
+                                             enum special special)
 {
        struct diff_filespec *s;
 
@@ -83,17 +120,22 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode)
                name = "/dev/null";
        s = alloc_filespec(name);
        fill_filespec(s, null_oid(), 0, mode);
-       if (name == file_from_standard_input)
+       if (special == SPECIAL_STDIN)
                populate_from_stdin(s);
+       else if (special == SPECIAL_PIPE)
+               populate_from_pipe(s);
        return s;
 }
 
 static int queue_diff(struct diff_options *o,
-                     const char *name1, const char *name2)
+                     const char *name1, const char *name2, int recursing)
 {
        int mode1 = 0, mode2 = 0;
+       enum special special1 = SPECIAL_NONE, special2 = SPECIAL_NONE;
 
-       if (get_mode(name1, &mode1) || get_mode(name2, &mode2))
+       /* Paths can only be special if we're not recursing. */
+       if (get_mode(name1, &mode1, recursing ? NULL : &special1) ||
+           get_mode(name2, &mode2, recursing ? NULL : &special2))
                return -1;
 
        if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2)) {
@@ -101,14 +143,14 @@ static int queue_diff(struct diff_options *o,
 
                if (S_ISDIR(mode1)) {
                        /* 2 is file that is created */
-                       d1 = noindex_filespec(NULL, 0);
-                       d2 = noindex_filespec(name2, mode2);
+                       d1 = noindex_filespec(NULL, 0, SPECIAL_NONE);
+                       d2 = noindex_filespec(name2, mode2, special2);
                        name2 = NULL;
                        mode2 = 0;
                } else {
                        /* 1 is file that is deleted */
-                       d1 = noindex_filespec(name1, mode1);
-                       d2 = noindex_filespec(NULL, 0);
+                       d1 = noindex_filespec(name1, mode1, special1);
+                       d2 = noindex_filespec(NULL, 0, SPECIAL_NONE);
                        name1 = NULL;
                        mode1 = 0;
                }
@@ -173,7 +215,7 @@ static int queue_diff(struct diff_options *o,
                                n2 = buffer2.buf;
                        }
 
-                       ret = queue_diff(o, n1, n2);
+                       ret = queue_diff(o, n1, n2, 1);
                }
                string_list_clear(&p1, 0);
                string_list_clear(&p2, 0);
@@ -187,10 +229,11 @@ static int queue_diff(struct diff_options *o,
                if (o->flags.reverse_diff) {
                        SWAP(mode1, mode2);
                        SWAP(name1, name2);
+                       SWAP(special1, special2);
                }
 
-               d1 = noindex_filespec(name1, mode1);
-               d2 = noindex_filespec(name2, mode2);
+               d1 = noindex_filespec(name1, mode1, special1);
+               d2 = noindex_filespec(name2, mode2, special2);
                diff_queue(&diff_queued_diff, d1, d2);
                return 0;
        }
@@ -215,13 +258,27 @@ static void append_basename(struct strbuf *path, const char *dir, const char *fi
  */
 static void fixup_paths(const char **path, struct strbuf *replacement)
 {
-       unsigned int isdir0, isdir1;
+       struct stat st;
+       unsigned int isdir0 = 0, isdir1 = 0;
+       unsigned int ispipe0 = 0, ispipe1 = 0;
+
+       if (path[0] != file_from_standard_input && !stat(path[0], &st)) {
+               isdir0 = S_ISDIR(st.st_mode);
+               ispipe0 = S_ISFIFO(st.st_mode);
+       }
+
+       if (path[1] != file_from_standard_input && !stat(path[1], &st)) {
+               isdir1 = S_ISDIR(st.st_mode);
+               ispipe1 = S_ISFIFO(st.st_mode);
+       }
+
+       if ((path[0] == file_from_standard_input && isdir1) ||
+           (isdir0 && path[1] == file_from_standard_input))
+               die(_("cannot compare stdin to a directory"));
+
+       if ((isdir0 && ispipe1) || (ispipe0 && isdir1))
+               die(_("cannot compare a named pipe to a directory"));
 
-       if (path[0] == file_from_standard_input ||
-           path[1] == file_from_standard_input)
-               return;
-       isdir0 = is_directory(path[0]);
-       isdir1 = is_directory(path[1]);
        if (isdir0 == isdir1)
                return;
        if (isdir0) {
@@ -255,8 +312,7 @@ int diff_no_index(struct rev_info *revs,
        };
        struct option *options;
 
-       options = parse_options_concat(no_index_options,
-                                      revs->diffopt.parseopts);
+       options = add_diff_options(no_index_options, &revs->diffopt);
        argc = parse_options(argc, argv, revs->prefix, options,
                             diff_no_index_usage, 0);
        if (argc != 2) {
@@ -296,7 +352,7 @@ int diff_no_index(struct rev_info *revs,
        setup_diff_pager(&revs->diffopt);
        revs->diffopt.flags.exit_with_status = 1;
 
-       if (queue_diff(&revs->diffopt, paths[0], paths[1]))
+       if (queue_diff(&revs->diffopt, paths[0], paths[1], 0))
                goto out;
        diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
        diffcore_std(&revs->diffopt);
@@ -306,7 +362,7 @@ int diff_no_index(struct rev_info *revs,
         * The return code for --no-index imitates diff(1):
         * 0 = no changes, 1 = changes, else error
         */
-       ret = diff_result_code(&revs->diffopt, 0);
+       ret = diff_result_code(&revs->diffopt);
 
 out:
        for (i = 0; i < ARRAY_SIZE(to_free); i++)
diff --git a/diff.c b/diff.c
index 648f6717a5597c30c423256a25e0fece08cd30de..a89a6a6128ad68de5d29d47061386f21d91a2337 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1,34 +1,45 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "base85.h"
 #include "config.h"
+#include "convert.h"
+#include "environment.h"
+#include "gettext.h"
 #include "tempfile.h"
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "delta.h"
+#include "hex.h"
 #include "xdiff-interface.h"
 #include "color.h"
-#include "attr.h"
 #include "run-command.h"
 #include "utf8.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "userdiff.h"
-#include "submodule-config.h"
 #include "submodule.h"
 #include "hashmap.h"
 #include "mem-pool.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "graph.h"
+#include "oid-array.h"
 #include "packfile.h"
+#include "pager.h"
 #include "parse-options.h"
 #include "help.h"
 #include "promisor-remote.h"
 #include "dir.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "read-cache-ll.h"
+#include "setup.h"
 #include "strmap.h"
+#include "ws.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -52,6 +63,7 @@ int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
 static int diff_relative;
+static int diff_stat_name_width;
 static int diff_stat_graph_width;
 static int diff_dirstat_permille_default = 30;
 static struct diff_options default_diff_options;
@@ -127,7 +139,7 @@ static int parse_dirstat_params(struct diff_options *options, const char *params
        int i;
 
        if (*params_copy)
-               string_list_split_in_place(&params, params_copy, ',', -1);
+               string_list_split_in_place(&params, params_copy, ",", -1);
        for (i = 0; i < params.nr; i++) {
                const char *p = params.items[i].string;
                if (!strcmp(p, "changes")) {
@@ -343,7 +355,8 @@ static unsigned parse_color_moved_ws(const char *arg)
        return ret;
 }
 
-int git_diff_ui_config(const char *var, const char *value, void *cb)
+int git_diff_ui_config(const char *var, const char *value,
+                      const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                diff_use_color_default = git_config_colorbool(var, value);
@@ -357,20 +370,24 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return 0;
        }
        if (!strcmp(var, "diff.colormovedws")) {
-               unsigned cm = parse_color_moved_ws(value);
+               unsigned cm;
+               if (!value)
+                       return config_error_nonbool(var);
+               cm = parse_color_moved_ws(value);
                if (cm & COLOR_MOVED_WS_ERROR)
                        return -1;
                diff_color_moved_ws_default = cm;
                return 0;
        }
        if (!strcmp(var, "diff.context")) {
-               diff_context_default = git_config_int(var, value);
+               diff_context_default = git_config_int(var, value, ctx->kvi);
                if (diff_context_default < 0)
                        return -1;
                return 0;
        }
        if (!strcmp(var, "diff.interhunkcontext")) {
-               diff_interhunk_context_default = git_config_int(var, value);
+               diff_interhunk_context_default = git_config_int(var, value,
+                                                               ctx->kvi);
                if (diff_interhunk_context_default < 0)
                        return -1;
                return 0;
@@ -395,8 +412,12 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_relative = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.statnamewidth")) {
+               diff_stat_name_width = git_config_int(var, value, ctx->kvi);
+               return 0;
+       }
        if (!strcmp(var, "diff.statgraphwidth")) {
-               diff_stat_graph_width = git_config_int(var, value);
+               diff_stat_graph_width = git_config_int(var, value, ctx->kvi);
                return 0;
        }
        if (!strcmp(var, "diff.external"))
@@ -406,10 +427,15 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "diff.orderfile"))
                return git_config_pathname(&diff_order_file_cfg, var, value);
 
-       if (!strcmp(var, "diff.ignoresubmodules"))
+       if (!strcmp(var, "diff.ignoresubmodules")) {
+               if (!value)
+                       return config_error_nonbool(var);
                handle_ignore_submodules_arg(&default_diff_options, value);
+       }
 
        if (!strcmp(var, "diff.submodule")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (parse_submodule_params(&default_diff_options, value))
                        warning(_("Unknown value for 'diff.submodule' config variable: '%s'"),
                                value);
@@ -417,24 +443,28 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, "diff.algorithm")) {
+               if (!value)
+                       return config_error_nonbool(var);
                diff_algorithm = parse_algorithm_value(value);
                if (diff_algorithm < 0)
-                       return -1;
+                       return error(_("unknown value for config '%s': %s"),
+                                    var, value);
                return 0;
        }
 
        if (git_color_config(var, value, cb) < 0)
                return -1;
 
-       return git_diff_basic_config(var, value, cb);
+       return git_diff_basic_config(var, value, ctx, cb);
 }
 
-int git_diff_basic_config(const char *var, const char *value, void *cb)
+int git_diff_basic_config(const char *var, const char *value,
+                         const struct config_context *ctx, void *cb)
 {
        const char *name;
 
        if (!strcmp(var, "diff.renamelimit")) {
-               diff_rename_limit_default = git_config_int(var, value);
+               diff_rename_limit_default = git_config_int(var, value, ctx->kvi);
                return 0;
        }
 
@@ -452,9 +482,13 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp(var, "diff.wserrorhighlight")) {
-               int val = parse_ws_error_highlight(value);
+               int val;
+               if (!value)
+                       return config_error_nonbool(var);
+               val = parse_ws_error_highlight(value);
                if (val < 0)
-                       return -1;
+                       return error(_("unknown value for config '%s': %s"),
+                                    var, value);
                ws_error_highlight_default = val;
                return 0;
        }
@@ -469,6 +503,8 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(var, "diff.dirstat")) {
                struct strbuf errmsg = STRBUF_INIT;
+               if (!value)
+                       return config_error_nonbool(var);
                default_diff_options.dirstat_permille = diff_dirstat_permille_default;
                if (parse_dirstat_params(&default_diff_options, value, &errmsg))
                        warning(_("Found errors in 'diff.dirstat' config variable:\n%s"),
@@ -481,7 +517,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
        if (git_diff_heuristic_config(var, value, cb) < 0)
                return -1;
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static char *quote_two(const char *one, const char *two)
@@ -604,7 +640,7 @@ static unsigned long diff_filespec_size(struct repository *r,
        return one->size;
 }
 
-static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
+static int count_trailing_blank(mmfile_t *mf)
 {
        char *ptr = mf->ptr;
        long size = mf->size;
@@ -622,7 +658,7 @@ static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
                for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
                        if (*prev_eol == '\n')
                                break;
-               if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
+               if (!ws_blank_line(prev_eol + 1, ptr - prev_eol))
                        break;
                cnt++;
                ptr = prev_eol - 1;
@@ -634,9 +670,8 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
                               struct emit_callback *ecbdata)
 {
        int l1, l2, at;
-       unsigned ws_rule = ecbdata->ws_rule;
-       l1 = count_trailing_blank(mf1, ws_rule);
-       l2 = count_trailing_blank(mf2, ws_rule);
+       l1 = count_trailing_blank(mf1);
+       l2 = count_trailing_blank(mf2);
        if (l2 <= l1) {
                ecbdata->blank_at_eof_in_preimage = 0;
                ecbdata->blank_at_eof_in_postimage = 0;
@@ -1583,7 +1618,7 @@ static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line
              ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
              ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
                return 0;
-       return ws_blank_line(line, len, ecbdata->ws_rule);
+       return ws_blank_line(line, len);
 }
 
 static void emit_add_line(struct emit_callback *ecbdata,
@@ -1955,7 +1990,7 @@ static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
 static void fn_out_diff_words_aux(void *priv,
                                  long minus_first, long minus_len,
                                  long plus_first, long plus_len,
-                                 const char *func, long funclen)
+                                 const char *func UNUSED, long funclen UNUSED)
 {
        struct diff_words_data *diff_words = priv;
        struct diff_words_style *style = diff_words->style;
@@ -2488,6 +2523,9 @@ static int diffstat_consume(void *priv, char *line, unsigned long len)
        struct diffstat_t *diffstat = priv;
        struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
 
+       if (!len)
+               BUG("xdiff fed us an empty line");
+
        if (line[0] == '+')
                x->added++;
        else if (line[0] == '-')
@@ -2621,7 +2659,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        continue;
                }
                fill_print_name(file);
-               len = strlen(file->print_name);
+               len = utf8_strwidth(file->print_name);
                if (max_len < len)
                        max_len = len;
 
@@ -2674,6 +2712,11 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
         * making the line longer than the maximum width.
         */
 
+       /*
+        * NEEDSWORK: line_prefix is often used for "log --graph" output
+        * and contains ANSI-colored string.  utf8_strnwidth() should be
+        * used to correctly count the display width instead of strlen().
+        */
        if (options->stat_width == -1)
                width = term_columns() - strlen(line_prefix);
        else
@@ -2681,12 +2724,14 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        number_width = decimal_width(max_change) > number_width ?
                decimal_width(max_change) : number_width;
 
+       if (options->stat_name_width == -1)
+               options->stat_name_width = diff_stat_name_width;
        if (options->stat_graph_width == -1)
                options->stat_graph_width = diff_stat_graph_width;
 
        /*
-        * Guarantee 3/8*16==6 for the graph part
-        * and 5/8*16==10 for the filename part
+        * Guarantee 3/8*16 == 6 for the graph part
+        * and 5/8*16 == 10 for the filename part
         */
        if (width < 16 + 6 + number_width)
                width = 16 + 6 + number_width;
@@ -2735,7 +2780,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                char *name = file->print_name;
                uintmax_t added = file->added;
                uintmax_t deleted = file->deleted;
-               int name_len;
+               int name_len, padding;
 
                if (!file->is_interesting && (added + deleted == 0))
                        continue;
@@ -2744,20 +2789,34 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                 * "scale" the filename
                 */
                len = name_width;
-               name_len = strlen(name);
+               name_len = utf8_strwidth(name);
                if (name_width < name_len) {
                        char *slash;
                        prefix = "...";
                        len -= 3;
+                       /*
+                        * NEEDSWORK: (name_len - len) counts the display
+                        * width, which would be shorter than the byte
+                        * length of the corresponding substring.
+                        * Advancing "name" by that number of bytes does
+                        * *NOT* skip over that many columns, so it is
+                        * very likely that chomping the pathname at the
+                        * slash we will find starting from "name" will
+                        * leave the resulting string still too long.
+                        */
                        name += name_len - len;
                        slash = strchr(name, '/');
                        if (slash)
                                name = slash;
                }
+               padding = len - utf8_strwidth(name);
+               if (padding < 0)
+                       padding = 0;
 
                if (file->is_binary) {
-                       strbuf_addf(&out, " %s%-*s |", prefix, len, name);
-                       strbuf_addf(&out, " %*s", number_width, "Bin");
+                       strbuf_addf(&out, " %s%s%*s | %*s",
+                                   prefix, name, padding, "",
+                                   number_width, "Bin");
                        if (!added && !deleted) {
                                strbuf_addch(&out, '\n');
                                emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
@@ -2777,8 +2836,9 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        continue;
                }
                else if (file->is_unmerged) {
-                       strbuf_addf(&out, " %s%-*s |", prefix, len, name);
-                       strbuf_addstr(&out, " Unmerged\n");
+                       strbuf_addf(&out, " %s%s%*s | %*s",
+                                   prefix, name, padding, "",
+                                   number_width, "Unmerged\n");
                        emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
                                         out.buf, out.len, 0);
                        strbuf_reset(&out);
@@ -2804,10 +2864,10 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                                add = total - del;
                        }
                }
-               strbuf_addf(&out, " %s%-*s |", prefix, len, name);
-               strbuf_addf(&out, " %*"PRIuMAX"%s",
-                       number_width, added + deleted,
-                       added + deleted ? " " : "");
+               strbuf_addf(&out, " %s%s%*s | %*"PRIuMAX"%s",
+                           prefix, name, padding, "",
+                           number_width, added + deleted,
+                           added + deleted ? " " : "");
                show_graph(&out, '+', add, add_c, reset);
                show_graph(&out, '-', del, del_c, reset);
                strbuf_addch(&out, '\n');
@@ -2971,6 +3031,24 @@ static int dirstat_compare(const void *_a, const void *_b)
        return strcmp(a->name, b->name);
 }
 
+static void conclude_dirstat(struct diff_options *options,
+                            struct dirstat_dir *dir,
+                            unsigned long changed)
+{
+       struct dirstat_file *to_free = dir->files;
+
+       if (!changed) {
+               /* This can happen even with many files, if everything was renames */
+               ;
+       } else {
+               /* Show all directories with more than x% of the changes */
+               QSORT(dir->files, dir->nr, dirstat_compare);
+               gather_dirstat(options, dir, changed, "", 0);
+       }
+
+       free(to_free);
+}
+
 static void show_dirstat(struct diff_options *options)
 {
        int i;
@@ -3060,13 +3138,7 @@ found_damage:
                dir.nr++;
        }
 
-       /* This can happen even with many files, if everything was renames */
-       if (!changed)
-               return;
-
-       /* Show all directories with more than x% of the changes */
-       QSORT(dir.files, dir.nr, dirstat_compare);
-       gather_dirstat(options, &dir, changed, "", 0);
+       conclude_dirstat(options, &dir, changed);
 }
 
 static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)
@@ -3104,13 +3176,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
                dir.nr++;
        }
 
-       /* This can happen even with many files, if everything was renames */
-       if (!changed)
-               return;
-
-       /* Show all directories with more than x% of the changes */
-       QSORT(dir.files, dir.nr, dirstat_compare);
-       gather_dirstat(options, &dir, changed, "", 0);
+       conclude_dirstat(options, &dir, changed);
 }
 
 static void free_diffstat_file(struct diffstat_file *f)
@@ -3162,8 +3228,9 @@ static int is_conflict_marker(const char *line, int marker_size, unsigned long l
 }
 
 static void checkdiff_consume_hunk(void *priv,
-                                  long ob, long on, long nb, long nn,
-                                  const char *func, long funclen)
+                                  long ob UNUSED, long on UNUSED,
+                                  long nb, long nn UNUSED,
+                                  const char *func UNUSED, long funclen UNUSED)
 
 {
        struct checkdiff_t *data = priv;
@@ -3351,6 +3418,17 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
                options->b_prefix = b;
 }
 
+void diff_set_noprefix(struct diff_options *options)
+{
+       options->a_prefix = options->b_prefix = "";
+}
+
+void diff_set_default_prefix(struct diff_options *options)
+{
+       options->a_prefix = "a/";
+       options->b_prefix = "b/";
+}
+
 struct userdiff_driver *get_textconv(struct repository *r,
                                     struct diff_filespec *one)
 {
@@ -3414,6 +3492,22 @@ static int diff_filepair_is_phoney(struct diff_filespec *one,
        return !DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two);
 }
 
+static int set_diff_algorithm(struct diff_options *opts,
+                             const char *alg)
+{
+       long value = parse_algorithm_value(alg);
+
+       if (value < 0)
+               return -1;
+
+       /* clear out previous settings */
+       DIFF_XDL_CLR(opts, NEED_MINIMAL);
+       opts->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+       opts->xdl_opts |= value;
+
+       return 0;
+}
+
 static void builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_filespec *one,
@@ -3491,18 +3585,21 @@ static void builtin_diff(const char *name_a,
                strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset);
                if (xfrm_msg)
                        strbuf_addstr(&header, xfrm_msg);
+               o->found_changes = 1;
                must_show_header = 1;
        }
        else if (lbl[1][0] == '/') {
                strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset);
                if (xfrm_msg)
                        strbuf_addstr(&header, xfrm_msg);
+               o->found_changes = 1;
                must_show_header = 1;
        }
        else {
                if (one->mode != two->mode) {
                        strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, meta, one->mode, reset);
                        strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, meta, two->mode, reset);
+                       o->found_changes = 1;
                        must_show_header = 1;
                }
                if (xfrm_msg)
@@ -4190,7 +4287,6 @@ static void prep_temp_blob(struct index_state *istate,
 }
 
 static struct diff_tempfile *prepare_temp_file(struct repository *r,
-                                              const char *name,
                                               struct diff_filespec *one)
 {
        struct diff_tempfile *temp = claim_diff_tempfile();
@@ -4208,18 +4304,18 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
 
        if (!S_ISGITLINK(one->mode) &&
            (!one->oid_valid ||
-            reuse_worktree_file(r->index, name, &one->oid, 1))) {
+            reuse_worktree_file(r->index, one->path, &one->oid, 1))) {
                struct stat st;
-               if (lstat(name, &st) < 0) {
+               if (lstat(one->path, &st) < 0) {
                        if (errno == ENOENT)
                                goto not_a_valid_file;
-                       die_errno("stat(%s)", name);
+                       die_errno("stat(%s)", one->path);
                }
                if (S_ISLNK(st.st_mode)) {
                        struct strbuf sb = STRBUF_INIT;
-                       if (strbuf_readlink(&sb, name, st.st_size) < 0)
-                               die_errno("readlink(%s)", name);
-                       prep_temp_blob(r->index, name, temp, sb.buf, sb.len,
+                       if (strbuf_readlink(&sb, one->path, st.st_size) < 0)
+                               die_errno("readlink(%s)", one->path);
+                       prep_temp_blob(r->index, one->path, temp, sb.buf, sb.len,
                                       (one->oid_valid ?
                                        &one->oid : null_oid()),
                                       (one->oid_valid ?
@@ -4228,7 +4324,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
                }
                else {
                        /* we can borrow from the file in the work tree */
-                       temp->name = name;
+                       temp->name = one->path;
                        if (!one->oid_valid)
                                oid_to_hex_r(temp->hex, null_oid());
                        else
@@ -4246,7 +4342,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
        else {
                if (diff_populate_filespec(r, one, NULL))
                        die("cannot read data blob for %s", one->path);
-               prep_temp_blob(r->index, name, temp,
+               prep_temp_blob(r->index, one->path, temp,
                               one->data, one->size,
                               &one->oid, one->mode);
        }
@@ -4255,10 +4351,9 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
 
 static void add_external_diff_name(struct repository *r,
                                   struct strvec *argv,
-                                  const char *name,
                                   struct diff_filespec *df)
 {
-       struct diff_tempfile *temp = prepare_temp_file(r, name, df);
+       struct diff_tempfile *temp = prepare_temp_file(r, df);
        strvec_push(argv, temp->name);
        strvec_push(argv, temp->hex);
        strvec_push(argv, temp->mode);
@@ -4278,35 +4373,32 @@ static void run_external_diff(const char *pgm,
                              const char *xfrm_msg,
                              struct diff_options *o)
 {
-       struct strvec argv = STRVEC_INIT;
-       struct strvec env = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        struct diff_queue_struct *q = &diff_queued_diff;
 
-       strvec_push(&argv, pgm);
-       strvec_push(&argv, name);
+       strvec_push(&cmd.args, pgm);
+       strvec_push(&cmd.args, name);
 
        if (one && two) {
-               add_external_diff_name(o->repo, &argv, name, one);
-               if (!other)
-                       add_external_diff_name(o->repo, &argv, name, two);
-               else {
-                       add_external_diff_name(o->repo, &argv, other, two);
-                       strvec_push(&argv, other);
-                       strvec_push(&argv, xfrm_msg);
+               add_external_diff_name(o->repo, &cmd.args, one);
+               add_external_diff_name(o->repo, &cmd.args, two);
+               if (other) {
+                       strvec_push(&cmd.args, other);
+                       strvec_push(&cmd.args, xfrm_msg);
                }
        }
 
-       strvec_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
-       strvec_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+       strvec_pushf(&cmd.env, "GIT_DIFF_PATH_COUNTER=%d",
+                    ++o->diff_path_counter);
+       strvec_pushf(&cmd.env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
 
        diff_free_filespec_data(one);
        diff_free_filespec_data(two);
-       if (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v))
+       cmd.use_shell = 1;
+       if (run_command(&cmd))
                die(_("external diff died, stopping at %s"), name);
 
        remove_tempfile();
-       strvec_clear(&argv);
-       strvec_clear(&env);
 }
 
 static int similarity_index(struct diff_filepair *p)
@@ -4317,7 +4409,7 @@ static int similarity_index(struct diff_filepair *p)
 static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
 {
        if (startup_info->have_repository)
-               return find_unique_abbrev(oid, abbrev);
+               return repo_find_unique_abbrev(the_repository, oid, abbrev);
        else {
                char *hex = oid_to_hex(oid);
                if (abbrev < 0)
@@ -4422,15 +4514,13 @@ static void run_diff_cmd(const char *pgm,
        const char *xfrm_msg = NULL;
        int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
        int must_show_header = 0;
+       struct userdiff_driver *drv = NULL;
 
-
-       if (o->flags.allow_external) {
-               struct userdiff_driver *drv;
-
+       if (o->flags.allow_external || !o->ignore_driver_algorithm)
                drv = userdiff_find_by_path(o->repo->index, attr_path);
-               if (drv && drv->external)
-                       pgm = drv->external;
-       }
+
+       if (o->flags.allow_external && drv && drv->external)
+               pgm = drv->external;
 
        if (msg) {
                /*
@@ -4447,12 +4537,16 @@ static void run_diff_cmd(const char *pgm,
                run_external_diff(pgm, name, other, one, two, xfrm_msg, o);
                return;
        }
-       if (one && two)
+       if (one && two) {
+               if (!o->ignore_driver_algorithm && drv && drv->algorithm)
+                       set_diff_algorithm(o, drv->algorithm);
+
                builtin_diff(name, other ? other : name,
                             one, two, xfrm_msg, must_show_header,
                             o, complete_rewrite);
-       else
+       } else {
                fprintf(o->file, "* Unmerged path %s\n", name);
+       }
 }
 
 static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *istate)
@@ -4549,6 +4643,14 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
        const char *name;
        const char *other;
 
+       if (!o->ignore_driver_algorithm) {
+               struct userdiff_driver *drv = userdiff_find_by_path(o->repo->index,
+                                                                   p->one->path);
+
+               if (drv && drv->algorithm)
+                       set_diff_algorithm(o, drv->algorithm);
+       }
+
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
                builtin_diffstat(p->one->path, NULL, NULL, NULL,
@@ -4593,8 +4695,6 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
        builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
 }
 
-static void prep_parse_options(struct diff_options *options);
-
 void repo_diff_setup(struct repository *r, struct diff_options *options)
 {
        memcpy(options, &default_diff_options, sizeof(*options));
@@ -4632,16 +4732,13 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
                options->flags.ignore_untracked_in_submodules = 1;
 
        if (diff_no_prefix) {
-               options->a_prefix = options->b_prefix = "";
+               diff_set_noprefix(options);
        } else if (!diff_mnemonic_prefix) {
-               options->a_prefix = "a/";
-               options->b_prefix = "b/";
+               diff_set_default_prefix(options);
        }
 
        options->color_moved = diff_color_moved_default;
        options->color_moved_ws_handling = diff_color_moved_ws_default;
-
-       prep_parse_options(options);
 }
 
 static const char diff_status_letters[] = {
@@ -4681,6 +4778,31 @@ unsigned diff_filter_bit(char status)
        return filter_bit[(int) status];
 }
 
+int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error)
+{
+       unsigned forbidden_magic;
+
+       if (ps->nr != 1) {
+               if (die_on_error)
+                       die(_("--follow requires exactly one pathspec"));
+               return 0;
+       }
+
+       forbidden_magic = ps->items[0].magic & ~(PATHSPEC_FROMTOP |
+                                                PATHSPEC_LITERAL);
+       if (forbidden_magic) {
+               if (die_on_error) {
+                       struct strbuf sb = STRBUF_INIT;
+                       pathspec_magic_names(forbidden_magic, &sb);
+                       die(_("pathspec magic not supported by --follow: %s"),
+                           sb.buf);
+               }
+               return 0;
+       }
+
+       return 1;
+}
+
 void diff_setup_done(struct diff_options *options)
 {
        unsigned check_mask = DIFF_FORMAT_NAME |
@@ -4735,6 +4857,10 @@ void diff_setup_done(struct diff_options *options)
        else
                options->prefix_length = 0;
 
+       /*
+        * --name-only, --name-status, --checkdiff, and -s
+        * turn other output format off.
+        */
        if (options->output_format & (DIFF_FORMAT_NAME |
                                      DIFF_FORMAT_NAME_STATUS |
                                      DIFF_FORMAT_CHECKDIFF |
@@ -4788,8 +4914,8 @@ void diff_setup_done(struct diff_options *options)
 
        options->diff_path_counter = 0;
 
-       if (options->flags.follow_renames && options->pathspec.nr != 1)
-               die(_("--follow requires exactly one pathspec"));
+       if (options->flags.follow_renames)
+               diff_check_follow_pathspec(&options->pathspec, 1);
 
        if (!options->use_color || external_diff())
                options->color_moved = 0;
@@ -4799,8 +4925,6 @@ void diff_setup_done(struct diff_options *options)
                        options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON];
                options->filter &= ~options->filter_not;
        }
-
-       FREE_AND_NULL(options->parseopts);
 }
 
 int parse_long_opt(const char *opt, const char **argv,
@@ -4868,6 +4992,7 @@ static int diff_opt_stat(const struct option *opt, const char *value, int unset)
        } else
                BUG("%s should not get here", opt->long_name);
 
+       options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
        options->output_format |= DIFF_FORMAT_DIFFSTAT;
        options->stat_name_width = name_width;
        options->stat_graph_width = graph_width;
@@ -4887,6 +5012,7 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params)
         * The caller knows a dirstat-related option is given from the command
         * line; allow it to say "return this_function();"
         */
+       options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
        options->output_format |= DIFF_FORMAT_DIRSTAT;
        return 1;
 }
@@ -4950,7 +5076,7 @@ static int diff_opt_find_object(const struct option *option,
        struct object_id oid;
 
        BUG_ON_OPT_NEG(unset);
-       if (get_oid(arg, &oid))
+       if (repo_get_oid(the_repository, arg, &oid))
                return error(_("unable to resolve '%s'"), arg);
 
        if (!opt->objfind)
@@ -5086,6 +5212,7 @@ static int diff_opt_compact_summary(const struct option *opt,
                options->flags.stat_with_summary = 0;
        } else {
                options->flags.stat_with_summary = 1;
+               options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
                options->output_format |= DIFF_FORMAT_DIFFSTAT;
        }
        return 0;
@@ -5095,17 +5222,32 @@ static int diff_opt_diff_algorithm(const struct option *opt,
                                   const char *arg, int unset)
 {
        struct diff_options *options = opt->value;
-       long value = parse_algorithm_value(arg);
 
        BUG_ON_OPT_NEG(unset);
-       if (value < 0)
+
+       if (set_diff_algorithm(options, arg))
                return error(_("option diff-algorithm accepts \"myers\", "
                               "\"minimal\", \"patience\" and \"histogram\""));
 
-       /* clear out previous settings */
-       DIFF_XDL_CLR(options, NEED_MINIMAL);
-       options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
-       options->xdl_opts |= value;
+       options->ignore_driver_algorithm = 1;
+
+       return 0;
+}
+
+static int diff_opt_diff_algorithm_no_arg(const struct option *opt,
+                                  const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
+
+       if (set_diff_algorithm(options, opt->long_name))
+               BUG("available diff algorithms include \"myers\", "
+                              "\"minimal\", \"patience\" and \"histogram\"");
+
+       options->ignore_driver_algorithm = 1;
+
        return 0;
 }
 
@@ -5208,8 +5350,18 @@ static int diff_opt_no_prefix(const struct option *opt,
 
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(optarg);
-       options->a_prefix = "";
-       options->b_prefix = "";
+       diff_set_noprefix(options);
+       return 0;
+}
+
+static int diff_opt_default_prefix(const struct option *opt,
+                                  const char *optarg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(optarg);
+       diff_set_default_prefix(options);
        return 0;
 }
 
@@ -5238,7 +5390,6 @@ static int diff_opt_patience(const struct option *opt,
 
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
-       options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        /*
         * Both --patience and --anchored use PATIENCE_DIFF
         * internally, so remove any anchors previously
@@ -5247,7 +5398,9 @@ static int diff_opt_patience(const struct option *opt,
        for (i = 0; i < options->anchors_nr; i++)
                free(options->anchors[i]);
        options->anchors_nr = 0;
-       return 0;
+       options->ignore_driver_algorithm = 1;
+
+       return set_diff_algorithm(options, "patience");
 }
 
 static int diff_opt_ignore_regex(const struct option *opt,
@@ -5397,16 +5550,20 @@ static int diff_opt_rotate_to(const struct option *opt, const char *arg, int uns
        return 0;
 }
 
-static void prep_parse_options(struct diff_options *options)
+/*
+ * Consider adding new flags to __git_diff_common_options
+ * in contrib/completion/git-completion.bash
+ */
+struct option *add_diff_options(const struct option *opts,
+                               struct diff_options *options)
 {
        struct option parseopts[] = {
                OPT_GROUP(N_("Diff output format options")),
                OPT_BITOP('p', "patch", &options->output_format,
                          N_("generate patch"),
                          DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
-               OPT_BIT_F('s', "no-patch", &options->output_format,
-                         N_("suppress diff output"),
-                         DIFF_FORMAT_NO_OUTPUT, PARSE_OPT_NONEG),
+               OPT_SET_INT('s', "no-patch", &options->output_format,
+                           N_("suppress diff output"), DIFF_FORMAT_NO_OUTPUT),
                OPT_BITOP('u', NULL, &options->output_format,
                          N_("generate patch"),
                          DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
@@ -5415,9 +5572,9 @@ static void prep_parse_options(struct diff_options *options)
                               PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_unified),
                OPT_BOOL('W', "function-context", &options->flags.funccontext,
                         N_("generate diffs with <n> lines context")),
-               OPT_BIT_F(0, "raw", &options->output_format,
+               OPT_BITOP(0, "raw", &options->output_format,
                          N_("generate the diff in raw format"),
-                         DIFF_FORMAT_RAW, PARSE_OPT_NONEG),
+                         DIFF_FORMAT_RAW, DIFF_FORMAT_NO_OUTPUT),
                OPT_BITOP(0, "patch-with-raw", &options->output_format,
                          N_("synonym for '-p --raw'"),
                          DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW,
@@ -5426,12 +5583,12 @@ static void prep_parse_options(struct diff_options *options)
                          N_("synonym for '-p --stat'"),
                          DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT,
                          DIFF_FORMAT_NO_OUTPUT),
-               OPT_BIT_F(0, "numstat", &options->output_format,
+               OPT_BITOP(0, "numstat", &options->output_format,
                          N_("machine friendly --stat"),
-                         DIFF_FORMAT_NUMSTAT, PARSE_OPT_NONEG),
-               OPT_BIT_F(0, "shortstat", &options->output_format,
+                         DIFF_FORMAT_NUMSTAT, DIFF_FORMAT_NO_OUTPUT),
+               OPT_BITOP(0, "shortstat", &options->output_format,
                          N_("output only the last line of --stat"),
-                         DIFF_FORMAT_SHORTSTAT, PARSE_OPT_NONEG),
+                         DIFF_FORMAT_SHORTSTAT, DIFF_FORMAT_NO_OUTPUT),
                OPT_CALLBACK_F('X', "dirstat", options, N_("<param1,param2>..."),
                               N_("output the distribution of relative amount of changes for each sub-directory"),
                               PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
@@ -5447,9 +5604,9 @@ static void prep_parse_options(struct diff_options *options)
                OPT_BIT_F(0, "check", &options->output_format,
                          N_("warn if changes introduce conflict markers or whitespace errors"),
                          DIFF_FORMAT_CHECKDIFF, PARSE_OPT_NONEG),
-               OPT_BIT_F(0, "summary", &options->output_format,
+               OPT_BITOP(0, "summary", &options->output_format,
                          N_("condensed summary such as creations, renames and mode changes"),
-                         DIFF_FORMAT_SUMMARY, PARSE_OPT_NONEG),
+                         DIFF_FORMAT_SUMMARY, DIFF_FORMAT_NO_OUTPUT),
                OPT_BIT_F(0, "name-only", &options->output_format,
                          N_("show only names of changed files"),
                          DIFF_FORMAT_NAME, PARSE_OPT_NONEG),
@@ -5500,6 +5657,9 @@ static void prep_parse_options(struct diff_options *options)
                OPT_CALLBACK_F(0, "no-prefix", options, NULL,
                               N_("do not show any source or destination prefix"),
                               PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_no_prefix),
+               OPT_CALLBACK_F(0, "default-prefix", options, NULL,
+                              N_("use default prefixes a/ and b/"),
+                              PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_default_prefix),
                OPT_INTEGER_F(0, "inter-hunk-context", &options->interhunkcontext,
                              N_("show context between diff hunks up to the specified number of lines"),
                              PARSE_OPT_NONEG),
@@ -5549,9 +5709,10 @@ static void prep_parse_options(struct diff_options *options)
                            N_("prevent rename/copy detection if the number of rename/copy targets exceeds given limit")),
 
                OPT_GROUP(N_("Diff algorithm options")),
-               OPT_BIT(0, "minimal", &options->xdl_opts,
-                       N_("produce the smallest possible diff"),
-                       XDF_NEED_MINIMAL),
+               OPT_CALLBACK_F(0, "minimal", options, NULL,
+                              N_("produce the smallest possible diff"),
+                              PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                              diff_opt_diff_algorithm_no_arg),
                OPT_BIT_F('w', "ignore-all-space", &options->xdl_opts,
                          N_("ignore whitespace when comparing lines"),
                          XDF_IGNORE_WHITESPACE, PARSE_OPT_NONEG),
@@ -5577,9 +5738,10 @@ static void prep_parse_options(struct diff_options *options)
                               N_("generate diff using the \"patience diff\" algorithm"),
                               PARSE_OPT_NONEG | PARSE_OPT_NOARG,
                               diff_opt_patience),
-               OPT_BITOP(0, "histogram", &options->xdl_opts,
-                         N_("generate diff using the \"histogram diff\" algorithm"),
-                         XDF_HISTOGRAM_DIFF, XDF_DIFF_ALGORITHM_MASK),
+               OPT_CALLBACK_F(0, "histogram", options, NULL,
+                              N_("generate diff using the \"histogram diff\" algorithm"),
+                              PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                              diff_opt_diff_algorithm_no_arg),
                OPT_CALLBACK_F(0, "diff-algorithm", options, N_("<algorithm>"),
                               N_("choose a diff algorithm"),
                               PARSE_OPT_NONEG, diff_opt_diff_algorithm),
@@ -5667,22 +5829,25 @@ static void prep_parse_options(struct diff_options *options)
                OPT_END()
        };
 
-       ALLOC_ARRAY(options->parseopts, ARRAY_SIZE(parseopts));
-       memcpy(options->parseopts, parseopts, sizeof(parseopts));
+       return parse_options_concat(opts, parseopts);
 }
 
 int diff_opt_parse(struct diff_options *options,
                   const char **av, int ac, const char *prefix)
 {
+       struct option no_options[] = { OPT_END() };
+       struct option *parseopts = add_diff_options(no_options, options);
+
        if (!prefix)
                prefix = "";
 
-       ac = parse_options(ac, av, prefix, options->parseopts, NULL,
+       ac = parse_options(ac, av, prefix, parseopts, NULL,
                           PARSE_OPT_KEEP_DASHDASH |
                           PARSE_OPT_KEEP_UNKNOWN_OPT |
                           PARSE_OPT_NO_INTERNAL_HELP |
                           PARSE_OPT_ONE_SHOT |
                           PARSE_OPT_STOP_AT_NON_OPTION);
+       free(parseopts);
 
        return ac;
 }
@@ -5750,6 +5915,13 @@ void diff_free_filepair(struct diff_filepair *p)
        free(p);
 }
 
+void diff_free_queue(struct diff_queue_struct *q)
+{
+       for (int i = 0; i < q->nr; i++)
+               diff_free_filepair(q->queue[i]);
+       free(q->queue);
+}
+
 const char *diff_aligned_abbrev(const struct object_id *oid, int len)
 {
        int abblen;
@@ -6063,6 +6235,8 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
                fprintf(opt->file, "%s", diff_line_prefix(opt));
                write_name_quoted(name_a, opt->file, opt->line_termination);
        }
+
+       opt->found_changes = 1;
 }
 
 static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs)
@@ -6206,7 +6380,7 @@ static void patch_id_add_mode(git_hash_ctx *ctx, unsigned mode)
 }
 
 /* returns 0 upon success, and writes result into oid */
-static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only, int stable)
+static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
@@ -6253,66 +6427,63 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
                if (p->one->mode == 0) {
                        patch_id_add_string(&ctx, "newfilemode");
                        patch_id_add_mode(&ctx, p->two->mode);
-                       patch_id_add_string(&ctx, "---/dev/null");
-                       patch_id_add_string(&ctx, "+++b/");
-                       the_hash_algo->update_fn(&ctx, p->two->path, len2);
                } else if (p->two->mode == 0) {
                        patch_id_add_string(&ctx, "deletedfilemode");
                        patch_id_add_mode(&ctx, p->one->mode);
-                       patch_id_add_string(&ctx, "---a/");
-                       the_hash_algo->update_fn(&ctx, p->one->path, len1);
-                       patch_id_add_string(&ctx, "+++/dev/null");
-               } else {
-                       patch_id_add_string(&ctx, "---a/");
-                       the_hash_algo->update_fn(&ctx, p->one->path, len1);
-                       patch_id_add_string(&ctx, "+++b/");
-                       the_hash_algo->update_fn(&ctx, p->two->path, len2);
+               } else if (p->one->mode != p->two->mode) {
+                       patch_id_add_string(&ctx, "oldmode");
+                       patch_id_add_mode(&ctx, p->one->mode);
+                       patch_id_add_string(&ctx, "newmode");
+                       patch_id_add_mode(&ctx, p->two->mode);
                }
 
-               if (diff_header_only)
-                       continue;
-
-               if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
-                   fill_mmfile(options->repo, &mf2, p->two) < 0)
-                       return error("unable to read files to diff");
-
-               if (diff_filespec_is_binary(options->repo, p->one) ||
+               if (diff_header_only) {
+                       /* don't do anything since we're only populating header info */
+               } else if (diff_filespec_is_binary(options->repo, p->one) ||
                    diff_filespec_is_binary(options->repo, p->two)) {
                        the_hash_algo->update_fn(&ctx, oid_to_hex(&p->one->oid),
                                        the_hash_algo->hexsz);
                        the_hash_algo->update_fn(&ctx, oid_to_hex(&p->two->oid),
                                        the_hash_algo->hexsz);
-                       continue;
-               }
-
-               xpp.flags = 0;
-               xecfg.ctxlen = 3;
-               xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
-               if (xdi_diff_outf(&mf1, &mf2, NULL,
-                                 patch_id_consume, &data, &xpp, &xecfg))
-                       return error("unable to generate patch-id diff for %s",
-                                    p->one->path);
+               } else {
+                       if (p->one->mode == 0) {
+                               patch_id_add_string(&ctx, "---/dev/null");
+                               patch_id_add_string(&ctx, "+++b/");
+                               the_hash_algo->update_fn(&ctx, p->two->path, len2);
+                       } else if (p->two->mode == 0) {
+                               patch_id_add_string(&ctx, "---a/");
+                               the_hash_algo->update_fn(&ctx, p->one->path, len1);
+                               patch_id_add_string(&ctx, "+++/dev/null");
+                       } else {
+                               patch_id_add_string(&ctx, "---a/");
+                               the_hash_algo->update_fn(&ctx, p->one->path, len1);
+                               patch_id_add_string(&ctx, "+++b/");
+                               the_hash_algo->update_fn(&ctx, p->two->path, len2);
+                       }
 
-               if (stable)
-                       flush_one_hunk(oid, &ctx);
+                       if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
+                           fill_mmfile(options->repo, &mf2, p->two) < 0)
+                               return error("unable to read files to diff");
+                       xpp.flags = 0;
+                       xecfg.ctxlen = 3;
+                       xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
+                       if (xdi_diff_outf(&mf1, &mf2, NULL,
+                                         patch_id_consume, &data, &xpp, &xecfg))
+                               return error("unable to generate patch-id diff for %s",
+                                            p->one->path);
+               }
+               flush_one_hunk(oid, &ctx);
        }
 
-       if (!stable)
-               the_hash_algo->final_oid_fn(oid, &ctx);
-
        return 0;
 }
 
-int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only, int stable)
+int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
-       int i;
-       int result = diff_get_patch_id(options, oid, diff_header_only, stable);
-
-       for (i = 0; i < q->nr; i++)
-               diff_free_filepair(q->queue[i]);
+       int result = diff_get_patch_id(options, oid, diff_header_only);
 
-       free(q->queue);
+       diff_free_queue(q);
        DIFF_QUEUE_CLEAR(q);
 
        return result;
@@ -6487,7 +6658,6 @@ void diff_free(struct diff_options *options)
        diff_free_file(options);
        diff_free_ignore_regex(options);
        clear_pathspec(&options->pathspec);
-       FREE_AND_NULL(options->parseopts);
 }
 
 void diff_flush(struct diff_options *options)
@@ -6545,6 +6715,21 @@ void diff_flush(struct diff_options *options)
                separator++;
        }
 
+       if (output_format & DIFF_FORMAT_PATCH) {
+               if (separator) {
+                       emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
+                       if (options->stat_sep)
+                               /* attach patch instead of inline */
+                               emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
+                                                NULL, 0, 0);
+               }
+
+               diff_flush_patch_all_file_pairs(options);
+       }
+
+       if (output_format & DIFF_FORMAT_CALLBACK)
+               options->format_callback(q, options, options->format_callback_data);
+
        if (output_format & DIFF_FORMAT_NO_OUTPUT &&
            options->flags.exit_with_status &&
            options->flags.diff_from_contents) {
@@ -6566,25 +6751,8 @@ void diff_flush(struct diff_options *options)
                }
        }
 
-       if (output_format & DIFF_FORMAT_PATCH) {
-               if (separator) {
-                       emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
-                       if (options->stat_sep)
-                               /* attach patch instead of inline */
-                               emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
-                                                NULL, 0, 0);
-               }
-
-               diff_flush_patch_all_file_pairs(options);
-       }
-
-       if (output_format & DIFF_FORMAT_CALLBACK)
-               options->format_callback(q, options, options->format_callback_data);
-
-       for (i = 0; i < q->nr; i++)
-               diff_free_filepair(q->queue[i]);
 free_queue:
-       free(q->queue);
+       diff_free_queue(q);
        DIFF_QUEUE_CLEAR(q);
        diff_free(options);
 
@@ -6783,6 +6951,13 @@ void diff_queued_diff_prefetch(void *repository)
        oid_array_clear(&to_fetch);
 }
 
+void init_diffstat_widths(struct diff_options *options)
+{
+       options->stat_width = -1;        /* use full terminal width */
+       options->stat_name_width = -1;   /* respect diff.statNameWidth config */
+       options->stat_graph_width = -1;  /* respect diff.statGraphWidth config */
+}
+
 void diffcore_std(struct diff_options *options)
 {
        int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT |
@@ -6799,7 +6974,7 @@ void diffcore_std(struct diff_options *options)
         * If no prefetching occurs, diffcore_rename() will prefetch if it
         * decides that it needs inexact rename detection.
         */
-       if (options->repo == the_repository && has_promisor_remote() &&
+       if (options->repo == the_repository && repo_has_promisor_remote(the_repository) &&
            (options->output_format & output_formats_to_prefetch ||
             options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))
                diff_queued_diff_prefetch(options->repo);
@@ -6836,16 +7011,14 @@ void diffcore_std(struct diff_options *options)
        options->found_follow = 0;
 }
 
-int diff_result_code(struct diff_options *opt, int status)
+int diff_result_code(struct diff_options *opt)
 {
        int result = 0;
 
        diff_warn_rename_limit("diff.renameLimit",
                               opt->needed_rename_limit,
                               opt->degraded_cc_to_c);
-       if (!opt->flags.exit_with_status &&
-           !(opt->output_format & DIFF_FORMAT_CHECKDIFF))
-               return status;
+
        if (opt->flags.exit_with_status &&
            opt->flags.has_changes)
                result |= 01;
@@ -6892,6 +7065,7 @@ void compute_diffstat(struct diff_options *options,
                if (check_pair_status(p))
                        diff_flush_stat(p, options, diffstat);
        }
+       options->found_changes = !!diffstat->nr;
 }
 
 void diff_addremove(struct diff_options *options,
@@ -7013,7 +7187,7 @@ static char *run_textconv(struct repository *r,
        struct strbuf buf = STRBUF_INIT;
        int err = 0;
 
-       temp = prepare_temp_file(r, spec->path, spec);
+       temp = prepare_temp_file(r, spec);
        strvec_push(&child.args, pgm);
        strvec_push(&child.args, temp->name);
 
diff --git a/diff.h b/diff.h
index 8ae18e5ab1ef31b5ff8911a3e1fb671bd4043482..66bd8aeb2936fbe9d6610f7dc1202e31e1450ebb 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -4,10 +4,11 @@
 #ifndef DIFF_H
 #define DIFF_H
 
-#include "tree-walk.h"
+#include "hash-ll.h"
 #include "pathspec.h"
-#include "object.h"
-#include "oidset.h"
+#include "strbuf.h"
+
+struct oidset;
 
 /**
  * The diff API is for programs that compare two sets of files (e.g. two trees,
@@ -71,7 +72,6 @@ struct oid_array;
 struct option;
 struct repository;
 struct rev_info;
-struct strbuf;
 struct userdiff_driver;
 
 typedef int (*pathchange_fn_t)(struct diff_options *options,
@@ -333,6 +333,7 @@ struct diff_options {
        int prefix_length;
        const char *stat_sep;
        int xdl_opts;
+       int ignore_driver_algorithm;
 
        /* see Documentation/diff-options.txt */
        char **anchors;
@@ -394,7 +395,6 @@ struct diff_options {
        unsigned color_moved_ws_handling;
 
        struct repository *repo;
-       struct option *parseopts;
        struct strmap *additional_path_headers;
 
        int no_free;
@@ -497,6 +497,8 @@ void diff_tree_combined(const struct object_id *oid, const struct oid_array *par
 void diff_tree_combined_merge(const struct commit *commit, struct rev_info *rev);
 
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
+void diff_set_noprefix(struct diff_options *options);
+void diff_set_default_prefix(struct diff_options *options);
 
 int diff_can_quit_early(struct diff_options *);
 
@@ -531,16 +533,24 @@ void free_diffstat_info(struct diffstat_t *diffstat);
 int parse_long_opt(const char *opt, const char **argv,
                   const char **optarg);
 
-int git_diff_basic_config(const char *var, const char *value, void *cb);
+struct config_context;
+int git_diff_basic_config(const char *var, const char *value,
+                         const struct config_context *ctx, void *cb);
 int git_diff_heuristic_config(const char *var, const char *value, void *cb);
 void init_diff_ui_defaults(void);
-int git_diff_ui_config(const char *var, const char *value, void *cb);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define diff_setup(diffopts) repo_diff_setup(the_repository, diffopts)
-#endif
+int git_diff_ui_config(const char *var, const char *value,
+                      const struct config_context *ctx, void *cb);
 void repo_diff_setup(struct repository *, struct diff_options *);
+struct option *add_diff_options(const struct option *, struct diff_options *);
 int diff_opt_parse(struct diff_options *, const char **, int, const char *);
 void diff_setup_done(struct diff_options *);
+
+/*
+ * Returns true if the pathspec can work with --follow mode. If die_on_error is
+ * set, die() with a specific error message rather than returning false.
+ */
+int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error);
+
 int git_config_rename(const char *var, const char *value);
 
 #define DIFF_DETECT_RENAME     1
@@ -563,6 +573,7 @@ int git_config_rename(const char *var, const char *value);
 
 #define DIFF_PICKAXE_IGNORE_CASE       32
 
+void init_diffstat_widths(struct diff_options *);
 void diffcore_std(struct diff_options *);
 void diffcore_fix_diff_index(void);
 
@@ -616,7 +627,7 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc);
 #define DIFF_STATUS_FILTER_BROKEN      'B'
 
 /*
- * This is different from find_unique_abbrev() in that
+ * This is different from repo_find_unique_abbrev() in that
  * it stuffs the result with dots for alignment.
  */
 const char *diff_aligned_abbrev(const struct object_id *sha1, int);
@@ -627,17 +638,17 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb);
 #define DIFF_SILENT_ON_REMOVED 01
 /* report racily-clean paths as modified */
 #define DIFF_RACY_IS_MODIFIED 02
-int run_diff_files(struct rev_info *revs, unsigned int option);
+void run_diff_files(struct rev_info *revs, unsigned int option);
 
 #define DIFF_INDEX_CACHED 01
 #define DIFF_INDEX_MERGE_BASE 02
-int run_diff_index(struct rev_info *revs, unsigned int option);
+void run_diff_index(struct rev_info *revs, unsigned int option);
 
 int do_diff_cache(const struct object_id *, struct diff_options *);
-int diff_flush_patch_id(struct diff_options *, struct object_id *, int, int);
+int diff_flush_patch_id(struct diff_options *, struct object_id *, int);
 void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx);
 
-int diff_result_code(struct diff_options *, int);
+int diff_result_code(struct diff_options *);
 
 int diff_no_index(struct rev_info *,
                  int implicit_no_index, int, const char **);
@@ -696,4 +707,6 @@ void print_stat_summary(FILE *fp, int files,
                        int insertions, int deletions);
 void setup_diff_pager(struct diff_options *);
 
+extern int diff_auto_refresh_index;
+
 #endif /* DIFF_H */
index 0d4a14964d00b3a77016fb0ed677b1847d1ab72c..49ba38aa7c0a58433380e560f6d84e0cc084a67f 100644 (file)
@@ -1,9 +1,10 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
-#include "diff.h"
+#include "git-compat-util.h"
 #include "diffcore.h"
+#include "hash.h"
+#include "object.h"
 #include "promisor-remote.h"
 
 static int should_break(struct repository *r,
@@ -65,7 +66,7 @@ static int should_break(struct repository *r,
            oideq(&src->oid, &dst->oid))
                return 0; /* they are the same */
 
-       if (r == the_repository && has_promisor_remote()) {
+       if (r == the_repository && repo_has_promisor_remote(the_repository)) {
                options.missing_object_cb = diff_queued_diff_prefetch;
                options.missing_object_data = r;
        }
index 18d8f766d70108e868a5bb21ad6c45550bb8c34d..4927ab8fb0c91554b849e3046e424cccc7eff325 100644 (file)
@@ -1,5 +1,4 @@
-#include "cache.h"
-#include "diff.h"
+#include "git-compat-util.h"
 #include "diffcore.h"
 
 /*
index 19e73311f9cd830da70428439b7b6a14d5345eec..e7d20ebd2d1b45b073ac2825b9371986d305ee29 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "wildmatch.h"
 
 static char **order;
 static int order_cnt;
index c88e50c632952afbf46eb859e93470fc207c54f0..b195fa4eb3c045c030a47c84edcaa702e447c8f8 100644 (file)
@@ -2,12 +2,13 @@
  * Copyright (C) 2005 Junio C Hamano
  * Copyright (C) 2010 Google Inc.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "xdiff-interface.h"
 #include "kwset.h"
-#include "commit.h"
+#include "oidset.h"
+#include "pretty.h"
 #include "quote.h"
 
 typedef int (*pickaxe_fn)(mmfile_t *one, mmfile_t *two,
@@ -38,7 +39,7 @@ static int diffgrep_consume(void *priv, char *line, unsigned long len)
 
 static int diff_grep(mmfile_t *one, mmfile_t *two,
                     struct diff_options *o,
-                    regex_t *regexp, kwset_t kws)
+                    regex_t *regexp, kwset_t kws UNUSED)
 {
        struct diffgrep_cb ecbdata;
        xpparam_t xpp;
@@ -114,7 +115,7 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws,
 }
 
 static int has_changes(mmfile_t *one, mmfile_t *two,
-                      struct diff_options *o,
+                      struct diff_options *o UNUSED,
                       regex_t *regexp, kwset_t kws)
 {
        unsigned int c1 = one ? contains(one, regexp, kws, 0) : 0;
index c0422d9e709a65d5a77d1444fcff0a4d313299bd..5a6e2bcac7147e487624c1bf53e1572a54b0d93d 100644 (file)
@@ -2,14 +2,18 @@
  *
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "hashmap.h"
+#include "mem-pool.h"
+#include "oid-array.h"
 #include "progress.h"
 #include "promisor-remote.h"
+#include "string-list.h"
 #include "strmap.h"
+#include "trace2.h"
 
 /* Table of rename/copy destinations */
 
@@ -981,7 +985,7 @@ static int find_basename_matches(struct diff_options *options,
                        strintmap_set(&dests, base, i);
        }
 
-       if (options->repo == the_repository && has_promisor_remote()) {
+       if (options->repo == the_repository && repo_has_promisor_remote(the_repository)) {
                dpf_options.missing_object_cb = basename_prefetch;
                dpf_options.missing_object_data = &prefetch_options;
        }
@@ -1567,7 +1571,7 @@ void diffcore_rename_extended(struct diff_options *options,
 
        /* Finish setting up dpf_options */
        prefetch_options.skip_unmodified = skip_unmodified;
-       if (options->repo == the_repository && has_promisor_remote()) {
+       if (options->repo == the_repository && repo_has_promisor_remote(the_repository)) {
                dpf_options.missing_object_cb = inexact_prefetch;
                dpf_options.missing_object_data = &prefetch_options;
        }
index 445f060ab0010ede4009b92c3d4acb0b34f644b9..533986cf632d4231fd067b244e2776a256af8e3e 100644 (file)
@@ -2,7 +2,8 @@
  * Copyright (C) 2021, Google LLC.
  * Based on diffcore-order.c, which is Copyright (C) 2005, Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "diff.h"
 #include "diffcore.h"
 
index badc2261c201831a620fcc7c29edcc1fd3bdbf1e..5ffe4ec788f84044df42b13eb847dec1f4e1fd36 100644 (file)
@@ -4,9 +4,11 @@
 #ifndef DIFFCORE_H
 #define DIFFCORE_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 struct diff_options;
+struct mem_pool;
+struct oid_array;
 struct repository;
 struct strintmap;
 struct strmap;
@@ -162,6 +164,7 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *,
                                 struct diff_filespec *,
                                 struct diff_filespec *);
 void diff_q(struct diff_queue_struct *, struct diff_filepair *);
+void diff_free_queue(struct diff_queue_struct *q);
 
 /* dir_rename_relevance: the reason we want rename information for a dir */
 enum dir_rename_relevance {
index 3764dd81a185b296a5d4c0a9fc8c0ae2bd4085ec..278b04243a3f40e63df433eea9549f90aed1c34d 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
 #include "iterator.h"
 #include "dir-iterator.h"
@@ -112,10 +112,7 @@ static int prepare_next_entry_data(struct dir_iterator_int *iter,
        iter->base.basename = iter->base.path.buf +
                              iter->levels[iter->levels_nr - 1].prefix_len;
 
-       if (iter->flags & DIR_ITERATOR_FOLLOW_SYMLINKS)
-               err = stat(iter->base.path.buf, &iter->base.st);
-       else
-               err = lstat(iter->base.path.buf, &iter->base.st);
+       err = lstat(iter->base.path.buf, &iter->base.st);
 
        saved_errno = errno;
        if (err && errno != ENOENT)
@@ -213,13 +210,10 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
        iter->flags = flags;
 
        /*
-        * Note: stat/lstat already checks for NULL or empty strings and
+        * Note: lstat already checks for NULL or empty strings and
         * nonexistent paths.
         */
-       if (iter->flags & DIR_ITERATOR_FOLLOW_SYMLINKS)
-               err = stat(iter->base.path.buf, &iter->base.st);
-       else
-               err = lstat(iter->base.path.buf, &iter->base.st);
+       err = lstat(iter->base.path.buf, &iter->base.st);
 
        if (err < 0) {
                saved_errno = errno;
index e3b6ff28007366568d981631d5b312bd849486c0..479e1ec784dfa4081430ea5a104302e79d10eae3 100644 (file)
  *   and ITER_ERROR is returned immediately. In both cases, a meaningful
  *   warning is emitted. Note: ENOENT errors are always ignored so that
  *   the API users may remove files during iteration.
- *
- * - DIR_ITERATOR_FOLLOW_SYMLINKS: make dir-iterator follow symlinks.
- *   i.e., linked directories' contents will be iterated over and
- *   iter->base.st will contain information on the referred files,
- *   not the symlinks themselves, which is the default behavior. Broken
- *   symlinks are ignored.
- *
- *   Note: setting DIR_ITERATOR_FOLLOW_SYMLINKS affects resolving the
- *   starting path as well (e.g., attempting to iterate starting at a
- *   symbolic link pointing to a directory without FOLLOW_SYMLINKS will
- *   result in an error).
- *
- * Warning: circular symlinks are also followed when
- * DIR_ITERATOR_FOLLOW_SYMLINKS is set. The iteration may end up with
- * an ELOOP if they happen and DIR_ITERATOR_PEDANTIC is set.
  */
 #define DIR_ITERATOR_PEDANTIC (1 << 0)
-#define DIR_ITERATOR_FOLLOW_SYMLINKS (1 << 1)
 
 struct dir_iterator {
        /* The current path: */
@@ -88,9 +72,7 @@ struct dir_iterator {
        const char *basename;
 
        /*
-        * The result of calling lstat() on path; or stat(), if the
-        * DIR_ITERATOR_FOLLOW_SYMLINKS flag was set at
-        * dir_iterator's initialization.
+        * The result of calling lstat() on path.
         */
        struct stat st;
 };
diff --git a/dir.c b/dir.c
index d604d1bab982980fd5506a0fae072bac3510a5ae..ac699542302cece1d1aea426cd4cc37940e6fead 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -5,19 +5,31 @@
  * Copyright (C) Linus Torvalds, 2005-2006
  *              Junio Hamano, 2005-2006
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "convert.h"
 #include "dir.h"
-#include "object-store.h"
-#include "attr.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "refs.h"
 #include "wildmatch.h"
 #include "pathspec.h"
 #include "utf8.h"
 #include "varint.h"
 #include "ewah/ewok.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
+#include "read-cache-ll.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule-config.h"
+#include "symlinks.h"
+#include "trace2.h"
+#include "tree.h"
 
 /*
  * Tells read_directory_recursive how a file or directory should be treated.
@@ -267,7 +279,7 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
        *size_out = 0;
        *data_out = NULL;
 
-       data = read_object_file(oid, &type, &sz);
+       data = repo_read_object_file(the_repository, oid, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return -1;
@@ -363,7 +375,7 @@ static int match_pathspec_item(struct index_state *istate,
                return 0;
 
        if (item->attr_match_nr &&
-           !match_pathspec_attrs(istate, name, namelen, item))
+           !match_pathspec_attrs(istate, name - prefix, namelen + prefix, item))
                return 0;
 
        /* If the match was just the prefix, we matched */
@@ -732,6 +744,13 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
                goto clear_hashmaps;
        }
 
+       if (!(given->flags & PATTERN_FLAG_MUSTBEDIR) &&
+           strcmp(given->pattern, "/*")) {
+               /* Not a cone pattern. */
+               warning(_("unrecognized pattern: '%s'"), given->pattern);
+               goto clear_hashmaps;
+       }
+
        prev = given->pattern;
        cur = given->pattern + 1;
        next = given->pattern + 2;
@@ -1183,7 +1202,7 @@ struct pattern_list *add_pattern_list(struct dir_struct *dir,
        struct pattern_list *pl;
        struct exclude_list_group *group;
 
-       group = &dir->exclude_list_group[group_type];
+       group = &dir->internal.exclude_list_group[group_type];
        ALLOC_GROW(group->pl, group->nr + 1, group->alloc);
        pl = &group->pl[group->nr++];
        memset(pl, 0, sizeof(*pl));
@@ -1204,7 +1223,7 @@ static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname,
         * differently when dir->untracked is non-NULL.
         */
        if (!dir->untracked)
-               dir->unmanaged_exclude_files++;
+               dir->internal.unmanaged_exclude_files++;
        pl = add_pattern_list(dir, EXC_FILE, fname);
        if (add_patterns(fname, "", 0, pl, NULL, 0, oid_stat) < 0)
                die(_("cannot use %s as an exclude file"), fname);
@@ -1212,7 +1231,7 @@ static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname,
 
 void add_patterns_from_file(struct dir_struct *dir, const char *fname)
 {
-       dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
+       dir->internal.unmanaged_exclude_files++; /* see validate_untracked_cache() */
        add_patterns_from_file_1(dir, fname, NULL);
 }
 
@@ -1512,7 +1531,7 @@ static struct path_pattern *last_matching_pattern_from_lists(
        struct exclude_list_group *group;
        struct path_pattern *pattern;
        for (i = EXC_CMDL; i <= EXC_FILE; i++) {
-               group = &dir->exclude_list_group[i];
+               group = &dir->internal.exclude_list_group[i];
                for (j = group->nr - 1; j >= 0; j--) {
                        pattern = last_matching_pattern_from_list(
                                pathname, pathlen, basename, dtype_p,
@@ -1538,20 +1557,20 @@ static void prep_exclude(struct dir_struct *dir,
        struct untracked_cache_dir *untracked;
        int current;
 
-       group = &dir->exclude_list_group[EXC_DIRS];
+       group = &dir->internal.exclude_list_group[EXC_DIRS];
 
        /*
         * Pop the exclude lists from the EXCL_DIRS exclude_list_group
         * which originate from directories not in the prefix of the
         * path being checked.
         */
-       while ((stk = dir->exclude_stack) != NULL) {
+       while ((stk = dir->internal.exclude_stack) != NULL) {
                if (stk->baselen <= baselen &&
-                   !strncmp(dir->basebuf.buf, base, stk->baselen))
+                   !strncmp(dir->internal.basebuf.buf, base, stk->baselen))
                        break;
-               pl = &group->pl[dir->exclude_stack->exclude_ix];
-               dir->exclude_stack = stk->prev;
-               dir->pattern = NULL;
+               pl = &group->pl[dir->internal.exclude_stack->exclude_ix];
+               dir->internal.exclude_stack = stk->prev;
+               dir->internal.pattern = NULL;
                free((char *)pl->src); /* see strbuf_detach() below */
                clear_pattern_list(pl);
                free(stk);
@@ -1559,7 +1578,7 @@ static void prep_exclude(struct dir_struct *dir,
        }
 
        /* Skip traversing into sub directories if the parent is excluded */
-       if (dir->pattern)
+       if (dir->internal.pattern)
                return;
 
        /*
@@ -1567,12 +1586,12 @@ static void prep_exclude(struct dir_struct *dir,
         * memset(dir, 0, sizeof(*dir)) before use. Changing all of
         * them seems lots of work for little benefit.
         */
-       if (!dir->basebuf.buf)
-               strbuf_init(&dir->basebuf, PATH_MAX);
+       if (!dir->internal.basebuf.buf)
+               strbuf_init(&dir->internal.basebuf, PATH_MAX);
 
        /* Read from the parent directories and push them down. */
        current = stk ? stk->baselen : -1;
-       strbuf_setlen(&dir->basebuf, current < 0 ? 0 : current);
+       strbuf_setlen(&dir->internal.basebuf, current < 0 ? 0 : current);
        if (dir->untracked)
                untracked = stk ? stk->ucd : dir->untracked->root;
        else
@@ -1592,32 +1611,33 @@ static void prep_exclude(struct dir_struct *dir,
                                die("oops in prep_exclude");
                        cp++;
                        untracked =
-                               lookup_untracked(dir->untracked, untracked,
+                               lookup_untracked(dir->untracked,
+                                                untracked,
                                                 base + current,
                                                 cp - base - current);
                }
-               stk->prev = dir->exclude_stack;
+               stk->prev = dir->internal.exclude_stack;
                stk->baselen = cp - base;
                stk->exclude_ix = group->nr;
                stk->ucd = untracked;
                pl = add_pattern_list(dir, EXC_DIRS, NULL);
-               strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
-               assert(stk->baselen == dir->basebuf.len);
+               strbuf_add(&dir->internal.basebuf, base + current, stk->baselen - current);
+               assert(stk->baselen == dir->internal.basebuf.len);
 
                /* Abort if the directory is excluded */
                if (stk->baselen) {
                        int dt = DT_DIR;
-                       dir->basebuf.buf[stk->baselen - 1] = 0;
-                       dir->pattern = last_matching_pattern_from_lists(dir,
+                       dir->internal.basebuf.buf[stk->baselen - 1] = 0;
+                       dir->internal.pattern = last_matching_pattern_from_lists(dir,
                                                                        istate,
-                               dir->basebuf.buf, stk->baselen - 1,
-                               dir->basebuf.buf + current, &dt);
-                       dir->basebuf.buf[stk->baselen - 1] = '/';
-                       if (dir->pattern &&
-                           dir->pattern->flags & PATTERN_FLAG_NEGATIVE)
-                               dir->pattern = NULL;
-                       if (dir->pattern) {
-                               dir->exclude_stack = stk;
+                               dir->internal.basebuf.buf, stk->baselen - 1,
+                               dir->internal.basebuf.buf + current, &dt);
+                       dir->internal.basebuf.buf[stk->baselen - 1] = '/';
+                       if (dir->internal.pattern &&
+                           dir->internal.pattern->flags & PATTERN_FLAG_NEGATIVE)
+                               dir->internal.pattern = NULL;
+                       if (dir->internal.pattern) {
+                               dir->internal.exclude_stack = stk;
                                return;
                        }
                }
@@ -1640,15 +1660,15 @@ static void prep_exclude(struct dir_struct *dir,
                      */
                     !is_null_oid(&untracked->exclude_oid))) {
                        /*
-                        * dir->basebuf gets reused by the traversal, but we
-                        * need fname to remain unchanged to ensure the src
-                        * member of each struct path_pattern correctly
+                        * dir->internal.basebuf gets reused by the traversal,
+                        * but we need fname to remain unchanged to ensure the
+                        * src member of each struct path_pattern correctly
                         * back-references its source file.  Other invocations
                         * of add_pattern_list provide stable strings, so we
                         * strbuf_detach() and free() here in the caller.
                         */
                        struct strbuf sb = STRBUF_INIT;
-                       strbuf_addbuf(&sb, &dir->basebuf);
+                       strbuf_addbuf(&sb, &dir->internal.basebuf);
                        strbuf_addstr(&sb, dir->exclude_per_dir);
                        pl->src = strbuf_detach(&sb, NULL);
                        add_patterns(pl->src, pl->src, stk->baselen, pl, istate,
@@ -1674,10 +1694,10 @@ static void prep_exclude(struct dir_struct *dir,
                        invalidate_gitignore(dir->untracked, untracked);
                        oidcpy(&untracked->exclude_oid, &oid_stat.oid);
                }
-               dir->exclude_stack = stk;
+               dir->internal.exclude_stack = stk;
                current = stk->baselen;
        }
-       strbuf_setlen(&dir->basebuf, baselen);
+       strbuf_setlen(&dir->internal.basebuf, baselen);
 }
 
 /*
@@ -1697,8 +1717,8 @@ struct path_pattern *last_matching_pattern(struct dir_struct *dir,
 
        prep_exclude(dir, istate, pathname, basename-pathname);
 
-       if (dir->pattern)
-               return dir->pattern;
+       if (dir->internal.pattern)
+               return dir->internal.pattern;
 
        return last_matching_pattern_from_lists(dir, istate, pathname, pathlen,
                        basename, dtype_p);
@@ -1735,7 +1755,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir,
        if (index_file_exists(istate, pathname, len, ignore_case))
                return NULL;
 
-       ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
+       ALLOC_GROW(dir->entries, dir->nr+1, dir->internal.alloc);
        return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
 }
 
@@ -1746,7 +1766,7 @@ struct dir_entry *dir_add_ignored(struct dir_struct *dir,
        if (!index_name_is_other(istate, pathname, len))
                return NULL;
 
-       ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc);
+       ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->internal.ignored_alloc);
        return dir->ignored[dir->ignored_nr++] = dir_entry_new(pathname, len);
 }
 
@@ -2158,7 +2178,8 @@ static int exclude_matches_pathspec(const char *path, int pathlen,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        for (i = 0; i < pathspec->nr; i++) {
                const struct pathspec_item *item = &pathspec->items[i];
@@ -2214,6 +2235,39 @@ static int get_index_dtype(struct index_state *istate,
        return DT_UNKNOWN;
 }
 
+unsigned char get_dtype(struct dirent *e, struct strbuf *path,
+                       int follow_symlink)
+{
+       struct stat st;
+       unsigned char dtype = DTYPE(e);
+       size_t base_path_len;
+
+       if (dtype != DT_UNKNOWN && !(follow_symlink && dtype == DT_LNK))
+               return dtype;
+
+       /*
+        * d_type unknown or unfollowed symlink, try to fall back on [l]stat
+        * results. If [l]stat fails, explicitly set DT_UNKNOWN.
+        */
+       base_path_len = path->len;
+       strbuf_addstr(path, e->d_name);
+       if ((follow_symlink && stat(path->buf, &st)) ||
+           (!follow_symlink && lstat(path->buf, &st)))
+               goto cleanup;
+
+       /* determine d_type from st_mode */
+       if (S_ISREG(st.st_mode))
+               dtype = DT_REG;
+       else if (S_ISDIR(st.st_mode))
+               dtype = DT_DIR;
+       else if (S_ISLNK(st.st_mode))
+               dtype = DT_LNK;
+
+cleanup:
+       strbuf_setlen(path, base_path_len);
+       return dtype;
+}
+
 static int resolve_dtype(int dtype, struct index_state *istate,
                         const char *path, int len)
 {
@@ -2562,7 +2616,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
 
        if (open_cached_dir(&cdir, dir, untracked, istate, &path, check_only))
                goto out;
-       dir->visited_directories++;
+       dir->internal.visited_directories++;
 
        if (untracked)
                untracked->check_only = !!check_only;
@@ -2571,7 +2625,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                /* check how the file or directory should be treated */
                state = treat_path(dir, untracked, &cdir, istate, &path,
                                   baselen, pathspec);
-               dir->visited_paths++;
+               dir->internal.visited_paths++;
 
                if (state > dir_state)
                        dir_state = state;
@@ -2579,7 +2633,8 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                /* recurse into subdir if instructed by treat_path */
                if (state == path_recurse) {
                        struct untracked_cache_dir *ud;
-                       ud = lookup_untracked(dir->untracked, untracked,
+                       ud = lookup_untracked(dir->untracked,
+                                             untracked,
                                              path.buf + baselen,
                                              path.len - baselen);
                        subdir_state =
@@ -2839,7 +2894,7 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
         * condition also catches running setup_standard_excludes()
         * before setting dir->untracked!
         */
-       if (dir->unmanaged_exclude_files)
+       if (dir->internal.unmanaged_exclude_files)
                return NULL;
 
        /*
@@ -2868,7 +2923,7 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
         * EXC_CMDL is not considered in the cache. If people set it,
         * skip the cache.
         */
-       if (dir->exclude_list_group[EXC_CMDL].nr)
+       if (dir->internal.exclude_list_group[EXC_CMDL].nr)
                return NULL;
 
        if (!ident_in_untracked(dir->untracked)) {
@@ -2928,15 +2983,15 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
 
        /* Validate $GIT_DIR/info/exclude and core.excludesfile */
        root = dir->untracked->root;
-       if (!oideq(&dir->ss_info_exclude.oid,
+       if (!oideq(&dir->internal.ss_info_exclude.oid,
                   &dir->untracked->ss_info_exclude.oid)) {
                invalidate_gitignore(dir->untracked, root);
-               dir->untracked->ss_info_exclude = dir->ss_info_exclude;
+               dir->untracked->ss_info_exclude = dir->internal.ss_info_exclude;
        }
-       if (!oideq(&dir->ss_excludes_file.oid,
+       if (!oideq(&dir->internal.ss_excludes_file.oid,
                   &dir->untracked->ss_excludes_file.oid)) {
                invalidate_gitignore(dir->untracked, root);
-               dir->untracked->ss_excludes_file = dir->ss_excludes_file;
+               dir->untracked->ss_excludes_file = dir->internal.ss_excludes_file;
        }
 
        /* Make sure this directory is not dropped out at saving phase */
@@ -2962,9 +3017,9 @@ static void emit_traversal_statistics(struct dir_struct *dir,
        }
 
        trace2_data_intmax("read_directory", repo,
-                          "directories-visited", dir->visited_directories);
+                          "directories-visited", dir->internal.visited_directories);
        trace2_data_intmax("read_directory", repo,
-                          "paths-visited", dir->visited_paths);
+                          "paths-visited", dir->internal.visited_paths);
 
        if (!dir->untracked)
                return;
@@ -2986,8 +3041,8 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
        struct untracked_cache_dir *untracked;
 
        trace2_region_enter("dir", "read_directory", istate->repo);
-       dir->visited_paths = 0;
-       dir->visited_directories = 0;
+       dir->internal.visited_paths = 0;
+       dir->internal.visited_directories = 0;
 
        if (has_symlink_leading_path(path, len)) {
                trace2_region_leave("dir", "read_directory", istate->repo);
@@ -3335,14 +3390,14 @@ void setup_standard_excludes(struct dir_struct *dir)
                excludes_file = xdg_config_home("ignore");
        if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
                add_patterns_from_file_1(dir, excludes_file,
-                                        dir->untracked ? &dir->ss_excludes_file : NULL);
+                                        dir->untracked ? &dir->internal.ss_excludes_file : NULL);
 
        /* per repository user preference */
        if (startup_info->have_repository) {
                const char *path = git_path_info_exclude();
                if (!access_or_warn(path, R_OK, 0))
                        add_patterns_from_file_1(dir, path,
-                                                dir->untracked ? &dir->ss_info_exclude : NULL);
+                                                dir->untracked ? &dir->internal.ss_info_exclude : NULL);
        }
 }
 
@@ -3398,7 +3453,7 @@ void dir_clear(struct dir_struct *dir)
        struct dir_struct new = DIR_INIT;
 
        for (i = EXC_CMDL; i <= EXC_FILE; i++) {
-               group = &dir->exclude_list_group[i];
+               group = &dir->internal.exclude_list_group[i];
                for (j = 0; j < group->nr; j++) {
                        pl = &group->pl[j];
                        if (i == EXC_DIRS)
@@ -3415,13 +3470,13 @@ void dir_clear(struct dir_struct *dir)
        free(dir->ignored);
        free(dir->entries);
 
-       stk = dir->exclude_stack;
+       stk = dir->internal.exclude_stack;
        while (stk) {
                struct exclude_stack *prev = stk->prev;
                free(stk);
                stk = prev;
        }
-       strbuf_release(&dir->basebuf);
+       strbuf_release(&dir->internal.basebuf);
 
        memcpy(dir, &new, sizeof(*dir));
 }
@@ -3581,8 +3636,12 @@ static void free_untracked(struct untracked_cache_dir *ucd)
 
 void free_untracked_cache(struct untracked_cache *uc)
 {
-       if (uc)
-               free_untracked(uc->root);
+       if (!uc)
+               return;
+
+       free(uc->exclude_per_dir_to_free);
+       strbuf_release(&uc->ident);
+       free_untracked(uc->root);
        free(uc);
 }
 
@@ -3739,7 +3798,7 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
                      next + offset + hashsz);
        uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
        exclude_per_dir = (const char *)next + exclude_per_dir_offset;
-       uc->exclude_per_dir = xstrdup(exclude_per_dir);
+       uc->exclude_per_dir = uc->exclude_per_dir_to_free = xstrdup(exclude_per_dir);
        /* NUL after exclude_per_dir is covered by sizeof(*ouc) */
        next += exclude_per_dir_offset + strlen(exclude_per_dir) + 1;
        if (next >= end)
diff --git a/dir.h b/dir.h
index 674747d93af7ad903b1e78a1418ff07c914a69ec..98aa85fcc0ee357a2df50014008c3e5ec12acb25 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -1,10 +1,14 @@
 #ifndef DIR_H
 #define DIR_H
 
-#include "cache.h"
+#include "hash-ll.h"
 #include "hashmap.h"
+#include "pathspec.h"
+#include "statinfo.h"
 #include "strbuf.h"
 
+struct repository;
+
 /**
  * The directory listing API is used to enumerate paths in the work tree,
  * optionally taking `.git/info/exclude` and `.gitignore` files per directory
@@ -39,6 +43,8 @@
  *
  */
 
+struct repository;
+
 struct dir_entry {
        unsigned int len;
        char name[FLEX_ARRAY]; /* more */
@@ -188,6 +194,7 @@ struct untracked_cache {
        struct oid_stat ss_info_exclude;
        struct oid_stat ss_excludes_file;
        const char *exclude_per_dir;
+       char *exclude_per_dir_to_free;
        struct strbuf ident;
        /*
         * dir_struct#flags must match dir_flags or the untracked
@@ -211,17 +218,6 @@ struct untracked_cache {
  */
 struct dir_struct {
 
-       /* The number of members in `entries[]` array. */
-       int nr;
-
-       /* Internal use; keeps track of allocation of `entries[]` array.*/
-       int alloc;
-
-       /* The number of members in `ignored[]` array. */
-       int ignored_nr;
-
-       int ignored_alloc;
-
        /* bit-field of options */
        enum {
 
@@ -286,66 +282,103 @@ struct dir_struct {
                DIR_SKIP_NESTED_GIT = 1<<9
        } flags;
 
+       /* The number of members in `entries[]` array. */
+       int nr; /* output only */
+
+       /* The number of members in `ignored[]` array. */
+       int ignored_nr; /* output only */
+
        /* An array of `struct dir_entry`, each element of which describes a path. */
-       struct dir_entry **entries;
+       struct dir_entry **entries; /* output only */
 
        /**
         * used for ignored paths with the `DIR_SHOW_IGNORED_TOO` and
         * `DIR_COLLECT_IGNORED` flags.
         */
-       struct dir_entry **ignored;
+       struct dir_entry **ignored; /* output only */
+
+       /* Enable/update untracked file cache if set */
+       struct untracked_cache *untracked;
 
        /**
-        * The name of the file to be read in each directory for excluded files
-        * (typically `.gitignore`).
+        * Deprecated: ls-files is the only allowed caller; all other callers
+        * should leave this as NULL; it pre-dated the
+        * setup_standard_excludes() mechanism that replaces this.
+        *
+        * This field tracks the name of the file to be read in each directory
+        * for excluded files (typically `.gitignore`).
         */
        const char *exclude_per_dir;
 
-       /*
-        * We maintain three groups of exclude pattern lists:
-        *
-        * EXC_CMDL lists patterns explicitly given on the command line.
-        * EXC_DIRS lists patterns obtained from per-directory ignore files.
-        * EXC_FILE lists patterns from fallback ignore files, e.g.
-        *   - .git/info/exclude
-        *   - core.excludesfile
-        *
-        * Each group contains multiple exclude lists, a single list
-        * per source.
-        */
+       struct dir_struct_internal {
+               /* Keeps track of allocation of `entries[]` array.*/
+               int alloc;
+
+               /* Keeps track of allocation of `ignored[]` array. */
+               int ignored_alloc;
+
+               /*
+                * We maintain three groups of exclude pattern lists:
+                *
+                * EXC_CMDL lists patterns explicitly given on the command line.
+                * EXC_DIRS lists patterns obtained from per-directory ignore
+                *          files.
+                * EXC_FILE lists patterns from fallback ignore files, e.g.
+                *   - .git/info/exclude
+                *   - core.excludesfile
+                *
+                * Each group contains multiple exclude lists, a single list
+                * per source.
+                */
 #define EXC_CMDL 0
 #define EXC_DIRS 1
 #define EXC_FILE 2
-       struct exclude_list_group exclude_list_group[3];
-
-       /*
-        * Temporary variables which are used during loading of the
-        * per-directory exclude lists.
-        *
-        * exclude_stack points to the top of the exclude_stack, and
-        * basebuf contains the full path to the current
-        * (sub)directory in the traversal. Exclude points to the
-        * matching exclude struct if the directory is excluded.
-        */
-       struct exclude_stack *exclude_stack;
-       struct path_pattern *pattern;
-       struct strbuf basebuf;
-
-       /* Enable untracked file cache if set */
-       struct untracked_cache *untracked;
-       struct oid_stat ss_info_exclude;
-       struct oid_stat ss_excludes_file;
-       unsigned unmanaged_exclude_files;
+               struct exclude_list_group exclude_list_group[3];
 
-       /* Stats about the traversal */
-       unsigned visited_paths;
-       unsigned visited_directories;
+               /*
+                * Temporary variables which are used during loading of the
+                * per-directory exclude lists.
+                *
+                * exclude_stack points to the top of the exclude_stack, and
+                * basebuf contains the full path to the current
+                * (sub)directory in the traversal. Exclude points to the
+                * matching exclude struct if the directory is excluded.
+                */
+               struct exclude_stack *exclude_stack;
+               struct path_pattern *pattern;
+               struct strbuf basebuf;
+
+               /* Additional metadata related to 'untracked' */
+               struct oid_stat ss_info_exclude;
+               struct oid_stat ss_excludes_file;
+               unsigned unmanaged_exclude_files;
+
+               /* Stats about the traversal */
+               unsigned visited_paths;
+               unsigned visited_directories;
+       } internal;
 };
 
 #define DIR_INIT { 0 }
 
 struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp);
 
+/*
+ * Get the d_type of a dirent. If the d_type is unknown, derive it from
+ * stat.st_mode using the path to the dirent's containing directory (path) and
+ * the name of the dirent itself.
+ *
+ * If 'follow_symlink' is 1, this function will attempt to follow DT_LNK types
+ * using 'stat'. Links are *not* followed recursively, so a symlink pointing
+ * to another symlink will still resolve to 'DT_LNK'.
+ *
+ * Note that 'path' is assumed to have a trailing slash. It is also modified
+ * in-place during the execution of the function, but is then reverted to its
+ * original value before returning.
+ */
+unsigned char get_dtype(struct dirent *e, struct strbuf *path,
+                       int follow_symlink);
+
 /*Count the number of slashes for string s*/
 int count_slashes(const char *s);
 
@@ -362,10 +395,6 @@ int count_slashes(const char *s);
 int simple_length(const char *match);
 int no_wildcard(const char *string);
 char *common_prefix(const struct pathspec *pathspec);
-int match_pathspec(struct index_state *istate,
-                  const struct pathspec *pathspec,
-                  const char *name, int namelen,
-                  int prefix, char *seen, int is_dir);
 int report_path_error(const char *ps_matched, const struct pathspec *pathspec);
 int within_depth(const char *name, int namelen, int depth, int max_depth);
 
@@ -532,15 +561,6 @@ int submodule_path_match(struct index_state *istate,
                         const char *submodule_name,
                         char *seen);
 
-static inline int ce_path_match(struct index_state *istate,
-                               const struct cache_entry *ce,
-                               const struct pathspec *pathspec,
-                               char *seen)
-{
-       return match_pathspec(istate, pathspec, ce->name, ce_namelen(ce), 0, seen,
-                             S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
-}
-
 static inline int dir_path_match(struct index_state *istate,
                                 const struct dir_entry *ent,
                                 const struct pathspec *pathspec,
@@ -641,4 +661,5 @@ static inline int starts_with_dot_dot_slash_native(const char *const path)
 
        return path_match_flags(path, what | PATH_MATCH_NATIVE);
 }
+
 #endif
index 008c04fe2f6e0a8b39bc02005f0a576d92784bae..b67b802ddf8493ea25ccbb1fc5c046240bfe917d 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -1,5 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "pager.h"
+#include "path.h"
 #include "strbuf.h"
 #include "strvec.h"
 #include "run-command.h"
@@ -126,3 +133,31 @@ int launch_sequence_editor(const char *path, struct strbuf *buffer,
 {
        return launch_specified_editor(git_sequence_editor(), path, buffer, env);
 }
+
+int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
+                             const char *const *env)
+{
+       char *path2 = NULL;
+       int fd, res = 0;
+
+       if (!is_absolute_path(path))
+               path = path2 = xstrdup(git_path("%s", path));
+
+       fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+       if (fd < 0)
+               res = error_errno(_("could not open '%s' for writing"), path);
+       else if (write_in_full(fd, buffer->buf, buffer->len) < 0) {
+               res = error_errno(_("could not write to '%s'"), path);
+               close(fd);
+       } else if (close(fd) < 0)
+               res = error_errno(_("could not close '%s'"), path);
+       else {
+               strbuf_reset(buffer);
+               if (launch_editor(path, buffer, env) < 0)
+                       res = error_errno(_("could not edit '%s'"), path);
+               unlink(path);
+       }
+
+       free(path2);
+       return res;
+}
diff --git a/editor.h b/editor.h
new file mode 100644 (file)
index 0000000..8016bb5
--- /dev/null
+++ b/editor.h
@@ -0,0 +1,34 @@
+#ifndef EDITOR_H
+#define EDITOR_H
+
+struct strbuf;
+
+const char *git_editor(void);
+const char *git_sequence_editor(void);
+int is_terminal_dumb(void);
+
+/**
+ * Launch the user preferred editor to edit a file and fill the buffer
+ * with the file's contents upon the user completing their editing. The
+ * third argument can be used to set the environment which the editor is
+ * run in. If the buffer is NULL the editor is launched as usual but the
+ * file's contents are not read into the buffer upon completion.
+ */
+int launch_editor(const char *path, struct strbuf *buffer,
+                 const char *const *env);
+
+int launch_sequence_editor(const char *path, struct strbuf *buffer,
+                          const char *const *env);
+
+/*
+ * In contrast to `launch_editor()`, this function writes out the contents
+ * of the specified file first, then clears the `buffer`, then launches
+ * the editor and reads back in the file contents into the `buffer`.
+ * Finally, it deletes the temporary file.
+ *
+ * If `path` is relative, it refers to a file in the `.git` directory.
+ */
+int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
+                             const char *const *env);
+
+#endif
diff --git a/entry.c b/entry.c
index 616e4f073c1d6bd4016252423d4ced0958612432..f918a3a78e8154fb62de1fb2378d100f9c47e9ca 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -1,9 +1,14 @@
-#include "cache.h"
-#include "blob.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "object-store-ll.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "name-hash.h"
+#include "sparse-index.h"
 #include "streaming.h"
 #include "submodule.h"
+#include "symlinks.h"
 #include "progress.h"
 #include "fsmonitor.h"
 #include "entry.h"
@@ -86,7 +91,8 @@ void *read_blob_entry(const struct cache_entry *ce, size_t *size)
 {
        enum object_type type;
        unsigned long ul;
-       void *blob_data = read_object_file(&ce->oid, &type, &ul);
+       void *blob_data = repo_read_object_file(the_repository, &ce->oid,
+                                               &type, &ul);
 
        *size = ul;
        if (blob_data) {
@@ -383,7 +389,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
                        return error("cannot create submodule directory %s", path);
                sub = submodule_from_ce(ce);
                if (sub)
-                       return submodule_move_head(ce->name,
+                       return submodule_move_head(ce->name, state->super_prefix,
                                NULL, oid_to_hex(&ce->oid),
                                state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
                break;
@@ -476,7 +482,7 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
                         * no pathname to return.
                         */
                        BUG("Can't remove entry to a path");
-               unlink_entry(ce);
+               unlink_entry(ce, state->super_prefix);
                return 0;
        }
 
@@ -510,10 +516,10 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
                                if (!(st.st_mode & S_IFDIR))
                                        unlink_or_warn(ce->name);
 
-                               return submodule_move_head(ce->name,
+                               return submodule_move_head(ce->name, state->super_prefix,
                                        NULL, oid_to_hex(&ce->oid), 0);
                        } else
-                               return submodule_move_head(ce->name,
+                               return submodule_move_head(ce->name, state->super_prefix,
                                        "HEAD", oid_to_hex(&ce->oid),
                                        state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
                }
@@ -560,12 +566,12 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
        return write_entry(ce, path.buf, ca, state, 0, nr_checkouts);
 }
 
-void unlink_entry(const struct cache_entry *ce)
+void unlink_entry(const struct cache_entry *ce, const char *super_prefix)
 {
        const struct submodule *sub = submodule_from_ce(ce);
        if (sub) {
                /* state.force is set at the caller. */
-               submodule_move_head(ce->name, "HEAD", NULL,
+               submodule_move_head(ce->name, super_prefix, "HEAD", NULL,
                                    SUBMODULE_MOVE_HEAD_FORCE);
        }
        if (check_leading_path(ce->name, ce_namelen(ce), 1) >= 0)
@@ -574,3 +580,8 @@ void unlink_entry(const struct cache_entry *ce)
                return;
        schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
+
+int remove_or_warn(unsigned int mode, const char *file)
+{
+       return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
+}
diff --git a/entry.h b/entry.h
index 9be4659881e6ec979bb4ac5233c71af44b337586..ca3ed35bc08654ee47bc6c9331c1fad2804bfd32 100644 (file)
--- a/entry.h
+++ b/entry.h
@@ -1,13 +1,16 @@
 #ifndef ENTRY_H
 #define ENTRY_H
 
-#include "cache.h"
 #include "convert.h"
 
+struct cache_entry;
+struct index_state;
+
 struct checkout {
        struct index_state *istate;
        const char *base_dir;
        int base_dir_len;
+       const char *super_prefix;
        struct delayed_checkout *delayed_checkout;
        struct checkout_metadata meta;
        unsigned force:1,
@@ -48,12 +51,21 @@ int finish_delayed_checkout(struct checkout *state, int show_progress);
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
+ *
+ * The "super_prefix" is either NULL, or the "--super-prefix" passed
+ * down from "read-tree" et al.
  */
-void unlink_entry(const struct cache_entry *ce);
+void unlink_entry(const struct cache_entry *ce, const char *super_prefix);
 
 void *read_blob_entry(const struct cache_entry *ce, size_t *size);
 int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st);
 void update_ce_after_write(const struct checkout *state, struct cache_entry *ce,
                           struct stat *st);
 
+/*
+ * Calls the correct function out of {unlink,rmdir}_or_warn based on
+ * the supplied file mode.
+ */
+int remove_or_warn(unsigned int mode, const char *path);
+
 #endif /* ENTRY_H */
index 18d042b467d26a9cb3b228db1ef5afa2d903e72c..90632a39bc995af8bf56166b4d89bba5d6dd5272 100644 (file)
@@ -7,19 +7,28 @@
  * even if you might want to know where the git directory etc
  * are.
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "branch.h"
+#include "convert.h"
 #include "environment.h"
+#include "gettext.h"
 #include "repository.h"
 #include "config.h"
 #include "refs.h"
 #include "fmt-merge-msg.h"
 #include "commit.h"
 #include "strvec.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "path.h"
+#include "replace-object.h"
 #include "tmp-objdir.h"
 #include "chdir-notify.h"
+#include "setup.h"
 #include "shallow.h"
+#include "trace.h"
+#include "write-or-die.h"
 
 int trust_executable_bit = 1;
 int trust_ctime = 1;
@@ -33,7 +42,6 @@ int is_bare_repository_cfg = -1; /* unspecified */
 int warn_ambiguous_refs = 1;
 int warn_on_object_refname_ambiguity = 1;
 int repository_format_precious_objects;
-int repository_format_worktree_config;
 const char *git_commit_encoding;
 const char *git_log_output_encoding;
 char *apply_default_whitespace;
@@ -50,16 +58,13 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 96 * 1024 * 1024;
 unsigned long big_file_threshold = 512 * 1024 * 1024;
-int pager_use_color = 1;
 const char *editor_program;
 const char *askpass_program;
 const char *excludes_file;
 enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
-int read_replace_refs = 1;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
-char *check_roundtrip_encoding = "SHIFT-JIS";
-unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
+const char *check_roundtrip_encoding = "SHIFT-JIS";
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
@@ -68,7 +73,7 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #endif
 enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
 char *notes_ref_name;
-int grafts_replace_parents = 1;
+int grafts_keep_true_parents;
 int core_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
@@ -76,6 +81,20 @@ int merge_log_config = -1;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
 unsigned long pack_size_limit_cfg;
 enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
+int max_allowed_tree_depth =
+#ifdef _MSC_VER
+       /*
+        * When traversing into too-deep trees, Visual C-compiled Git seems to
+        * run into some internal stack overflow detection in the
+        * `RtlpAllocateHeap()` function that is called from within
+        * `git_inflate_init()`'s call tree. The following value seems to be
+        * low enough to avoid that by letting Git exit with an error before
+        * the stack overflow can occur.
+        */
+       512;
+#else
+       2048;
+#endif
 
 #ifndef PROTECT_HFS_DEFAULT
 #define PROTECT_HFS_DEFAULT 0
@@ -102,10 +121,8 @@ char *git_work_tree_cfg;
 
 static char *git_namespace;
 
-static char *super_prefix;
-
 /*
- * Repository-local GIT_* environment variables; see cache.h for details.
+ * Repository-local GIT_* environment variables; see environment.h for details.
  */
 const char * const local_repo_env[] = {
        ALTERNATE_DB_ENVIRONMENT,
@@ -121,7 +138,6 @@ const char * const local_repo_env[] = {
        NO_REPLACE_OBJECTS_ENVIRONMENT,
        GIT_REPLACE_REF_BASE_ENVIRONMENT,
        GIT_PREFIX_ENVIRONMENT,
-       GIT_SUPER_PREFIX_ENVIRONMENT,
        GIT_SHALLOW_FILE_ENVIRONMENT,
        GIT_COMMON_DIR_ENVIRONMENT,
        NULL
@@ -180,7 +196,7 @@ void setup_git_env(const char *git_dir)
        strvec_clear(&to_free);
 
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
-               read_replace_refs = 0;
+               disable_replace_refs();
        replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
        git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
                                                          : "refs/replace/");
@@ -234,16 +250,6 @@ const char *strip_namespace(const char *namespaced_ref)
        return NULL;
 }
 
-const char *get_super_prefix(void)
-{
-       static int initialized;
-       if (!initialized) {
-               super_prefix = xstrdup_or_null(getenv(GIT_SUPER_PREFIX_ENVIRONMENT));
-               initialized = 1;
-       }
-       return super_prefix;
-}
-
 static int git_work_tree_initialized;
 
 /*
index d438b5c8f3a62226a3a1cc32bb9ab9d9def25a4e..e5351c9dd95ea6e7afe77b1db466d6ab30310491 100644 (file)
@@ -1,7 +1,15 @@
 #ifndef ENVIRONMENT_H
 #define ENVIRONMENT_H
 
-#include "strvec.h"
+struct repository;
+struct strvec;
+
+/*
+ * The character that begins a commented line in user-editable file
+ * that is subject to stripspace.
+ */
+extern char comment_line_char;
+extern int auto_comment_line_char;
 
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
  */
 const char *getenv_safe(struct strvec *argv, const char *name);
 
+/* Double-check local_repo_env below if you add to this list. */
+#define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
+#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
+#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
+#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
+#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
+#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
+#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
+#define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
+#define GIT_SHALLOW_FILE_ENVIRONMENT "GIT_SHALLOW_FILE"
+#define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
+#define CONFIG_ENVIRONMENT "GIT_CONFIG"
+#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
+#define CONFIG_COUNT_ENVIRONMENT "GIT_CONFIG_COUNT"
+#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
+#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
+#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
+#define GITATTRIBUTES_FILE ".gitattributes"
+#define INFOATTRIBUTES_FILE "info/attributes"
+#define ATTRIBUTE_MACRO_PREFIX "[attr]"
+#define GITMODULES_FILE ".gitmodules"
+#define GITMODULES_INDEX ":.gitmodules"
+#define GITMODULES_HEAD "HEAD:.gitmodules"
+#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
+#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
+#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
+#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
+#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
+#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
+#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
+#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
+#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
+#define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
+#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
+#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
+#define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
+
+/*
+ * Environment variable used in handshaking the wire protocol.
+ * Contains a colon ':' separated list of keys with optional values
+ * 'key[=value]'.  Presence of unknown keys and values must be
+ * ignored.
+ */
+#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
+/* HTTP header used to handshake the wire protocol */
+#define GIT_PROTOCOL_HEADER "Git-Protocol"
+
+/*
+ * This environment variable is expected to contain a boolean indicating
+ * whether we should or should not treat:
+ *
+ *   GIT_DIR=foo.git git ...
+ *
+ * as if GIT_WORK_TREE=. was given. It's not expected that users will make use
+ * of this, but we use it internally to communicate to sub-processes that we
+ * are in a bare repo. If not set, defaults to true.
+ */
+#define GIT_IMPLICIT_WORK_TREE_ENVIRONMENT "GIT_IMPLICIT_WORK_TREE"
+
+/*
+ * Repository-local GIT_* environment variables; these will be cleared
+ * when git spawns a sub-process that runs inside another repository.
+ * The array is NULL-terminated, which makes it easy to pass in the "env"
+ * parameter of a run-command invocation, or to do a simple walk.
+ */
+extern const char * const local_repo_env[];
+
+void setup_git_env(const char *git_dir);
+
+/*
+ * Returns true iff we have a configured git repository (either via
+ * setup_git_directory, or in the environment via $GIT_DIR).
+ */
+int have_git_dir(void);
+
+extern int is_bare_repository_cfg;
+int is_bare_repository(void);
+extern char *git_work_tree_cfg;
+const char *get_git_dir(void);
+const char *get_git_common_dir(void);
+const char *get_object_directory(void);
+char *get_index_file(void);
+char *get_graft_file(struct repository *r);
+void set_git_dir(const char *path, int make_realpath);
+const char *get_git_namespace(void);
+const char *strip_namespace(const char *namespaced_ref);
+const char *get_git_work_tree(void);
+void set_git_work_tree(const char *tree);
+
+#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
+
+/* Environment bits from configuration mechanism */
+extern int trust_executable_bit;
+extern int trust_ctime;
+extern int check_stat;
+extern int has_symlinks;
+extern int minimum_abbrev, default_abbrev;
+extern int ignore_case;
+extern int assume_unchanged;
+extern int prefer_symlink_refs;
+extern int warn_ambiguous_refs;
+extern int warn_on_object_refname_ambiguity;
+extern char *apply_default_whitespace;
+extern char *apply_default_ignorewhitespace;
+extern const char *git_attributes_file;
+extern const char *git_hooks_path;
+extern int zlib_compression_level;
+extern int pack_compression_level;
+extern size_t packed_git_window_size;
+extern size_t packed_git_limit;
+extern size_t delta_base_cache_limit;
+extern unsigned long big_file_threshold;
+extern unsigned long pack_size_limit_cfg;
+extern int max_allowed_tree_depth;
+
+/*
+ * Accessors for the core.sharedrepository config which lazy-load the value
+ * from the config (if not already set). The "reset" function can be
+ * used to unset "set" or cached value, meaning that the value will be loaded
+ * fresh from the config file on the next call to get_shared_repository().
+ */
+void set_shared_repository(int value);
+int get_shared_repository(void);
+void reset_shared_repository(void);
+
+extern int core_preload_index;
+extern int precomposed_unicode;
+extern int protect_hfs;
+extern int protect_ntfs;
+
+extern int core_apply_sparse_checkout;
+extern int core_sparse_checkout_cone;
+extern int sparse_expect_files_outside_of_patterns;
+
+/*
+ * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
+ */
+int use_optional_locks(void);
+
+enum log_refs_config {
+       LOG_REFS_UNSET = -1,
+       LOG_REFS_NONE = 0,
+       LOG_REFS_NORMAL,
+       LOG_REFS_ALWAYS
+};
+extern enum log_refs_config log_all_ref_updates;
+
+enum rebase_setup_type {
+       AUTOREBASE_NEVER = 0,
+       AUTOREBASE_LOCAL,
+       AUTOREBASE_REMOTE,
+       AUTOREBASE_ALWAYS
+};
+
+enum push_default_type {
+       PUSH_DEFAULT_NOTHING = 0,
+       PUSH_DEFAULT_MATCHING,
+       PUSH_DEFAULT_SIMPLE,
+       PUSH_DEFAULT_UPSTREAM,
+       PUSH_DEFAULT_CURRENT,
+       PUSH_DEFAULT_UNSPECIFIED
+};
+
+extern enum rebase_setup_type autorebase;
+extern enum push_default_type push_default;
+
+enum object_creation_mode {
+       OBJECT_CREATION_USES_HARDLINKS = 0,
+       OBJECT_CREATION_USES_RENAMES = 1
+};
+
+extern enum object_creation_mode object_creation_mode;
+
+extern char *notes_ref_name;
+
+extern int grafts_keep_true_parents;
+
+extern int repository_format_precious_objects;
+
+/*
+ * Create a temporary file rooted in the object database directory, or
+ * die on failure. The filename is taken from "pattern", which should have the
+ * usual "XXXXXX" trailer, and the resulting filename is written into the
+ * "template" buffer. Returns the open descriptor.
+ */
+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
+
+/*
+ * Create a pack .keep file named "name" (which should generally be the output
+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
+ * error.
+ */
+int odb_pack_keep(const char *name);
+
+const char *get_log_output_encoding(void);
+const char *get_commit_output_encoding(void);
+
+extern const char *git_commit_encoding;
+extern const char *git_log_output_encoding;
+
+extern const char *editor_program;
+extern const char *askpass_program;
+extern const char *excludes_file;
+
+/*
+ * Should we print an ellipsis after an abbreviated SHA-1 value
+ * when doing diff-raw output or indicating a detached HEAD?
+ */
+int print_sha1_ellipsis(void);
+
 #endif
index ac618641632f8c347b2a2900fb15b0de9ed8dd8a..ac7e0af622a8fc74b678b57f464698d1bc9a45e7 100644 (file)
@@ -16,7 +16,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "ewok.h"
 
 #define EWAH_MASK(x) ((eword_t)1 << (x % BITS_IN_EWORD))
@@ -169,6 +169,15 @@ size_t bitmap_popcount(struct bitmap *self)
        return count;
 }
 
+int bitmap_is_empty(struct bitmap *self)
+{
+       size_t i;
+       for (i = 0; i < self->word_alloc; i++)
+               if (self->words[i])
+                       return 0;
+       return 1;
+}
+
 int bitmap_equals(struct bitmap *self, struct bitmap *other)
 {
        struct bitmap *big, *small;
index 6fe48d3ae0449a0298deb109b967ef3479ef0c47..8785cbc54a821cd2cb89ba0226a29a233d8c19ff 100644 (file)
@@ -19,7 +19,6 @@
 #include "git-compat-util.h"
 #include "ewok.h"
 #include "ewok_rlw.h"
-#include "cache.h"
 
 static inline size_t min_size(size_t a, size_t b)
 {
index 7eb8b9b63013daa70c91687108307d38a9eb1e09..c11d76c6f336930e0ea540a5b40c594bdb9f5be2 100644 (file)
@@ -189,5 +189,6 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);
 void bitmap_or(struct bitmap *self, const struct bitmap *other);
 
 size_t bitmap_popcount(struct bitmap *self);
+int bitmap_is_empty(struct bitmap *self);
 
 #endif
index eeb2ee52b83441fd51bd79cea8bf13682bcd522e..909777f61f4fa3d9d8c51c25db96e8d1f30544ae 100644 (file)
@@ -1,7 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
 #include "exec-cmd.h"
-#include "quote.h"
+#include "gettext.h"
+#include "path.h"
+#include "run-command.h"
 #include "strvec.h"
+#include "trace.h"
+#include "trace2.h"
 
 #if defined(RUNTIME_PREFIX)
 
@@ -252,7 +258,7 @@ static const char *system_prefix(void)
  * This is called during initialization, but No work needs to be done here when
  * runtime prefix is not being used.
  */
-void git_resolve_executable_dir(const char *argv0)
+void git_resolve_executable_dir(const char *argv0 UNUSED)
 {
 }
 
index 998fc2fa1ed4abd32ad0e723115f568768134d3e..5b8aa0adc77621504619b831bdd842b83f4867a1 100644 (file)
@@ -1,24 +1,28 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "repository.h"
 #include "config.h"
+#include "date.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "commit.h"
 #include "tag.h"
-#include "exec-cmd.h"
 #include "pack.h"
 #include "sideband.h"
 #include "fetch-pack.h"
 #include "remote.h"
 #include "run-command.h"
 #include "connect.h"
-#include "transport.h"
+#include "trace2.h"
 #include "version.h"
 #include "oid-array.h"
 #include "oidset.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "connected.h"
 #include "fetch-negotiator.h"
 #include "fsck.h"
@@ -722,7 +726,7 @@ static void filter_refs(struct fetch_pack_args *args,
        *refs = newlist;
 }
 
-static void mark_alternate_complete(struct fetch_negotiator *unused,
+static void mark_alternate_complete(struct fetch_negotiator *negotiator UNUSED,
                                    struct object *obj)
 {
        mark_complete(&obj->oid);
@@ -762,9 +766,9 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
                if (!commit) {
                        struct object *o;
 
-                       if (!has_object_file_with_flags(&ref->old_oid,
-                                               OBJECT_INFO_QUICK |
-                                               OBJECT_INFO_SKIP_FETCH_OBJECT))
+                       if (!repo_has_object_file_with_flags(the_repository, &ref->old_oid,
+                                                            OBJECT_INFO_QUICK |
+                                                            OBJECT_INFO_SKIP_FETCH_OBJECT))
                                continue;
                        o = parse_object(the_repository, &ref->old_oid);
                        if (!o || o->type != OBJ_COMMIT)
@@ -1094,7 +1098,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        struct ref *ref = copy_ref_list(orig_ref);
        struct object_id oid;
        const char *agent_feature;
-       int agent_len;
+       size_t agent_len;
        struct fetch_negotiator negotiator_alloc;
        struct fetch_negotiator *negotiator;
 
@@ -1112,7 +1116,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                agent_supported = 1;
                if (agent_len)
                        print_verbose(args, _("Server version is %.*s"),
-                                     agent_len, agent_feature);
+                                     (int)agent_len, agent_feature);
        }
 
        if (!server_supports("session-id"))
@@ -1317,15 +1321,15 @@ static void write_fetch_command_and_capabilities(struct strbuf *req_buf,
 {
        const char *hash_name;
 
-       if (server_supports_v2("fetch", 1))
-               packet_buf_write(req_buf, "command=fetch");
-       if (server_supports_v2("agent", 0))
+       ensure_server_supports_v2("fetch");
+       packet_buf_write(req_buf, "command=fetch");
+       if (server_supports_v2("agent"))
                packet_buf_write(req_buf, "agent=%s", git_user_agent_sanitized());
-       if (advertise_sid && server_supports_v2("session-id", 0))
+       if (advertise_sid && server_supports_v2("session-id"))
                packet_buf_write(req_buf, "session-id=%s", trace2_session_id());
-       if (server_options && server_options->nr &&
-           server_supports_v2("server-option", 1)) {
+       if (server_options && server_options->nr) {
                int i;
+               ensure_server_supports_v2("server-option");
                for (i = 0; i < server_options->nr; i++)
                        packet_buf_write(req_buf, "server-option=%s",
                                         server_options->items[i].string);
@@ -1853,8 +1857,11 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
        return ref;
 }
 
-static int fetch_pack_config_cb(const char *var, const char *value, void *cb)
+static int fetch_pack_config_cb(const char *var, const char *value,
+                               const struct config_context *ctx, void *cb)
 {
+       const char *msg_id;
+
        if (strcmp(var, "fetch.fsck.skiplist") == 0) {
                const char *path;
 
@@ -1866,16 +1873,18 @@ static int fetch_pack_config_cb(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (skip_prefix(var, "fetch.fsck.", &var)) {
-               if (is_valid_msg_type(var, value))
+       if (skip_prefix(var, "fetch.fsck.", &msg_id)) {
+               if (!value)
+                       return config_error_nonbool(var);
+               if (is_valid_msg_type(msg_id, value))
                        strbuf_addf(&fsck_msg_types, "%c%s=%s",
-                               fsck_msg_types.len ? ',' : '=', var, value);
+                               fsck_msg_types.len ? ',' : '=', msg_id, value);
                else
-                       warning("Skipping unknown msg id '%s'", var);
+                       warning("Skipping unknown msg id '%s'", msg_id);
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 static void fetch_pack_config(void)
@@ -1904,10 +1913,10 @@ static void fetch_pack_setup(void)
        if (did_setup)
                return;
        fetch_pack_config();
-       if (0 <= transfer_unpack_limit)
-               unpack_limit = transfer_unpack_limit;
-       else if (0 <= fetch_unpack_limit)
+       if (0 <= fetch_unpack_limit)
                unpack_limit = fetch_unpack_limit;
+       else if (0 <= transfer_unpack_limit)
+               unpack_limit = transfer_unpack_limit;
        did_setup = 1;
 }
 
@@ -1963,7 +1972,7 @@ static void update_shallow(struct fetch_pack_args *args,
                struct oid_array extra = OID_ARRAY_INIT;
                struct object_id *oid = si->shallow->oid;
                for (i = 0; i < si->shallow->nr; i++)
-                       if (has_object_file(&oid[i]))
+                       if (repo_has_object_file(the_repository, &oid[i]))
                                oid_array_append(&extra, &oid[i]);
                if (extra.nr) {
                        setup_alternate_shallow(&shallow_lock,
index 8c7752fc8212c7f911f9f462a2b306c48022bc2f..6775d265175748d2fccd2b9571c864a6ec091f20 100644 (file)
@@ -2,7 +2,6 @@
 #define FETCH_PACK_H
 
 #include "string-list.h"
-#include "run-command.h"
 #include "protocol.h"
 #include "list-objects-filter-options.h"
 #include "oidset.h"
index f48f44f9cd1dbdd3aeafbe5a4bd3598e783d0e25..66e47449a092d67b66a172a0e325593d5e494efa 100644 (file)
@@ -1,8 +1,12 @@
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "diff.h"
 #include "diff-merges.h"
+#include "hex.h"
 #include "revision.h"
 #include "tag.h"
 #include "string-list.h"
 #include "fmt-merge-msg.h"
 #include "commit-reach.h"
 #include "gpg-interface.h"
+#include "wildmatch.h"
 
 static int use_branch_desc;
 static int suppress_dest_pattern_seen;
 static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
 
-int fmt_merge_msg_config(const char *key, const char *value, void *cb)
+int fmt_merge_msg_config(const char *key, const char *value,
+                        const struct config_context *ctx, void *cb)
 {
-       int status = 0;
-
        if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
                int is_bool;
-               merge_log_config = git_config_bool_or_int(key, value, &is_bool);
+               merge_log_config = git_config_bool_or_int(key, value, ctx->kvi, &is_bool);
                if (!is_bool && merge_log_config < 0)
                        return error("%s: negative length %s", key, value);
                if (is_bool && merge_log_config)
@@ -37,10 +41,7 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
                        string_list_append(&suppress_dest_patterns, value);
                suppress_dest_pattern_seen = 1;
        } else {
-               status = git_gpg_config(key, value, NULL);
-               if (status)
-                       return status;
-               return git_default_config(key, value, cb);
+               return git_default_config(key, value, ctx, cb);
        }
        return 0;
 }
@@ -271,9 +272,10 @@ static void record_person_from_buf(int which, struct string_list *people,
 static void record_person(int which, struct string_list *people,
                          struct commit *commit)
 {
-       const char *buffer = get_commit_buffer(commit, NULL);
+       const char *buffer = repo_get_commit_buffer(the_repository, commit,
+                                                   NULL);
        record_person_from_buf(which, people, buffer);
-       unuse_commit_buffer(commit, buffer);
+       repo_unuse_commit_buffer(the_repository, commit, buffer);
 }
 
 static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
@@ -384,7 +386,8 @@ static void shortlog(const char *name,
                if (subjects.nr > limit)
                        continue;
 
-               format_commit_message(commit, "%s", &sb, &ctx);
+               repo_format_commit_message(the_repository, commit, "%s", &sb,
+                                          &ctx);
                strbuf_ltrim(&sb);
 
                if (!sb.len)
@@ -506,7 +509,8 @@ static void fmt_tag_signature(struct strbuf *tagbuf,
        strbuf_complete_line(tagbuf);
        if (sig->len) {
                strbuf_addch(tagbuf, '\n');
-               strbuf_add_commented_lines(tagbuf, sig->buf, sig->len);
+               strbuf_add_commented_lines(tagbuf, sig->buf, sig->len,
+                                          comment_line_char);
        }
 }
 
@@ -519,7 +523,8 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                struct object_id *oid = origins.items[i].util;
                enum object_type type;
                unsigned long size;
-               char *buf = read_object_file(oid, &type, &size);
+               char *buf = repo_read_object_file(the_repository, oid, &type,
+                                                 &size);
                char *origbuf = buf;
                unsigned long len = size;
                struct signature_check sigc = { NULL };
@@ -551,7 +556,8 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                                strbuf_addch(&tagline, '\n');
                                strbuf_add_commented_lines(&tagline,
                                                origins.items[first_tag].string,
-                                               strlen(origins.items[first_tag].string));
+                                               strlen(origins.items[first_tag].string),
+                                               comment_line_char);
                                strbuf_insert(&tagbuf, 0, tagline.buf,
                                              tagline.len);
                                strbuf_release(&tagline);
@@ -559,7 +565,8 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                        strbuf_addch(&tagbuf, '\n');
                        strbuf_add_commented_lines(&tagbuf,
                                        origins.items[i].string,
-                                       strlen(origins.items[i].string));
+                                       strlen(origins.items[i].string),
+                                       comment_line_char);
                        fmt_tag_signature(&tagbuf, &sig, buf, len);
                }
                strbuf_release(&payload);
@@ -605,7 +612,9 @@ static void find_merge_parents(struct merge_parents *result,
                 * util field yet.
                 */
                obj = parse_object(the_repository, &oid);
-               parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
+               parent = (struct commit *)repo_peel_to_type(the_repository,
+                                                           NULL, 0, obj,
+                                                           OBJ_COMMIT);
                if (!parent)
                        continue;
                commit_list_insert(parent, &parents);
index 99054042dc5e574b2ef1c00394331955239e0324..73ca3e44652204867457092954d6f9d9ff04ad95 100644 (file)
@@ -13,7 +13,8 @@ struct fmt_merge_msg_opts {
 };
 
 extern int merge_log_config;
-int fmt_merge_msg_config(const char *key, const char *value, void *cb);
+int fmt_merge_msg_config(const char *key, const char *value,
+                        const struct config_context *ctx, void *cb);
 int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                  struct fmt_merge_msg_opts *);
 
diff --git a/fsck.c b/fsck.c
index 47eaeedd7076ba60a621e072abb405127b3c33fe..1ad02fcdfabea74dc7f3d1c214400516d327a421 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -1,5 +1,9 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "date.h"
+#include "dir.h"
+#include "hex.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
 #include "object.h"
 #include "attr.h"
@@ -12,7 +16,6 @@
 #include "refs.h"
 #include "url.h"
 #include "utf8.h"
-#include "decorate.h"
 #include "oidset.h"
 #include "packfile.h"
 #include "submodule-config.h"
@@ -20,6 +23,8 @@
 #include "credential.h"
 #include "help.h"
 
+static ssize_t max_tree_entry_len = 4096;
+
 #define STR(x) #x
 #define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type },
 static struct {
@@ -150,15 +155,29 @@ void fsck_set_msg_type(struct fsck_options *options,
                       const char *msg_id_str, const char *msg_type_str)
 {
        int msg_id = parse_msg_id(msg_id_str);
-       enum fsck_msg_type msg_type = parse_msg_type(msg_type_str);
+       char *to_free = NULL;
+       enum fsck_msg_type msg_type;
 
        if (msg_id < 0)
                die("Unhandled message id: %s", msg_id_str);
 
+       if (msg_id == FSCK_MSG_LARGE_PATHNAME) {
+               const char *colon = strchr(msg_type_str, ':');
+               if (colon) {
+                       msg_type_str = to_free =
+                               xmemdupz(msg_type_str, colon - msg_type_str);
+                       colon++;
+                       if (!git_parse_ssize_t(colon, &max_tree_entry_len))
+                               die("unable to parse max tree entry len: %s", colon);
+               }
+       }
+       msg_type = parse_msg_type(msg_type_str);
+
        if (msg_type != FSCK_ERROR && msg_id_info[msg_id].msg_type == FSCK_FATAL)
                die("Cannot demote %s to %s", msg_id_str, msg_type_str);
 
        fsck_set_msg_type_from_ids(options, msg_id, msg_type);
+       free(to_free);
 }
 
 void fsck_set_msg_types(struct fsck_options *options, const char *values)
@@ -353,7 +372,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
        int result;
        const char *name;
 
-       if (parse_commit(commit))
+       if (repo_parse_commit(the_repository, commit))
                return -1;
 
        name = fsck_get_object_name(options, &commit->object.oid);
@@ -361,7 +380,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
                fsck_put_object_name(options, get_commit_tree_oid(commit),
                                     "%s:", name);
 
-       result = options->walk((struct object *)get_commit_tree(commit),
+       result = options->walk((struct object *) repo_get_commit_tree(the_repository, commit),
                               OBJ_TREE, data, options);
        if (result < 0)
                return result;
@@ -574,6 +593,7 @@ static int fsck_tree(const struct object_id *tree_oid,
        int has_bad_modes = 0;
        int has_dup_entries = 0;
        int not_properly_sorted = 0;
+       int has_large_name = 0;
        struct tree_desc desc;
        unsigned o_mode;
        const char *o_name;
@@ -603,6 +623,7 @@ static int fsck_tree(const struct object_id *tree_oid,
                has_dotdot |= !strcmp(name, "..");
                has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name);
                has_zero_pad |= *(char *)desc.buffer == '0';
+               has_large_name |= tree_entry_len(&desc.entry) > max_tree_entry_len;
 
                if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) {
                        if (!S_ISLNK(mode))
@@ -745,9 +766,30 @@ static int fsck_tree(const struct object_id *tree_oid,
                retval += report(options, tree_oid, OBJ_TREE,
                                 FSCK_MSG_TREE_NOT_SORTED,
                                 "not properly sorted");
+       if (has_large_name)
+               retval += report(options, tree_oid, OBJ_TREE,
+                                FSCK_MSG_LARGE_PATHNAME,
+                                "contains excessively large pathname");
        return retval;
 }
 
+/*
+ * Confirm that the headers of a commit or tag object end in a reasonable way,
+ * either with the usual "\n\n" separator, or at least with a trailing newline
+ * on the final header line.
+ *
+ * This property is important for the memory safety of our callers. It allows
+ * them to scan the buffer linewise without constantly checking the remaining
+ * size as long as:
+ *
+ *   - they check that there are bytes left in the buffer at the start of any
+ *     line (i.e., that the last newline they saw was not the final one we
+ *     found here)
+ *
+ *   - any intra-line scanning they do will stop at a newline, which will worst
+ *     case hit the newline we found here as the end-of-header. This makes it
+ *     OK for them to use helpers like parse_oid_hex(), or even skip_prefix().
+ */
 static int verify_headers(const void *data, unsigned long size,
                          const struct object_id *oid, enum object_type type,
                          struct fsck_options *options)
@@ -808,6 +850,20 @@ static int fsck_ident(const char **ident,
        if (*p != ' ')
                return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
        p++;
+       /*
+        * Our timestamp parser is based on the C strto*() functions, which
+        * will happily eat whitespace, including the newline that is supposed
+        * to prevent us walking past the end of the buffer. So do our own
+        * scan, skipping linear whitespace but not newlines, and then
+        * confirming we found a digit. We _could_ be even more strict here,
+        * as we really expect only a single space, but since we have
+        * traditionally allowed extra whitespace, we'll continue to do so.
+        */
+       while (*p == ' ' || *p == '\t')
+               p++;
+       if (!isdigit(*p))
+               return report(options, oid, type, FSCK_MSG_BAD_DATE,
+                             "invalid author/committer line - bad date");
        if (*p == '0' && p[1] != ' ')
                return report(options, oid, type, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
        if (date_overflows(parse_timestamp(p, &end, 10)))
@@ -834,12 +890,18 @@ static int fsck_commit(const struct object_id *oid,
        unsigned author_count;
        int err;
        const char *buffer_begin = buffer;
+       const char *buffer_end = buffer + size;
        const char *p;
 
+       /*
+        * We _must_ stop parsing immediately if this reports failure, as the
+        * memory safety of the rest of the function depends on it. See the
+        * comment above the definition of verify_headers() for more details.
+        */
        if (verify_headers(buffer, size, oid, OBJ_COMMIT, options))
                return -1;
 
-       if (!skip_prefix(buffer, "tree ", &buffer))
+       if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
                return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
        if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
                err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
@@ -847,7 +909,7 @@ static int fsck_commit(const struct object_id *oid,
                        return err;
        }
        buffer = p + 1;
-       while (skip_prefix(buffer, "parent ", &buffer)) {
+       while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
                if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
                        err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
                        if (err)
@@ -856,7 +918,7 @@ static int fsck_commit(const struct object_id *oid,
                buffer = p + 1;
        }
        author_count = 0;
-       while (skip_prefix(buffer, "author ", &buffer)) {
+       while (buffer < buffer_end && skip_prefix(buffer, "author ", &buffer)) {
                author_count++;
                err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
                if (err)
@@ -868,7 +930,7 @@ static int fsck_commit(const struct object_id *oid,
                err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
        if (err)
                return err;
-       if (!skip_prefix(buffer, "committer ", &buffer))
+       if (buffer >= buffer_end || !skip_prefix(buffer, "committer ", &buffer))
                return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
        err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
        if (err)
@@ -899,13 +961,19 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
        int ret = 0;
        char *eol;
        struct strbuf sb = STRBUF_INIT;
+       const char *buffer_end = buffer + size;
        const char *p;
 
+       /*
+        * We _must_ stop parsing immediately if this reports failure, as the
+        * memory safety of the rest of the function depends on it. See the
+        * comment above the definition of verify_headers() for more details.
+        */
        ret = verify_headers(buffer, size, oid, OBJ_TAG, options);
        if (ret)
                goto done;
 
-       if (!skip_prefix(buffer, "object ", &buffer)) {
+       if (buffer >= buffer_end || !skip_prefix(buffer, "object ", &buffer)) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
                goto done;
        }
@@ -916,11 +984,11 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
        }
        buffer = p + 1;
 
-       if (!skip_prefix(buffer, "type ", &buffer)) {
+       if (buffer >= buffer_end || !skip_prefix(buffer, "type ", &buffer)) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
                goto done;
        }
-       eol = strchr(buffer, '\n');
+       eol = memchr(buffer, '\n', buffer_end - buffer);
        if (!eol) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
                goto done;
@@ -932,11 +1000,11 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
                goto done;
        buffer = eol + 1;
 
-       if (!skip_prefix(buffer, "tag ", &buffer)) {
+       if (buffer >= buffer_end || !skip_prefix(buffer, "tag ", &buffer)) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
                goto done;
        }
-       eol = strchr(buffer, '\n');
+       eol = memchr(buffer, '\n', buffer_end - buffer);
        if (!eol) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
                goto done;
@@ -952,7 +1020,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
        }
        buffer = eol + 1;
 
-       if (!skip_prefix(buffer, "tagger ", &buffer)) {
+       if (buffer >= buffer_end || !skip_prefix(buffer, "tagger ", &buffer)) {
                /* early tags do not contain 'tagger' lines; warn only */
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
                if (ret)
@@ -960,10 +1028,8 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
        }
        else
                ret = fsck_ident(&buffer, oid, OBJ_TAG, options);
-       if (!*buffer)
-               goto done;
 
-       if (!starts_with(buffer, "\n")) {
+       if (buffer < buffer_end && !starts_with(buffer, "\n")) {
                /*
                 * The verify_headers() check will allow
                 * e.g. "[...]tagger <tagger>\nsome
@@ -1119,7 +1185,9 @@ struct fsck_gitmodules_data {
        int ret;
 };
 
-static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
+static int fsck_gitmodules_fn(const char *var, const char *value,
+                             const struct config_context *ctx UNUSED,
+                             void *vdata)
 {
        struct fsck_gitmodules_data *data = vdata;
        const char *subsection, *key;
@@ -1192,7 +1260,8 @@ static int fsck_blob(const struct object_id *oid, const char *buf,
                data.ret = 0;
                config_opts.error_action = CONFIG_ERROR_SILENT;
                if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
-                                       ".gitmodules", buf, size, &data, &config_opts))
+                                       ".gitmodules", buf, size, &data,
+                                       CONFIG_SCOPE_UNKNOWN, &config_opts))
                        data.ret |= report(options, oid, OBJ_BLOB,
                                        FSCK_MSG_GITMODULES_PARSE,
                                        "could not parse gitmodules blob");
@@ -1237,26 +1306,33 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
        if (!obj)
                return report(options, NULL, OBJ_NONE, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
 
-       if (obj->type == OBJ_BLOB)
-               return fsck_blob(&obj->oid, data, size, options);
-       if (obj->type == OBJ_TREE)
-               return fsck_tree(&obj->oid, data, size, options);
-       if (obj->type == OBJ_COMMIT)
-               return fsck_commit(&obj->oid, data, size, options);
-       if (obj->type == OBJ_TAG)
-               return fsck_tag(&obj->oid, data, size, options);
+       return fsck_buffer(&obj->oid, obj->type, data, size, options);
+}
+
+int fsck_buffer(const struct object_id *oid, enum object_type type,
+               void *data, unsigned long size,
+               struct fsck_options *options)
+{
+       if (type == OBJ_BLOB)
+               return fsck_blob(oid, data, size, options);
+       if (type == OBJ_TREE)
+               return fsck_tree(oid, data, size, options);
+       if (type == OBJ_COMMIT)
+               return fsck_commit(oid, data, size, options);
+       if (type == OBJ_TAG)
+               return fsck_tag(oid, data, size, options);
 
-       return report(options, &obj->oid, obj->type,
+       return report(options, oid, type,
                      FSCK_MSG_UNKNOWN_TYPE,
                      "unknown type '%d' (internal fsck error)",
-                     obj->type);
+                     type);
 }
 
 int fsck_error_function(struct fsck_options *o,
                        const struct object_id *oid,
-                       enum object_type object_type,
+                       enum object_type object_type UNUSED,
                        enum fsck_msg_type msg_type,
-                       enum fsck_msg_id msg_id,
+                       enum fsck_msg_id msg_id UNUSED,
                        const char *message)
 {
        if (msg_type == FSCK_WARN) {
@@ -1284,7 +1360,7 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,
                if (oidset_contains(blobs_done, oid))
                        continue;
 
-               buf = read_object_file(oid, &type, &size);
+               buf = repo_read_object_file(the_repository, oid, &type, &size);
                if (!buf) {
                        if (is_promisor_object(oid))
                                continue;
@@ -1322,9 +1398,12 @@ int fsck_finish(struct fsck_options *options)
        return ret;
 }
 
-int git_fsck_config(const char *var, const char *value, void *cb)
+int git_fsck_config(const char *var, const char *value,
+                   const struct config_context *ctx, void *cb)
 {
        struct fsck_options *options = cb;
+       const char *msg_id;
+
        if (strcmp(var, "fsck.skiplist") == 0) {
                const char *path;
                struct strbuf sb = STRBUF_INIT;
@@ -1338,12 +1417,14 @@ int git_fsck_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (skip_prefix(var, "fsck.", &var)) {
-               fsck_set_msg_type(options, var, value);
+       if (skip_prefix(var, "fsck.", &msg_id)) {
+               if (!value)
+                       return config_error_nonbool(var);
+               fsck_set_msg_type(options, msg_id, value);
                return 0;
        }
 
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
 
 /*
diff --git a/fsck.h b/fsck.h
index 121b8319852da6824a8d905c22911ca1c65436d5..e3adf9d91159878ae09b35a9fe12370fdbf85f98 100644 (file)
--- a/fsck.h
+++ b/fsck.h
@@ -1,6 +1,7 @@
 #ifndef GIT_FSCK_H
 #define GIT_FSCK_H
 
+#include "object.h"
 #include "oidset.h"
 
 enum fsck_msg_type {
@@ -13,6 +14,12 @@ enum fsck_msg_type {
        FSCK_WARN,
 };
 
+/*
+ * Documentation/fsck-msgids.txt documents these; when
+ * modifying this list in any way, make sure to keep the
+ * two in sync.
+ */
+
 #define FOREACH_FSCK_MSG_ID(FUNC) \
        /* fatal errors */ \
        FUNC(NUL_IN_HEADER, FATAL) \
@@ -24,7 +31,6 @@ enum fsck_msg_type {
        FUNC(BAD_NAME, ERROR) \
        FUNC(BAD_OBJECT_SHA1, ERROR) \
        FUNC(BAD_PARENT_SHA1, ERROR) \
-       FUNC(BAD_TAG_OBJECT, ERROR) \
        FUNC(BAD_TIMEZONE, ERROR) \
        FUNC(BAD_TREE, ERROR) \
        FUNC(BAD_TREE_SHA1, ERROR) \
@@ -40,7 +46,6 @@ enum fsck_msg_type {
        FUNC(MISSING_TAG, ERROR) \
        FUNC(MISSING_TAG_ENTRY, ERROR) \
        FUNC(MISSING_TREE, ERROR) \
-       FUNC(MISSING_TREE_OBJECT, ERROR) \
        FUNC(MISSING_TYPE, ERROR) \
        FUNC(MISSING_TYPE_ENTRY, ERROR) \
        FUNC(MULTIPLE_AUTHORS, ERROR) \
@@ -68,6 +73,7 @@ enum fsck_msg_type {
        FUNC(NULL_SHA1, WARN) \
        FUNC(ZERO_PADDED_FILEMODE, WARN) \
        FUNC(NUL_IN_COMMIT, WARN) \
+       FUNC(LARGE_PATHNAME, WARN) \
        /* infos (reported as warnings, but ignored by default) */ \
        FUNC(BAD_FILEMODE, INFO) \
        FUNC(GITMODULES_PARSE, INFO) \
@@ -179,6 +185,14 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options);
 int fsck_object(struct object *obj, void *data, unsigned long size,
        struct fsck_options *options);
 
+/*
+ * Same as fsck_object(), but for when the caller doesn't have an object
+ * struct.
+ */
+int fsck_buffer(const struct object_id *oid, enum object_type,
+               void *data, unsigned long size,
+               struct fsck_options *options);
+
 /*
  * fsck a tag, and pass info about it back to the caller. This is
  * exposed fsck_object() internals for git-mktag(1).
@@ -220,10 +234,12 @@ void fsck_put_object_name(struct fsck_options *options,
 const char *fsck_describe_object(struct fsck_options *options,
                                 const struct object_id *oid);
 
+struct key_value_info;
 /*
  * git_config() callback for use by fsck-y tools that want to support
  * fsck.<msg> fsck.skipList etc.
  */
-int git_fsck_config(const char *var, const char *value, void *cb);
+int git_fsck_config(const char *var, const char *value,
+                   const struct config_context *ctx, void *cb);
 
 #endif
index 2102a5c9ff5bb07345064386b8ef9b618ebf86d8..5cbbec8d940ba75a541dc0398a930f8f393562b6 100644 (file)
@@ -3,11 +3,9 @@
 
 #ifdef HAVE_FSMONITOR_DAEMON_BACKEND
 
-#include "cache.h"
-#include "dir.h"
-#include "run-command.h"
-#include "simple-ipc.h"
+#include "hashmap.h"
 #include "thread-utils.h"
+#include "fsmonitor-path-utils.h"
 
 struct fsmonitor_batch;
 struct fsmonitor_token_data;
@@ -43,6 +41,7 @@ struct fsmonitor_daemon_state {
 
        struct strbuf path_worktree_watch;
        struct strbuf path_gitdir_watch;
+       struct alias_info alias;
        int nr_paths_watching;
 
        struct fsmonitor_token_data *current_token_data;
@@ -59,6 +58,7 @@ struct fsmonitor_daemon_state {
 
        struct ipc_server_data *ipc_server_data;
        struct strbuf path_ipc;
+
 };
 
 /*
@@ -97,7 +97,7 @@ struct fsmonitor_daemon_state {
  * to only mean an external GITDIR referenced by a ".git" file.
  *
  * The platform FS event backends will receive watch-specific
- * relative paths (except for those OS's that always emit absolute
+ * relative paths (except for those OSes that always emit absolute
  * paths).  We use the following enum and routines to classify each
  * path so that we know how to handle it.  There is a slight asymmetry
  * here because ".git/" is inside the working directory and the
index 789e7397baa48d9172213b5319618d81ff6d60d1..45471b5b741a19515b5d0caea2dd01dd3e9fce25 100644 (file)
@@ -1,7 +1,8 @@
-#include "cache.h"
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "simple-ipc.h"
 #include "fsmonitor-ipc.h"
+#include "repository.h"
 #include "run-command.h"
 #include "strbuf.h"
 #include "trace2.h"
@@ -18,7 +19,7 @@ int fsmonitor_ipc__is_supported(void)
        return 0;
 }
 
-const char *fsmonitor_ipc__get_path(void)
+const char *fsmonitor_ipc__get_path(struct repository *r UNUSED)
 {
        return NULL;
 }
@@ -28,14 +29,14 @@ enum ipc_active_state fsmonitor_ipc__get_state(void)
        return IPC_STATE__OTHER_ERROR;
 }
 
-int fsmonitor_ipc__send_query(const char *since_token,
-                             struct strbuf *answer)
+int fsmonitor_ipc__send_query(const char *since_token UNUSED,
+                             struct strbuf *answer UNUSED)
 {
        return -1;
 }
 
-int fsmonitor_ipc__send_command(const char *command,
-                               struct strbuf *answer)
+int fsmonitor_ipc__send_command(const char *command UNUSED,
+                               struct strbuf *answer UNUSED)
 {
        return -1;
 }
@@ -47,19 +48,21 @@ int fsmonitor_ipc__is_supported(void)
        return 1;
 }
 
-GIT_PATH_FUNC(fsmonitor_ipc__get_path, "fsmonitor--daemon.ipc")
-
 enum ipc_active_state fsmonitor_ipc__get_state(void)
 {
-       return ipc_get_active_state(fsmonitor_ipc__get_path());
+       return ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));
 }
 
 static int spawn_daemon(void)
 {
-       const char *args[] = { "fsmonitor--daemon", "start", NULL };
+       struct child_process cmd = CHILD_PROCESS_INIT;
+
+       cmd.git_cmd = 1;
+       cmd.no_stdin = 1;
+       cmd.trace2_child_class = "fsmonitor";
+       strvec_pushl(&cmd.args, "fsmonitor--daemon", "start", NULL);
 
-       return run_command_v_opt_tr2(args, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,
-                                   "fsmonitor");
+       return run_command(&cmd);
 }
 
 int fsmonitor_ipc__send_query(const char *since_token,
@@ -81,8 +84,8 @@ int fsmonitor_ipc__send_query(const char *since_token,
        trace2_data_string("fsm_client", NULL, "query/command", tok);
 
 try_again:
-       state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
-                                      &connection);
+       state = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),
+                                               &options, &connection);
 
        switch (state) {
        case IPC_STATE__LISTENING:
@@ -117,13 +120,13 @@ try_again:
 
        case IPC_STATE__INVALID_PATH:
                ret = error(_("fsmonitor_ipc__send_query: invalid path '%s'"),
-                           fsmonitor_ipc__get_path());
+                           fsmonitor_ipc__get_path(the_repository));
                goto done;
 
        case IPC_STATE__OTHER_ERROR:
        default:
                ret = error(_("fsmonitor_ipc__send_query: unspecified error on '%s'"),
-                           fsmonitor_ipc__get_path());
+                           fsmonitor_ipc__get_path(the_repository));
                goto done;
        }
 
@@ -149,8 +152,8 @@ int fsmonitor_ipc__send_command(const char *command,
        options.wait_if_busy = 1;
        options.wait_if_not_found = 0;
 
-       state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
-                                      &connection);
+       state = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),
+                                               &options, &connection);
        if (state != IPC_STATE__LISTENING) {
                die(_("fsmonitor--daemon is not running"));
                return -1;
index b6a7067c3af511bda2e3c2f4d4a8af436b8f5ad2..8b489da762b04756b2373332eb72634889d96b7f 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "simple-ipc.h"
 
+struct repository;
+
 /*
  * Returns true if built-in file system monitor daemon is defined
  * for this platform.
@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);
  *
  * Returns NULL if the daemon is not supported on this platform.
  */
-const char *fsmonitor_ipc__get_path(void);
+const char *fsmonitor_ipc__get_path(struct repository *r);
 
 /*
  * Try to determine whether there is a `git-fsmonitor--daemon` process
diff --git a/fsmonitor-ll.h b/fsmonitor-ll.h
new file mode 100644 (file)
index 0000000..0504ca0
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef FSMONITOR_LL_H
+#define FSMONITOR_LL_H
+
+struct index_state;
+struct strbuf;
+
+extern struct trace_key trace_fsmonitor;
+
+/*
+ * Read the fsmonitor index extension and (if configured) restore the
+ * CE_FSMONITOR_VALID state.
+ */
+int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
+
+/*
+ * Fill the fsmonitor_dirty ewah bits with their state from the index,
+ * before it is split during writing.
+ */
+void fill_fsmonitor_bitmap(struct index_state *istate);
+
+/*
+ * Write the CE_FSMONITOR_VALID state into the fsmonitor index
+ * extension.  Reads from the fsmonitor_dirty ewah in the index.
+ */
+void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension
+ */
+void add_fsmonitor(struct index_state *istate);
+void remove_fsmonitor(struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension as necessary based on the current
+ * core.fsmonitor setting.
+ */
+void tweak_fsmonitor(struct index_state *istate);
+
+/*
+ * Run the configured fsmonitor integration script and clear the
+ * CE_FSMONITOR_VALID bit for any files returned as dirty.  Also invalidate
+ * any corresponding untracked cache directory structures. Optimized to only
+ * run the first time it is called.
+ */
+void refresh_fsmonitor(struct index_state *istate);
+
+/*
+ * Does the received result contain the "trivial" response?
+ */
+int fsmonitor_is_trivial_response(const struct strbuf *query_result);
+
+#endif /* FSMONITOR_LL_H */
diff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h
new file mode 100644 (file)
index 0000000..5bfdfb8
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef FSM_PATH_UTILS_H
+#define FSM_PATH_UTILS_H
+
+#include "strbuf.h"
+
+struct alias_info
+{
+       struct strbuf alias;
+       struct strbuf points_to;
+};
+
+struct fs_info {
+       int is_remote;
+       char *typename;
+};
+
+/*
+ * Get some basic filesystem information for the given path
+ *
+ * The caller owns the storage that is occupied by fs_info and
+ * is responsible for releasing it.
+ *
+ * Returns -1 on error, zero otherwise.
+ */
+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);
+
+/*
+ * Determines if the filesystem that path resides on is remote.
+ *
+ * Returns -1 on error, 0 if not remote, 1 if remote.
+ */
+int fsmonitor__is_fs_remote(const char *path);
+
+/*
+ * Get the alias in given path, if any.
+ *
+ * Sets alias to the first alias that matches any part of the path.
+ *
+ * If an alias is found, info.alias and info.points_to are set to the
+ * found mapping.
+ *
+ * Returns -1 on error, 0 otherwise.
+ *
+ * The caller owns the storage that is occupied by info.alias and
+ * info.points_to and is responsible for releasing it.
+ */
+int fsmonitor__get_alias(const char *path, struct alias_info *info);
+
+/*
+ * Resolve the path against the given alias.
+ *
+ * Returns the resolved path if there is one, NULL otherwise.
+ *
+ * The caller owns the storage that the returned string occupies and
+ * is responsible for releasing it.
+ */
+char *fsmonitor__resolve_alias(const char *path,
+       const struct alias_info *info);
+
+#endif
index 464424a1e924c63073a501fc0b9c030904074314..a6a9e6bc199ec2ea0b608212da2f5b4a6edd94ed 100644 (file)
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
 #include "repository.h"
+#include "fsmonitor-ipc.h"
 #include "fsmonitor-settings.h"
+#include "fsmonitor-path-utils.h"
 
 /*
  * We keep this structure defintion private and have getters
@@ -13,7 +16,54 @@ struct fsmonitor_settings {
        char *hook_path;
 };
 
-static enum fsmonitor_reason check_for_incompatible(struct repository *r)
+/*
+ * Remote working directories are problematic for FSMonitor.
+ *
+ * The underlying file system on the server machine and/or the remote
+ * mount type dictates whether notification events are available at
+ * all to remote client machines.
+ *
+ * Kernel differences between the server and client machines also
+ * dictate the how (buffering, frequency, de-dup) the events are
+ * delivered to client machine processes.
+ *
+ * A client machine (such as a laptop) may choose to suspend/resume
+ * and it is unclear (without lots of testing) whether the watcher can
+ * resync after a resume.  We might be able to treat this as a normal
+ * "events were dropped by the kernel" event and do our normal "flush
+ * and resync" --or-- we might need to close the existing (zombie?)
+ * notification fd and create a new one.
+ *
+ * In theory, the above issues need to be addressed whether we are
+ * using the Hook or IPC API.
+ *
+ * So (for now at least), mark remote working directories as
+ * incompatible unless 'fsmonitor.allowRemote' is true.
+ *
+ */
+#ifdef HAVE_FSMONITOR_OS_SETTINGS
+static enum fsmonitor_reason check_remote(struct repository *r)
+{
+       int allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */
+       int is_remote = fsmonitor__is_fs_remote(r->worktree);
+
+       switch (is_remote) {
+               case 0:
+                       return FSMONITOR_REASON_OK;
+               case 1:
+                       repo_config_get_bool(r, "fsmonitor.allowremote", &allow_remote);
+                       if (allow_remote < 1)
+                               return FSMONITOR_REASON_REMOTE;
+                       else
+                               return FSMONITOR_REASON_OK;
+               default:
+                       return FSMONITOR_REASON_ERROR;
+       }
+}
+#endif
+
+static enum fsmonitor_reason check_for_incompatible(struct repository *r,
+                                                   int ipc MAYBE_UNUSED)
 {
        if (!r->worktree) {
                /*
@@ -27,7 +77,10 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)
        {
                enum fsmonitor_reason reason;
 
-               reason = fsm_os__incompatible(r);
+               reason = check_remote(r);
+               if (reason != FSMONITOR_REASON_OK)
+                       return reason;
+               reason = fsm_os__incompatible(r, ipc);
                if (reason != FSMONITOR_REASON_OK)
                        return reason;
        }
@@ -92,8 +145,6 @@ static void lookup_fsmonitor_settings(struct repository *r)
 
 enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                lookup_fsmonitor_settings(r);
 
@@ -102,8 +153,6 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
 
 const char *fsm_settings__get_hook_path(struct repository *r)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                lookup_fsmonitor_settings(r);
 
@@ -112,7 +161,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)
 
 void fsm_settings__set_ipc(struct repository *r)
 {
-       enum fsmonitor_reason reason = check_for_incompatible(r);
+       enum fsmonitor_reason reason = check_for_incompatible(r, 1);
 
        if (reason != FSMONITOR_REASON_OK) {
                fsm_settings__set_incompatible(r, reason);
@@ -123,8 +172,6 @@ void fsm_settings__set_ipc(struct repository *r)
         * Caller requested IPC explicitly, so avoid (possibly
         * recursive) config lookup.
         */
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                r->settings.fsmonitor = alloc_settings();
 
@@ -135,7 +182,7 @@ void fsm_settings__set_ipc(struct repository *r)
 
 void fsm_settings__set_hook(struct repository *r, const char *path)
 {
-       enum fsmonitor_reason reason = check_for_incompatible(r);
+       enum fsmonitor_reason reason = check_for_incompatible(r, 0);
 
        if (reason != FSMONITOR_REASON_OK) {
                fsm_settings__set_incompatible(r, reason);
@@ -146,8 +193,6 @@ void fsm_settings__set_hook(struct repository *r, const char *path)
         * Caller requested hook explicitly, so avoid (possibly
         * recursive) config lookup.
         */
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                r->settings.fsmonitor = alloc_settings();
 
@@ -159,8 +204,6 @@ void fsm_settings__set_hook(struct repository *r, const char *path)
 
 void fsm_settings__set_disabled(struct repository *r)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                r->settings.fsmonitor = alloc_settings();
 
@@ -172,8 +215,6 @@ void fsm_settings__set_disabled(struct repository *r)
 void fsm_settings__set_incompatible(struct repository *r,
                                    enum fsmonitor_reason reason)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                r->settings.fsmonitor = alloc_settings();
 
@@ -184,18 +225,17 @@ void fsm_settings__set_incompatible(struct repository *r,
 
 enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                lookup_fsmonitor_settings(r);
 
        return r->settings.fsmonitor->reason;
 }
 
-char *fsm_settings__get_incompatible_msg(const struct repository *r,
+char *fsm_settings__get_incompatible_msg(struct repository *r,
                                         enum fsmonitor_reason reason)
 {
        struct strbuf msg = STRBUF_INIT;
+       const char *socket_dir;
 
        switch (reason) {
        case FSMONITOR_REASON_UNTESTED:
@@ -231,9 +271,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,
                goto done;
 
        case FSMONITOR_REASON_NOSOCKETS:
+               socket_dir = dirname((char *)fsmonitor_ipc__get_path(r));
                strbuf_addf(&msg,
-                           _("repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"),
-                           r->worktree);
+                           _("socket directory '%s' is incompatible with fsmonitor due"
+                             " to lack of Unix sockets support"),
+                           socket_dir);
                goto done;
        }
 
index d9c2605197ff8e957ffb0346dba78d85d3e1dab7..ab02e3995ee8f4eb3739359ea4b31f2a9bf697c9 100644 (file)
@@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);
 const char *fsm_settings__get_hook_path(struct repository *r);
 
 enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);
-char *fsm_settings__get_incompatible_msg(const struct repository *r,
+char *fsm_settings__get_incompatible_msg(struct repository *r,
                                         enum fsmonitor_reason reason);
 
 struct fsmonitor_settings;
@@ -48,7 +48,7 @@ struct fsmonitor_settings;
  * fsm_os__* routines should considered private to fsm_settings__
  * routines.
  */
-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);
+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);
 #endif /* HAVE_FSMONITOR_OS_SETTINGS */
 
 #endif /* FSMONITOR_SETTINGS_H */
index 57d6a483beede8e3b254634cdb944b19445875d1..f670c50937898342f693708c706a0db270be3a6d 100644 (file)
@@ -1,11 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "dir.h"
+#include "environment.h"
 #include "ewah/ewok.h"
 #include "fsmonitor.h"
 #include "fsmonitor-ipc.h"
 #include "run-command.h"
 #include "strbuf.h"
+#include "trace2.h"
 
 #define INDEX_EXTENSION_VERSION1       (1)
 #define INDEX_EXTENSION_VERSION2       (2)
@@ -295,6 +297,7 @@ static int fsmonitor_force_update_threshold = 100;
 
 void refresh_fsmonitor(struct index_state *istate)
 {
+       static int warn_once = 0;
        struct strbuf query_result = STRBUF_INIT;
        int query_success = 0, hook_version = -1;
        size_t bol = 0; /* beginning of line */
@@ -303,8 +306,16 @@ void refresh_fsmonitor(struct index_state *istate)
        char *buf;
        unsigned int i;
        int is_trivial = 0;
-       struct repository *r = istate->repo ? istate->repo : the_repository;
+       struct repository *r = istate->repo;
        enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
+       enum fsmonitor_reason reason = fsm_settings__get_reason(r);
+
+       if (!warn_once && reason > FSMONITOR_REASON_OK) {
+               char *msg = fsm_settings__get_incompatible_msg(r, reason);
+               warn_once = 1;
+               warning("%s", msg);
+               free(msg);
+       }
 
        if (fsm_mode <= FSMONITOR_MODE_DISABLED ||
            istate->fsmonitor_has_run_once)
index edf7ce5203b1a850e26c4b81cdb565350cd7f748..5195a8624db82b27f8fbb533675b35e841a2a44b 100644 (file)
@@ -1,54 +1,12 @@
 #ifndef FSMONITOR_H
 #define FSMONITOR_H
 
-#include "cache.h"
+#include "fsmonitor-ll.h"
 #include "dir.h"
 #include "fsmonitor-settings.h"
-
-extern struct trace_key trace_fsmonitor;
-
-/*
- * Read the fsmonitor index extension and (if configured) restore the
- * CE_FSMONITOR_VALID state.
- */
-int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
-
-/*
- * Fill the fsmonitor_dirty ewah bits with their state from the index,
- * before it is split during writing.
- */
-void fill_fsmonitor_bitmap(struct index_state *istate);
-
-/*
- * Write the CE_FSMONITOR_VALID state into the fsmonitor index
- * extension.  Reads from the fsmonitor_dirty ewah in the index.
- */
-void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
-
-/*
- * Add/remove the fsmonitor index extension
- */
-void add_fsmonitor(struct index_state *istate);
-void remove_fsmonitor(struct index_state *istate);
-
-/*
- * Add/remove the fsmonitor index extension as necessary based on the current
- * core.fsmonitor setting.
- */
-void tweak_fsmonitor(struct index_state *istate);
-
-/*
- * Run the configured fsmonitor integration script and clear the
- * CE_FSMONITOR_VALID bit for any files returned as dirty.  Also invalidate
- * any corresponding untracked cache directory structures. Optimized to only
- * run the first time it is called.
- */
-void refresh_fsmonitor(struct index_state *istate);
-
-/*
- * Does the received result contain the "trivial" response?
- */
-int fsmonitor_is_trivial_response(const struct strbuf *query_result);
+#include "object.h"
+#include "read-cache-ll.h"
+#include "trace.h"
 
 /*
  * Check if refresh_fsmonitor has been called at least once.
@@ -86,7 +44,7 @@ static inline void mark_fsmonitor_valid(struct index_state *istate, struct cache
            !(ce->ce_flags & CE_FSMONITOR_VALID)) {
                if (S_ISGITLINK(ce->ce_mode))
                        return;
-               istate->cache_changed = 1;
+               istate->cache_changed |= FSMONITOR_CHANGED;
                ce->ce_flags |= CE_FSMONITOR_VALID;
                trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
        }
index 7fff88c8da7e216700eb14cfd81a340f904eceb9..57facbc21ec2545e5bdeb1e738445ad403f419b4 100644 (file)
--- a/gettext.c
+++ b/gettext.c
@@ -2,15 +2,14 @@
  * Copyright (c) 2010 Ævar Arnfjörð Bjarmason
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
 #include "exec-cmd.h"
 #include "gettext.h"
-#include "strbuf.h"
 #include "utf8.h"
-#include "config.h"
 
 #ifndef NO_GETTEXT
-#      include <locale.h>
 #      include <libintl.h>
 #      ifdef GIT_WINDOWS_NATIVE
 
@@ -80,7 +79,6 @@ static int test_vsnprintf(const char *fmt, ...)
 
 static void init_gettext_charset(const char *domain)
 {
-       setlocale(LC_CTYPE, "");
        charset = locale_charset();
        bind_textdomain_codeset(domain, charset);
 
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
deleted file mode 100755 (executable)
index 95887fd..0000000
+++ /dev/null
@@ -1,1920 +0,0 @@
-#!/usr/bin/perl
-
-use 5.008;
-use strict;
-use warnings;
-use Git qw(unquote_path);
-use Git::I18N;
-
-binmode(STDOUT, ":raw");
-
-my $repo = Git->repository();
-
-my $menu_use_color = $repo->get_colorbool('color.interactive');
-my ($prompt_color, $header_color, $help_color) =
-       $menu_use_color ? (
-               $repo->get_color('color.interactive.prompt', 'bold blue'),
-               $repo->get_color('color.interactive.header', 'bold'),
-               $repo->get_color('color.interactive.help', 'red bold'),
-       ) : ();
-my $error_color = ();
-if ($menu_use_color) {
-       my $help_color_spec = ($repo->config('color.interactive.help') or
-                               'red bold');
-       $error_color = $repo->get_color('color.interactive.error',
-                                       $help_color_spec);
-}
-
-my $diff_use_color = $repo->get_colorbool('color.diff');
-my ($fraginfo_color) =
-       $diff_use_color ? (
-               $repo->get_color('color.diff.frag', 'cyan'),
-       ) : ();
-my ($diff_context_color) =
-       $diff_use_color ? (
-               $repo->get_color($repo->config('color.diff.context') ? 'color.diff.context' : 'color.diff.plain', ''),
-       ) : ();
-my ($diff_old_color) =
-       $diff_use_color ? (
-               $repo->get_color('color.diff.old', 'red'),
-       ) : ();
-my ($diff_new_color) =
-       $diff_use_color ? (
-               $repo->get_color('color.diff.new', 'green'),
-       ) : ();
-
-my $normal_color = $repo->get_color("", "reset");
-
-my $diff_algorithm = $repo->config('diff.algorithm');
-my $diff_filter = $repo->config('interactive.difffilter');
-
-my $use_readkey = 0;
-my $use_termcap = 0;
-my %term_escapes;
-
-sub ReadMode;
-sub ReadKey;
-if ($repo->config_bool("interactive.singlekey")) {
-       eval {
-               require Term::ReadKey;
-               Term::ReadKey->import;
-               $use_readkey = 1;
-       };
-       if (!$use_readkey) {
-               print STDERR "missing Term::ReadKey, disabling interactive.singlekey\n";
-       }
-       eval {
-               require Term::Cap;
-               my $termcap = Term::Cap->Tgetent;
-               foreach (values %$termcap) {
-                       $term_escapes{$_} = 1 if /^\e/;
-               }
-               $use_termcap = 1;
-       };
-}
-
-sub colored {
-       my $color = shift;
-       my $string = join("", @_);
-
-       if (defined $color) {
-               # Put a color code at the beginning of each line, a reset at the end
-               # color after newlines that are not at the end of the string
-               $string =~ s/(\n+)(.)/$1$color$2/g;
-               # reset before newlines
-               $string =~ s/(\n+)/$normal_color$1/g;
-               # codes at beginning and end (if necessary):
-               $string =~ s/^/$color/;
-               $string =~ s/$/$normal_color/ unless $string =~ /\n$/;
-       }
-       return $string;
-}
-
-# command line options
-my $patch_mode_only;
-my $patch_mode;
-my $patch_mode_revision;
-
-sub apply_patch;
-sub apply_patch_for_checkout_commit;
-sub apply_patch_for_stash;
-
-my %patch_modes = (
-       'stage' => {
-               DIFF => 'diff-files -p',
-               APPLY => sub { apply_patch 'apply --cached', @_; },
-               APPLY_CHECK => 'apply --cached',
-               FILTER => 'file-only',
-               IS_REVERSE => 0,
-       },
-       'stash' => {
-               DIFF => 'diff-index -p HEAD',
-               APPLY => sub { apply_patch 'apply --cached', @_; },
-               APPLY_CHECK => 'apply --cached',
-               FILTER => undef,
-               IS_REVERSE => 0,
-       },
-       'reset_head' => {
-               DIFF => 'diff-index -p --cached',
-               APPLY => sub { apply_patch 'apply -R --cached', @_; },
-               APPLY_CHECK => 'apply -R --cached',
-               FILTER => 'index-only',
-               IS_REVERSE => 1,
-       },
-       'reset_nothead' => {
-               DIFF => 'diff-index -R -p --cached',
-               APPLY => sub { apply_patch 'apply --cached', @_; },
-               APPLY_CHECK => 'apply --cached',
-               FILTER => 'index-only',
-               IS_REVERSE => 0,
-       },
-       'checkout_index' => {
-               DIFF => 'diff-files -p',
-               APPLY => sub { apply_patch 'apply -R', @_; },
-               APPLY_CHECK => 'apply -R',
-               FILTER => 'file-only',
-               IS_REVERSE => 1,
-       },
-       'checkout_head' => {
-               DIFF => 'diff-index -p',
-               APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
-               APPLY_CHECK => 'apply -R',
-               FILTER => undef,
-               IS_REVERSE => 1,
-       },
-       'checkout_nothead' => {
-               DIFF => 'diff-index -R -p',
-               APPLY => sub { apply_patch_for_checkout_commit '', @_ },
-               APPLY_CHECK => 'apply',
-               FILTER => undef,
-               IS_REVERSE => 0,
-       },
-       'worktree_head' => {
-               DIFF => 'diff-index -p',
-               APPLY => sub { apply_patch 'apply -R', @_ },
-               APPLY_CHECK => 'apply -R',
-               FILTER => undef,
-               IS_REVERSE => 1,
-       },
-       'worktree_nothead' => {
-               DIFF => 'diff-index -R -p',
-               APPLY => sub { apply_patch 'apply', @_ },
-               APPLY_CHECK => 'apply',
-               FILTER => undef,
-               IS_REVERSE => 0,
-       },
-);
-
-$patch_mode = 'stage';
-my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
-
-sub run_cmd_pipe {
-       if ($^O eq 'MSWin32') {
-               my @invalid = grep {m/[":*]/} @_;
-               die "$^O does not support: @invalid\n" if @invalid;
-               my @args = map { m/ /o ? "\"$_\"": $_ } @_;
-               return qx{@args};
-       } else {
-               my $fh = undef;
-               open($fh, '-|', @_) or die;
-               my @out = <$fh>;
-               close $fh || die "Cannot close @_ ($!)";
-               return @out;
-       }
-}
-
-my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));
-
-if (!defined $GIT_DIR) {
-       exit(1); # rev-parse would have already said "not a git repo"
-}
-chomp($GIT_DIR);
-
-sub refresh {
-       my $fh;
-       open $fh, 'git update-index --refresh |'
-           or die;
-       while (<$fh>) {
-               ;# ignore 'needs update'
-       }
-       close $fh;
-}
-
-sub list_untracked {
-       map {
-               chomp $_;
-               unquote_path($_);
-       }
-       run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
-}
-
-# TRANSLATORS: you can adjust this to align "git add -i" status menu
-my $status_fmt = __('%12s %12s %s');
-my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path'));
-
-{
-       my $initial;
-       sub is_initial_commit {
-               $initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
-                       unless defined $initial;
-               return $initial;
-       }
-}
-
-{
-       my $empty_tree;
-       sub get_empty_tree {
-               return $empty_tree if defined $empty_tree;
-
-               ($empty_tree) = run_cmd_pipe(qw(git hash-object -t tree /dev/null));
-               chomp $empty_tree;
-               return $empty_tree;
-       }
-}
-
-sub get_diff_reference {
-       my $ref = shift;
-       if (defined $ref and $ref ne 'HEAD') {
-               return $ref;
-       } elsif (is_initial_commit()) {
-               return get_empty_tree();
-       } else {
-               return 'HEAD';
-       }
-}
-
-# Returns list of hashes, contents of each of which are:
-# VALUE:       pathname
-# BINARY:      is a binary path
-# INDEX:       is index different from HEAD?
-# FILE:                is file different from index?
-# INDEX_ADDDEL:        is it add/delete between HEAD and index?
-# FILE_ADDDEL: is it add/delete between index and file?
-# UNMERGED:    is the path unmerged
-
-sub list_modified {
-       my ($only) = @_;
-       my (%data, @return);
-       my ($add, $del, $adddel, $file);
-
-       my $reference = get_diff_reference($patch_mode_revision);
-       for (run_cmd_pipe(qw(git diff-index --cached
-                            --numstat --summary), $reference,
-                            '--', @ARGV)) {
-               if (($add, $del, $file) =
-                   /^([-\d]+)  ([-\d]+)        (.*)/) {
-                       my ($change, $bin);
-                       $file = unquote_path($file);
-                       if ($add eq '-' && $del eq '-') {
-                               $change = __('binary');
-                               $bin = 1;
-                       }
-                       else {
-                               $change = "+$add/-$del";
-                       }
-                       $data{$file} = {
-                               INDEX => $change,
-                               BINARY => $bin,
-                               FILE => __('nothing'),
-                       }
-               }
-               elsif (($adddel, $file) =
-                      /^ (create|delete) mode [0-7]+ (.*)$/) {
-                       $file = unquote_path($file);
-                       $data{$file}{INDEX_ADDDEL} = $adddel;
-               }
-       }
-
-       for (run_cmd_pipe(qw(git diff-files --ignore-submodules=dirty --numstat --summary --raw --), @ARGV)) {
-               if (($add, $del, $file) =
-                   /^([-\d]+)  ([-\d]+)        (.*)/) {
-                       $file = unquote_path($file);
-                       my ($change, $bin);
-                       if ($add eq '-' && $del eq '-') {
-                               $change = __('binary');
-                               $bin = 1;
-                       }
-                       else {
-                               $change = "+$add/-$del";
-                       }
-                       $data{$file}{FILE} = $change;
-                       if ($bin) {
-                               $data{$file}{BINARY} = 1;
-                       }
-               }
-               elsif (($adddel, $file) =
-                      /^ (create|delete) mode [0-7]+ (.*)$/) {
-                       $file = unquote_path($file);
-                       $data{$file}{FILE_ADDDEL} = $adddel;
-               }
-               elsif (/^:[0-7]+ [0-7]+ [0-9a-f]+ [0-9a-f]+ (.) (.*)$/) {
-                       $file = unquote_path($2);
-                       if (!exists $data{$file}) {
-                               $data{$file} = +{
-                                       INDEX => __('unchanged'),
-                                       BINARY => 0,
-                               };
-                       }
-                       if ($1 eq 'U') {
-                               $data{$file}{UNMERGED} = 1;
-                       }
-               }
-       }
-
-       for (sort keys %data) {
-               my $it = $data{$_};
-
-               if ($only) {
-                       if ($only eq 'index-only') {
-                               next if ($it->{INDEX} eq __('unchanged'));
-                       }
-                       if ($only eq 'file-only') {
-                               next if ($it->{FILE} eq __('nothing'));
-                       }
-               }
-               push @return, +{
-                       VALUE => $_,
-                       %$it,
-               };
-       }
-       return @return;
-}
-
-sub find_unique {
-       my ($string, @stuff) = @_;
-       my $found = undef;
-       for (my $i = 0; $i < @stuff; $i++) {
-               my $it = $stuff[$i];
-               my $hit = undef;
-               if (ref $it) {
-                       if ((ref $it) eq 'ARRAY') {
-                               $it = $it->[0];
-                       }
-                       else {
-                               $it = $it->{VALUE};
-                       }
-               }
-               eval {
-                       if ($it =~ /^$string/) {
-                               $hit = 1;
-                       };
-               };
-               if (defined $hit && defined $found) {
-                       return undef;
-               }
-               if ($hit) {
-                       $found = $i + 1;
-               }
-       }
-       return $found;
-}
-
-# inserts string into trie and updates count for each character
-sub update_trie {
-       my ($trie, $string) = @_;
-       foreach (split //, $string) {
-               $trie = $trie->{$_} ||= {COUNT => 0};
-               $trie->{COUNT}++;
-       }
-}
-
-# returns an array of tuples (prefix, remainder)
-sub find_unique_prefixes {
-       my @stuff = @_;
-       my @return = ();
-
-       # any single prefix exceeding the soft limit is omitted
-       # if any prefix exceeds the hard limit all are omitted
-       # 0 indicates no limit
-       my $soft_limit = 0;
-       my $hard_limit = 3;
-
-       # build a trie modelling all possible options
-       my %trie;
-       foreach my $print (@stuff) {
-               if ((ref $print) eq 'ARRAY') {
-                       $print = $print->[0];
-               }
-               elsif ((ref $print) eq 'HASH') {
-                       $print = $print->{VALUE};
-               }
-               update_trie(\%trie, $print);
-               push @return, $print;
-       }
-
-       # use the trie to find the unique prefixes
-       for (my $i = 0; $i < @return; $i++) {
-               my $ret = $return[$i];
-               my @letters = split //, $ret;
-               my %search = %trie;
-               my ($prefix, $remainder);
-               my $j;
-               for ($j = 0; $j < @letters; $j++) {
-                       my $letter = $letters[$j];
-                       if ($search{$letter}{COUNT} == 1) {
-                               $prefix = substr $ret, 0, $j + 1;
-                               $remainder = substr $ret, $j + 1;
-                               last;
-                       }
-                       else {
-                               my $prefix = substr $ret, 0, $j;
-                               return ()
-                                   if ($hard_limit && $j + 1 > $hard_limit);
-                       }
-                       %search = %{$search{$letter}};
-               }
-               if (ord($letters[0]) > 127 ||
-                   ($soft_limit && $j + 1 > $soft_limit)) {
-                       $prefix = undef;
-                       $remainder = $ret;
-               }
-               $return[$i] = [$prefix, $remainder];
-       }
-       return @return;
-}
-
-# filters out prefixes which have special meaning to list_and_choose()
-sub is_valid_prefix {
-       my $prefix = shift;
-       return (defined $prefix) &&
-           !($prefix =~ /[\s,]/) && # separators
-           !($prefix =~ /^-/) &&    # deselection
-           !($prefix =~ /^\d+/) &&  # selection
-           ($prefix ne '*') &&      # "all" wildcard
-           ($prefix ne '?');        # prompt help
-}
-
-# given a prefix/remainder tuple return a string with the prefix highlighted
-# for now use square brackets; later might use ANSI colors (underline, bold)
-sub highlight_prefix {
-       my $prefix = shift;
-       my $remainder = shift;
-
-       if (!defined $prefix) {
-               return $remainder;
-       }
-
-       if (!is_valid_prefix($prefix)) {
-               return "$prefix$remainder";
-       }
-
-       if (!$menu_use_color) {
-               return "[$prefix]$remainder";
-       }
-
-       return "$prompt_color$prefix$normal_color$remainder";
-}
-
-sub error_msg {
-       print STDERR colored $error_color, @_;
-}
-
-sub list_and_choose {
-       my ($opts, @stuff) = @_;
-       my (@chosen, @return);
-       if (!@stuff) {
-           return @return;
-       }
-       my $i;
-       my @prefixes = find_unique_prefixes(@stuff) unless $opts->{LIST_ONLY};
-
-      TOPLOOP:
-       while (1) {
-               my $last_lf = 0;
-
-               if ($opts->{HEADER}) {
-                       my $indent = $opts->{LIST_FLAT} ? "" : "     ";
-                       print colored $header_color, "$indent$opts->{HEADER}\n";
-               }
-               for ($i = 0; $i < @stuff; $i++) {
-                       my $chosen = $chosen[$i] ? '*' : ' ';
-                       my $print = $stuff[$i];
-                       my $ref = ref $print;
-                       my $highlighted = highlight_prefix(@{$prefixes[$i]})
-                           if @prefixes;
-                       if ($ref eq 'ARRAY') {
-                               $print = $highlighted || $print->[0];
-                       }
-                       elsif ($ref eq 'HASH') {
-                               my $value = $highlighted || $print->{VALUE};
-                               $print = sprintf($status_fmt,
-                                   $print->{INDEX},
-                                   $print->{FILE},
-                                   $value);
-                       }
-                       else {
-                               $print = $highlighted || $print;
-                       }
-                       printf("%s%2d: %s", $chosen, $i+1, $print);
-                       if (($opts->{LIST_FLAT}) &&
-                           (($i + 1) % ($opts->{LIST_FLAT}))) {
-                               print "\t";
-                               $last_lf = 0;
-                       }
-                       else {
-                               print "\n";
-                               $last_lf = 1;
-                       }
-               }
-               if (!$last_lf) {
-                       print "\n";
-               }
-
-               return if ($opts->{LIST_ONLY});
-
-               print colored $prompt_color, $opts->{PROMPT};
-               if ($opts->{SINGLETON}) {
-                       print "> ";
-               }
-               else {
-                       print ">> ";
-               }
-               my $line = <STDIN>;
-               if (!$line) {
-                       print "\n";
-                       $opts->{ON_EOF}->() if $opts->{ON_EOF};
-                       last;
-               }
-               chomp $line;
-               last if $line eq '';
-               if ($line eq '?') {
-                       $opts->{SINGLETON} ?
-                           singleton_prompt_help_cmd() :
-                           prompt_help_cmd();
-                       next TOPLOOP;
-               }
-               for my $choice (split(/[\s,]+/, $line)) {
-                       my $choose = 1;
-                       my ($bottom, $top);
-
-                       # Input that begins with '-'; unchoose
-                       if ($choice =~ s/^-//) {
-                               $choose = 0;
-                       }
-                       # A range can be specified like 5-7 or 5-.
-                       if ($choice =~ /^(\d+)-(\d*)$/) {
-                               ($bottom, $top) = ($1, length($2) ? $2 : 1 + @stuff);
-                       }
-                       elsif ($choice =~ /^\d+$/) {
-                               $bottom = $top = $choice;
-                       }
-                       elsif ($choice eq '*') {
-                               $bottom = 1;
-                               $top = 1 + @stuff;
-                       }
-                       else {
-                               $bottom = $top = find_unique($choice, @stuff);
-                               if (!defined $bottom) {
-                                       error_msg sprintf(__("Huh (%s)?\n"), $choice);
-                                       next TOPLOOP;
-                               }
-                       }
-                       if ($opts->{SINGLETON} && $bottom != $top) {
-                               error_msg sprintf(__("Huh (%s)?\n"), $choice);
-                               next TOPLOOP;
-                       }
-                       for ($i = $bottom-1; $i <= $top-1; $i++) {
-                               next if (@stuff <= $i || $i < 0);
-                               $chosen[$i] = $choose;
-                       }
-               }
-               last if ($opts->{IMMEDIATE} || $line eq '*');
-       }
-       for ($i = 0; $i < @stuff; $i++) {
-               if ($chosen[$i]) {
-                       push @return, $stuff[$i];
-               }
-       }
-       return @return;
-}
-
-sub singleton_prompt_help_cmd {
-       print colored $help_color, __ <<'EOF' ;
-Prompt help:
-1          - select a numbered item
-foo        - select item based on unique prefix
-           - (empty) select nothing
-EOF
-}
-
-sub prompt_help_cmd {
-       print colored $help_color, __ <<'EOF' ;
-Prompt help:
-1          - select a single item
-3-5        - select a range of items
-2-3,6-9    - select multiple ranges
-foo        - select item based on unique prefix
--...       - unselect specified items
-*          - choose all items
-           - (empty) finish selecting
-EOF
-}
-
-sub status_cmd {
-       list_and_choose({ LIST_ONLY => 1, HEADER => $status_head },
-                       list_modified());
-       print "\n";
-}
-
-sub say_n_paths {
-       my $did = shift @_;
-       my $cnt = scalar @_;
-       if ($did eq 'added') {
-               printf(__n("added %d path\n", "added %d paths\n",
-                          $cnt), $cnt);
-       } elsif ($did eq 'updated') {
-               printf(__n("updated %d path\n", "updated %d paths\n",
-                          $cnt), $cnt);
-       } elsif ($did eq 'reverted') {
-               printf(__n("reverted %d path\n", "reverted %d paths\n",
-                          $cnt), $cnt);
-       } else {
-               printf(__n("touched %d path\n", "touched %d paths\n",
-                          $cnt), $cnt);
-       }
-}
-
-sub update_cmd {
-       my @mods = list_modified('file-only');
-       return if (!@mods);
-
-       my @update = list_and_choose({ PROMPT => __('Update'),
-                                      HEADER => $status_head, },
-                                    @mods);
-       if (@update) {
-               system(qw(git update-index --add --remove --),
-                      map { $_->{VALUE} } @update);
-               say_n_paths('updated', @update);
-       }
-       print "\n";
-}
-
-sub revert_cmd {
-       my @update = list_and_choose({ PROMPT => __('Revert'),
-                                      HEADER => $status_head, },
-                                    list_modified());
-       if (@update) {
-               if (is_initial_commit()) {
-                       system(qw(git rm --cached),
-                               map { $_->{VALUE} } @update);
-               }
-               else {
-                       my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
-                                                map { $_->{VALUE} } @update);
-                       my $fh;
-                       open $fh, '| git update-index --index-info'
-                           or die;
-                       for (@lines) {
-                               print $fh $_;
-                       }
-                       close($fh);
-                       for (@update) {
-                               if ($_->{INDEX_ADDDEL} &&
-                                   $_->{INDEX_ADDDEL} eq 'create') {
-                                       system(qw(git update-index --force-remove --),
-                                              $_->{VALUE});
-                                       printf(__("note: %s is untracked now.\n"), $_->{VALUE});
-                               }
-                       }
-               }
-               refresh();
-               say_n_paths('reverted', @update);
-       }
-       print "\n";
-}
-
-sub add_untracked_cmd {
-       my @add = list_and_choose({ PROMPT => __('Add untracked') },
-                                 list_untracked());
-       if (@add) {
-               system(qw(git update-index --add --), @add);
-               say_n_paths('added', @add);
-       } else {
-               print __("No untracked files.\n");
-       }
-       print "\n";
-}
-
-sub run_git_apply {
-       my $cmd = shift;
-       my $fh;
-       open $fh, '| git ' . $cmd . " --allow-overlap";
-       print $fh @_;
-       return close $fh;
-}
-
-sub parse_diff {
-       my ($path) = @_;
-       my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
-       if (defined $diff_algorithm) {
-               splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
-       }
-       if (defined $patch_mode_revision) {
-               push @diff_cmd, get_diff_reference($patch_mode_revision);
-       }
-       my @diff = run_cmd_pipe("git", @diff_cmd, qw(--no-color --), $path);
-       my @colored = ();
-       if ($diff_use_color) {
-               my @display_cmd = ("git", @diff_cmd, qw(--color --), $path);
-               if (defined $diff_filter) {
-                       # quotemeta is overkill, but sufficient for shell-quoting
-                       my $diff = join(' ', map { quotemeta } @display_cmd);
-                       @display_cmd = ("$diff | $diff_filter");
-               }
-
-               @colored = run_cmd_pipe(@display_cmd);
-       }
-       my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
-
-       if (@colored && @colored != @diff) {
-               print STDERR
-                 "fatal: mismatched output from interactive.diffFilter\n",
-                 "hint: Your filter must maintain a one-to-one correspondence\n",
-                 "hint: between its input and output lines.\n";
-               exit 1;
-       }
-
-       for (my $i = 0; $i < @diff; $i++) {
-               if ($diff[$i] =~ /^@@ /) {
-                       push @hunk, { TEXT => [], DISPLAY => [],
-                               TYPE => 'hunk' };
-               }
-               push @{$hunk[-1]{TEXT}}, $diff[$i];
-               push @{$hunk[-1]{DISPLAY}},
-                       (@colored ? $colored[$i] : $diff[$i]);
-       }
-       return @hunk;
-}
-
-sub parse_diff_header {
-       my $src = shift;
-
-       my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
-       my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
-       my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };
-       my $addition;
-
-       for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
-               if ($src->{TEXT}->[$i] =~ /^new file/) {
-                       $addition = 1;
-                       $head->{TYPE} = 'addition';
-               }
-               my $dest =
-                  $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
-                  $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
-                  $head;
-               push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
-               push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
-       }
-       return ($head, $mode, $deletion, $addition);
-}
-
-sub hunk_splittable {
-       my ($text) = @_;
-
-       my @s = split_hunk($text);
-       return (1 < @s);
-}
-
-sub parse_hunk_header {
-       my ($line) = @_;
-       my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
-           $line =~ /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
-       $o_cnt = 1 unless defined $o_cnt;
-       $n_cnt = 1 unless defined $n_cnt;
-       return ($o_ofs, $o_cnt, $n_ofs, $n_cnt);
-}
-
-sub format_hunk_header {
-       my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = @_;
-       return ("@@ -$o_ofs" .
-               (($o_cnt != 1) ? ",$o_cnt" : '') .
-               " +$n_ofs" .
-               (($n_cnt != 1) ? ",$n_cnt" : '') .
-               " @@\n");
-}
-
-sub split_hunk {
-       my ($text, $display) = @_;
-       my @split = ();
-       if (!defined $display) {
-               $display = $text;
-       }
-       # If there are context lines in the middle of a hunk,
-       # it can be split, but we would need to take care of
-       # overlaps later.
-
-       my ($o_ofs, undef, $n_ofs) = parse_hunk_header($text->[0]);
-       my $hunk_start = 1;
-
-      OUTER:
-       while (1) {
-               my $next_hunk_start = undef;
-               my $i = $hunk_start - 1;
-               my $this = +{
-                       TEXT => [],
-                       DISPLAY => [],
-                       TYPE => 'hunk',
-                       OLD => $o_ofs,
-                       NEW => $n_ofs,
-                       OCNT => 0,
-                       NCNT => 0,
-                       ADDDEL => 0,
-                       POSTCTX => 0,
-                       USE => undef,
-               };
-
-               while (++$i < @$text) {
-                       my $line = $text->[$i];
-                       my $display = $display->[$i];
-                       if ($line =~ /^\\/) {
-                               push @{$this->{TEXT}}, $line;
-                               push @{$this->{DISPLAY}}, $display;
-                               next;
-                       }
-                       if ($line =~ /^ /) {
-                               if ($this->{ADDDEL} &&
-                                   !defined $next_hunk_start) {
-                                       # We have seen leading context and
-                                       # adds/dels and then here is another
-                                       # context, which is trailing for this
-                                       # split hunk and leading for the next
-                                       # one.
-                                       $next_hunk_start = $i;
-                               }
-                               push @{$this->{TEXT}}, $line;
-                               push @{$this->{DISPLAY}}, $display;
-                               $this->{OCNT}++;
-                               $this->{NCNT}++;
-                               if (defined $next_hunk_start) {
-                                       $this->{POSTCTX}++;
-                               }
-                               next;
-                       }
-
-                       # add/del
-                       if (defined $next_hunk_start) {
-                               # We are done with the current hunk and
-                               # this is the first real change for the
-                               # next split one.
-                               $hunk_start = $next_hunk_start;
-                               $o_ofs = $this->{OLD} + $this->{OCNT};
-                               $n_ofs = $this->{NEW} + $this->{NCNT};
-                               $o_ofs -= $this->{POSTCTX};
-                               $n_ofs -= $this->{POSTCTX};
-                               push @split, $this;
-                               redo OUTER;
-                       }
-                       push @{$this->{TEXT}}, $line;
-                       push @{$this->{DISPLAY}}, $display;
-                       $this->{ADDDEL}++;
-                       if ($line =~ /^-/) {
-                               $this->{OCNT}++;
-                       }
-                       else {
-                               $this->{NCNT}++;
-                       }
-               }
-
-               push @split, $this;
-               last;
-       }
-
-       for my $hunk (@split) {
-               $o_ofs = $hunk->{OLD};
-               $n_ofs = $hunk->{NEW};
-               my $o_cnt = $hunk->{OCNT};
-               my $n_cnt = $hunk->{NCNT};
-
-               my $head = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
-               my $display_head = $head;
-               unshift @{$hunk->{TEXT}}, $head;
-               if ($diff_use_color) {
-                       $display_head = colored($fraginfo_color, $head);
-               }
-               unshift @{$hunk->{DISPLAY}}, $display_head;
-       }
-       return @split;
-}
-
-sub find_last_o_ctx {
-       my ($it) = @_;
-       my $text = $it->{TEXT};
-       my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]);
-       my $i = @{$text};
-       my $last_o_ctx = $o_ofs + $o_cnt;
-       while (0 < --$i) {
-               my $line = $text->[$i];
-               if ($line =~ /^ /) {
-                       $last_o_ctx--;
-                       next;
-               }
-               last;
-       }
-       return $last_o_ctx;
-}
-
-sub merge_hunk {
-       my ($prev, $this) = @_;
-       my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =
-           parse_hunk_header($prev->{TEXT}[0]);
-       my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =
-           parse_hunk_header($this->{TEXT}[0]);
-
-       my (@line, $i, $ofs, $o_cnt, $n_cnt);
-       $ofs = $o0_ofs;
-       $o_cnt = $n_cnt = 0;
-       for ($i = 1; $i < @{$prev->{TEXT}}; $i++) {
-               my $line = $prev->{TEXT}[$i];
-               if ($line =~ /^\+/) {
-                       $n_cnt++;
-                       push @line, $line;
-                       next;
-               } elsif ($line =~ /^\\/) {
-                       push @line, $line;
-                       next;
-               }
-
-               last if ($o1_ofs <= $ofs);
-
-               $o_cnt++;
-               $ofs++;
-               if ($line =~ /^ /) {
-                       $n_cnt++;
-               }
-               push @line, $line;
-       }
-
-       for ($i = 1; $i < @{$this->{TEXT}}; $i++) {
-               my $line = $this->{TEXT}[$i];
-               if ($line =~ /^\+/) {
-                       $n_cnt++;
-                       push @line, $line;
-                       next;
-               } elsif ($line =~ /^\\/) {
-                       push @line, $line;
-                       next;
-               }
-               $ofs++;
-               $o_cnt++;
-               if ($line =~ /^ /) {
-                       $n_cnt++;
-               }
-               push @line, $line;
-       }
-       my $head = format_hunk_header($o0_ofs, $o_cnt, $n0_ofs, $n_cnt);
-       @{$prev->{TEXT}} = ($head, @line);
-}
-
-sub coalesce_overlapping_hunks {
-       my (@in) = @_;
-       my @out = ();
-
-       my ($last_o_ctx, $last_was_dirty);
-       my $ofs_delta = 0;
-
-       for (@in) {
-               if ($_->{TYPE} ne 'hunk') {
-                       push @out, $_;
-                       next;
-               }
-               my $text = $_->{TEXT};
-               my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
-                                               parse_hunk_header($text->[0]);
-               unless ($_->{USE}) {
-                       $ofs_delta += $o_cnt - $n_cnt;
-                       # If this hunk has been edited then subtract
-                       # the delta that is due to the edit.
-                       if ($_->{OFS_DELTA}) {
-                               $ofs_delta -= $_->{OFS_DELTA};
-                       }
-                       next;
-               }
-               if ($ofs_delta) {
-                       if ($patch_mode_flavour{IS_REVERSE}) {
-                               $o_ofs -= $ofs_delta;
-                       } else {
-                               $n_ofs += $ofs_delta;
-                       }
-                       $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
-                                                            $n_ofs, $n_cnt);
-               }
-               # If this hunk was edited then adjust the offset delta
-               # to reflect the edit.
-               if ($_->{OFS_DELTA}) {
-                       $ofs_delta += $_->{OFS_DELTA};
-               }
-               if (defined $last_o_ctx &&
-                   $o_ofs <= $last_o_ctx &&
-                   !$_->{DIRTY} &&
-                   !$last_was_dirty) {
-                       merge_hunk($out[-1], $_);
-               }
-               else {
-                       push @out, $_;
-               }
-               $last_o_ctx = find_last_o_ctx($out[-1]);
-               $last_was_dirty = $_->{DIRTY};
-       }
-       return @out;
-}
-
-sub reassemble_patch {
-       my $head = shift;
-       my @patch;
-
-       # Include everything in the header except the beginning of the diff.
-       push @patch, (grep { !/^[-+]{3}/ } @$head);
-
-       # Then include any headers from the hunk lines, which must
-       # come before any actual hunk.
-       while (@_ && $_[0] !~ /^@/) {
-               push @patch, shift;
-       }
-
-       # Then begin the diff.
-       push @patch, grep { /^[-+]{3}/ } @$head;
-
-       # And then the actual hunks.
-       push @patch, @_;
-
-       return @patch;
-}
-
-sub color_diff {
-       return map {
-               colored((/^@/  ? $fraginfo_color :
-                        /^\+/ ? $diff_new_color :
-                        /^-/  ? $diff_old_color :
-                        $diff_context_color),
-                       $_);
-       } @_;
-}
-
-my %edit_hunk_manually_modes = (
-       stage => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for staging."),
-       stash => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for stashing."),
-       reset_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for unstaging."),
-       reset_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
-       checkout_index => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
-       checkout_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
-       checkout_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
-       worktree_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
-       worktree_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
-);
-
-sub recount_edited_hunk {
-       local $_;
-       my ($oldtext, $newtext) = @_;
-       my ($o_cnt, $n_cnt) = (0, 0);
-       for (@{$newtext}[1..$#{$newtext}]) {
-               my $mode = substr($_, 0, 1);
-               if ($mode eq '-') {
-                       $o_cnt++;
-               } elsif ($mode eq '+') {
-                       $n_cnt++;
-               } elsif ($mode eq ' ' or $mode eq "\n") {
-                       $o_cnt++;
-                       $n_cnt++;
-               }
-       }
-       my ($o_ofs, undef, $n_ofs, undef) =
-                                       parse_hunk_header($newtext->[0]);
-       $newtext->[0] = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
-       my (undef, $orig_o_cnt, undef, $orig_n_cnt) =
-                                       parse_hunk_header($oldtext->[0]);
-       # Return the change in the number of lines inserted by this hunk
-       return $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;
-}
-
-sub edit_hunk_manually {
-       my ($oldtext) = @_;
-
-       my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
-       my $fh;
-       open $fh, '>', $hunkfile
-               or die sprintf(__("failed to open hunk edit file for writing: %s"), $!);
-       print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom for a quick guide.\n");
-       print $fh @$oldtext;
-       my $is_reverse = $patch_mode_flavour{IS_REVERSE};
-       my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');
-       my $comment_line_char = Git::get_comment_line_char;
-       print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, $remove_plus, $comment_line_char),
----
-To remove '%s' lines, make them ' ' lines (context).
-To remove '%s' lines, delete them.
-Lines starting with %s will be removed.
-EOF
-__($edit_hunk_manually_modes{$patch_mode}),
-# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-__ <<EOF2 ;
-If it does not apply cleanly, you will be given an opportunity to
-edit again.  If all lines of the hunk are removed, then the edit is
-aborted and the hunk is left unchanged.
-EOF2
-       close $fh;
-
-       chomp(my ($editor) = run_cmd_pipe(qw(git var GIT_EDITOR)));
-       system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
-
-       if ($? != 0) {
-               return undef;
-       }
-
-       open $fh, '<', $hunkfile
-               or die sprintf(__("failed to open hunk edit file for reading: %s"), $!);
-       my @newtext = grep { !/^\Q$comment_line_char\E/ } <$fh>;
-       close $fh;
-       unlink $hunkfile;
-
-       # Abort if nothing remains
-       if (!grep { /\S/ } @newtext) {
-               return undef;
-       }
-
-       # Reinsert the first hunk header if the user accidentally deleted it
-       if ($newtext[0] !~ /^@/) {
-               unshift @newtext, $oldtext->[0];
-       }
-       return \@newtext;
-}
-
-sub diff_applies {
-       return run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --check',
-                            map { @{$_->{TEXT}} } @_);
-}
-
-sub _restore_terminal_and_die {
-       ReadMode 'restore';
-       print "\n";
-       exit 1;
-}
-
-sub prompt_single_character {
-       if ($use_readkey) {
-               local $SIG{TERM} = \&_restore_terminal_and_die;
-               local $SIG{INT} = \&_restore_terminal_and_die;
-               ReadMode 'cbreak';
-               my $key = ReadKey 0;
-               ReadMode 'restore';
-               if (defined $key) {
-                       if ($use_termcap and $key eq "\e") {
-                               while (!defined $term_escapes{$key}) {
-                                       my $next = ReadKey 0.5;
-                                       last if (!defined $next);
-                                       $key .= $next;
-                               }
-                               $key =~ s/\e/^[/;
-                       }
-                       print "$key";
-               }
-               print "\n";
-               return $key;
-       } else {
-               return <STDIN>;
-       }
-}
-
-sub prompt_yesno {
-       my ($prompt) = @_;
-       while (1) {
-               print colored $prompt_color, $prompt;
-               my $line = prompt_single_character;
-               return undef unless defined $line;
-               return 0 if $line =~ /^n/i;
-               return 1 if $line =~ /^y/i;
-       }
-}
-
-sub edit_hunk_loop {
-       my ($head, $hunks, $ix) = @_;
-       my $hunk = $hunks->[$ix];
-       my $text = $hunk->{TEXT};
-
-       while (1) {
-               my $newtext = edit_hunk_manually($text);
-               if (!defined $newtext) {
-                       return undef;
-               }
-               my $newhunk = {
-                       TEXT => $newtext,
-                       TYPE => $hunk->{TYPE},
-                       USE => 1,
-                       DIRTY => 1,
-               };
-               $newhunk->{OFS_DELTA} = recount_edited_hunk($text, $newtext);
-               # If this hunk has already been edited then add the
-               # offset delta of the previous edit to get the real
-               # delta from the original unedited hunk.
-               $hunk->{OFS_DELTA} and
-                               $newhunk->{OFS_DELTA} += $hunk->{OFS_DELTA};
-               if (diff_applies($head,
-                                @{$hunks}[0..$ix-1],
-                                $newhunk,
-                                @{$hunks}[$ix+1..$#{$hunks}])) {
-                       $newhunk->{DISPLAY} = [color_diff(@{$newtext})];
-                       return $newhunk;
-               }
-               else {
-                       prompt_yesno(
-                               # TRANSLATORS: do not translate [y/n]
-                               # The program will only accept that input
-                               # at this point.
-                               # Consider translating (saying "no" discards!) as
-                               # (saying "n" for "no" discards!) if the translation
-                               # of the word "no" does not start with n.
-                               __('Your edited hunk does not apply. Edit again '
-                                  . '(saying "no" discards!) [y/n]? ')
-                               ) or return undef;
-               }
-       }
-}
-
-my %help_patch_modes = (
-       stage => N__(
-"y - stage this hunk
-n - do not stage this hunk
-q - quit; do not stage this hunk or any of the remaining ones
-a - stage this hunk and all later hunks in the file
-d - do not stage this hunk or any of the later hunks in the file"),
-       stash => N__(
-"y - stash this hunk
-n - do not stash this hunk
-q - quit; do not stash this hunk or any of the remaining ones
-a - stash this hunk and all later hunks in the file
-d - do not stash this hunk or any of the later hunks in the file"),
-       reset_head => N__(
-"y - unstage this hunk
-n - do not unstage this hunk
-q - quit; do not unstage this hunk or any of the remaining ones
-a - unstage this hunk and all later hunks in the file
-d - do not unstage this hunk or any of the later hunks in the file"),
-       reset_nothead => N__(
-"y - apply this hunk to index
-n - do not apply this hunk to index
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
-       checkout_index => N__(
-"y - discard this hunk from worktree
-n - do not discard this hunk from worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
-       checkout_head => N__(
-"y - discard this hunk from index and worktree
-n - do not discard this hunk from index and worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
-       checkout_nothead => N__(
-"y - apply this hunk to index and worktree
-n - do not apply this hunk to index and worktree
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
-       worktree_head => N__(
-"y - discard this hunk from worktree
-n - do not discard this hunk from worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
-       worktree_nothead => N__(
-"y - apply this hunk to worktree
-n - do not apply this hunk to worktree
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
-);
-
-sub help_patch_cmd {
-       local $_;
-       my $other = $_[0] . ",?";
-       print colored $help_color, __($help_patch_modes{$patch_mode}), "\n",
-               map { "$_\n" } grep {
-                       my $c = quotemeta(substr($_, 0, 1));
-                       $other =~ /,$c/
-               } split "\n", __ <<EOF ;
-g - select a hunk to go to
-/ - search for a hunk matching the given regex
-j - leave this hunk undecided, see next undecided hunk
-J - leave this hunk undecided, see next hunk
-k - leave this hunk undecided, see previous undecided hunk
-K - leave this hunk undecided, see previous hunk
-s - split the current hunk into smaller hunks
-e - manually edit the current hunk
-? - print help
-EOF
-}
-
-sub apply_patch {
-       my $cmd = shift;
-       my $ret = run_git_apply $cmd, @_;
-       if (!$ret) {
-               print STDERR @_;
-       }
-       return $ret;
-}
-
-sub apply_patch_for_checkout_commit {
-       my $reverse = shift;
-       my $applies_index = run_git_apply 'apply '.$reverse.' --cached --check', @_;
-       my $applies_worktree = run_git_apply 'apply '.$reverse.' --check', @_;
-
-       if ($applies_worktree && $applies_index) {
-               run_git_apply 'apply '.$reverse.' --cached', @_;
-               run_git_apply 'apply '.$reverse, @_;
-               return 1;
-       } elsif (!$applies_index) {
-               print colored $error_color, __("The selected hunks do not apply to the index!\n");
-               if (prompt_yesno __("Apply them to the worktree anyway? ")) {
-                       return run_git_apply 'apply '.$reverse, @_;
-               } else {
-                       print colored $error_color, __("Nothing was applied.\n");
-                       return 0;
-               }
-       } else {
-               print STDERR @_;
-               return 0;
-       }
-}
-
-sub patch_update_cmd {
-       my @all_mods = list_modified($patch_mode_flavour{FILTER});
-       error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
-               for grep { $_->{UNMERGED} } @all_mods;
-       @all_mods = grep { !$_->{UNMERGED} } @all_mods;
-
-       my @mods = grep { !($_->{BINARY}) } @all_mods;
-       my @them;
-
-       if (!@mods) {
-               if (@all_mods) {
-                       print STDERR __("Only binary files changed.\n");
-               } else {
-                       print STDERR __("No changes.\n");
-               }
-               return 0;
-       }
-       if ($patch_mode_only) {
-               @them = @mods;
-       }
-       else {
-               @them = list_and_choose({ PROMPT => __('Patch update'),
-                                         HEADER => $status_head, },
-                                       @mods);
-       }
-       for (@them) {
-               return 0 if patch_update_file($_->{VALUE});
-       }
-}
-
-# Generate a one line summary of a hunk.
-sub summarize_hunk {
-       my $rhunk = shift;
-       my $summary = $rhunk->{TEXT}[0];
-
-       # Keep the line numbers, discard extra context.
-       $summary =~ s/@@(.*?)@@.*/$1 /s;
-       $summary .= " " x (20 - length $summary);
-
-       # Add some user context.
-       for my $line (@{$rhunk->{TEXT}}) {
-               if ($line =~ m/^[+-].*\w/) {
-                       $summary .= $line;
-                       last;
-               }
-       }
-
-       chomp $summary;
-       return substr($summary, 0, 80) . "\n";
-}
-
-
-# Print a one-line summary of each hunk in the array ref in
-# the first argument, starting with the index in the 2nd.
-sub display_hunks {
-       my ($hunks, $i) = @_;
-       my $ctr = 0;
-       $i ||= 0;
-       for (; $i < @$hunks && $ctr < 20; $i++, $ctr++) {
-               my $status = " ";
-               if (defined $hunks->[$i]{USE}) {
-                       $status = $hunks->[$i]{USE} ? "+" : "-";
-               }
-               printf "%s%2d: %s",
-                       $status,
-                       $i + 1,
-                       summarize_hunk($hunks->[$i]);
-       }
-       return $i;
-}
-
-my %patch_update_prompt_modes = (
-       stage => {
-               mode => N__("Stage mode change [y,n,q,a,d%s,?]? "),
-               deletion => N__("Stage deletion [y,n,q,a,d%s,?]? "),
-               addition => N__("Stage addition [y,n,q,a,d%s,?]? "),
-               hunk => N__("Stage this hunk [y,n,q,a,d%s,?]? "),
-       },
-       stash => {
-               mode => N__("Stash mode change [y,n,q,a,d%s,?]? "),
-               deletion => N__("Stash deletion [y,n,q,a,d%s,?]? "),
-               addition => N__("Stash addition [y,n,q,a,d%s,?]? "),
-               hunk => N__("Stash this hunk [y,n,q,a,d%s,?]? "),
-       },
-       reset_head => {
-               mode => N__("Unstage mode change [y,n,q,a,d%s,?]? "),
-               deletion => N__("Unstage deletion [y,n,q,a,d%s,?]? "),
-               addition => N__("Unstage addition [y,n,q,a,d%s,?]? "),
-               hunk => N__("Unstage this hunk [y,n,q,a,d%s,?]? "),
-       },
-       reset_nothead => {
-               mode => N__("Apply mode change to index [y,n,q,a,d%s,?]? "),
-               deletion => N__("Apply deletion to index [y,n,q,a,d%s,?]? "),
-               addition => N__("Apply addition to index [y,n,q,a,d%s,?]? "),
-               hunk => N__("Apply this hunk to index [y,n,q,a,d%s,?]? "),
-       },
-       checkout_index => {
-               mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
-       },
-       checkout_head => {
-               mode => N__("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
-       },
-       checkout_nothead => {
-               mode => N__("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
-       },
-       worktree_head => {
-               mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
-       },
-       worktree_nothead => {
-               mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Apply addition to worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
-       },
-);
-
-sub patch_update_file {
-       my $quit = 0;
-       my ($ix, $num);
-       my $path = shift;
-       my ($head, @hunk) = parse_diff($path);
-       ($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);
-       for (@{$head->{DISPLAY}}) {
-               print;
-       }
-
-       if (@{$mode->{TEXT}}) {
-               unshift @hunk, $mode;
-       }
-       if (@{$deletion->{TEXT}}) {
-               foreach my $hunk (@hunk) {
-                       push @{$deletion->{TEXT}}, @{$hunk->{TEXT}};
-                       push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};
-               }
-               @hunk = ($deletion);
-       }
-
-       $num = scalar @hunk;
-       $ix = 0;
-
-       while (1) {
-               my ($prev, $next, $other, $undecided, $i);
-               $other = '';
-
-               last if ($ix and !$num);
-               if ($num <= $ix) {
-                       $ix = 0;
-               }
-               for ($i = 0; $i < $ix; $i++) {
-                       if (!defined $hunk[$i]{USE}) {
-                               $prev = 1;
-                               $other .= ',k';
-                               last;
-                       }
-               }
-               if ($ix) {
-                       $other .= ',K';
-               }
-               for ($i = $ix + 1; $i < $num; $i++) {
-                       if (!defined $hunk[$i]{USE}) {
-                               $next = 1;
-                               $other .= ',j';
-                               last;
-                       }
-               }
-               if ($ix < $num - 1) {
-                       $other .= ',J';
-               }
-               if ($num > 1) {
-                       $other .= ',g,/';
-               }
-               for ($i = 0; $i < $num; $i++) {
-                       if (!defined $hunk[$i]{USE}) {
-                               $undecided = 1;
-                               last;
-                       }
-               }
-               last if (!$undecided && ($num || !$addition));
-
-               if ($num) {
-                       if ($hunk[$ix]{TYPE} eq 'hunk' &&
-                           hunk_splittable($hunk[$ix]{TEXT})) {
-                               $other .= ',s';
-                       }
-                       if ($hunk[$ix]{TYPE} eq 'hunk') {
-                               $other .= ',e';
-                       }
-                       for (@{$hunk[$ix]{DISPLAY}}) {
-                               print;
-                       }
-               }
-               my $type = $num ? $hunk[$ix]{TYPE} : $head->{TYPE};
-               print colored $prompt_color, "(", ($ix+1), "/", ($num ? $num : 1), ") ",
-                       sprintf(__($patch_update_prompt_modes{$patch_mode}{$type}), $other);
-
-               my $line = prompt_single_character;
-               last unless defined $line;
-               if ($line) {
-                       if ($line =~ /^y/i) {
-                               if ($num) {
-                                       $hunk[$ix]{USE} = 1;
-                               } else {
-                                       $head->{USE} = 1;
-                               }
-                       }
-                       elsif ($line =~ /^n/i) {
-                               if ($num) {
-                                       $hunk[$ix]{USE} = 0;
-                               } else {
-                                       $head->{USE} = 0;
-                               }
-                       }
-                       elsif ($line =~ /^a/i) {
-                               if ($num) {
-                                       while ($ix < $num) {
-                                               if (!defined $hunk[$ix]{USE}) {
-                                                       $hunk[$ix]{USE} = 1;
-                                               }
-                                               $ix++;
-                                       }
-                               } else {
-                                       $head->{USE} = 1;
-                                       $ix++;
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^g(.*)/) {
-                               my $response = $1;
-                               unless ($other =~ /g/) {
-                                       error_msg __("No other hunks to goto\n");
-                                       next;
-                               }
-                               my $no = $ix > 10 ? $ix - 10 : 0;
-                               while ($response eq '') {
-                                       $no = display_hunks(\@hunk, $no);
-                                       if ($no < $num) {
-                                               print __("go to which hunk (<ret> to see more)? ");
-                                       } else {
-                                               print __("go to which hunk? ");
-                                       }
-                                       $response = <STDIN>;
-                                       if (!defined $response) {
-                                               $response = '';
-                                       }
-                                       chomp $response;
-                               }
-                               if ($response !~ /^\s*\d+\s*$/) {
-                                       error_msg sprintf(__("Invalid number: '%s'\n"),
-                                                            $response);
-                               } elsif (0 < $response && $response <= $num) {
-                                       $ix = $response - 1;
-                               } else {
-                                       error_msg sprintf(__n("Sorry, only %d hunk available.\n",
-                                                             "Sorry, only %d hunks available.\n", $num), $num);
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^d/i) {
-                               if ($num) {
-                                       while ($ix < $num) {
-                                               if (!defined $hunk[$ix]{USE}) {
-                                                       $hunk[$ix]{USE} = 0;
-                                               }
-                                               $ix++;
-                                       }
-                               } else {
-                                       $head->{USE} = 0;
-                                       $ix++;
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^q/i) {
-                               if ($num) {
-                                       for ($i = 0; $i < $num; $i++) {
-                                               if (!defined $hunk[$i]{USE}) {
-                                                       $hunk[$i]{USE} = 0;
-                                               }
-                                       }
-                               } elsif (!defined $head->{USE}) {
-                                       $head->{USE} = 0;
-                               }
-                               $quit = 1;
-                               last;
-                       }
-                       elsif ($line =~ m|^/(.*)|) {
-                               my $regex = $1;
-                               unless ($other =~ m|/|) {
-                                       error_msg __("No other hunks to search\n");
-                                       next;
-                               }
-                               if ($regex eq "") {
-                                       print colored $prompt_color, __("search for regex? ");
-                                       $regex = <STDIN>;
-                                       if (defined $regex) {
-                                               chomp $regex;
-                                       }
-                               }
-                               my $search_string;
-                               eval {
-                                       $search_string = qr{$regex}m;
-                               };
-                               if ($@) {
-                                       my ($err,$exp) = ($@, $1);
-                                       $err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
-                                       error_msg sprintf(__("Malformed search regexp %s: %s\n"), $exp, $err);
-                                       next;
-                               }
-                               my $iy = $ix;
-                               while (1) {
-                                       my $text = join ("", @{$hunk[$iy]{TEXT}});
-                                       last if ($text =~ $search_string);
-                                       $iy++;
-                                       $iy = 0 if ($iy >= $num);
-                                       if ($ix == $iy) {
-                                               error_msg __("No hunk matches the given pattern\n");
-                                               last;
-                                       }
-                               }
-                               $ix = $iy;
-                               next;
-                       }
-                       elsif ($line =~ /^K/) {
-                               if ($other =~ /K/) {
-                                       $ix--;
-                               }
-                               else {
-                                       error_msg __("No previous hunk\n");
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^J/) {
-                               if ($other =~ /J/) {
-                                       $ix++;
-                               }
-                               else {
-                                       error_msg __("No next hunk\n");
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^k/) {
-                               if ($other =~ /k/) {
-                                       while (1) {
-                                               $ix--;
-                                               last if (!$ix ||
-                                                        !defined $hunk[$ix]{USE});
-                                       }
-                               }
-                               else {
-                                       error_msg __("No previous hunk\n");
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^j/) {
-                               if ($other !~ /j/) {
-                                       error_msg __("No next hunk\n");
-                                       next;
-                               }
-                       }
-                       elsif ($line =~ /^s/) {
-                               unless ($other =~ /s/) {
-                                       error_msg __("Sorry, cannot split this hunk\n");
-                                       next;
-                               }
-                               my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
-                               if (1 < @split) {
-                                       print colored $header_color, sprintf(
-                                               __n("Split into %d hunk.\n",
-                                                   "Split into %d hunks.\n",
-                                                   scalar(@split)), scalar(@split));
-                               }
-                               splice (@hunk, $ix, 1, @split);
-                               $num = scalar @hunk;
-                               next;
-                       }
-                       elsif ($line =~ /^e/) {
-                               unless ($other =~ /e/) {
-                                       error_msg __("Sorry, cannot edit this hunk\n");
-                                       next;
-                               }
-                               my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
-                               if (defined $newhunk) {
-                                       splice @hunk, $ix, 1, $newhunk;
-                               }
-                       }
-                       else {
-                               help_patch_cmd($other);
-                               next;
-                       }
-                       # soft increment
-                       while (1) {
-                               $ix++;
-                               last if ($ix >= $num ||
-                                        !defined $hunk[$ix]{USE});
-                       }
-               }
-       }
-
-       @hunk = coalesce_overlapping_hunks(@hunk) if ($num);
-
-       my $n_lofs = 0;
-       my @result = ();
-       for (@hunk) {
-               if ($_->{USE}) {
-                       push @result, @{$_->{TEXT}};
-               }
-       }
-
-       if (@result or $head->{USE}) {
-               my @patch = reassemble_patch($head->{TEXT}, @result);
-               my $apply_routine = $patch_mode_flavour{APPLY};
-               &$apply_routine(@patch);
-               refresh();
-       }
-
-       print "\n";
-       return $quit;
-}
-
-sub diff_cmd {
-       my @mods = list_modified('index-only');
-       @mods = grep { !($_->{BINARY}) } @mods;
-       return if (!@mods);
-       my (@them) = list_and_choose({ PROMPT => __('Review diff'),
-                                    IMMEDIATE => 1,
-                                    HEADER => $status_head, },
-                                  @mods);
-       return if (!@them);
-       my $reference = (is_initial_commit()) ? get_empty_tree() : 'HEAD';
-       system(qw(git diff -p --cached), $reference, '--',
-               map { $_->{VALUE} } @them);
-}
-
-sub quit_cmd {
-       print __("Bye.\n");
-       exit(0);
-}
-
-sub help_cmd {
-# TRANSLATORS: please do not translate the command names
-# 'status', 'update', 'revert', etc.
-       print colored $help_color, __ <<'EOF' ;
-status        - show paths with changes
-update        - add working tree state to the staged set of changes
-revert        - revert staged set of changes back to the HEAD version
-patch         - pick hunks and update selectively
-diff          - view diff between HEAD and index
-add untracked - add contents of untracked files to the staged set of changes
-EOF
-}
-
-sub process_args {
-       return unless @ARGV;
-       my $arg = shift @ARGV;
-       if ($arg =~ /--patch(?:=(.*))?/) {
-               if (defined $1) {
-                       if ($1 eq 'reset') {
-                               $patch_mode = 'reset_head';
-                               $patch_mode_revision = 'HEAD';
-                               $arg = shift @ARGV or die __("missing --");
-                               if ($arg ne '--') {
-                                       $patch_mode_revision = $arg;
-
-                                       # NEEDSWORK: Instead of comparing to the literal "HEAD",
-                                       # compare the commit objects instead so that other ways of
-                                       # saying the same thing (such as "@") are also handled
-                                       # appropriately.
-                                       #
-                                       # This applies to the cases below too.
-                                       $patch_mode = ($arg eq 'HEAD' ?
-                                                      'reset_head' : 'reset_nothead');
-                                       $arg = shift @ARGV or die __("missing --");
-                               }
-                       } elsif ($1 eq 'checkout') {
-                               $arg = shift @ARGV or die __("missing --");
-                               if ($arg eq '--') {
-                                       $patch_mode = 'checkout_index';
-                               } else {
-                                       $patch_mode_revision = $arg;
-                                       $patch_mode = ($arg eq 'HEAD' ?
-                                                      'checkout_head' : 'checkout_nothead');
-                                       $arg = shift @ARGV or die __("missing --");
-                               }
-                       } elsif ($1 eq 'worktree') {
-                               $arg = shift @ARGV or die __("missing --");
-                               if ($arg eq '--') {
-                                       $patch_mode = 'checkout_index';
-                               } else {
-                                       $patch_mode_revision = $arg;
-                                       $patch_mode = ($arg eq 'HEAD' ?
-                                                      'worktree_head' : 'worktree_nothead');
-                                       $arg = shift @ARGV or die __("missing --");
-                               }
-                       } elsif ($1 eq 'stage' or $1 eq 'stash') {
-                               $patch_mode = $1;
-                               $arg = shift @ARGV or die __("missing --");
-                       } else {
-                               die sprintf(__("unknown --patch mode: %s"), $1);
-                       }
-               } else {
-                       $patch_mode = 'stage';
-                       $arg = shift @ARGV or die __("missing --");
-               }
-               die sprintf(__("invalid argument %s, expecting --"),
-                              $arg) unless $arg eq "--";
-               %patch_mode_flavour = %{$patch_modes{$patch_mode}};
-               $patch_mode_only = 1;
-       }
-       elsif ($arg ne "--") {
-               die sprintf(__("invalid argument %s, expecting --"), $arg);
-       }
-}
-
-sub main_loop {
-       my @cmd = ([ 'status', \&status_cmd, ],
-                  [ 'update', \&update_cmd, ],
-                  [ 'revert', \&revert_cmd, ],
-                  [ 'add untracked', \&add_untracked_cmd, ],
-                  [ 'patch', \&patch_update_cmd, ],
-                  [ 'diff', \&diff_cmd, ],
-                  [ 'quit', \&quit_cmd, ],
-                  [ 'help', \&help_cmd, ],
-       );
-       while (1) {
-               my ($it) = list_and_choose({ PROMPT => __('What now'),
-                                            SINGLETON => 1,
-                                            LIST_FLAT => 4,
-                                            HEADER => __('*** Commands ***'),
-                                            ON_EOF => \&quit_cmd,
-                                            IMMEDIATE => 1 }, @cmd);
-               if ($it) {
-                       eval {
-                               $it->[1]->();
-                       };
-                       if ($@) {
-                               print "$@";
-                       }
-               }
-       }
-}
-
-process_args();
-refresh();
-if ($patch_mode_only) {
-       patch_update_cmd();
-}
-else {
-       status_cmd();
-       main_loop();
-}
index b7c173c345544d61a887cfbebb58703478a29d2d..f5a317b89961ce3bd6ec64afa96ae0e550b7e4d0 100755 (executable)
@@ -54,7 +54,7 @@ and can contain multiple, unrelated branches.
 
 =cut
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Std;
diff --git a/git-bisect.sh b/git-bisect.sh
deleted file mode 100755 (executable)
index 405cf76..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/bin/sh
-
-USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]'
-LONG_USAGE='git bisect help
-       print this long help message.
-git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]
-                [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
-       reset bisect state and start bisection.
-git bisect (bad|new) [<rev>]
-       mark <rev> a known-bad revision/
-               a revision after change in a given property.
-git bisect (good|old) [<rev>...]
-       mark <rev>... known-good revisions/
-               revisions before change in a given property.
-git bisect terms [--term-good | --term-bad]
-       show the terms used for old and new commits (default: bad, good)
-git bisect skip [(<rev>|<range>)...]
-       mark <rev>... untestable revisions.
-git bisect next
-       find next bisection to test and check it out.
-git bisect reset [<commit>]
-       finish bisection search and go back to commit.
-git bisect (visualize|view)
-       show bisect status in gitk.
-git bisect replay <logfile>
-       replay bisection log.
-git bisect log
-       show bisect log.
-git bisect run <cmd>...
-       use <cmd>... to automatically bisect.
-
-Please use "git help bisect" to get the full man page.'
-
-OPTIONS_SPEC=
-. git-sh-setup
-
-TERM_BAD=bad
-TERM_GOOD=good
-
-get_terms () {
-       if test -s "$GIT_DIR/BISECT_TERMS"
-       then
-               {
-               read TERM_BAD
-               read TERM_GOOD
-               } <"$GIT_DIR/BISECT_TERMS"
-       fi
-}
-
-case "$#" in
-0)
-       usage ;;
-*)
-       cmd="$1"
-       get_terms
-       shift
-       case "$cmd" in
-       help)
-               git bisect -h ;;
-       start)
-               git bisect--helper --bisect-start "$@" ;;
-       bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
-               git bisect--helper --bisect-state "$cmd" "$@" ;;
-       skip)
-               git bisect--helper --bisect-skip "$@" || exit;;
-       next)
-               # Not sure we want "next" at the UI level anymore.
-               git bisect--helper --bisect-next "$@" || exit ;;
-       visualize|view)
-               git bisect--helper --bisect-visualize "$@" || exit;;
-       reset)
-               git bisect--helper --bisect-reset "$@" ;;
-       replay)
-               git bisect--helper --bisect-replay "$@" || exit;;
-       log)
-               git bisect--helper --bisect-log || exit ;;
-       run)
-               git bisect--helper --bisect-run "$@" || exit;;
-       terms)
-               git bisect--helper --bisect-terms "$@" || exit;;
-       *)
-               usage ;;
-       esac
-esac
index 9bfd7ce76d027b8dbbbd5be1fc67e92f26c4acfa..7c2a6538e5afea607f3d9a1c09cc6aea5539d8de 100644 (file)
@@ -97,8 +97,14 @@ struct strbuf;
 # define BARF_UNLESS_AN_ARRAY(arr)                                             \
        BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(__typeof__(arr), \
                                                           __typeof__(&(arr)[0])))
+# define BARF_UNLESS_COPYABLE(dst, src) \
+       BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(__typeof__(*(dst)), \
+                                                         __typeof__(*(src))))
 #else
 # define BARF_UNLESS_AN_ARRAY(arr) 0
+# define BARF_UNLESS_COPYABLE(dst, src) \
+       BUILD_ASSERT_OR_ZERO(0 ? ((*(dst) = *(src)), 0) : \
+                                sizeof(*(dst)) == sizeof(*(src)))
 #endif
 /*
  * ARRAY_SIZE - get the number of elements in a visible array
@@ -219,12 +225,14 @@ struct strbuf;
 #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
@@ -313,7 +321,9 @@ typedef unsigned long uintptr_t;
 #ifdef PRECOMPOSE_UNICODE
 #include "compat/precompose_utf8.h"
 #else
-static inline const char *precompose_argv_prefix(int argc, const char **argv, const char *prefix)
+static inline const char *precompose_argv_prefix(int argc UNUSED,
+                                                const char **argv UNUSED,
+                                                const char *prefix)
 {
        return prefix;
 }
@@ -330,6 +340,25 @@ static inline const char *precompose_string_if_needed(const char *in)
 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;
@@ -338,9 +367,13 @@ struct itimerval {
 #endif
 
 #ifdef NO_SETITIMER
-static inline int setitimer(int which, const struct itimerval *value, struct itimerval *newvalue) {
+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
@@ -390,6 +423,10 @@ char *gitdirname(char *);
 #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
@@ -408,8 +445,10 @@ typedef uintmax_t timestamp_t;
 #endif
 
 #ifndef platform_core_config
+struct config_context;
 static inline int noop_core_config(const char *var UNUSED,
                                   const char *value UNUSED,
+                                  const struct config_context *ctx UNUSED,
                                   void *cb UNUSED)
 {
        return 0;
@@ -423,7 +462,7 @@ int lstat_cache_aware_rmdir(const char *path);
 #endif
 
 #ifndef has_dos_drive_prefix
-static inline int git_has_dos_drive_prefix(const char *path)
+static inline int git_has_dos_drive_prefix(const char *path UNUSED)
 {
        return 0;
 }
@@ -431,7 +470,7 @@ static inline int git_has_dos_drive_prefix(const char *path)
 #endif
 
 #ifndef skip_dos_drive_prefix
-static inline int git_skip_dos_drive_prefix(char **path)
+static inline int git_skip_dos_drive_prefix(char **path UNUSED)
 {
        return 0;
 }
@@ -593,9 +632,7 @@ static inline int git_has_dir_sep(const char *path)
 
 #include "compat/bswap.h"
 
-#include "wildmatch.h"
-
-struct strbuf;
+#include "wrapper.h"
 
 /* General helper functions */
 NORETURN void usage(const char *err);
@@ -647,15 +684,12 @@ void set_warn_routine(report_fn routine);
 report_fn get_warn_routine(void);
 void set_die_is_recursing_routine(int (*routine)(void));
 
-int starts_with(const char *str, const char *prefix);
-int istarts_with(const char *str, const char *prefix);
-
 /*
- * If the string "str" begins with the string found in "prefix", return 1.
+ * If the string "str" begins with the string found in "prefix", return true.
  * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in
  * the string right after the prefix).
  *
- * Otherwise, return 0 and leave "out" untouched.
+ * Otherwise, return false and leave "out" untouched.
  *
  * Examples:
  *
@@ -666,91 +700,63 @@ int istarts_with(const char *str, const char *prefix);
  *   [skip prefix if present, otherwise use whole string]
  *   skip_prefix(name, "refs/heads/", &name);
  */
-static inline int skip_prefix(const char *str, const char *prefix,
-                             const char **out)
+static inline bool skip_prefix(const char *str, const char *prefix,
+                              const char **out)
 {
        do {
                if (!*prefix) {
                        *out = str;
-                       return 1;
+                       return true;
                }
        } while (*str++ == *prefix++);
-       return 0;
-}
-
-/*
- * If the string "str" is the same as the string in "prefix", then the "arg"
- * parameter is set to the "def" parameter and 1 is returned.
- * If the string "str" begins with the string found in "prefix" and then a
- * "=" sign, then the "arg" parameter is set to "str + strlen(prefix) + 1"
- * (i.e., to the point in the string right after the prefix and the "=" sign),
- * and 1 is returned.
- *
- * Otherwise, return 0 and leave "arg" untouched.
- *
- * When we accept both a "--key" and a "--key=<val>" option, this function
- * can be used instead of !strcmp(arg, "--key") and then
- * skip_prefix(arg, "--key=", &arg) to parse such an option.
- */
-int skip_to_optional_arg_default(const char *str, const char *prefix,
-                                const char **arg, const char *def);
-
-static inline int skip_to_optional_arg(const char *str, const char *prefix,
-                                      const char **arg)
-{
-       return skip_to_optional_arg_default(str, prefix, arg, "");
+       return false;
 }
 
 /*
  * Like skip_prefix, but promises never to read past "len" bytes of the input
  * buffer, and returns the remaining number of bytes in "out" via "outlen".
  */
-static inline int skip_prefix_mem(const char *buf, size_t len,
-                                 const char *prefix,
-                                 const char **out, size_t *outlen)
+static inline bool skip_prefix_mem(const char *buf, size_t len,
+                                  const char *prefix,
+                                  const char **out, size_t *outlen)
 {
        size_t prefix_len = strlen(prefix);
        if (prefix_len <= len && !memcmp(buf, prefix, prefix_len)) {
                *out = buf + prefix_len;
                *outlen = len - prefix_len;
-               return 1;
+               return true;
        }
-       return 0;
+       return false;
 }
 
 /*
- * If buf ends with suffix, return 1 and subtract the length of the suffix
- * from *len. Otherwise, return 0 and leave *len untouched.
+ * If buf ends with suffix, return true and subtract the length of the suffix
+ * from *len. Otherwise, return false and leave *len untouched.
  */
-static inline int strip_suffix_mem(const char *buf, size_t *len,
-                                  const char *suffix)
+static inline bool strip_suffix_mem(const char *buf, size_t *len,
+                                   const char *suffix)
 {
        size_t suflen = strlen(suffix);
        if (*len < suflen || memcmp(buf + (*len - suflen), suffix, suflen))
-               return 0;
+               return false;
        *len -= suflen;
-       return 1;
+       return true;
 }
 
 /*
- * If str ends with suffix, return 1 and set *len to the size of the string
- * without the suffix. Otherwise, return 0 and set *len to the size of the
+ * If str ends with suffix, return true and set *len to the size of the string
+ * without the suffix. Otherwise, return false and set *len to the size of the
  * string.
  *
  * Note that we do _not_ NUL-terminate str to the new length.
  */
-static inline int strip_suffix(const char *str, const char *suffix, size_t *len)
+static inline bool strip_suffix(const char *str, const char *suffix,
+                               size_t *len)
 {
        *len = strlen(str);
        return strip_suffix_mem(str, len, suffix);
 }
 
-static inline int ends_with(const char *str, const char *suffix)
-{
-       size_t len;
-       return strip_suffix(str, suffix, &len);
-}
-
 #define SWAP(a, b) do {                                                \
        void *_swap_a_ptr = &(a);                               \
        void *_swap_b_ptr = &(b);                               \
@@ -846,12 +852,6 @@ int git_lstat(const char *, struct stat *);
 #define pread git_pread
 ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
 #endif
-/*
- * Forward decl that will remind us if its twin in cache.h changes.
- * This function is used in compat/pread.c.  But we can't include
- * cache.h there.
- */
-ssize_t read_in_full(int fd, void *buf, size_t count);
 
 #ifdef NO_SETENV
 #define setenv gitsetenv
@@ -1015,6 +1015,15 @@ static inline unsigned long cast_size_t_to_ulong(size_t a)
        return (unsigned long)a;
 }
 
+static inline uint32_t cast_size_t_to_uint32_t(size_t a)
+{
+       if (a != (uint32_t)a)
+               die("object too large to read on this platform: %"
+                   PRIuMAX" is cut off to %u",
+                   (uintmax_t)a, (uint32_t)a);
+       return (uint32_t)a;
+}
+
 static inline int cast_size_t_to_int(size_t a)
 {
        if (a > INT_MAX)
@@ -1053,36 +1062,6 @@ static inline int cast_size_t_to_int(size_t a)
 # define xalloca(size)      (xmalloc(size))
 # define xalloca_free(p)    (free(p))
 #endif
-char *xstrdup(const char *str);
-void *xmalloc(size_t size);
-void *xmallocz(size_t size);
-void *xmallocz_gently(size_t size);
-void *xmemdupz(const void *data, size_t len);
-char *xstrndup(const char *str, size_t len);
-void *xrealloc(void *ptr, size_t size);
-void *xcalloc(size_t nmemb, size_t size);
-void xsetenv(const char *name, const char *value, int overwrite);
-void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-const char *mmap_os_err(void);
-void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-int xopen(const char *path, int flags, ...);
-ssize_t xread(int fd, void *buf, size_t len);
-ssize_t xwrite(int fd, const void *buf, size_t len);
-ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
-int xdup(int fd);
-FILE *xfopen(const char *path, const char *mode);
-FILE *xfdopen(int fd, const char *mode);
-int xmkstemp(char *temp_filename);
-int xmkstemp_mode(char *temp_filename, int mode);
-char *xgetcwd(void);
-FILE *fopen_for_writing(const char *path);
-FILE *fopen_or_warn(const char *path, const char *mode);
-
-/*
- * Like strncmp, but only return zero if s is NUL-terminated and exactly len
- * characters long.  If it is not, consider it greater than t.
- */
-int xstrncmpz(const char *s, const char *t, size_t len);
 
 /*
  * FREE_AND_NULL(ptr) is like free(ptr) followed by ptr = NULL. Note
@@ -1095,7 +1074,7 @@ int xstrncmpz(const char *s, const char *t, size_t len);
 #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
 
 #define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \
-       BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+       BARF_UNLESS_COPYABLE((dst), (src)))
 static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
 {
        if (n)
@@ -1103,13 +1082,18 @@ static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
 }
 
 #define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \
-       BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+       BARF_UNLESS_COPYABLE((dst), (src)))
 static inline void move_array(void *dst, const void *src, size_t n, size_t size)
 {
        if (n)
                memmove(dst, src, st_mult(size, n));
 }
 
+#define DUP_ARRAY(dst, src, n) do { \
+       size_t dup_array_n_ = (n); \
+       COPY_ARRAY(ALLOC_ARRAY((dst), dup_array_n_), (src), dup_array_n_); \
+} while (0)
+
 /*
  * These functions help you allocate structs with flex arrays, and copy
  * the data directly into the array. For example, if you had:
@@ -1167,6 +1151,81 @@ static inline void move_array(void *dst, const void *src, size_t n, size_t size)
 #define FLEXPTR_ALLOC_STR(x, ptrname, str) \
        FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str))
 
+#define alloc_nr(x) (((x)+16)*3/2)
+
+/**
+ * Dynamically growing an array using realloc() is error prone and boring.
+ *
+ * Define your array with:
+ *
+ * - a pointer (`item`) that points at the array, initialized to `NULL`
+ *   (although please name the variable based on its contents, not on its
+ *   type);
+ *
+ * - an integer variable (`alloc`) that keeps track of how big the current
+ *   allocation is, initialized to `0`;
+ *
+ * - another integer variable (`nr`) to keep track of how many elements the
+ *   array currently has, initialized to `0`.
+ *
+ * Then before adding `n`th element to the item, call `ALLOC_GROW(item, n,
+ * alloc)`.  This ensures that the array can hold at least `n` elements by
+ * calling `realloc(3)` and adjusting `alloc` variable.
+ *
+ * ------------
+ * sometype *item;
+ * size_t nr;
+ * size_t alloc
+ *
+ * for (i = 0; i < nr; i++)
+ *     if (we like item[i] already)
+ *             return;
+ *
+ * // we did not like any existing one, so add one
+ * ALLOC_GROW(item, nr + 1, alloc);
+ * item[nr++] = value you like;
+ * ------------
+ *
+ * You are responsible for updating the `nr` variable.
+ *
+ * If you need to specify the number of elements to allocate explicitly
+ * then use the macro `REALLOC_ARRAY(item, alloc)` instead of `ALLOC_GROW`.
+ *
+ * Consider using ALLOC_GROW_BY instead of ALLOC_GROW as it has some
+ * added niceties.
+ *
+ * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
+ */
+#define ALLOC_GROW(x, nr, alloc) \
+       do { \
+               if ((nr) > alloc) { \
+                       if (alloc_nr(alloc) < (nr)) \
+                               alloc = (nr); \
+                       else \
+                               alloc = alloc_nr(alloc); \
+                       REALLOC_ARRAY(x, alloc); \
+               } \
+       } while (0)
+
+/*
+ * Similar to ALLOC_GROW but handles updating of the nr value and
+ * zeroing the bytes of the newly-grown array elements.
+ *
+ * DO NOT USE any expression with side-effect for any of the
+ * arguments.
+ */
+#define ALLOC_GROW_BY(x, nr, increase, alloc) \
+       do { \
+               if (increase) { \
+                       size_t new_nr = nr + (increase); \
+                       if (new_nr < nr) \
+                               BUG("negative growth in ALLOC_GROW_BY"); \
+                       ALLOC_GROW(x, new_nr, alloc); \
+                       memset((x) + nr, 0, sizeof(*(x)) * (increase)); \
+                       nr = new_nr; \
+               } \
+       } while (0)
+
 static inline char *xstrdup_or_null(const char *str)
 {
        return str ? xstrdup(str) : NULL;
@@ -1179,78 +1238,11 @@ static inline size_t xsize_t(off_t len)
        return (size_t) len;
 }
 
-__attribute__((format (printf, 3, 4)))
-int xsnprintf(char *dst, size_t max, const char *fmt, ...);
-
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 256
 #endif
 
-int xgethostname(char *buf, size_t len);
-
-/* in ctype.c, for kwset users */
-extern const unsigned char tolower_trans_tbl[256];
-
-/* Sane ctype - no locale, and works with signed chars */
-#undef isascii
-#undef isspace
-#undef isdigit
-#undef isalpha
-#undef isalnum
-#undef isprint
-#undef islower
-#undef isupper
-#undef tolower
-#undef toupper
-#undef iscntrl
-#undef ispunct
-#undef isxdigit
-
-extern const unsigned char sane_ctype[256];
-#define GIT_SPACE 0x01
-#define GIT_DIGIT 0x02
-#define GIT_ALPHA 0x04
-#define GIT_GLOB_SPECIAL 0x08
-#define GIT_REGEX_SPECIAL 0x10
-#define GIT_PATHSPEC_MAGIC 0x20
-#define GIT_CNTRL 0x40
-#define GIT_PUNCT 0x80
-#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
-#define isascii(x) (((x) & ~0x7f) == 0)
-#define isspace(x) sane_istest(x,GIT_SPACE)
-#define isdigit(x) sane_istest(x,GIT_DIGIT)
-#define isalpha(x) sane_istest(x,GIT_ALPHA)
-#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
-#define isprint(x) ((x) >= 0x20 && (x) <= 0x7e)
-#define islower(x) sane_iscase(x, 1)
-#define isupper(x) sane_iscase(x, 0)
-#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
-#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
-#define iscntrl(x) (sane_istest(x,GIT_CNTRL))
-#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \
-               GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)
-#define isxdigit(x) (hexval_table[(unsigned char)(x)] != -1)
-#define tolower(x) sane_case((unsigned char)(x), 0x20)
-#define toupper(x) sane_case((unsigned char)(x), 0)
-#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
-
-static inline int sane_case(int x, int high)
-{
-       if (sane_istest(x, GIT_ALPHA))
-               x = (x & ~0x20) | high;
-       return x;
-}
-
-static inline int sane_iscase(int x, int is_lower)
-{
-       if (!sane_istest(x, GIT_ALPHA))
-               return 0;
-
-       if (is_lower)
-               return (x & 0x20) != 0;
-       else
-               return (x & 0x20) == 0;
-}
+#include "sane-ctype.h"
 
 /*
  * Like skip_prefix, but compare case-insensitively. Note that the comparison
@@ -1269,6 +1261,25 @@ static inline int skip_iprefix(const char *str, const char *prefix,
        return 0;
 }
 
+/*
+ * Like skip_prefix_mem, but compare case-insensitively. Note that the
+ * comparison is done via tolower(), so it is strictly ASCII (no multi-byte
+ * characters or locale-specific conversions).
+ */
+static inline int skip_iprefix_mem(const char *buf, size_t len,
+                                  const char *prefix,
+                                  const char **out, size_t *outlen)
+{
+       do {
+               if (!*prefix) {
+                       *out = buf;
+                       *outlen = len;
+                       return 1;
+               }
+       } while (len-- > 0 && tolower(*buf++) == tolower(*prefix++));
+       return 0;
+}
+
 static inline int strtoul_ui(char const *s, int base, unsigned int *result)
 {
        unsigned long ul;
@@ -1339,6 +1350,11 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,
        return regexec(preg, buf, nmatch, pmatch, eflags | REG_STARTEND);
 }
 
+#ifdef USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+int git_regcomp(regex_t *preg, const char *pattern, int cflags);
+#define regcomp git_regcomp
+#endif
+
 #ifndef DIR_HAS_BSD_GROUP_SEMANTICS
 # define FORCE_DIR_SET_GID S_ISGID
 #else
@@ -1403,85 +1419,24 @@ void bug_fl(const char *file, int line, const char *fmt, ...);
 #endif
 #endif
 
-enum fsync_action {
-       FSYNC_WRITEOUT_ONLY,
-       FSYNC_HARDWARE_FLUSH
-};
-
-/*
- * Issues an fsync against the specified file according to the specified mode.
- *
- * FSYNC_WRITEOUT_ONLY attempts to use interfaces available on some operating
- * systems to flush the OS cache without issuing a flush command to the storage
- * controller. If those interfaces are unavailable, the function fails with
- * ENOSYS.
- *
- * FSYNC_HARDWARE_FLUSH does an OS writeout and hardware flush to ensure that
- * changes are durable. It is not expected to fail.
- */
-int git_fsync(int fd, enum fsync_action action);
-
-/*
- * Writes out trace statistics for fsync using the trace2 API.
- */
-void trace_git_fsync_stats(void);
-
-/*
- * Preserves errno, prints a message, but gives no warning for ENOENT.
- * Returns 0 on success, which includes trying to unlink an object that does
- * not exist.
- */
-int unlink_or_warn(const char *path);
- /*
-  * Tries to unlink file.  Returns 0 if unlink succeeded
-  * or the file already didn't exist.  Returns -1 and
-  * appends a message to err suitable for
-  * 'error("%s", err->buf)' on error.
-  */
-int unlink_or_msg(const char *file, struct strbuf *err);
-/*
- * Preserves errno, prints a message, but gives no warning for ENOENT.
- * Returns 0 on success, which includes trying to remove a directory that does
- * not exist.
- */
-int rmdir_or_warn(const char *path);
-/*
- * Calls the correct function out of {unlink,rmdir}_or_warn based on
- * the supplied file mode.
- */
-int remove_or_warn(unsigned int mode, const char *path);
-
-/*
- * Call access(2), but warn for any error except "missing file"
- * (ENOENT or ENOTDIR).
- */
-#define ACCESS_EACCES_OK (1U << 0)
-int access_or_warn(const char *path, int mode, unsigned flag);
-int access_or_die(const char *path, int mode, unsigned flag);
-
-/* Warn on an inaccessible file if errno indicates this is an error */
-int warn_on_fopen_errors(const char *path);
-
-/*
- * Open with O_NOFOLLOW, or equivalent. Note that the fallback equivalent
- * may be racy. Do not use this as protection against an attacker who can
- * simultaneously create paths.
- */
-int open_nofollow(const char *path, int flags);
-
 #ifndef SHELL_PATH
 # define SHELL_PATH "/bin/sh"
 #endif
 
 #ifndef _POSIX_THREAD_SAFE_FUNCTIONS
-static inline void flockfile(FILE *fh)
+static inline void git_flockfile(FILE *fh UNUSED)
 {
        ; /* nothing */
 }
-static inline void funlockfile(FILE *fh)
+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
 
@@ -1603,13 +1558,4 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset)
        ((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))
 #endif /* !__GNUC__ */
 
-void sleep_millisec(int millisec);
-
-/*
- * Generate len bytes from the system cryptographically secure PRNG.
- * Returns 0 on success and -1 on error, setting errno.  The inability to
- * satisfy the full request is an error.
- */
-int csprng_bytes(void *buf, size_t len);
-
 #endif
index 289d4bc684dc26256f497636ca5c080fc6589226..1e03ba94d1b27196c74636fc643781e2b25577a9 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Std;
index 7bf3c12d6789741fc33a9a9032c697bb89dc2fc0..211ec8459a0b84e95e84b0b67ff3c9a7742ad4be 100755 (executable)
@@ -13,7 +13,7 @@
 # The head revision is on branch "origin" by default.
 # You can change that with the '-o' option.
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Long;
@@ -329,7 +329,7 @@ sub conn {
                        # Use a HTTP Proxy. Only works for HTTP proxies that
                        # don't require user authentication
                        #
-                       # See: http://www.ietf.org/rfc/rfc2817.txt
+                       # See: https://www.ietf.org/rfc/rfc2817.txt
 
                        $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
                        die "Socket to $proxyhost: $!\n" unless defined $s;
index 7b757360e28c012eb26632889f831c7fef465e16..124f598bdc07058b139f8dcbf8245128c52d9176 100755 (executable)
@@ -15,7 +15,7 @@
 ####
 ####
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use bytes;
index 992124cc67ce579e89ae86e4cca42ba2c80ea1ea..e4e820e68095928765940be51fba0cdb8e5d609c 100755 (executable)
@@ -75,6 +75,11 @@ then
                merge_tool="$GIT_DIFF_TOOL"
        else
                merge_tool="$(get_merge_tool)"
+               subshell_exit_status=$?
+               if test $subshell_exit_status -gt 1
+               then
+                       exit $subshell_exit_status
+               fi
        fi
 fi
 
index 56c85a85c1e4930ae1cef3b51be4b4e5bf283773..3f80435436c11d2f36ee626450ae72697570ca8e 100644 (file)
@@ -116,7 +116,7 @@ ifeq ($(uname_S),Darwin)
        TKEXECUTABLE = $(shell basename "$(TKFRAMEWORK)" .app)
 endif
 
-ifeq ($(findstring $(MAKEFLAGS),s),s)
+ifeq ($(findstring $(firstword -$(MAKEFLAGS)),s),s)
 QUIET_GEN =
 endif
 
@@ -138,25 +138,10 @@ GITGUI_SCRIPT   := $$0
 GITGUI_RELATIVE :=
 GITGUI_MACOSXAPP :=
 
-ifeq ($(uname_O),Cygwin)
-       GITGUI_SCRIPT := `cygpath --windows --absolute "$(GITGUI_SCRIPT)"`
-
-       # Is this a Cygwin Tcl/Tk binary?  If so it knows how to do
-       # POSIX path translation just like cygpath does and we must
-       # keep libdir in POSIX format so Cygwin packages of git-gui
-       # work no matter where the user installs them.
-       #
-       ifeq ($(shell echo 'puts [file normalize /]' | '$(TCL_PATH_SQ)'),$(shell cygpath --mixed --absolute /))
-               gg_libdir_sed_in := $(gg_libdir)
-       else
-               gg_libdir_sed_in := $(shell cygpath --windows --absolute "$(gg_libdir)")
-       endif
-else
-       ifeq ($(exedir),$(gg_libdir))
-               GITGUI_RELATIVE := 1
-       endif
-       gg_libdir_sed_in := $(gg_libdir)
+ifeq ($(exedir),$(gg_libdir))
+       GITGUI_RELATIVE := 1
 endif
+gg_libdir_sed_in := $(gg_libdir)
 ifeq ($(uname_S),Darwin)
        ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y)
                GITGUI_MACOSXAPP := YesPlease
index 5ce2122fbc618384f66f4e1f08f98bca047db1c0..b460b649a8ccfec587dab03fb3c516007ab1a060 100644 (file)
@@ -88,7 +88,7 @@ that you first use `git-format-patch` to generate the emails, audit them, and
 then send them via `git-send-email`.
 
 A pretty good guide to configuring and using `git-send-email` can be found
-[here](https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/)
+[here](https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/).
 
 ### Using your email client
 
index 201524c34edac053f908c927a00270a7b1fdc09a..507fb2b6826cf6ca264290f3a6eb907b8d525c11 100755 (executable)
@@ -24,7 +24,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, see <http://www.gnu.org/licenses/>.}]
+along with this program; if not, see <https://www.gnu.org/licenses/>.}]
 
 ######################################################################
 ##
@@ -44,6 +44,132 @@ if {[catch {package require Tcl 8.5} err]
 
 catch {rename send {}} ; # What an evil concept...
 
+######################################################################
+##
+## Enabling platform-specific code paths
+
+proc is_MacOSX {} {
+       if {[tk windowingsystem] eq {aqua}} {
+               return 1
+       }
+       return 0
+}
+
+proc is_Windows {} {
+       if {$::tcl_platform(platform) eq {windows}} {
+               return 1
+       }
+       return 0
+}
+
+set _iscygwin {}
+proc is_Cygwin {} {
+       global _iscygwin
+       if {$_iscygwin eq {}} {
+               if {[string match "CYGWIN_*" $::tcl_platform(os)]} {
+                       set _iscygwin 1
+               } else {
+                       set _iscygwin 0
+               }
+       }
+       return $_iscygwin
+}
+
+######################################################################
+##
+## PATH lookup
+
+set _search_path {}
+proc _which {what args} {
+       global env _search_exe _search_path
+
+       if {$_search_path eq {}} {
+               if {[is_Windows]} {
+                       set gitguidir [file dirname [info script]]
+                       regsub -all ";" $gitguidir "\\;" gitguidir
+                       set env(PATH) "$gitguidir;$env(PATH)"
+                       set _search_path [split $env(PATH) {;}]
+                       # Skip empty `PATH` elements
+                       set _search_path [lsearch -all -inline -not -exact \
+                               $_search_path ""]
+                       set _search_exe .exe
+               } else {
+                       set _search_path [split $env(PATH) :]
+                       set _search_exe {}
+               }
+       }
+
+       if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
+               set suffix {}
+       } else {
+               set suffix $_search_exe
+       }
+
+       foreach p $_search_path {
+               set p [file join $p $what$suffix]
+               if {[file exists $p]} {
+                       return [file normalize $p]
+               }
+       }
+       return {}
+}
+
+proc sanitize_command_line {command_line from_index} {
+       set i $from_index
+       while {$i < [llength $command_line]} {
+               set cmd [lindex $command_line $i]
+               if {[llength [file split $cmd]] < 2} {
+                       set fullpath [_which $cmd]
+                       if {$fullpath eq ""} {
+                               throw {NOT-FOUND} "$cmd not found in PATH"
+                       }
+                       lset command_line $i $fullpath
+               }
+
+               # handle piped commands, e.g. `exec A | B`
+               for {incr i} {$i < [llength $command_line]} {incr i} {
+                       if {[lindex $command_line $i] eq "|"} {
+                               incr i
+                               break
+                       }
+               }
+       }
+       return $command_line
+}
+
+# Override `exec` to avoid unsafe PATH lookup
+
+rename exec real_exec
+
+proc exec {args} {
+       # skip options
+       for {set i 0} {$i < [llength $args]} {incr i} {
+               set arg [lindex $args $i]
+               if {$arg eq "--"} {
+                       incr i
+                       break
+               }
+               if {[string range $arg 0 0] ne "-"} {
+                       break
+               }
+       }
+       set args [sanitize_command_line $args $i]
+       uplevel 1 real_exec $args
+}
+
+# Override `open` to avoid unsafe PATH lookup
+
+rename open real_open
+
+proc open {args} {
+       set arg0 [lindex $args 0]
+       if {[string range $arg0 0 0] eq "|"} {
+               set command_line [string trim [string range $arg0 1 end]]
+               lset args 0 "| [sanitize_command_line $command_line 0]"
+       }
+       uplevel 1 real_open $args
+}
+
 ######################################################################
 ##
 ## locate our library
@@ -163,8 +289,6 @@ set _isbare {}
 set _gitexec {}
 set _githtmldir {}
 set _reponame {}
-set _iscygwin {}
-set _search_path {}
 set _shellpath {@@SHELL_PATH@@}
 
 set _trace [lsearch -exact $argv --trace]
@@ -211,14 +335,7 @@ proc gitexec {args} {
                if {[catch {set _gitexec [git --exec-path]} err]} {
                        error "Git not installed?\n\n$err"
                }
-               if {[is_Cygwin]} {
-                       set _gitexec [exec cygpath \
-                               --windows \
-                               --absolute \
-                               $_gitexec]
-               } else {
-                       set _gitexec [file normalize $_gitexec]
-               }
+               set _gitexec [file normalize $_gitexec]
        }
        if {$args eq {}} {
                return $_gitexec
@@ -233,14 +350,7 @@ proc githtmldir {args} {
                        # Git not installed or option not yet supported
                        return {}
                }
-               if {[is_Cygwin]} {
-                       set _githtmldir [exec cygpath \
-                               --windows \
-                               --absolute \
-                               $_githtmldir]
-               } else {
-                       set _githtmldir [file normalize $_githtmldir]
-               }
+               set _githtmldir [file normalize $_githtmldir]
        }
        if {$args eq {}} {
                return $_githtmldir
@@ -252,40 +362,6 @@ proc reponame {} {
        return $::_reponame
 }
 
-proc is_MacOSX {} {
-       if {[tk windowingsystem] eq {aqua}} {
-               return 1
-       }
-       return 0
-}
-
-proc is_Windows {} {
-       if {$::tcl_platform(platform) eq {windows}} {
-               return 1
-       }
-       return 0
-}
-
-proc is_Cygwin {} {
-       global _iscygwin
-       if {$_iscygwin eq {}} {
-               if {$::tcl_platform(platform) eq {windows}} {
-                       if {[catch {set p [exec cygpath --windir]} err]} {
-                               set _iscygwin 0
-                       } else {
-                               set _iscygwin 1
-                               # Handle MSys2 which is only cygwin when MSYSTEM is MSYS.
-                               if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} {
-                                       set _iscygwin 0
-                               }
-                       }
-               } else {
-                       set _iscygwin 0
-               }
-       }
-       return $_iscygwin
-}
-
 proc is_enabled {option} {
        global enabled_options
        if {[catch {set on $enabled_options($option)}]} {return 0}
@@ -448,44 +524,6 @@ proc _git_cmd {name} {
        return $v
 }
 
-proc _which {what args} {
-       global env _search_exe _search_path
-
-       if {$_search_path eq {}} {
-               if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
-                       set _search_path [split [exec cygpath \
-                               --windows \
-                               --path \
-                               --absolute \
-                               $env(PATH)] {;}]
-                       set _search_exe .exe
-               } elseif {[is_Windows]} {
-                       set gitguidir [file dirname [info script]]
-                       regsub -all ";" $gitguidir "\\;" gitguidir
-                       set env(PATH) "$gitguidir;$env(PATH)"
-                       set _search_path [split $env(PATH) {;}]
-                       set _search_exe .exe
-               } else {
-                       set _search_path [split $env(PATH) :]
-                       set _search_exe {}
-               }
-       }
-
-       if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
-               set suffix {}
-       } else {
-               set suffix $_search_exe
-       }
-
-       foreach p $_search_path {
-               set p [file join $p $what$suffix]
-               if {[file exists $p]} {
-                       return [file normalize $p]
-               }
-       }
-       return {}
-}
-
 # Test a file for a hashbang to identify executable scripts on Windows.
 proc is_shellscript {filename} {
        if {![file exists $filename]} {return 0}
@@ -623,31 +661,8 @@ proc git_write {args} {
 }
 
 proc githook_read {hook_name args} {
-       set pchook [gitdir hooks $hook_name]
-       lappend args 2>@1
-
-       # On Windows [file executable] might lie so we need to ask
-       # the shell if the hook is executable.  Yes that's annoying.
-       #
-       if {[is_Windows]} {
-               upvar #0 _sh interp
-               if {![info exists interp]} {
-                       set interp [_which sh]
-               }
-               if {$interp eq {}} {
-                       error "hook execution requires sh (not in PATH)"
-               }
-
-               set scr {if test -x "$1";then exec "$@";fi}
-               set sh_c [list $interp -c $scr $interp $pchook]
-               return [_open_stdout_stderr [concat $sh_c $args]]
-       }
-
-       if {[file executable $pchook]} {
-               return [_open_stdout_stderr [concat [list $pchook] $args]]
-       }
-
-       return {}
+       set cmd [concat git hook run --ignore-missing $hook_name -- $args 2>@1]
+       return [_open_stdout_stderr $cmd]
 }
 
 proc kill_file_process {fd} {
@@ -1259,9 +1274,6 @@ if {$_gitdir eq "."} {
        set _gitdir [pwd]
 }
 
-if {![file isdirectory $_gitdir] && [is_Cygwin]} {
-       catch {set _gitdir [exec cygpath --windows $_gitdir]}
-}
 if {![file isdirectory $_gitdir]} {
        catch {wm withdraw .}
        error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
@@ -1273,11 +1285,7 @@ apply_config
 
 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
 if {[package vcompare $_git_version 1.7.0] >= 0} {
-       if { [is_Cygwin] } {
-               catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]}
-       } else {
-               set _gitworktree [git rev-parse --show-toplevel]
-       }
+       set _gitworktree [git rev-parse --show-toplevel]
 } else {
        # try to set work tree from environment, core.worktree or use
        # cdup to obtain a relative path to the top of the worktree. If
@@ -1502,24 +1510,8 @@ proc rescan {after {honor_trustmtime 1}} {
        }
 }
 
-if {[is_Cygwin]} {
-       set is_git_info_exclude {}
-       proc have_info_exclude {} {
-               global is_git_info_exclude
-
-               if {$is_git_info_exclude eq {}} {
-                       if {[catch {exec test -f [gitdir info exclude]}]} {
-                               set is_git_info_exclude 0
-                       } else {
-                               set is_git_info_exclude 1
-                       }
-               }
-               return $is_git_info_exclude
-       }
-} else {
-       proc have_info_exclude {} {
-               return [file readable [gitdir info exclude]]
-       }
+proc have_info_exclude {} {
+       return [file readable [gitdir info exclude]]
 }
 
 proc rescan_stage2 {fd after} {
@@ -2259,7 +2251,9 @@ proc do_git_gui {} {
 
 # Get the system-specific explorer app/command.
 proc get_explorer {} {
-       if {[is_Cygwin] || [is_Windows]} {
+       if {[is_Cygwin]} {
+               set explorer "/bin/cygstart.exe --explore"
+       } elseif {[is_Windows]} {
                set explorer "explorer.exe"
        } elseif {[is_MacOSX]} {
                set explorer "open"
@@ -2373,7 +2367,7 @@ proc do_quit {{rc {1}}} {
        set ret_code $rc
 
        # Briefly enable send again, working around Tk bug
-       # http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
+       # https://sourceforge.net/p/tktoolkit/bugs/2343/
        tk appname [appname]
 
        destroy .
@@ -3053,16 +3047,12 @@ if {[is_MacOSX]} {
 set doc_path [githtmldir]
 if {$doc_path ne {}} {
        set doc_path [file join $doc_path index.html]
-
-       if {[is_Cygwin]} {
-               set doc_path [exec cygpath --mixed $doc_path]
-       }
 }
 
 if {[file isfile $doc_path]} {
        set doc_url "file:$doc_path"
 } else {
-       set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
+       set doc_url {https://www.kernel.org/pub/software/scm/git/docs/}
 }
 
 proc start_browser {url} {
@@ -4028,60 +4018,6 @@ set file_lists($ui_workdir) [list]
 wm title . "[appname] ([reponame]) [file normalize $_gitworktree]"
 focus -force $ui_comm
 
-# -- Warn the user about environmental problems.  Cygwin's Tcl
-#    does *not* pass its env array onto any processes it spawns.
-#    This means that git processes get none of our environment.
-#
-if {[is_Cygwin]} {
-       set ignored_env 0
-       set suggest_user {}
-       set msg [mc "Possible environment issues exist.
-
-The following environment variables are probably
-going to be ignored by any Git subprocess run
-by %s:
-
-" [appname]]
-       foreach name [array names env] {
-               switch -regexp -- $name {
-               {^GIT_INDEX_FILE$} -
-               {^GIT_OBJECT_DIRECTORY$} -
-               {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
-               {^GIT_DIFF_OPTS$} -
-               {^GIT_EXTERNAL_DIFF$} -
-               {^GIT_PAGER$} -
-               {^GIT_TRACE$} -
-               {^GIT_CONFIG$} -
-               {^GIT_(AUTHOR|COMMITTER)_DATE$} {
-                       append msg " - $name\n"
-                       incr ignored_env
-               }
-               {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
-                       append msg " - $name\n"
-                       incr ignored_env
-                       set suggest_user $name
-               }
-               }
-       }
-       if {$ignored_env > 0} {
-               append msg [mc "
-This is due to a known issue with the
-Tcl binary distributed by Cygwin."]
-
-               if {$suggest_user ne {}} {
-                       append msg [mc "
-
-A good replacement for %s
-is placing values for the user.name and
-user.email settings into your personal
-~/.gitconfig file.
-" $suggest_user]
-               }
-               warn_popup $msg
-       }
-       unset ignored_env msg suggest_user name
-}
-
 # -- Only initialize complex UI if we are going to stay running.
 #
 if {[is_enabled transport]} {
index af1fee7c751dc635bcc9c621161dc3558f297cb8..d23abedcb36fd93ab3f12694d607bf354d6cf208 100644 (file)
@@ -174,9 +174,6 @@ constructor pick {} {
                        -foreground blue \
                        -underline 1
                set home $::env(HOME)
-               if {[is_Cygwin]} {
-                       set home [exec cygpath --windows --absolute $home]
-               }
                set home "[file normalize $home]/"
                set hlen [string length $home]
                foreach p $sorted_recent {
@@ -374,18 +371,6 @@ proc _objdir {path} {
                return $objdir
        }
 
-       if {[is_Cygwin]} {
-               set objdir [file join $path .git objects.lnk]
-               if {[file isfile $objdir]} {
-                       return [win32_read_lnk $objdir]
-               }
-
-               set objdir [file join $path objects.lnk]
-               if {[file isfile $objdir]} {
-                       return [win32_read_lnk $objdir]
-               }
-       }
-
        return {}
 }
 
@@ -623,12 +608,6 @@ method _do_clone2 {} {
        }
 
        set giturl $origin_url
-       if {[is_Cygwin] && [file isdirectory $giturl]} {
-               set giturl [exec cygpath --unix --absolute $giturl]
-               if {$clone_type eq {shared}} {
-                       set objdir [exec cygpath --unix --absolute $objdir]
-               }
-       }
 
        if {[file exists $local_path]} {
                error_popup [mc "Location %s already exists." $local_path]
@@ -668,11 +647,7 @@ method _do_clone2 {} {
                                fconfigure $f_cp -translation binary -encoding binary
                                cd $objdir
                                while {[gets $f_in line] >= 0} {
-                                       if {[is_Cygwin]} {
-                                               puts $f_cp [exec cygpath --unix --absolute $line]
-                                       } else {
-                                               puts $f_cp [file normalize $line]
-                                       }
+                                       puts $f_cp [file normalize $line]
                                }
                                close $f_in
                                close $f_cp
index 32668fc9c6debee6de9882719c305392c1e4791a..d2e0fa60c3ba3f770f525a8d01c66f17826aea75 100644 (file)
@@ -3,7 +3,7 @@
 # (Copied from gitk, commit fd8ccbec4f0161)
 
 # This list of encoding names and aliases is distilled from
-# http://www.iana.org/assignments/character-sets.
+# https://www.iana.org/assignments/character-sets.
 # Not all of them are supported by Tcl.
 set encoding_aliases {
     { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
index 97d1d7aa02686606912dd5ff7b512343083a17c0..674a41f5e0c868b70d84202381fec8b5919f962f 100644 (file)
@@ -27,13 +27,10 @@ proc do_windows_shortcut {} {
 }
 
 proc do_cygwin_shortcut {} {
-       global argv0 _gitworktree
+       global argv0 _gitworktree oguilib
 
        if {[catch {
                set desktop [exec cygpath \
-                       --windows \
-                       --absolute \
-                       --long-name \
                        --desktop]
                }]} {
                        set desktop .
@@ -48,19 +45,19 @@ proc do_cygwin_shortcut {} {
                        set fn ${fn}.lnk
                }
                if {[catch {
-                               set sh [exec cygpath \
-                                       --windows \
-                                       --absolute \
-                                       /bin/sh.exe]
-                               set me [exec cygpath \
-                                       --unix \
-                                       --absolute \
-                                       $argv0]
-                               win32_create_lnk $fn [list \
-                                       $sh -c \
-                                       "CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \
-                                       ] \
-                                       [file normalize $_gitworktree]
+                               set repodir [file normalize $_gitworktree]
+                               set shargs {-c \
+                                       "CHERE_INVOKING=1 \
+                                       source /etc/profile; \
+                                       git gui"}
+                               exec /bin/mkshortcut.exe \
+                                       --arguments $shargs \
+                                       --desc "git-gui on $repodir" \
+                                       --icon $oguilib/git-gui.ico \
+                                       --name $fn \
+                                       --show min \
+                                       --workingdir $repodir \
+                                       /bin/sh.exe
                        } err]} {
                        error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
                }
index 2514bc22abf4aedfaa8d641976c96fbf222bb8a7..116233100d781a61303c1f63be153e50c52f90a1 100644 (file)
@@ -39,7 +39,7 @@ in your language?
 If you do not know what your language should be named, you need to find
 it.  This currently follows ISO 639-1 two letter codes:
 
-       http://www.loc.gov/standards/iso639-2/php/code_list.php
+       https://www.loc.gov/standards/iso639-2/php/code_list.php
 
 For example, if you are preparing a translation for Afrikaans, the
 language code is "af".  If there already is a translation for your
index c68f49454cd83f48ce3452df178766606effbad9..994431c887277410e50b8a592febf3a1e855469a 100755 (executable)
@@ -432,7 +432,7 @@ mongoose_conf() {
 # Mongoose web server configuration file.
 # Lines starting with '#' and empty lines are ignored.
 # For detailed description of every option, visit
-# http://code.google.com/p/mongoose/wiki/MongooseManual
+# https://code.google.com/p/mongoose/wiki/MongooseManual
 
 root           $root
 ports          $port
@@ -458,7 +458,7 @@ plackup_conf () {
 #!$PERL
 
 # gitweb - simple web interface to track changes in git repositories
-#          PSGI wrapper and server starter (see http://plackperl.org)
+#          PSGI wrapper and server starter (see https://plackperl.org)
 
 use strict;
 
index 9f99201bcca1eada84c3f19d1ca44cb41b3f3c59..1ff26170ffcff8e3ebe768ecc946b1c7deb53949 100644 (file)
@@ -97,7 +97,42 @@ merge_mode () {
        test "$TOOL_MODE" = merge
 }
 
+get_gui_default () {
+       if diff_mode
+       then
+               GUI_DEFAULT_KEY="difftool.guiDefault"
+       else
+               GUI_DEFAULT_KEY="mergetool.guiDefault"
+       fi
+       GUI_DEFAULT_CONFIG_LCASE=$(git config --default false --get "$GUI_DEFAULT_KEY" | tr 'A-Z' 'a-z')
+       if test "$GUI_DEFAULT_CONFIG_LCASE" = "auto"
+       then
+               if test -n "$DISPLAY"
+               then
+                       GUI_DEFAULT=true
+               else
+                       GUI_DEFAULT=false
+               fi
+       else
+               GUI_DEFAULT=$(git config --default false --bool --get "$GUI_DEFAULT_KEY")
+               subshell_exit_status=$?
+               if test $subshell_exit_status -ne 0
+               then
+                       exit $subshell_exit_status
+               fi
+       fi
+       echo $GUI_DEFAULT
+}
+
 gui_mode () {
+       if test -z "$GIT_MERGETOOL_GUI"
+       then
+               GIT_MERGETOOL_GUI=$(get_gui_default)
+               if test $? -ne 0
+               then
+                       exit 2
+               fi
+       fi
        test "$GIT_MERGETOOL_GUI" = true
 }
 
@@ -467,6 +502,11 @@ get_merge_tool () {
        is_guessed=false
        # Check if a merge tool has been configured
        merge_tool=$(get_configured_merge_tool)
+       subshell_exit_status=$?
+       if test $subshell_exit_status -gt "1"
+       then
+               exit $subshell_exit_status
+       fi
        # Try to guess an appropriate merge tool if no tool has been set.
        if test -z "$merge_tool"
        then
index f751d9cfe2090485914559d467578e8a68cc823c..8a922893f75f220a8fce59a605405aceeaf91d7e 100755 (executable)
@@ -451,7 +451,7 @@ print_noop_and_exit () {
 
 main () {
        prompt=$(git config --bool mergetool.prompt)
-       GIT_MERGETOOL_GUI=false
+       GIT_MERGETOOL_GUI=
        guessed_merge_tool=false
        orderfile=
 
@@ -511,9 +511,14 @@ main () {
 
        if test -z "$merge_tool"
        then
-               if ! merge_tool=$(get_merge_tool)
+               merge_tool=$(get_merge_tool)
+               subshell_exit_status=$?
+               if test $subshell_exit_status = 1
                then
                        guessed_merge_tool=true
+               elif test $subshell_exit_status -gt 1
+               then
+                       exit $subshell_exit_status
                fi
        fi
        merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
index d26a980e5acb66eda31d32e075d90736bcac4e3a..3ea1c405e55ac9e0081f4b5c544dbc97d04fe507 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1522,6 +1522,10 @@ class LargeFileSystem(object):
            file is stored in the large file system and handles all necessary
            steps.
            """
+        # symlinks aren't processed by smudge/clean filters
+        if git_mode == "120000":
+            return (git_mode, contents)
+
         if self.exceedsLargeFileThreshold(relPath, contents) or self.hasLargeFileExtension(relPath):
             contentTempFile = self.generateTempFile(contents)
             pointer_git_mode, contents, localLargeFile = self.generatePointer(contentTempFile)
@@ -4247,7 +4251,8 @@ class P4Sync(Command, P4UserMap):
         if self.tempBranches != []:
             for branch in self.tempBranches:
                 read_pipe(["git", "update-ref", "-d", branch])
-            os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
+            if len(read_pipe(["git", "for-each-ref", self.tempBranchLocation])) > 0:
+                   die("There are unexpected temporary branches")
 
         # Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
         # a convenient shortcut refname "p4".
index 2d0e44656cc6ca4cc0cd8b579a1ebd8798e90b5f..01640a044bb10f4167338fca957c43a240e1b6c0 100755 (executable)
@@ -153,7 +153,7 @@ for you to fetch changes up to %H:
 if test $(git cat-file -t "$head") = tag
 then
        git cat-file tag "$head" |
-       sed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p
+       sed -n -e '1,/^$/d' -e '/^-----BEGIN \(PGP\|SSH\|SIGNED\) /q' -e p
        echo
        echo "----------------------------------------------------------------"
 fi &&
index 5861e99a6eb2a16bb45e759645e124a66a50e107..821b2b3a135ab6f4ab0085ac6acbea1d4eb1c320 100755 (executable)
@@ -16,7 +16,7 @@
 #    and second line is the subject of the message.
 #
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Getopt::Long;
@@ -26,22 +26,10 @@ use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
-package FakeTerm;
-sub new {
-       my ($class, $reason) = @_;
-       return bless \$reason, shift;
-}
-sub readline {
-       my $self = shift;
-       die "Cannot use readline on FakeTerm: $$self";
-}
-package main;
-
-
 sub usage {
        print <<EOT;
-git send-email' [<options>] <file|directory>
-git send-email' [<options>] <format-patch options>
+git send-email [<options>] <file|directory>
+git send-email [<options>] <format-patch options>
 git send-email --dump-aliases
 
   Composing:
@@ -87,8 +75,10 @@ git send-email --dump-aliases
 
   Automating:
     --identity              <str>  * Use the sendemail.<id> options.
-    --to-cmd                <str>  * Email To: via `<str> \$patch_path`
-    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
+    --to-cmd                <str>  * Email To: via `<str> \$patch_path`.
+    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`.
+    --header-cmd            <str>  * Add headers via `<str> \$patch_path`.
+    --no-header-cmd                * Disable any header command in use.
     --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
     --[no-]cc-cover                * Email Cc: addresses in the cover letter.
     --[no-]to-cover                * Email To: addresses in the cover letter.
@@ -129,13 +119,16 @@ sub completion_helper {
 
        foreach my $key (keys %$original_opts) {
                unless (exists $not_for_completion{$key}) {
-                       $key =~ s/!$//;
+                       my $negatable = ($key =~ s/!$//);
 
                        if ($key =~ /[:=][si]$/) {
                                $key =~ s/[:=][si]$//;
                                push (@send_email_opts, "--$_=") foreach (split (/\|/, $key));
                        } else {
                                push (@send_email_opts, "--$_") foreach (split (/\|/, $key));
+                               if ($negatable) {
+                                       push (@send_email_opts, "--no-$_") foreach (split (/\|/, $key));
+                               }
                        }
                }
        }
@@ -202,7 +195,7 @@ my (@to,@cc,@xh,$envelope_sender,
        $author,$sender,$smtp_authpass,$annotate,$compose,$time);
 # Things we either get from config, *or* are overridden on the
 # command-line.
-my ($no_cc, $no_to, $no_bcc, $no_identity);
+my ($no_cc, $no_to, $no_bcc, $no_identity, $no_header_cmd);
 my (@config_to, @getopt_to);
 my (@config_cc, @getopt_cc);
 my (@config_bcc, @getopt_bcc);
@@ -220,6 +213,10 @@ my $compose_filename;
 my $force = 0;
 my $dump_aliases = 0;
 
+# Variables to prevent short format-patch options from being captured
+# as abbreviated send-email options
+my $reroll_count;
+
 # Handle interactive edition of files.
 my $multiedit;
 my $editor;
@@ -234,7 +231,7 @@ sub system_or_msg {
        my @sprintf_args = ($cmd_name ? $cmd_name : $args->[0], $exit_code);
        if (defined $msg) {
                # Quiet the 'redundant' warning category, except we
-               # need to support down to Perl 5.8, so we can't do a
+               # need to support down to Perl 5.8.1, so we can't do a
                # "no warnings 'redundant'", since that category was
                # introduced in perl 5.22, and asking for it will die
                # on older perls.
@@ -265,7 +262,7 @@ sub do_edit {
 # Variables with corresponding config settings
 my ($suppress_from, $signed_off_by_cc);
 my ($cover_cc, $cover_to);
-my ($to_cmd, $cc_cmd);
+my ($to_cmd, $cc_cmd, $header_cmd);
 my ($smtp_server, $smtp_server_port, @smtp_server_options);
 my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
 my ($batch_size, $relogin_delay);
@@ -314,6 +311,7 @@ my %config_settings = (
     "tocmd" => \$to_cmd,
     "cc" => \@config_cc,
     "cccmd" => \$cc_cmd,
+    "headercmd" => \$header_cmd,
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@config_bcc,
     "suppresscc" => \@suppress_cc,
@@ -496,7 +494,6 @@ my %options = (
                    "bcc=s" => \@getopt_bcc,
                    "no-bcc" => \$no_bcc,
                    "chain-reply-to!" => \$chain_reply_to,
-                   "no-chain-reply-to" => sub {$chain_reply_to = 0},
                    "sendmail-cmd=s" => \$sendmail_cmd,
                    "smtp-server=s" => \$smtp_server,
                    "smtp-server-option=s" => \@smtp_server_options,
@@ -511,37 +508,31 @@ my %options = (
                    "smtp-auth=s" => \$smtp_auth,
                    "no-smtp-auth" => sub {$smtp_auth = 'none'},
                    "annotate!" => \$annotate,
-                   "no-annotate" => sub {$annotate = 0},
                    "compose" => \$compose,
                    "quiet" => \$quiet,
                    "cc-cmd=s" => \$cc_cmd,
+                   "header-cmd=s" => \$header_cmd,
+                   "no-header-cmd" => \$no_header_cmd,
                    "suppress-from!" => \$suppress_from,
-                   "no-suppress-from" => sub {$suppress_from = 0},
                    "suppress-cc=s" => \@suppress_cc,
                    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
-                   "no-signed-off-cc|no-signed-off-by-cc" => sub {$signed_off_by_cc = 0},
-                   "cc-cover|cc-cover!" => \$cover_cc,
-                   "no-cc-cover" => sub {$cover_cc = 0},
-                   "to-cover|to-cover!" => \$cover_to,
-                   "no-to-cover" => sub {$cover_to = 0},
+                   "cc-cover!" => \$cover_cc,
+                   "to-cover!" => \$cover_to,
                    "confirm=s" => \$confirm,
                    "dry-run" => \$dry_run,
                    "envelope-sender=s" => \$envelope_sender,
                    "thread!" => \$thread,
-                   "no-thread" => sub {$thread = 0},
                    "validate!" => \$validate,
-                   "no-validate" => sub {$validate = 0},
                    "transfer-encoding=s" => \$target_xfer_encoding,
                    "format-patch!" => \$format_patch,
-                   "no-format-patch" => sub {$format_patch = 0},
                    "8bit-encoding=s" => \$auto_8bit_encoding,
                    "compose-encoding=s" => \$compose_encoding,
                    "force" => \$force,
                    "xmailer!" => \$use_xmailer,
-                   "no-xmailer" => sub {$use_xmailer = 0},
                    "batch-size=i" => \$batch_size,
                    "relogin-delay=i" => \$relogin_delay,
                    "git-completion-helper" => \$git_completion_helper,
+                   "v=s" => \$reroll_count,
 );
 $rc = GetOptions(%options);
 
@@ -782,19 +773,27 @@ if (@rev_list_opts) {
        die __("Cannot run git format-patch from outside a repository\n")
                unless $repo;
        require File::Temp;
-       push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1), @rev_list_opts);
+       push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1),
+                                   defined $reroll_count ? ('-v', $reroll_count) : (),
+                                   @rev_list_opts);
 }
 
-@files = handle_backup_files(@files);
-
-if ($validate) {
-       foreach my $f (@files) {
-               unless (-p $f) {
-                       validate_patch($f, $target_xfer_encoding);
-               }
-       }
+if (defined $sender) {
+       $sender =~ s/^\s+|\s+$//g;
+       ($sender) = expand_aliases($sender);
+} else {
+       $sender = $repoauthor->() || $repocommitter->() || '';
 }
 
+# $sender could be an already sanitized address
+# (e.g. sendemail.from could be manually sanitized by user).
+# But it's a no-op to run sanitize_address on an already sanitized address.
+$sender = sanitize_address($sender);
+
+$time = time - scalar $#files;
+
+@files = handle_backup_files(@files);
+
 if (@files) {
        unless ($quiet) {
                print $_,"\n" for (@files);
@@ -831,6 +830,9 @@ if ($compose) {
        my $tpl_subject = $initial_subject || '';
        my $tpl_in_reply_to = $initial_in_reply_to || '';
        my $tpl_reply_to = $reply_to || '';
+       my $tpl_to = join(',', @initial_to);
+       my $tpl_cc = join(',', @initial_cc);
+       my $tpl_bcc = join(', ', @initial_bcc);
 
        print $c <<EOT1, Git::prefix_lines("GIT: ", __(<<EOT2)), <<EOT3;
 From $tpl_sender # This line is ignored.
@@ -842,6 +844,9 @@ for the patch you are writing.
 Clear the body content if you don't wish to send a summary.
 EOT2
 From: $tpl_sender
+To: $tpl_to
+Cc: $tpl_cc
+Bcc: $tpl_bcc
 Reply-To: $tpl_reply_to
 Subject: $tpl_subject
 In-Reply-To: $tpl_in_reply_to
@@ -858,88 +863,82 @@ EOT3
                do_edit($compose_filename);
        }
 
+       open my $c2, ">", $compose_filename . ".final"
+               or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
+
        open $c, "<", $compose_filename
                or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!);
 
+       my $need_8bit_cte = file_has_nonascii($compose_filename);
+       my $in_body = 0;
+       my $summary_empty = 1;
        if (!defined $compose_encoding) {
                $compose_encoding = "UTF-8";
        }
-
-       my %parsed_email;
-       while (my $line = <$c>) {
-               next if $line =~ m/^GIT:/;
-               parse_header_line($line, \%parsed_email);
-               if ($line =~ /^$/) {
-                       $parsed_email{'body'} = filter_body($c);
+       while(<$c>) {
+               next if m/^GIT:/;
+               if ($in_body) {
+                       $summary_empty = 0 unless (/^\n$/);
+               } elsif (/^\n$/) {
+                       $in_body = 1;
+                       if ($need_8bit_cte) {
+                               print $c2 "MIME-Version: 1.0\n",
+                                        "Content-Type: text/plain; ",
+                                          "charset=$compose_encoding\n",
+                                        "Content-Transfer-Encoding: 8bit\n";
+                       }
+               } elsif (/^MIME-Version:/i) {
+                       $need_8bit_cte = 0;
+               } elsif (/^Subject:\s*(.+)\s*$/i) {
+                       $initial_subject = $1;
+                       my $subject = $initial_subject;
+                       $_ = "Subject: " .
+                               quote_subject($subject, $compose_encoding) .
+                               "\n";
+               } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
+                       $initial_in_reply_to = $1;
+                       next;
+               } elsif (/^Reply-To:\s*(.+)\s*$/i) {
+                       $reply_to = $1;
+               } elsif (/^From:\s*(.+)\s*$/i) {
+                       $sender = $1;
+                       next;
+               } elsif (/^To:\s*(.+)\s*$/i) {
+                       @initial_to = parse_address_line($1);
+                       next;
+               } elsif (/^Cc:\s*(.+)\s*$/i) {
+                       @initial_cc = parse_address_line($1);
+                       next;
+               } elsif (/^Bcc:/i) {
+                       @initial_bcc = parse_address_line($1);
+                       next;
                }
+               print $c2 $_;
        }
        close $c;
+       close $c2;
 
-       open my $c2, ">", $compose_filename . ".final"
-       or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
-
-
-       if ($parsed_email{'From'}) {
-               $sender = delete($parsed_email{'From'});
-       }
-       if ($parsed_email{'In-Reply-To'}) {
-               $initial_in_reply_to = delete($parsed_email{'In-Reply-To'});
-       }
-       if ($parsed_email{'Reply-To'}) {
-               $reply_to = delete($parsed_email{'Reply-To'});
-       }
-       if ($parsed_email{'Subject'}) {
-               $initial_subject = delete($parsed_email{'Subject'});
-               print $c2 "Subject: " .
-                       quote_subject($initial_subject, $compose_encoding) .
-                       "\n";
-       }
-
-       if ($parsed_email{'MIME-Version'}) {
-               print $c2 "MIME-Version: $parsed_email{'MIME-Version'}\n",
-                               "Content-Type: $parsed_email{'Content-Type'};\n",
-                               "Content-Transfer-Encoding: $parsed_email{'Content-Transfer-Encoding'}\n";
-               delete($parsed_email{'MIME-Version'});
-               delete($parsed_email{'Content-Type'});
-               delete($parsed_email{'Content-Transfer-Encoding'});
-       } elsif (file_has_nonascii($compose_filename)) {
-               my $content_type = (delete($parsed_email{'Content-Type'}) or
-                       "text/plain; charset=$compose_encoding");
-               print $c2 "MIME-Version: 1.0\n",
-                       "Content-Type: $content_type\n",
-                       "Content-Transfer-Encoding: 8bit\n";
-       }
-       # Preserve unknown headers
-       foreach my $key (keys %parsed_email) {
-               next if $key eq 'body';
-               print $c2 "$key: $parsed_email{$key}";
-       }
-
-       if ($parsed_email{'body'}) {
-               print $c2 "\n$parsed_email{'body'}\n";
-               delete($parsed_email{'body'});
-       } else {
+       if ($summary_empty) {
                print __("Summary email is empty, skipping it\n");
                $compose = -1;
        }
-
-       close $c2;
-
 } elsif ($annotate) {
        do_edit(@files);
 }
 
-sub term {
-       my $term = eval {
+{
+       # Only instantiate one $term per program run, since some
+       # Term::ReadLine providers refuse to create a second instance.
+       my $term;
+       sub term {
                require Term::ReadLine;
-               $ENV{"GIT_SEND_EMAIL_NOTTY"}
-                       ? Term::ReadLine->new('git-send-email', \*STDIN, \*STDOUT)
-                       : Term::ReadLine->new('git-send-email');
-       };
-       if ($@) {
-               $term = FakeTerm->new("$@: going non-interactive");
+               if (!defined $term) {
+                       $term = $ENV{"GIT_SEND_EMAIL_NOTTY"}
+                               ? Term::ReadLine->new('git-send-email', \*STDIN, \*STDOUT)
+                               : Term::ReadLine->new('git-send-email');
+               }
+               return $term;
        }
-       return $term;
 }
 
 sub ask {
@@ -977,32 +976,6 @@ sub ask {
        return;
 }
 
-sub parse_header_line {
-       my $lines = shift;
-       my $parsed_line = shift;
-       my $addr_pat = join "|", qw(To Cc Bcc);
-
-       foreach (split(/\n/, $lines)) {
-               if (/^($addr_pat):\s*(.+)$/i) {
-                       $parsed_line->{$1} = [ parse_address_line($2) ];
-               } elsif (/^([^:]*):\s*(.+)\s*$/i) {
-                       $parsed_line->{$1} = $2;
-               }
-       }
-}
-
-sub filter_body {
-       my $c = shift;
-       my $body = "";
-       while (my $body_line = <$c>) {
-               if ($body_line !~ m/^GIT:/) {
-                       $body .= $body_line;
-               }
-       }
-       return $body;
-}
-
-
 my %broken_encoding;
 
 sub file_declares_8bit_cte {
@@ -1043,18 +1016,6 @@ if (!$force) {
        }
 }
 
-if (defined $sender) {
-       $sender =~ s/^\s+|\s+$//g;
-       ($sender) = expand_aliases($sender);
-} else {
-       $sender = $repoauthor->() || $repocommitter->() || '';
-}
-
-# $sender could be an already sanitized address
-# (e.g. sendemail.from could be manually sanitized by user).
-# But it's a no-op to run sanitize_address on an already sanitized address.
-$sender = sanitize_address($sender);
-
 my $to_whom = __("To whom should the emails be sent (if anyone)?");
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
@@ -1146,10 +1107,10 @@ sub extract_valid_address {
 
 sub extract_valid_address_or_die {
        my $address = shift;
-       $address = extract_valid_address($address);
+       my $valid_address = extract_valid_address($address);
        die sprintf(__("error: unable to extract a valid address from: %s\n"), $address)
-               if !$address;
-       return $address;
+               if !$valid_address;
+       return $valid_address;
 }
 
 sub validate_address {
@@ -1214,10 +1175,6 @@ sub make_message_id {
        #print "new message id = $message_id\n"; # Was useful for debugging
 }
 
-
-
-$time = time - scalar $#files;
-
 sub unquote_rfc2047 {
        local ($_) = @_;
        my $charset;
@@ -1495,16 +1452,7 @@ sub file_name_is_absolute {
        return File::Spec::Functions::file_name_is_absolute($path);
 }
 
-# Prepares the email, then asks the user what to do.
-#
-# If the user chooses to send the email, it's sent and 1 is returned.
-# If the user chooses not to send the email, 0 is returned.
-# If the user decides they want to make further edits, -1 is returned and the
-# caller is expected to call send_message again after the edits are performed.
-#
-# If an error occurs sending the email, this just dies.
-
-sub send_message {
+sub gen_header {
        my @recipients = unique_email_list(@to);
        @cc = (grep { my $cc = extract_valid_address_or_die($_);
                      not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
@@ -1530,7 +1478,7 @@ sub send_message {
 To: $to${ccline}
 Subject: $subject
 Date: $date
-Message-Id: $message_id
+Message-ID: $message_id
 ";
        if ($use_xmailer) {
                $header .= "X-Mailer: git-send-email $gitversion\n";
@@ -1546,6 +1494,22 @@ Message-Id: $message_id
        if (@xh) {
                $header .= join("\n", @xh) . "\n";
        }
+       my $recipients_ref = \@recipients;
+       return ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header);
+}
+
+# Prepares the email, then asks the user what to do.
+#
+# If the user chooses to send the email, it's sent and 1 is returned.
+# If the user chooses not to send the email, 0 is returned.
+# If the user decides they want to make further edits, -1 is returned and the
+# caller is expected to call send_message again after the edits are performed.
+#
+# If an error occurs sending the email, this just dies.
+
+sub send_message {
+       my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header();
+       my @recipients = @$recipients_ref;
 
        my @sendmail_parameters = ('-i', @recipients);
        my $raw_from = $sender;
@@ -1731,15 +1695,8 @@ EOF
        return 1;
 }
 
-$in_reply_to = $initial_in_reply_to;
-$references = $initial_in_reply_to || '';
-$message_num = 0;
-
-# Prepares the email, prompts the user, sends it out
-# Returns 0 if an edit was done and the function should be called again, or 1
-# otherwise.
-sub process_file {
-       my ($t) = @_;
+sub pre_process_file {
+       my ($t, $quiet) = @_;
 
        open my $fh, "<", $t or die sprintf(__("can't open file %s"), $t);
 
@@ -1758,16 +1715,17 @@ sub process_file {
        $subject = $initial_subject;
        $message = "";
        $message_num++;
-       # First unfold multiline header fields
+       undef $message_id;
+       # Retrieve and unfold header fields.
+       my @header_lines = ();
        while(<$fh>) {
                last if /^\s*$/;
-               if (/^\s+\S/ and @header) {
-                       chomp($header[$#header]);
-                       s/^\s+/ /;
-                       $header[$#header] .= $_;
-           } else {
-                       push(@header, $_);
-               }
+               push(@header_lines, $_);
+       }
+       @header = unfold_headers(@header_lines);
+       # Add computed headers, if applicable.
+       unless ($no_header_cmd || ! $header_cmd) {
+               push @header, invoke_header_cmd($header_cmd, $t);
        }
        # Now parse the header
        foreach(@header) {
@@ -1825,7 +1783,7 @@ sub process_file {
                                $has_mime_version = 1;
                                push @xh, $_;
                        }
-                       elsif (/^Message-Id: (.*)/i) {
+                       elsif (/^Message-ID: (.*)/i) {
                                $message_id = $1;
                        }
                        elsif (/^Content-Transfer-Encoding: (.*)/i) {
@@ -1893,9 +1851,9 @@ sub process_file {
        }
        close $fh;
 
-       push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t)
+       push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t, $quiet)
                if defined $to_cmd;
-       push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t)
+       push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t, $quiet)
                if defined $cc_cmd && !$suppress_cc{'cccmd'};
 
        if ($broken_encoding{$t} && !$has_content_type) {
@@ -1954,6 +1912,15 @@ sub process_file {
                        @initial_to = @to;
                }
        }
+}
+
+# Prepares the email, prompts the user, and sends it out
+# Returns 0 if an edit was done and the function should be called again, or 1
+# on the email being successfully sent out.
+sub process_file {
+       my ($t) = @_;
+
+        pre_process_file($t, $quiet);
 
        my $message_was_sent = send_message();
        if ($message_was_sent == -1) {
@@ -1993,21 +1960,102 @@ sub process_file {
        return 1;
 }
 
+sub initialize_modified_loop_vars {
+       $in_reply_to = $initial_in_reply_to;
+       $references = $initial_in_reply_to || '';
+       $message_num = 0;
+}
+
+if ($validate) {
+       # FIFOs can only be read once, exclude them from validation.
+       my @real_files = ();
+       foreach my $f (@files) {
+               unless (-p $f) {
+                       push(@real_files, $f);
+               }
+       }
+
+       # Run the loop once again to avoid gaps in the counter due to FIFO
+       # arguments provided by the user.
+       my $num = 1;
+       my $num_files = scalar @real_files;
+       $ENV{GIT_SENDEMAIL_FILE_TOTAL} = "$num_files";
+       initialize_modified_loop_vars();
+       foreach my $r (@real_files) {
+               $ENV{GIT_SENDEMAIL_FILE_COUNTER} = "$num";
+               pre_process_file($r, 1);
+               validate_patch($r, $target_xfer_encoding);
+               $num += 1;
+       }
+       delete $ENV{GIT_SENDEMAIL_FILE_COUNTER};
+       delete $ENV{GIT_SENDEMAIL_FILE_TOTAL};
+}
+
+initialize_modified_loop_vars();
 foreach my $t (@files) {
        while (!process_file($t)) {
                # user edited the file
        }
 }
 
+# Execute a command and return its output lines as an array.  Blank
+# lines which do not appear at the end of the output are reported as
+# errors.
+sub execute_cmd {
+       my ($prefix, $cmd, $file) = @_;
+       my @lines = ();
+       my $seen_blank_line = 0;
+       open my $fh, "-|", "$cmd \Q$file\E"
+               or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
+       while (my $line = <$fh>) {
+               die sprintf(__("(%s) Malformed output from '%s'"), $prefix, $cmd)
+                   if $seen_blank_line;
+               if ($line =~ /^$/) {
+                       $seen_blank_line = $line =~ /^$/;
+                       next;
+               }
+               push @lines, $line;
+       }
+       close $fh
+           or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
+       return @lines;
+}
+
+# Process headers lines, unfolding multiline headers as defined by RFC
+# 2822.
+sub unfold_headers {
+       my @headers;
+       foreach(@_) {
+               last if /^\s*$/;
+               if (/^\s+\S/ and @headers) {
+                       chomp($headers[$#headers]);
+                       s/^\s+/ /;
+                       $headers[$#headers] .= $_;
+               } else {
+                       push(@headers, $_);
+               }
+       }
+       return @headers;
+}
+
+# Invoke the provided CMD with FILE as an argument, which should
+# output RFC 2822 email headers. Fold multiline headers and return the
+# headers as an array.
+sub invoke_header_cmd {
+       my ($cmd, $file) = @_;
+       my @lines = execute_cmd("header-cmd", $header_cmd, $file);
+       return unfold_headers(@lines);
+}
+
 # Execute a command (e.g. $to_cmd) to get a list of email addresses
 # and return a results array
 sub recipients_cmd {
-       my ($prefix, $what, $cmd, $file) = @_;
-
+       my ($prefix, $what, $cmd, $file, $quiet) = @_;
+       my @lines = ();
        my @addresses = ();
-       open my $fh, "-|", "$cmd \Q$file\E"
-           or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
-       while (my $address = <$fh>) {
+
+       @lines = execute_cmd($prefix, $cmd, $file);
+       for my $address (@lines) {
                $address =~ s/^\s*//g;
                $address =~ s/\s*$//g;
                $address = sanitize_address($address);
@@ -2016,8 +2064,6 @@ sub recipients_cmd {
                printf(__("(%s) Adding %s: %s from: '%s'\n"),
                       $prefix, $what, $address, $cmd) unless $quiet;
                }
-       close $fh
-           or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
        return @addresses;
 }
 
@@ -2088,10 +2134,21 @@ sub validate_patch {
                        chdir($repo->wc_path() or $repo->repo_path())
                                or die("chdir: $!");
                        local $ENV{"GIT_DIR"} = $repo->repo_path();
+
+                       my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header();
+
+                       require File::Temp;
+                       my ($header_filehandle, $header_filename) = File::Temp::tempfile(
+                            TEMPLATE => ".gitsendemail.header.XXXXXX",
+                            DIR => $repo->repo_path(),
+                            UNLINK => 1,
+                        );
+                       print $header_filehandle $header;
+
                        my @cmd = ("git", "hook", "run", "--ignore-missing",
                                    $hook_name, "--");
-                       my @cmd_msg = (@cmd, "<patch>");
-                       my @cmd_run = (@cmd, $target);
+                       my @cmd_msg = (@cmd, "<patch>", "<header>");
+                       my @cmd_run = (@cmd, $target, $header_filename);
                        $hook_error = system_or_msg(\@cmd_run, undef, "@cmd_msg");
                        chdir($cwd_save) or die("chdir: $!");
                }
index 5e5d21c010f7d4337dbf95cd50eeaf8c97791ba8..7f9582d92343450e0c71ba72e5faee845cc1165b 100755 (executable)
@@ -244,6 +244,9 @@ cmd_update()
                -q|--quiet)
                        quiet=1
                        ;;
+               -v|--verbose)
+                       quiet=0
+                       ;;
                --progress)
                        progress=1
                        ;;
@@ -343,7 +346,6 @@ cmd_update()
                ${recursive:+--recursive} \
                ${init:+--init} \
                ${nofetch:+--no-fetch} \
-               ${wt_prefix:+--prefix "$wt_prefix"} \
                ${rebase:+--rebase} \
                ${merge:+--merge} \
                ${checkout:+--checkout} \
@@ -557,7 +559,7 @@ cmd_sync()
 
 cmd_absorbgitdirs()
 {
-       git submodule--helper absorbgitdirs --prefix "$wt_prefix" "$@"
+       git ${wt_prefix:+-C "$wt_prefix"} submodule--helper absorbgitdirs "$@"
 }
 
 # This loop parses the command line arguments to find the
index be987e316f92acda1ff83c880ead3c6848db487c..b0d0a50984bcea740f9486deda974aa3e53cbe1c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
 # License: GPL v2 or later
-use 5.008;
+use 5.008001;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use strict;
 use vars qw/   $AUTHOR $VERSION
@@ -297,28 +297,12 @@ my %cmd = (
                {} ],
 );
 
-package FakeTerm;
-sub new {
-       my ($class, $reason) = @_;
-       return bless \$reason, shift;
-}
-sub readline {
-       my $self = shift;
-       die "Cannot use readline on FakeTerm: $$self";
-}
-package main;
-
 my $term;
 sub term_init {
-       $term = eval {
-               require Term::ReadLine;
-               $ENV{"GIT_SVN_NOTTY"}
+       require Term::ReadLine;
+       $term = $ENV{"GIT_SVN_NOTTY"}
                        ? new Term::ReadLine 'git-svn', \*STDIN, \*STDOUT
                        : new Term::ReadLine 'git-svn';
-       };
-       if ($@) {
-               $term = new FakeTerm "$@: going non-interactive";
-       }
 }
 
 my $cmd;
similarity index 99%
rename from zlib.c
rename to git-zlib.c
index d594cba3fc9d82d94b9277e886f2bee265e552f6..d43bbeb6daa4c195048f7012e0bccff8c5b4b5f1 100644 (file)
--- a/zlib.c
@@ -2,7 +2,8 @@
  * zlib wrappers to make sure we don't silently miss errors
  * at init time.
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "git-zlib.h"
 
 static const char *zerr_to_string(int status)
 {
diff --git a/git-zlib.h b/git-zlib.h
new file mode 100644 (file)
index 0000000..d8a670a
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef GIT_ZLIB_H
+#define GIT_ZLIB_H
+
+typedef struct git_zstream {
+       z_stream z;
+       unsigned long avail_in;
+       unsigned long avail_out;
+       unsigned long total_in;
+       unsigned long total_out;
+       unsigned char *next_in;
+       unsigned char *next_out;
+} git_zstream;
+
+void git_inflate_init(git_zstream *);
+void git_inflate_init_gzip_only(git_zstream *);
+void git_inflate_end(git_zstream *);
+int git_inflate(git_zstream *, int flush);
+
+void git_deflate_init(git_zstream *, int level);
+void git_deflate_init_gzip(git_zstream *, int level);
+void git_deflate_init_raw(git_zstream *, int level);
+void git_deflate_end(git_zstream *);
+int git_deflate_abort(git_zstream *);
+int git_deflate_end_gently(git_zstream *);
+int git_deflate(git_zstream *, int flush);
+unsigned long git_deflate_bound(git_zstream *, unsigned long);
+
+#endif /* GIT_ZLIB_H */
diff --git a/git.c b/git.c
index da411c53822a1893f7774ae721b5931452741bba..7068a184b0a2947c6d62c3331c6f1a418708c7be 100644 (file)
--- a/git.c
+++ b/git.c
@@ -1,10 +1,19 @@
 #include "builtin.h"
 #include "config.h"
+#include "environment.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "help.h"
+#include "pager.h"
+#include "read-cache-ll.h"
 #include "run-command.h"
 #include "alias.h"
+#include "replace-object.h"
+#include "setup.h"
+#include "attr.h"
 #include "shallow.h"
+#include "trace.h"
+#include "trace2.h"
 
 #define RUN_SETUP              (1<<0)
 #define RUN_SETUP_GENTLY       (1<<1)
@@ -14,9 +23,8 @@
  * RUN_SETUP for reading from the configuration file.
  */
 #define NEED_WORK_TREE         (1<<3)
-#define SUPPORT_SUPER_PREFIX   (1<<4)
-#define DELAY_PAGER_CONFIG     (1<<5)
-#define NO_PARSEOPT            (1<<6) /* parse-options is not used */
+#define DELAY_PAGER_CONFIG     (1<<4)
+#define NO_PARSEOPT            (1<<5) /* parse-options is not used */
 
 struct cmd_struct {
        const char *cmd;
@@ -29,8 +37,7 @@ const char git_usage_string[] =
           "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
           "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
           "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-          "           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-          "           <command> [<args>]");
+          "           [--config-env=<name>=<envvar>] <command> [<args>]");
 
 const char git_more_info_string[] =
        N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -180,7 +187,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--no-replace-objects")) {
-                       read_replace_refs = 0;
+                       disable_replace_refs();
                        setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
                        if (envchanged)
                                *envchanged = 1;
@@ -226,20 +233,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
-               } else if (!strcmp(cmd, "--super-prefix")) {
-                       if (*argc < 2) {
-                               fprintf(stderr, _("no prefix given for --super-prefix\n" ));
-                               usage(git_usage_string);
-                       }
-                       setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
-                       if (envchanged)
-                               *envchanged = 1;
-                       (*argv)++;
-                       (*argc)--;
-               } else if (skip_prefix(cmd, "--super-prefix=", &cmd)) {
-                       setenv(GIT_SUPER_PREFIX_ENVIRONMENT, cmd, 1);
-                       if (envchanged)
-                               *envchanged = 1;
                } else if (!strcmp(cmd, "--bare")) {
                        char *cwd = xgetcwd();
                        is_bare_repository_cfg = 1;
@@ -323,6 +316,21 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        } else {
                                exit(list_cmds(cmd));
                        }
+               } else if (!strcmp(cmd, "--attr-source")) {
+                       if (*argc < 2) {
+                               fprintf(stderr, _("no attribute source given for --attr-source\n" ));
+                               usage(git_usage_string);
+                       }
+                       setenv(GIT_ATTR_SOURCE_ENVIRONMENT, (*argv)[1], 1);
+                       if (envchanged)
+                               *envchanged = 1;
+                       (*argv)++;
+                       (*argc)--;
+               } else if (skip_prefix(cmd, "--attr-source=", &cmd)) {
+                       set_git_attr_source(cmd);
+                       setenv(GIT_ATTR_SOURCE_ENVIRONMENT, cmd, 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else {
                        fprintf(stderr, _("unknown option: %s\n"), cmd);
                        usage(git_usage_string);
@@ -446,14 +454,9 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
                use_pager = 1;
        if (run_setup && startup_info->have_repository)
                /* get_git_dir() may set up repo, avoid that */
-               trace_repo_setup(prefix);
+               trace_repo_setup();
        commit_pager_choice();
 
-       if (!help && get_super_prefix()) {
-               if (!(p->option & SUPPORT_SUPER_PREFIX))
-                       die(_("%s doesn't support --super-prefix"), p->cmd);
-       }
-
        if (!help && p->option & NEED_WORK_TREE)
                setup_work_tree();
 
@@ -492,7 +495,7 @@ static struct cmd_struct commands[] = {
        { "annotate", cmd_annotate, RUN_SETUP },
        { "apply", cmd_apply, RUN_SETUP_GENTLY },
        { "archive", cmd_archive, RUN_SETUP_GENTLY },
-       { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
+       { "bisect", cmd_bisect, RUN_SETUP },
        { "blame", cmd_blame, RUN_SETUP },
        { "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
        { "bugreport", cmd_bugreport, RUN_SETUP_GENTLY },
@@ -504,7 +507,7 @@ static struct cmd_struct commands[] = {
        { "check-ref-format", cmd_check_ref_format, NO_PARSEOPT  },
        { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
        { "checkout--worker", cmd_checkout__worker,
-               RUN_SETUP | NEED_WORK_TREE | SUPPORT_SUPER_PREFIX },
+               RUN_SETUP | NEED_WORK_TREE },
        { "checkout-index", cmd_checkout_index,
                RUN_SETUP | NEED_WORK_TREE},
        { "cherry", cmd_cherry, RUN_SETUP },
@@ -528,7 +531,6 @@ static struct cmd_struct commands[] = {
        { "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
        { "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
        { "difftool", cmd_difftool, RUN_SETUP_GENTLY },
-       { "env--helper", cmd_env__helper },
        { "fast-export", cmd_fast_export, RUN_SETUP },
        { "fast-import", cmd_fast_import, RUN_SETUP | NO_PARSEOPT },
        { "fetch", cmd_fetch, RUN_SETUP },
@@ -539,7 +541,7 @@ static struct cmd_struct commands[] = {
        { "format-patch", cmd_format_patch, RUN_SETUP },
        { "fsck", cmd_fsck, RUN_SETUP },
        { "fsck-objects", cmd_fsck, RUN_SETUP },
-       { "fsmonitor--daemon", cmd_fsmonitor__daemon, SUPPORT_SUPER_PREFIX | RUN_SETUP },
+       { "fsmonitor--daemon", cmd_fsmonitor__daemon, RUN_SETUP },
        { "gc", cmd_gc, RUN_SETUP },
        { "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
        { "grep", cmd_grep, RUN_SETUP_GENTLY },
@@ -583,7 +585,7 @@ static struct cmd_struct commands[] = {
        { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
        { "push", cmd_push, RUN_SETUP },
        { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
-       { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
+       { "read-tree", cmd_read_tree, RUN_SETUP },
        { "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
        { "receive-pack", cmd_receive_pack },
        { "reflog", cmd_reflog, RUN_SETUP },
@@ -592,6 +594,7 @@ static struct cmd_struct commands[] = {
        { "remote-fd", cmd_remote_fd, NO_PARSEOPT },
        { "repack", cmd_repack, RUN_SETUP },
        { "replace", cmd_replace, RUN_SETUP },
+       { "replay", cmd_replay, RUN_SETUP },
        { "rerere", cmd_rerere, RUN_SETUP },
        { "reset", cmd_reset, RUN_SETUP },
        { "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
@@ -605,12 +608,12 @@ static struct cmd_struct commands[] = {
        { "show-branch", cmd_show_branch, RUN_SETUP },
        { "show-index", cmd_show_index, RUN_SETUP_GENTLY },
        { "show-ref", cmd_show_ref, RUN_SETUP },
-       { "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
+       { "sparse-checkout", cmd_sparse_checkout, RUN_SETUP },
        { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
        { "stash", cmd_stash, RUN_SETUP | NEED_WORK_TREE },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
        { "stripspace", cmd_stripspace },
-       { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+       { "submodule--helper", cmd_submodule__helper, RUN_SETUP },
        { "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
        { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
        { "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
@@ -727,9 +730,6 @@ static void execv_dashed_external(const char **argv)
        struct child_process cmd = CHILD_PROCESS_INIT;
        int status;
 
-       if (get_super_prefix())
-               die(_("%s doesn't support --super-prefix"), argv[0]);
-
        if (use_pager == -1 && !is_builtin(argv[0]))
                use_pager = check_pager_config(argv[0]);
        commit_pager_choice();
@@ -787,7 +787,7 @@ static int run_argv(int *argcp, const char ***argv)
                if (!done_alias)
                        handle_builtin(*argcp, *argv);
                else if (get_builtin(**argv)) {
-                       struct strvec args = STRVEC_INIT;
+                       struct child_process cmd = CHILD_PROCESS_INIT;
                        int i;
 
                        /*
@@ -799,23 +799,23 @@ static int run_argv(int *argcp, const char ***argv)
                         */
                        trace2_cmd_name("_run_git_alias_");
 
-                       if (get_super_prefix())
-                               die("%s doesn't support --super-prefix", **argv);
-
                        commit_pager_choice();
 
-                       strvec_push(&args, "git");
+                       strvec_push(&cmd.args, "git");
                        for (i = 0; i < *argcp; i++)
-                               strvec_push(&args, (*argv)[i]);
+                               strvec_push(&cmd.args, (*argv)[i]);
 
-                       trace_argv_printf(args.v, "trace: exec:");
+                       trace_argv_printf(cmd.args.v, "trace: exec:");
 
                        /*
                         * if we fail because the command is not found, it is
                         * OK to return. Otherwise, we just pass along the status code.
                         */
-                       i = run_command_v_opt_tr2(args.v, RUN_SILENT_EXEC_FAILURE |
-                                                 RUN_CLEAN_ON_EXIT | RUN_WAIT_AFTER_CLEAN, "git_alias");
+                       cmd.silent_exec_failure = 1;
+                       cmd.clean_on_exit = 1;
+                       cmd.wait_after_clean = 1;
+                       cmd.trace2_child_class = "git_alias";
+                       i = run_command(&cmd);
                        if (i >= 0 || errno != ENOENT)
                                exit(i);
                        die("could not execute builtin %s", **argv);
@@ -894,12 +894,8 @@ int cmd_main(int argc, const char **argv)
        argv++;
        argc--;
        handle_options(&argv, &argc, NULL);
-       if (argc > 0) {
-               if (!strcmp("--version", argv[0]) || !strcmp("-v", argv[0]))
-                       argv[0] = "version";
-               else if (!strcmp("--help", argv[0]) || !strcmp("-h", argv[0]))
-                       argv[0] = "help";
-       } else {
+
+       if (!argc) {
                /* The user didn't specify a command; give them help */
                commit_pager_choice();
                printf(_("usage: %s\n\n"), git_usage_string);
@@ -907,6 +903,12 @@ int cmd_main(int argc, const char **argv)
                printf("\n%s\n", _(git_more_info_string));
                exit(1);
        }
+
+       if (!strcmp("--version", argv[0]) || !strcmp("-v", argv[0]))
+               argv[0] = "version";
+       else if (!strcmp("--help", argv[0]) || !strcmp("-h", argv[0]))
+               argv[0] = "help";
+
        cmd = argv[0];
 
        /*
index 0ae7d685904b85f97a69b243e30f010671d050e3..7a087f123d7563d27a88751f70ead6172441af20 100755 (executable)
@@ -353,6 +353,16 @@ proc parseviewrevs {view revs} {
     return $ret
 }
 
+# Escapes a list of filter paths to be passed to git log via stdin. Note that
+# paths must not be quoted.
+proc escape_filter_paths {paths} {
+       set escaped [list]
+       foreach path $paths {
+               lappend escaped [string map {\\ \\\\ "\ " "\\\ "} $path]
+       }
+       return $escaped
+}
+
 # Start off a git log process and arrange to read its output
 proc start_rev_list {view} {
     global startmsecs commitidx viewcomplete curview
@@ -405,14 +415,17 @@ proc start_rev_list {view} {
         if {$revs eq {}} {
             return 0
         }
-        set args [concat $vflags($view) $revs]
+        set args $vflags($view)
     } else {
+        set revs {}
         set args $vorigargs($view)
     }
 
     if {[catch {
         set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
-                        --parents --boundary $args "--" $files] r]
+                        --parents --boundary $args --stdin \
+                        "<<[join [concat $revs "--" \
+                                [escape_filter_paths $files]] "\\n"]"] r]
     } err]} {
         error_popup "[mc "Error executing git log:"] $err"
         return 0
@@ -554,13 +567,20 @@ proc updatecommits {} {
             set revs $newrevs
             set vposids($view) [lsort -unique [concat $oldpos $vposids($view)]]
         }
-        set args [concat $vflags($view) $revs --not $oldpos]
+        set args $vflags($view)
+        foreach r $oldpos {
+                lappend revs "^$r"
+        }
     } else {
+        set revs {}
         set args $vorigargs($view)
     }
     if {[catch {
         set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
-                        --parents --boundary $args "--" $vfilelimit($view)] r]
+                        --parents --boundary $args --stdin \
+                        "<<[join [concat $revs "--" \
+                                [escape_filter_paths \
+                                        $vfilelimit($view)]] "\\n"]"] r]
     } err]} {
         error_popup "[mc "Error executing git log:"] $err"
         return
@@ -10231,10 +10251,16 @@ proc getallcommits {} {
             foreach id $seeds {
                 lappend ids "^$id"
             }
+            lappend ids "--"
         }
     }
     if {$ids ne {}} {
-        set fd [open [concat $cmd $ids] r]
+        if {$ids eq "--all"} {
+            set cmd [concat $cmd "--all"]
+        } else {
+            set cmd [concat $cmd --stdin "<<[join $ids "\\n"]"]
+        }
+        set fd [open $cmd r]
         fconfigure $fd -blocking 0
         incr allcommits
         nowbusy allcommits
@@ -11930,7 +11956,7 @@ proc formatdate {d} {
 }
 
 # This list of encoding names and aliases is distilled from
-# http://www.iana.org/assignments/character-sets.
+# https://www.iana.org/assignments/character-sets.
 # Not all of them are supported by Tcl.
 set encoding_aliases {
     { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
@@ -12446,7 +12472,7 @@ if {[tk windowingsystem] eq "aqua"} {
 
 catch {
     # follow the XDG base directory specification by default. See
-    # http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+    # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
     if {[info exists env(XDG_CONFIG_HOME)] && $env(XDG_CONFIG_HOME) ne ""} {
         # XDG_CONFIG_HOME environment variable is set
         set config_file [file join $env(XDG_CONFIG_HOME) git gitk]
index a58e6b3c44b0ef6175df1417a3ed6f7775bd9953..5bfa4968c4a43193c009045dbd6a6f187a319c76 100644 (file)
@@ -29,7 +29,7 @@ Requirements
 ------------
 
  - Core git tools
- - Perl 5.8
+ - Perl 5.8.1
  - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
  - web server
 
@@ -203,7 +203,7 @@ You can specify the following configuration variables when building GIT:
    created.  [Default: /etc/gitweb.conf]
  * HIGHLIGHT_BIN
    Path to the highlight executable to use (must be the one from
-   http://www.andre-simon.de due to assumptions about parameters and output).
+   http://andre-simon.de/zip/download.php due to assumptions about parameters and output).
    Useful if highlight is not installed on your webserver's PATH.
    [Default: highlight]
 
index e66eb3d9bad7cf627d5ed35e13e32dafb556d5cd..fc6d5dd522bf4f4c9869e4e14ea5a07409155784 100755 (executable)
@@ -7,7 +7,7 @@
 #
 # This program is licensed under the GPLv2
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 # handle ACL in file access tests
@@ -122,9 +122,9 @@ our $favicon = "++GITWEB_FAVICON++";
 our $javascript = "++GITWEB_JS++";
 
 # URI and label (title) of GIT logo link
-#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
+#our $logo_url = "https://www.kernel.org/pub/software/scm/git/docs/";
 #our $logo_label = "git documentation";
-our $logo_url = "http://git-scm.com/";
+our $logo_url = "https://git-scm.com/";
 our $logo_label = "git homepage";
 
 # source of projects list
@@ -197,7 +197,7 @@ our @diff_opts = ('-M'); # taken from git_commit
 our $prevent_xss = 0;
 
 # Path to the highlight executable to use (must be the one from
-# http://www.andre-simon.de due to assumptions about parameters and output).
+# http://andre-simon.de/zip/download.php due to assumptions about parameters and output).
 # Useful if highlight is not installed on your webserver's PATH.
 # [Default: highlight]
 our $highlight_bin = "++HIGHLIGHT_BIN++";
@@ -269,7 +269,7 @@ our %avatar_size = (
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
-# configuration for 'highlight' (http://www.andre-simon.de/)
+# configuration for 'highlight' (http://andre-simon.de/doku/highlight/en/highlight.php)
 # match by basename
 our %highlight_basename = (
        #'Program' => 'py',
@@ -8193,7 +8193,7 @@ sub git_feed {
        my $have_blame = gitweb_check_feature('blame');
 
        # Atom: http://www.atomenabled.org/developers/syndication/
-       # RSS:  http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
+       # RSS:  https://web.archive.org/web/20030729001534/http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
        if ($format ne 'rss' && $format ne 'atom') {
                die_error(400, "Unknown web feed format");
        }
index 3212601032622fef063f06066bd7131061de36d9..48d2e5101542ad96dcac78aace13d3bbee40d303 100644 (file)
@@ -667,7 +667,7 @@ div.remote {
 }
 
 
-/* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
+/* Style definition generated by highlight 2.4.5, http://andre-simon.de/doku/highlight/en/highlight.php */
 
 /* Highlighting theme definition: */
 
index 018bbb7d4cb72eeb786c3309ba318e7789a02b37..99e3eb8c3d97ae8705ff8e656512479d9f84a90e 100644 (file)
@@ -123,8 +123,8 @@ function addCssRule(selector, style) {
  * NOTE that there are limits and differences compared to native
  * getElementsByClassName as defined by e.g.:
  *   https://developer.mozilla.org/en/DOM/document.getElementsByClassName
- *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
- *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
+ *   https://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
+ *   https://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
  *
  * Namely, this implementation supports only single class name as
  * argument and not set of space-separated tokens representing classes,
@@ -133,11 +133,11 @@ function addCssRule(selector, style) {
  * (via getElementsByTagName).
  *
  * Based on
- *   http://code.google.com/p/getelementsbyclassname/
+ *   https://code.google.com/p/getelementsbyclassname/
  *   http://www.dustindiaz.com/getelementsbyclass/
- *   http://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
+ *   https://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
  *
- * See also http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
+ * See also https://johnresig.com/blog/getelementsbyclassname-speed-comparison/
  *
  * @param {String} class: name of _single_ class to find
  * @param {String} [taghint] limit search to given tags
index f877a1ea56460de8e7c831b58ab4cfbda837a304..f614105033230c5f5356ff18c49bd47ff500cb35 100644 (file)
@@ -1,14 +1,31 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
 #include "config.h"
+#include "date.h"
+#include "gettext.h"
 #include "run-command.h"
 #include "strbuf.h"
 #include "dir.h"
+#include "ident.h"
 #include "gpg-interface.h"
+#include "path.h"
 #include "sigchain.h"
 #include "tempfile.h"
 #include "alias.h"
 
+static int git_gpg_config(const char *, const char *,
+                         const struct config_context *, void *);
+
+static void gpg_interface_lazy_init(void)
+{
+       static int done;
+
+       if (done)
+               return;
+       done = 1;
+       git_config(git_gpg_config, NULL);
+}
+
 static char *configured_signing_key;
 static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file;
 static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
@@ -569,8 +586,8 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
                }
        }
 
-       strbuf_stripspace(&ssh_keygen_out, 0);
-       strbuf_stripspace(&ssh_keygen_err, 0);
+       strbuf_stripspace(&ssh_keygen_out, '\0');
+       strbuf_stripspace(&ssh_keygen_err, '\0');
        /* Add stderr outputs to show the user actual ssh-keygen errors */
        strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len);
        strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);
@@ -632,8 +649,10 @@ int check_signature(struct signature_check *sigc,
        struct gpg_format *fmt;
        int status;
 
+       gpg_interface_lazy_init();
+
        sigc->result = 'N';
-       sigc->trust_level = -1;
+       sigc->trust_level = TRUST_UNDEFINED;
 
        fmt = get_format_by_sig(signature);
        if (!fmt)
@@ -695,11 +714,15 @@ int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct
 
 void set_signing_key(const char *key)
 {
+       gpg_interface_lazy_init();
+
        free(configured_signing_key);
        configured_signing_key = xstrdup(key);
 }
 
-int git_gpg_config(const char *var, const char *value, void *cb UNUSED)
+static int git_gpg_config(const char *var, const char *value,
+                         const struct config_context *ctx UNUSED,
+                         void *cb UNUSED)
 {
        struct gpg_format *fmt = NULL;
        char *fmtname = NULL;
@@ -738,23 +761,14 @@ int git_gpg_config(const char *var, const char *value, void *cb UNUSED)
                return 0;
        }
 
-       if (!strcmp(var, "gpg.ssh.defaultkeycommand")) {
-               if (!value)
-                       return config_error_nonbool(var);
+       if (!strcmp(var, "gpg.ssh.defaultkeycommand"))
                return git_config_string(&ssh_default_key_command, var, value);
-       }
 
-       if (!strcmp(var, "gpg.ssh.allowedsignersfile")) {
-               if (!value)
-                       return config_error_nonbool(var);
+       if (!strcmp(var, "gpg.ssh.allowedsignersfile"))
                return git_config_pathname(&ssh_allowed_signers, var, value);
-       }
 
-       if (!strcmp(var, "gpg.ssh.revocationfile")) {
-               if (!value)
-                       return config_error_nonbool(var);
+       if (!strcmp(var, "gpg.ssh.revocationfile"))
                return git_config_pathname(&ssh_revocation_file, var, value);
-       }
 
        if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
                fmtname = "openpgp";
@@ -888,6 +902,8 @@ static const char *get_ssh_key_id(void) {
 /* Returns a textual but unique representation of the signing key */
 const char *get_signing_key_id(void)
 {
+       gpg_interface_lazy_init();
+
        if (use_format->get_key_id) {
                return use_format->get_key_id();
        }
@@ -898,6 +914,8 @@ const char *get_signing_key_id(void)
 
 const char *get_signing_key(void)
 {
+       gpg_interface_lazy_init();
+
        if (configured_signing_key)
                return configured_signing_key;
        if (use_format->get_default_key) {
@@ -923,6 +941,8 @@ const char *gpg_trust_level_to_str(enum signature_trust_level level)
 
 int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
 {
+       gpg_interface_lazy_init();
+
        return use_format->sign_buffer(buffer, signature, signing_key);
 }
 
@@ -977,9 +997,13 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
                        break; /* found */
        }
        ret |= !cp;
+       if (ret) {
+               error(_("gpg failed to sign the data:\n%s"),
+                     gpg_status.len ? gpg_status.buf : "(no gpg output)");
+               strbuf_release(&gpg_status);
+               return -1;
+       }
        strbuf_release(&gpg_status);
-       if (ret)
-               return error(_("gpg failed to sign the data"));
 
        /* Strip CR from the line endings, in case we are on Windows. */
        remove_cr_after(signature, bottom);
@@ -998,6 +1022,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
        char *ssh_signing_key_file = NULL;
        struct strbuf ssh_signature_filename = STRBUF_INIT;
        const char *literal_key = NULL;
+       int literal_ssh_key = 0;
 
        if (!signing_key || signing_key[0] == '\0')
                return error(
@@ -1005,6 +1030,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
 
        if (is_literal_ssh_key(signing_key, &literal_key)) {
                /* A literal ssh key */
+               literal_ssh_key = 1;
                key_file = mks_tempfile_t(".git_signing_key_tmpXXXXXX");
                if (!key_file)
                        return error_errno(
@@ -1019,7 +1045,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
                ssh_signing_key_file = strbuf_detach(&key_file->filename, NULL);
        } else {
                /* We assume a file */
-               ssh_signing_key_file = expand_user_path(signing_key, 1);
+               ssh_signing_key_file = interpolate_path(signing_key, 1);
        }
 
        buffer_file = mks_tempfile_t(".git_signing_buffer_tmpXXXXXX");
@@ -1039,8 +1065,10 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
                     "-Y", "sign",
                     "-n", "git",
                     "-f", ssh_signing_key_file,
-                    buffer_file->filename.buf,
                     NULL);
+       if (literal_ssh_key)
+               strvec_push(&signer.args, "-U");
+       strvec_push(&signer.args, buffer_file->filename.buf);
 
        sigchain_push(SIGPIPE, SIG_IGN);
        ret = pipe_command(&signer, NULL, 0, NULL, 0, &signer_stderr, 0);
index 8a9ef41779e2fe91305dcb1b13cf66a282446cf6..143cdc1c02d4b31fa08f92fbcae1691c80d2c2d6 100644 (file)
@@ -79,7 +79,6 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
  */
 const char *gpg_trust_level_to_str(enum signature_trust_level level);
 
-int git_gpg_config(const char *, const char *, void *);
 void set_signing_key(const char *);
 const char *get_signing_key(void);
 
diff --git a/graph.c b/graph.c
index 568b6e7cd41512d17cc68e0595675ee858165164..1ca34770ee8139f25e0a2476c5aa3cd8b06fb89c 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "config.h"
 #include "commit.h"
 #include "color.h"
@@ -338,7 +339,6 @@ void graph_setup_line_prefix(struct diff_options *diffopt)
                diffopt->output_prefix = diff_output_prefix_callback;
 }
 
-
 struct git_graph *graph_init(struct rev_info *opt)
 {
        struct git_graph *graph = xmalloc(sizeof(struct git_graph));
diff --git a/graph.h b/graph.h
index e88632a0140f70ef25535e23120c898c007b8349..3fd1dcb2e94d4399fc40406f9ec71b319cda0d5f 100644 (file)
--- a/graph.h
+++ b/graph.h
@@ -130,7 +130,7 @@ void graph_setup_line_prefix(struct diff_options *diffopt);
  * This functions must be called BEFORE graph_init() is called.
  *
  * NOTE: This function isn't used in Git outside graph.c but it is used
- * by CGit (http://git.zx2c4.com/cgit/) to use HTML for colors.
+ * by CGit (https://git.zx2c4.com/cgit/) to use HTML for colors.
  */
 void graph_set_column_colors(const char **colors, unsigned short colors_max);
 
@@ -196,7 +196,7 @@ int graph_is_commit_finished(struct git_graph const *graph);
  * graph_update() is called.
  *
  * NOTE: This function isn't used in Git outside graph.c but it is used
- * by CGit (http://git.zx2c4.com/cgit/) to wrap HTML around graph lines.
+ * by CGit (https://git.zx2c4.com/cgit/) to wrap HTML around graph lines.
  */
 int graph_next_line(struct git_graph *graph, struct strbuf *sb);
 
diff --git a/grep.c b/grep.c
index 52a894c989087c70f215fc46d9f17489de546d8c..5f23d1a50cabb35732f9515355fe0a85a33d2ff8 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1,12 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
 #include "grep.h"
-#include "object-store.h"
+#include "hex.h"
+#include "object-store-ll.h"
+#include "pretty.h"
 #include "userdiff.h"
 #include "xdiff-interface.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "commit.h"
 #include "quote.h"
 #include "help.h"
 
@@ -14,7 +16,7 @@ static int grep_source_load(struct grep_source *gs);
 static int grep_source_is_binary(struct grep_source *gs,
                                 struct index_state *istate);
 
-static void std_output(struct grep_opt *opt, const void *buf, size_t size)
+static void std_output(struct grep_opt *opt UNUSED, const void *buf, size_t size)
 {
        fwrite(buf, size, 1, stdout);
 }
@@ -52,7 +54,8 @@ define_list_config_array_extra(color_grep_slots, {"match"});
  * Read the configuration file once and store it in
  * the grep_defaults template.
  */
-int grep_config(const char *var, const char *value, void *cb)
+int grep_config(const char *var, const char *value,
+               const struct config_context *ctx, void *cb)
 {
        struct grep_opt *opt = cb;
        const char *slot;
@@ -87,9 +90,9 @@ int grep_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "color.grep"))
                opt->color = git_config_colorbool(var, value);
        if (!strcmp(var, "color.grep.match")) {
-               if (grep_config("color.grep.matchcontext", value, cb) < 0)
+               if (grep_config("color.grep.matchcontext", value, ctx, cb) < 0)
                        return -1;
-               if (grep_config("color.grep.matchselected", value, cb) < 0)
+               if (grep_config("color.grep.matchselected", value, ctx, cb) < 0)
                        return -1;
        } else if (skip_prefix(var, "color.grep.", &slot)) {
                int i = LOOKUP_CONFIG(color_grep_slots, slot);
@@ -262,6 +265,31 @@ static void pcre2_free(void *pointer, MAYBE_UNUSED void *memory_data)
        free(pointer);
 }
 
+static int pcre2_jit_functional(void)
+{
+       static int jit_working = -1;
+       pcre2_code *code;
+       size_t off;
+       int err;
+
+       if (jit_working != -1)
+               return jit_working;
+
+       /*
+        * Try to JIT compile a simple pattern to probe if the JIT is
+        * working in general. It might fail for systems where creating
+        * memory mappings for runtime code generation is restricted.
+        */
+       code = pcre2_compile((PCRE2_SPTR)".", 1, 0, &err, &off, NULL);
+       if (!code)
+               return 0;
+
+       jit_working = pcre2_jit_compile(code, PCRE2_JIT_COMPLETE) == 0;
+       pcre2_code_free(code);
+
+       return jit_working;
+}
+
 static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
 {
        int error;
@@ -293,7 +321,16 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
                options |= PCRE2_CASELESS;
        }
        if (!opt->ignore_locale && is_utf8_locale() && !literal)
-               options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
+               options |= (PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_INVALID_UTF);
+
+#ifndef GIT_PCRE2_VERSION_10_35_OR_HIGHER
+       /*
+        * Work around a JIT bug related to invalid Unicode character handling
+        * fixed in 10.35:
+        * https://github.com/PCRE2Project/pcre2/commit/c21bd977547d
+        */
+       options &= ~PCRE2_UCP;
+#endif
 
 #ifndef GIT_PCRE2_VERSION_10_36_OR_HIGHER
        /* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
@@ -317,8 +354,29 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
        pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on);
        if (p->pcre2_jit_on) {
                jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE);
-               if (jitret)
-                       die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret);
+               if (jitret == PCRE2_ERROR_NOMEMORY && !pcre2_jit_functional()) {
+                       /*
+                        * Even though pcre2_config(PCRE2_CONFIG_JIT, ...)
+                        * indicated JIT support, the library might still
+                        * fail to generate JIT code for various reasons,
+                        * e.g. when SELinux's 'deny_execmem' or PaX's
+                        * MPROTECT prevent creating W|X memory mappings.
+                        *
+                        * Instead of faling hard, fall back to interpreter
+                        * mode, just as if the pattern was prefixed with
+                        * '(*NO_JIT)'.
+                        */
+                       p->pcre2_jit_on = 0;
+                       return;
+               } else if (jitret) {
+                       int need_clip = p->patternlen > 64;
+                       int clip_len = need_clip ? 64 : p->patternlen;
+                       die("Couldn't JIT the PCRE2 pattern '%.*s'%s, got '%d'%s",
+                           clip_len, p->pattern, need_clip ? "..." : "", jitret,
+                           pcre2_jit_functional()
+                           ? "\nPerhaps prefix (*NO_JIT) to your pattern?"
+                           : "");
+               }
 
                /*
                 * The pcre2_config(PCRE2_CONFIG_JIT, ...) call just
@@ -393,18 +451,20 @@ static void free_pcre2_pattern(struct grep_pat *p)
        pcre2_general_context_free(p->pcre2_general_context);
 }
 #else /* !USE_LIBPCRE2 */
-static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
+static void compile_pcre2_pattern(struct grep_pat *p UNUSED,
+                                 const struct grep_opt *opt UNUSED)
 {
        die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
 }
 
-static int pcre2match(struct grep_pat *p, const char *line, const char *eol,
-               regmatch_t *match, int eflags)
+static int pcre2match(struct grep_pat *p UNUSED, const char *line UNUSED,
+                     const char *eol UNUSED, regmatch_t *match UNUSED,
+                     int eflags UNUSED)
 {
        return 1;
 }
 
-static void free_pcre2_pattern(struct grep_pat *p)
+static void free_pcre2_pattern(struct grep_pat *p UNUSED)
 {
 }
 
@@ -708,6 +768,7 @@ void compile_grep_patterns(struct grep_opt *opt)
 {
        struct grep_pat *p;
        struct grep_expr *header_expr = prep_header_patterns(opt);
+       int extended = 0;
 
        for (p = opt->pattern_list; p; p = p->next) {
                switch (p->token) {
@@ -717,14 +778,14 @@ void compile_grep_patterns(struct grep_opt *opt)
                        compile_regexp(p, opt);
                        break;
                default:
-                       opt->extended = 1;
+                       extended = 1;
                        break;
                }
        }
 
        if (opt->all_match || opt->no_body_match || header_expr)
-               opt->extended = 1;
-       else if (!opt->extended)
+               extended = 1;
+       else if (!extended)
                return;
 
        p = opt->pattern_list;
@@ -768,11 +829,11 @@ static void free_pattern_expr(struct grep_expr *x)
        free(x);
 }
 
-void free_grep_patterns(struct grep_opt *opt)
+static void free_grep_pat(struct grep_pat *pattern)
 {
        struct grep_pat *p, *n;
 
-       for (p = opt->pattern_list; p; p = n) {
+       for (p = pattern; p; p = n) {
                n = p->next;
                switch (p->token) {
                case GREP_PATTERN: /* atom */
@@ -789,10 +850,15 @@ void free_grep_patterns(struct grep_opt *opt)
                }
                free(p);
        }
+}
 
-       if (!opt->extended)
-               return;
-       free_pattern_expr(opt->pattern_expression);
+void free_grep_patterns(struct grep_opt *opt)
+{
+       free_grep_pat(opt->pattern_list);
+       free_grep_pat(opt->header_list);
+
+       if (opt->pattern_expression)
+               free_pattern_expr(opt->pattern_expression);
 }
 
 static const char *end_of_line(const char *cp, unsigned long *left)
@@ -971,8 +1037,6 @@ static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x,
 {
        int h = 0;
 
-       if (!x)
-               die("Not a valid grep expression");
        switch (x->node) {
        case GREP_NODE_TRUE:
                h = 1;
@@ -1052,7 +1116,7 @@ static int match_line(struct grep_opt *opt,
        struct grep_pat *p;
        int hit = 0;
 
-       if (opt->extended)
+       if (opt->pattern_expression)
                return match_expr(opt, bol, eol, ctx, col, icol,
                                  collect_hits);
 
@@ -1370,7 +1434,7 @@ static int should_lookahead(struct grep_opt *opt)
 {
        struct grep_pat *p;
 
-       if (opt->extended)
+       if (opt->pattern_expression)
                return 0; /* punt for too complex stuff */
        if (opt->invert)
                return 0;
diff --git a/grep.h b/grep.h
index bdcadce61b8027adc760cd6e7fc51aa4e94c3d06..926c0875c42f63f961447a30f95c39d7ae031999 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -7,6 +7,9 @@
 #if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 36) || PCRE2_MAJOR >= 11
 #define GIT_PCRE2_VERSION_10_36_OR_HIGHER
 #endif
+#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 35) || PCRE2_MAJOR >= 11
+#define GIT_PCRE2_VERSION_10_35_OR_HIGHER
+#endif
 #if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 34) || PCRE2_MAJOR >= 11
 #define GIT_PCRE2_VERSION_10_34_OR_HIGHER
 #endif
@@ -151,7 +154,6 @@ struct grep_opt {
 #define GREP_BINARY_TEXT       2
        int binary;
        int allow_textconv;
-       int extended;
        int use_reflog_filter;
        int relative;
        int pathname;
@@ -200,7 +202,9 @@ struct grep_opt {
        .output = std_output, \
 }
 
-int grep_config(const char *var, const char *value, void *);
+struct config_context;
+int grep_config(const char *var, const char *value,
+               const struct config_context *ctx, void *data);
 void grep_init(struct grep_opt *, struct repository *repo);
 
 void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
diff --git a/hash-ll.h b/hash-ll.h
new file mode 100644 (file)
index 0000000..10d84cc
--- /dev/null
+++ b/hash-ll.h
@@ -0,0 +1,309 @@
+#ifndef HASH_LL_H
+#define HASH_LL_H
+
+#if defined(SHA1_APPLE)
+#include <CommonCrypto/CommonDigest.h>
+#elif defined(SHA1_OPENSSL)
+#  include <openssl/sha.h>
+#  if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+#    define SHA1_NEEDS_CLONE_HELPER
+#    include "sha1/openssl.h"
+#  endif
+#elif defined(SHA1_DC)
+#include "sha1dc_git.h"
+#else /* SHA1_BLK */
+#include "block-sha1/sha1.h"
+#endif
+
+#if defined(SHA256_NETTLE)
+#include "sha256/nettle.h"
+#elif defined(SHA256_GCRYPT)
+#define SHA256_NEEDS_CLONE_HELPER
+#include "sha256/gcrypt.h"
+#elif defined(SHA256_OPENSSL)
+#  include <openssl/sha.h>
+#  if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+#    define SHA256_NEEDS_CLONE_HELPER
+#    include "sha256/openssl.h"
+#  endif
+#else
+#include "sha256/block/sha256.h"
+#endif
+
+#ifndef platform_SHA_CTX
+/*
+ * platform's underlying implementation of SHA-1; could be OpenSSL,
+ * blk_SHA, Apple CommonCrypto, etc...  Note that the relevant
+ * SHA-1 header may have already defined platform_SHA_CTX for our
+ * own implementations like block-sha1, so we list
+ * the default for OpenSSL compatible SHA-1 implementations here.
+ */
+#define platform_SHA_CTX       SHA_CTX
+#define platform_SHA1_Init     SHA1_Init
+#define platform_SHA1_Update   SHA1_Update
+#define platform_SHA1_Final            SHA1_Final
+#endif
+
+#define git_SHA_CTX            platform_SHA_CTX
+#define git_SHA1_Init          platform_SHA1_Init
+#define git_SHA1_Update                platform_SHA1_Update
+#define git_SHA1_Final         platform_SHA1_Final
+
+#ifdef platform_SHA1_Clone
+#define git_SHA1_Clone platform_SHA1_Clone
+#endif
+
+#ifndef platform_SHA256_CTX
+#define platform_SHA256_CTX    SHA256_CTX
+#define platform_SHA256_Init   SHA256_Init
+#define platform_SHA256_Update SHA256_Update
+#define platform_SHA256_Final  SHA256_Final
+#endif
+
+#define git_SHA256_CTX         platform_SHA256_CTX
+#define git_SHA256_Init                platform_SHA256_Init
+#define git_SHA256_Update      platform_SHA256_Update
+#define git_SHA256_Final       platform_SHA256_Final
+
+#ifdef platform_SHA256_Clone
+#define git_SHA256_Clone       platform_SHA256_Clone
+#endif
+
+#ifdef SHA1_MAX_BLOCK_SIZE
+#include "compat/sha1-chunked.h"
+#undef git_SHA1_Update
+#define git_SHA1_Update                git_SHA1_Update_Chunked
+#endif
+
+#ifndef SHA1_NEEDS_CLONE_HELPER
+static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
+{
+       memcpy(dst, src, sizeof(*dst));
+}
+#endif
+
+#ifndef SHA256_NEEDS_CLONE_HELPER
+static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
+{
+       memcpy(dst, src, sizeof(*dst));
+}
+#endif
+
+/*
+ * Note that these constants are suitable for indexing the hash_algos array and
+ * comparing against each other, but are otherwise arbitrary, so they should not
+ * be exposed to the user or serialized to disk.  To know whether a
+ * git_hash_algo struct points to some usable hash function, test the format_id
+ * field for being non-zero.  Use the name field for user-visible situations and
+ * the format_id field for fixed-length fields on disk.
+ */
+/* An unknown hash function. */
+#define GIT_HASH_UNKNOWN 0
+/* SHA-1 */
+#define GIT_HASH_SHA1 1
+/* SHA-256  */
+#define GIT_HASH_SHA256 2
+/* Number of algorithms supported (including unknown). */
+#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
+
+/* "sha1", big-endian */
+#define GIT_SHA1_FORMAT_ID 0x73686131
+
+/* The length in bytes and in hex digits of an object name (SHA-1 value). */
+#define GIT_SHA1_RAWSZ 20
+#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The block size of SHA-1. */
+#define GIT_SHA1_BLKSZ 64
+
+/* "s256", big-endian */
+#define GIT_SHA256_FORMAT_ID 0x73323536
+
+/* The length in bytes and in hex digits of an object name (SHA-256 value). */
+#define GIT_SHA256_RAWSZ 32
+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
+/* The block size of SHA-256. */
+#define GIT_SHA256_BLKSZ 64
+
+/* The length in byte and in hex digits of the largest possible hash value. */
+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
+/* The largest possible block size for any supported hash. */
+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
+
+struct object_id {
+       unsigned char hash[GIT_MAX_RAWSZ];
+       int algo;       /* XXX requires 4-byte alignment */
+};
+
+#define GET_OID_QUIETLY           01
+#define GET_OID_COMMIT            02
+#define GET_OID_COMMITTISH        04
+#define GET_OID_TREE             010
+#define GET_OID_TREEISH          020
+#define GET_OID_BLOB             040
+#define GET_OID_FOLLOW_SYMLINKS 0100
+#define GET_OID_RECORD_PATH     0200
+#define GET_OID_ONLY_TO_DIE    04000
+#define GET_OID_REQUIRE_PATH  010000
+
+#define GET_OID_DISAMBIGUATORS \
+       (GET_OID_COMMIT | GET_OID_COMMITTISH | \
+       GET_OID_TREE | GET_OID_TREEISH | \
+       GET_OID_BLOB)
+
+enum get_oid_result {
+       FOUND = 0,
+       MISSING_OBJECT = -1, /* The requested object is missing */
+       SHORT_NAME_AMBIGUOUS = -2,
+       /* The following only apply when symlinks are followed */
+       DANGLING_SYMLINK = -4, /*
+                               * The initial symlink is there, but
+                               * (transitively) points to a missing
+                               * in-tree file
+                               */
+       SYMLINK_LOOP = -5,
+       NOT_DIR = -6, /*
+                      * Somewhere along the symlink chain, a path is
+                      * requested which contains a file as a
+                      * non-final element.
+                      */
+};
+
+/* A suitably aligned type for stack allocations of hash contexts. */
+union git_hash_ctx {
+       git_SHA_CTX sha1;
+       git_SHA256_CTX sha256;
+};
+typedef union git_hash_ctx git_hash_ctx;
+
+typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
+typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
+typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
+typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
+typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);
+
+struct git_hash_algo {
+       /*
+        * The name of the algorithm, as appears in the config file and in
+        * messages.
+        */
+       const char *name;
+
+       /* A four-byte version identifier, used in pack indices. */
+       uint32_t format_id;
+
+       /* The length of the hash in binary. */
+       size_t rawsz;
+
+       /* The length of the hash in hex characters. */
+       size_t hexsz;
+
+       /* The block size of the hash. */
+       size_t blksz;
+
+       /* The hash initialization function. */
+       git_hash_init_fn init_fn;
+
+       /* The hash context cloning function. */
+       git_hash_clone_fn clone_fn;
+
+       /* The hash update function. */
+       git_hash_update_fn update_fn;
+
+       /* The hash finalization function. */
+       git_hash_final_fn final_fn;
+
+       /* The hash finalization function for object IDs. */
+       git_hash_final_oid_fn final_oid_fn;
+
+       /* The OID of the empty tree. */
+       const struct object_id *empty_tree;
+
+       /* The OID of the empty blob. */
+       const struct object_id *empty_blob;
+
+       /* The all-zeros OID. */
+       const struct object_id *null_oid;
+};
+extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
+
+/*
+ * Return a GIT_HASH_* constant based on the name.  Returns GIT_HASH_UNKNOWN if
+ * the name doesn't match a known algorithm.
+ */
+int hash_algo_by_name(const char *name);
+/* Identical, except based on the format ID. */
+int hash_algo_by_id(uint32_t format_id);
+/* Identical, except based on the length. */
+int hash_algo_by_length(int len);
+/* Identical, except for a pointer to struct git_hash_algo. */
+static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
+{
+       return p - hash_algos;
+}
+
+const struct object_id *null_oid(void);
+
+static inline int hashcmp_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
+{
+       /*
+        * Teach the compiler that there are only two possibilities of hash size
+        * here, so that it can optimize for this case as much as possible.
+        */
+       if (algop->rawsz == GIT_MAX_RAWSZ)
+               return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+       return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+}
+
+static inline int hasheq_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
+{
+       /*
+        * We write this here instead of deferring to hashcmp so that the
+        * compiler can properly inline it and avoid calling memcmp.
+        */
+       if (algop->rawsz == GIT_MAX_RAWSZ)
+               return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+       return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+}
+
+static inline void oidcpy(struct object_id *dst, const struct object_id *src)
+{
+       memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
+       dst->algo = src->algo;
+}
+
+static inline struct object_id *oiddup(const struct object_id *src)
+{
+       struct object_id *dst = xmalloc(sizeof(struct object_id));
+       oidcpy(dst, src);
+       return dst;
+}
+
+static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
+{
+       oid->algo = hash_algo_by_ptr(algop);
+}
+
+/*
+ * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
+ * for use in hash tables. Cryptographic hashes are supposed to have
+ * uniform distribution, so in contrast to `memhash()`, this just copies
+ * the first `sizeof(int)` bytes without shuffling any bits. Note that
+ * the results will be different on big-endian and little-endian
+ * platforms, so they should not be stored or transferred over the net.
+ */
+static inline unsigned int oidhash(const struct object_id *oid)
+{
+       /*
+        * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
+        * platforms that don't support unaligned reads.
+        */
+       unsigned int hash;
+       memcpy(&hash, oid->hash, sizeof(hash));
+       return hash;
+}
+
+const char *empty_tree_oid_hex(void);
+const char *empty_blob_oid_hex(void);
+
+#endif
index b98ed5e11e8a3af02a99b6d2a977cc804a122414..9f0f95e2b9e0c44e79bfe6ee3ec43b61e7991f4a 100644 (file)
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hash.h"
 #include "hash-lookup.h"
+#include "read-cache-ll.h"
 
 static uint32_t take2(const struct object_id *oid, size_t ofs)
 {
diff --git a/hash.h b/hash.h
index 36b64165fc96375457415a6348eeebd2944c41f9..615ae0691d070b4bdc110fecad3a0c484d2b234c 100644 (file)
--- a/hash.h
+++ b/hash.h
 #ifndef HASH_H
 #define HASH_H
 
-#include "git-compat-util.h"
+#include "hash-ll.h"
 #include "repository.h"
 
-#if defined(SHA1_APPLE)
-#include <CommonCrypto/CommonDigest.h>
-#elif defined(SHA1_OPENSSL)
-#include <openssl/sha.h>
-#elif defined(SHA1_DC)
-#include "sha1dc_git.h"
-#else /* SHA1_BLK */
-#include "block-sha1/sha1.h"
-#endif
-
-#if defined(SHA256_NETTLE)
-#include "sha256/nettle.h"
-#elif defined(SHA256_GCRYPT)
-#define SHA256_NEEDS_CLONE_HELPER
-#include "sha256/gcrypt.h"
-#elif defined(SHA256_OPENSSL)
-#include <openssl/sha.h>
-#else
-#include "sha256/block/sha256.h"
-#endif
-
-#ifndef platform_SHA_CTX
-/*
- * platform's underlying implementation of SHA-1; could be OpenSSL,
- * blk_SHA, Apple CommonCrypto, etc...  Note that the relevant
- * SHA-1 header may have already defined platform_SHA_CTX for our
- * own implementations like block-sha1, so we list
- * the default for OpenSSL compatible SHA-1 implementations here.
- */
-#define platform_SHA_CTX       SHA_CTX
-#define platform_SHA1_Init     SHA1_Init
-#define platform_SHA1_Update   SHA1_Update
-#define platform_SHA1_Final            SHA1_Final
-#endif
-
-#define git_SHA_CTX            platform_SHA_CTX
-#define git_SHA1_Init          platform_SHA1_Init
-#define git_SHA1_Update                platform_SHA1_Update
-#define git_SHA1_Final         platform_SHA1_Final
-
-#ifndef platform_SHA256_CTX
-#define platform_SHA256_CTX    SHA256_CTX
-#define platform_SHA256_Init   SHA256_Init
-#define platform_SHA256_Update SHA256_Update
-#define platform_SHA256_Final  SHA256_Final
-#endif
-
-#define git_SHA256_CTX         platform_SHA256_CTX
-#define git_SHA256_Init                platform_SHA256_Init
-#define git_SHA256_Update      platform_SHA256_Update
-#define git_SHA256_Final       platform_SHA256_Final
-
-#ifdef platform_SHA256_Clone
-#define git_SHA256_Clone       platform_SHA256_Clone
-#endif
-
-#ifdef SHA1_MAX_BLOCK_SIZE
-#include "compat/sha1-chunked.h"
-#undef git_SHA1_Update
-#define git_SHA1_Update                git_SHA1_Update_Chunked
-#endif
-
-static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
-{
-       memcpy(dst, src, sizeof(*dst));
-}
-
-#ifndef SHA256_NEEDS_CLONE_HELPER
-static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
-{
-       memcpy(dst, src, sizeof(*dst));
-}
-#endif
-
-/*
- * Note that these constants are suitable for indexing the hash_algos array and
- * comparing against each other, but are otherwise arbitrary, so they should not
- * be exposed to the user or serialized to disk.  To know whether a
- * git_hash_algo struct points to some usable hash function, test the format_id
- * field for being non-zero.  Use the name field for user-visible situations and
- * the format_id field for fixed-length fields on disk.
- */
-/* An unknown hash function. */
-#define GIT_HASH_UNKNOWN 0
-/* SHA-1 */
-#define GIT_HASH_SHA1 1
-/* SHA-256  */
-#define GIT_HASH_SHA256 2
-/* Number of algorithms supported (including unknown). */
-#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
-
-/* "sha1", big-endian */
-#define GIT_SHA1_FORMAT_ID 0x73686131
-
-/* The length in bytes and in hex digits of an object name (SHA-1 value). */
-#define GIT_SHA1_RAWSZ 20
-#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
-/* The block size of SHA-1. */
-#define GIT_SHA1_BLKSZ 64
-
-/* "s256", big-endian */
-#define GIT_SHA256_FORMAT_ID 0x73323536
-
-/* The length in bytes and in hex digits of an object name (SHA-256 value). */
-#define GIT_SHA256_RAWSZ 32
-#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
-/* The block size of SHA-256. */
-#define GIT_SHA256_BLKSZ 64
-
-/* The length in byte and in hex digits of the largest possible hash value. */
-#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
-#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
-/* The largest possible block size for any supported hash. */
-#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
-
-struct object_id {
-       unsigned char hash[GIT_MAX_RAWSZ];
-       int algo;       /* XXX requires 4-byte alignment */
-};
-
-/* A suitably aligned type for stack allocations of hash contexts. */
-union git_hash_ctx {
-       git_SHA_CTX sha1;
-       git_SHA256_CTX sha256;
-};
-typedef union git_hash_ctx git_hash_ctx;
-
-typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
-typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
-typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
-typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
-typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);
-
-struct git_hash_algo {
-       /*
-        * The name of the algorithm, as appears in the config file and in
-        * messages.
-        */
-       const char *name;
-
-       /* A four-byte version identifier, used in pack indices. */
-       uint32_t format_id;
-
-       /* The length of the hash in binary. */
-       size_t rawsz;
-
-       /* The length of the hash in hex characters. */
-       size_t hexsz;
-
-       /* The block size of the hash. */
-       size_t blksz;
-
-       /* The hash initialization function. */
-       git_hash_init_fn init_fn;
-
-       /* The hash context cloning function. */
-       git_hash_clone_fn clone_fn;
-
-       /* The hash update function. */
-       git_hash_update_fn update_fn;
-
-       /* The hash finalization function. */
-       git_hash_final_fn final_fn;
-
-       /* The hash finalization function for object IDs. */
-       git_hash_final_oid_fn final_oid_fn;
-
-       /* The OID of the empty tree. */
-       const struct object_id *empty_tree;
-
-       /* The OID of the empty blob. */
-       const struct object_id *empty_blob;
-
-       /* The all-zeros OID. */
-       const struct object_id *null_oid;
-};
-extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
-
-/*
- * Return a GIT_HASH_* constant based on the name.  Returns GIT_HASH_UNKNOWN if
- * the name doesn't match a known algorithm.
- */
-int hash_algo_by_name(const char *name);
-/* Identical, except based on the format ID. */
-int hash_algo_by_id(uint32_t format_id);
-/* Identical, except based on the length. */
-int hash_algo_by_length(int len);
-/* Identical, except for a pointer to struct git_hash_algo. */
-static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
-{
-       return p - hash_algos;
-}
-
 #define the_hash_algo the_repository->hash_algo
 
-const struct object_id *null_oid(void);
-
-static inline int hashcmp_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
-{
-       /*
-        * Teach the compiler that there are only two possibilities of hash size
-        * here, so that it can optimize for this case as much as possible.
-        */
-       if (algop->rawsz == GIT_MAX_RAWSZ)
-               return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
-       return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
-}
-
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
        return hashcmp_algop(sha1, sha2, the_hash_algo);
@@ -227,17 +21,6 @@ static inline int oidcmp(const struct object_id *oid1, const struct object_id *o
        return hashcmp_algop(oid1->hash, oid2->hash, algop);
 }
 
-static inline int hasheq_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
-{
-       /*
-        * We write this here instead of deferring to hashcmp so that the
-        * compiler can properly inline it and avoid calling memcmp.
-        */
-       if (algop->rawsz == GIT_MAX_RAWSZ)
-               return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
-       return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
-}
-
 static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
 {
        return hasheq_algop(sha1, sha2, the_hash_algo);
@@ -263,12 +46,6 @@ static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
        memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
 }
 
-static inline void oidcpy(struct object_id *dst, const struct object_id *src)
-{
-       memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
-       dst->algo = src->algo;
-}
-
 /* Like oidcpy() but zero-pads the unused bytes in dst's hash array. */
 static inline void oidcpy_with_padding(struct object_id *dst,
                                       const struct object_id *src)
@@ -285,13 +62,6 @@ static inline void oidcpy_with_padding(struct object_id *dst,
        dst->algo = src->algo;
 }
 
-static inline struct object_id *oiddup(const struct object_id *src)
-{
-       struct object_id *dst = xmalloc(sizeof(struct object_id));
-       oidcpy(dst, src);
-       return dst;
-}
-
 static inline void hashclr(unsigned char *hash)
 {
        memset(hash, 0, the_hash_algo->rawsz);
@@ -329,12 +99,4 @@ static inline int is_empty_tree_oid(const struct object_id *oid)
        return oideq(oid, the_hash_algo->empty_tree);
 }
 
-static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
-{
-       oid->algo = hash_algo_by_ptr(algop);
-}
-
-const char *empty_tree_oid_hex(void);
-const char *empty_blob_oid_hex(void);
-
 #endif
index cf5fea87eb02bf753d408f1eeb00f5de2a02e907..ee45ef00852c86b5f0fe0de8177c806c53d7efa0 100644 (file)
--- a/hashmap.c
+++ b/hashmap.c
@@ -1,7 +1,7 @@
 /*
  * Generic implementation of hash-based key value mappings.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "hashmap.h"
 
 #define FNV32_BASE ((unsigned int) 0x811c9dc5)
index 7251687d730d608437a8ed24e4551cf9344fd7f4..c8216e47bb21171a59cdcc1f17b600ae60699dfa 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -1,8 +1,6 @@
 #ifndef HASHMAP_H
 #define HASHMAP_H
 
-#include "hash.h"
-
 /*
  * Generic implementation of hash-based key-value mappings.
  *
@@ -120,25 +118,6 @@ unsigned int memhash(const void *buf, size_t len);
 unsigned int memihash(const void *buf, size_t len);
 unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len);
 
-/*
- * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
- * for use in hash tables. Cryptographic hashes are supposed to have
- * uniform distribution, so in contrast to `memhash()`, this just copies
- * the first `sizeof(int)` bytes without shuffling any bits. Note that
- * the results will be different on big-endian and little-endian
- * platforms, so they should not be stored or transferred over the net.
- */
-static inline unsigned int oidhash(const struct object_id *oid)
-{
-       /*
-        * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
-        * platforms that don't support unaligned reads.
-        */
-       unsigned int hash;
-       memcpy(&hash, oid->hash, sizeof(hash));
-       return hash;
-}
-
 /*
  * struct hashmap_entry is an opaque structure representing an entry in the
  * hash table.
@@ -270,7 +249,7 @@ void hashmap_clear_(struct hashmap *map, ssize_t offset);
 #define hashmap_clear(map) hashmap_clear_(map, -1)
 
 /*
- * Similar to hashmap_clear(), except that the table is no deallocated; it
+ * Similar to hashmap_clear(), except that the table is not deallocated; it
  * is merely zeroed out but left the same size as before.  If the hashmap
  * will be reused, this avoids the overhead of deallocating and
  * reallocating map->table.  As with hashmap_clear(), you may need to free
diff --git a/help.c b/help.c
index d04542d8261dd4e8eb2287edc3623492b34f3ce4..2dbe57b413b2a8f2e8262fb2c022711aa2d60a2a 100644 (file)
--- a/help.c
+++ b/help.c
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "builtin.h"
 #include "exec-cmd.h"
 #include "run-command.h"
 #include "levenshtein.h"
+#include "gettext.h"
 #include "help.h"
 #include "command-list.h"
 #include "string-list.h"
@@ -307,7 +308,8 @@ void load_command_list(const char *prefix,
        exclude_cmds(other_cmds, main_cmds);
 }
 
-static int get_colopts(const char *var, const char *value, void *data)
+static int get_colopts(const char *var, const char *value,
+                      const struct config_context *ctx UNUSED, void *data)
 {
        unsigned int *colopts = data;
 
@@ -457,12 +459,16 @@ void list_developer_interfaces_help(void)
        putchar('\n');
 }
 
-static int get_alias(const char *var, const char *value, void *data)
+static int get_alias(const char *var, const char *value,
+                    const struct config_context *ctx UNUSED, void *data)
 {
        struct string_list *list = data;
 
-       if (skip_prefix(var, "alias.", &var))
+       if (skip_prefix(var, "alias.", &var)) {
+               if (!value)
+                       return config_error_nonbool(var);
                string_list_append(list, var)->util = xstrdup(value);
+       }
 
        return 0;
 }
@@ -540,7 +546,9 @@ static struct cmdnames aliases;
 #define AUTOCORRECT_NEVER (-2)
 #define AUTOCORRECT_IMMEDIATELY (-1)
 
-static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
+static int git_unknown_cmd_config(const char *var, const char *value,
+                                 const struct config_context *ctx,
+                                 void *cb UNUSED)
 {
        const char *p;
 
@@ -554,7 +562,7 @@ static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
                } else if (!strcmp(value, "prompt")) {
                        autocorrect = AUTOCORRECT_PROMPT;
                } else {
-                       int v = git_config_int(var, value);
+                       int v = git_config_int(var, value, ctx->kvi);
                        autocorrect = (v < 0)
                                ? AUTOCORRECT_IMMEDIATELY : v;
                }
@@ -563,7 +571,7 @@ static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
        if (skip_prefix(var, "alias.", &p))
                add_cmdname(&aliases, p, strlen(p));
 
-       return git_default_config(var, value, cb);
+       return 0;
 }
 
 static int levenshtein_compare(const void *p1, const void *p2)
@@ -757,7 +765,7 @@ int cmd_version(int argc, const char **argv, const char *prefix)
        struct strbuf buf = STRBUF_INIT;
        int build_options = 0;
        const char * const usage[] = {
-               N_("git version [<options>]"),
+               N_("git version [--build-options]"),
                NULL
        };
        struct option options[] = {
diff --git a/hex-ll.c b/hex-ll.c
new file mode 100644 (file)
index 0000000..4d7ece1
--- /dev/null
+++ b/hex-ll.c
@@ -0,0 +1,49 @@
+#include "git-compat-util.h"
+#include "hex-ll.h"
+
+const signed char hexval_table[256] = {
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 00-07 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 08-0f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 10-17 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 18-1f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 20-27 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 28-2f */
+         0,  1,  2,  3,  4,  5,  6,  7,                /* 30-37 */
+         8,  9, -1, -1, -1, -1, -1, -1,                /* 38-3f */
+        -1, 10, 11, 12, 13, 14, 15, -1,                /* 40-47 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 48-4f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 50-57 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 58-5f */
+        -1, 10, 11, 12, 13, 14, 15, -1,                /* 60-67 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 68-67 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 70-77 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 78-7f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 80-87 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 88-8f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 90-97 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 98-9f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* a0-a7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* a8-af */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* b0-b7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* b8-bf */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* c0-c7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* c8-cf */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* d0-d7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* d8-df */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* e0-e7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* e8-ef */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* f0-f7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* f8-ff */
+};
+
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
+{
+       for (; len; len--, hex += 2) {
+               unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+
+               if (val & ~0xff)
+                       return -1;
+               *binary++ = val;
+       }
+       return 0;
+}
diff --git a/hex-ll.h b/hex-ll.h
new file mode 100644 (file)
index 0000000..a381fa8
--- /dev/null
+++ b/hex-ll.h
@@ -0,0 +1,27 @@
+#ifndef HEX_LL_H
+#define HEX_LL_H
+
+extern const signed char hexval_table[256];
+static inline unsigned int hexval(unsigned char c)
+{
+       return hexval_table[c];
+}
+
+/*
+ * Convert two consecutive hexadecimal digits into a char.  Return a
+ * negative value on error.  Don't run over the end of short strings.
+ */
+static inline int hex2chr(const char *s)
+{
+       unsigned int val = hexval(s[0]);
+       return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
+}
+
+/*
+ * Read `len` pairs of hexadecimal digits from `hex` and write the
+ * values to `binary` as `len` bytes. Return 0 on success, or -1 if
+ * the input does not consist of hex digits).
+ */
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
+
+#endif
diff --git a/hex.c b/hex.c
index 4f64d34696379d35e7ace281a5d471f55f1737ad..d42262bdca71a8703ce191393f0217f0c589a75e 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -1,51 +1,6 @@
-#include "cache.h"
-
-const signed char hexval_table[256] = {
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 00-07 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 08-0f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 10-17 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 18-1f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 20-27 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 28-2f */
-         0,  1,  2,  3,  4,  5,  6,  7,                /* 30-37 */
-         8,  9, -1, -1, -1, -1, -1, -1,                /* 38-3f */
-        -1, 10, 11, 12, 13, 14, 15, -1,                /* 40-47 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 48-4f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 50-57 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 58-5f */
-        -1, 10, 11, 12, 13, 14, 15, -1,                /* 60-67 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 68-67 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 70-77 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 78-7f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 80-87 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 88-8f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 90-97 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 98-9f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* a0-a7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* a8-af */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* b0-b7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* b8-bf */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* c0-c7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* c8-cf */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* d0-d7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* d8-df */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* e0-e7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* e8-ef */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* f0-f7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* f8-ff */
-};
-
-int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
-{
-       for (; len; len--, hex += 2) {
-               unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
-
-               if (val & ~0xff)
-                       return -1;
-               *binary++ = val;
-       }
-       return 0;
-}
+#include "git-compat-util.h"
+#include "hash.h"
+#include "hex.h"
 
 static int get_hash_hex_algop(const char *hex, unsigned char *hash,
                              const struct git_hash_algo *algop)
@@ -61,7 +16,7 @@ static int get_hash_hex_algop(const char *hex, unsigned char *hash,
        return 0;
 }
 
-int get_sha1_hex(const char *hex, unsigned char *sha1)
+int get_hash_hex(const char *hex, unsigned char *sha1)
 {
        return get_hash_hex_algop(hex, sha1, the_hash_algo);
 }
diff --git a/hex.h b/hex.h
new file mode 100644 (file)
index 0000000..e0b83f7
--- /dev/null
+++ b/hex.h
@@ -0,0 +1,64 @@
+#ifndef HEX_H
+#define HEX_H
+
+#include "hash-ll.h"
+#include "hex-ll.h"
+
+/*
+ * Try to read a hash (specified by the_hash_algo) in hexadecimal
+ * format from the 40 (or whatever length the hash algorithm uses)
+ * characters starting at hex.  Write the 20-byte (or the length of
+ * the hash) result to hash in binary form.
+ * Return 0 on success.  Reading stops if a NUL is encountered in the
+ * input, so it is safe to pass this function an arbitrary
+ * null-terminated string.
+ */
+int get_hash_hex(const char *hex, unsigned char *hash);
+int get_oid_hex(const char *hex, struct object_id *oid);
+
+/* Like get_oid_hex, but for an arbitrary hash algorithm. */
+int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
+
+/*
+ * Convert a binary hash in "unsigned char []" or an object name in
+ * "struct object_id *" to its hex equivalent. The `_r` variant is reentrant,
+ * and writes the NUL-terminated output to the buffer `out`, which must be at
+ * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
+ * convenience.
+ *
+ * The non-`_r` variant returns a static buffer, but uses a ring of 4
+ * buffers, making it safe to make multiple calls for a single statement, like:
+ *
+ *   printf("%s -> %s", hash_to_hex(one), hash_to_hex(two));
+ *   printf("%s -> %s", oid_to_hex(one), oid_to_hex(two));
+ */
+char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
+char *oid_to_hex_r(char *out, const struct object_id *oid);
+char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);      /* static buffer result! */
+char *hash_to_hex(const unsigned char *hash);                                          /* same static buffer */
+char *oid_to_hex(const struct object_id *oid);                                         /* same static buffer */
+
+/*
+ * Parse a 40-character hexadecimal object ID starting from hex, updating the
+ * pointer specified by end when parsing stops.  The resulting object ID is
+ * stored in oid.  Returns 0 on success.  Parsing will stop on the first NUL or
+ * other invalid character.  end is only updated on success; otherwise, it is
+ * unmodified.
+ */
+int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+
+/* Like parse_oid_hex, but for an arbitrary hash algorithm. */
+int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end,
+                       const struct git_hash_algo *algo);
+
+
+/*
+ * These functions work like get_oid_hex and parse_oid_hex, but they will parse
+ * a hex value for any algorithm. The algorithm is detected based on the length
+ * and the algorithm in use is returned. If this is not a hex object ID in any
+ * algorithm, returns GIT_HASH_UNKNOWN.
+ */
+int get_oid_hex_any(const char *hex, struct object_id *oid);
+int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
+
+#endif
diff --git a/hook.c b/hook.c
index a493939a4fc5901d85a9ad08ca10669641598389..f6306d72b31879705977a023a7865c3f1677404c 100644 (file)
--- a/hook.c
+++ b/hook.c
@@ -1,7 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
+#include "gettext.h"
 #include "hook.h"
+#include "path.h"
 #include "run-command.h"
 #include "config.h"
+#include "strbuf.h"
 
 const char *find_hook(const char *name)
 {
@@ -43,9 +48,9 @@ int hook_exists(const char *name)
 }
 
 static int pick_next_hook(struct child_process *cp,
-                         struct strbuf *out,
+                         struct strbuf *out UNUSED,
                          void *pp_cb,
-                         void **pp_task_cb)
+                         void **pp_task_cb UNUSED)
 {
        struct hook_cb_data *hook_cb = pp_cb;
        const char *hook_path = hook_cb->hook_path;
@@ -55,6 +60,11 @@ static int pick_next_hook(struct child_process *cp,
 
        cp->no_stdin = 1;
        strvec_pushv(&cp->env, hook_cb->options->env.v);
+       /* reopen the file for stdin; run_command closes it. */
+       if (hook_cb->options->path_to_stdin) {
+               cp->no_stdin = 0;
+               cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
+       }
        cp->stdout_to_stderr = 1;
        cp->trace2_hook_name = hook_cb->hook_name;
        cp->dir = hook_cb->options->dir;
@@ -72,9 +82,9 @@ static int pick_next_hook(struct child_process *cp,
        return 1;
 }
 
-static int notify_start_failure(struct strbuf *out,
+static int notify_start_failure(struct strbuf *out UNUSED,
                                void *pp_cb,
-                               void *pp_task_cp)
+                               void *pp_task_cp UNUSED)
 {
        struct hook_cb_data *hook_cb = pp_cb;
 
@@ -84,9 +94,9 @@ static int notify_start_failure(struct strbuf *out,
 }
 
 static int notify_hook_finished(int result,
-                               struct strbuf *out,
+                               struct strbuf *out UNUSED,
                                void *pp_cb,
-                               void *pp_task_cb)
+                               void *pp_task_cb UNUSED)
 {
        struct hook_cb_data *hook_cb = pp_cb;
        struct run_hooks_opt *opt = hook_cb->options;
@@ -114,8 +124,20 @@ int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
                .options = options,
        };
        const char *const hook_path = find_hook(hook_name);
-       int jobs = 1;
        int ret = 0;
+       const struct run_process_parallel_opts opts = {
+               .tr2_category = "hook",
+               .tr2_label = hook_name,
+
+               .processes = 1,
+               .ungroup = 1,
+
+               .get_next_task = pick_next_hook,
+               .start_failure = notify_start_failure,
+               .task_finished = notify_hook_finished,
+
+               .data = &cb_data,
+       };
 
        if (!options)
                BUG("a struct run_hooks_opt must be provided to run_hooks");
@@ -137,14 +159,7 @@ int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
                cb_data.hook_path = abs_path.buf;
        }
 
-       run_processes_parallel_ungroup = 1;
-       run_processes_parallel_tr2(jobs,
-                                  pick_next_hook,
-                                  notify_start_failure,
-                                  notify_hook_finished,
-                                  &cb_data,
-                                  "hook",
-                                  hook_name);
+       run_processes_parallel(&opts);
        ret = cb_data.rc;
 cleanup:
        strbuf_release(&abs_path);
diff --git a/hook.h b/hook.h
index 4258b13da0d7c3c88a81b79eb25e8cc6a29dc6b6..19ab9a5806e1c1de11f7b300471c957fa5e6b4d4 100644 (file)
--- a/hook.h
+++ b/hook.h
@@ -30,6 +30,11 @@ struct run_hooks_opt
         * was invoked.
         */
        int *invoked_hook;
+
+       /**
+        * Path to file which should be piped to stdin for each hook.
+        */
+       const char *path_to_stdin;
 };
 
 #define RUN_HOOKS_OPT_INIT { \
index 6eb3b2fe51c6fe839f2dec5dd584cf174d5b5f73..ff07b87e6461446610c35708a39f22a5e5b36aff 100644 (file)
@@ -1,5 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "git-zlib.h"
+#include "hex.h"
+#include "path.h"
 #include "repository.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "url.h"
 #include "strvec.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "protocol.h"
 #include "date.h"
+#include "write-or-die.h"
 
 static const char content_type[] = "Content-Type";
 static const char content_length[] = "Content-Length";
@@ -524,7 +529,7 @@ static int show_text_ref(const char *name, const struct object_id *oid,
        return 0;
 }
 
-static void get_info_refs(struct strbuf *hdr, char *arg)
+static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
 {
        const char *service_name = get_parameter("service");
        struct strbuf buf = STRBUF_INIT;
@@ -553,7 +558,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg)
 
        } else {
                select_getanyfile(hdr);
-               for_each_namespaced_ref(show_text_ref, &buf);
+               for_each_namespaced_ref(NULL, show_text_ref, &buf);
                send_strbuf(hdr, "text/plain", &buf);
        }
        strbuf_release(&buf);
@@ -578,7 +583,7 @@ static int show_head_ref(const char *refname, const struct object_id *oid,
        return 0;
 }
 
-static void get_head(struct strbuf *hdr, char *arg)
+static void get_head(struct strbuf *hdr, char *arg UNUSED)
 {
        struct strbuf buf = STRBUF_INIT;
 
@@ -588,7 +593,7 @@ static void get_head(struct strbuf *hdr, char *arg)
        strbuf_release(&buf);
 }
 
-static void get_info_packs(struct strbuf *hdr, char *arg)
+static void get_info_packs(struct strbuf *hdr, char *arg UNUSED)
 {
        size_t objdirlen = strlen(get_object_directory());
        struct strbuf buf = STRBUF_INIT;
@@ -736,7 +741,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
        return 0;
 }
 
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
 {
        char *method = getenv("REQUEST_METHOD");
        const char *proto_header;
@@ -759,10 +764,14 @@ int cmd_main(int argc, const char **argv)
                struct service_cmd *c = &services[i];
                regex_t re;
                regmatch_t out[1];
+               int ret;
 
                if (regcomp(&re, c->pattern, REG_EXTENDED))
                        die("Bogus regex in service table: %s", c->pattern);
-               if (!regexec(&re, dir, 1, out, 0)) {
+               ret = regexec(&re, dir, 1, out, 0);
+               regfree(&re);
+
+               if (!ret) {
                        size_t n;
 
                        if (strcmp(method, c->method))
@@ -774,7 +783,6 @@ int cmd_main(int argc, const char **argv)
                        dir[out[0].rm_so] = 0;
                        break;
                }
-               regfree(&re);
        }
 
        if (!cmd)
@@ -786,6 +794,7 @@ int cmd_main(int argc, const char **argv)
        if (!getenv("GIT_HTTP_EXPORT_ALL") &&
            access("git-daemon-export-ok", F_OK) )
                not_found(&hdr, "Repository not exported: '%s'", dir);
+       free(dir);
 
        http_config();
        max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER",
@@ -795,5 +804,6 @@ int cmd_main(int argc, const char **argv)
                setenv(GIT_PROTOCOL_ENVIRONMENT, proto_header, 0);
 
        cmd->imp(&hdr, cmd_arg);
+       free(cmd_arg);
        return 0;
 }
index 31bc5c7767ce865bb3fb1a89f311f7d2b03c7e6d..bec94988bbe563a5048c6dd5905f1a9827193489 100644 (file)
@@ -1,10 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "exec-cmd.h"
+#include "gettext.h"
+#include "hex.h"
 #include "http.h"
 #include "walker.h"
+#include "setup.h"
 #include "strvec.h"
+#include "url.h"
 #include "urlmatch.h"
+#include "trace2.h"
 
 static const char http_fetch_usage[] = "git http-fetch "
 "[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin | --packfile=hash | commit-id] url";
@@ -137,6 +141,8 @@ int cmd_main(int argc, const char **argv)
        if (nongit)
                die(_("not a git repository"));
 
+       trace2_cmd_name("http-fetch");
+
        git_config(git_default_config, NULL);
 
        if (packfile) {
index 785b3193429e0dcdde1c04211466a424a882b8a5..12d111374107a7a071ac90d035c5172af2b63c79 100644 (file)
@@ -1,19 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "commit.h"
 #include "tag.h"
 #include "blob.h"
 #include "http.h"
-#include "refs.h"
 #include "diff.h"
 #include "revision.h"
-#include "exec-cmd.h"
 #include "remote.h"
 #include "list-objects.h"
+#include "setup.h"
 #include "sigchain.h"
 #include "strvec.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "url.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "commit-reach.h"
 
 #ifdef EXPAT_NEEDS_XMLPARSE_H
@@ -362,7 +366,8 @@ static void start_put(struct transfer_request *request)
        ssize_t size;
        git_zstream stream;
 
-       unpacked = read_object_file(&request->obj->oid, &type, &len);
+       unpacked = repo_read_object_file(the_repository, &request->obj->oid,
+                                        &type, &len);
        hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
 
        /* Set it up */
@@ -601,7 +606,7 @@ static void finish_request(struct transfer_request *request)
 }
 
 static int is_running_queue;
-static int fill_active_slot(void *unused)
+static int fill_active_slot(void *data UNUSED)
 {
        struct transfer_request *request;
 
@@ -777,7 +782,7 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
 static void one_remote_ref(const char *refname);
 
 static void
-xml_start_tag(void *userData, const char *name, const char **atts)
+xml_start_tag(void *userData, const char *name, const char **atts UNUSED)
 {
        struct xml_ctx *ctx = (struct xml_ctx *)userData;
        const char *c = strchr(name, ':');
@@ -1331,7 +1336,8 @@ static int get_delta(struct rev_info *revs, struct remote_lock *lock)
        int count = 0;
 
        while ((commit = get_revision(revs)) != NULL) {
-               p = process_tree(get_commit_tree(commit), p);
+               p = process_tree(repo_get_commit_tree(the_repository, commit),
+                                p);
                commit->object.flags |= LOCAL;
                if (!(commit->object.flags & UNINTERESTING))
                        count += add_send_request(&commit->object, lock);
@@ -1426,7 +1432,7 @@ static void one_remote_ref(const char *refname)
         * Fetch a copy of the object if it doesn't exist locally - it
         * may be required for updating server info later.
         */
-       if (repo->can_update_info_refs && !has_object_file(&ref->old_oid)) {
+       if (repo->can_update_info_refs && !repo_has_object_file(the_repository, &ref->old_oid)) {
                obj = lookup_unknown_object(the_repository, &ref->old_oid);
                fprintf(stderr, "  fetch %s for %s\n",
                        oid_to_hex(&ref->old_oid), refname);
@@ -1570,7 +1576,7 @@ static int verify_merge_base(struct object_id *head_oid, struct ref *remote)
        struct commit *branch = lookup_commit_or_die(&remote->old_oid,
                                                     remote->name);
 
-       return in_merge_bases(branch, head);
+       return repo_in_merge_bases(the_repository, branch, head);
 }
 
 static int delete_remote_branch(const char *pattern, int force)
@@ -1627,14 +1633,14 @@ static int delete_remote_branch(const char *pattern, int force)
                        return error("Remote HEAD symrefs too deep");
                if (is_null_oid(&head_oid))
                        return error("Unable to resolve remote HEAD");
-               if (!has_object_file(&head_oid))
+               if (!repo_has_object_file(the_repository, &head_oid))
                        return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", oid_to_hex(&head_oid));
 
                /* Remote branch must resolve to a known object */
                if (is_null_oid(&remote_ref->old_oid))
                        return error("Unable to resolve remote branch %s",
                                     remote_ref->name);
-               if (!has_object_file(&remote_ref->old_oid))
+               if (!repo_has_object_file(the_repository, &remote_ref->old_oid))
                        return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, oid_to_hex(&remote_ref->old_oid));
 
                /* Remote branch must be an ancestor of remote HEAD */
@@ -1855,7 +1861,7 @@ int cmd_main(int argc, const char **argv)
                if (!force_all &&
                    !is_null_oid(&ref->old_oid) &&
                    !ref->force) {
-                       if (!has_object_file(&ref->old_oid) ||
+                       if (!repo_has_object_file(the_repository, &ref->old_oid) ||
                            !ref_newer(&ref->peer_ref->new_oid,
                                       &ref->old_oid)) {
                                /*
index b8f0f98ae146999adf3770cc7338cbacbbbf8cb3..b395ef13279eaa3f672e36512a7816fee209abf9 100644 (file)
@@ -1,12 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "repository.h"
-#include "commit.h"
+#include "hex.h"
 #include "walker.h"
 #include "http.h"
 #include "list.h"
 #include "transport.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 struct alt_base {
        char *base;
@@ -52,8 +52,7 @@ static void fetch_alternates(struct walker *walker, const char *base);
 
 static void process_object_response(void *callback_data);
 
-static void start_object_request(struct walker *walker,
-                                struct object_request *obj_req)
+static void start_object_request(struct object_request *obj_req)
 {
        struct active_request_slot *slot;
        struct http_object_request *req;
@@ -110,7 +109,7 @@ static void process_object_response(void *callback_data)
                        obj_req->repo =
                                obj_req->repo->next;
                        release_http_object_request(obj_req->req);
-                       start_object_request(walker, obj_req);
+                       start_object_request(obj_req);
                        return;
                }
        }
@@ -127,7 +126,7 @@ static void release_object_request(struct object_request *obj_req)
        free(obj_req);
 }
 
-static int fill_active_slot(struct walker *walker)
+static int fill_active_slot(void *data UNUSED)
 {
        struct object_request *obj_req;
        struct list_head *pos, *tmp, *head = &object_queue_head;
@@ -135,10 +134,10 @@ static int fill_active_slot(struct walker *walker)
        list_for_each_safe(pos, tmp, head) {
                obj_req = list_entry(pos, struct object_request, node);
                if (obj_req->state == WAITING) {
-                       if (has_object_file(&obj_req->oid))
+                       if (repo_has_object_file(the_repository, &obj_req->oid))
                                obj_req->state = COMPLETE;
                        else {
-                               start_object_request(walker, obj_req);
+                               start_object_request(obj_req);
                                return 1;
                        }
                }
@@ -492,7 +491,7 @@ static int fetch_object(struct walker *walker, unsigned char *hash)
        if (!obj_req)
                return error("Couldn't find request for %s in the queue", hex);
 
-       if (has_object_file(&obj_req->oid)) {
+       if (repo_has_object_file(the_repository, &obj_req->oid)) {
                if (obj_req->req)
                        abort_http_object_request(obj_req->req);
                abort_object_request(obj_req);
@@ -613,7 +612,7 @@ struct walker *get_http_walker(const char *url)
        walker->cleanup = cleanup;
        walker->data = data;
 
-       add_fill_function(walker, (int (*)(void *)) fill_active_slot);
+       add_fill_function(NULL, fill_active_slot);
 
        return walker;
 }
diff --git a/http.c b/http.c
index 06de051c532fc2712caaef994575132f81f3a88d..e73b136e5897bd8fce2f874b20f316141ba59c31 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,9 +1,9 @@
 #include "git-compat-util.h"
 #include "git-curl-compat.h"
+#include "hex.h"
 #include "http.h"
 #include "config.h"
 #include "pack.h"
-#include "sideband.h"
 #include "run-command.h"
 #include "url.h"
 #include "urlmatch.h"
 #include "version.h"
 #include "pkt-line.h"
 #include "gettext.h"
+#include "trace.h"
 #include "transport.h"
 #include "packfile.h"
-#include "protocol.h"
 #include "string-list.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 
 static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
 static int trace_curl_data = 1;
@@ -39,6 +40,7 @@ static int curl_ssl_verify = -1;
 static int curl_ssl_try;
 static const char *curl_http_version = NULL;
 static const char *ssl_cert;
+static const char *ssl_cert_type;
 static const char *ssl_cipherlist;
 static const char *ssl_version;
 static struct {
@@ -58,6 +60,7 @@ static struct {
 #endif
 };
 static const char *ssl_key;
+static const char *ssl_key_type;
 static const char *ssl_capath;
 static const char *curl_no_proxy;
 #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
@@ -181,7 +184,117 @@ size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
        return nmemb;
 }
 
-size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
+/*
+ * A folded header continuation line starts with any number of spaces or
+ * horizontal tab characters (SP or HTAB) as per RFC 7230 section 3.2.
+ * It is not a continuation line if the line starts with any other character.
+ */
+static inline int is_hdr_continuation(const char *ptr, const size_t size)
+{
+       return size && (*ptr == ' ' || *ptr == '\t');
+}
+
+static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p UNUSED)
+{
+       size_t size = eltsize * nmemb;
+       struct strvec *values = &http_auth.wwwauth_headers;
+       struct strbuf buf = STRBUF_INIT;
+       const char *val;
+       size_t val_len;
+
+       /*
+        * Header lines may not come NULL-terminated from libcurl so we must
+        * limit all scans to the maximum length of the header line, or leverage
+        * strbufs for all operations.
+        *
+        * In addition, it is possible that header values can be split over
+        * multiple lines as per RFC 7230. 'Line folding' has been deprecated
+        * but older servers may still emit them. A continuation header field
+        * value is identified as starting with a space or horizontal tab.
+        *
+        * The formal definition of a header field as given in RFC 7230 is:
+        *
+        * header-field   = field-name ":" OWS field-value OWS
+        *
+        * field-name     = token
+        * field-value    = *( field-content / obs-fold )
+        * field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+        * field-vchar    = VCHAR / obs-text
+        *
+        * obs-fold       = CRLF 1*( SP / HTAB )
+        *                ; obsolete line folding
+        *                ; see Section 3.2.4
+        */
+
+       /* Start of a new WWW-Authenticate header */
+       if (skip_iprefix_mem(ptr, size, "www-authenticate:", &val, &val_len)) {
+               strbuf_add(&buf, val, val_len);
+
+               /*
+                * Strip the CRLF that should be present at the end of each
+                * field as well as any trailing or leading whitespace from the
+                * value.
+                */
+               strbuf_trim(&buf);
+
+               strvec_push(values, buf.buf);
+               http_auth.header_is_last_match = 1;
+               goto exit;
+       }
+
+       /*
+        * This line could be a continuation of the previously matched header
+        * field. If this is the case then we should append this value to the
+        * end of the previously consumed value.
+        */
+       if (http_auth.header_is_last_match && is_hdr_continuation(ptr, size)) {
+               /*
+                * Trim the CRLF and any leading or trailing from this line.
+                */
+               strbuf_add(&buf, ptr, size);
+               strbuf_trim(&buf);
+
+               /*
+                * At this point we should always have at least one existing
+                * value, even if it is empty. Do not bother appending the new
+                * value if this continuation header is itself empty.
+                */
+               if (!values->nr) {
+                       BUG("should have at least one existing header value");
+               } else if (buf.len) {
+                       char *prev = xstrdup(values->v[values->nr - 1]);
+
+                       /* Join two non-empty values with a single space. */
+                       const char *const sp = *prev ? " " : "";
+
+                       strvec_pop(values);
+                       strvec_pushf(values, "%s%s%s", prev, sp, buf.buf);
+                       free(prev);
+               }
+
+               goto exit;
+       }
+
+       /* Not a continuation of a previously matched auth header line. */
+       http_auth.header_is_last_match = 0;
+
+       /*
+        * If this is a HTTP status line and not a header field, this signals
+        * a different HTTP response. libcurl writes all the output of all
+        * response headers of all responses, including redirects.
+        * We only care about the last HTTP request response's headers so clear
+        * the existing array.
+        */
+       if (skip_iprefix_mem(ptr, size, "http/", &val, &val_len))
+               strvec_clear(values);
+
+exit:
+       strbuf_release(&buf);
+       return size;
+}
+
+size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,
+                  void *data UNUSED)
 {
        return nmemb;
 }
@@ -249,7 +362,8 @@ static void process_curl_messages(void)
        }
 }
 
-static int http_options(const char *var, const char *value, void *cb)
+static int http_options(const char *var, const char *value,
+                       const struct config_context *ctx, void *data)
 {
        if (!strcmp("http.version", var)) {
                return git_config_string(&curl_http_version, var, value);
@@ -264,8 +378,12 @@ static int http_options(const char *var, const char *value, void *cb)
                return git_config_string(&ssl_version, var, value);
        if (!strcmp("http.sslcert", var))
                return git_config_pathname(&ssl_cert, var, value);
+       if (!strcmp("http.sslcerttype", var))
+               return git_config_string(&ssl_cert_type, var, value);
        if (!strcmp("http.sslkey", var))
                return git_config_pathname(&ssl_key, var, value);
+       if (!strcmp("http.sslkeytype", var))
+               return git_config_string(&ssl_key_type, var, value);
        if (!strcmp("http.sslcapath", var))
                return git_config_pathname(&ssl_capath, var, value);
        if (!strcmp("http.sslcainfo", var))
@@ -295,21 +413,21 @@ static int http_options(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp("http.minsessions", var)) {
-               min_curl_sessions = git_config_int(var, value);
+               min_curl_sessions = git_config_int(var, value, ctx->kvi);
                if (min_curl_sessions > 1)
                        min_curl_sessions = 1;
                return 0;
        }
        if (!strcmp("http.maxrequests", var)) {
-               max_requests = git_config_int(var, value);
+               max_requests = git_config_int(var, value, ctx->kvi);
                return 0;
        }
        if (!strcmp("http.lowspeedlimit", var)) {
-               curl_low_speed_limit = (long)git_config_int(var, value);
+               curl_low_speed_limit = (long)git_config_int(var, value, ctx->kvi);
                return 0;
        }
        if (!strcmp("http.lowspeedtime", var)) {
-               curl_low_speed_time = (long)git_config_int(var, value);
+               curl_low_speed_time = (long)git_config_int(var, value, ctx->kvi);
                return 0;
        }
 
@@ -345,7 +463,7 @@ static int http_options(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp("http.postbuffer", var)) {
-               http_post_buffer = git_config_ssize_t(var, value);
+               http_post_buffer = git_config_ssize_t(var, value, ctx->kvi);
                if (http_post_buffer < 0)
                        warning(_("negative value for http.postBuffer; defaulting to %d"), LARGE_PACKET_MAX);
                if (http_post_buffer < LARGE_PACKET_MAX)
@@ -416,7 +534,7 @@ static int http_options(const char *var, const char *value, void *cb)
        }
 
        /* Fall back on the default ones */
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, data);
 }
 
 static int curl_empty_auth_enabled(void)
@@ -558,13 +676,15 @@ static void set_curl_keepalive(CURL *c)
 }
 #endif
 
-static void redact_sensitive_header(struct strbuf *header)
+/* Return 1 if redactions have been made, 0 otherwise. */
+static int redact_sensitive_header(struct strbuf *header, size_t offset)
 {
+       int ret = 0;
        const char *sensitive_header;
 
        if (trace_curl_redact &&
-           (skip_iprefix(header->buf, "Authorization:", &sensitive_header) ||
-            skip_iprefix(header->buf, "Proxy-Authorization:", &sensitive_header))) {
+           (skip_iprefix(header->buf + offset, "Authorization:", &sensitive_header) ||
+            skip_iprefix(header->buf + offset, "Proxy-Authorization:", &sensitive_header))) {
                /* The first token is the type, which is OK to log */
                while (isspace(*sensitive_header))
                        sensitive_header++;
@@ -573,8 +693,9 @@ static void redact_sensitive_header(struct strbuf *header)
                /* Everything else is opaque and possibly sensitive */
                strbuf_setlen(header,  sensitive_header - header->buf);
                strbuf_addstr(header, " <redacted>");
+               ret = 1;
        } else if (trace_curl_redact &&
-                  skip_iprefix(header->buf, "Cookie:", &sensitive_header)) {
+                  skip_iprefix(header->buf + offset, "Cookie:", &sensitive_header)) {
                struct strbuf redacted_header = STRBUF_INIT;
                const char *cookie;
 
@@ -610,6 +731,52 @@ static void redact_sensitive_header(struct strbuf *header)
 
                strbuf_setlen(header, sensitive_header - header->buf);
                strbuf_addbuf(header, &redacted_header);
+               ret = 1;
+       }
+       return ret;
+}
+
+static int match_curl_h2_trace(const char *line, const char **out)
+{
+       const char *p;
+
+       /*
+        * curl prior to 8.1.0 gives us:
+        *
+        *     h2h3 [<header-name>: <header-val>]
+        *
+        * Starting in 8.1.0, the first token became just "h2".
+        */
+       if (skip_iprefix(line, "h2h3 [", out) ||
+           skip_iprefix(line, "h2 [", out))
+               return 1;
+
+       /*
+        * curl 8.3.0 uses:
+        *   [HTTP/2] [<stream-id>] [<header-name>: <header-val>]
+        * where <stream-id> is numeric.
+        */
+       if (skip_iprefix(line, "[HTTP/2] [", &p)) {
+               while (isdigit(*p))
+                       p++;
+               if (skip_prefix(p, "] [", out))
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* Redact headers in info */
+static void redact_sensitive_info_header(struct strbuf *header)
+{
+       const char *sensitive_header;
+
+       if (trace_curl_redact &&
+           match_curl_h2_trace(header->buf, &sensitive_header)) {
+               if (redact_sensitive_header(header, sensitive_header - header->buf)) {
+                       /* redaction ate our closing bracket */
+                       strbuf_addch(header, ']');
+               }
        }
 }
 
@@ -627,7 +794,7 @@ static void curl_dump_header(const char *text, unsigned char *ptr, size_t size,
 
        for (header = headers; *header; header++) {
                if (hide_sensitive_header)
-                       redact_sensitive_header(*header);
+                       redact_sensitive_header(*header, 0);
                strbuf_insertstr((*header), 0, text);
                strbuf_insertstr((*header), strlen(text), ": ");
                strbuf_rtrim((*header));
@@ -666,14 +833,28 @@ static void curl_dump_data(const char *text, unsigned char *ptr, size_t size)
        strbuf_release(&out);
 }
 
-static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp)
+static void curl_dump_info(char *data, size_t size)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       strbuf_add(&buf, data, size);
+
+       redact_sensitive_info_header(&buf);
+       trace_printf_key(&trace_curl, "== Info: %s", buf.buf);
+
+       strbuf_release(&buf);
+}
+
+static int curl_trace(CURL *handle UNUSED, curl_infotype type,
+                     char *data, size_t size,
+                     void *userp UNUSED)
 {
        const char *text;
        enum { NO_FILTER = 0, DO_FILTER = 1 };
 
        switch (type) {
        case CURLINFO_TEXT:
-               trace_printf_key(&trace_curl, "== Info: %s", data);
+               curl_dump_info(data, size);
                break;
        case CURLINFO_HEADER_OUT:
                text = "=> Send header";
@@ -869,10 +1050,14 @@ static CURL *get_curl_handle(void)
 
        if (ssl_cert)
                curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
+       if (ssl_cert_type)
+               curl_easy_setopt(result, CURLOPT_SSLCERTTYPE, ssl_cert_type);
        if (has_cert_password())
                curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password);
        if (ssl_key)
                curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
+       if (ssl_key_type)
+               curl_easy_setopt(result, CURLOPT_SSLKEYTYPE, ssl_key_type);
        if (ssl_capath)
                curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
 #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
@@ -1107,7 +1292,9 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
                curl_ssl_verify = 0;
 
        set_from_env(&ssl_cert, "GIT_SSL_CERT");
+       set_from_env(&ssl_cert_type, "GIT_SSL_CERT_TYPE");
        set_from_env(&ssl_key, "GIT_SSL_KEY");
+       set_from_env(&ssl_key_type, "GIT_SSL_KEY_TYPE");
        set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
        set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
@@ -1713,7 +1900,7 @@ static void write_accept_language(struct strbuf *buf)
         * MAX_DECIMAL_PLACES must not be larger than 3. If it is larger than
         * that, q-value will be smaller than 0.001, the minimum q-value the
         * HTTP specification allows. See
-        * http://tools.ietf.org/html/rfc7231#section-5.3.1 for q-value.
+        * https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1 for q-value.
         */
        const int MAX_DECIMAL_PLACES = 3;
        const int MAX_LANGUAGE_TAGS = 1000;
@@ -1860,6 +2047,8 @@ static int http_request(const char *url,
                                         fwrite_buffer);
        }
 
+       curl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION, fwrite_wwwauth);
+
        accept_language = http_get_accept_language_header();
 
        if (accept_language)
diff --git a/http.h b/http.h
index 77c042706c6597e347d1b8cc05d3f5972e2de117..3af19a8bf53e0550fa7c877bb661c34463283adf 100644 (file)
--- a/http.h
+++ b/http.h
@@ -1,14 +1,15 @@
 #ifndef HTTP_H
 #define HTTP_H
 
-#include "cache.h"
+struct packed_git;
+
+#include "git-zlib.h"
 
 #include <curl/curl.h>
 #include <curl/easy.h>
 
 #include "strbuf.h"
 #include "remote.h"
-#include "url.h"
 
 #define DEFAULT_MAX_REQUESTS 5
 
diff --git a/ident.c b/ident.c
index 6de76f9421d57f38c478cca68fcb97c4ede2d36c..cc7afdbf8197e981e02b6257a4618197063db69f 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -5,10 +5,13 @@
  *
  * Copyright (C) 2005 Linus Torvalds
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "ident.h"
 #include "config.h"
 #include "date.h"
+#include "gettext.h"
 #include "mailmap.h"
+#include "strbuf.h"
 
 static struct strbuf git_default_name = STRBUF_INIT;
 static struct strbuf git_default_email = STRBUF_INIT;
@@ -200,7 +203,6 @@ void reset_ident_date(void)
 static int crud(unsigned char c)
 {
        return  c <= 32  ||
-               c == '.' ||
                c == ',' ||
                c == ':' ||
                c == ';' ||
@@ -668,7 +670,9 @@ static int set_ident(const char *var, const char *value)
        return 0;
 }
 
-int git_ident_config(const char *var, const char *value, void *data UNUSED)
+int git_ident_config(const char *var, const char *value,
+                    const struct config_context *ctx UNUSED,
+                    void *data UNUSED)
 {
        if (!strcmp(var, "user.useconfigonly")) {
                ident_use_config_only = git_config_bool(var, value);
diff --git a/ident.h b/ident.h
new file mode 100644 (file)
index 0000000..6a79feb
--- /dev/null
+++ b/ident.h
@@ -0,0 +1,69 @@
+#ifndef IDENT_H
+#define IDENT_H
+
+#include "string-list.h"
+
+struct ident_split {
+       const char *name_begin;
+       const char *name_end;
+       const char *mail_begin;
+       const char *mail_end;
+       const char *date_begin;
+       const char *date_end;
+       const char *tz_begin;
+       const char *tz_end;
+};
+
+#define IDENT_STRICT          1
+#define IDENT_NO_DATE         2
+#define IDENT_NO_NAME         4
+
+enum want_ident {
+       WANT_BLANK_IDENT,
+       WANT_AUTHOR_IDENT,
+       WANT_COMMITTER_IDENT
+};
+
+const char *ident_default_name(void);
+const char *ident_default_email(void);
+/*
+ * Prepare an ident to fall back on if the user didn't configure it.
+ */
+void prepare_fallback_ident(const char *name, const char *email);
+void reset_ident_date(void);
+/*
+ * Signals an success with 0, but time part of the result may be NULL
+ * if the input lacks timestamp and zone
+ */
+int split_ident_line(struct ident_split *, const char *, int);
+
+/*
+ * Given a commit or tag object buffer and the commit or tag headers, replaces
+ * the idents in the headers with their canonical versions using the mailmap mechanism.
+ */
+void apply_mailmap_to_header(struct strbuf *, const char **, struct string_list *);
+
+/*
+ * Compare split idents for equality or strict ordering. Note that we
+ * compare only the ident part of the line, ignoring any timestamp.
+ *
+ * Because there are two fields, we must choose one as the primary key; we
+ * currently arbitrarily pick the email.
+ */
+int ident_cmp(const struct ident_split *, const struct ident_split *);
+
+const char *git_author_info(int);
+const char *git_committer_info(int);
+const char *fmt_ident(const char *name, const char *email,
+                     enum want_ident whose_ident,
+                     const char *date_str, int);
+const char *fmt_name(enum want_ident);
+
+int committer_ident_sufficiently_given(void);
+int author_ident_sufficiently_given(void);
+
+struct config_context;
+int git_ident_config(const char *, const char *, const struct config_context *,
+                    void *);
+
+#endif
index a50af56b827033dc68923e038566e7718fb81a65..d662811ee83a3531299accb59b8acea0e4de67d8 100644 (file)
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *  along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "credential.h"
-#include "exec-cmd.h"
+#include "gettext.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "setup.h"
 #if defined(NO_OPENSSL) && !defined(HAVE_OPENSSL_CSPRNG)
 typedef void *SSL;
 #endif
@@ -133,12 +134,10 @@ struct imap_store {
 };
 
 struct imap_cmd_cb {
-       int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
-       void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
+       int (*cont)(struct imap_store *ctx, const char *prompt);
        void *ctx;
        char *data;
        int dlen;
-       int uid;
 };
 
 struct imap_cmd {
@@ -205,10 +204,14 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
                else
                        fprintf(stderr, "%s: unexpected EOF\n", func);
        }
+       /* mark as used to appease -Wunused-parameter with NO_OPENSSL */
+       (void)sock;
 }
 
 #ifdef NO_OPENSSL
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock UNUSED,
+                             int use_tls_only UNUSED,
+                             int verify UNUSED)
 {
        fprintf(stderr, "SSL requested but SSL support not compiled in\n");
        return -1;
@@ -782,7 +785,7 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
                                if (n != (int)cmdp->cb.dlen)
                                        return RESP_BAD;
                        } else if (cmdp->cb.cont) {
-                               if (cmdp->cb.cont(ctx, cmdp, cmd))
+                               if (cmdp->cb.cont(ctx, cmd))
                                        return RESP_BAD;
                        } else {
                                fprintf(stderr, "IMAP error: unexpected command continuation request\n");
@@ -824,8 +827,6 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
                        }
                        if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
                                resp = resp2;
-                       if (cmdp->cb.done)
-                               cmdp->cb.done(ctx, cmdp, resp);
                        free(cmdp->cb.data);
                        free(cmdp->cmd);
                        free(cmdp);
@@ -857,7 +858,7 @@ static void imap_close_store(struct imap_store *ctx)
 
 /*
  * hexchar() and cram() functions are based on the code from the isync
- * project (http://isync.sf.net/).
+ * project (https://isync.sourceforge.io/).
  */
 static char hexchar(unsigned int b)
 {
@@ -905,7 +906,9 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
 
 #else
 
-static char *cram(const char *challenge_64, const char *user, const char *pass)
+static char *cram(const char *challenge_64 UNUSED,
+                 const char *user UNUSED,
+                 const char *pass UNUSED)
 {
        die("If you want to use CRAM-MD5 authenticate method, "
            "you have to build git-imap-send with OpenSSL library.");
@@ -913,7 +916,7 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
 
 #endif
 
-static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
+static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
 {
        int ret;
        char *response;
@@ -1319,7 +1322,8 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
        return 1;
 }
 
-static int git_imap_config(const char *var, const char *val, void *cb)
+static int git_imap_config(const char *var, const char *val,
+                          const struct config_context *ctx, void *cb)
 {
 
        if (!strcmp("imap.sslverify", var))
@@ -1337,10 +1341,10 @@ static int git_imap_config(const char *var, const char *val, void *cb)
        else if (!strcmp("imap.authmethod", var))
                return git_config_string(&server.auth_method, var, val);
        else if (!strcmp("imap.port", var))
-               server.port = git_config_int(var, val);
+               server.port = git_config_int(var, val, ctx->kvi);
        else if (!strcmp("imap.host", var)) {
                if (!val) {
-                       git_die_config("imap.host", "Missing value for 'imap.host'");
+                       return config_error_nonbool(var);
                } else {
                        if (starts_with(val, "imap:"))
                                val += 5;
@@ -1353,7 +1357,7 @@ static int git_imap_config(const char *var, const char *val, void *cb)
                        server.host = xstrdup(val);
                }
        } else
-               return git_default_config(var, val, cb);
+               return git_default_config(var, val, ctx, cb);
 
        return 0;
 }
@@ -1411,16 +1415,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
        if (!curl)
                die("curl_easy_init failed");
 
-       server_fill_credential(&server, cred);
-       curl_easy_setopt(curl, CURLOPT_USERNAME, server.user);
-       curl_easy_setopt(curl, CURLOPT_PASSWORD, server.pass);
+       server_fill_credential(srvc, cred);
+       curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
+       curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
 
-       strbuf_addstr(&path, server.use_ssl ? "imaps://" : "imap://");
-       strbuf_addstr(&path, server.host);
+       strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
+       strbuf_addstr(&path, srvc->host);
        if (!path.len || path.buf[path.len - 1] != '/')
                strbuf_addch(&path, '/');
 
-       uri_encoded_folder = curl_easy_escape(curl, server.folder, 0);
+       uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
        if (!uri_encoded_folder)
                die("failed to encode server folder");
        strbuf_addstr(&path, uri_encoded_folder);
@@ -1428,25 +1432,25 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
 
        curl_easy_setopt(curl, CURLOPT_URL, path.buf);
        strbuf_release(&path);
-       curl_easy_setopt(curl, CURLOPT_PORT, server.port);
+       curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
 
-       if (server.auth_method) {
+       if (srvc->auth_method) {
 #ifndef GIT_CURL_HAVE_CURLOPT_LOGIN_OPTIONS
                warning("No LOGIN_OPTIONS support in this cURL version");
 #else
                struct strbuf auth = STRBUF_INIT;
                strbuf_addstr(&auth, "AUTH=");
-               strbuf_addstr(&auth, server.auth_method);
+               strbuf_addstr(&auth, srvc->auth_method);
                curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
                strbuf_release(&auth);
 #endif
        }
 
-       if (!server.use_ssl)
+       if (!srvc->use_ssl)
                curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY);
 
-       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, server.ssl_verify);
-       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, server.ssl_verify);
+       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
+       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
 
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
 
index f1cfd8fa8c664bfca0dcaada59e59af8dd2db38a..005c820aa42e1f3c98bd2c1a376bd3a52bfc32a2 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "json-writer.h"
 
 void jw_init(struct json_writer *jw)
index 209355e0f12ce296c120b5e5b9f1651a2daf311f..04413bd1afda5d6693d5dfec2e96645557e1bb45 100644 (file)
@@ -3,8 +3,8 @@
 
 /*
  * JSON data structures are defined at:
- * [1] http://www.ietf.org/rfc/rfc7159.txt
- * [2] http://json.org/
+ * [1] https://www.ietf.org/rfc/rfc7159.txt
+ * [2] https://www.json.org/
  *
  * The JSON-writer API allows one to build JSON data structures using a
  * simple wrapper around a "struct strbuf" buffer.  It is intended as a
diff --git a/khash.h b/khash.h
index cb79bf885674ea6fc54fdcea17543739530a3b14..ff8816317785862662b1eeb2fe5036b801be1773 100644 (file)
--- a/khash.h
+++ b/khash.h
@@ -26,8 +26,7 @@
 #ifndef __AC_KHASH_H
 #define __AC_KHASH_H
 
-#include "cache.h"
-#include "hashmap.h"
+#include "hash.h"
 
 #define AC_VERSION_KHASH_H "0.2.8"
 
@@ -62,7 +61,7 @@ static inline khint_t __ac_X31_hash_string(const char *s)
 static const double __ac_HASH_UPPER = 0.77;
 
 #define __KHASH_TYPE(name, khkey_t, khval_t) \
-       typedef struct { \
+       typedef struct kh_##name { \
                khint_t n_buckets, size, n_occupied, upper_bound; \
                khint32_t *flags; \
                khkey_t *keys; \
diff --git a/kwset.c b/kwset.c
index 08aadf03117c5069116bb7e7fa4aa5ed7a9b1fb0..695e47b7ccfaf1aaa0e866933dffa8b7fcc51868 100644 (file)
--- a/kwset.c
+++ b/kwset.c
@@ -18,7 +18,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>. */
+   along with this program; if not, see <https://www.gnu.org/licenses/>. */
 
 /* Written August 1989 by Mike Haertel.
    The author may be reached (Email) at the address mike@ai.mit.edu,
@@ -32,7 +32,7 @@
    String Matching:  An Aid to Bibliographic Search," CACM June 1975,
    Vol. 18, No. 6, which describes the failure function used below. */
 
-#include "cache.h"
+#include "git-compat-util.h"
 
 #include "kwset.h"
 #include "compat/obstack.h"
@@ -49,6 +49,42 @@ static void *obstack_chunk_alloc(long size)
 
 #define U(c) ((unsigned char) (c))
 
+/* For case-insensitive kwset */
+const unsigned char tolower_trans_tbl[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+        ' ',  '!',  '"',  '#',  '$',  '%',  '&', 0x27,
+        '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+        '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+        '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+        '@',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+        'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+        'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+        'x',  'y',  'z',  '[', 0x5c,  ']',  '^',  '_',
+        '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+        'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+        'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+        'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7f,
+       0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+       0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+       0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+       0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+       0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+       0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+       0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+       0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+       0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+       0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+       0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+       0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+       0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+       0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+       0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+       0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+};
+
 /* Balanced tree of edges and labels leaving a given trie node. */
 struct tree
 {
diff --git a/kwset.h b/kwset.h
index f50ecae573b9ff77e0169fa93e733e68ef9fa300..c722664e5a7eeef9900a86738a5553b3eace49fc 100644 (file)
--- a/kwset.h
+++ b/kwset.h
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>. */
+   along with this program; if not, see <https://www.gnu.org/licenses/>. */
 
 /* Written August 1989 by Mike Haertel.
    The author may be reached (Email) at the address mike@ai.mit.edu,
    or (US mail) as Mike Haertel c/o Free Software Foundation. */
 
+extern const unsigned char tolower_trans_tbl[256];
+
 struct kwsmatch
 {
   int index;                   /* Index number of matching keyword. */
index d2632690d5107b53ee8a7ac4832cd85eb8c7bfc1..fd8026fe20182e5d90dad6db72ab453b5ed0a23c 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "levenshtein.h"
 
 /*
index 51d93310a4dee18ccfb559803583500ec45b27b4..8ff6ccb77241fd249f58827ecc1add65356648a4 100644 (file)
@@ -1,21 +1,22 @@
 #include "git-compat-util.h"
+#include "diffcore.h"
 #include "line-range.h"
-#include "cache.h"
+#include "hex.h"
 #include "tag.h"
-#include "blob.h"
 #include "tree.h"
 #include "diff.h"
 #include "commit.h"
 #include "decorate.h"
+#include "repository.h"
 #include "revision.h"
 #include "xdiff-interface.h"
 #include "strbuf.h"
 #include "log-tree.h"
-#include "graph.h"
-#include "userdiff.h"
 #include "line-log.h"
+#include "setup.h"
 #include "strvec.h"
 #include "bloom.h"
+#include "tree-walk.h"
 
 static void range_set_grow(struct range_set *rs, size_t extra)
 {
@@ -1089,10 +1090,8 @@ static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
 
 static void free_diffqueues(int n, struct diff_queue_struct *dq)
 {
-       int i, j;
-       for (i = 0; i < n; i++)
-               for (j = 0; j < dq[i].nr; j++)
-                       diff_free_filepair(dq[i].queue[j]);
+       for (int i = 0; i < n; i++)
+               diff_free_queue(&dq[i]);
        free(dq);
 }
 
@@ -1195,6 +1194,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c
        if (parent)
                add_line_range(rev, parent, parent_range);
        free_line_log_data(parent_range);
+       diff_free_queue(&queue);
        return changed;
 }
 
@@ -1282,7 +1282,8 @@ int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit
        return changed;
 }
 
-static enum rewrite_result line_log_rewrite_one(struct rev_info *rev, struct commit **pp)
+static enum rewrite_result line_log_rewrite_one(struct rev_info *rev UNUSED,
+                                               struct commit **pp)
 {
        for (;;) {
                struct commit *p = *pp;
@@ -1324,3 +1325,13 @@ int line_log_filter(struct rev_info *rev)
 
        return 0;
 }
+
+static void free_void_line_log_data(void *data)
+{
+       free_line_log_data(data);
+}
+
+void line_log_free(struct rev_info *rev)
+{
+       clear_decoration(&rev->line_log_data, free_void_line_log_data);
+}
index 82ae8d98a403bb644eede373a8def1a37a811fdd..e9dadbc1a58e2c657511b03f289994143b62bbfb 100644 (file)
@@ -1,10 +1,9 @@
 #ifndef LINE_LOG_H
 #define LINE_LOG_H
 
-#include "diffcore.h"
-
 struct rev_info;
 struct commit;
+struct string_list;
 
 /* A range [start,end].  Lines are numbered starting at 0, and the
  * ranges include start but exclude end. */
@@ -59,4 +58,6 @@ int line_log_process_ranges_arbitrary_commit(struct rev_info *rev,
 
 int line_log_print(struct rev_info *rev, struct commit *commit);
 
+void line_log_free(struct rev_info *rev);
+
 #endif /* LINE_LOG_H */
index 955a8a9535575fe0a397497d765f3e0e97334259..60f0e5ada81967d3743ef866438aaf9b8efdc497 100644 (file)
@@ -1,7 +1,6 @@
 #include "git-compat-util.h"
 #include "line-range.h"
 #include "xdiff-interface.h"
-#include "strbuf.h"
 #include "userdiff.h"
 
 /*
@@ -135,7 +134,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
 {
        int reg_error;
        regmatch_t match[1];
-       while (1) {
+       while (*start) {
                const char *bol, *eol;
                reg_error = regexec(regexp, start, 1, match, 0);
                if (reg_error == REG_NOMATCH)
@@ -148,8 +147,8 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
                /* determine extent of line matched */
                bol = start+match[0].rm_so;
                eol = start+match[0].rm_eo;
-               while (bol > start && *bol != '\n')
-                       bol--;
+               while (bol > start && *--bol != '\n')
+                       ; /* nothing */
                if (*bol == '\n')
                        bol++;
                while (*eol && *eol != '\n')
@@ -161,6 +160,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
                        return bol;
                start = eol;
        }
+       return NULL;
 }
 
 static const char *parse_range_funcname(
index ecffc09be6ec5c1a9646ac152a899f3415f6321d..5416cbcf409d2689d4fe620564f2e010a93131b8 100644 (file)
@@ -3,7 +3,7 @@
  * algorithm for dense and sparse linear assignment problems</i>. Computing,
  * 38(4), 325-340.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "linear-assignment.h"
 
 #define COST(column, row) cost[(column) + column_count * (row)]
index 533966023804776ec19ca06c7e84b4b264c07314..c5f363ca6f729ace28756467470e56363fad7a1e 100644 (file)
@@ -1,14 +1,11 @@
-#include "cache.h"
-#include "commit.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "revision.h"
-#include "strvec.h"
-#include "list-objects.h"
-#include "list-objects-filter.h"
+#include "gettext.h"
 #include "list-objects-filter-options.h"
 #include "promisor-remote.h"
 #include "trace.h"
 #include "url.h"
+#include "parse-options.h"
 
 static int parse_combine_filter(
        struct list_objects_filter_options *filter_options,
@@ -290,10 +287,6 @@ int opt_parse_list_objects_filter(const struct option *opt,
                                  const char *arg, int unset)
 {
        struct list_objects_filter_options *filter_options = opt->value;
-       opt_lof_init init = (opt_lof_init)opt->defval;
-
-       if (init)
-               filter_options = init(opt->value);
 
        if (unset || !arg)
                list_objects_filter_set_no_filter(filter_options);
@@ -345,7 +338,7 @@ void partial_clone_register(
        char *filter_name;
 
        /* Check if it is already registered */
-       if ((promisor_remote = promisor_remote_find(remote))) {
+       if ((promisor_remote = repo_promisor_remote_find(the_repository, remote))) {
                if (promisor_remote->partial_clone_filter)
                        /*
                         * Remote is already registered and a filter is already
@@ -373,14 +366,15 @@ void partial_clone_register(
        free(filter_name);
 
        /* Make sure the config info are reset */
-       promisor_remote_reinit();
+       repo_promisor_remote_reinit(the_repository);
 }
 
 void partial_clone_get_default_filter_spec(
        struct list_objects_filter_options *filter_options,
        const char *remote)
 {
-       struct promisor_remote *promisor = promisor_remote_find(remote);
+       struct promisor_remote *promisor = repo_promisor_remote_find(the_repository,
+                                                                    remote);
        struct strbuf errbuf = STRBUF_INIT;
 
        /*
index 7eeadab2dd0551c5eea8f7f14cd741841317230e..55fab8563d20a5a7ada0e9fd1215303808c5853d 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef LIST_OBJECTS_FILTER_OPTIONS_H
 #define LIST_OBJECTS_FILTER_OPTIONS_H
 
-#include "cache.h"
-#include "parse-options.h"
-#include "string-list.h"
+#include "gettext.h"
+#include "object.h"
+#include "strbuf.h"
+
+struct option;
 
 /*
  * The list of defined filters for list-objects.
@@ -111,27 +113,13 @@ void parse_list_objects_filter(
  * The opt->value to opt_parse_list_objects_filter() is either a
  * "struct list_objects_filter_option *" when using
  * OPT_PARSE_LIST_OBJECTS_FILTER().
- *
- * Or, if using no "struct option" field is used by the callback,
- * except the "defval" which is expected to be an "opt_lof_init"
- * function, which is called with the "opt->value" and must return a
- * pointer to the ""struct list_objects_filter_option *" to be used.
- *
- * The OPT_PARSE_LIST_OBJECTS_FILTER_INIT() can be used e.g. the
- * "struct list_objects_filter_option" is embedded in a "struct
- * rev_info", which the "defval" could be tasked with lazily
- * initializing. See cmd_pack_objects() for an example.
  */
 int opt_parse_list_objects_filter(const struct option *opt,
                                  const char *arg, int unset);
-typedef struct list_objects_filter_options *(*opt_lof_init)(void *);
-#define OPT_PARSE_LIST_OBJECTS_FILTER_INIT(fo, init) \
-       { OPTION_CALLBACK, 0, "filter", (fo), N_("args"), \
-         N_("object filtering"), 0, opt_parse_list_objects_filter, \
-         (intptr_t)(init) }
 
 #define OPT_PARSE_LIST_OBJECTS_FILTER(fo) \
-       OPT_PARSE_LIST_OBJECTS_FILTER_INIT((fo), NULL)
+       OPT_CALLBACK(0, "filter", (fo), N_("args"), \
+                    N_("object filtering"), opt_parse_list_objects_filter)
 
 /*
  * Translates abbreviated numbers in the filter's filter_spec into their
index 1c1ee3d1bb18d1737bd9aded0472b899b1864459..da287cc8e0dd2935cb230dd256d5f268495ed40f 100644 (file)
@@ -1,18 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
-#include "tag.h"
+#include "gettext.h"
+#include "hex.h"
 #include "commit.h"
-#include "tree.h"
-#include "blob.h"
 #include "diff.h"
-#include "tree-walk.h"
 #include "revision.h"
-#include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "oidmap.h"
 #include "oidset.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 
 /* Remember to update object flag allocation in object.h */
 /*
@@ -70,13 +68,13 @@ struct filter {
 };
 
 static enum list_objects_filter_result filter_blobs_none(
-       struct repository *r,
+       struct repository *r UNUSED,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
-       const char *pathname,
-       const char *filename,
+       const char *pathname UNUSED,
+       const char *filename UNUSED,
        struct oidset *omits,
-       void *filter_data_)
+       void *filter_data_ UNUSED)
 {
        switch (filter_situation) {
        default:
@@ -112,7 +110,7 @@ static enum list_objects_filter_result filter_blobs_none(
 }
 
 static void filter_blobs_none__init(
-       struct list_objects_filter_options *filter_options,
+       struct list_objects_filter_options *filter_options UNUSED,
        struct filter *filter)
 {
        filter->filter_object_fn = filter_blobs_none;
@@ -159,11 +157,11 @@ static int filter_trees_update_omits(
 }
 
 static enum list_objects_filter_result filter_trees_depth(
-       struct repository *r,
+       struct repository *r UNUSED,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
-       const char *pathname,
-       const char *filename,
+       const char *pathname UNUSED,
+       const char *filename UNUSED,
        struct oidset *omits,
        void *filter_data_)
 {
@@ -274,8 +272,8 @@ static enum list_objects_filter_result filter_blobs_limit(
        struct repository *r,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
-       const char *pathname,
-       const char *filename,
+       const char *pathname UNUSED,
+       const char *filename UNUSED,
        struct oidset *omits,
        void *filter_data_)
 {
@@ -514,6 +512,7 @@ static enum list_objects_filter_result filter_sparse(
 static void filter_sparse_free(void *filter_data)
 {
        struct filter_sparse_data *d = filter_data;
+       clear_pattern_list(&d->pl);
        free(d->array_frame);
        free(d);
 }
@@ -554,12 +553,12 @@ struct filter_object_type_data {
 };
 
 static enum list_objects_filter_result filter_object_type(
-       struct repository *r,
+       struct repository *r UNUSED,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
-       const char *pathname,
-       const char *filename,
-       struct oidset *omits,
+       const char *pathname UNUSED,
+       const char *filename UNUSED,
+       struct oidset *omits UNUSED,
        void *filter_data_)
 {
        struct filter_object_type_data *filter_data = filter_data_;
@@ -675,7 +674,7 @@ static enum list_objects_filter_result filter_combine(
        struct object *obj,
        const char *pathname,
        const char *filename,
-       struct oidset *omits,
+       struct oidset *omits UNUSED,
        void *filter_data)
 {
        struct combine_filter_data *d = filter_data;
@@ -709,6 +708,7 @@ static void filter_combine__free(void *filter_data)
                        BUG("expected oidset to be cleared already");
        }
        free(d->sub);
+       free(d);
 }
 
 static void add_all(struct oidset *dest, struct oidset *src) {
index 250d9de41cb56072e95420530b203ce417191a2b..f39b68faf54889144abd81e2c1f9c6f19edd8d4f 100644 (file)
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tag.h"
 #include "commit.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tree.h"
 #include "blob.h"
 #include "diff.h"
@@ -10,8 +12,9 @@
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "trace.h"
+#include "environment.h"
 
 struct traversal_context {
        struct rev_info *revs;
@@ -19,6 +22,7 @@ struct traversal_context {
        show_commit_fn show_commit;
        void *show_data;
        struct filter *filter;
+       int depth;
 };
 
 static void show_commit(struct traversal_context *ctx,
@@ -35,6 +39,9 @@ static void show_object(struct traversal_context *ctx,
 {
        if (!ctx->show_object)
                return;
+       if (ctx->revs->unpacked && has_object_pack(&object->oid))
+               return;
+
        ctx->show_object(object, name, ctx->show_data);
 }
 
@@ -64,7 +71,7 @@ static void process_blob(struct traversal_context *ctx,
         * of missing objects.
         */
        if (ctx->revs->exclude_promisor_objects &&
-           !has_object_file(&obj->oid) &&
+           !repo_has_object_file(the_repository, &obj->oid) &&
            is_promisor_object(&obj->oid))
                return;
 
@@ -81,36 +88,6 @@ static void process_blob(struct traversal_context *ctx,
        strbuf_setlen(path, pathlen);
 }
 
-/*
- * Processing a gitlink entry currently does nothing, since
- * we do not recurse into the subproject.
- *
- * We *could* eventually add a flag that actually does that,
- * which would involve:
- *  - is the subproject actually checked out?
- *  - if so, see if the subproject has already been added
- *    to the alternates list, and add it if not.
- *  - process the commit (or tag) the gitlink points to
- *    recursively.
- *
- * However, it's unclear whether there is really ever any
- * reason to see superprojects and subprojects as such a
- * "unified" object pool (potentially resulting in a totally
- * humongous pack - avoiding which was the whole point of
- * having gitlinks in the first place!).
- *
- * So for now, there is just a note that we *could* follow
- * the link, and how to do it. Whether it necessarily makes
- * any sense what-so-ever to ever do that is another issue.
- */
-static void process_gitlink(struct traversal_context *ctx,
-                           const unsigned char *sha1,
-                           struct strbuf *path,
-                           const char *name)
-{
-       /* Nothing to do */
-}
-
 static void process_tree(struct traversal_context *ctx,
                         struct tree *tree,
                         struct strbuf *base,
@@ -130,7 +107,7 @@ static void process_tree_contents(struct traversal_context *ctx,
        while (tree_entry(&desc, &entry)) {
                if (match != all_entries_interesting) {
                        match = tree_entry_interesting(ctx->revs->repo->index,
-                                                      &entry, base, 0,
+                                                      &entry, base,
                                                       &ctx->revs->diffopt.pathspec);
                        if (match == all_entries_not_interesting)
                                break;
@@ -146,11 +123,12 @@ static void process_tree_contents(struct traversal_context *ctx,
                                    entry.path, oid_to_hex(&tree->object.oid));
                        }
                        t->object.flags |= NOT_USER_GIVEN;
+                       ctx->depth++;
                        process_tree(ctx, t, base, entry.path);
+                       ctx->depth--;
                }
                else if (S_ISGITLINK(entry.mode))
-                       process_gitlink(ctx, entry.oid.hash,
-                                       base, entry.path);
+                       ; /* ignore gitlink */
                else {
                        struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
                        if (!b) {
@@ -185,6 +163,9 @@ static void process_tree(struct traversal_context *ctx,
            !revs->include_check_obj(&tree->object, revs->include_check_data))
                return;
 
+       if (ctx->depth > max_allowed_tree_depth)
+               die("exceeded maximum allowed tree depth");
+
        failed_parse = parse_tree_gently(tree, 1);
        if (failed_parse) {
                if (revs->ignore_missing_links)
@@ -199,7 +180,7 @@ static void process_tree(struct traversal_context *ctx,
                    is_promisor_object(&obj->oid))
                        return;
 
-               if (!revs->do_not_die_on_missing_tree)
+               if (!revs->do_not_die_on_missing_objects)
                        die("bad tree object %s", oid_to_hex(&obj->oid));
        }
 
@@ -258,7 +239,8 @@ static void mark_edge_parents_uninteresting(struct commit *commit,
                struct commit *parent = parents->item;
                if (!(parent->object.flags & UNINTERESTING))
                        continue;
-               mark_tree_uninteresting(revs->repo, get_commit_tree(parent));
+               mark_tree_uninteresting(revs->repo,
+                                       repo_get_commit_tree(the_repository, parent));
                if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
                        parent->object.flags |= SHOWN;
                        show_edge(parent);
@@ -275,7 +257,8 @@ static void add_edge_parents(struct commit *commit,
 
        for (parents = commit->parents; parents; parents = parents->next) {
                struct commit *parent = parents->item;
-               struct tree *tree = get_commit_tree(parent);
+               struct tree *tree = repo_get_commit_tree(the_repository,
+                                                        parent);
 
                if (!tree)
                        continue;
@@ -306,7 +289,8 @@ void mark_edges_uninteresting(struct rev_info *revs,
 
                for (list = revs->commits; list; list = list->next) {
                        struct commit *commit = list->item;
-                       struct tree *tree = get_commit_tree(commit);
+                       struct tree *tree = repo_get_commit_tree(the_repository,
+                                                                commit);
 
                        if (commit->object.flags & UNINTERESTING)
                                tree->object.flags |= UNINTERESTING;
@@ -322,7 +306,7 @@ void mark_edges_uninteresting(struct rev_info *revs,
                        struct commit *commit = list->item;
                        if (commit->object.flags & UNINTERESTING) {
                                mark_tree_uninteresting(revs->repo,
-                                                       get_commit_tree(commit));
+                                                       repo_get_commit_tree(the_repository, commit));
                                if (revs->edge_hint_aggressive && !(commit->object.flags & SHOWN)) {
                                        commit->object.flags |= SHOWN;
                                        show_edge(commit);
@@ -340,7 +324,7 @@ void mark_edges_uninteresting(struct rev_info *revs,
                        if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
                                continue;
                        mark_tree_uninteresting(revs->repo,
-                                               get_commit_tree(commit));
+                                               repo_get_commit_tree(the_repository, commit));
                        if (!(obj->flags & SHOWN)) {
                                obj->flags |= SHOWN;
                                show_edge(commit);
@@ -375,6 +359,7 @@ static void traverse_non_commits(struct traversal_context *ctx,
                if (!path)
                        path = "";
                if (obj->type == OBJ_TREE) {
+                       ctx->depth = 0;
                        process_tree(ctx, (struct tree *)obj, base, path);
                        continue;
                }
@@ -407,8 +392,12 @@ static void do_traverse(struct traversal_context *ctx)
                 */
                if (!ctx->revs->tree_objects)
                        ; /* do not bother loading tree */
-               else if (get_commit_tree(commit)) {
-                       struct tree *tree = get_commit_tree(commit);
+               else if (ctx->revs->do_not_die_on_missing_objects &&
+                        oidset_contains(&ctx->revs->missing_commits, &commit->object.oid))
+                       ;
+               else if (repo_get_commit_tree(the_repository, commit)) {
+                       struct tree *tree = repo_get_commit_tree(the_repository,
+                                                                commit);
                        tree->object.flags |= NOT_USER_GIVEN;
                        add_pending_tree(ctx->revs, tree);
                } else if (commit->object.parsed) {
diff --git a/list.h b/list.h
index 362a4cd7f5f10f17f174086911dc30e60e07e6ec..98428010f4d131580f8d3fa640efc6a10651a35b 100644 (file)
--- a/list.h
+++ b/list.h
@@ -19,7 +19,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, see
- * <http://www.gnu.org/licenses/>.
+ * <https://www.gnu.org/licenses/>.
  */
 
 #ifndef LIST_H
index cc9a4b84283be34cdcd72770d77e2ef1adb998a6..1d5ed0168287464e24d60e30b0cfbce181676535 100644 (file)
@@ -2,7 +2,9 @@
  * Copyright (c) 2005, Junio C Hamano
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "gettext.h"
 #include "lockfile.h"
 
 /*
index 1dd5fcbf7be433740d83382db4c401d438a51720..337b9334cdbafe0d3ffd2bf2fd36898f6d112410 100644 (file)
@@ -1,8 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit-reach.h"
 #include "config.h"
 #include "diff.h"
-#include "object-store.h"
+#include "diffcore.h"
+#include "environment.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "repository.h"
 #include "tmp-objdir.h"
 #include "commit.h"
@@ -12,6 +16,8 @@
 #include "merge-ort.h"
 #include "reflog-walk.h"
 #include "refs.h"
+#include "replace-object.h"
+#include "revision.h"
 #include "string-list.h"
 #include "color.h"
 #include "gpg-interface.h"
@@ -20,6 +26,9 @@
 #include "help.h"
 #include "range-diff.h"
 #include "strmap.h"
+#include "tree.h"
+#include "wildmatch.h"
+#include "write-or-die.h"
 
 static struct decoration name_decoration = { "object names" };
 static int decoration_loaded;
@@ -150,7 +159,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
 
        if (starts_with(refname, git_replace_ref_base)) {
                struct object_id original_oid;
-               if (!read_replace_refs)
+               if (!replace_refs_enabled(the_repository))
                        return 0;
                if (get_oid_hex(refname + strlen(git_replace_ref_base),
                                &original_oid)) {
@@ -196,7 +205,8 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
        return 0;
 }
 
-static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
+static int add_graft_decoration(const struct commit_graft *graft,
+                               void *cb_data UNUSED)
 {
        struct commit *commit = lookup_commit(the_repository, &graft->oid);
        if (!commit)
@@ -233,7 +243,8 @@ static void show_parents(struct commit *commit, int abbrev, FILE *file)
        struct commit_list *p;
        for (p = commit->parents; p ; p = p->next) {
                struct commit *parent = p->item;
-               fprintf(file, " %s", find_unique_abbrev(&parent->object.oid, abbrev));
+               fprintf(file, " %s",
+                       repo_find_unique_abbrev(the_repository, &parent->object.oid, abbrev));
        }
 }
 
@@ -241,7 +252,8 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre
 {
        struct commit_list *p = lookup_decoration(&opt->children, &commit->object);
        for ( ; p; p = p->next) {
-               fprintf(opt->diffopt.file, " %s", find_unique_abbrev(&p->item->object.oid, abbrev));
+               fprintf(opt->diffopt.file, " %s",
+                       repo_find_unique_abbrev(the_repository, &p->item->object.oid, abbrev));
        }
 }
 
@@ -292,26 +304,43 @@ static void show_name(struct strbuf *sb, const struct name_decoration *decoratio
 
 /*
  * The caller makes sure there is no funny color before calling.
- * format_decorations_extended makes sure the same after return.
+ * format_decorations ensures the same after return.
  */
-void format_decorations_extended(struct strbuf *sb,
+void format_decorations(struct strbuf *sb,
                        const struct commit *commit,
                        int use_color,
-                       const char *prefix,
-                       const char *separator,
-                       const char *suffix)
+                       const struct decoration_options *opts)
 {
        const struct name_decoration *decoration;
        const struct name_decoration *current_and_HEAD;
-       const char *color_commit =
-               diff_get_color(use_color, DIFF_COMMIT);
-       const char *color_reset =
-               decorate_get_color(use_color, DECORATION_NONE);
+       const char *color_commit, *color_reset;
+
+       const char *prefix = " (";
+       const char *suffix = ")";
+       const char *separator = ", ";
+       const char *pointer = " -> ";
+       const char *tag = "tag: ";
 
        decoration = get_name_decoration(&commit->object);
        if (!decoration)
                return;
 
+       if (opts) {
+               if (opts->prefix)
+                       prefix = opts->prefix;
+               if (opts->suffix)
+                       suffix = opts->suffix;
+               if (opts->separator)
+                       separator = opts->separator;
+               if (opts->pointer)
+                       pointer = opts->pointer;
+               if (opts->tag)
+                       tag = opts->tag;
+       }
+
+       color_commit = diff_get_color(use_color, DIFF_COMMIT);
+       color_reset = decorate_get_color(use_color, DECORATION_NONE);
+
        current_and_HEAD = current_pointed_by_HEAD(decoration);
        while (decoration) {
                /*
@@ -320,31 +349,44 @@ void format_decorations_extended(struct strbuf *sb,
                 * appeared, skipping the entry for current.
                 */
                if (decoration != current_and_HEAD) {
-                       strbuf_addstr(sb, color_commit);
-                       strbuf_addstr(sb, prefix);
-                       strbuf_addstr(sb, color_reset);
-                       strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
-                       if (decoration->type == DECORATION_REF_TAG)
-                               strbuf_addstr(sb, "tag: ");
+                       const char *color =
+                               decorate_get_color(use_color, decoration->type);
+
+                       if (*prefix) {
+                               strbuf_addstr(sb, color_commit);
+                               strbuf_addstr(sb, prefix);
+                               strbuf_addstr(sb, color_reset);
+                       }
 
+                       if (*tag && decoration->type == DECORATION_REF_TAG) {
+                               strbuf_addstr(sb, color);
+                               strbuf_addstr(sb, tag);
+                               strbuf_addstr(sb, color_reset);
+                       }
+
+                       strbuf_addstr(sb, color);
                        show_name(sb, decoration);
+                       strbuf_addstr(sb, color_reset);
 
                        if (current_and_HEAD &&
                            decoration->type == DECORATION_REF_HEAD) {
-                               strbuf_addstr(sb, " -> ");
+                               strbuf_addstr(sb, color_commit);
+                               strbuf_addstr(sb, pointer);
                                strbuf_addstr(sb, color_reset);
                                strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type));
                                show_name(sb, current_and_HEAD);
+                               strbuf_addstr(sb, color_reset);
                        }
-                       strbuf_addstr(sb, color_reset);
 
                        prefix = separator;
                }
                decoration = decoration->next;
        }
-       strbuf_addstr(sb, color_commit);
-       strbuf_addstr(sb, suffix);
-       strbuf_addstr(sb, color_reset);
+       if (*suffix) {
+               strbuf_addstr(sb, color_commit);
+               strbuf_addstr(sb, suffix);
+               strbuf_addstr(sb, color_reset);
+       }
 }
 
 void show_decorations(struct rev_info *opt, struct commit *commit)
@@ -359,7 +401,7 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
        }
        if (!opt->show_decorations)
                return;
-       format_decorations(&sb, commit, opt->diffopt.use_color);
+       format_decorations(&sb, commit, opt->diffopt.use_color, NULL);
        fputs(sb.buf, opt->diffopt.file);
        strbuf_release(&sb);
 }
@@ -405,7 +447,8 @@ void fmt_output_commit(struct strbuf *filename,
        struct pretty_print_context ctx = {0};
        struct strbuf subject = STRBUF_INIT;
 
-       format_commit_message(commit, "%f", &subject, &ctx);
+       repo_format_commit_message(the_repository, commit, "%f", &subject,
+                                  &ctx);
        fmt_output_subject(filename, subject.buf, info);
        strbuf_release(&subject);
 }
@@ -440,7 +483,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
        fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
        graph_show_oneline(opt->graph);
        if (opt->message_id) {
-               fprintf(opt->diffopt.file, "Message-Id: <%s>\n", opt->message_id);
+               fprintf(opt->diffopt.file, "Message-ID: <%s>\n", opt->message_id);
                graph_show_oneline(opt->graph);
        }
        if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
@@ -644,7 +687,8 @@ void show_log(struct rev_info *opt)
 
                if (!opt->graph)
                        put_revision_mark(opt, commit);
-               fputs(find_unique_abbrev(&commit->object.oid, abbrev_commit), opt->diffopt.file);
+               fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev_commit),
+                     opt->diffopt.file);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit, opt->diffopt.file);
                if (opt->children.name)
@@ -706,8 +750,8 @@ void show_log(struct rev_info *opt)
 
                if (!opt->graph)
                        put_revision_mark(opt, commit);
-               fputs(find_unique_abbrev(&commit->object.oid,
-                                        abbrev_commit),
+               fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid,
+                                             abbrev_commit),
                      opt->diffopt.file);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit, opt->diffopt.file);
@@ -715,7 +759,7 @@ void show_log(struct rev_info *opt)
                        show_children(opt, commit, abbrev_commit);
                if (parent)
                        fprintf(opt->diffopt.file, " (from %s)",
-                              find_unique_abbrev(&parent->object.oid, abbrev_commit));
+                              repo_find_unique_abbrev(the_repository, &parent->object.oid, abbrev_commit));
                fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);
                show_decorations(opt, commit);
                if (opt->commit_format == CMIT_FMT_ONELINE) {
@@ -846,7 +890,7 @@ void show_log(struct rev_info *opt)
                 * Pass minimum required diff-options to range-diff; others
                 * can be added later if deemed desirable.
                 */
-               diff_setup(&opts);
+               repo_diff_setup(the_repository, &opts);
                opts.file = opt->diffopt.file;
                opts.use_color = opt->diffopt.use_color;
                diff_setup_done(&opts);
@@ -982,15 +1026,17 @@ static int do_remerge_diff(struct rev_info *opt,
        o.msg_header_prefix = "remerge";
 
        ctx.abbrev = DEFAULT_ABBREV;
-       format_commit_message(parent1, "%h (%s)", &parent1_desc, &ctx);
-       format_commit_message(parent2, "%h (%s)", &parent2_desc, &ctx);
+       repo_format_commit_message(the_repository, parent1, "%h (%s)",
+                                  &parent1_desc, &ctx);
+       repo_format_commit_message(the_repository, parent2, "%h (%s)",
+                                  &parent2_desc, &ctx);
        o.branch1 = parent1_desc.buf;
        o.branch2 = parent2_desc.buf;
 
        /* Parse the relevant commits and get the merge bases */
        parse_commit_or_die(parent1);
        parse_commit_or_die(parent2);
-       bases = get_merge_bases(parent1, parent2);
+       bases = repo_get_merge_bases(the_repository, parent1, parent2);
 
        /* Re-merge the parents */
        merge_incore_recursive(&o, bases, parent1, parent2, &res);
index e7e4641cf83c5b7cfd4457b54a88115723ac979d..41c776fea52e6867800caf2eb919379fc6e54218 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef LOG_TREE_H
 #define LOG_TREE_H
 
-#include "revision.h"
+struct rev_info;
 
 struct log_info {
        struct commit *commit, *parent;
@@ -13,17 +13,20 @@ struct decoration_filter {
        struct string_list *exclude_ref_config_pattern;
 };
 
+struct decoration_options {
+       char *prefix;
+       char *suffix;
+       char *separator;
+       char *pointer;
+       char *tag;
+};
+
 int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
 int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
 void show_log(struct rev_info *opt);
-void format_decorations_extended(struct strbuf *sb, const struct commit *commit,
-                            int use_color,
-                            const char *prefix,
-                            const char *separator,
-                            const char *suffix);
-#define format_decorations(strbuf, commit, color) \
-                            format_decorations_extended((strbuf), (commit), (color), " (", ", ", ")")
+void format_decorations(struct strbuf *sb, const struct commit *commit,
+                       int use_color, const struct decoration_options *opts);
 void show_decorations(struct rev_info *opt, struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                             const char **extra_headers_p,
index fa0d01b47c1286767068df928e83dd74c5f7b49e..819cbefee3de07c7cb7f8f92e1fd715b1b2f7af6 100644 (file)
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -1,44 +1,42 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "repository.h"
 #include "refs.h"
-#include "remote.h"
 #include "strvec.h"
 #include "ls-refs.h"
 #include "pkt-line.h"
 #include "config.h"
+#include "string-list.h"
 
-static int config_read;
-static int advertise_unborn;
-static int allow_unborn;
-
-static void ensure_config_read(void)
+static enum {
+       UNBORN_IGNORE = 0,
+       UNBORN_ALLOW,
+       UNBORN_ADVERTISE /* implies ALLOW */
+} unborn_config(struct repository *r)
 {
        const char *str = NULL;
 
-       if (config_read)
-               return;
-
-       if (repo_config_get_string_tmp(the_repository, "lsrefs.unborn", &str)) {
+       if (repo_config_get_string_tmp(r, "lsrefs.unborn", &str)) {
                /*
                 * If there is no such config, advertise and allow it by
                 * default.
                 */
-               advertise_unborn = 1;
-               allow_unborn = 1;
+               return UNBORN_ADVERTISE;
        } else {
                if (!strcmp(str, "advertise")) {
-                       advertise_unborn = 1;
-                       allow_unborn = 1;
+                       return UNBORN_ADVERTISE;
                } else if (!strcmp(str, "allow")) {
-                       allow_unborn = 1;
+                       return UNBORN_ALLOW;
                } else if (!strcmp(str, "ignore")) {
-                       /* do nothing */
+                       return UNBORN_IGNORE;
                } else {
                        die(_("invalid value for '%s': '%s'"),
                            "lsrefs.unborn", str);
                }
        }
-       config_read = 1;
 }
 
 /*
@@ -73,6 +71,7 @@ struct ls_refs_data {
        unsigned symrefs;
        struct strvec prefixes;
        struct strbuf buf;
+       struct strvec hidden_refs;
        unsigned unborn : 1;
 };
 
@@ -84,7 +83,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
 
        strbuf_reset(&data->buf);
 
-       if (ref_is_hidden(refname_nons, refname))
+       if (ref_is_hidden(refname_nons, refname, &data->hidden_refs))
                return 0;
 
        if (!ref_match(&data->prefixes, refname_nons))
@@ -137,14 +136,16 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
 }
 
 static int ls_refs_config(const char *var, const char *value,
-                         void *data UNUSED)
+                         const struct config_context *ctx UNUSED,
+                         void *cb_data)
 {
+       struct ls_refs_data *data = cb_data;
        /*
         * We only serve fetches over v2 for now, so respect only "uploadpack"
         * config. This may need to eventually be expanded to "receive", but we
         * don't yet know how that information will be passed to ls-refs.
         */
-       return parse_hide_refs_config(var, value, "uploadpack");
+       return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
 }
 
 int ls_refs(struct repository *r, struct packet_reader *request)
@@ -154,9 +155,9 @@ int ls_refs(struct repository *r, struct packet_reader *request)
        memset(&data, 0, sizeof(data));
        strvec_init(&data.prefixes);
        strbuf_init(&data.buf, 0);
+       strvec_init(&data.hidden_refs);
 
-       ensure_config_read();
-       git_config(ls_refs_config, NULL);
+       git_config(ls_refs_config, &data);
 
        while (packet_reader_read(request) == PACKET_READ_NORMAL) {
                const char *arg = request->line;
@@ -171,7 +172,7 @@ int ls_refs(struct repository *r, struct packet_reader *request)
                                strvec_push(&data.prefixes, out);
                }
                else if (!strcmp("unborn", arg))
-                       data.unborn = allow_unborn;
+                       data.unborn = !!unborn_config(r);
                else
                        die(_("unexpected line: '%s'"), arg);
        }
@@ -190,21 +191,21 @@ int ls_refs(struct repository *r, struct packet_reader *request)
        send_possibly_unborn_head(&data);
        if (!data.prefixes.nr)
                strvec_push(&data.prefixes, "");
-       for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
-                                    send_ref, &data);
+       refs_for_each_fullref_in_prefixes(get_main_ref_store(r),
+                                         get_git_namespace(), data.prefixes.v,
+                                         hidden_refs_to_excludes(&data.hidden_refs),
+                                         send_ref, &data);
        packet_fflush(stdout);
        strvec_clear(&data.prefixes);
        strbuf_release(&data.buf);
+       strvec_clear(&data.hidden_refs);
        return 0;
 }
 
 int ls_refs_advertise(struct repository *r, struct strbuf *value)
 {
-       if (value) {
-               ensure_config_read();
-               if (advertise_unborn)
-                       strbuf_addstr(value, "unborn");
-       }
+       if (value && unborn_config(r) == UNBORN_ADVERTISE)
+               strbuf_addstr(value, "unborn");
 
        return 1;
 }
index 833d28612f77473e9a50d6329ba55bd9706ba81d..94b9b0abf228b891bc1df5a71bcd96807ed982a8 100644 (file)
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
+#include "hex-ll.h"
 #include "utf8.h"
 #include "strbuf.h"
 #include "mailinfo.h"
@@ -56,12 +58,13 @@ static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
 
 static const char *unquote_comment(struct strbuf *outbuf, const char *in)
 {
-       int c;
        int take_next_literally = 0;
+       int depth = 1;
 
        strbuf_addch(outbuf, '(');
 
-       while ((c = *in++) != 0) {
+       while (*in) {
+               int c = *in++;
                if (take_next_literally == 1) {
                        take_next_literally = 0;
                } else {
@@ -70,11 +73,14 @@ static const char *unquote_comment(struct strbuf *outbuf, const char *in)
                                take_next_literally = 1;
                                continue;
                        case '(':
-                               in = unquote_comment(outbuf, in);
+                               strbuf_addch(outbuf, '(');
+                               depth++;
                                continue;
                        case ')':
                                strbuf_addch(outbuf, ')');
-                               return in;
+                               if (!--depth)
+                                       return in;
+                               continue;
                        }
                }
 
@@ -86,10 +92,10 @@ static const char *unquote_comment(struct strbuf *outbuf, const char *in)
 
 static const char *unquote_quoted_string(struct strbuf *outbuf, const char *in)
 {
-       int c;
        int take_next_literally = 0;
 
-       while ((c = *in++) != 0) {
+       while (*in) {
+               int c = *in++;
                if (take_next_literally == 1) {
                        take_next_literally = 0;
                } else {
@@ -597,7 +603,7 @@ static int check_header(struct mailinfo *mi,
                ret = 1;
                goto check_header_out;
        }
-       if (parse_header(line, "Message-Id", mi, &sb)) {
+       if (parse_header(line, "Message-ID", mi, &sb)) {
                if (mi->add_message_id)
                        mi->message_id = strbuf_detach(&sb, NULL);
                ret = 1;
@@ -829,7 +835,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
        if (patchbreak(line)) {
                if (mi->message_id)
                        strbuf_addf(&mi->log_message,
-                                   "Message-Id: %s\n", mi->message_id);
+                                   "Message-ID: %s\n", mi->message_id);
                return 1;
        }
 
@@ -1239,17 +1245,20 @@ int mailinfo_parse_quoted_cr_action(const char *actionstr, int *action)
        return 0;
 }
 
-static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+static int git_mailinfo_config(const char *var, const char *value,
+                              const struct config_context *ctx, void *mi_)
 {
        struct mailinfo *mi = mi_;
 
        if (!starts_with(var, "mailinfo."))
-               return git_default_config(var, value, NULL);
+               return git_default_config(var, value, ctx, NULL);
        if (!strcmp(var, "mailinfo.scissors")) {
                mi->use_scissors = git_config_bool(var, value);
                return 0;
        }
        if (!strcmp(var, "mailinfo.quotedcr")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (mailinfo_parse_quoted_cr_action(value, &mi->quoted_cr) != 0)
                        return error(_("bad action '%s' for '%s'"), value, var);
                return 0;
index da2589b08229813963347115a001d8ce6f45ae32..3d6a5e9400f4c8195d0f58ab58f3f9a4dfa0acba 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -1,17 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
 #include "string-list.h"
 #include "mailmap.h"
-#include "object-store.h"
-
-#define DEBUG_MAILMAP 0
-#if DEBUG_MAILMAP
-#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
-#define debug_str(X) ((X) ? (X) : "(none)")
-#else
-__attribute__((format (printf, 1, 2)))
-static inline void debug_mm(const char *format, ...) {}
-static inline const char *debug_str(const char *s) { return s; }
-#endif
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "setup.h"
 
 const char *git_mailmap_file;
 const char *git_mailmap_blob;
@@ -30,23 +23,17 @@ struct mailmap_entry {
        struct string_list namemap;
 };
 
-static void free_mailmap_info(void *p, const char *s)
+static void free_mailmap_info(void *p, const char *s UNUSED)
 {
        struct mailmap_info *mi = (struct mailmap_info *)p;
-       debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
-                s, debug_str(mi->name), debug_str(mi->email));
        free(mi->name);
        free(mi->email);
        free(mi);
 }
 
-static void free_mailmap_entry(void *p, const char *s)
+static void free_mailmap_entry(void *p, const char *s UNUSED)
 {
        struct mailmap_entry *me = (struct mailmap_entry *)p;
-       debug_mm("mailmap: removing entries for <%s>, with %"PRIuMAX" sub-entries\n",
-                s, (uintmax_t)me->namemap.nr);
-       debug_mm("mailmap: - simple: '%s' <%s>\n",
-                debug_str(me->name), debug_str(me->email));
 
        free(me->name);
        free(me->email);
@@ -93,8 +80,6 @@ static void add_mapping(struct string_list *map,
        }
 
        if (!old_name) {
-               debug_mm("mailmap: adding (simple) entry for '%s'\n", old_email);
-
                /* Replace current name and new email for simple entry */
                if (new_name) {
                        free(me->name);
@@ -106,15 +91,10 @@ static void add_mapping(struct string_list *map,
                }
        } else {
                struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
-               debug_mm("mailmap: adding (complex) entry for '%s'\n", old_email);
                mi->name = xstrdup_or_null(new_name);
                mi->email = xstrdup_or_null(new_email);
                string_list_insert(&me->namemap, old_name)->util = mi;
        }
-
-       debug_mm("mailmap:  '%s' <%s> -> '%s' <%s>\n",
-                debug_str(old_name), old_email,
-                debug_str(new_name), debug_str(new_email));
 }
 
 static char *parse_name_and_email(char *buffer, char **name,
@@ -213,10 +193,10 @@ static int read_mailmap_blob(struct string_list *map, const char *name)
 
        if (!name)
                return 0;
-       if (get_oid(name, &oid) < 0)
+       if (repo_get_oid(the_repository, name, &oid) < 0)
                return 0;
 
-       buf = read_object_file(&oid, &type, &size);
+       buf = repo_read_object_file(the_repository, &oid, &type, &size);
        if (!buf)
                return error("unable to read mailmap object at %s", name);
        if (type != OBJ_BLOB)
@@ -250,11 +230,8 @@ int read_mailmap(struct string_list *map)
 
 void clear_mailmap(struct string_list *map)
 {
-       debug_mm("mailmap: clearing %"PRIuMAX" entries...\n",
-                (uintmax_t)map->nr);
        map->strdup_strings = 1;
        string_list_clear_func(map, free_mailmap_entry);
-       debug_mm("mailmap: cleared\n");
 }
 
 /*
@@ -315,10 +292,6 @@ int map_user(struct string_list *map,
        struct string_list_item *item;
        struct mailmap_entry *me;
 
-       debug_mm("map_user: map '%.*s' <%.*s>\n",
-                (int)*namelen, debug_str(*name),
-                (int)*emaillen, debug_str(*email));
-
        item = lookup_prefix(map, *email, *emaillen);
        if (item) {
                me = (struct mailmap_entry *)item->util;
@@ -336,10 +309,8 @@ int map_user(struct string_list *map,
        }
        if (item) {
                struct mailmap_info *mi = (struct mailmap_info *)item->util;
-               if (mi->name == NULL && mi->email == NULL) {
-                       debug_mm("map_user:  -- (no simple mapping)\n");
+               if (mi->name == NULL && mi->email == NULL)
                        return 0;
-               }
                if (mi->email) {
                                *email = mi->email;
                                *emaillen = strlen(*email);
@@ -348,11 +319,7 @@ int map_user(struct string_list *map,
                                *name = mi->name;
                                *namelen = strlen(*name);
                }
-               debug_mm("map_user:  to '%.*s' <%.*s>\n",
-                        (int)*namelen, debug_str(*name),
-                        (int)*emaillen, debug_str(*email));
                return 1;
        }
-       debug_mm("map_user:  --\n");
        return 0;
 }
index 7e99fccb46c92e012fa32fafb6ed27c217fdcc4d..0f8fd2c586feaccd4bb4df772b8e8393148ccf6c 100644 (file)
--- a/mailmap.h
+++ b/mailmap.h
@@ -3,6 +3,9 @@
 
 struct string_list;
 
+extern const char *git_mailmap_file;
+extern const char *git_mailmap_blob;
+
 int read_mailmap(struct string_list *map);
 void clear_mailmap(struct string_list *map);
 
index 49398e599fe3afdc88f481608fb58b0887b019e3..0885ac681cd5055f80fb5088b04c92eed8e7f291 100644 (file)
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hex.h"
+#include "match-trees.h"
+#include "strbuf.h"
 #include "tree.h"
 #include "tree-walk.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 static int score_missing(unsigned mode)
 {
@@ -55,7 +58,7 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,
        enum object_type type;
        unsigned long size;
 
-       buffer = read_object_file(hash, &type, &size);
+       buffer = repo_read_object_file(the_repository, hash, &type, &size);
        if (!buffer)
                die("unable to read tree (%s)", oid_to_hex(hash));
        if (type != OBJ_TREE)
@@ -188,7 +191,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
        if (*subpath)
                subpath++;
 
-       buf = read_object_file(oid1, &type, &sz);
+       buf = repo_read_object_file(the_repository, oid1, &type, &sz);
        if (!buf)
                die("cannot read tree %s", oid_to_hex(oid1));
        init_tree_desc(&desc, buf, sz);
diff --git a/match-trees.h b/match-trees.h
new file mode 100644 (file)
index 0000000..e3877ac
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef MATCH_TREES_H
+#define MATCH_TREES_H
+
+struct object_id;
+struct repository;
+
+void shift_tree(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, int);
+void shift_tree_by(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, const char *);
+
+#endif /* MATCH_TREES_H */
index 599d8e895f81216e0e0cb44696734cb2a157309b..c7d62560201984db8efcb675da9b4ede933c5cbd 100644 (file)
@@ -2,7 +2,7 @@
  * Memory Pool implementation logic.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "mem-pool.h"
 
 #define BLOCK_GROWTH_SIZE (1024 * 1024 - sizeof(struct mp_block))
@@ -89,9 +89,7 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)
        struct mp_block *p = NULL;
        void *r;
 
-       /* round up to a 'GIT_MAX_ALIGNMENT' alignment */
-       if (len & (GIT_MAX_ALIGNMENT - 1))
-               len += GIT_MAX_ALIGNMENT - (len & (GIT_MAX_ALIGNMENT - 1));
+       len = DIV_ROUND_UP(len, GIT_MAX_ALIGNMENT) * GIT_MAX_ALIGNMENT;
 
        if (pool->mp_block &&
            pool->mp_block->end - pool->mp_block->next_free >= len)
@@ -99,9 +97,9 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)
 
        if (!p) {
                if (len >= (pool->block_alloc / 2))
-                       return mem_pool_alloc_block(pool, len, pool->mp_block);
-
-               p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
+                       p = mem_pool_alloc_block(pool, len, pool->mp_block);
+               else
+                       p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
        }
 
        r = p->next_free;
index 8138090f81cf726ee9834daa7edbfc5b542aec44..2f659fd01432d4249d1d2be1876f0edfd8c9cd09 100644 (file)
@@ -1,10 +1,8 @@
-#include "cache.h"
-#include "run-command.h"
-#include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "git-compat-util.h"
+#include "merge-ll.h"
 #include "blob.h"
 #include "merge-blobs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
 {
@@ -12,7 +10,8 @@ static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
        unsigned long size;
        enum object_type type;
 
-       buf = read_object_file(&obj->object.oid, &type, &size);
+       buf = repo_read_object_file(the_repository, &obj->object.oid, &type,
+                                   &size);
        if (!buf)
                return -1;
        if (type != OBJ_BLOB) {
@@ -78,7 +77,8 @@ void *merge_blobs(struct index_state *istate, const char *path,
                        return NULL;
                if (!our)
                        our = their;
-               return read_object_file(&our->object.oid, &type, size);
+               return repo_read_object_file(the_repository, &our->object.oid,
+                                            &type, size);
        }
 
        if (fill_mmfile_blob(&f1, our) < 0)
similarity index 87%
rename from ll-merge.c
rename to merge-ll.c
index 8955d7e1f6eec73ef797781c2bd0d76d43d1ef19..1df58ebaac08582d042268f5a07cf5cbd9d7b1c3 100644 (file)
@@ -4,13 +4,15 @@
  * Copyright (c) 2007 Junio C Hamano
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "convert.h"
 #include "attr.h"
 #include "xdiff-interface.h"
 #include "run-command.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "quote.h"
+#include "strbuf.h"
 
 struct ll_merge_driver;
 
@@ -49,14 +51,14 @@ void reset_merge_attributes(void)
 /*
  * Built-in low-levels
  */
-static enum ll_merge_result ll_binary_merge(const struct ll_merge_driver *drv_unused,
+static enum ll_merge_result ll_binary_merge(const struct ll_merge_driver *drv UNUSED,
                           mmbuffer_t *result,
-                          const char *path,
-                          mmfile_t *orig, const char *orig_name,
-                          mmfile_t *src1, const char *name1,
-                          mmfile_t *src2, const char *name2,
+                          const char *path UNUSED,
+                          mmfile_t *orig, const char *orig_name UNUSED,
+                          mmfile_t *src1, const char *name1 UNUSED,
+                          mmfile_t *src2, const char *name2 UNUSED,
                           const struct ll_merge_options *opts,
-                          int marker_size)
+                          int marker_size UNUSED)
 {
        enum ll_merge_result ret;
        mmfile_t *stolen;
@@ -183,30 +185,21 @@ static void create_temp(mmfile_t *src, char *path, size_t len)
 static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
                        mmbuffer_t *result,
                        const char *path,
-                       mmfile_t *orig, const char *orig_name,
-                       mmfile_t *src1, const char *name1,
-                       mmfile_t *src2, const char *name2,
+                       mmfile_t *orig, const char *orig_name UNUSED,
+                       mmfile_t *src1, const char *name1 UNUSED,
+                       mmfile_t *src2, const char *name2 UNUSED,
                        const struct ll_merge_options *opts,
                        int marker_size)
 {
-       char temp[4][50];
+       char temp[3][50];
        struct strbuf cmd = STRBUF_INIT;
-       struct strbuf_expand_dict_entry dict[6];
-       struct strbuf path_sq = STRBUF_INIT;
-       const char *args[] = { NULL, NULL };
+       const char *format = fn->cmdline;
+       struct child_process child = CHILD_PROCESS_INIT;
        int status, fd, i;
        struct stat st;
        enum ll_merge_result ret;
        assert(opts);
 
-       sq_quote_buf(&path_sq, path);
-       dict[0].placeholder = "O"; dict[0].value = temp[0];
-       dict[1].placeholder = "A"; dict[1].value = temp[1];
-       dict[2].placeholder = "B"; dict[2].value = temp[2];
-       dict[3].placeholder = "L"; dict[3].value = temp[3];
-       dict[4].placeholder = "P"; dict[4].value = path_sq.buf;
-       dict[5].placeholder = NULL; dict[5].value = NULL;
-
        if (!fn->cmdline)
                die("custom merge driver %s lacks command line.", fn->name);
 
@@ -215,12 +208,27 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
        create_temp(orig, temp[0], sizeof(temp[0]));
        create_temp(src1, temp[1], sizeof(temp[1]));
        create_temp(src2, temp[2], sizeof(temp[2]));
-       xsnprintf(temp[3], sizeof(temp[3]), "%d", marker_size);
 
-       strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
+       while (strbuf_expand_step(&cmd, &format)) {
+               if (skip_prefix(format, "%", &format))
+                       strbuf_addch(&cmd, '%');
+               else if (skip_prefix(format, "O", &format))
+                       strbuf_addstr(&cmd, temp[0]);
+               else if (skip_prefix(format, "A", &format))
+                       strbuf_addstr(&cmd, temp[1]);
+               else if (skip_prefix(format, "B", &format))
+                       strbuf_addstr(&cmd, temp[2]);
+               else if (skip_prefix(format, "L", &format))
+                       strbuf_addf(&cmd, "%d", marker_size);
+               else if (skip_prefix(format, "P", &format))
+                       sq_quote_buf(&cmd, path);
+               else
+                       strbuf_addch(&cmd, '%');
+       }
 
-       args[0] = cmd.buf;
-       status = run_command_v_opt(args, RUN_USING_SHELL);
+       child.use_shell = 1;
+       strvec_push(&child.args, cmd.buf);
+       status = run_command(&child);
        fd = open(temp[1], O_RDONLY);
        if (fd < 0)
                goto bad;
@@ -238,8 +246,13 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
        for (i = 0; i < 3; i++)
                unlink_or_warn(temp[i]);
        strbuf_release(&cmd);
-       strbuf_release(&path_sq);
-       ret = (status > 0) ? LL_MERGE_CONFLICT : status;
+       if (!status)
+               ret = LL_MERGE_OK;
+       else if (status <= 128)
+               ret = LL_MERGE_CONFLICT;
+       else
+               /* died due to a signal: WTERMSIG(status) + 128 */
+               ret = LL_MERGE_ERROR;
        return ret;
 }
 
@@ -250,6 +263,7 @@ static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
 static const char *default_ll_merge;
 
 static int read_merge_config(const char *var, const char *value,
+                            const struct config_context *ctx UNUSED,
                             void *cb UNUSED)
 {
        struct ll_merge_driver *fn;
@@ -287,7 +301,7 @@ static int read_merge_config(const char *var, const char *value,
 
        if (!strcmp("driver", key)) {
                if (!value)
-                       return error("%s: lacks value", var);
+                       return config_error_nonbool(var);
                /*
                 * merge.<name>.driver specifies the command line:
                 *
similarity index 100%
rename from ll-merge.h
rename to merge-ll.h
index 748924a69ba3262088c444c0145bc407750daa0a..4acedf3c3386d7c9c2e57eebabddb1fe9ec27331 100644 (file)
@@ -1,6 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hash.h"
 #include "merge-ort.h"
 #include "merge-ort-wrappers.h"
+#include "read-cache-ll.h"
+#include "tree.h"
 
 #include "commit.h"
 
index e5f41cce48143db0526676ebc4949a4431cb8d9f..77ba7f3020c8c112bf2e6bff9dae78d4d0ca847b 100644 (file)
  * "cale", "peedy", or "ins" instead of "ort"?)
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "merge-ort.h"
 
 #include "alloc.h"
 #include "attr.h"
-#include "blob.h"
 #include "cache-tree.h"
 #include "commit.h"
 #include "commit-reach.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "entry.h"
-#include "ll-merge.h"
-#include "object-store.h"
+#include "merge-ll.h"
+#include "match-trees.h"
+#include "mem-pool.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
+#include "path.h"
 #include "promisor-remote.h"
+#include "read-cache-ll.h"
 #include "revision.h"
+#include "sparse-index.h"
 #include "strmap.h"
-#include "submodule-config.h"
-#include "submodule.h"
+#include "trace2.h"
 #include "tree.h"
 #include "unpack-trees.h"
 #include "xdiff-interface.h"
@@ -397,7 +405,7 @@ struct conflicted_submodule_item {
        int flag;
 };
 
-static void conflicted_submodule_item_free(void *util, const char *str)
+static void conflicted_submodule_item_free(void *util, const char *str UNUSED)
 {
        struct conflicted_submodule_item *item = util;
 
@@ -710,23 +718,6 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
        renames->callback_data_nr = renames->callback_data_alloc = 0;
 }
 
-__attribute__((format (printf, 2, 3)))
-static int err(struct merge_options *opt, const char *err, ...)
-{
-       va_list params;
-       struct strbuf sb = STRBUF_INIT;
-
-       strbuf_addstr(&sb, "error: ");
-       va_start(params, err);
-       strbuf_vaddf(&sb, err, params);
-       va_end(params);
-
-       error("%s", sb.buf);
-       strbuf_release(&sb);
-
-       return -1;
-}
-
 static void format_commit(struct strbuf *sb,
                          int indent,
                          struct repository *repo,
@@ -1908,6 +1899,7 @@ static void initialize_attr_index(struct merge_options *opt)
        struct index_state *attr_index = &opt->priv->attr_index;
        struct cache_entry *ce;
 
+       attr_index->repo = opt->repo;
        attr_index->initialized = 1;
 
        if (!opt->renormalize)
@@ -2042,7 +2034,7 @@ static int handle_content_merge(struct merge_options *opt,
         * the three blobs to merge on various sides of history.
         *
         * extra_marker_size is the amount to extend conflict markers in
-        * ll_merge; this is neeed if we have content merges of content
+        * ll_merge; this is needed if we have content merges of content
         * merges, which happens for example with rename/rename(2to1) and
         * rename/add conflicts.
         */
@@ -2111,13 +2103,12 @@ static int handle_content_merge(struct merge_options *opt,
                                          &result_buf);
 
                if ((merge_status < 0) || !result_buf.ptr)
-                       ret = err(opt, _("Failed to execute internal merge"));
+                       ret = error(_("failed to execute internal merge"));
 
                if (!ret &&
                    write_object_file(result_buf.ptr, result_buf.size,
                                      OBJ_BLOB, &result->oid))
-                       ret = err(opt, _("Unable to add %s to database"),
-                                 path);
+                       ret = error(_("unable to add %s to database"), path);
 
                free(result_buf.ptr);
                if (ret)
@@ -2619,8 +2610,40 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
        }
 
        assert(ci->filemask == 2 || ci->filemask == 4);
-       assert(ci->dirmask == 0);
-       strmap_remove(&opt->priv->paths, old_path, 0);
+       assert(ci->dirmask == 0 || ci->dirmask == 1);
+       if (ci->dirmask == 0)
+               strmap_remove(&opt->priv->paths, old_path, 0);
+       else {
+               /*
+                * This file exists on one side, but we still had a directory
+                * at the old location that we can't remove until after
+                * processing all paths below it.  So, make a copy of ci in
+                * new_ci and only put the file information into it.
+                */
+               new_ci = mem_pool_calloc(&opt->priv->pool, 1, sizeof(*new_ci));
+               memcpy(new_ci, ci, sizeof(*ci));
+               assert(!new_ci->match_mask);
+               new_ci->dirmask = 0;
+               new_ci->stages[1].mode = 0;
+               oidcpy(&new_ci->stages[1].oid, null_oid());
+
+               /*
+                * Now that we have the file information in new_ci, make sure
+                * ci only has the directory information.
+                */
+               ci->filemask = 0;
+               ci->merged.clean = 1;
+               for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+                       if (ci->dirmask & (1 << i))
+                               continue;
+                       /* zero out any entries related to files */
+                       ci->stages[i].mode = 0;
+                       oidcpy(&ci->stages[i].oid, null_oid());
+               }
+
+               // Now we want to focus on new_ci, so reassign ci to it
+               ci = new_ci;
+       }
 
        branch_with_new_path   = (ci->filemask == 2) ? opt->branch1 : opt->branch2;
        branch_with_dir_rename = (ci->filemask == 2) ? opt->branch2 : opt->branch1;
@@ -3299,10 +3322,7 @@ static int collect_renames(struct merge_options *opt,
        return clean;
 }
 
-static int detect_and_process_renames(struct merge_options *opt,
-                                     struct tree *merge_base,
-                                     struct tree *side1,
-                                     struct tree *side2)
+static int detect_and_process_renames(struct merge_options *opt)
 {
        struct diff_queue_struct combined = { 0 };
        struct rename_info *renames = &opt->priv->renames;
@@ -3466,19 +3486,18 @@ static int sort_dirs_next_to_their_children(const char *one, const char *two)
                return c1 - c2;
 }
 
-static int read_oid_strbuf(struct merge_options *opt,
-                          const struct object_id *oid,
+static int read_oid_strbuf(const struct object_id *oid,
                           struct strbuf *dst)
 {
        void *buf;
        enum object_type type;
        unsigned long size;
-       buf = read_object_file(oid, &type, &size);
+       buf = repo_read_object_file(the_repository, oid, &type, &size);
        if (!buf)
-               return err(opt, _("cannot read object %s"), oid_to_hex(oid));
+               return error(_("cannot read object %s"), oid_to_hex(oid));
        if (type != OBJ_BLOB) {
                free(buf);
-               return err(opt, _("object %s is not a blob"), oid_to_hex(oid));
+               return error(_("object %s is not a blob"), oid_to_hex(oid));
        }
        strbuf_attach(dst, buf, size, size + 1);
        return 0;
@@ -3502,8 +3521,8 @@ static int blob_unchanged(struct merge_options *opt,
        if (oideq(&base->oid, &side->oid))
                return 1;
 
-       if (read_oid_strbuf(opt, &base->oid, &basebuf) ||
-           read_oid_strbuf(opt, &side->oid, &sidebuf))
+       if (read_oid_strbuf(&base->oid, &basebuf) ||
+           read_oid_strbuf(&side->oid, &sidebuf))
                goto error_return;
        /*
         * Note: binary | is used so that both renormalizations are
@@ -4184,7 +4203,7 @@ static void prefetch_for_content_merges(struct merge_options *opt,
        struct string_list_item *e;
        struct oid_array to_fetch = OID_ARRAY_INIT;
 
-       if (opt->repo != the_repository || !has_promisor_remote())
+       if (opt->repo != the_repository || !repo_has_promisor_remote(the_repository))
                return;
 
        for (e = &plist->items[plist->nr-1]; e >= plist->items; --e) {
@@ -4683,14 +4702,14 @@ void merge_switch_to_result(struct merge_options *opt,
 void merge_finalize(struct merge_options *opt,
                    struct merge_result *result)
 {
-       struct merge_options_internal *opti = result->priv;
-
        if (opt->renormalize)
                git_attr_set_direction(GIT_ATTR_CHECKIN);
        assert(opt->priv == NULL);
 
-       clear_or_reinit_internal_opts(opti, 0);
-       FREE_AND_NULL(opti);
+       if (result->priv) {
+               clear_or_reinit_internal_opts(result->priv, 0);
+               FREE_AND_NULL(result->priv);
+       }
 }
 
 /*** Function Grouping: helper functions for merge_incore_*() ***/
@@ -4859,8 +4878,7 @@ 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_options *opt,
-                                        struct merge_result *result,
+static void merge_check_renames_reusable(struct merge_result *result,
                                         struct tree *merge_base,
                                         struct tree *side1,
                                         struct tree *side2)
@@ -4930,7 +4948,7 @@ redo:
                 * TRANSLATORS: The %s arguments are: 1) tree hash of a merge
                 * base, and 2-3) the trees for the two trees we're merging.
                 */
-               err(opt, _("collecting merge info failed for trees %s, %s, %s"),
+               error(_("collecting merge info failed for trees %s, %s, %s"),
                    oid_to_hex(&merge_base->object.oid),
                    oid_to_hex(&side1->object.oid),
                    oid_to_hex(&side2->object.oid));
@@ -4940,8 +4958,7 @@ redo:
        trace2_region_leave("merge", "collect_merge_info", opt->repo);
 
        trace2_region_enter("merge", "renames", opt->repo);
-       result->clean = detect_and_process_renames(opt, merge_base,
-                                                  side1, side2);
+       result->clean = detect_and_process_renames(opt);
        trace2_region_leave("merge", "renames", opt->repo);
        if (opt->priv->renames.redo_after_renames == 2) {
                trace2_region_enter("merge", "reset_maps", opt->repo);
@@ -4985,7 +5002,7 @@ static void merge_ort_internal(struct merge_options *opt,
        struct strbuf merge_base_abbrev = STRBUF_INIT;
 
        if (!merge_bases) {
-               merge_bases = get_merge_bases(h1, h2);
+               merge_bases = repo_get_merge_bases(the_repository, h1, h2);
                /* See merge-ort.h:merge_incore_recursive() declaration NOTE */
                merge_bases = reverse_commit_list(merge_bases);
        }
@@ -5063,7 +5080,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(opt, result, merge_base, side1, side2);
+       merge_check_renames_reusable(result, merge_base, side1, side2);
        merge_start(opt, result);
        /*
         * Record the trees used in this merge, so if there's a next merge in
index a994c9a5fcdb040b3ecb80db0489a50c1394ac75..ce56ec1a7802c5eac51d404e03220b83e52ebe1c 100644 (file)
@@ -2,7 +2,7 @@
 #define MERGE_ORT_H
 
 #include "merge-recursive.h"
-#include "hash.h"
+#include "hash-ll.h"
 
 struct commit;
 struct tree;
index 4ddd3adea003e32fb7f16fbfa56a886a5d4a0712..a0c3e7a2d9105dd895f0ae2613d0f87f00b8d794 100644 (file)
@@ -3,14 +3,10 @@
  * Fredrik Kuivinen.
  * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "merge-recursive.h"
 
-#include "advice.h"
 #include "alloc.h"
-#include "attr.h"
-#include "blob.h"
-#include "builtin.h"
 #include "cache-tree.h"
 #include "commit.h"
 #include "commit-reach.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "dir.h"
-#include "ll-merge.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "merge-ll.h"
 #include "lockfile.h"
-#include "object-store.h"
+#include "match-trees.h"
+#include "name-hash.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
 #include "revision.h"
+#include "sparse-index.h"
 #include "string-list.h"
-#include "submodule-config.h"
-#include "submodule.h"
+#include "symlinks.h"
 #include "tag.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
@@ -412,7 +416,7 @@ static int unpack_trees_start(struct merge_options *opt,
 {
        int rc;
        struct tree_desc t[3];
-       struct index_state tmp_index = { NULL };
+       struct index_state tmp_index = INDEX_STATE_INIT(opt->repo);
 
        memset(&opt->priv->unpack_opts, 0, sizeof(opt->priv->unpack_opts));
        if (opt->priv->call_depth)
@@ -951,7 +955,8 @@ static int update_file_flags(struct merge_options *opt,
                        goto update_index;
                }
 
-               buf = read_object_file(&contents->oid, &type, &size);
+               buf = repo_read_object_file(the_repository, &contents->oid,
+                                           &type, &size);
                if (!buf) {
                        ret = err(opt, _("cannot read object %s '%s'"),
                                  oid_to_hex(&contents->oid), path);
@@ -1373,12 +1378,12 @@ static int merge_mode_and_contents(struct merge_options *opt,
                                                  extra_marker_size);
 
                        if ((merge_status < 0) || !result_buf.ptr)
-                               ret = err(opt, _("Failed to execute internal merge"));
+                               ret = err(opt, _("failed to execute internal merge"));
 
                        if (!ret &&
                            write_object_file(result_buf.ptr, result_buf.size,
                                              OBJ_BLOB, &result->blob.oid))
-                               ret = err(opt, _("Unable to add %s to database"),
+                               ret = err(opt, _("unable to add %s to database"),
                                          a->path);
 
                        free(result_buf.ptr);
@@ -2100,7 +2105,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt,
        if (!new_path) {
                /* This should only happen when entry->non_unique_new_dir set */
                if (!entry->non_unique_new_dir)
-                       BUG("entry->non_unqiue_dir not set and !new_path");
+                       BUG("entry->non_unique_new_dir not set and !new_path");
                output(opt, 1, _("CONFLICT (directory rename split): "
                               "Unclear where to place %s because directory "
                               "%s was renamed to multiple other directories, "
@@ -3021,7 +3026,7 @@ static int read_oid_strbuf(struct merge_options *opt,
        void *buf;
        enum object_type type;
        unsigned long size;
-       buf = read_object_file(oid, &type, &size);
+       buf = repo_read_object_file(the_repository, oid, &type, &size);
        if (!buf)
                return err(opt, _("cannot read object %s"), oid_to_hex(oid));
        if (type != OBJ_BLOB) {
@@ -3592,7 +3597,7 @@ static int merge_recursive_internal(struct merge_options *opt,
        }
 
        if (!merge_bases) {
-               merge_bases = get_merge_bases(h1, h2);
+               merge_bases = repo_get_merge_bases(the_repository, h1, h2);
                merge_bases = reverse_commit_list(merge_bases);
        }
 
@@ -3797,7 +3802,7 @@ static struct commit *get_ref(struct repository *repo,
                return make_virtual_commit(repo, (struct tree*)object, name);
        if (object->type != OBJ_COMMIT)
                return NULL;
-       if (parse_commit((struct commit *)object))
+       if (repo_parse_commit(repo, (struct commit *)object))
                return NULL;
        return (struct commit *)object;
 }
@@ -3902,6 +3907,22 @@ void init_merge_options(struct merge_options *opt,
                opt->buffer_output = 0;
 }
 
+/*
+ * For now, members of merge_options do not need deep copying, but
+ * it may change in the future, in which case we would need to update
+ * this, and also make a matching change to clear_merge_options() to
+ * release the resources held by a copied instance.
+ */
+void copy_merge_options(struct merge_options *dst, struct merge_options *src)
+{
+       *dst = *src;
+}
+
+void clear_merge_options(struct merge_options *opt UNUSED)
+{
+       ; /* no-op as our copy is shallow right now */
+}
+
 int parse_merge_opt(struct merge_options *opt, const char *s)
 {
        const char *arg;
index b88000e3c25277d07d20b7ba29755b9670cd28ff..3d3b3e3c295deb0dc8470958d01f8ed1e6ef0611 100644 (file)
@@ -55,6 +55,9 @@ struct merge_options {
 
 void init_merge_options(struct merge_options *opt, struct repository *repo);
 
+void copy_merge_options(struct merge_options *dst, struct merge_options *src);
+void clear_merge_options(struct merge_options *opt);
+
 /* parse the option in s and update the relevant field of opt */
 int parse_merge_opt(struct merge_options *opt, const char *s);
 
diff --git a/merge.c b/merge.c
index 2382ff66d351cce20848b6f2ff056258adb2d4d7..ca89b312d173530f8c8a1e5d07470b9d2404e9d4 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -1,13 +1,16 @@
-#include "cache.h"
-#include "diff.h"
-#include "diffcore.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "lockfile.h"
+#include "merge.h"
 #include "commit.h"
+#include "repository.h"
 #include "run-command.h"
 #include "resolve-undo.h"
+#include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
-#include "dir.h"
 
 static const char *merge_argument(struct commit *commit)
 {
@@ -19,22 +22,22 @@ int try_merge_command(struct repository *r,
                      const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
 {
-       struct strvec args = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        int i, ret;
        struct commit_list *j;
 
-       strvec_pushf(&args, "merge-%s", strategy);
+       strvec_pushf(&cmd.args, "merge-%s", strategy);
        for (i = 0; i < xopts_nr; i++)
-               strvec_pushf(&args, "--%s", xopts[i]);
+               strvec_pushf(&cmd.args, "--%s", xopts[i]);
        for (j = common; j; j = j->next)
-               strvec_push(&args, merge_argument(j->item));
-       strvec_push(&args, "--");
-       strvec_push(&args, head_arg);
+               strvec_push(&cmd.args, merge_argument(j->item));
+       strvec_push(&cmd.args, "--");
+       strvec_push(&cmd.args, head_arg);
        for (j = remotes; j; j = j->next)
-               strvec_push(&args, merge_argument(j->item));
+               strvec_push(&cmd.args, merge_argument(j->item));
 
-       ret = run_command_v_opt(args.v, RUN_GIT_CMD);
-       strvec_clear(&args);
+       cmd.git_cmd = 1;
+       ret = run_command(&cmd);
 
        discard_index(r->index);
        if (repo_read_index(r) < 0)
diff --git a/merge.h b/merge.h
new file mode 100644 (file)
index 0000000..21ac7ef
--- /dev/null
+++ b/merge.h
@@ -0,0 +1,17 @@
+#ifndef MERGE_H
+#define MERGE_H
+
+struct commit_list;
+struct object_id;
+struct repository;
+
+int try_merge_command(struct repository *r,
+               const char *strategy, size_t xopts_nr,
+               const char **xopts, struct commit_list *common,
+               const char *head_arg, struct commit_list *remotes);
+int checkout_fast_forward(struct repository *r,
+                         const struct object_id *from,
+                         const struct object_id *to,
+                         int overwrite_ignore);
+
+#endif /* MERGE_H */
diff --git a/midx.c b/midx.c
index 3a8dcfe98e2e937c23d80db3e27fa89ccd36308b..85e1c2cd1287b34e91d9dcccc74d330c93ff809c 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -1,10 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
 #include "csum-file.h"
 #include "dir.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "hash-lookup.h"
 #include "midx.h"
 #include "progress.h"
@@ -17,6 +21,7 @@
 #include "refs.h"
 #include "revision.h"
 #include "list-objects.h"
+#include "pack-revindex.h"
 
 #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
 #define MIDX_VERSION 1
@@ -29,6 +34,7 @@
 
 #define MIDX_CHUNK_ALIGNMENT 4
 #define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
+#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
 #define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
@@ -37,6 +43,7 @@
 #define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
 #define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
 #define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
+#define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t))
 #define MIDX_LARGE_OFFSET_NEEDED 0x80000000
 
 #define PACK_EXPIRED UINT_MAX
@@ -60,6 +67,7 @@ void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m)
 static int midx_read_oid_fanout(const unsigned char *chunk_start,
                                size_t chunk_size, void *data)
 {
+       int i;
        struct multi_pack_index *m = data;
        m->chunk_oid_fanout = (uint32_t *)chunk_start;
 
@@ -67,6 +75,43 @@ static int midx_read_oid_fanout(const unsigned char *chunk_start,
                error(_("multi-pack-index OID fanout is of the wrong size"));
                return 1;
        }
+       for (i = 0; i < 255; i++) {
+               uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
+               uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i+1]);
+
+               if (oid_fanout1 > oid_fanout2) {
+                       error(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"),
+                             i, oid_fanout1, oid_fanout2, i + 1);
+                       return 1;
+               }
+       }
+       m->num_objects = ntohl(m->chunk_oid_fanout[255]);
+       return 0;
+}
+
+static int midx_read_oid_lookup(const unsigned char *chunk_start,
+                               size_t chunk_size, void *data)
+{
+       struct multi_pack_index *m = data;
+       m->chunk_oid_lookup = chunk_start;
+
+       if (chunk_size != st_mult(m->hash_len, m->num_objects)) {
+               error(_("multi-pack-index OID lookup chunk is the wrong size"));
+               return 1;
+       }
+       return 0;
+}
+
+static int midx_read_object_offsets(const unsigned char *chunk_start,
+                                   size_t chunk_size, void *data)
+{
+       struct multi_pack_index *m = data;
+       m->chunk_object_offsets = chunk_start;
+
+       if (chunk_size != st_mult(m->num_objects, MIDX_CHUNK_OFFSET_WIDTH)) {
+               error(_("multi-pack-index object offset chunk is the wrong size"));
+               return 1;
+       }
        return 0;
 }
 
@@ -133,36 +178,49 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local
 
        m->num_packs = get_be32(m->data + MIDX_BYTE_NUM_PACKS);
 
+       m->preferred_pack_idx = -1;
+
        cf = init_chunkfile(NULL);
 
        if (read_table_of_contents(cf, m->data, midx_size,
-                                  MIDX_HEADER_SIZE, m->num_chunks))
+                                  MIDX_HEADER_SIZE, m->num_chunks,
+                                  MIDX_CHUNK_ALIGNMENT))
                goto cleanup_fail;
 
-       if (pair_chunk(cf, MIDX_CHUNKID_PACKNAMES, &m->chunk_pack_names) == CHUNK_NOT_FOUND)
-               die(_("multi-pack-index missing required pack-name chunk"));
-       if (read_chunk(cf, MIDX_CHUNKID_OIDFANOUT, midx_read_oid_fanout, m) == CHUNK_NOT_FOUND)
-               die(_("multi-pack-index missing required OID fanout chunk"));
-       if (pair_chunk(cf, MIDX_CHUNKID_OIDLOOKUP, &m->chunk_oid_lookup) == CHUNK_NOT_FOUND)
-               die(_("multi-pack-index missing required OID lookup chunk"));
-       if (pair_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS, &m->chunk_object_offsets) == CHUNK_NOT_FOUND)
-               die(_("multi-pack-index missing required object offsets chunk"));
-
-       pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets);
+       if (pair_chunk(cf, MIDX_CHUNKID_PACKNAMES, &m->chunk_pack_names, &m->chunk_pack_names_len))
+               die(_("multi-pack-index required pack-name chunk missing or corrupted"));
+       if (read_chunk(cf, MIDX_CHUNKID_OIDFANOUT, midx_read_oid_fanout, m))
+               die(_("multi-pack-index required OID fanout chunk missing or corrupted"));
+       if (read_chunk(cf, MIDX_CHUNKID_OIDLOOKUP, midx_read_oid_lookup, m))
+               die(_("multi-pack-index required OID lookup chunk missing or corrupted"));
+       if (read_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS, midx_read_object_offsets, m))
+               die(_("multi-pack-index required object offsets chunk missing or corrupted"));
+
+       pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets,
+                  &m->chunk_large_offsets_len);
+       pair_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+                  (const unsigned char **)&m->chunk_bitmapped_packs,
+                  &m->chunk_bitmapped_packs_len);
 
        if (git_env_bool("GIT_TEST_MIDX_READ_RIDX", 1))
-               pair_chunk(cf, MIDX_CHUNKID_REVINDEX, &m->chunk_revindex);
-
-       m->num_objects = ntohl(m->chunk_oid_fanout[255]);
+               pair_chunk(cf, MIDX_CHUNKID_REVINDEX, &m->chunk_revindex,
+                          &m->chunk_revindex_len);
 
        CALLOC_ARRAY(m->pack_names, m->num_packs);
        CALLOC_ARRAY(m->packs, m->num_packs);
 
        cur_pack_name = (const char *)m->chunk_pack_names;
        for (i = 0; i < m->num_packs; i++) {
+               const char *end;
+               size_t avail = m->chunk_pack_names_len -
+                               (cur_pack_name - (const char *)m->chunk_pack_names);
+
                m->pack_names[i] = cur_pack_name;
 
-               cur_pack_name += strlen(cur_pack_name) + 1;
+               end = memchr(cur_pack_name, '\0', avail);
+               if (!end)
+                       die(_("multi-pack-index pack-name chunk is too short"));
+               cur_pack_name = end + 1;
 
                if (i && strcmp(m->pack_names[i], m->pack_names[i - 1]) <= 0)
                        die(_("multi-pack-index pack names out of order: '%s' before '%s'"),
@@ -236,6 +294,26 @@ int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t
        return 0;
 }
 
+int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
+                      struct bitmapped_pack *bp, uint32_t pack_int_id)
+{
+       if (!m->chunk_bitmapped_packs)
+               return error(_("MIDX does not contain the BTMP chunk"));
+
+       if (prepare_midx_pack(r, m, pack_int_id))
+               return error(_("could not load bitmapped pack %"PRIu32), pack_int_id);
+
+       bp->p = m->packs[pack_int_id];
+       bp->bitmap_pos = get_be32((char *)m->chunk_bitmapped_packs +
+                                 MIDX_CHUNK_BITMAPPED_PACKS_WIDTH * pack_int_id);
+       bp->bitmap_nr = get_be32((char *)m->chunk_bitmapped_packs +
+                                MIDX_CHUNK_BITMAPPED_PACKS_WIDTH * pack_int_id +
+                                sizeof(uint32_t));
+       bp->pack_int_id = pack_int_id;
+
+       return 0;
+}
+
 int bsearch_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result)
 {
        return bsearch_hash(oid->hash, m->chunk_oid_fanout, m->chunk_oid_lookup,
@@ -249,7 +327,7 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid,
        if (n >= m->num_objects)
                return NULL;
 
-       oidread(oid, m->chunk_oid_lookup + m->hash_len * n);
+       oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n));
        return oid;
 }
 
@@ -266,6 +344,8 @@ off_t nth_midxed_offset(struct multi_pack_index *m, uint32_t pos)
                        die(_("multi-pack-index stores a 64-bit offset, but off_t is too small"));
 
                offset32 ^= MIDX_LARGE_OFFSET_NEEDED;
+               if (offset32 >= m->chunk_large_offsets_len / sizeof(uint64_t))
+                       die(_("multi-pack-index large offset out of bounds"));
                return get_be64(m->chunk_large_offsets + sizeof(uint64_t) * offset32);
        }
 
@@ -278,7 +358,7 @@ uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos)
                        (off_t)pos * MIDX_CHUNK_OFFSET_WIDTH);
 }
 
-int fill_midx_entry(struct repository * r,
+int fill_midx_entry(struct repository *r,
                    const struct object_id *oid,
                    struct pack_entry *e,
                    struct multi_pack_index *m)
@@ -351,7 +431,8 @@ static int cmp_idx_or_pack_name(const char *idx_or_pack_name,
        return strcmp(idx_or_pack_name, idx_name);
 }
 
-int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
+int midx_locate_pack(struct multi_pack_index *m, const char *idx_or_pack_name,
+                    uint32_t *pos)
 {
        uint32_t first = 0, last = m->num_packs;
 
@@ -362,8 +443,11 @@ int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
 
                current = m->pack_names[mid];
                cmp = cmp_idx_or_pack_name(idx_or_pack_name, current);
-               if (!cmp)
+               if (!cmp) {
+                       if (pos)
+                               *pos = mid;
                        return 1;
+               }
                if (cmp > 0) {
                        first = mid + 1;
                        continue;
@@ -374,6 +458,28 @@ int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
        return 0;
 }
 
+int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
+{
+       return midx_locate_pack(m, idx_or_pack_name, NULL);
+}
+
+int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id)
+{
+       if (m->preferred_pack_idx == -1) {
+               if (load_midx_revindex(m) < 0) {
+                       m->preferred_pack_idx = -2;
+                       return -1;
+               }
+
+               m->preferred_pack_idx =
+                       nth_midxed_pack_int_id(m, pack_pos_to_midx(m, 0));
+       } else if (m->preferred_pack_idx == -2)
+               return -1; /* no revindex */
+
+       *pack_int_id = m->preferred_pack_idx;
+       return 0;
+}
+
 int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local)
 {
        struct multi_pack_index *m;
@@ -416,13 +522,31 @@ static size_t write_midx_header(struct hashfile *f,
        return MIDX_HEADER_SIZE;
 }
 
+#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
+
 struct pack_info {
        uint32_t orig_pack_int_id;
        char *pack_name;
        struct packed_git *p;
+
+       uint32_t bitmap_pos;
+       uint32_t bitmap_nr;
+
        unsigned expired : 1;
 };
 
+static void fill_pack_info(struct pack_info *info,
+                          struct packed_git *p, const char *pack_name,
+                          uint32_t orig_pack_int_id)
+{
+       memset(info, 0, sizeof(struct pack_info));
+
+       info->orig_pack_int_id = orig_pack_int_id;
+       info->pack_name = xstrdup(pack_name);
+       info->p = p;
+       info->bitmap_pos = BITMAP_POS_UNKNOWN;
+}
+
 static int pack_info_compare(const void *_a, const void *_b)
 {
        struct pack_info *a = (struct pack_info *)_a;
@@ -440,14 +564,14 @@ static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
 
 struct write_midx_context {
        struct pack_info *info;
-       uint32_t nr;
-       uint32_t alloc;
+       size_t nr;
+       size_t alloc;
        struct multi_pack_index *m;
        struct progress *progress;
        unsigned pack_paths_checked;
 
        struct pack_midx_entry *entries;
-       uint32_t entries_nr;
+       size_t entries_nr;
 
        uint32_t *pack_perm;
        uint32_t *pack_order;
@@ -463,6 +587,7 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
                             const char *file_name, void *data)
 {
        struct write_midx_context *ctx = data;
+       struct packed_git *p;
 
        if (ends_with(file_name, ".idx")) {
                display_progress(ctx->progress, ++ctx->pack_paths_checked);
@@ -489,27 +614,22 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
 
                ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
 
-               ctx->info[ctx->nr].p = add_packed_git(full_path,
-                                                     full_path_len,
-                                                     0);
-
-               if (!ctx->info[ctx->nr].p) {
+               p = add_packed_git(full_path, full_path_len, 0);
+               if (!p) {
                        warning(_("failed to add packfile '%s'"),
                                full_path);
                        return;
                }
 
-               if (open_pack_index(ctx->info[ctx->nr].p)) {
+               if (open_pack_index(p)) {
                        warning(_("failed to open pack-index '%s'"),
                                full_path);
-                       close_pack(ctx->info[ctx->nr].p);
-                       FREE_AND_NULL(ctx->info[ctx->nr].p);
+                       close_pack(p);
+                       free(p);
                        return;
                }
 
-               ctx->info[ctx->nr].pack_name = xstrdup(file_name);
-               ctx->info[ctx->nr].orig_pack_int_id = ctx->nr;
-               ctx->info[ctx->nr].expired = 0;
+               fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
                ctx->nr++;
        }
 }
@@ -579,12 +699,14 @@ static void fill_pack_entry(uint32_t pack_int_id,
 
 struct midx_fanout {
        struct pack_midx_entry *entries;
-       uint32_t nr;
-       uint32_t alloc;
+       size_t nr, alloc;
 };
 
-static void midx_fanout_grow(struct midx_fanout *fanout, uint32_t nr)
+static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr)
 {
+       if (nr < fanout->nr)
+               BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")",
+                   (uintmax_t)nr, (uintmax_t)fanout->nr);
        ALLOC_GROW(fanout->entries, nr, fanout->alloc);
 }
 
@@ -663,17 +785,18 @@ static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
 static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
                                                  struct pack_info *info,
                                                  uint32_t nr_packs,
-                                                 uint32_t *nr_objects,
+                                                 size_t *nr_objects,
                                                  int preferred_pack)
 {
        uint32_t cur_fanout, cur_pack, cur_object;
-       uint32_t alloc_objects, total_objects = 0;
+       size_t alloc_objects, total_objects = 0;
        struct midx_fanout fanout = { 0 };
        struct pack_midx_entry *deduplicated_entries = NULL;
        uint32_t start_pack = m ? m->num_packs : 0;
 
        for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
-               total_objects += info[cur_pack].p->num_objects;
+               total_objects = st_add(total_objects,
+                                      info[cur_pack].p->num_objects);
 
        /*
         * As we de-duplicate by fanout value, we expect the fanout
@@ -716,7 +839,8 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
                                                &fanout.entries[cur_object].oid))
                                continue;
 
-                       ALLOC_GROW(deduplicated_entries, *nr_objects + 1, alloc_objects);
+                       ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
+                                  alloc_objects);
                        memcpy(&deduplicated_entries[*nr_objects],
                               &fanout.entries[cur_object],
                               sizeof(struct pack_midx_entry));
@@ -761,6 +885,26 @@ static int write_midx_pack_names(struct hashfile *f, void *data)
        return 0;
 }
 
+static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
+{
+       struct write_midx_context *ctx = data;
+       size_t i;
+
+       for (i = 0; i < ctx->nr; i++) {
+               struct pack_info *pack = &ctx->info[i];
+               if (pack->expired)
+                       continue;
+
+               if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
+                       BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
+                           pack->pack_name, pack->bitmap_nr);
+
+               hashwrite_be32(f, pack->bitmap_pos);
+               hashwrite_be32(f, pack->bitmap_nr);
+       }
+       return 0;
+}
+
 static int write_midx_oid_fanout(struct hashfile *f,
                                 void *data)
 {
@@ -913,6 +1057,8 @@ static uint32_t *midx_pack_order(struct write_midx_context *ctx)
        uint32_t *pack_order;
        uint32_t i;
 
+       trace2_region_enter("midx", "midx_pack_order", the_repository);
+
        ALLOC_ARRAY(data, ctx->entries_nr);
        for (i = 0; i < ctx->entries_nr; i++) {
                struct pack_midx_entry *e = &ctx->entries[i];
@@ -926,10 +1072,23 @@ static uint32_t *midx_pack_order(struct write_midx_context *ctx)
        QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
 
        ALLOC_ARRAY(pack_order, ctx->entries_nr);
-       for (i = 0; i < ctx->entries_nr; i++)
+       for (i = 0; i < ctx->entries_nr; i++) {
+               struct pack_midx_entry *e = &ctx->entries[data[i].nr];
+               struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
+               if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+                       pack->bitmap_pos = i;
+               pack->bitmap_nr++;
                pack_order[i] = data[i].nr;
+       }
+       for (i = 0; i < ctx->nr; i++) {
+               struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
+               if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+                       pack->bitmap_pos = 0;
+       }
        free(data);
 
+       trace2_region_leave("midx", "midx_pack_order", the_repository);
+
        return pack_order;
 }
 
@@ -939,6 +1098,8 @@ static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
        struct strbuf buf = STRBUF_INIT;
        const char *tmp_file;
 
+       trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
+
        strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
 
        tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
@@ -948,6 +1109,8 @@ static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
                die(_("cannot store reverse index file"));
 
        strbuf_release(&buf);
+
+       trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
 }
 
 static void clear_midx_files_ext(const char *object_dir, const char *ext,
@@ -963,6 +1126,8 @@ static void prepare_midx_packing_data(struct packing_data *pdata,
 {
        uint32_t i;
 
+       trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
+
        memset(pdata, 0, sizeof(struct packing_data));
        prepare_packing_data(the_repository, pdata);
 
@@ -973,6 +1138,8 @@ static void prepare_midx_packing_data(struct packing_data *pdata,
                oe_set_in_pack(pdata, to,
                               ctx->info[ctx->pack_perm[from->pack_int_id]].p);
        }
+
+       trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
 }
 
 static int add_ref_to_pending(const char *refname,
@@ -980,6 +1147,7 @@ static int add_ref_to_pending(const char *refname,
                              int flag, void *cb_data)
 {
        struct rev_info *revs = (struct rev_info*)cb_data;
+       struct object_id peeled;
        struct object *object;
 
        if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
@@ -987,6 +1155,9 @@ static int add_ref_to_pending(const char *refname,
                return 0;
        }
 
+       if (!peel_iterated_oid(oid, &peeled))
+               oid = &peeled;
+
        object = parse_object_or_die(oid, refname);
        if (object->type != OBJ_COMMIT)
                return 0;
@@ -1066,6 +1237,9 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
        struct rev_info revs;
        struct bitmap_commit_cb cb = {0};
 
+       trace2_region_enter("midx", "find_commits_for_midx_bitmap",
+                           the_repository);
+
        cb.ctx = ctx;
 
        repo_init_revisions(the_repository, &revs, NULL);
@@ -1099,6 +1273,10 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
                *indexed_commits_nr_p = cb.commits_nr;
 
        release_revisions(&revs);
+
+       trace2_region_leave("midx", "find_commits_for_midx_bitmap",
+                           the_repository);
+
        return cb.commits;
 }
 
@@ -1116,6 +1294,8 @@ static int write_midx_bitmap(const char *midx_name,
        char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
                                        hash_to_hex(midx_hash));
 
+       trace2_region_enter("midx", "write_midx_bitmap", the_repository);
+
        if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
                options |= BITMAP_OPT_HASH_CACHE;
 
@@ -1161,6 +1341,9 @@ static int write_midx_bitmap(const char *midx_name,
 cleanup:
        free(index);
        free(bitmap_name);
+
+       trace2_region_leave("midx", "write_midx_bitmap", the_repository);
+
        return ret;
 }
 
@@ -1202,11 +1385,14 @@ static int write_midx_internal(const char *object_dir,
        struct hashfile *f = NULL;
        struct lock_file lk;
        struct write_midx_context ctx = { 0 };
+       int bitmapped_packs_concat_len = 0;
        int pack_name_concat_len = 0;
        int dropped_packs = 0;
        int result = 0;
        struct chunkfile *cf;
 
+       trace2_region_enter("midx", "write_midx_internal", the_repository);
+
        get_midx_filename(&midx_name, object_dir);
        if (safe_create_leading_directories(midx_name.buf))
                die_errno(_("unable to create leading directories of %s"),
@@ -1235,11 +1421,6 @@ static int write_midx_internal(const char *object_dir,
                for (i = 0; i < ctx.m->num_packs; i++) {
                        ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
 
-                       ctx.info[ctx.nr].orig_pack_int_id = i;
-                       ctx.info[ctx.nr].pack_name = xstrdup(ctx.m->pack_names[i]);
-                       ctx.info[ctx.nr].p = ctx.m->packs[i];
-                       ctx.info[ctx.nr].expired = 0;
-
                        if (flags & MIDX_WRITE_REV_INDEX) {
                                /*
                                 * If generating a reverse index, need to have
@@ -1255,10 +1436,10 @@ static int write_midx_internal(const char *object_dir,
                                if (open_pack_index(ctx.m->packs[i]))
                                        die(_("could not open index for %s"),
                                            ctx.m->packs[i]->pack_name);
-                               ctx.info[ctx.nr].p = ctx.m->packs[i];
                        }
 
-                       ctx.nr++;
+                       fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
+                                      ctx.m->pack_names[i], i);
                }
        }
 
@@ -1296,17 +1477,17 @@ static int write_midx_internal(const char *object_dir,
        }
 
        if (preferred_pack_name) {
-               int found = 0;
+               ctx.preferred_pack_idx = -1;
+
                for (i = 0; i < ctx.nr; i++) {
                        if (!cmp_idx_or_pack_name(preferred_pack_name,
                                                  ctx.info[i].pack_name)) {
                                ctx.preferred_pack_idx = i;
-                               found = 1;
                                break;
                        }
                }
 
-               if (!found)
+               if (ctx.preferred_pack_idx == -1)
                        warning(_("unknown preferred pack: '%s'"),
                                preferred_pack_name);
        } else if (ctx.nr &&
@@ -1417,8 +1598,10 @@ static int write_midx_internal(const char *object_dir,
        }
 
        for (i = 0; i < ctx.nr; i++) {
-               if (!ctx.info[i].expired)
-                       pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
+               if (ctx.info[i].expired)
+                       continue;
+               pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
+               bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
        }
 
        /* Check that the preferred pack wasn't expired (if given). */
@@ -1461,22 +1644,26 @@ static int write_midx_internal(const char *object_dir,
        add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
                  write_midx_oid_fanout);
        add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
-                 (size_t)ctx.entries_nr * the_hash_algo->rawsz,
+                 st_mult(ctx.entries_nr, the_hash_algo->rawsz),
                  write_midx_oid_lookup);
        add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
-                 (size_t)ctx.entries_nr * MIDX_CHUNK_OFFSET_WIDTH,
+                 st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH),
                  write_midx_object_offsets);
 
        if (ctx.large_offsets_needed)
                add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
-                       (size_t)ctx.num_large_offsets * MIDX_CHUNK_LARGE_OFFSET_WIDTH,
+                       st_mult(ctx.num_large_offsets,
+                               MIDX_CHUNK_LARGE_OFFSET_WIDTH),
                        write_midx_large_offsets);
 
        if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
                ctx.pack_order = midx_pack_order(&ctx);
                add_chunk(cf, MIDX_CHUNKID_REVINDEX,
-                         ctx.entries_nr * sizeof(uint32_t),
+                         st_mult(ctx.entries_nr, sizeof(uint32_t)),
                          write_midx_revindex);
+               add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+                         bitmapped_packs_concat_len,
+                         write_midx_bitmapped_packs);
        }
 
        write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
@@ -1516,8 +1703,13 @@ static int write_midx_internal(const char *object_dir,
                                      flags) < 0) {
                        error(_("could not write multi-pack bitmap"));
                        result = 1;
+                       clear_packing_data(&pdata);
+                       free(commits);
                        goto cleanup;
                }
+
+               clear_packing_data(&pdata);
+               free(commits);
        }
        /*
         * NOTE: Do not use ctx.entries beyond this point, since it might
@@ -1548,6 +1740,8 @@ cleanup:
        free(ctx.pack_order);
        strbuf_release(&midx_name);
 
+       trace2_region_leave("midx", "write_midx_internal", the_repository);
+
        return result;
 }
 
@@ -1575,7 +1769,7 @@ struct clear_midx_data {
        const char *ext;
 };
 
-static void clear_midx_file_ext(const char *full_path, size_t full_path_len,
+static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUSED,
                                const char *file_name, void *_data)
 {
        struct clear_midx_data *data = _data;
@@ -1704,15 +1898,6 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag
        }
        stop_progress(&progress);
 
-       for (i = 0; i < 255; i++) {
-               uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
-               uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i + 1]);
-
-               if (oid_fanout1 > oid_fanout2)
-                       midx_report(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"),
-                                   i, oid_fanout1, oid_fanout2, i + 1);
-       }
-
        if (m->num_objects == 0) {
                midx_report(_("the midx contains no oid"));
                /*
@@ -1951,8 +2136,8 @@ static int fill_included_packs_batch(struct repository *r,
                if (open_pack_index(p) || !p->num_objects)
                        continue;
 
-               expected_size = (size_t)(p->pack_size
-                                        * pack_info[i].referenced_objects);
+               expected_size = st_mult(p->pack_size,
+                                       pack_info[i].referenced_objects);
                expected_size /= p->num_objects;
 
                if (expected_size >= batch_size)
diff --git a/midx.h b/midx.h
index 5578cd7b835e2b396e502e8abaf4560ab765c850..b374a7afafb867a9ef9f0fd9913ec2552914859c 100644 (file)
--- a/midx.h
+++ b/midx.h
@@ -1,12 +1,12 @@
 #ifndef MIDX_H
 #define MIDX_H
 
-#include "repository.h"
 #include "string-list.h"
 
 struct object_id;
 struct pack_entry;
 struct repository;
+struct bitmapped_pack;
 
 #define GIT_TEST_MULTI_PACK_INDEX "GIT_TEST_MULTI_PACK_INDEX"
 #define GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP \
@@ -28,15 +28,21 @@ struct multi_pack_index {
        unsigned char num_chunks;
        uint32_t num_packs;
        uint32_t num_objects;
+       int preferred_pack_idx;
 
        int local;
 
        const unsigned char *chunk_pack_names;
+       size_t chunk_pack_names_len;
+       const uint32_t *chunk_bitmapped_packs;
+       size_t chunk_bitmapped_packs_len;
        const uint32_t *chunk_oid_fanout;
        const unsigned char *chunk_oid_lookup;
        const unsigned char *chunk_object_offsets;
        const unsigned char *chunk_large_offsets;
+       size_t chunk_large_offsets_len;
        const unsigned char *chunk_revindex;
+       size_t chunk_revindex_len;
 
        const char **pack_names;
        struct packed_git **packs;
@@ -55,6 +61,8 @@ void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m);
 
 struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local);
 int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t pack_int_id);
+int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
+                      struct bitmapped_pack *bp, uint32_t pack_int_id);
 int bsearch_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result);
 off_t nth_midxed_offset(struct multi_pack_index *m, uint32_t pos);
 uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos);
@@ -62,7 +70,11 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid,
                                        struct multi_pack_index *m,
                                        uint32_t n);
 int fill_midx_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e, struct multi_pack_index *m);
-int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name);
+int midx_contains_pack(struct multi_pack_index *m,
+                      const char *idx_or_pack_name);
+int midx_locate_pack(struct multi_pack_index *m, const char *idx_or_pack_name,
+                    uint32_t *pos);
+int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id);
 int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local);
 
 /*
index cd009c7c8ae455324bc9e48aa0f1f4df24211b66..251f036eef6983a66ac13013e5bee3ef770e8f9f 100644 (file)
@@ -5,8 +5,14 @@
  *
  * Copyright (C) 2008 Linus Torvalds
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "object.h"
+#include "read-cache-ll.h"
 #include "thread-utils.h"
+#include "trace.h"
 #include "trace2.h"
 #include "sparse-index.h"
 
diff --git a/name-hash.h b/name-hash.h
new file mode 100644 (file)
index 0000000..b1b4b0f
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef NAME_HASH_H
+#define NAME_HASH_H
+
+struct cache_entry;
+struct index_state;
+
+int index_dir_exists(struct index_state *istate, const char *name, int namelen);
+void adjust_dirname_case(struct index_state *istate, char *name);
+struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
+
+int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
+void add_name_hash(struct index_state *istate, struct cache_entry *ce);
+void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
+void free_name_hash(struct index_state *istate);
+
+#endif /* NAME_HASH_H */
index b7e79feaf042290c041fbcec439fc2aa8e84a3df..9a5b69632728d1d6877556a330e666ba530a894a 100644 (file)
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "default.h"
 #include "../commit.h"
 #include "../fetch-negotiator.h"
 #include "../prio-queue.h"
 #include "../refs.h"
+#include "../repository.h"
 #include "../tag.h"
 
 /* Remember to update object flag allocation in object.h */
@@ -25,7 +26,7 @@ static void rev_list_push(struct negotiation_state *ns,
        if (!(commit->object.flags & mark)) {
                commit->object.flags |= mark;
 
-               if (parse_commit(commit))
+               if (repo_parse_commit(the_repository, commit))
                        return;
 
                prio_queue_put(&ns->rev_list, commit);
@@ -55,30 +56,49 @@ static int clear_marks(const char *refname, const struct object_id *oid,
 static void mark_common(struct negotiation_state *ns, struct commit *commit,
                int ancestors_only, int dont_parse)
 {
-       if (commit != NULL && !(commit->object.flags & COMMON)) {
-               struct object *o = (struct object *)commit;
+       struct prio_queue queue = { NULL };
+
+       if (!commit || (commit->object.flags & COMMON))
+               return;
+
+       prio_queue_put(&queue, commit);
+       if (!ancestors_only) {
+               commit->object.flags |= COMMON;
 
-               if (!ancestors_only)
-                       o->flags |= COMMON;
+               if ((commit->object.flags & SEEN) && !(commit->object.flags & POPPED))
+                       ns->non_common_revs--;
+       }
+       while ((commit = prio_queue_get(&queue))) {
+               struct object *o = (struct object *)commit;
 
                if (!(o->flags & SEEN))
                        rev_list_push(ns, commit, SEEN);
                else {
                        struct commit_list *parents;
 
-                       if (!ancestors_only && !(o->flags & POPPED))
-                               ns->non_common_revs--;
                        if (!o->parsed && !dont_parse)
-                               if (parse_commit(commit))
-                                       return;
+                               if (repo_parse_commit(the_repository, commit))
+                                       continue;
 
                        for (parents = commit->parents;
                                        parents;
-                                       parents = parents->next)
-                               mark_common(ns, parents->item, 0,
-                                           dont_parse);
+                                       parents = parents->next) {
+                               struct commit *p = parents->item;
+
+                               if (p->object.flags & COMMON)
+                                       continue;
+
+                               p->object.flags |= COMMON;
+
+                               if ((p->object.flags & SEEN) && !(p->object.flags & POPPED))
+                                       ns->non_common_revs--;
+
+                               prio_queue_put(&queue, parents->item);
+                       }
                }
        }
+
+       clear_prio_queue(&queue);
 }
 
 /*
@@ -96,7 +116,7 @@ static const struct object_id *get_rev(struct negotiation_state *ns)
                        return NULL;
 
                commit = prio_queue_get(&ns->rev_list);
-               parse_commit(commit);
+               repo_parse_commit(the_repository, commit);
                parents = commit->parents;
 
                commit->object.flags |= POPPED;
index 60569b83501a0d789452a934fcabcfc7aa5b24e4..65e3c200084aa4a198145e2433ad6055425ae2fa 100644 (file)
@@ -1,24 +1,25 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "noop.h"
-#include "../commit.h"
 #include "../fetch-negotiator.h"
 
-static void known_common(struct fetch_negotiator *n, struct commit *c)
+static void known_common(struct fetch_negotiator *n UNUSED,
+                        struct commit *c UNUSED)
 {
        /* do nothing */
 }
 
-static void add_tip(struct fetch_negotiator *n, struct commit *c)
+static void add_tip(struct fetch_negotiator *n UNUSED,
+                   struct commit *c UNUSED)
 {
        /* do nothing */
 }
 
-static const struct object_id *next(struct fetch_negotiator *n)
+static const struct object_id *next(struct fetch_negotiator *n UNUSED)
 {
        return NULL;
 }
 
-static int ack(struct fetch_negotiator *n, struct commit *c)
+static int ack(struct fetch_negotiator *n UNUSED, struct commit *c UNUSED)
 {
        /*
         * This negotiator does not emit any commits, so there is no commit to
@@ -28,7 +29,7 @@ static int ack(struct fetch_negotiator *n, struct commit *c)
        return 0;
 }
 
-static void release(struct fetch_negotiator *n)
+static void release(struct fetch_negotiator *n UNUSED)
 {
        /* nothing to release */
 }
index c4398f5ae15d320b5f7167105a08d94fe6bbf4fa..5b91520430c1703762e8712e632850420cba7be8 100644 (file)
@@ -1,9 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "skipping.h"
 #include "../commit.h"
 #include "../fetch-negotiator.h"
+#include "../hex.h"
 #include "../prio-queue.h"
 #include "../refs.h"
+#include "../repository.h"
 #include "../tag.h"
 
 /* Remember to update object flag allocation in object.h */
@@ -50,7 +52,7 @@ struct data {
        int non_common_revs;
 };
 
-static int compare(const void *a_, const void *b_, void *unused)
+static int compare(const void *a_, const void *b_, void *data UNUSED)
 {
        const struct entry *a = a_;
        const struct entry *b = b_;
@@ -84,24 +86,37 @@ static int clear_marks(const char *refname, const struct object_id *oid,
 }
 
 /*
- * Mark this SEEN commit and all its SEEN ancestors as COMMON.
+ * Mark this SEEN commit and all its parsed SEEN ancestors as COMMON.
  */
-static void mark_common(struct data *data, struct commit *c)
+static void mark_common(struct data *data, struct commit *seen_commit)
 {
-       struct commit_list *p;
+       struct prio_queue queue = { NULL };
+       struct commit *c;
 
-       if (c->object.flags & COMMON)
+       if (seen_commit->object.flags & COMMON)
                return;
-       c->object.flags |= COMMON;
-       if (!(c->object.flags & POPPED))
-               data->non_common_revs--;
 
-       if (!c->object.parsed)
-               return;
-       for (p = c->parents; p; p = p->next) {
-               if (p->item->object.flags & SEEN)
-                       mark_common(data, p->item);
+       prio_queue_put(&queue, seen_commit);
+       seen_commit->object.flags |= COMMON;
+       while ((c = prio_queue_get(&queue))) {
+               struct commit_list *p;
+
+               if (!(c->object.flags & POPPED))
+                       data->non_common_revs--;
+
+               if (!c->object.parsed)
+                       continue;
+               for (p = c->parents; p; p = p->next) {
+                       if (!(p->item->object.flags & SEEN) ||
+                           (p->item->object.flags & COMMON))
+                               continue;
+
+                       p->item->object.flags |= COMMON;
+                       prio_queue_put(&queue, p->item);
+               }
        }
+
+       clear_prio_queue(&queue);
 }
 
 /*
@@ -178,7 +193,7 @@ static const struct object_id *get_rev(struct data *data)
                if (!(commit->object.flags & COMMON) && !entry->ttl)
                        to_send = commit;
 
-               parse_commit(commit);
+               repo_parse_commit(the_repository, commit);
                for (p = commit->parents; p; p = p->next)
                        parent_pushed |= push_parent(data, entry, p->item);
 
diff --git a/neue b/neue
new file mode 100644 (file)
index 0000000..e69de29
index 9dfd251a8151d4205297b5c27064a01acf448f69..0e1d5b1ac7a48d9bda8f76091a55be7c527e68cc 100644 (file)
@@ -1,9 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "notes-cache.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "pretty.h"
 #include "repository.h"
 #include "commit.h"
 #include "refs.h"
+#include "strbuf.h"
 
 static int notes_cache_match_validity(struct repository *r,
                                      const char *ref,
@@ -23,7 +25,8 @@ static int notes_cache_match_validity(struct repository *r,
                return 0;
 
        memset(&pretty_ctx, 0, sizeof(pretty_ctx));
-       format_commit_message(commit, "%s", &msg, &pretty_ctx);
+       repo_format_commit_message(r, commit, "%s", &msg,
+                                  &pretty_ctx);
        strbuf_trim(&msg);
 
        ret = !strcmp(msg.buf, validity);
@@ -81,7 +84,7 @@ char *notes_cache_get(struct notes_cache *c, struct object_id *key_oid,
        value_oid = get_note(&c->tree, key_oid);
        if (!value_oid)
                return NULL;
-       value = read_object_file(value_oid, &type, &size);
+       value = repo_read_object_file(the_repository, value_oid, &type, &size);
 
        *outsize = size;
        return value;
index b4cc594a790965aca297fe87392a50b643e6fb41..8799b522a55f31869dcc8cbfd7229cc8db65af4c 100644 (file)
@@ -1,16 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "commit.h"
+#include "gettext.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "hex.h"
 #include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "dir.h"
 #include "notes.h"
 #include "notes-merge.h"
 #include "strbuf.h"
+#include "trace.h"
 #include "notes-utils.h"
 #include "commit-reach.h"
 
@@ -326,7 +333,7 @@ static void write_note_to_worktree(const struct object_id *obj,
 {
        enum object_type type;
        unsigned long size;
-       void *buf = read_object_file(note, &type, &size);
+       void *buf = repo_read_object_file(the_repository, note, &type, &size);
 
        if (!buf)
                die("cannot read note %s for object %s",
@@ -566,7 +573,7 @@ int notes_merge(struct notes_merge_options *o,
        trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid));
 
        /* Dereference o->remote_ref into remote_oid */
-       if (get_oid(o->remote_ref, &remote_oid)) {
+       if (repo_get_oid(the_repository, o->remote_ref, &remote_oid)) {
                /*
                 * Failed to get remote_oid. If o->remote_ref looks like an
                 * unborn ref, perform the merge using an empty notes tree.
@@ -600,7 +607,7 @@ int notes_merge(struct notes_merge_options *o,
        assert(local && remote);
 
        /* Find merge bases */
-       bases = get_merge_bases(local, remote);
+       bases = repo_get_merge_bases(the_repository, local, remote);
        if (!bases) {
                base_oid = null_oid();
                base_tree_oid = the_hash_algo->empty_tree;
@@ -678,7 +685,8 @@ int notes_merge_commit(struct notes_merge_options *o,
        DIR *dir;
        struct dirent *e;
        struct strbuf path = STRBUF_INIT;
-       const char *buffer = get_commit_buffer(partial_commit, NULL);
+       const char *buffer = repo_get_commit_buffer(the_repository,
+                                                   partial_commit, NULL);
        const char *msg = strstr(buffer, "\n\n");
        int baselen;
 
@@ -725,7 +733,7 @@ int notes_merge_commit(struct notes_merge_options *o,
 
        create_notes_commit(o->repo, partial_tree, partial_commit->parents, msg,
                            strlen(msg), result_oid);
-       unuse_commit_buffer(partial_commit, buffer);
+       repo_unuse_commit_buffer(the_repository, partial_commit, buffer);
        if (o->verbosity >= 4)
                printf("Finalized notes merge commit: %s\n",
                        oid_to_hex(result_oid));
index d7d18e30f5a281278aa87fd64a4e47da8410a5eb..6197a5a4556400603ed7e669d5cccb0db4089e21 100644 (file)
@@ -1,9 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "commit.h"
+#include "environment.h"
+#include "gettext.h"
 #include "refs.h"
 #include "notes-utils.h"
-#include "repository.h"
+#include "strbuf.h"
 
 void create_notes_commit(struct repository *r,
                         struct notes_tree *t,
@@ -23,7 +25,7 @@ void create_notes_commit(struct repository *r,
                struct object_id parent_oid;
                if (!read_ref(t->ref, &parent_oid)) {
                        struct commit *parent = lookup_commit(r, &parent_oid);
-                       if (parse_commit(parent))
+                       if (repo_parse_commit(r, parent))
                                die("Failed to find/parse commit %s", t->ref);
                        commit_list_insert(parent, &parents);
                }
@@ -91,7 +93,9 @@ static combine_notes_fn parse_combine_notes_fn(const char *v)
                return NULL;
 }
 
-static int notes_rewrite_config(const char *k, const char *v, void *cb)
+static int notes_rewrite_config(const char *k, const char *v,
+                               const struct config_context *ctx UNUSED,
+                               void *cb)
 {
        struct notes_rewrite_cfg *c = cb;
        if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
@@ -107,6 +111,8 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb)
                }
                return 0;
        } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
+               if (!v)
+                       return config_error_nonbool(k);
                /* note that a refs/ prefix is implied in the
                 * underlying for_each_glob_ref */
                if (starts_with(v, "refs/notes/"))
diff --git a/notes.c b/notes.c
index f2805d51bb15131c46d41fa9554c43ab2e9a492d..fed1eda80cd7e41a2e21af26aed81e1dd472b27c 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "hex.h"
 #include "notes.h"
-#include "object-store.h"
-#include "blob.h"
-#include "tree.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "utf8.h"
 #include "strbuf.h"
 #include "tree-walk.h"
@@ -752,7 +753,7 @@ static int write_each_non_note_until(const char *note_path,
        return 0;
 }
 
-static int write_each_note(const struct object_id *object_oid,
+static int write_each_note(const struct object_id *object_oid UNUSED,
                const struct object_id *note_oid, char *note_path,
                void *cb_data)
 {
@@ -780,13 +781,14 @@ struct note_delete_list {
 };
 
 static int prune_notes_helper(const struct object_id *object_oid,
-               const struct object_id *note_oid, char *note_path,
-               void *cb_data)
+                             const struct object_id *note_oid UNUSED,
+                             char *note_path UNUSED,
+                             void *cb_data)
 {
        struct note_delete_list **l = (struct note_delete_list **) cb_data;
        struct note_delete_list *n;
 
-       if (has_object_file(object_oid))
+       if (repo_has_object_file(the_repository, object_oid))
                return 0; /* nothing to do for this note */
 
        /* failed to find object => prune this note */
@@ -807,13 +809,15 @@ int combine_notes_concatenate(struct object_id *cur_oid,
 
        /* read in both note blob objects */
        if (!is_null_oid(new_oid))
-               new_msg = read_object_file(new_oid, &new_type, &new_len);
+               new_msg = repo_read_object_file(the_repository, new_oid,
+                                               &new_type, &new_len);
        if (!new_msg || !new_len || new_type != OBJ_BLOB) {
                free(new_msg);
                return 0;
        }
        if (!is_null_oid(cur_oid))
-               cur_msg = read_object_file(cur_oid, &cur_type, &cur_len);
+               cur_msg = repo_read_object_file(the_repository, cur_oid,
+                                               &cur_type, &cur_len);
        if (!cur_msg || !cur_len || cur_type != OBJ_BLOB) {
                free(cur_msg);
                free(new_msg);
@@ -848,8 +852,8 @@ int combine_notes_overwrite(struct object_id *cur_oid,
        return 0;
 }
 
-int combine_notes_ignore(struct object_id *cur_oid,
-                        const struct object_id *new_oid)
+int combine_notes_ignore(struct object_id *cur_oid UNUSED,
+                        const struct object_id *new_oid UNUSED)
 {
        return 0;
 }
@@ -869,7 +873,7 @@ static int string_list_add_note_lines(struct string_list *list,
                return 0;
 
        /* read_sha1_file NUL-terminates */
-       data = read_object_file(oid, &t, &len);
+       data = repo_read_object_file(the_repository, oid, &t, &len);
        if (t != OBJ_BLOB || !data || !len) {
                free(data);
                return t != OBJ_BLOB || !data;
@@ -944,7 +948,7 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
                for_each_glob_ref(string_list_add_one_ref, glob, list);
        } else {
                struct object_id oid;
-               if (get_oid(glob, &oid))
+               if (repo_get_oid(the_repository, glob, &oid))
                        warning("notes ref %s is invalid", glob);
                if (!unsorted_string_list_has_string(list, glob))
                        string_list_append(list, glob);
@@ -958,7 +962,7 @@ void string_list_add_refs_from_colon_sep(struct string_list *list,
        char *globs_copy = xstrdup(globs);
        int i;
 
-       string_list_split_in_place(&split, globs_copy, ':', -1);
+       string_list_split_in_place(&split, globs_copy, ":", -1);
        string_list_remove_empty_items(&split, 0);
 
        for (i = 0; i < split.nr; i++)
@@ -968,7 +972,9 @@ void string_list_add_refs_from_colon_sep(struct string_list *list,
        free(globs_copy);
 }
 
-static int notes_display_config(const char *k, const char *v, void *cb)
+static int notes_display_config(const char *k, const char *v,
+                               const struct config_context *ctx UNUSED,
+                               void *cb)
 {
        int *load_refs = cb;
 
@@ -1014,14 +1020,14 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
        t->root = (struct int_node *) xcalloc(1, sizeof(struct int_node));
        t->first_non_note = NULL;
        t->prev_non_note = NULL;
-       t->ref = xstrdup_or_null(notes_ref);
+       t->ref = xstrdup(notes_ref);
        t->update_ref = (flags & NOTES_INIT_WRITABLE) ? t->ref : NULL;
        t->combine_notes = combine_notes;
        t->initialized = 1;
        t->dirty = 0;
 
-       if (flags & NOTES_INIT_EMPTY || !notes_ref ||
-           get_oid_treeish(notes_ref, &object_oid))
+       if (flags & NOTES_INIT_EMPTY ||
+           repo_get_oid_treeish(the_repository, notes_ref, &object_oid))
                return;
        if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))
                die("Cannot use notes ref %s", notes_ref);
@@ -1264,7 +1270,7 @@ static void format_note(struct notes_tree *t, const struct object_id *object_oid
        if (!oid)
                return;
 
-       if (!(msg = read_object_file(oid, &type, &msglen)) || type != OBJ_BLOB) {
+       if (!(msg = repo_read_object_file(the_repository, oid, &type, &msglen)) || type != OBJ_BLOB) {
                free(msg);
                return;
        }
@@ -1348,7 +1354,7 @@ void expand_loose_notes_ref(struct strbuf *sb)
 {
        struct object_id object;
 
-       if (get_oid(sb->buf, &object)) {
+       if (repo_get_oid(the_repository, sb->buf, &object)) {
                /* fallback to expand_notes_ref */
                expand_notes_ref(sb);
        }
diff --git a/notes.h b/notes.h
index c1682c39a97bf6d84940b64b353286dfb91282a3..064fd7143aad33424714de671f08238bdd463190 100644 (file)
--- a/notes.h
+++ b/notes.h
@@ -256,7 +256,17 @@ void free_notes(struct notes_tree *t);
 struct string_list;
 
 struct display_notes_opt {
+       /*
+        * Less than `0` is "unset", which means that the default notes
+        * are shown iff no other notes are given. Otherwise,
+        * treat it like a boolean.
+        */
        int use_default_notes;
+
+       /*
+        * A list of globs (in the same style as notes.displayRef) where
+        * notes should be loaded from.
+        */
        struct string_list extra_notes_refs;
 };
 
@@ -283,14 +293,7 @@ void disable_display_notes(struct display_notes_opt *opt, int *show_notes);
 /*
  * Load the notes machinery for displaying several notes trees.
  *
- * If 'opt' is not NULL, then it specifies additional settings for the
- * displaying:
- *
- * - suppress_default_notes indicates that the notes from
- *   core.notesRef and notes.displayRef should not be loaded.
- *
- * - extra_notes_refs may contain a list of globs (in the same style
- *   as notes.displayRef) where notes should be loaded from.
+ * 'opt' may be NULL.
  */
 void load_display_notes(struct display_notes_opt *opt);
 
index 5b270f046dda1d4234af6174cd80c07f64f9dff6..619f039ebc7ceb52467e4810c5c4cf3afabb907a 100644 (file)
@@ -6,33 +6,35 @@
  * This handles basic git object files - packing, unpacking,
  * creation etc.
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "convert.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "string-list.h"
 #include "lockfile.h"
-#include "delta.h"
 #include "pack.h"
-#include "blob.h"
 #include "commit.h"
 #include "run-command.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
 #include "refs.h"
-#include "pack-revindex.h"
-#include "hash-lookup.h"
 #include "bulk-checkin.h"
 #include "repository.h"
 #include "replace-object.h"
 #include "streaming.h"
 #include "dir.h"
 #include "list.h"
-#include "mergesort.h"
 #include "quote.h"
 #include "packfile.h"
+#include "object-file.h"
 #include "object-store.h"
+#include "oidtree.h"
+#include "path.h"
 #include "promisor-remote.h"
+#include "setup.h"
 #include "submodule.h"
+#include "fsck.h"
 
 /* The maximum size for an object header. */
 #define MAX_HEADER_LEN 32
@@ -140,27 +142,32 @@ static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx)
        oid->algo = GIT_HASH_SHA256;
 }
 
-static void git_hash_unknown_init(git_hash_ctx *ctx)
+static void git_hash_unknown_init(git_hash_ctx *ctx UNUSED)
 {
        BUG("trying to init unknown hash");
 }
 
-static void git_hash_unknown_clone(git_hash_ctx *dst, const git_hash_ctx *src)
+static void git_hash_unknown_clone(git_hash_ctx *dst UNUSED,
+                                  const git_hash_ctx *src UNUSED)
 {
        BUG("trying to clone unknown hash");
 }
 
-static void git_hash_unknown_update(git_hash_ctx *ctx, const void *data, size_t len)
+static void git_hash_unknown_update(git_hash_ctx *ctx UNUSED,
+                                   const void *data UNUSED,
+                                   size_t len UNUSED)
 {
        BUG("trying to update unknown hash");
 }
 
-static void git_hash_unknown_final(unsigned char *hash, git_hash_ctx *ctx)
+static void git_hash_unknown_final(unsigned char *hash UNUSED,
+                                  git_hash_ctx *ctx UNUSED)
 {
        BUG("trying to finalize unknown hash");
 }
 
-static void git_hash_unknown_final_oid(struct object_id *oid, git_hash_ctx *ctx)
+static void git_hash_unknown_final_oid(struct object_id *oid UNUSED,
+                                      git_hash_ctx *ctx UNUSED)
 {
        BUG("trying to finalize unknown hash");
 }
@@ -261,7 +268,7 @@ int hash_algo_by_length(int len)
 
 /*
  * This is meant to hold a *small* number of objects that you would
- * want read_object_file() to be able to return, but yet you do not want
+ * want repo_read_object_file() to be able to return, but yet you do not want
  * to write them into the object store (e.g. a browse-only
  * application).
  */
@@ -503,7 +510,9 @@ static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,
 {
        struct object_directory *ent;
        struct strbuf pathbuf = STRBUF_INIT;
+       struct strbuf tmp = STRBUF_INIT;
        khiter_t pos;
+       int ret = -1;
 
        if (!is_absolute_path(entry->buf) && relative_base) {
                strbuf_realpath(&pathbuf, relative_base, 1);
@@ -511,12 +520,12 @@ static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,
        }
        strbuf_addbuf(&pathbuf, entry);
 
-       if (strbuf_normalize_path(&pathbuf) < 0 && relative_base) {
+       if (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {
                error(_("unable to normalize alternate object path: %s"),
                      pathbuf.buf);
-               strbuf_release(&pathbuf);
-               return -1;
+               goto error;
        }
+       strbuf_swap(&pathbuf, &tmp);
 
        /*
         * The trailing slash after the directory name is given by
@@ -525,10 +534,8 @@ static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,
        while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
                strbuf_setlen(&pathbuf, pathbuf.len - 1);
 
-       if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos)) {
-               strbuf_release(&pathbuf);
-               return -1;
-       }
+       if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))
+               goto error;
 
        CALLOC_ARRAY(ent, 1);
        /* pathbuf.buf is already in r->objects->odb_by_path */
@@ -543,8 +550,11 @@ static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,
 
        /* recursively add alternates */
        read_info_alternates(r, ent->path, depth + 1);
-
-       return 0;
+       ret = 0;
+ error:
+       strbuf_release(&tmp);
+       strbuf_release(&pathbuf);
+       return ret;
 }
 
 static const char *parse_alt_odb_entry(const char *string,
@@ -591,10 +601,7 @@ static void link_alt_odb_entries(struct repository *r, const char *alt,
                return;
        }
 
-       strbuf_add_absolute_path(&objdirbuf, r->objects->odb->path);
-       if (strbuf_normalize_path(&objdirbuf) < 0)
-               die(_("unable to normalize object directory: %s"),
-                   objdirbuf.buf);
+       strbuf_realpath(&objdirbuf, r->objects->odb->path, 1);
 
        while (*alt) {
                alt = parse_alt_odb_entry(alt, sep, &entry);
@@ -938,6 +945,12 @@ void prepare_alt_odb(struct repository *r)
        r->objects->loaded_alternates = 1;
 }
 
+int has_alt_odb(struct repository *r)
+{
+       prepare_alt_odb(r);
+       return !!r->objects->odb->next;
+}
+
 /* Returns 1 if we have successfully freshened the file, 0 otherwise. */
 static int freshen_file(const char *fn)
 {
@@ -1206,35 +1219,25 @@ static int quick_has_loose(struct repository *r,
 }
 
 /*
- * Map the loose object at "path" if it is not NULL, or the path found by
- * searching for a loose object named "oid".
+ * Map and close the given loose object fd. The path argument is used for
+ * error reporting.
  */
-static void *map_loose_object_1(struct repository *r, const char *path,
-                            const struct object_id *oid, unsigned long *size)
+static void *map_fd(int fd, const char *path, unsigned long *size)
 {
-       void *map;
-       int fd;
-
-       if (path)
-               fd = git_open(path);
-       else
-               fd = open_loose_object(r, oid, &path);
-       map = NULL;
-       if (fd >= 0) {
-               struct stat st;
+       void *map = NULL;
+       struct stat st;
 
-               if (!fstat(fd, &st)) {
-                       *size = xsize_t(st.st_size);
-                       if (!*size) {
-                               /* mmap() is forbidden on empty files */
-                               error(_("object file %s is empty"), path);
-                               close(fd);
-                               return NULL;
-                       }
-                       map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (!fstat(fd, &st)) {
+               *size = xsize_t(st.st_size);
+               if (!*size) {
+                       /* mmap() is forbidden on empty files */
+                       error(_("object file %s is empty"), path);
+                       close(fd);
+                       return NULL;
                }
-               close(fd);
+               map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
        }
+       close(fd);
        return map;
 }
 
@@ -1242,7 +1245,12 @@ void *map_loose_object(struct repository *r,
                       const struct object_id *oid,
                       unsigned long *size)
 {
-       return map_loose_object_1(r, NULL, oid, size);
+       const char *p;
+       int fd = open_loose_object(r, oid, &p);
+
+       if (fd < 0)
+               return NULL;
+       return map_fd(fd, p, size);
 }
 
 enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
@@ -1422,7 +1430,9 @@ static int loose_object_info(struct repository *r,
                             struct object_info *oi, int flags)
 {
        int status = 0;
+       int fd;
        unsigned long mapsize;
+       const char *path;
        void *map;
        git_zstream stream;
        char hdr[MAX_HEADER_LEN];
@@ -1443,7 +1453,6 @@ static int loose_object_info(struct repository *r,
         * object even exists.
         */
        if (!oi->typep && !oi->type_name && !oi->sizep && !oi->contentp) {
-               const char *path;
                struct stat st;
                if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
                        return quick_has_loose(r, oid) ? 0 : -1;
@@ -1454,7 +1463,13 @@ static int loose_object_info(struct repository *r,
                return 0;
        }
 
-       map = map_loose_object(r, oid, &mapsize);
+       fd = open_loose_object(r, oid, &path);
+       if (fd < 0) {
+               if (errno != ENOENT)
+                       error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
+               return -1;
+       }
+       map = map_fd(fd, path, &mapsize);
        if (!map)
                return -1;
 
@@ -1492,6 +1507,10 @@ static int loose_object_info(struct repository *r,
                break;
        }
 
+       if (status && (flags & OBJECT_INFO_DIE_IF_CORRUPT))
+               die(_("loose object %s (stored in %s) is corrupt"),
+                   oid_to_hex(oid), path);
+
        git_inflate_end(&stream);
 cleanup:
        munmap(map, mapsize);
@@ -1570,9 +1589,6 @@ static int do_oid_object_info_extended(struct repository *r,
                if (find_pack_entry(r, real, &e))
                        break;
 
-               if (flags & OBJECT_INFO_IGNORE_LOOSE)
-                       return -1;
-
                /* Most likely it's a loose object. */
                if (!loose_object_info(r, real, oi, flags))
                        return 0;
@@ -1599,15 +1615,20 @@ static int do_oid_object_info_extended(struct repository *r,
                if (fetch_if_missing && repo_has_promisor_remote(r) &&
                    !already_retried &&
                    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {
-                       /*
-                        * TODO Investigate checking promisor_remote_get_direct()
-                        * TODO return value and stopping on error here.
-                        */
                        promisor_remote_get_direct(r, real, 1);
                        already_retried = 1;
                        continue;
                }
 
+               if (flags & OBJECT_INFO_DIE_IF_CORRUPT) {
+                       const struct packed_git *p;
+                       if ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))
+                               die(_("replacement %s not found for %s"),
+                                   oid_to_hex(real), oid_to_hex(oid));
+                       if ((p = has_packed_and_bad(r, real)))
+                               die(_("packed object %s (stored in %s) is corrupt"),
+                                   oid_to_hex(real), p->pack_name);
+               }
                return -1;
        }
 
@@ -1658,28 +1679,13 @@ int oid_object_info(struct repository *r,
        return type;
 }
 
-static void *read_object(struct repository *r,
-                        const struct object_id *oid, enum object_type *type,
-                        unsigned long *size)
-{
-       struct object_info oi = OBJECT_INFO_INIT;
-       void *content;
-       oi.typep = type;
-       oi.sizep = size;
-       oi.contentp = &content;
-
-       if (oid_object_info_extended(r, oid, &oi, 0) < 0)
-               return NULL;
-       return content;
-}
-
 int pretend_object_file(void *buf, unsigned long len, enum object_type type,
                        struct object_id *oid)
 {
        struct cached_object *co;
 
        hash_object_file(the_hash_algo, buf, len, type, oid);
-       if (has_object_file_with_flags(oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
+       if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
            find_cached_object(oid))
                return 0;
        ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
@@ -1694,46 +1700,25 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 
 /*
  * This function dies on corrupt objects; the callers who want to
- * deal with them should arrange to call read_object() and give error
- * messages themselves.
+ * deal with them should arrange to call oid_object_info_extended() and give
+ * error messages themselves.
  */
-void *read_object_file_extended(struct repository *r,
-                               const struct object_id *oid,
-                               enum object_type *type,
-                               unsigned long *size,
-                               int lookup_replace)
+void *repo_read_object_file(struct repository *r,
+                           const struct object_id *oid,
+                           enum object_type *type,
+                           unsigned long *size)
 {
+       struct object_info oi = OBJECT_INFO_INIT;
+       unsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;
        void *data;
-       const struct packed_git *p;
-       const char *path;
-       struct stat st;
-       const struct object_id *repl = lookup_replace ?
-               lookup_replace_object(r, oid) : oid;
 
-       errno = 0;
-       data = read_object(r, repl, type, size);
-       if (data)
-               return data;
-
-       obj_read_lock();
-       if (errno && errno != ENOENT)
-               die_errno(_("failed to read object %s"), oid_to_hex(oid));
-
-       /* die if we replaced an object with one that does not exist */
-       if (repl != oid)
-               die(_("replacement %s not found for %s"),
-                   oid_to_hex(repl), oid_to_hex(oid));
-
-       if (!stat_loose_object(r, repl, &st, &path))
-               die(_("loose object %s (stored in %s) is corrupt"),
-                   oid_to_hex(repl), path);
-
-       if ((p = has_packed_and_bad(r, repl)))
-               die(_("packed object %s (stored in %s) is corrupt"),
-                   oid_to_hex(repl), p->pack_name);
-       obj_read_unlock();
+       oi.typep = type;
+       oi.sizep = size;
+       oi.contentp = &data;
+       if (oid_object_info_extended(r, oid, &oi, flags))
+               return NULL;
 
-       return NULL;
+       return data;
 }
 
 void *read_object_with_reference(struct repository *r,
@@ -1863,13 +1848,6 @@ out:
        return 0;
 }
 
-static int write_buffer(int fd, const void *buf, size_t len)
-{
-       if (write_in_full(fd, buf, len) < 0)
-               return error_errno(_("file write error"));
-       return 0;
-}
-
 static void hash_object_file_literally(const struct git_hash_algo *algo,
                                       const void *buf, unsigned long len,
                                       const char *type, struct object_id *oid)
@@ -2014,8 +1992,8 @@ static int write_loose_object_common(git_hash_ctx *c,
 
        ret = git_deflate(stream, flush ? Z_FINISH : 0);
        the_hash_algo->update_fn(c, in0, stream->next_in - in0);
-       if (write_buffer(fd, compressed, stream->next_out - compressed) < 0)
-               die(_("unable to write loose object file"));
+       if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
+               die_errno(_("unable to write loose object file"));
        stream->next_out = compressed;
        stream->avail_out = compressed_len;
 
@@ -2268,6 +2246,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
 {
        void *buf;
        unsigned long len;
+       struct object_info oi = OBJECT_INFO_INIT;
        enum object_type type;
        char hdr[MAX_HEADER_LEN];
        int hdrlen;
@@ -2275,8 +2254,10 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
 
        if (has_loose_object(oid))
                return 0;
-       buf = read_object(the_repository, oid, &type, &len);
-       if (!buf)
+       oi.typep = &type;
+       oi.sizep = &len;
+       oi.contentp = &buf;
+       if (oid_object_info_extended(the_repository, oid, &oi, 0))
                return error(_("cannot read object for %s"), oid_to_hex(oid));
        hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
        ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
@@ -2311,32 +2292,21 @@ int repo_has_object_file(struct repository *r,
        return repo_has_object_file_with_flags(r, oid, 0);
 }
 
-static void check_tree(const void *buf, size_t size)
-{
-       struct tree_desc desc;
-       struct name_entry entry;
-
-       init_tree_desc(&desc, buf, size);
-       while (tree_entry(&desc, &entry))
-               /* do nothing
-                * tree_entry() will die() on malformed entries */
-               ;
-}
-
-static void check_commit(const void *buf, size_t size)
-{
-       struct commit c;
-       memset(&c, 0, sizeof(c));
-       if (parse_commit_buffer(the_repository, &c, buf, size, 0))
-               die(_("corrupt commit"));
-}
-
-static void check_tag(const void *buf, size_t size)
-{
-       struct tag t;
-       memset(&t, 0, sizeof(t));
-       if (parse_tag_buffer(the_repository, &t, buf, size))
-               die(_("corrupt tag"));
+/*
+ * We can't use the normal fsck_error_function() for index_mem(),
+ * because we don't yet have a valid oid for it to report. Instead,
+ * report the minimal fsck error here, and rely on the caller to
+ * give more context.
+ */
+static int hash_format_check_report(struct fsck_options *opts UNUSED,
+                                    const struct object_id *oid UNUSED,
+                                    enum object_type object_type UNUSED,
+                                    enum fsck_msg_type msg_type UNUSED,
+                                    enum fsck_msg_id msg_id UNUSED,
+                                    const char *message)
+{
+       error(_("object fails fsck: %s"), message);
+       return 1;
 }
 
 static int index_mem(struct index_state *istate,
@@ -2363,12 +2333,13 @@ static int index_mem(struct index_state *istate,
                }
        }
        if (flags & HASH_FORMAT_CHECK) {
-               if (type == OBJ_TREE)
-                       check_tree(buf, size);
-               if (type == OBJ_COMMIT)
-                       check_commit(buf, size);
-               if (type == OBJ_TAG)
-                       check_tag(buf, size);
+               struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
+
+               opts.strict = 1;
+               opts.error_func = hash_format_check_report;
+               if (fsck_buffer(null_oid(), type, buf, size, &opts))
+                       die(_("refusing to create malformed object"));
+               fsck_finish(&opts);
        }
 
        if (write_object)
@@ -2467,11 +2438,11 @@ static int index_core(struct index_state *istate,
  * binary blobs, they generally do not want to get any conversion, and
  * callers should avoid this code path when filters are requested.
  */
-static int index_stream(struct object_id *oid, int fd, size_t size,
-                       enum object_type type, const char *path,
-                       unsigned flags)
+static int index_blob_stream(struct object_id *oid, int fd, size_t size,
+                            const char *path,
+                            unsigned flags)
 {
-       return index_bulk_checkin(oid, fd, size, type, path, flags);
+       return index_blob_bulk_checkin(oid, fd, size, path, flags);
 }
 
 int index_fd(struct index_state *istate, struct object_id *oid,
@@ -2493,8 +2464,8 @@ int index_fd(struct index_state *istate, struct object_id *oid,
                ret = index_core(istate, oid, fd, xsize_t(st->st_size),
                                 type, path, flags);
        else
-               ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
-                                  flags);
+               ret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,
+                                       flags);
        close(fd);
        return ret;
 }
@@ -2680,7 +2651,8 @@ int for_each_loose_object(each_loose_object_fn cb, void *data,
        return 0;
 }
 
-static int append_loose_object(const struct object_id *oid, const char *path,
+static int append_loose_object(const struct object_id *oid,
+                              const char *path UNUSED,
                               void *data)
 {
        oidtree_insert(data, oid);
@@ -2791,13 +2763,16 @@ int read_loose_object(const char *path,
                      struct object_info *oi)
 {
        int ret = -1;
+       int fd;
        void *map = NULL;
        unsigned long mapsize;
        git_zstream stream;
        char hdr[MAX_HEADER_LEN];
        unsigned long *size = oi->sizep;
 
-       map = map_loose_object_1(the_repository, path, NULL, &mapsize);
+       fd = git_open(path);
+       if (fd >= 0)
+               map = map_fd(fd, path, &mapsize);
        if (!map) {
                error_errno(_("unable to mmap %s"), path);
                goto out;
diff --git a/object-file.h b/object-file.h
new file mode 100644 (file)
index 0000000..d641461
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef OBJECT_FILE_H
+#define OBJECT_FILE_H
+
+#include "git-zlib.h"
+#include "object.h"
+
+struct index_state;
+
+/*
+ * Set this to 0 to prevent oid_object_info_extended() from fetching missing
+ * blobs. This has a difference only if extensions.partialClone is set.
+ *
+ * Its default value is 1.
+ */
+extern int fetch_if_missing;
+
+#define HASH_WRITE_OBJECT 1
+#define HASH_FORMAT_CHECK 2
+#define HASH_RENORMALIZE  4
+#define HASH_SILENT 8
+int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
+
+/*
+ * Create the directory containing the named path, using care to be
+ * somewhat safe against races. Return one of the scld_error values to
+ * indicate success/failure. On error, set errno to describe the
+ * problem.
+ *
+ * SCLD_VANISHED indicates that one of the ancestor directories of the
+ * path existed at one point during the function call and then
+ * suddenly vanished, probably because another process pruned the
+ * directory while we were working.  To be robust against this kind of
+ * race, callers might want to try invoking the function again when it
+ * returns SCLD_VANISHED.
+ *
+ * safe_create_leading_directories() temporarily changes path while it
+ * is working but restores it before returning.
+ * safe_create_leading_directories_const() doesn't modify path, even
+ * temporarily. Both these variants adjust the permissions of the
+ * created directories to honor core.sharedRepository, so they are best
+ * suited for files inside the git dir. For working tree files, use
+ * safe_create_leading_directories_no_share() instead, as it ignores
+ * the core.sharedRepository setting.
+ */
+enum scld_error {
+       SCLD_OK = 0,
+       SCLD_FAILED = -1,
+       SCLD_PERMS = -2,
+       SCLD_EXISTS = -3,
+       SCLD_VANISHED = -4
+};
+enum scld_error safe_create_leading_directories(char *path);
+enum scld_error safe_create_leading_directories_const(const char *path);
+enum scld_error safe_create_leading_directories_no_share(char *path);
+
+int mkdir_in_gitdir(const char *path);
+
+int git_open_cloexec(const char *name, int flags);
+#define git_open(name) git_open_cloexec(name, O_RDONLY)
+
+/**
+ * unpack_loose_header() initializes the data stream needed to unpack
+ * a loose object header.
+ *
+ * Returns:
+ *
+ * - ULHR_OK on success
+ * - ULHR_BAD on error
+ * - ULHR_TOO_LONG if the header was too long
+ *
+ * It will only parse up to MAX_HEADER_LEN bytes unless an optional
+ * "hdrbuf" argument is non-NULL. This is intended for use with
+ * OBJECT_INFO_ALLOW_UNKNOWN_TYPE to extract the bad type for (error)
+ * reporting. The full header will be extracted to "hdrbuf" for use
+ * with parse_loose_header(), ULHR_TOO_LONG will still be returned
+ * from this function to indicate that the header was too long.
+ */
+enum unpack_loose_header_result {
+       ULHR_OK,
+       ULHR_BAD,
+       ULHR_TOO_LONG,
+};
+enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
+                                                   unsigned char *map,
+                                                   unsigned long mapsize,
+                                                   void *buffer,
+                                                   unsigned long bufsiz,
+                                                   struct strbuf *hdrbuf);
+
+/**
+ * parse_loose_header() parses the starting "<type> <len>\0" of an
+ * object. If it doesn't follow that format -1 is returned. To check
+ * the validity of the <type> populate the "typep" in the "struct
+ * object_info". It will be OBJ_BAD if the object type is unknown. The
+ * parsed <len> can be retrieved via "oi->sizep", and from there
+ * passed to unpack_loose_rest().
+ */
+struct object_info;
+int parse_loose_header(const char *hdr, struct object_info *oi);
+
+/**
+ * With in-core object data in "buf", rehash it to make sure the
+ * object name actually matches "oid" to detect object corruption.
+ *
+ * A negative value indicates an error, usually that the OID is not
+ * what we expected, but it might also indicate another error.
+ */
+int check_object_signature(struct repository *r, const struct object_id *oid,
+                          void *map, unsigned long size,
+                          enum object_type type);
+
+/**
+ * A streaming version of check_object_signature().
+ * Try reading the object named with "oid" using
+ * the streaming interface and rehash it to do the same.
+ */
+int stream_object_signature(struct repository *r, const struct object_id *oid);
+
+int finalize_object_file(const char *tmpfile, const char *filename);
+
+/* Helper to check and "touch" a file */
+int check_and_freshen_file(const char *fn, int freshen);
+
+void *read_object_with_reference(struct repository *r,
+                                const struct object_id *oid,
+                                enum object_type required_type,
+                                unsigned long *size,
+                                struct object_id *oid_ret);
+
+#endif /* OBJECT_FILE_H */
index 2dd1a0f56e1e442dec47dfbbcdd46d58aecc812c..3a2ef5d6800173fa669bdfcb2612bf21a7c6417a 100644 (file)
@@ -1,18 +1,25 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "object-name.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tag.h"
 #include "commit.h"
 #include "tree.h"
-#include "blob.h"
 #include "tree-walk.h"
 #include "refs.h"
 #include "remote.h"
 #include "dir.h"
 #include "oid-array.h"
+#include "oidtree.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "pretty.h"
+#include "object-store-ll.h"
+#include "read-cache-ll.h"
 #include "repository.h"
-#include "submodule.h"
+#include "setup.h"
 #include "midx.h"
 #include "commit-reach.h"
 #include "date.h"
@@ -223,7 +230,7 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
 
 static int disambiguate_commit_only(struct repository *r,
                                    const struct object_id *oid,
-                                   void *cb_data_unused)
+                                   void *cb_data UNUSED)
 {
        int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_COMMIT;
@@ -231,7 +238,7 @@ static int disambiguate_commit_only(struct repository *r,
 
 static int disambiguate_committish_only(struct repository *r,
                                        const struct object_id *oid,
-                                       void *cb_data_unused)
+                                       void *cb_data UNUSED)
 {
        struct object *obj;
        int kind;
@@ -251,7 +258,7 @@ static int disambiguate_committish_only(struct repository *r,
 
 static int disambiguate_tree_only(struct repository *r,
                                  const struct object_id *oid,
-                                 void *cb_data_unused)
+                                 void *cb_data UNUSED)
 {
        int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_TREE;
@@ -259,7 +266,7 @@ static int disambiguate_tree_only(struct repository *r,
 
 static int disambiguate_treeish_only(struct repository *r,
                                     const struct object_id *oid,
-                                    void *cb_data_unused)
+                                    void *cb_data UNUSED)
 {
        struct object *obj;
        int kind;
@@ -279,7 +286,7 @@ static int disambiguate_treeish_only(struct repository *r,
 
 static int disambiguate_blob_only(struct repository *r,
                                  const struct object_id *oid,
-                                 void *cb_data_unused)
+                                 void *cb_data UNUSED)
 {
        int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_BLOB;
@@ -394,8 +401,10 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)
                if (commit) {
                        struct pretty_print_context pp = {0};
                        pp.date_mode.type = DATE_SHORT;
-                       format_commit_message(commit, "%ad", &date, &pp);
-                       format_commit_message(commit, "%s", &msg, &pp);
+                       repo_format_commit_message(the_repository, commit,
+                                                  "%ad", &date, &pp);
+                       repo_format_commit_message(the_repository, commit,
+                                                  "%s", &msg, &pp);
                }
 
                /*
@@ -473,7 +482,7 @@ static int collect_ambiguous(const struct object_id *oid, void *data)
        return 0;
 }
 
-static int repo_collect_ambiguous(struct repository *r,
+static int repo_collect_ambiguous(struct repository *r UNUSED,
                                  const struct object_id *oid,
                                  void *data)
 {
@@ -665,7 +674,7 @@ static int extend_abbrev_len(const struct object_id *oid, void *cb_data)
        return 0;
 }
 
-static int repo_extend_abbrev_len(struct repository *r,
+static int repo_extend_abbrev_len(struct repository *r UNUSED,
                                  const struct object_id *oid,
                                  void *cb_data)
 {
@@ -758,6 +767,21 @@ static void find_abbrev_len_packed(struct min_abbrev_data *mad)
                find_abbrev_len_for_pack(p, mad);
 }
 
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+                                  const struct object_id *oid, int abbrev_len)
+{
+       int r;
+       strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
+       r = repo_find_unique_abbrev_r(repo, sb->buf + sb->len, oid, abbrev_len);
+       strbuf_setlen(sb, sb->len + r);
+}
+
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
+                             int abbrev_len)
+{
+       strbuf_repo_add_unique_abbrev(sb, the_repository, oid, abbrev_len);
+}
+
 int repo_find_unique_abbrev_r(struct repository *r, char *hex,
                              const struct object_id *oid, int len)
 {
@@ -898,6 +922,7 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
        char *real_ref = NULL;
        int refs_found = 0;
        int at, reflog_len, nth_prior = 0;
+       int fatal = !(flags & GET_OID_QUIETLY);
 
        if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
                if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
@@ -952,11 +977,11 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
 
        if (!len && reflog_len)
                /* allow "@{...}" to mean the current branch reflog */
-               refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref, 0);
+               refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref, !fatal);
        else if (reflog_len)
                refs_found = repo_dwim_log(r, str, len, oid, &real_ref);
        else
-               refs_found = repo_dwim_ref(r, str, len, oid, &real_ref, 0);
+               refs_found = repo_dwim_ref(r, str, len, oid, &real_ref, !fatal);
 
        if (!refs_found)
                return -1;
@@ -1036,7 +1061,7 @@ static enum get_oid_result get_parent(struct repository *r,
        if (ret)
                return ret;
        commit = lookup_commit_reference(r, &oid);
-       if (parse_commit(commit))
+       if (repo_parse_commit(r, commit))
                return MISSING_OBJECT;
        if (!idx) {
                oidcpy(result, &commit->object.oid);
@@ -1070,7 +1095,7 @@ static enum get_oid_result get_nth_ancestor(struct repository *r,
                return MISSING_OBJECT;
 
        while (generation--) {
-               if (parse_commit(commit) || !commit->parents)
+               if (repo_parse_commit(r, commit) || !commit->parents)
                        return MISSING_OBJECT;
                commit = commit->parents->item;
        }
@@ -1361,10 +1386,10 @@ static int get_oid_oneline(struct repository *r,
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
                if (!parse_object(r, &commit->object.oid))
                        continue;
-               buf = get_commit_buffer(commit, NULL);
+               buf = repo_get_commit_buffer(r, commit, NULL);
                p = strstr(buf, "\n\n");
                matches = negative ^ (p && !regexec(&regex, p + 2, 0, NULL, 0));
-               unuse_commit_buffer(commit, buf);
+               repo_unuse_commit_buffer(r, commit, buf);
 
                if (matches) {
                        oidcpy(oid, &commit->object.oid);
@@ -1666,7 +1691,8 @@ void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed)
        struct interpret_branch_name_options options = {
                .allowed = allowed
        };
-       int used = interpret_branch_name(name, len, sb, &options);
+       int used = repo_interpret_branch_name(the_repository, name, len, sb,
+                                             &options);
 
        if (used < 0)
                used = 0;
@@ -1719,7 +1745,7 @@ int get_oidf(struct object_id *oid, const char *fmt, ...)
        strbuf_vaddf(&sb, fmt, ap);
        va_end(ap);
 
-       ret = get_oid(sb.buf, oid);
+       ret = repo_get_oid(the_repository, sb.buf, oid);
        strbuf_release(&sb);
 
        return ret;
diff --git a/object-name.h b/object-name.h
new file mode 100644 (file)
index 0000000..9ae5223
--- /dev/null
@@ -0,0 +1,130 @@
+#ifndef OBJECT_NAME_H
+#define OBJECT_NAME_H
+
+#include "object.h"
+#include "strbuf.h"
+
+struct object_id;
+struct repository;
+
+struct object_context {
+       unsigned short mode;
+       /*
+        * symlink_path is only used by get_tree_entry_follow_symlinks,
+        * and only for symlinks that point outside the repository.
+        */
+       struct strbuf symlink_path;
+       /*
+        * If GET_OID_RECORD_PATH is set, this will record path (if any)
+        * found when resolving the name. The caller is responsible for
+        * releasing the memory.
+        */
+       char *path;
+};
+
+/*
+ * Return an abbreviated sha1 unique within this repository's object database.
+ * The result will be at least `len` characters long, and will be NUL
+ * terminated.
+ *
+ * The non-`_r` version returns a static buffer which remains valid until 4
+ * more calls to repo_find_unique_abbrev are made.
+ *
+ * The `_r` variant writes to a buffer supplied by the caller, which must be at
+ * least `GIT_MAX_HEXSZ + 1` bytes. The return value is the number of bytes
+ * written (excluding the NUL terminator).
+ *
+ * Note that while this version avoids the static buffer, it is not fully
+ * reentrant, as it calls into other non-reentrant git code.
+ */
+const char *repo_find_unique_abbrev(struct repository *r, const struct object_id *oid, int len);
+int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len);
+
+/**
+ * Add the abbreviation, as generated by repo_find_unique_abbrev(), of `sha1` to
+ * the strbuf `sb`.
+ */
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+                                  const struct object_id *oid, int abbrev_len);
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
+                             int abbrev_len);
+
+int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
+__attribute__((format (printf, 2, 3)))
+int get_oidf(struct object_id *oid, const char *fmt, ...);
+int repo_get_oid_commit(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_committish(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_tree(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_treeish(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_blob(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_mb(struct repository *r, const char *str, struct object_id *oid);
+void maybe_die_on_misspelt_object_name(struct repository *repo,
+                                      const char *name,
+                                      const char *prefix);
+enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
+                                        unsigned flags, struct object_id *oid,
+                                        struct object_context *oc);
+
+
+typedef int each_abbrev_fn(const struct object_id *oid, void *);
+int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *);
+
+int set_disambiguate_hint_config(const char *var, const char *value);
+
+/*
+ * This reads short-hand syntax that not only evaluates to a commit
+ * object name, but also can act as if the end user spelled the name
+ * of the branch from the command line.
+ *
+ * - "@{-N}" finds the name of the Nth previous branch we were on, and
+ *   places the name of the branch in the given buf and returns the
+ *   number of characters parsed if successful.
+ *
+ * - "<branch>@{upstream}" finds the name of the other ref that
+ *   <branch> is configured to merge with (missing <branch> defaults
+ *   to the current branch), and places the name of the branch in the
+ *   given buf and returns the number of characters parsed if
+ *   successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ */
+#define INTERPRET_BRANCH_LOCAL (1<<0)
+#define INTERPRET_BRANCH_REMOTE (1<<1)
+#define INTERPRET_BRANCH_HEAD (1<<2)
+struct interpret_branch_name_options {
+       /*
+        * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+        * expansions: local branches ("refs/heads/"), remote branches
+        * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+        * allowed, even ones to refs outside of those namespaces.
+        */
+       unsigned allowed;
+
+       /*
+        * If ^{upstream} or ^{push} (or equivalent) is requested, and the
+        * branch in question does not have such a reference, return -1 instead
+        * of die()-ing.
+        */
+       unsigned nonfatal_dangling_mark : 1;
+};
+int repo_interpret_branch_name(struct repository *r,
+                              const char *str, int len,
+                              struct strbuf *buf,
+                              const struct interpret_branch_name_options *options);
+
+struct object *repo_peel_to_type(struct repository *r,
+                                const char *name, int namelen,
+                                struct object *o, enum object_type);
+
+/* Convert to/from hex/sha1 representation */
+#define MINIMUM_ABBREV minimum_abbrev
+#define DEFAULT_ABBREV default_abbrev
+
+/* used when the code does not know or care what the default abbrev is */
+#define FALLBACK_DEFAULT_ABBREV 7
+
+#endif /* OBJECT_NAME_H */
diff --git a/object-store-ll.h b/object-store-ll.h
new file mode 100644 (file)
index 0000000..26a3895
--- /dev/null
@@ -0,0 +1,533 @@
+#ifndef OBJECT_STORE_LL_H
+#define OBJECT_STORE_LL_H
+
+#include "hashmap.h"
+#include "object.h"
+#include "list.h"
+#include "thread-utils.h"
+#include "oidset.h"
+
+struct oidmap;
+struct oidtree;
+struct strbuf;
+
+struct object_directory {
+       struct object_directory *next;
+
+       /*
+        * Used to store the results of readdir(3) calls when we are OK
+        * sacrificing accuracy due to races for speed. That includes
+        * object existence with OBJECT_INFO_QUICK, as well as
+        * our search for unique abbreviated hashes. Don't use it for tasks
+        * requiring greater accuracy!
+        *
+        * Be sure to call odb_load_loose_cache() before using.
+        */
+       uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
+       struct oidtree *loose_objects_cache;
+
+       /*
+        * This is a temporary object store created by the tmp_objdir
+        * facility. Disable ref updates since the objects in the store
+        * might be discarded on rollback.
+        */
+       int disable_ref_updates;
+
+       /*
+        * This object store is ephemeral, so there is no need to fsync.
+        */
+       int will_destroy;
+
+       /*
+        * Path to the alternative object store. If this is a relative path,
+        * it is relative to the current working directory.
+        */
+       char *path;
+};
+
+struct input_stream {
+       const void *(*read)(struct input_stream *, unsigned long *len);
+       void *data;
+       int is_finished;
+};
+
+void prepare_alt_odb(struct repository *r);
+int has_alt_odb(struct repository *r);
+char *compute_alternate_path(const char *path, struct strbuf *err);
+struct object_directory *find_odb(struct repository *r, const char *obj_dir);
+typedef int alt_odb_fn(struct object_directory *, void *);
+int foreach_alt_odb(alt_odb_fn, void*);
+typedef void alternate_ref_fn(const struct object_id *oid, void *);
+void for_each_alternate_ref(alternate_ref_fn, void *);
+
+/*
+ * Add the directory to the on-disk alternates file; the new entry will also
+ * take effect in the current process.
+ */
+void add_to_alternates_file(const char *dir);
+
+/*
+ * Add the directory to the in-memory list of alternates (along with any
+ * recursive alternates it points to), but do not modify the on-disk alternates
+ * file.
+ */
+void add_to_alternates_memory(const char *dir);
+
+/*
+ * Replace the current writable object directory with the specified temporary
+ * object directory; returns the former primary object directory.
+ */
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
+
+/*
+ * Restore a previous ODB replaced by set_temporary_main_odb.
+ */
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
+
+/*
+ * Populate and return the loose object cache array corresponding to the
+ * given object ID.
+ */
+struct oidtree *odb_loose_cache(struct object_directory *odb,
+                                 const struct object_id *oid);
+
+/* Empty the loose object cache for the specified object directory. */
+void odb_clear_loose_cache(struct object_directory *odb);
+
+/* Clear and free the specified object directory */
+void free_object_directory(struct object_directory *odb);
+
+struct packed_git {
+       struct hashmap_entry packmap_ent;
+       struct packed_git *next;
+       struct list_head mru;
+       struct pack_window *windows;
+       off_t pack_size;
+       const void *index_data;
+       size_t index_size;
+       uint32_t num_objects;
+       size_t crc_offset;
+       struct oidset bad_objects;
+       int index_version;
+       time_t mtime;
+       int pack_fd;
+       int index;              /* for builtin/pack-objects.c */
+       unsigned pack_local:1,
+                pack_keep:1,
+                pack_keep_in_core:1,
+                freshened:1,
+                do_not_close:1,
+                pack_promisor:1,
+                multi_pack_index:1,
+                is_cruft:1;
+       unsigned char hash[GIT_MAX_RAWSZ];
+       struct revindex_entry *revindex;
+       const uint32_t *revindex_data;
+       const uint32_t *revindex_map;
+       size_t revindex_size;
+       /*
+        * mtimes_map points at the beginning of the memory mapped region of
+        * this pack's corresponding .mtimes file, and mtimes_size is the size
+        * of that .mtimes file
+        */
+       const uint32_t *mtimes_map;
+       size_t mtimes_size;
+       /* something like ".git/objects/pack/xxxxx.pack" */
+       char pack_name[FLEX_ARRAY]; /* more */
+};
+
+struct multi_pack_index;
+
+static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,
+                                    const struct hashmap_entry *entry,
+                                    const struct hashmap_entry *entry2,
+                                    const void *keydata)
+{
+       const char *key = keydata;
+       const struct packed_git *pg1, *pg2;
+
+       pg1 = container_of(entry, const struct packed_git, packmap_ent);
+       pg2 = container_of(entry2, const struct packed_git, packmap_ent);
+
+       return strcmp(pg1->pack_name, key ? key : pg2->pack_name);
+}
+
+struct raw_object_store {
+       /*
+        * Set of all object directories; the main directory is first (and
+        * cannot be NULL after initialization). Subsequent directories are
+        * alternates.
+        */
+       struct object_directory *odb;
+       struct object_directory **odb_tail;
+       struct kh_odb_path_map *odb_by_path;
+
+       int loaded_alternates;
+
+       /*
+        * A list of alternate object directories loaded from the environment;
+        * this should not generally need to be accessed directly, but will
+        * populate the "odb" list when prepare_alt_odb() is run.
+        */
+       char *alternate_db;
+
+       /*
+        * Objects that should be substituted by other objects
+        * (see git-replace(1)).
+        */
+       struct oidmap *replace_map;
+       unsigned replace_map_initialized : 1;
+       pthread_mutex_t replace_mutex; /* protect object replace functions */
+
+       struct commit_graph *commit_graph;
+       unsigned commit_graph_attempted : 1; /* if loading has been attempted */
+
+       /*
+        * private data
+        *
+        * should only be accessed directly by packfile.c and midx.c
+        */
+       struct multi_pack_index *multi_pack_index;
+
+       /*
+        * private data
+        *
+        * should only be accessed directly by packfile.c
+        */
+
+       struct packed_git *packed_git;
+       /* A most-recently-used ordered version of the packed_git list. */
+       struct list_head packed_git_mru;
+
+       struct {
+               struct packed_git **packs;
+               unsigned flags;
+       } kept_pack_cache;
+
+       /*
+        * A map of packfiles to packed_git structs for tracking which
+        * packs have been loaded already.
+        */
+       struct hashmap pack_map;
+
+       /*
+        * A fast, rough count of the number of objects in the repository.
+        * These two fields are not meant for direct access. Use
+        * repo_approximate_object_count() instead.
+        */
+       unsigned long approximate_object_count;
+       unsigned approximate_object_count_valid : 1;
+
+       /*
+        * Whether packed_git has already been populated with this repository's
+        * packs.
+        */
+       unsigned packed_git_initialized : 1;
+};
+
+struct raw_object_store *raw_object_store_new(void);
+void raw_object_store_clear(struct raw_object_store *o);
+
+/*
+ * Put in `buf` the name of the file in the local object database that
+ * would be used to store a loose object with the specified oid.
+ */
+const char *loose_object_path(struct repository *r, struct strbuf *buf,
+                             const struct object_id *oid);
+
+void *map_loose_object(struct repository *r, const struct object_id *oid,
+                      unsigned long *size);
+
+void *repo_read_object_file(struct repository *r,
+                           const struct object_id *oid,
+                           enum object_type *type,
+                           unsigned long *size);
+
+/* Read and unpack an object file into memory, write memory to an object file */
+int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
+
+void hash_object_file(const struct git_hash_algo *algo, const void *buf,
+                     unsigned long len, enum object_type type,
+                     struct object_id *oid);
+
+int write_object_file_flags(const void *buf, unsigned long len,
+                           enum object_type type, struct object_id *oid,
+                           unsigned flags);
+static inline int write_object_file(const void *buf, unsigned long len,
+                                   enum object_type type, struct object_id *oid)
+{
+       return write_object_file_flags(buf, len, type, oid, 0);
+}
+
+int write_object_file_literally(const void *buf, unsigned long len,
+                               const char *type, struct object_id *oid,
+                               unsigned flags);
+int stream_loose_object(struct input_stream *in_stream, size_t len,
+                       struct object_id *oid);
+
+/*
+ * Add an object file to the in-memory object store, without writing it
+ * to disk.
+ *
+ * Callers are responsible for calling write_object_file to record the
+ * object in persistent storage before writing any other new objects
+ * that reference it.
+ */
+int pretend_object_file(void *, unsigned long, enum object_type,
+                       struct object_id *oid);
+
+int force_object_loose(const struct object_id *oid, time_t mtime);
+
+struct object_info {
+       /* Request */
+       enum object_type *typep;
+       unsigned long *sizep;
+       off_t *disk_sizep;
+       struct object_id *delta_base_oid;
+       struct strbuf *type_name;
+       void **contentp;
+
+       /* Response */
+       enum {
+               OI_CACHED,
+               OI_LOOSE,
+               OI_PACKED,
+               OI_DBCACHED
+       } whence;
+       union {
+               /*
+                * struct {
+                *      ... Nothing to expose in this case
+                * } cached;
+                * struct {
+                *      ... Nothing to expose in this case
+                * } loose;
+                */
+               struct {
+                       struct packed_git *pack;
+                       off_t offset;
+                       unsigned int is_delta;
+               } packed;
+       } u;
+};
+
+/*
+ * Initializer for a "struct object_info" that wants no items. You may
+ * also memset() the memory to all-zeroes.
+ */
+#define OBJECT_INFO_INIT { 0 }
+
+/* Invoke lookup_replace_object() on the given hash */
+#define OBJECT_INFO_LOOKUP_REPLACE 1
+/* Allow reading from a loose object file of unknown/bogus type */
+#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
+/* Do not retry packed storage after checking packed and loose storage */
+#define OBJECT_INFO_QUICK 8
+/*
+ * Do not attempt to fetch the object if missing (even if fetch_is_missing is
+ * nonzero).
+ */
+#define OBJECT_INFO_SKIP_FETCH_OBJECT 16
+/*
+ * This is meant for bulk prefetching of missing blobs in a partial
+ * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
+ */
+#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
+
+/* Die if object corruption (not just an object being missing) was detected. */
+#define OBJECT_INFO_DIE_IF_CORRUPT 32
+
+int oid_object_info_extended(struct repository *r,
+                            const struct object_id *,
+                            struct object_info *, unsigned flags);
+
+/*
+ * Open the loose object at path, check its hash, and return the contents,
+ * use the "oi" argument to assert things about the object, or e.g. populate its
+ * type, and size. If the object is a blob, then "contents" may return NULL,
+ * to allow streaming of large blobs.
+ *
+ * Returns 0 on success, negative on error (details may be written to stderr).
+ */
+int read_loose_object(const char *path,
+                     const struct object_id *expected_oid,
+                     struct object_id *real_oid,
+                     void **contents,
+                     struct object_info *oi);
+
+/* Retry packed storage after checking packed and loose storage */
+#define HAS_OBJECT_RECHECK_PACKED 1
+
+/*
+ * Returns 1 if the object exists. This function will not lazily fetch objects
+ * in a partial clone.
+ */
+int has_object(struct repository *r, const struct object_id *oid,
+              unsigned flags);
+
+/*
+ * These macros and functions are deprecated. If checking existence for an
+ * object that is likely to be missing and/or whose absence is relatively
+ * inconsequential (or is consequential but the caller is prepared to handle
+ * it), use has_object(), which has better defaults (no lazy fetch in a partial
+ * clone and no rechecking of packed storage). In the unlikely event that a
+ * caller needs to assert existence of an object that it fully expects to
+ * exist, and wants to trigger a lazy fetch in a partial clone, use
+ * oid_object_info_extended() with a NULL struct object_info.
+ *
+ * These functions can be removed once all callers have migrated to
+ * has_object() and/or oid_object_info_extended().
+ */
+int repo_has_object_file(struct repository *r, const struct object_id *oid);
+int repo_has_object_file_with_flags(struct repository *r,
+                                   const struct object_id *oid, int flags);
+
+/*
+ * Return true iff an alternate object database has a loose object
+ * with the specified name.  This function does not respect replace
+ * references.
+ */
+int has_loose_object_nonlocal(const struct object_id *);
+
+int has_loose_object(const struct object_id *);
+
+/**
+ * format_object_header() is a thin wrapper around s xsnprintf() that
+ * writes the initial "<type> <obj-len>" part of the loose object
+ * header. It returns the size that snprintf() returns + 1.
+ */
+int format_object_header(char *str, size_t size, enum object_type type,
+                        size_t objsize);
+
+void assert_oid_type(const struct object_id *oid, enum object_type expect);
+
+/*
+ * Enabling the object read lock allows multiple threads to safely call the
+ * following functions in parallel: repo_read_object_file(),
+ * read_object_with_reference(), oid_object_info() and oid_object_info_extended().
+ *
+ * obj_read_lock() and obj_read_unlock() may also be used to protect other
+ * section which cannot execute in parallel with object reading. Since the used
+ * lock is a recursive mutex, these sections can even contain calls to object
+ * reading functions. However, beware that in these cases zlib inflation won't
+ * be performed in parallel, losing performance.
+ *
+ * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If
+ * any of its callees end up calling it, this recursive call won't benefit from
+ * parallel inflation.
+ */
+void enable_obj_read_lock(void);
+void disable_obj_read_lock(void);
+
+extern int obj_read_use_lock;
+extern pthread_mutex_t obj_read_mutex;
+
+static inline void obj_read_lock(void)
+{
+       if(obj_read_use_lock)
+               pthread_mutex_lock(&obj_read_mutex);
+}
+
+static inline void obj_read_unlock(void)
+{
+       if(obj_read_use_lock)
+               pthread_mutex_unlock(&obj_read_mutex);
+}
+
+/*
+ * Iterate over the files in the loose-object parts of the object
+ * directory "path", triggering the following callbacks:
+ *
+ *  - loose_object is called for each loose object we find.
+ *
+ *  - loose_cruft is called for any files that do not appear to be
+ *    loose objects. Note that we only look in the loose object
+ *    directories "objects/[0-9a-f]{2}/", so we will not report
+ *    "objects/foobar" as cruft.
+ *
+ *  - loose_subdir is called for each top-level hashed subdirectory
+ *    of the object directory (e.g., "$OBJDIR/f0"). It is called
+ *    after the objects in the directory are processed.
+ *
+ * Any callback that is NULL will be ignored. Callbacks returning non-zero
+ * will end the iteration.
+ *
+ * In the "buf" variant, "path" is a strbuf which will also be used as a
+ * scratch buffer, but restored to its original contents before
+ * the function returns.
+ */
+typedef int each_loose_object_fn(const struct object_id *oid,
+                                const char *path,
+                                void *data);
+typedef int each_loose_cruft_fn(const char *basename,
+                               const char *path,
+                               void *data);
+typedef int each_loose_subdir_fn(unsigned int nr,
+                                const char *path,
+                                void *data);
+int for_each_file_in_obj_subdir(unsigned int subdir_nr,
+                               struct strbuf *path,
+                               each_loose_object_fn obj_cb,
+                               each_loose_cruft_fn cruft_cb,
+                               each_loose_subdir_fn subdir_cb,
+                               void *data);
+int for_each_loose_file_in_objdir(const char *path,
+                                 each_loose_object_fn obj_cb,
+                                 each_loose_cruft_fn cruft_cb,
+                                 each_loose_subdir_fn subdir_cb,
+                                 void *data);
+int for_each_loose_file_in_objdir_buf(struct strbuf *path,
+                                     each_loose_object_fn obj_cb,
+                                     each_loose_cruft_fn cruft_cb,
+                                     each_loose_subdir_fn subdir_cb,
+                                     void *data);
+
+/* Flags for for_each_*_object() below. */
+enum for_each_object_flags {
+       /* Iterate only over local objects, not alternates. */
+       FOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),
+
+       /* Only iterate over packs obtained from the promisor remote. */
+       FOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),
+
+       /*
+        * Visit objects within a pack in packfile order rather than .idx order
+        */
+       FOR_EACH_OBJECT_PACK_ORDER = (1<<2),
+
+       /* Only iterate over packs that are not marked as kept in-core. */
+       FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),
+
+       /* Only iterate over packs that do not have .keep files. */
+       FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),
+};
+
+/*
+ * Iterate over all accessible loose objects without respect to
+ * reachability. By default, this includes both local and alternate objects.
+ * The order in which objects are visited is unspecified.
+ *
+ * Any flags specific to packs are ignored.
+ */
+int for_each_loose_object(each_loose_object_fn, void *,
+                         enum for_each_object_flags flags);
+
+/*
+ * Iterate over all accessible packed objects without respect to reachability.
+ * By default, this includes both local and alternate packs.
+ *
+ * Note that some objects may appear twice if they are found in multiple packs.
+ * Each pack is visited in an unspecified order. By default, objects within a
+ * pack are visited in pack-idx order (i.e., sorted by oid).
+ */
+typedef int each_packed_object_fn(const struct object_id *oid,
+                                 struct packed_git *pack,
+                                 uint32_t pos,
+                                 void *data);
+int for_each_object_in_pack(struct packed_git *p,
+                           each_packed_object_fn, void *data,
+                           enum for_each_object_flags flags);
+int for_each_packed_object(each_packed_object_fn, void *,
+                          enum for_each_object_flags flags);
+
+#endif /* OBJECT_STORE_LL_H */
index 1be57abaf10d7aa527df7f22ebaf660f6430d4d8..1b3e3d7d0145be18b523ea836aaac3e84c06af4e 100644 (file)
 #ifndef OBJECT_STORE_H
 #define OBJECT_STORE_H
 
-#include "cache.h"
-#include "oidmap.h"
-#include "list.h"
-#include "oid-array.h"
-#include "strbuf.h"
-#include "thread-utils.h"
 #include "khash.h"
 #include "dir.h"
-#include "oidtree.h"
-#include "oidset.h"
-
-struct object_directory {
-       struct object_directory *next;
-
-       /*
-        * Used to store the results of readdir(3) calls when we are OK
-        * sacrificing accuracy due to races for speed. That includes
-        * object existence with OBJECT_INFO_QUICK, as well as
-        * our search for unique abbreviated hashes. Don't use it for tasks
-        * requiring greater accuracy!
-        *
-        * Be sure to call odb_load_loose_cache() before using.
-        */
-       uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
-       struct oidtree *loose_objects_cache;
-
-       /*
-        * This is a temporary object store created by the tmp_objdir
-        * facility. Disable ref updates since the objects in the store
-        * might be discarded on rollback.
-        */
-       int disable_ref_updates;
-
-       /*
-        * This object store is ephemeral, so there is no need to fsync.
-        */
-       int will_destroy;
-
-       /*
-        * Path to the alternative object store. If this is a relative path,
-        * it is relative to the current working directory.
-        */
-       char *path;
-};
-
-struct input_stream {
-       const void *(*read)(struct input_stream *, unsigned long *len);
-       void *data;
-       int is_finished;
-};
+#include "object-store-ll.h"
 
 KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
        struct object_directory *, 1, fspathhash, fspatheq)
 
-void prepare_alt_odb(struct repository *r);
-char *compute_alternate_path(const char *path, struct strbuf *err);
-struct object_directory *find_odb(struct repository *r, const char *obj_dir);
-typedef int alt_odb_fn(struct object_directory *, void *);
-int foreach_alt_odb(alt_odb_fn, void*);
-typedef void alternate_ref_fn(const struct object_id *oid, void *);
-void for_each_alternate_ref(alternate_ref_fn, void *);
-
-/*
- * Add the directory to the on-disk alternates file; the new entry will also
- * take effect in the current process.
- */
-void add_to_alternates_file(const char *dir);
-
-/*
- * Add the directory to the in-memory list of alternates (along with any
- * recursive alternates it points to), but do not modify the on-disk alternates
- * file.
- */
-void add_to_alternates_memory(const char *dir);
-
-/*
- * Replace the current writable object directory with the specified temporary
- * object directory; returns the former primary object directory.
- */
-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
-
-/*
- * Restore a previous ODB replaced by set_temporary_main_odb.
- */
-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
-
-/*
- * Populate and return the loose object cache array corresponding to the
- * given object ID.
- */
-struct oidtree *odb_loose_cache(struct object_directory *odb,
-                                 const struct object_id *oid);
-
-/* Empty the loose object cache for the specified object directory. */
-void odb_clear_loose_cache(struct object_directory *odb);
-
-/* Clear and free the specified object directory */
-void free_object_directory(struct object_directory *odb);
-
-struct packed_git {
-       struct hashmap_entry packmap_ent;
-       struct packed_git *next;
-       struct list_head mru;
-       struct pack_window *windows;
-       off_t pack_size;
-       const void *index_data;
-       size_t index_size;
-       uint32_t num_objects;
-       uint32_t crc_offset;
-       struct oidset bad_objects;
-       int index_version;
-       time_t mtime;
-       int pack_fd;
-       int index;              /* for builtin/pack-objects.c */
-       unsigned pack_local:1,
-                pack_keep:1,
-                pack_keep_in_core:1,
-                freshened:1,
-                do_not_close:1,
-                pack_promisor:1,
-                multi_pack_index:1,
-                is_cruft:1;
-       unsigned char hash[GIT_MAX_RAWSZ];
-       struct revindex_entry *revindex;
-       const uint32_t *revindex_data;
-       const uint32_t *revindex_map;
-       size_t revindex_size;
-       /*
-        * mtimes_map points at the beginning of the memory mapped region of
-        * this pack's corresponding .mtimes file, and mtimes_size is the size
-        * of that .mtimes file
-        */
-       const uint32_t *mtimes_map;
-       size_t mtimes_size;
-       /* something like ".git/objects/pack/xxxxx.pack" */
-       char pack_name[FLEX_ARRAY]; /* more */
-};
-
-struct multi_pack_index;
-
-static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,
-                                    const struct hashmap_entry *entry,
-                                    const struct hashmap_entry *entry2,
-                                    const void *keydata)
-{
-       const char *key = keydata;
-       const struct packed_git *pg1, *pg2;
-
-       pg1 = container_of(entry, const struct packed_git, packmap_ent);
-       pg2 = container_of(entry2, const struct packed_git, packmap_ent);
-
-       return strcmp(pg1->pack_name, key ? key : pg2->pack_name);
-}
-
-struct raw_object_store {
-       /*
-        * Set of all object directories; the main directory is first (and
-        * cannot be NULL after initialization). Subsequent directories are
-        * alternates.
-        */
-       struct object_directory *odb;
-       struct object_directory **odb_tail;
-       kh_odb_path_map_t *odb_by_path;
-
-       int loaded_alternates;
-
-       /*
-        * A list of alternate object directories loaded from the environment;
-        * this should not generally need to be accessed directly, but will
-        * populate the "odb" list when prepare_alt_odb() is run.
-        */
-       char *alternate_db;
-
-       /*
-        * Objects that should be substituted by other objects
-        * (see git-replace(1)).
-        */
-       struct oidmap *replace_map;
-       unsigned replace_map_initialized : 1;
-       pthread_mutex_t replace_mutex; /* protect object replace functions */
-
-       struct commit_graph *commit_graph;
-       unsigned commit_graph_attempted : 1; /* if loading has been attempted */
-
-       /*
-        * private data
-        *
-        * should only be accessed directly by packfile.c and midx.c
-        */
-       struct multi_pack_index *multi_pack_index;
-
-       /*
-        * private data
-        *
-        * should only be accessed directly by packfile.c
-        */
-
-       struct packed_git *packed_git;
-       /* A most-recently-used ordered version of the packed_git list. */
-       struct list_head packed_git_mru;
-
-       struct {
-               struct packed_git **packs;
-               unsigned flags;
-       } kept_pack_cache;
-
-       /*
-        * A map of packfiles to packed_git structs for tracking which
-        * packs have been loaded already.
-        */
-       struct hashmap pack_map;
-
-       /*
-        * A fast, rough count of the number of objects in the repository.
-        * These two fields are not meant for direct access. Use
-        * approximate_object_count() instead.
-        */
-       unsigned long approximate_object_count;
-       unsigned approximate_object_count_valid : 1;
-
-       /*
-        * Whether packed_git has already been populated with this repository's
-        * packs.
-        */
-       unsigned packed_git_initialized : 1;
-};
-
-struct raw_object_store *raw_object_store_new(void);
-void raw_object_store_clear(struct raw_object_store *o);
-
-/*
- * Put in `buf` the name of the file in the local object database that
- * would be used to store a loose object with the specified oid.
- */
-const char *loose_object_path(struct repository *r, struct strbuf *buf,
-                             const struct object_id *oid);
-
-void *map_loose_object(struct repository *r, const struct object_id *oid,
-                      unsigned long *size);
-
-void *read_object_file_extended(struct repository *r,
-                               const struct object_id *oid,
-                               enum object_type *type,
-                               unsigned long *size, int lookup_replace);
-static inline void *repo_read_object_file(struct repository *r,
-                                         const struct object_id *oid,
-                                         enum object_type *type,
-                                         unsigned long *size)
-{
-       return read_object_file_extended(r, oid, type, size, 1);
-}
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define read_object_file(oid, type, size) repo_read_object_file(the_repository, oid, type, size)
-#endif
-
-/* Read and unpack an object file into memory, write memory to an object file */
-int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
-
-void hash_object_file(const struct git_hash_algo *algo, const void *buf,
-                     unsigned long len, enum object_type type,
-                     struct object_id *oid);
-
-int write_object_file_flags(const void *buf, unsigned long len,
-                           enum object_type type, struct object_id *oid,
-                           unsigned flags);
-static inline int write_object_file(const void *buf, unsigned long len,
-                                   enum object_type type, struct object_id *oid)
-{
-       return write_object_file_flags(buf, len, type, oid, 0);
-}
-
-int write_object_file_literally(const void *buf, unsigned long len,
-                               const char *type, struct object_id *oid,
-                               unsigned flags);
-int stream_loose_object(struct input_stream *in_stream, size_t len,
-                       struct object_id *oid);
-
-/*
- * Add an object file to the in-memory object store, without writing it
- * to disk.
- *
- * Callers are responsible for calling write_object_file to record the
- * object in persistent storage before writing any other new objects
- * that reference it.
- */
-int pretend_object_file(void *, unsigned long, enum object_type,
-                       struct object_id *oid);
-
-int force_object_loose(const struct object_id *oid, time_t mtime);
-
-/*
- * Open the loose object at path, check its hash, and return the contents,
- * use the "oi" argument to assert things about the object, or e.g. populate its
- * type, and size. If the object is a blob, then "contents" may return NULL,
- * to allow streaming of large blobs.
- *
- * Returns 0 on success, negative on error (details may be written to stderr).
- */
-int read_loose_object(const char *path,
-                     const struct object_id *expected_oid,
-                     struct object_id *real_oid,
-                     void **contents,
-                     struct object_info *oi);
-
-/* Retry packed storage after checking packed and loose storage */
-#define HAS_OBJECT_RECHECK_PACKED 1
-
-/*
- * Returns 1 if the object exists. This function will not lazily fetch objects
- * in a partial clone.
- */
-int has_object(struct repository *r, const struct object_id *oid,
-              unsigned flags);
-
-/*
- * These macros and functions are deprecated. If checking existence for an
- * object that is likely to be missing and/or whose absence is relatively
- * inconsequential (or is consequential but the caller is prepared to handle
- * it), use has_object(), which has better defaults (no lazy fetch in a partial
- * clone and no rechecking of packed storage). In the unlikely event that a
- * caller needs to assert existence of an object that it fully expects to
- * exist, and wants to trigger a lazy fetch in a partial clone, use
- * oid_object_info_extended() with a NULL struct object_info.
- *
- * These functions can be removed once all callers have migrated to
- * has_object() and/or oid_object_info_extended().
- */
-int repo_has_object_file(struct repository *r, const struct object_id *oid);
-int repo_has_object_file_with_flags(struct repository *r,
-                                   const struct object_id *oid, int flags);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define has_object_file(oid) repo_has_object_file(the_repository, oid)
-#define has_object_file_with_flags(oid, flags) repo_has_object_file_with_flags(the_repository, oid, flags)
-#endif
-
-/*
- * Return true iff an alternate object database has a loose object
- * with the specified name.  This function does not respect replace
- * references.
- */
-int has_loose_object_nonlocal(const struct object_id *);
-
-int has_loose_object(const struct object_id *);
-
-/**
- * format_object_header() is a thin wrapper around s xsnprintf() that
- * writes the initial "<type> <obj-len>" part of the loose object
- * header. It returns the size that snprintf() returns + 1.
- */
-int format_object_header(char *str, size_t size, enum object_type type,
-                        size_t objsize);
-
-void assert_oid_type(const struct object_id *oid, enum object_type expect);
-
-/*
- * Enabling the object read lock allows multiple threads to safely call the
- * following functions in parallel: repo_read_object_file(), read_object_file(),
- * read_object_file_extended(), read_object_with_reference(), read_object(),
- * oid_object_info() and oid_object_info_extended().
- *
- * obj_read_lock() and obj_read_unlock() may also be used to protect other
- * section which cannot execute in parallel with object reading. Since the used
- * lock is a recursive mutex, these sections can even contain calls to object
- * reading functions. However, beware that in these cases zlib inflation won't
- * be performed in parallel, losing performance.
- *
- * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If
- * any of its callees end up calling it, this recursive call won't benefit from
- * parallel inflation.
- */
-void enable_obj_read_lock(void);
-void disable_obj_read_lock(void);
-
-extern int obj_read_use_lock;
-extern pthread_mutex_t obj_read_mutex;
-
-static inline void obj_read_lock(void)
-{
-       if(obj_read_use_lock)
-               pthread_mutex_lock(&obj_read_mutex);
-}
-
-static inline void obj_read_unlock(void)
-{
-       if(obj_read_use_lock)
-               pthread_mutex_unlock(&obj_read_mutex);
-}
-
-struct object_info {
-       /* Request */
-       enum object_type *typep;
-       unsigned long *sizep;
-       off_t *disk_sizep;
-       struct object_id *delta_base_oid;
-       struct strbuf *type_name;
-       void **contentp;
-
-       /* Response */
-       enum {
-               OI_CACHED,
-               OI_LOOSE,
-               OI_PACKED,
-               OI_DBCACHED
-       } whence;
-       union {
-               /*
-                * struct {
-                *      ... Nothing to expose in this case
-                * } cached;
-                * struct {
-                *      ... Nothing to expose in this case
-                * } loose;
-                */
-               struct {
-                       struct packed_git *pack;
-                       off_t offset;
-                       unsigned int is_delta;
-               } packed;
-       } u;
-};
-
-/*
- * Initializer for a "struct object_info" that wants no items. You may
- * also memset() the memory to all-zeroes.
- */
-#define OBJECT_INFO_INIT { 0 }
-
-/* Invoke lookup_replace_object() on the given hash */
-#define OBJECT_INFO_LOOKUP_REPLACE 1
-/* Allow reading from a loose object file of unknown/bogus type */
-#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
-/* Do not retry packed storage after checking packed and loose storage */
-#define OBJECT_INFO_QUICK 8
-/* Do not check loose object */
-#define OBJECT_INFO_IGNORE_LOOSE 16
-/*
- * Do not attempt to fetch the object if missing (even if fetch_is_missing is
- * nonzero).
- */
-#define OBJECT_INFO_SKIP_FETCH_OBJECT 32
-/*
- * This is meant for bulk prefetching of missing blobs in a partial
- * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
- */
-#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
-
-int oid_object_info_extended(struct repository *r,
-                            const struct object_id *,
-                            struct object_info *, unsigned flags);
-
-/*
- * Iterate over the files in the loose-object parts of the object
- * directory "path", triggering the following callbacks:
- *
- *  - loose_object is called for each loose object we find.
- *
- *  - loose_cruft is called for any files that do not appear to be
- *    loose objects. Note that we only look in the loose object
- *    directories "objects/[0-9a-f]{2}/", so we will not report
- *    "objects/foobar" as cruft.
- *
- *  - loose_subdir is called for each top-level hashed subdirectory
- *    of the object directory (e.g., "$OBJDIR/f0"). It is called
- *    after the objects in the directory are processed.
- *
- * Any callback that is NULL will be ignored. Callbacks returning non-zero
- * will end the iteration.
- *
- * In the "buf" variant, "path" is a strbuf which will also be used as a
- * scratch buffer, but restored to its original contents before
- * the function returns.
- */
-typedef int each_loose_object_fn(const struct object_id *oid,
-                                const char *path,
-                                void *data);
-typedef int each_loose_cruft_fn(const char *basename,
-                               const char *path,
-                               void *data);
-typedef int each_loose_subdir_fn(unsigned int nr,
-                                const char *path,
-                                void *data);
-int for_each_file_in_obj_subdir(unsigned int subdir_nr,
-                               struct strbuf *path,
-                               each_loose_object_fn obj_cb,
-                               each_loose_cruft_fn cruft_cb,
-                               each_loose_subdir_fn subdir_cb,
-                               void *data);
-int for_each_loose_file_in_objdir(const char *path,
-                                 each_loose_object_fn obj_cb,
-                                 each_loose_cruft_fn cruft_cb,
-                                 each_loose_subdir_fn subdir_cb,
-                                 void *data);
-int for_each_loose_file_in_objdir_buf(struct strbuf *path,
-                                     each_loose_object_fn obj_cb,
-                                     each_loose_cruft_fn cruft_cb,
-                                     each_loose_subdir_fn subdir_cb,
-                                     void *data);
-
-/* Flags for for_each_*_object() below. */
-enum for_each_object_flags {
-       /* Iterate only over local objects, not alternates. */
-       FOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),
-
-       /* Only iterate over packs obtained from the promisor remote. */
-       FOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),
-
-       /*
-        * Visit objects within a pack in packfile order rather than .idx order
-        */
-       FOR_EACH_OBJECT_PACK_ORDER = (1<<2),
-
-       /* Only iterate over packs that are not marked as kept in-core. */
-       FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),
-
-       /* Only iterate over packs that do not have .keep files. */
-       FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),
-};
-
-/*
- * Iterate over all accessible loose objects without respect to
- * reachability. By default, this includes both local and alternate objects.
- * The order in which objects are visited is unspecified.
- *
- * Any flags specific to packs are ignored.
- */
-int for_each_loose_object(each_loose_object_fn, void *,
-                         enum for_each_object_flags flags);
-
-/*
- * Iterate over all accessible packed objects without respect to reachability.
- * By default, this includes both local and alternate packs.
- *
- * Note that some objects may appear twice if they are found in multiple packs.
- * Each pack is visited in an unspecified order. By default, objects within a
- * pack are visited in pack-idx order (i.e., sorted by oid).
- */
-typedef int each_packed_object_fn(const struct object_id *oid,
-                                 struct packed_git *pack,
-                                 uint32_t pos,
-                                 void *data);
-int for_each_object_in_pack(struct packed_git *p,
-                           each_packed_object_fn, void *data,
-                           enum for_each_object_flags flags);
-int for_each_packed_object(each_packed_object_fn, void *,
-                          enum for_each_object_flags flags);
-
 #endif /* OBJECT_STORE_H */
index 8a74eb85e94603458e0fe7eafbc58a40a3380b10..2c61e4c86217e633d2e28acd0b3ae654584ede7d 100644 (file)
--- a/object.c
+++ b/object.c
@@ -1,8 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
 #include "object.h"
 #include "replace-object.h"
+#include "object-file.h"
 #include "object-store.h"
 #include "blob.h"
+#include "statinfo.h"
 #include "tree.h"
 #include "commit.h"
 #include "tag.h"
@@ -212,8 +216,7 @@ struct object *parse_object_buffer(struct repository *r, const struct object_id
        if (type == OBJ_BLOB) {
                struct blob *blob = lookup_blob(r, oid);
                if (blob) {
-                       if (parse_blob_buffer(blob, buffer, size))
-                               return NULL;
+                       parse_blob_buffer(blob);
                        obj = &blob->object;
                }
        } else if (type == OBJ_TREE) {
@@ -286,14 +289,13 @@ struct object *parse_object_with_flags(struct repository *r,
                        return &commit->object;
        }
 
-       if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
-           (!obj && repo_has_object_file(r, oid) &&
-            oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
+       if ((!obj || obj->type == OBJ_BLOB) &&
+           oid_object_info(r, oid, NULL) == OBJ_BLOB) {
                if (!skip_hash && stream_object_signature(r, repl) < 0) {
                        error(_("hash mismatch %s"), oid_to_hex(oid));
                        return NULL;
                }
-               parse_blob_buffer(lookup_blob(r, oid), NULL, 0);
+               parse_blob_buffer(lookup_blob(r, oid));
                return lookup_object(r, oid);
        }
 
@@ -355,6 +357,12 @@ void object_list_free(struct object_list **list)
  */
 static char object_array_slopbuf[1];
 
+void object_array_init(struct object_array *array)
+{
+       struct object_array blank = OBJECT_ARRAY_INIT;
+       memcpy(array, &blank, sizeof(*array));
+}
+
 void add_object_array_with_path(struct object *obj, const char *name,
                                struct object_array *array,
                                unsigned mode, const char *path)
index 31ebe114585bb14cb95f4c74486635754d199955..114d45954d082229ed747dee16f2510387145771 100644 (file)
--- a/object.h
+++ b/object.h
@@ -1,9 +1,10 @@
 #ifndef OBJECT_H
 #define OBJECT_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 struct buffer_slab;
+struct repository;
 
 struct parsed_object_pool {
        struct object **obj_hash;
@@ -57,6 +58,8 @@ struct object_array {
 
 #define OBJECT_ARRAY_INIT { 0 }
 
+void object_array_init(struct object_array *array);
+
 /*
  * object flag allocation:
  * revision.h:               0---------10         15             23------27
@@ -81,6 +84,70 @@ struct object_array {
  */
 #define FLAG_BITS  28
 
+#define TYPE_BITS 3
+
+/*
+ * Values in this enum (except those outside the 3 bit range) are part
+ * of pack file format. See gitformat-pack(5) for more information.
+ */
+enum object_type {
+       OBJ_BAD = -1,
+       OBJ_NONE = 0,
+       OBJ_COMMIT = 1,
+       OBJ_TREE = 2,
+       OBJ_BLOB = 3,
+       OBJ_TAG = 4,
+       /* 5 for future expansion */
+       OBJ_OFS_DELTA = 6,
+       OBJ_REF_DELTA = 7,
+       OBJ_ANY,
+       OBJ_MAX
+};
+
+/* unknown mode (impossible combination S_IFIFO|S_IFCHR) */
+#define S_IFINVALID     0030000
+
+/*
+ * A "directory link" is a link to another git directory.
+ *
+ * The value 0160000 is not normally a valid mode, and
+ * also just happens to be S_IFDIR + S_IFLNK
+ */
+#define S_IFGITLINK    0160000
+#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
+
+#define S_ISSPARSEDIR(m) ((m) == S_IFDIR)
+
+static inline enum object_type object_type(unsigned int mode)
+{
+       return S_ISDIR(mode) ? OBJ_TREE :
+               S_ISGITLINK(mode) ? OBJ_COMMIT :
+               OBJ_BLOB;
+}
+
+#define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
+static inline unsigned int create_ce_mode(unsigned int mode)
+{
+       if (S_ISLNK(mode))
+               return S_IFLNK;
+       if (S_ISSPARSEDIR(mode))
+               return S_IFDIR;
+       if (S_ISDIR(mode) || S_ISGITLINK(mode))
+               return S_IFGITLINK;
+       return S_IFREG | ce_permissions(mode);
+}
+
+static inline unsigned int canon_mode(unsigned int mode)
+{
+       if (S_ISREG(mode))
+               return S_IFREG | ce_permissions(mode);
+       if (S_ISLNK(mode))
+               return S_IFLNK;
+       if (S_ISDIR(mode))
+               return S_IFDIR;
+       return S_IFGITLINK;
+}
+
 /*
  * The object type is stored in 3 bits.
  */
index 73ba76e9e9a223306a03c3d04123c0c4af4aeda2..8e4717746c3183bd47c3c746c747efd83c61168c 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "oid-array.h"
 #include "hash-lookup.h"
 
index 49965fe856814393c9381788dced2b05d35aed1f..8b1bc4dec9496e3b49ba064cc0936925625e5c79 100644 (file)
--- a/oidmap.c
+++ b/oidmap.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hash.h"
 #include "oidmap.h"
 
 static int oidmap_neq(const void *hashmap_cmp_fn_data UNUSED,
index c66a83ab1d6891dbe6916d13339b64456b8cc440..05c673eb7c1163d912dd7e54466ae9931be5426b 100644 (file)
--- a/oidmap.h
+++ b/oidmap.h
@@ -1,7 +1,7 @@
 #ifndef OIDMAP_H
 #define OIDMAP_H
 
-#include "cache.h"
+#include "hash-ll.h"
 #include "hashmap.h"
 
 /*
index b36a2bae86470236a51ffe6bec7792222de478fe..d1e5376316ecd5f9dcf549e1067697283bdc712c 100644 (file)
--- a/oidset.c
+++ b/oidset.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "oidset.h"
+#include "hex.h"
+#include "strbuf.h"
 
 void oidset_init(struct oidset *set, size_t initial_size)
 {
index 0d39389bee29be3f0496185331df1ac85f87003b..daef175dc71d6c7d55fc89ef1e286334afbe00b7 100644 (file)
--- a/oidtree.c
+++ b/oidtree.c
@@ -2,8 +2,8 @@
  * A wrapper around cbtree which stores oids
  * May be used to replace oid-array for prefix (abbreviation) matches
  */
+#include "git-compat-util.h"
 #include "oidtree.h"
-#include "alloc.h"
 #include "hash.h"
 
 struct oidtree_iter_data {
index 77898f510a12272ae2302805896eaf9eecb5ce7d..55c83513fdd5f3823a3b3a430ce814094deed5ba 100644 (file)
--- a/oidtree.h
+++ b/oidtree.h
@@ -2,7 +2,7 @@
 #define OIDTREE_H
 
 #include "cbtree.h"
-#include "hash.h"
+#include "hash-ll.h"
 #include "mem-pool.h"
 
 struct oidtree {
diff --git a/oss-fuzz/.gitignore b/oss-fuzz/.gitignore
new file mode 100644 (file)
index 0000000..5b95408
--- /dev/null
@@ -0,0 +1,4 @@
+fuzz-commit-graph
+fuzz-date
+fuzz-pack-headers
+fuzz-pack-idx
similarity index 96%
rename from fuzz-commit-graph.c
rename to oss-fuzz/fuzz-commit-graph.c
index 914026f5d80f876c8a7c28a914a58295a5a72346..2992079dd97d75892f4f411cd4a979f459d68170 100644 (file)
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
 #include "commit-graph.h"
 #include "repository.h"
 
diff --git a/oss-fuzz/fuzz-date.c b/oss-fuzz/fuzz-date.c
new file mode 100644 (file)
index 0000000..036378b
--- /dev/null
@@ -0,0 +1,49 @@
+#include "git-compat-util.h"
+#include "date.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       int local;
+       int num;
+       char *str;
+       int16_t tz;
+       timestamp_t ts;
+       enum date_mode_type dmtype;
+       struct date_mode *dm;
+
+       if (size <= 4)
+               /*
+                * we use the first byte to fuzz dmtype and the
+                * second byte to fuzz local, then the next two
+                * bytes to fuzz tz offset. The remainder
+                * (at least one byte) is fed as input to
+                * approxidate_careful().
+                */
+               return 0;
+
+       local = !!(*data++ & 0x10);
+       num = *data++ % DATE_UNIX;
+       if (num >= DATE_STRFTIME)
+               num++;
+       dmtype = (enum date_mode_type)num;
+       size -= 2;
+
+       tz = *data++;
+       tz = (tz << 8) | *data++;
+       size -= 2;
+
+       str = xmemdupz(data, size);
+
+       ts = approxidate_careful(str, &num);
+       free(str);
+
+       dm = date_mode_from_type(dmtype);
+       dm->local = local;
+       show_date(ts, (int)tz, dm);
+
+       date_mode_release(dm);
+
+       return 0;
+}
similarity index 91%
rename from fuzz-pack-headers.c
rename to oss-fuzz/fuzz-pack-headers.c
index 99da1d0fd385eb3bc0c7f0b4a0b8cd167e0cb5e0..150c0f5fa2d7ec2b9dd6a14f6000e5b58b753aa9 100644 (file)
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
 #include "packfile.h"
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
similarity index 81%
rename from fuzz-pack-idx.c
rename to oss-fuzz/fuzz-pack-idx.c
index 0c3d777aac8de618a36777c5c82edae302c1bbcc..3e190214d1487e4bd1f04315698e0f041402648e 100644 (file)
@@ -1,4 +1,5 @@
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "object-store-ll.h"
 #include "packfile.h"
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
index a213f5eddc5df2401f1232f37514050ea91efc31..990a9498d731942bf2b01580a8ac77d08ba04d5f 100644 (file)
@@ -1,18 +1,22 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "commit.h"
-#include "tag.h"
 #include "diff.h"
 #include "revision.h"
-#include "list-objects.h"
 #include "progress.h"
-#include "pack-revindex.h"
 #include "pack.h"
 #include "pack-bitmap.h"
 #include "hash-lookup.h"
 #include "pack-objects.h"
+#include "path.h"
 #include "commit-reach.h"
 #include "prio-queue.h"
+#include "trace2.h"
+#include "tree.h"
+#include "tree-walk.h"
 
 struct bitmapped_commit {
        struct commit *commit;
@@ -191,6 +195,13 @@ struct bb_commit {
        unsigned idx; /* within selected array */
 };
 
+static void clear_bb_commit(struct bb_commit *commit)
+{
+       free_commit_list(commit->reverse_edges);
+       bitmap_free(commit->commit_mask);
+       bitmap_free(commit->bitmap);
+}
+
 define_commit_slab(bb_data, struct bb_commit);
 
 struct bitmap_builder {
@@ -332,7 +343,7 @@ next:
 
 static void bitmap_builder_clear(struct bitmap_builder *bb)
 {
-       clear_bb_data(&bb->data);
+       deep_clear_bb_data(&bb->data, clear_bb_commit);
        free(bb->commits);
        bb->commits_nr = bb->commits_alloc = 0;
 }
@@ -384,6 +395,8 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
        return 0;
 }
 
+static int reused_bitmaps_nr;
+
 static int fill_bitmap_commit(struct bb_commit *ent,
                              struct commit *commit,
                              struct prio_queue *queue,
@@ -404,13 +417,19 @@ static int fill_bitmap_commit(struct bb_commit *ent,
 
                if (old_bitmap && mapping) {
                        struct ewah_bitmap *old = bitmap_for_commit(old_bitmap, c);
+                       struct bitmap *remapped = bitmap_new();
                        /*
                         * If this commit has an old bitmap, then translate that
                         * bitmap and add its bits to this one. No need to walk
                         * parents or the tree for this commit.
                         */
-                       if (old && !rebuild_bitmap(mapping, old, ent->bitmap))
+                       if (old && !rebuild_bitmap(mapping, old, remapped)) {
+                               bitmap_or(ent->bitmap, remapped);
+                               bitmap_free(remapped);
+                               reused_bitmaps_nr++;
                                continue;
+                       }
+                       bitmap_free(remapped);
                }
 
                /*
@@ -421,7 +440,8 @@ static int fill_bitmap_commit(struct bb_commit *ent,
                if (!found)
                        return -1;
                bitmap_set(ent->bitmap, pos);
-               prio_queue_put(tree_queue, get_commit_tree(c));
+               prio_queue_put(tree_queue,
+                              repo_get_commit_tree(the_repository, c));
 
                for (p = c->parents; p; p = p->next) {
                        pos = find_object_pos(&p->item->object.oid, &found);
@@ -526,6 +546,8 @@ int bitmap_writer_build(struct packing_data *to_pack)
 
        trace2_region_leave("pack-bitmap-write", "building_bitmaps_total",
                            the_repository);
+       trace2_data_intmax("pack-bitmap-write", the_repository,
+                          "building_bitmaps_reused", reused_bitmaps_nr);
 
        stop_progress(&writer.progress);
 
index 440407f1be742c7e2a19a62f5adb968d409bcc29..229a11fb00fc20257385aca4cd653c26f8ce6b31 100644 (file)
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
+#include "gettext.h"
+#include "hex.h"
 #include "strbuf.h"
 #include "tag.h"
 #include "diff.h"
@@ -12,7 +14,9 @@
 #include "pack-objects.h"
 #include "packfile.h"
 #include "repository.h"
-#include "object-store.h"
+#include "trace2.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "list-objects-filter-options.h"
 #include "midx.h"
 #include "config.h"
@@ -334,7 +338,7 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
        struct stat st;
        char *bitmap_name = midx_bitmap_filename(midx);
        int fd = git_open(bitmap_name);
-       uint32_t i;
+       uint32_t i, preferred_pack;
        struct packed_git *preferred;
 
        if (fd < 0) {
@@ -354,8 +358,8 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
        if (bitmap_git->pack || bitmap_git->midx) {
                struct strbuf buf = STRBUF_INIT;
                get_midx_filename(&buf, midx->object_dir);
-               /* ignore extra bitmap file; we can only handle one */
-               warning(_("ignoring extra bitmap file: '%s'"), buf.buf);
+               trace2_data_string("bitmap", the_repository,
+                                  "ignoring extra midx bitmap file", buf.buf);
                close(fd);
                strbuf_release(&buf);
                return -1;
@@ -376,18 +380,25 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
                goto cleanup;
        }
 
-       if (load_midx_revindex(bitmap_git->midx) < 0) {
+       if (load_midx_revindex(bitmap_git->midx)) {
                warning(_("multi-pack bitmap is missing required reverse index"));
                goto cleanup;
        }
 
        for (i = 0; i < bitmap_git->midx->num_packs; i++) {
-               if (prepare_midx_pack(the_repository, bitmap_git->midx, i))
-                       die(_("could not open pack %s"),
-                           bitmap_git->midx->pack_names[i]);
+               if (prepare_midx_pack(the_repository, bitmap_git->midx, i)) {
+                       warning(_("could not open pack %s"),
+                               bitmap_git->midx->pack_names[i]);
+                       goto cleanup;
+               }
+       }
+
+       if (midx_preferred_pack(bitmap_git->midx, &preferred_pack) < 0) {
+               warning(_("could not determine MIDX preferred pack"));
+               goto cleanup;
        }
 
-       preferred = bitmap_git->midx->packs[midx_preferred_pack(bitmap_git)];
+       preferred = bitmap_git->midx->packs[preferred_pack];
        if (!is_pack_valid(preferred)) {
                warning(_("preferred pack (%s) is invalid"),
                        preferred->pack_name);
@@ -411,9 +422,6 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
        struct stat st;
        char *bitmap_name;
 
-       if (open_pack_index(packfile))
-               return -1;
-
        bitmap_name = pack_bitmap_filename(packfile);
        fd = git_open(bitmap_name);
 
@@ -432,8 +440,8 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
        }
 
        if (bitmap_git->pack || bitmap_git->midx) {
-               /* ignore extra bitmap file; we can only handle one */
-               warning(_("ignoring extra bitmap file: '%s'"), packfile->pack_name);
+               trace2_data_string("bitmap", the_repository,
+                                  "ignoring extra bitmap file", packfile->pack_name);
                close(fd);
                return -1;
        }
@@ -458,10 +466,12 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
                return -1;
        }
 
+       trace2_data_string("bitmap", the_repository, "opened bitmap file",
+                          packfile->pack_name);
        return 0;
 }
 
-static int load_reverse_index(struct bitmap_index *bitmap_git)
+static int load_reverse_index(struct repository *r, struct bitmap_index *bitmap_git)
 {
        if (bitmap_is_midx(bitmap_git)) {
                uint32_t i;
@@ -475,23 +485,23 @@ static int load_reverse_index(struct bitmap_index *bitmap_git)
                 * since we will need to make use of them in pack-objects.
                 */
                for (i = 0; i < bitmap_git->midx->num_packs; i++) {
-                       ret = load_pack_revindex(bitmap_git->midx->packs[i]);
+                       ret = load_pack_revindex(r, bitmap_git->midx->packs[i]);
                        if (ret)
                                return ret;
                }
                return 0;
        }
-       return load_pack_revindex(bitmap_git->pack);
+       return load_pack_revindex(r, bitmap_git->pack);
 }
 
-static int load_bitmap(struct bitmap_index *bitmap_git)
+static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git)
 {
        assert(bitmap_git->map);
 
        bitmap_git->bitmaps = kh_init_oid_map();
        bitmap_git->ext_index.positions = kh_init_oid_pos();
 
-       if (load_reverse_index(bitmap_git))
+       if (load_reverse_index(r, bitmap_git))
                goto failed;
 
        if (!(bitmap_git->commits = read_bitmap_1(bitmap_git)) ||
@@ -525,11 +535,16 @@ static int open_pack_bitmap(struct repository *r,
        struct packed_git *p;
        int ret = -1;
 
-       assert(!bitmap_git->map);
-
        for (p = get_all_packs(r); p; p = p->next) {
-               if (open_pack_bitmap_1(bitmap_git, p) == 0)
+               if (open_pack_bitmap_1(bitmap_git, p) == 0) {
                        ret = 0;
+                       /*
+                        * The only reason to keep looking is to report
+                        * duplicates.
+                        */
+                       if (!trace2_is_enabled())
+                               break;
+               }
        }
 
        return ret;
@@ -553,18 +568,27 @@ static int open_midx_bitmap(struct repository *r,
 static int open_bitmap(struct repository *r,
                       struct bitmap_index *bitmap_git)
 {
+       int found;
+
        assert(!bitmap_git->map);
 
-       if (!open_midx_bitmap(r, bitmap_git))
-               return 0;
-       return open_pack_bitmap(r, bitmap_git);
+       found = !open_midx_bitmap(r, bitmap_git);
+
+       /*
+        * these will all be skipped if we opened a midx bitmap; but run it
+        * anyway if tracing is enabled to report the duplicates
+        */
+       if (!found || trace2_is_enabled())
+               found |= !open_pack_bitmap(r, bitmap_git);
+
+       return found ? 0 : -1;
 }
 
 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(bitmap_git))
+       if (!open_bitmap(r, bitmap_git) && !load_bitmap(r, bitmap_git))
                return bitmap_git;
 
        free_bitmap_index(bitmap_git);
@@ -573,9 +597,10 @@ struct bitmap_index *prepare_bitmap_git(struct repository *r)
 
 struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx)
 {
+       struct repository *r = the_repository;
        struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
 
-       if (!open_midx_bitmap_1(bitmap_git, midx) && !load_bitmap(bitmap_git))
+       if (!open_midx_bitmap_1(bitmap_git, midx) && !load_bitmap(r, bitmap_git))
                return bitmap_git;
 
        free_bitmap_index(bitmap_git);
@@ -938,7 +963,8 @@ static void show_object(struct object *object, const char *name, void *data_)
        bitmap_set(data->base, bitmap_pos);
 }
 
-static void show_commit(struct commit *commit, void *data)
+static void show_commit(struct commit *commit UNUSED,
+                       void *data UNUSED)
 {
 }
 
@@ -1023,6 +1049,161 @@ static int add_commit_to_bitmap(struct bitmap_index *bitmap_git,
        return 1;
 }
 
+static struct bitmap *fill_in_bitmap(struct bitmap_index *bitmap_git,
+                                    struct rev_info *revs,
+                                    struct bitmap *base,
+                                    struct bitmap *seen)
+{
+       struct include_data incdata;
+       struct bitmap_show_data show_data;
+
+       if (!base)
+               base = bitmap_new();
+
+       incdata.bitmap_git = bitmap_git;
+       incdata.base = base;
+       incdata.seen = seen;
+
+       revs->include_check = should_include;
+       revs->include_check_obj = should_include_obj;
+       revs->include_check_data = &incdata;
+
+       if (prepare_revision_walk(revs))
+               die(_("revision walk setup failed"));
+
+       show_data.bitmap_git = bitmap_git;
+       show_data.base = base;
+
+       traverse_commit_list(revs, show_commit, show_object, &show_data);
+
+       revs->include_check = NULL;
+       revs->include_check_obj = NULL;
+       revs->include_check_data = NULL;
+
+       return base;
+}
+
+struct bitmap_boundary_cb {
+       struct bitmap_index *bitmap_git;
+       struct bitmap *base;
+
+       struct object_array boundary;
+};
+
+static void show_boundary_commit(struct commit *commit, void *_data)
+{
+       struct bitmap_boundary_cb *data = _data;
+
+       if (commit->object.flags & BOUNDARY)
+               add_object_array(&commit->object, "", &data->boundary);
+
+       if (commit->object.flags & UNINTERESTING) {
+               if (bitmap_walk_contains(data->bitmap_git, data->base,
+                                        &commit->object.oid))
+                       return;
+
+               add_commit_to_bitmap(data->bitmap_git, &data->base, commit);
+       }
+}
+
+static void show_boundary_object(struct object *object UNUSED,
+                                const char *name UNUSED,
+                                void *data UNUSED)
+{
+       BUG("should not be called");
+}
+
+static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
+                                           struct rev_info *revs,
+                                           struct object_list *roots)
+{
+       struct bitmap_boundary_cb cb;
+       struct object_list *root;
+       unsigned int i;
+       unsigned int tmp_blobs, tmp_trees, tmp_tags;
+       int any_missing = 0;
+
+       cb.bitmap_git = bitmap_git;
+       cb.base = bitmap_new();
+       object_array_init(&cb.boundary);
+
+       revs->ignore_missing_links = 1;
+
+       /*
+        * OR in any existing reachability bitmaps among `roots` into
+        * `cb.base`.
+        */
+       for (root = roots; root; root = root->next) {
+               struct object *object = root->item;
+               if (object->type != OBJ_COMMIT ||
+                   bitmap_walk_contains(bitmap_git, cb.base, &object->oid))
+                       continue;
+
+               if (add_commit_to_bitmap(bitmap_git, &cb.base,
+                                        (struct commit *)object))
+                       continue;
+
+               any_missing = 1;
+       }
+
+       if (!any_missing)
+               goto cleanup;
+
+       tmp_blobs = revs->blob_objects;
+       tmp_trees = revs->tree_objects;
+       tmp_tags = revs->blob_objects;
+       revs->blob_objects = 0;
+       revs->tree_objects = 0;
+       revs->tag_objects = 0;
+
+       /*
+        * We didn't have complete coverage of the roots. First setup a
+        * revision walk to (a) OR in any bitmaps that are UNINTERESTING
+        * between the tips and boundary, and (b) record the boundary.
+        */
+       trace2_region_enter("pack-bitmap", "boundary-prepare", the_repository);
+       if (prepare_revision_walk(revs))
+               die("revision walk setup failed");
+       trace2_region_leave("pack-bitmap", "boundary-prepare", the_repository);
+
+       trace2_region_enter("pack-bitmap", "boundary-traverse", the_repository);
+       revs->boundary = 1;
+       traverse_commit_list_filtered(revs,
+                                     show_boundary_commit,
+                                     show_boundary_object,
+                                     &cb, NULL);
+       revs->boundary = 0;
+       trace2_region_leave("pack-bitmap", "boundary-traverse", the_repository);
+
+       revs->blob_objects = tmp_blobs;
+       revs->tree_objects = tmp_trees;
+       revs->tag_objects = tmp_tags;
+
+       reset_revision_walk();
+       clear_object_flags(UNINTERESTING);
+
+       /*
+        * Then add the boundary commit(s) as fill-in traversal tips.
+        */
+       trace2_region_enter("pack-bitmap", "boundary-fill-in", the_repository);
+       for (i = 0; i < cb.boundary.nr; i++) {
+               struct object *obj = cb.boundary.objects[i].item;
+               if (bitmap_walk_contains(bitmap_git, cb.base, &obj->oid))
+                       obj->flags |= SEEN;
+               else
+                       add_pending_object(revs, obj, "");
+       }
+       if (revs->pending.nr)
+               cb.base = fill_in_bitmap(bitmap_git, revs, cb.base, NULL);
+       trace2_region_leave("pack-bitmap", "boundary-fill-in", the_repository);
+
+cleanup:
+       object_array_clear(&cb.boundary);
+       revs->ignore_missing_links = 0;
+
+       return cb.base;
+}
+
 static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
                                   struct rev_info *revs,
                                   struct object_list *roots,
@@ -1089,35 +1270,23 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
        }
 
        if (needs_walk) {
-               struct include_data incdata;
-               struct bitmap_show_data show_data;
-
-               if (!base)
-                       base = bitmap_new();
-
-               incdata.bitmap_git = bitmap_git;
-               incdata.base = base;
-               incdata.seen = seen;
-
-               revs->include_check = should_include;
-               revs->include_check_obj = should_include_obj;
-               revs->include_check_data = &incdata;
-
-               if (prepare_revision_walk(revs))
-                       die(_("revision walk setup failed"));
-
-               show_data.bitmap_git = bitmap_git;
-               show_data.base = base;
-
-               traverse_commit_list(revs,
-                                    show_commit, show_object,
-                                    &show_data);
-
-               revs->include_check = NULL;
-               revs->include_check_obj = NULL;
-               revs->include_check_data = NULL;
+               /*
+                * This fill-in traversal may walk over some objects
+                * again, since we have already traversed in order to
+                * find the boundary.
+                *
+                * But this extra walk should be extremely cheap, since
+                * all commit objects are loaded into memory, and
+                * because we skip walking to parents that are
+                * UNINTERESTING, since it will be marked in the haves
+                * bitmap already (or it has an on-disk bitmap, since
+                * OR-ing it in covers all of its ancestors).
+                */
+               base = fill_in_bitmap(bitmap_git, revs, base, seen);
        }
 
+       object_list_free(&not_mapped);
+
        return base;
 }
 
@@ -1132,7 +1301,7 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,
        for (i = 0; i < eindex->count; ++i) {
                struct object *obj;
 
-               if (!bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))
+               if (!bitmap_get(objects, st_add(bitmap_num_objects(bitmap_git), i)))
                        continue;
 
                obj = eindex->objects[i];
@@ -1311,7 +1480,7 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
         * them individually.
         */
        for (i = 0; i < eindex->count; i++) {
-               uint32_t pos = i + bitmap_num_objects(bitmap_git);
+               size_t pos = st_add(i, bitmap_num_objects(bitmap_git));
                if (eindex->objects[i]->type == type &&
                    bitmap_get(to_filter, pos) &&
                    !bitmap_get(tips, pos))
@@ -1402,7 +1571,7 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
        }
 
        for (i = 0; i < eindex->count; i++) {
-               uint32_t pos = i + bitmap_num_objects(bitmap_git);
+               size_t pos = st_add(i, bitmap_num_objects(bitmap_git));
                if (eindex->objects[i]->type == OBJ_BLOB &&
                    bitmap_get(to_filter, pos) &&
                    !bitmap_get(tips, pos) &&
@@ -1504,10 +1673,35 @@ static int can_filter_bitmap(struct list_objects_filter_options *filter)
        return !filter_bitmap(NULL, NULL, NULL, filter);
 }
 
+
+static void filter_packed_objects_from_bitmap(struct bitmap_index *bitmap_git,
+                                             struct bitmap *result)
+{
+       struct eindex *eindex = &bitmap_git->ext_index;
+       uint32_t objects_nr;
+       size_t i, pos;
+
+       objects_nr = bitmap_num_objects(bitmap_git);
+       pos = objects_nr / BITS_IN_EWORD;
+
+       if (pos > result->word_alloc)
+               pos = result->word_alloc;
+
+       memset(result->words, 0x00, sizeof(eword_t) * pos);
+       for (i = pos * BITS_IN_EWORD; i < objects_nr; i++)
+               bitmap_unset(result, i);
+
+       for (i = 0; i < eindex->count; ++i) {
+               if (has_object_pack(&eindex->objects[i]->oid))
+                       bitmap_unset(result, objects_nr + i);
+       }
+}
+
 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
                                         int filter_provided_objects)
 {
        unsigned int i;
+       int use_boundary_traversal;
 
        struct object_list *wants = NULL;
        struct object_list *haves = NULL;
@@ -1558,13 +1752,21 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
                        object_list_insert(object, &wants);
        }
 
-       /*
-        * if we have a HAVES list, but none of those haves is contained
-        * in the packfile that has a bitmap, we don't have anything to
-        * optimize here
-        */
-       if (haves && !in_bitmapped_pack(bitmap_git, haves))
-               goto cleanup;
+       use_boundary_traversal = git_env_bool(GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL, -1);
+       if (use_boundary_traversal < 0) {
+               prepare_repo_settings(revs->repo);
+               use_boundary_traversal = revs->repo->settings.pack_use_bitmap_boundary_traversal;
+       }
+
+       if (!use_boundary_traversal) {
+               /*
+                * if we have a HAVES list, but none of those haves is contained
+                * in the packfile that has a bitmap, we don't have anything to
+                * optimize here
+                */
+               if (haves && !in_bitmapped_pack(bitmap_git, haves))
+                       goto cleanup;
+       }
 
        /* if we don't want anything, we're done here */
        if (!wants)
@@ -1575,21 +1777,35 @@ 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(bitmap_git) < 0)
+       if (load_bitmap(revs->repo, bitmap_git) < 0)
                goto cleanup;
 
-       object_array_clear(&revs->pending);
+       if (!use_boundary_traversal)
+               object_array_clear(&revs->pending);
 
        if (haves) {
-               revs->ignore_missing_links = 1;
-               haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
-               reset_revision_walk();
-               revs->ignore_missing_links = 0;
+               if (use_boundary_traversal) {
+                       trace2_region_enter("pack-bitmap", "haves/boundary", the_repository);
+                       haves_bitmap = find_boundary_objects(bitmap_git, revs, haves);
+                       trace2_region_leave("pack-bitmap", "haves/boundary", the_repository);
+               } else {
+                       trace2_region_enter("pack-bitmap", "haves/classic", the_repository);
+                       revs->ignore_missing_links = 1;
+                       haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
+                       reset_revision_walk();
+                       revs->ignore_missing_links = 0;
+                       trace2_region_leave("pack-bitmap", "haves/classic", the_repository);
+               }
 
                if (!haves_bitmap)
                        BUG("failed to perform bitmap walk");
        }
 
+       if (use_boundary_traversal) {
+               object_array_clear(&revs->pending);
+               reset_revision_walk();
+       }
+
        wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap);
 
        if (!wants_bitmap)
@@ -1603,6 +1819,9 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
                      wants_bitmap,
                      &revs->filter);
 
+       if (revs->unpacked)
+               filter_packed_objects_from_bitmap(bitmap_git, wants_bitmap);
+
        bitmap_git->result = wants_bitmap;
        bitmap_git->haves = haves_bitmap;
 
@@ -1622,8 +1841,10 @@ cleanup:
  * -1 means "stop trying further objects"; 0 means we may or may not have
  * reused, but you can keep feeding bits.
  */
-static int try_partial_reuse(struct packed_git *pack,
-                            size_t pos,
+static int try_partial_reuse(struct bitmap_index *bitmap_git,
+                            struct bitmapped_pack *pack,
+                            size_t bitmap_pos,
+                            uint32_t pack_pos,
                             struct bitmap *reuse,
                             struct pack_window **w_curs)
 {
@@ -1631,40 +1852,18 @@ static int try_partial_reuse(struct packed_git *pack,
        enum object_type type;
        unsigned long size;
 
-       /*
-        * try_partial_reuse() is called either on (a) objects in the
-        * bitmapped pack (in the case of a single-pack bitmap) or (b)
-        * objects in the preferred pack of a multi-pack bitmap.
-        * Importantly, the latter can pretend as if only a single pack
-        * exists because:
-        *
-        *   - The first pack->num_objects bits of a MIDX bitmap are
-        *     reserved for the preferred pack, and
-        *
-        *   - Ties due to duplicate objects are always resolved in
-        *     favor of the preferred pack.
-        *
-        * Therefore we do not need to ever ask the MIDX for its copy of
-        * an object by OID, since it will always select it from the
-        * preferred pack. Likewise, the selected copy of the base
-        * object for any deltas will reside in the same pack.
-        *
-        * This means that we can reuse pos when looking up the bit in
-        * the reuse bitmap, too, since bits corresponding to the
-        * preferred pack precede all bits from other packs.
-        */
-
-       if (pos >= pack->num_objects)
-               return -1; /* not actually in the pack or MIDX preferred pack */
+       if (pack_pos >= pack->p->num_objects)
+               return -1; /* not actually in the pack */
 
-       offset = delta_obj_offset = pack_pos_to_offset(packpos);
-       type = unpack_object_header(pack, w_curs, &offset, &size);
+       offset = delta_obj_offset = pack_pos_to_offset(pack->p, pack_pos);
+       type = unpack_object_header(pack->p, w_curs, &offset, &size);
        if (type < 0)
                return -1; /* broken packfile, punt */
 
        if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA) {
                off_t base_offset;
                uint32_t base_pos;
+               uint32_t base_bitmap_pos;
 
                /*
                 * Find the position of the base object so we can look it up
@@ -1674,24 +1873,48 @@ static int try_partial_reuse(struct packed_git *pack,
                 * and the normal slow path will complain about it in
                 * more detail.
                 */
-               base_offset = get_delta_base(pack, w_curs, &offset, type,
+               base_offset = get_delta_base(pack->p, w_curs, &offset, type,
                                             delta_obj_offset);
                if (!base_offset)
                        return 0;
-               if (offset_to_pack_pos(pack, base_offset, &base_pos) < 0)
-                       return 0;
 
-               /*
-                * We assume delta dependencies always point backwards. This
-                * lets us do a single pass, and is basically always true
-                * due to the way OFS_DELTAs work. You would not typically
-                * find REF_DELTA in a bitmapped pack, since we only bitmap
-                * packs we write fresh, and OFS_DELTA is the default). But
-                * let's double check to make sure the pack wasn't written with
-                * odd parameters.
-                */
-               if (base_pos >= pos)
-                       return 0;
+               offset_to_pack_pos(pack->p, base_offset, &base_pos);
+
+               if (bitmap_is_midx(bitmap_git)) {
+                       /*
+                        * Cross-pack deltas are rejected for now, but could
+                        * theoretically be supported in the future.
+                        *
+                        * We would need to ensure that we're sending both
+                        * halves of the delta/base pair, regardless of whether
+                        * or not the two cross a pack boundary. If they do,
+                        * then we must convert the delta to an REF_DELTA to
+                        * refer back to the base in the other pack.
+                        * */
+                       if (midx_pair_to_pack_pos(bitmap_git->midx,
+                                                 pack->pack_int_id,
+                                                 base_offset,
+                                                 &base_bitmap_pos) < 0) {
+                               return 0;
+                       }
+               } else {
+                       if (offset_to_pack_pos(pack->p, base_offset,
+                                              &base_pos) < 0)
+                               return 0;
+                       /*
+                        * We assume delta dependencies always point backwards.
+                        * This lets us do a single pass, and is basically
+                        * always true due to the way OFS_DELTAs work. You would
+                        * not typically find REF_DELTA in a bitmapped pack,
+                        * since we only bitmap packs we write fresh, and
+                        * OFS_DELTA is the default). But let's double check to
+                        * make sure the pack wasn't written with odd
+                        * parameters.
+                        */
+                       if (base_pos >= pack_pos)
+                               return 0;
+                       base_bitmap_pos = pack->bitmap_pos + base_pos;
+               }
 
                /*
                 * And finally, if we're not sending the base as part of our
@@ -1701,76 +1924,89 @@ static int try_partial_reuse(struct packed_git *pack,
                 * to REF_DELTA on the fly. Better to just let the normal
                 * object_entry code path handle it.
                 */
-               if (!bitmap_get(reuse, base_pos))
+               if (!bitmap_get(reuse, base_bitmap_pos))
                        return 0;
        }
 
        /*
         * If we got here, then the object is OK to reuse. Mark it.
         */
-       bitmap_set(reuse, pos);
+       bitmap_set(reuse, bitmap_pos);
        return 0;
 }
 
-uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)
-{
-       struct multi_pack_index *m = bitmap_git->midx;
-       if (!m)
-               BUG("midx_preferred_pack: requires non-empty MIDX");
-       return nth_midxed_pack_int_id(m, pack_pos_to_midx(bitmap_git->midx, 0));
-}
-
-int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
-                                      struct packed_git **packfile_out,
-                                      uint32_t *entries,
-                                      struct bitmap **reuse_out)
+static void reuse_partial_packfile_from_bitmap_1(struct bitmap_index *bitmap_git,
+                                                struct bitmapped_pack *pack,
+                                                struct bitmap *reuse)
 {
-       struct packed_git *pack;
        struct bitmap *result = bitmap_git->result;
-       struct bitmap *reuse;
        struct pack_window *w_curs = NULL;
-       size_t i = 0;
-       uint32_t offset;
-       uint32_t objects_nr;
+       size_t pos = pack->bitmap_pos / BITS_IN_EWORD;
 
-       assert(result);
+       if (!pack->bitmap_pos) {
+               /*
+                * If we're processing the first (in the case of a MIDX, the
+                * preferred pack) or the only (in the case of single-pack
+                * bitmaps) pack, then we can reuse whole words at a time.
+                *
+                * This is because we know that any deltas in this range *must*
+                * have their bases chosen from the same pack, since:
+                *
+                * - In the single pack case, there is no other pack to choose
+                *   them from.
+                *
+                * - In the MIDX case, the first pack is the preferred pack, so
+                *   all ties are broken in favor of that pack (i.e. the one
+                *   we're currently processing). So any duplicate bases will be
+                *   resolved in favor of the pack we're processing.
+                */
+               while (pos < result->word_alloc &&
+                      pos < pack->bitmap_nr / BITS_IN_EWORD &&
+                      result->words[pos] == (eword_t)~0)
+                       pos++;
+               memset(reuse->words, 0xFF, pos * sizeof(eword_t));
+       }
 
-       load_reverse_index(bitmap_git);
+       for (; pos < result->word_alloc; pos++) {
+               eword_t word = result->words[pos];
+               size_t offset;
 
-       if (bitmap_is_midx(bitmap_git))
-               pack = bitmap_git->midx->packs[midx_preferred_pack(bitmap_git)];
-       else
-               pack = bitmap_git->pack;
-       objects_nr = pack->num_objects;
+               for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+                       size_t bit_pos;
+                       uint32_t pack_pos;
 
-       while (i < result->word_alloc && result->words[i] == (eword_t)~0)
-               i++;
+                       if (word >> offset == 0)
+                               break;
 
-       /*
-        * Don't mark objects not in the packfile or preferred pack. This bitmap
-        * marks objects eligible for reuse, but the pack-reuse code only
-        * understands how to reuse a single pack. Since the preferred pack is
-        * guaranteed to have all bases for its deltas (in a multi-pack bitmap),
-        * we use it instead of another pack. In single-pack bitmaps, the choice
-        * is made for us.
-        */
-       if (i > objects_nr / BITS_IN_EWORD)
-               i = objects_nr / BITS_IN_EWORD;
+                       offset += ewah_bit_ctz64(word >> offset);
 
-       reuse = bitmap_word_alloc(i);
-       memset(reuse->words, 0xFF, i * sizeof(eword_t));
+                       bit_pos = pos * BITS_IN_EWORD + offset;
+                       if (bit_pos < pack->bitmap_pos)
+                               continue;
+                       if (bit_pos >= pack->bitmap_pos + pack->bitmap_nr)
+                               goto done;
 
-       for (; i < result->word_alloc; ++i) {
-               eword_t word = result->words[i];
-               size_t pos = (i * BITS_IN_EWORD);
+                       if (bitmap_is_midx(bitmap_git)) {
+                               uint32_t midx_pos;
+                               off_t ofs;
 
-               for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
-                       if ((word >> offset) == 0)
-                               break;
+                               midx_pos = pack_pos_to_midx(bitmap_git->midx, bit_pos);
+                               ofs = nth_midxed_offset(bitmap_git->midx, midx_pos);
 
-                       offset += ewah_bit_ctz64(word >> offset);
-                       if (try_partial_reuse(pack, pos + offset,
-                                             reuse, &w_curs) < 0) {
+                               if (offset_to_pack_pos(pack->p, ofs, &pack_pos) < 0)
+                                       BUG("could not find object in pack %s "
+                                           "at offset %"PRIuMAX" in MIDX",
+                                           pack_basename(pack->p), (uintmax_t)ofs);
+                       } else {
+                               pack_pos = cast_size_t_to_uint32_t(st_sub(bit_pos, pack->bitmap_pos));
+                               if (pack_pos >= pack->p->num_objects)
+                                       BUG("advanced beyond the end of pack %s (%"PRIuMAX" > %"PRIu32")",
+                                           pack_basename(pack->p), (uintmax_t)pack_pos,
+                                           pack->p->num_objects);
+                       }
+
+                       if (try_partial_reuse(bitmap_git, pack, bit_pos,
+                                             pack_pos, reuse, &w_curs) < 0) {
                                /*
                                 * try_partial_reuse indicated we couldn't reuse
                                 * any bits, so there is no point in trying more
@@ -1787,11 +2023,97 @@ int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
 
 done:
        unuse_pack(&w_curs);
+}
 
-       *entries = bitmap_popcount(reuse);
-       if (!*entries) {
-               bitmap_free(reuse);
+static int bitmapped_pack_cmp(const void *va, const void *vb)
+{
+       const struct bitmapped_pack *a = va;
+       const struct bitmapped_pack *b = vb;
+
+       if (a->bitmap_pos < b->bitmap_pos)
                return -1;
+       if (a->bitmap_pos > b->bitmap_pos)
+               return 1;
+       return 0;
+}
+
+void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
+                                       struct bitmapped_pack **packs_out,
+                                       size_t *packs_nr_out,
+                                       struct bitmap **reuse_out,
+                                       int multi_pack_reuse)
+{
+       struct repository *r = the_repository;
+       struct bitmapped_pack *packs = NULL;
+       struct bitmap *result = bitmap_git->result;
+       struct bitmap *reuse;
+       size_t i;
+       size_t packs_nr = 0, packs_alloc = 0;
+       size_t word_alloc;
+       uint32_t objects_nr = 0;
+
+       assert(result);
+
+       load_reverse_index(r, bitmap_git);
+
+       if (bitmap_is_midx(bitmap_git)) {
+               for (i = 0; i < bitmap_git->midx->num_packs; i++) {
+                       struct bitmapped_pack pack;
+                       if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {
+                               warning(_("unable to load pack: '%s', disabling pack-reuse"),
+                                       bitmap_git->midx->pack_names[i]);
+                               free(packs);
+                               return;
+                       }
+
+                       if (!pack.bitmap_nr)
+                               continue;
+
+                       if (!multi_pack_reuse && pack.bitmap_pos) {
+                               /*
+                                * If we're only reusing a single pack, skip
+                                * over any packs which are not positioned at
+                                * the beginning of the MIDX bitmap.
+                                *
+                                * This is consistent with the existing
+                                * single-pack reuse behavior, which only reuses
+                                * parts of the MIDX's preferred pack.
+                                */
+                               continue;
+                       }
+
+                       ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
+                       memcpy(&packs[packs_nr++], &pack, sizeof(pack));
+
+                       objects_nr += pack.p->num_objects;
+
+                       if (!multi_pack_reuse)
+                               break;
+               }
+
+               QSORT(packs, packs_nr, bitmapped_pack_cmp);
+       } else {
+               ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
+
+               packs[packs_nr].p = bitmap_git->pack;
+               packs[packs_nr].bitmap_nr = bitmap_git->pack->num_objects;
+               packs[packs_nr].bitmap_pos = 0;
+
+               objects_nr = packs[packs_nr++].bitmap_nr;
+       }
+
+       word_alloc = objects_nr / BITS_IN_EWORD;
+       if (objects_nr % BITS_IN_EWORD)
+               word_alloc++;
+       reuse = bitmap_word_alloc(word_alloc);
+
+       for (i = 0; i < packs_nr; i++)
+               reuse_partial_packfile_from_bitmap_1(bitmap_git, &packs[i], reuse);
+
+       if (bitmap_is_empty(reuse)) {
+               free(packs);
+               bitmap_free(reuse);
+               return;
        }
 
        /*
@@ -1799,9 +2121,9 @@ done:
         * need to be handled separately.
         */
        bitmap_and_not(result, reuse);
-       *packfile_out = pack;
+       *packs_out = packs;
+       *packs_nr_out = packs_nr;
        *reuse_out = reuse;
-       return 0;
 }
 
 int bitmap_walk_contains(struct bitmap_index *bitmap_git,
@@ -1852,7 +2174,8 @@ 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, bitmap_num_objects(bitmap_git) + i))
+                   bitmap_get(objects,
+                              st_add(bitmap_num_objects(bitmap_git), i)))
                        count++;
        }
 
@@ -1927,7 +2250,8 @@ static void test_bitmap_type(struct bitmap_test_data *tdata,
                    type_name(bitmap_type));
 }
 
-static void test_show_object(struct object *object, const char *name,
+static void test_show_object(struct object *object,
+                            const char *name UNUSED,
                             void *data)
 {
        struct bitmap_test_data *tdata = data;
@@ -2114,12 +2438,13 @@ int rebuild_bitmap(const uint32_t *reposition,
 uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
                                struct packing_data *mapping)
 {
+       struct repository *r = the_repository;
        uint32_t i, num_objects;
        uint32_t *reposition;
 
        if (!bitmap_is_midx(bitmap_git))
-               load_reverse_index(bitmap_git);
-       else if (load_midx_revindex(bitmap_git->midx) < 0)
+               load_reverse_index(r, bitmap_git);
+       else if (load_midx_revindex(bitmap_git->midx))
                BUG("rebuild_existing_bitmaps: missing required rev-cache "
                    "extension");
 
@@ -2264,7 +2589,8 @@ static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git)
        for (i = 0; i < eindex->count; i++) {
                struct object *obj = eindex->objects[i];
 
-               if (!bitmap_get(result, bitmap_num_objects(bitmap_git) + i))
+               if (!bitmap_get(result,
+                               st_add(bitmap_num_objects(bitmap_git), i)))
                        continue;
 
                if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)
@@ -2301,7 +2627,11 @@ int bitmap_is_midx(struct bitmap_index *bitmap_git)
 
 const struct string_list *bitmap_preferred_tips(struct repository *r)
 {
-       return repo_config_get_value_multi(r, "pack.preferbitmaptips");
+       const struct string_list *dest;
+
+       if (!repo_config_get_string_multi(r, "pack.preferbitmaptips", &dest))
+               return dest;
+       return NULL;
 }
 
 int bitmap_is_preferred_refname(struct repository *r, const char *refname)
@@ -2319,3 +2649,48 @@ int bitmap_is_preferred_refname(struct repository *r, const char *refname)
 
        return 0;
 }
+
+static int verify_bitmap_file(const char *name)
+{
+       struct stat st;
+       unsigned char *data;
+       int fd = git_open(name);
+       int res = 0;
+
+       /* It is OK to not have the file. */
+       if (fd < 0 || fstat(fd, &st)) {
+               if (fd >= 0)
+                       close(fd);
+               return 0;
+       }
+
+       data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       close(fd);
+       if (!hashfile_checksum_valid(data, st.st_size))
+               res = error(_("bitmap file '%s' has invalid checksum"),
+                           name);
+
+       munmap(data, st.st_size);
+       return res;
+}
+
+int verify_bitmap_files(struct repository *r)
+{
+       int res = 0;
+
+       for (struct multi_pack_index *m = get_multi_pack_index(r);
+            m; m = m->next) {
+               char *midx_bitmap_name = midx_bitmap_filename(m);
+               res |= verify_bitmap_file(midx_bitmap_name);
+               free(midx_bitmap_name);
+       }
+
+       for (struct packed_git *p = get_all_packs(r);
+            p; p = p->next) {
+               char *pack_bitmap_name = pack_bitmap_filename(p);
+               res |= verify_bitmap_file(pack_bitmap_name);
+               free(pack_bitmap_name);
+       }
+
+       return res;
+}
index f0180b5276b15d6567eda9984637333811fa851b..c7dea13217a00ef544d16a45d0f78de8f0e8ebc0 100644 (file)
@@ -52,6 +52,15 @@ typedef int (*show_reachable_fn)(
 
 struct bitmap_index;
 
+struct bitmapped_pack {
+       struct packed_git *p;
+
+       uint32_t bitmap_pos;
+       uint32_t bitmap_nr;
+
+       uint32_t pack_int_id; /* MIDX only */
+};
+
 struct bitmap_index *prepare_bitmap_git(struct repository *r);
 struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx);
 void count_bitmap_commit_list(struct bitmap_index *, uint32_t *commits,
@@ -62,13 +71,17 @@ void traverse_bitmap_commit_list(struct bitmap_index *,
 void test_bitmap_walk(struct rev_info *revs);
 int test_bitmap_commits(struct repository *r);
 int test_bitmap_hashes(struct repository *r);
+
+#define GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL \
+       "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL"
+
 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
                                         int filter_provided_objects);
-uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);
-int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
-                                      struct packed_git **packfile,
-                                      uint32_t *entries,
-                                      struct bitmap **reuse_out);
+void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
+                                       struct bitmapped_pack **packs_out,
+                                       size_t *packs_nr_out,
+                                       struct bitmap **reuse_out,
+                                       int multi_pack_reuse);
 int rebuild_existing_bitmaps(struct bitmap_index *, struct packing_data *mapping,
                             kh_oid_map_t *reused_bitmaps, int show_progress);
 void free_bitmap_index(struct bitmap_index *);
@@ -111,4 +124,6 @@ int bitmap_is_midx(struct bitmap_index *bitmap_git);
 const struct string_list *bitmap_preferred_tips(struct repository *r);
 int bitmap_is_preferred_refname(struct repository *r, const char *refname);
 
+int verify_bitmap_files(struct repository *r);
+
 #endif
index bfb593ba7261a18a8283604f4e1cfdd10eb978f6..25104d5b14c1e8f9a9988f106a1ad1a6823ca31c 100644 (file)
@@ -1,10 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "pack.h"
-#include "pack-revindex.h"
 #include "progress.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 
 struct idx_entry {
        off_t                offset;
index 0f9785fc5e4ed9c26f7c1f5503395e74da0d74b3..cdf30b8d2b0e809db7ee7e219b39863fad702bd4 100644 (file)
@@ -1,7 +1,10 @@
 #include "git-compat-util.h"
+#include "gettext.h"
 #include "pack-mtimes.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "packfile.h"
+#include "strbuf.h"
 
 static char *pack_mtimes_filename(struct packed_git *p)
 {
index cc957b3e852716541f764a40641462cb39fe0407..107327cec0bc5202051b6139e926fd27a1ce6c9c 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef PACK_MTIMES_H
 #define PACK_MTIMES_H
 
-#include "git-compat-util.h"
-
 #define MTIMES_SIGNATURE 0x4d544d45 /* "MTME" */
 #define MTIMES_VERSION 1
 
index 272e8d451739e8466a35df155520c9c54d85e75c..a9d9855063aea85f4b1b8f70f301c644c5d5e225 100644 (file)
@@ -1,9 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "object.h"
 #include "pack.h"
 #include "pack-objects.h"
 #include "packfile.h"
-#include "config.h"
+#include "parse.h"
 
 static uint32_t locate_object_entry_hash(struct packing_data *pdata,
                                         const struct object_id *oid,
@@ -151,6 +151,21 @@ void prepare_packing_data(struct repository *r, struct packing_data *pdata)
        init_recursive_mutex(&pdata->odb_lock);
 }
 
+void clear_packing_data(struct packing_data *pdata)
+{
+       if (!pdata)
+               return;
+
+       free(pdata->cruft_mtime);
+       free(pdata->in_pack);
+       free(pdata->in_pack_by_idx);
+       free(pdata->in_pack_pos);
+       free(pdata->index);
+       free(pdata->layer);
+       free(pdata->objects);
+       free(pdata->tree_depth);
+}
+
 struct object_entry *packlist_alloc(struct packing_data *pdata,
                                    const struct object_id *oid)
 {
index 579476687c4f7123c7861cde595a1ce94f87ae1f..b9898a4e64b8b4d53b21ea776c16f79d9794efef 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef PACK_OBJECTS_H
 #define PACK_OBJECTS_H
 
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "thread-utils.h"
 #include "pack.h"
 
@@ -169,6 +169,7 @@ struct packing_data {
 };
 
 void prepare_packing_data(struct repository *r, struct packing_data *pdata);
+void clear_packing_data(struct packing_data *pdata);
 
 /* Protect access to object database */
 static inline void packing_data_lock(struct packing_data *pdata)
index 08dc1601679f1011f7ff38b7a721dbb4dd116c85..a7624d8be8e58e0807cf0b2dc5e3bae52ac53af9 100644 (file)
@@ -1,9 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "pack-revindex.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "packfile.h"
-#include "config.h"
+#include "strbuf.h"
+#include "trace2.h"
+#include "parse.h"
 #include "midx.h"
+#include "csum-file.h"
 
 struct revindex_entry {
        off_t offset;
@@ -204,10 +209,14 @@ static int load_revindex_from_disk(char *revindex_name,
        size_t revindex_size;
        struct revindex_header *hdr;
 
+       if (git_env_bool(GIT_TEST_REV_INDEX_DIE_ON_DISK, 0))
+               die("dying as requested by '%s'", GIT_TEST_REV_INDEX_DIE_ON_DISK);
+
        fd = git_open(revindex_name);
 
        if (fd < 0) {
-               ret = -1;
+               /* "No file" means return 1. */
+               ret = 1;
                goto cleanup;
        }
        if (fstat(fd, &st)) {
@@ -259,7 +268,7 @@ cleanup:
        return ret;
 }
 
-static int load_pack_revindex_from_disk(struct packed_git *p)
+int load_pack_revindex_from_disk(struct packed_git *p)
 {
        char *revindex_name;
        int ret;
@@ -282,18 +291,69 @@ cleanup:
        return ret;
 }
 
-int load_pack_revindex(struct packed_git *p)
+int load_pack_revindex(struct repository *r, struct packed_git *p)
 {
        if (p->revindex || p->revindex_data)
                return 0;
 
-       if (!load_pack_revindex_from_disk(p))
+       prepare_repo_settings(r);
+
+       if (r->settings.pack_read_reverse_index &&
+           !load_pack_revindex_from_disk(p))
                return 0;
        else if (!create_pack_revindex_in_memory(p))
                return 0;
        return -1;
 }
 
+/*
+ * verify_pack_revindex verifies that the on-disk rev-index for the given
+ * pack-file is the same that would be created if written from scratch.
+ *
+ * A negative number is returned on error.
+ */
+int verify_pack_revindex(struct packed_git *p)
+{
+       int res = 0;
+
+       /* Do not bother checking if not initialized. */
+       if (!p->revindex_map || !p->revindex_data)
+               return res;
+
+       if (!hashfile_checksum_valid((const unsigned char *)p->revindex_map, p->revindex_size)) {
+               error(_("invalid checksum"));
+               res = -1;
+       }
+
+       /* This may fail due to a broken .idx. */
+       if (create_pack_revindex_in_memory(p))
+               return res;
+
+       for (size_t i = 0; i < p->num_objects; i++) {
+               uint32_t nr = p->revindex[i].nr;
+               uint32_t rev_val = get_be32(p->revindex_data + i);
+
+               if (nr != rev_val) {
+                       error(_("invalid rev-index position at %"PRIu64": %"PRIu32" != %"PRIu32""),
+                             (uint64_t)i, nr, rev_val);
+                       res = -1;
+               }
+       }
+
+       return res;
+}
+
+static int can_use_midx_ridx_chunk(struct multi_pack_index *m)
+{
+       if (!m->chunk_revindex)
+               return 0;
+       if (m->chunk_revindex_len != st_mult(sizeof(uint32_t), m->num_objects)) {
+               error(_("multi-pack-index reverse-index chunk is the wrong size"));
+               return 0;
+       }
+       return 1;
+}
+
 int load_midx_revindex(struct multi_pack_index *m)
 {
        struct strbuf revindex_name = STRBUF_INIT;
@@ -302,7 +362,7 @@ int load_midx_revindex(struct multi_pack_index *m)
        if (m->revindex_data)
                return 0;
 
-       if (m->chunk_revindex) {
+       if (can_use_midx_ridx_chunk(m)) {
                /*
                 * If the MIDX `m` has a `RIDX` chunk, then use its contents for
                 * the reverse index instead of trying to load a separate `.rev`
@@ -355,7 +415,7 @@ int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
 {
        unsigned lo, hi;
 
-       if (load_pack_revindex(p) < 0)
+       if (load_pack_revindex(the_repository, p) < 0)
                return -1;
 
        lo = 0;
@@ -460,19 +520,12 @@ static int midx_pack_order_cmp(const void *va, const void *vb)
        return 0;
 }
 
-int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
+static int midx_key_to_pack_pos(struct multi_pack_index *m,
+                               struct midx_pack_key *key,
+                               uint32_t *pos)
 {
-       struct midx_pack_key key;
        uint32_t *found;
 
-       if (!m->revindex_data)
-               BUG("midx_to_pack_pos: reverse index not yet loaded");
-       if (m->num_objects <= at)
-               BUG("midx_to_pack_pos: out-of-bounds object at %"PRIu32, at);
-
-       key.pack = nth_midxed_pack_int_id(m, at);
-       key.offset = nth_midxed_offset(m, at);
-       key.midx = m;
        /*
         * The preferred pack sorts first, so determine its identifier by
         * looking at the first object in pseudo-pack order.
@@ -482,14 +535,43 @@ int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
         * implicitly is preferred (and includes all its objects, since ties are
         * broken first by pack identifier).
         */
-       key.preferred_pack = nth_midxed_pack_int_id(m, pack_pos_to_midx(m, 0));
+       if (midx_preferred_pack(key->midx, &key->preferred_pack) < 0)
+               return error(_("could not determine preferred pack"));
 
-       found = bsearch(&key, m->revindex_data, m->num_objects,
-                       sizeof(*m->revindex_data), midx_pack_order_cmp);
+       found = bsearch(key, m->revindex_data, m->num_objects,
+                       sizeof(*m->revindex_data),
+                       midx_pack_order_cmp);
 
        if (!found)
-               return error("bad offset for revindex");
+               return -1;
 
        *pos = found - m->revindex_data;
        return 0;
 }
+
+int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
+{
+       struct midx_pack_key key;
+
+       if (!m->revindex_data)
+               BUG("midx_to_pack_pos: reverse index not yet loaded");
+       if (m->num_objects <= at)
+               BUG("midx_to_pack_pos: out-of-bounds object at %"PRIu32, at);
+
+       key.pack = nth_midxed_pack_int_id(m, at);
+       key.offset = nth_midxed_offset(m, at);
+       key.midx = m;
+
+       return midx_key_to_pack_pos(m, &key, pos);
+}
+
+int midx_pair_to_pack_pos(struct multi_pack_index *m, uint32_t pack_int_id,
+                         off_t ofs, uint32_t *pos)
+{
+       struct midx_pack_key key = {
+               .pack = pack_int_id,
+               .offset = ofs,
+               .midx = m,
+       };
+       return midx_key_to_pack_pos(m, &key, pos);
+}
index 4974e75eb4d0e9d002fc70a87e5bb4526e1e49f7..422c2487ae32d8a4500f4a151ca673364fed96a0 100644 (file)
 #define RIDX_SIGNATURE 0x52494458 /* "RIDX" */
 #define RIDX_VERSION 1
 
-#define GIT_TEST_WRITE_REV_INDEX "GIT_TEST_WRITE_REV_INDEX"
+#define GIT_TEST_NO_WRITE_REV_INDEX "GIT_TEST_NO_WRITE_REV_INDEX"
 #define GIT_TEST_REV_INDEX_DIE_IN_MEMORY "GIT_TEST_REV_INDEX_DIE_IN_MEMORY"
+#define GIT_TEST_REV_INDEX_DIE_ON_DISK "GIT_TEST_REV_INDEX_DIE_ON_DISK"
 
 struct packed_git;
 struct multi_pack_index;
+struct repository;
 
 /*
  * load_pack_revindex populates the revindex's internal data-structures for the
@@ -47,7 +49,23 @@ struct multi_pack_index;
  * If a '.rev' file is present it is mmap'd, and pointers are assigned into it
  * (instead of using the in-memory variant).
  */
-int load_pack_revindex(struct packed_git *p);
+int load_pack_revindex(struct repository *r, struct packed_git *p);
+
+/*
+ * Specifically load a pack revindex from disk.
+ *
+ * Returns 0 on success, 1 on "no .rev file", and -1 when there is an
+ * error parsing the .rev file.
+ */
+int load_pack_revindex_from_disk(struct packed_git *p);
+
+/*
+ * verify_pack_revindex verifies that the on-disk rev-index for the given
+ * pack-file is the same that would be created if written from scratch.
+ *
+ * A negative number is returned on error.
+ */
+int verify_pack_revindex(struct packed_git *p);
 
 /*
  * load_midx_revindex loads the '.rev' file corresponding to the given
@@ -124,4 +142,7 @@ uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos);
  */
 int midx_to_pack_pos(struct multi_pack_index *midx, uint32_t at, uint32_t *pos);
 
+int midx_pair_to_pack_pos(struct multi_pack_index *midx, uint32_t pack_id,
+                         off_t ofs, uint32_t *pos);
+
 #endif
index 00787e306db4c010ae68845f27ed69966fb9e250..80ecfa544c5d9f7980adfad829e32cabd281ecc4 100644 (file)
@@ -1,12 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "pack.h"
 #include "csum-file.h"
 #include "remote.h"
 #include "chunk-format.h"
 #include "pack-mtimes.h"
-#include "oidmap.h"
-#include "chunk-format.h"
 #include "pack-objects.h"
+#include "pack-revindex.h"
+#include "path.h"
+#include "strbuf.h"
 
 void reset_pack_idx_option(struct pack_idx_option *opts)
 {
@@ -310,13 +314,13 @@ static void write_mtimes_trailer(struct hashfile *f, const unsigned char *hash)
        hashwrite(f, hash, the_hash_algo->rawsz);
 }
 
-static const char *write_mtimes_file(struct packing_data *to_pack,
-                                    struct pack_idx_entry **objects,
-                                    uint32_t nr_objects,
-                                    const unsigned char *hash)
+static char *write_mtimes_file(struct packing_data *to_pack,
+                              struct pack_idx_entry **objects,
+                              uint32_t nr_objects,
+                              const unsigned char *hash)
 {
        struct strbuf tmp_file = STRBUF_INIT;
-       const char *mtimes_name;
+       char *mtimes_name;
        struct hashfile *f;
        int fd;
 
@@ -542,7 +546,7 @@ void stage_tmp_packfiles(struct strbuf *name_buffer,
                         char **idx_tmp_name)
 {
        const char *rev_tmp_name = NULL;
-       const char *mtimes_tmp_name = NULL;
+       char *mtimes_tmp_name = NULL;
 
        if (adjust_shared_perm(pack_tmp_name))
                die_errno("unable to make temporary pack file readable");
@@ -566,6 +570,9 @@ void stage_tmp_packfiles(struct strbuf *name_buffer,
                rename_tmp_packfile(name_buffer, rev_tmp_name, "rev");
        if (mtimes_tmp_name)
                rename_tmp_packfile(name_buffer, mtimes_tmp_name, "mtimes");
+
+       free((char *)rev_tmp_name);
+       free(mtimes_tmp_name);
 }
 
 void write_promisor_file(const char *promisor_name, struct ref **sought, int nr_sought)
diff --git a/pack.h b/pack.h
index 01d385903adcba22f46633aec3f7c2fff78776f3..3ab9e3f60c0b0341c3cb37e9026bdc217ba6aa97 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -4,6 +4,8 @@
 #include "object.h"
 #include "csum-file.h"
 
+struct packed_git;
+struct pack_window;
 struct repository;
 
 /*
index c0d7dd93f46dfbc07ea0f17839d4a2d1a9d43622..84a005674d8749407afffc1f6fd670501ce764d4 100644 (file)
@@ -1,4 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "list.h"
 #include "pack.h"
 #include "repository.h"
@@ -6,16 +9,18 @@
 #include "mergesort.h"
 #include "packfile.h"
 #include "delta.h"
-#include "streaming.h"
 #include "hash-lookup.h"
 #include "commit.h"
 #include "object.h"
 #include "tag.h"
+#include "trace.h"
 #include "tree-walk.h"
 #include "tree.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "midx.h"
 #include "commit-graph.h"
+#include "pack-revindex.h"
 #include "promisor-remote.h"
 
 char *odb_pack_name(struct strbuf *buf,
@@ -178,7 +183,7 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
                     */
                    (sizeof(off_t) <= 4))
                        return error("pack too large for current definition of off_t in %s", path);
-               p->crc_offset = 8 + 4 * 256 + nr * hashsz;
+               p->crc_offset = st_add(8 + 4 * 256, st_mult(nr, hashsz));
        }
 
        p->index_version = version;
@@ -373,7 +378,7 @@ void close_object_store(struct raw_object_store *o)
 
 void unlink_pack_path(const char *pack_name, int force_delete)
 {
-       static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor", ".mtimes"};
+       static const char *exts[] = {".idx", ".pack", ".rev", ".keep", ".bitmap", ".promisor", ".mtimes"};
        int i;
        struct strbuf buf = STRBUF_INIT;
        size_t plen;
@@ -745,7 +750,7 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
        p->pack_local = local;
        p->mtime = st.st_mtime;
        if (path_len < the_hash_algo->hexsz ||
-           get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->hash))
+           get_hash_hex(path + path_len - the_hash_algo->hexsz, p->hash))
                hashclr(p->hash);
        return p;
 }
@@ -1008,6 +1013,16 @@ void reprepare_packed_git(struct repository *r)
        struct object_directory *odb;
 
        obj_read_lock();
+
+       /*
+        * Reprepare alt odbs, in case the alternates file was modified
+        * during the course of this process. This only _adds_ odbs to
+        * the linked list, so existing odbs will continue to exist for
+        * the lifetime of the process.
+        */
+       r->objects->loaded_alternates = 0;
+       prepare_alt_odb(r);
+
        for (odb = r->objects->odb; odb; odb = odb->next)
                odb_clear_loose_cache(odb);
 
@@ -1650,22 +1665,6 @@ struct unpack_entry_stack_ent {
        unsigned long size;
 };
 
-static void *read_object(struct repository *r,
-                        const struct object_id *oid,
-                        enum object_type *type,
-                        unsigned long *size)
-{
-       struct object_info oi = OBJECT_INFO_INIT;
-       void *content;
-       oi.typep = type;
-       oi.sizep = size;
-       oi.contentp = &content;
-
-       if (oid_object_info_extended(r, oid, &oi, 0) < 0)
-               return NULL;
-       return content;
-}
-
 void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
                   enum object_type *final_type, unsigned long *final_size)
 {
@@ -1798,6 +1797,8 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
                        uint32_t pos;
                        struct object_id base_oid;
                        if (!(offset_to_pack_pos(p, obj_offset, &pos))) {
+                               struct object_info oi = OBJECT_INFO_INIT;
+
                                nth_packed_object_id(&base_oid, p,
                                                     pack_pos_to_index(p, pos));
                                error("failed to read delta base object %s"
@@ -1805,7 +1806,13 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
                                      oid_to_hex(&base_oid), (uintmax_t)obj_offset,
                                      p->pack_name);
                                mark_bad_packed_object(p, &base_oid);
-                               base = read_object(r, &base_oid, &type, &base_size);
+
+                               oi.typep = &type;
+                               oi.sizep = &base_size;
+                               oi.contentp = &base;
+                               if (oid_object_info_extended(r, &base_oid, &oi, 0) < 0)
+                                       base = NULL;
+
                                external_base = base;
                        }
                }
@@ -1910,10 +1917,10 @@ int nth_packed_object_id(struct object_id *oid,
                return -1;
        index += 4 * 256;
        if (p->index_version == 1) {
-               oidread(oid, index + (hashsz + 4) * n + 4);
+               oidread(oid, index + st_add(st_mult(hashsz + 4, n), 4));
        } else {
                index += 8;
-               oidread(oid, index + hashsz * n);
+               oidread(oid, index + st_mult(hashsz, n));
        }
        return 0;
 }
@@ -1938,14 +1945,15 @@ off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
        const unsigned int hashsz = the_hash_algo->rawsz;
        index += 4 * 256;
        if (p->index_version == 1) {
-               return ntohl(*((uint32_t *)(index + (hashsz + 4) * (size_t)n)));
+               return ntohl(*((uint32_t *)(index + st_mult(hashsz + 4, n))));
        } else {
                uint32_t off;
-               index += 8 + (size_t)p->num_objects * (hashsz + 4);
-               off = ntohl(*((uint32_t *)(index + 4 * n)));
+               index += st_add(8, st_mult(p->num_objects, hashsz + 4));
+               off = ntohl(*((uint32_t *)(index + st_mult(4, n))));
                if (!(off & 0x80000000))
                        return off;
-               index += (size_t)p->num_objects * 4 + (off & 0x7fffffff) * 8;
+               index += st_add(st_mult(p->num_objects, 4),
+                               st_mult(off & 0x7fffffff, 8));
                check_pack_index_ptr(p, index);
                return get_be64(index);
        }
@@ -2144,7 +2152,7 @@ int for_each_object_in_pack(struct packed_git *p,
        int r = 0;
 
        if (flags & FOR_EACH_OBJECT_PACK_ORDER) {
-               if (load_pack_revindex(p))
+               if (load_pack_revindex(the_repository, p))
                        return -1;
        }
 
@@ -2212,8 +2220,8 @@ int for_each_packed_object(each_packed_object_fn cb, void *data,
 }
 
 static int add_promisor_object(const struct object_id *oid,
-                              struct packed_git *pack,
-                              uint32_t pos,
+                              struct packed_git *pack UNUSED,
+                              uint32_t pos UNUSED,
                               void *set_)
 {
        struct oidset *set = set_;
@@ -2271,7 +2279,7 @@ int is_promisor_object(const struct object_id *oid)
        static int promisor_objects_prepared;
 
        if (!promisor_objects_prepared) {
-               if (has_promisor_remote()) {
+               if (repo_has_promisor_remote(the_repository)) {
                        for_each_packed_object(add_promisor_object,
                                               &promisor_objects,
                                               FOR_EACH_OBJECT_PROMISOR_ONLY |
index a3f6723857bf120f611e4f506e85428132f7ccc3..28c8fd3e39a23ae997a66a11dfd0d5d281dbc28f 100644 (file)
@@ -1,13 +1,27 @@
 #ifndef PACKFILE_H
 #define PACKFILE_H
 
-#include "cache.h"
+#include "object.h"
 #include "oidset.h"
 
 /* in object-store.h */
 struct packed_git;
 struct object_info;
 
+struct pack_window {
+       struct pack_window *next;
+       unsigned char *base;
+       off_t offset;
+       size_t len;
+       unsigned int last_used;
+       unsigned int inuse_cnt;
+};
+
+struct pack_entry {
+       off_t offset;
+       struct packed_git *p;
+};
+
 /*
  * Generate the filename to be used for a pack file with checksum "sha1" and
  * extension "ext". The result is written into the strbuf "buf", overwriting
@@ -40,7 +54,7 @@ const char *pack_basename(struct packed_git *p);
 struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
 typedef void each_file_in_pack_dir_fn(const char *full_path, size_t full_path_len,
-                                     const char *file_pach, void *data);
+                                     const char *file_name, void *data);
 void for_each_file_in_pack_dir(const char *objdir,
                               each_file_in_pack_dir_fn fn,
                               void *data);
@@ -65,7 +79,6 @@ struct packed_git *get_all_packs(struct repository *r);
  * for speed.
  */
 unsigned long repo_approximate_object_count(struct repository *r);
-#define approximate_object_count() repo_approximate_object_count(the_repository)
 
 struct packed_git *find_sha1_pack(const unsigned char *sha1,
                                  struct packed_git *packs);
diff --git a/pager.c b/pager.c
index b66bbff2785cb6ed032edb4f0d9cd68f6a841c36..b8822a9381e49487ecb9bf37f1a42f1414eb8ff3 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -1,9 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "editor.h"
+#include "pager.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "alias.h"
 
+int pager_use_color = 1;
+
 #ifndef DEFAULT_PAGER
 #define DEFAULT_PAGER "less"
 #endif
@@ -39,6 +43,7 @@ static void wait_for_pager_signal(int signo)
 }
 
 static int core_pager_config(const char *var, const char *value,
+                            const struct config_context *ctx UNUSED,
                             void *data UNUSED)
 {
        if (!strcmp(var, "core.pager"))
@@ -224,7 +229,9 @@ struct pager_command_config_data {
        char *value;
 };
 
-static int pager_command_config(const char *var, const char *value, void *vdata)
+static int pager_command_config(const char *var, const char *value,
+                               const struct config_context *ctx UNUSED,
+                               void *vdata)
 {
        struct pager_command_config_data *data = vdata;
        const char *cmd;
diff --git a/pager.h b/pager.h
new file mode 100644 (file)
index 0000000..b774330
--- /dev/null
+++ b/pager.h
@@ -0,0 +1,17 @@
+#ifndef PAGER_H
+#define PAGER_H
+
+struct child_process;
+
+const char *git_pager(int stdout_is_tty);
+void setup_pager(void);
+int pager_in_use(void);
+int term_columns(void);
+void term_clear_line(void);
+int decimal_width(uintmax_t);
+int check_pager_config(const char *cmd);
+void prepare_pager_args(struct child_process *, const char *pager);
+
+extern int pager_use_color;
+
+#endif /* PAGER_H */
index 4f6819f2406ea8f90651b7769cbaf1758ce4c098..b5a714c7111bda0d179077ef1614bbe5ce1570b5 100644 (file)
@@ -1,12 +1,17 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "entry.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "parallel-checkout.h"
 #include "pkt-line.h"
 #include "progress.h"
+#include "read-cache-ll.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "streaming.h"
+#include "symlinks.h"
 #include "thread-utils.h"
 #include "trace2.h"
 
index d346dbe2100ddceb53f8ce149ad4ed794b772f53..bdc7fae49719dfef060739854ae7491045afb6f2 100644 (file)
@@ -1,9 +1,12 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
 #include "branch.h"
-#include "cache.h"
 #include "commit.h"
 #include "color.h"
+#include "date.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "oid-array.h"
@@ -91,7 +94,7 @@ int parse_opt_commits(const struct option *opt, const char *arg, int unset)
 
        if (!arg)
                return -1;
-       if (get_oid(arg, &oid))
+       if (repo_get_oid(the_repository, arg, &oid))
                return error("malformed object name %s", arg);
        commit = lookup_commit_reference(the_repository, &oid);
        if (!commit)
@@ -110,7 +113,7 @@ int parse_opt_commit(const struct option *opt, const char *arg, int unset)
 
        if (!arg)
                return -1;
-       if (get_oid(arg, &oid))
+       if (repo_get_oid(the_repository, arg, &oid))
                return error("malformed object name %s", arg);
        commit = lookup_commit_reference(the_repository, &oid);
        if (!commit)
@@ -129,7 +132,7 @@ int parse_opt_object_name(const struct option *opt, const char *arg, int unset)
        }
        if (!arg)
                return -1;
-       if (get_oid(arg, &oid))
+       if (repo_get_oid(the_repository, arg, &oid))
                return error(_("malformed object name '%s'"), arg);
        oid_array_append(opt->value, &oid);
        return 0;
@@ -146,7 +149,7 @@ int parse_opt_object_id(const struct option *opt, const char *arg, int unset)
        }
        if (!arg)
                return -1;
-       if (get_oid(arg, &oid))
+       if (repo_get_oid(the_repository, arg, &oid))
                return error(_("malformed object name '%s'"), arg);
        *target = oid;
        return 0;
@@ -208,24 +211,27 @@ int parse_opt_string_list(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
-int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)
+int parse_opt_strvec(const struct option *opt, const char *arg, int unset)
 {
+       struct strvec *v = opt->value;
+
+       if (unset) {
+               strvec_clear(v);
+               return 0;
+       }
+
+       if (!arg)
+               return -1;
+
+       strvec_push(v, arg);
        return 0;
 }
 
-/**
- * Report that the option is unknown, so that other code can handle
- * it. This can be used as a callback together with
- * OPTION_LOWLEVEL_CALLBACK to allow an option to be documented in the
- * "-h" output even if it's not being handled directly by
- * parse_options().
- */
-enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
-                                          const struct option *opt,
-                                          const char *arg, int unset)
+int parse_opt_noop_cb(const struct option *opt UNUSED,
+                     const char *arg UNUSED,
+                     int unset UNUSED)
 {
-       BUG_ON_OPT_ARG(arg);
-       return PARSE_OPT_UNKNOWN;
+       return 0;
 }
 
 /**
index a1ec932f0f9ff32e82223735dfd927473051fd3b..4ce2b7ca1689eeb374fa2e122e1cd2436421d405 100644 (file)
@@ -1,9 +1,10 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
-#include "cache.h"
-#include "config.h"
-#include "commit.h"
-#include "color.h"
+#include "abspath.h"
+#include "parse.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "string-list.h"
 #include "utf8.h"
 
 static int disallow_abbreviated_options;
@@ -59,50 +60,18 @@ static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p,
        return 0;
 }
 
-static void fix_filename(const char *prefix, const char **file)
+static void fix_filename(const char *prefix, char **file)
 {
-       if (!file || !*file || !prefix || is_absolute_path(*file)
-           || !strcmp("-", *file))
-               return;
-       *file = prefix_filename(prefix, *file);
-}
-
-static enum parse_opt_result opt_command_mode_error(
-       const struct option *opt,
-       const struct option *all_opts,
-       enum opt_parsed flags)
-{
-       const struct option *that;
-       struct strbuf that_name = STRBUF_INIT;
-
-       /*
-        * Find the other option that was used to set the variable
-        * already, and report that this is not compatible with it.
-        */
-       for (that = all_opts; that->type != OPTION_END; that++) {
-               if (that == opt ||
-                   !(that->flags & PARSE_OPT_CMDMODE) ||
-                   that->value != opt->value ||
-                   that->defval != *(int *)opt->value)
-                       continue;
-
-               if (that->long_name)
-                       strbuf_addf(&that_name, "--%s", that->long_name);
-               else
-                       strbuf_addf(&that_name, "-%c", that->short_name);
-               error(_("%s is incompatible with %s"),
-                     optname(opt, flags), that_name.buf);
-               strbuf_release(&that_name);
-               return PARSE_OPT_ERROR;
-       }
-       return error(_("%s : incompatible with something else"),
-                    optname(opt, flags));
+       if (!file || !*file)
+               ; /* leave as NULL */
+       else
+               *file = prefix_filename_except_for_dash(prefix, *file);
 }
 
-static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
-                                      const struct option *opt,
-                                      const struct option *all_opts,
-                                      enum opt_parsed flags)
+static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+                                         const struct option *opt,
+                                         enum opt_parsed flags,
+                                         const char **argp)
 {
        const char *s, *arg;
        const int unset = flags & OPT_UNSET;
@@ -115,14 +84,6 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
        if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
                return error(_("%s takes no value"), optname(opt, flags));
 
-       /*
-        * Giving the same mode option twice, although unnecessary,
-        * is not a grave error, so let it pass.
-        */
-       if ((opt->flags & PARSE_OPT_CMDMODE) &&
-           *(int *)opt->value && *(int *)opt->value != opt->defval)
-               return opt_command_mode_error(opt, all_opts, flags);
-
        switch (opt->type) {
        case OPTION_LOWLEVEL_CALLBACK:
                return opt->ll_callback(p, opt, NULL, unset);
@@ -177,7 +138,7 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
                        err = get_arg(p, opt, flags, (const char **)opt->value);
 
                if (!err)
-                       fix_filename(p->prefix, (const char **)opt->value);
+                       fix_filename(p->prefix, (char **)opt->value);
                return err;
 
        case OPTION_CALLBACK:
@@ -197,6 +158,8 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
                        p_unset = 0;
                        p_arg = arg;
                }
+               if (opt->flags & PARSE_OPT_CMDMODE)
+                       *argp = p_arg;
                if (opt->callback)
                        return (*opt->callback)(opt, p_arg, p_unset) ? (-1) : 0;
                else
@@ -244,16 +207,92 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
        }
 }
 
+struct parse_opt_cmdmode_list {
+       int value, *value_ptr;
+       const struct option *opt;
+       const char *arg;
+       enum opt_parsed flags;
+       struct parse_opt_cmdmode_list *next;
+};
+
+static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,
+                              const struct option *opts)
+{
+       ctx->cmdmode_list = NULL;
+
+       for (; opts->type != OPTION_END; opts++) {
+               struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list;
+               int *value_ptr = opts->value;
+
+               if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr)
+                       continue;
+
+               while (elem && elem->value_ptr != value_ptr)
+                       elem = elem->next;
+               if (elem)
+                       continue;
+
+               CALLOC_ARRAY(elem, 1);
+               elem->value_ptr = value_ptr;
+               elem->value = *value_ptr;
+               elem->next = ctx->cmdmode_list;
+               ctx->cmdmode_list = elem;
+       }
+}
+
+static char *optnamearg(const struct option *opt, const char *arg,
+                       enum opt_parsed flags)
+{
+       if (flags & OPT_SHORT)
+               return xstrfmt("-%c%s", opt->short_name, arg ? arg : "");
+       return xstrfmt("--%s%s%s%s", flags & OPT_UNSET ? "no-" : "",
+                      opt->long_name, arg ? "=" : "", arg ? arg : "");
+}
+
+static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
+                                      const struct option *opt,
+                                      enum opt_parsed flags)
+{
+       const char *arg = NULL;
+       enum parse_opt_result result = do_get_value(p, opt, flags, &arg);
+       struct parse_opt_cmdmode_list *elem = p->cmdmode_list;
+       char *opt_name, *other_opt_name;
+
+       for (; elem; elem = elem->next) {
+               if (*elem->value_ptr == elem->value)
+                       continue;
+
+               if (elem->opt &&
+                   (elem->opt->flags | opt->flags) & PARSE_OPT_CMDMODE)
+                       break;
+
+               elem->opt = opt;
+               elem->arg = arg;
+               elem->flags = flags;
+               elem->value = *elem->value_ptr;
+       }
+
+       if (result || !elem)
+               return result;
+
+       opt_name = optnamearg(opt, arg, flags);
+       other_opt_name = optnamearg(elem->opt, elem->arg, elem->flags);
+       error(_("options '%s' and '%s' cannot be used together"),
+             opt_name, other_opt_name);
+       free(opt_name);
+       free(other_opt_name);
+       return -1;
+}
+
 static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p,
                                             const struct option *options)
 {
-       const struct option *all_opts = options;
        const struct option *numopt = NULL;
 
        for (; options->type != OPTION_END; options++) {
                if (options->short_name == *p->opt) {
                        p->opt = p->opt[1] ? p->opt + 1 : NULL;
-                       return get_value(p, options, all_opts, OPT_SHORT);
+                       return get_value(p, options, OPT_SHORT);
                }
 
                /*
@@ -315,7 +354,6 @@ static enum parse_opt_result parse_long_opt(
        struct parse_opt_ctx_t *p, const char *arg,
        const struct option *options)
 {
-       const struct option *all_opts = options;
        const char *arg_end = strchrnul(arg, '=');
        const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
        enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
@@ -384,7 +422,7 @@ is_abbreviated:
                                continue;
                        p->opt = rest + 1;
                }
-               return get_value(p, options, all_opts, flags ^ opt_flags);
+               return get_value(p, options, flags ^ opt_flags);
        }
 
        if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
@@ -402,7 +440,7 @@ is_abbreviated:
                return PARSE_OPT_HELP;
        }
        if (abbrev_option)
-               return get_value(p, abbrev_option, all_opts, abbrev_flags);
+               return get_value(p, abbrev_option, abbrev_flags);
        return PARSE_OPT_UNKNOWN;
 }
 
@@ -410,13 +448,11 @@ static enum parse_opt_result parse_nodash_opt(struct parse_opt_ctx_t *p,
                                              const char *arg,
                                              const struct option *options)
 {
-       const struct option *all_opts = options;
-
        for (; options->type != OPTION_END; options++) {
                if (!(options->flags & PARSE_OPT_NODASH))
                        continue;
                if (options->short_name == arg[0] && arg[1] == '\0')
-                       return get_value(p, options, all_opts, OPT_SHORT);
+                       return get_value(p, options, OPT_SHORT);
        }
        return PARSE_OPT_ERROR;
 }
@@ -478,6 +514,9 @@ static void parse_options_check(const struct option *opts)
                     opts->long_name))
                        optbug(opts, "uses feature "
                               "not supported for dashless options");
+               if (opts->type == OPTION_SET_INT && !opts->defval &&
+                   opts->long_name && !(opts->flags & PARSE_OPT_NONEG))
+                       optbug(opts, "OPTION_SET_INT 0 should not be negatable");
                switch (opts->type) {
                case OPTION_COUNTUP:
                case OPTION_BIT:
@@ -568,6 +607,7 @@ static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
            (flags & PARSE_OPT_KEEP_ARGV0))
                BUG("Can't keep argv0 if you don't have it");
        parse_options_check(options);
+       build_cmdmode_list(ctx, options);
 }
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
@@ -702,8 +742,7 @@ static struct option *preprocess_options(struct parse_opt_ctx_t *ctx,
        if (!nr_aliases)
                return NULL;
 
-       ALLOC_ARRAY(newopt, nr + 1);
-       COPY_ARRAY(newopt, options, nr + 1);
+       DUP_ARRAY(newopt, options, nr + 1);
 
        /* each alias has two string pointers and NULL */
        CALLOC_ARRAY(ctx->alias_groups, 3 * (nr_aliases + 1));
@@ -889,13 +928,18 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,
                        continue;
                }
 
-               if (!arg[2] /* "--" */ ||
-                   !strcmp(arg + 2, "end-of-options")) {
+               if (!arg[2] /* "--" */) {
                        if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
                                ctx->argc--;
                                ctx->argv++;
                        }
                        break;
+               } else if (!strcmp(arg + 2, "end-of-options")) {
+                       if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) {
+                               ctx->argc--;
+                               ctx->argv++;
+                       }
+                       break;
                }
 
                if (internal_help && !strcmp(arg + 2, "help-all"))
@@ -1001,6 +1045,11 @@ int parse_options(int argc, const char **argv,
        precompose_argv_prefix(argc, argv, NULL);
        free_preprocessed_options(real_options);
        free(ctx.alias_groups);
+       for (struct parse_opt_cmdmode_list *elem = ctx.cmdmode_list; elem;) {
+               struct parse_opt_cmdmode_list *next = elem->next;
+               free(elem);
+               elem = next;
+       }
        return parse_options_end(&ctx);
 }
 
@@ -1019,14 +1068,37 @@ static int usage_argh(const struct option *opts, FILE *outfile)
        return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
 }
 
-#define USAGE_OPTS_WIDTH 24
-#define USAGE_GAP         2
+static int usage_indent(FILE *outfile)
+{
+       return fprintf(outfile, "    ");
+}
+
+#define USAGE_OPTS_WIDTH 26
+
+static void usage_padding(FILE *outfile, size_t pos)
+{
+       if (pos < USAGE_OPTS_WIDTH)
+               fprintf(outfile, "%*s", USAGE_OPTS_WIDTH - (int)pos, "");
+       else
+               fprintf(outfile, "\n%*s", USAGE_OPTS_WIDTH, "");
+}
+
+static const struct option *find_option_by_long_name(const struct option *opts,
+                                                    const char *long_name)
+{
+       for (; opts->type != OPTION_END; opts++) {
+               if (opts->long_name && !strcmp(opts->long_name, long_name))
+                       return opts;
+       }
+       return NULL;
+}
 
 static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                                                         const char * const *usagestr,
                                                         const struct option *opts,
                                                         int full, int err)
 {
+       const struct option *all_opts = opts;
        FILE *outfile = err ? stderr : stdout;
        int need_newline;
 
@@ -1107,7 +1179,8 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
 
        for (; opts->type != OPTION_END; opts++) {
                size_t pos;
-               int pad;
+               const char *cp, *np;
+               const char *positive_name = NULL;
 
                if (opts->type == OPTION_SUBCOMMAND)
                        continue;
@@ -1126,7 +1199,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
                        need_newline = 0;
                }
 
-               pos = fprintf(outfile, "    ");
+               pos = usage_indent(outfile);
                if (opts->short_name) {
                        if (opts->flags & PARSE_OPT_NODASH)
                                pos += fprintf(outfile, "%c", opts->short_name);
@@ -1135,8 +1208,15 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
                }
                if (opts->long_name && opts->short_name)
                        pos += fprintf(outfile, ", ");
-               if (opts->long_name)
-                       pos += fprintf(outfile, "--%s", opts->long_name);
+               if (opts->long_name) {
+                       const char *long_name = opts->long_name;
+                       if ((opts->flags & PARSE_OPT_NONEG) ||
+                           skip_prefix(long_name, "no-", &positive_name))
+                               pos += fprintf(outfile, "--%s", long_name);
+                       else
+                               pos += fprintf(outfile, "--[no-]%s", long_name);
+               }
+
                if (opts->type == OPTION_NUMBER)
                        pos += utf8_fprintf(outfile, _("-NUM"));
 
@@ -1144,19 +1224,32 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
                    !(opts->flags & PARSE_OPT_NOARG))
                        pos += usage_argh(opts, outfile);
 
-               if (pos <= USAGE_OPTS_WIDTH)
-                       pad = USAGE_OPTS_WIDTH - pos;
-               else {
-                       fputc('\n', outfile);
-                       pad = USAGE_OPTS_WIDTH;
-               }
                if (opts->type == OPTION_ALIAS) {
-                       fprintf(outfile, "%*s", pad + USAGE_GAP, "");
+                       usage_padding(outfile, pos);
                        fprintf_ln(outfile, _("alias of --%s"),
                                   (const char *)opts->value);
                        continue;
                }
-               fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help));
+
+               for (cp = opts->help ? _(opts->help) : ""; *cp; cp = np) {
+                       np = strchrnul(cp, '\n');
+                       if (*np)
+                               np++;
+                       usage_padding(outfile, pos);
+                       fwrite(cp, 1, np - cp, outfile);
+                       pos = 0;
+               }
+               fputc('\n', outfile);
+
+               if (positive_name) {
+                       if (find_option_by_long_name(all_opts, positive_name))
+                               continue;
+                       pos = usage_indent(outfile);
+                       pos += fprintf(outfile, "--%s", positive_name);
+                       usage_padding(outfile, pos);
+                       fprintf_ln(outfile, _("opposite of --no-%s"),
+                                  positive_name);
+               }
        }
        fputc('\n', outfile);
 
index b6ef86e0d15e3dc36364977dd3515fa118dee165..bd62e20268d01d0e9d7afbcb300c440bb3adaa9f 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef PARSE_OPTIONS_H
 #define PARSE_OPTIONS_H
 
+#include "gettext.h"
+
 /**
  * Refer to Documentation/technical/api-parse-options.txt for the API doc.
  */
@@ -158,71 +160,211 @@ struct option {
        parse_opt_subcommand_fn *subcommand_fn;
 };
 
-#define OPT_BIT_F(s, l, v, h, b, f) { OPTION_BIT, (s), (l), (v), NULL, (h), \
-                                     PARSE_OPT_NOARG|(f), NULL, (b) }
-#define OPT_COUNTUP_F(s, l, v, h, f) { OPTION_COUNTUP, (s), (l), (v), NULL, \
-                                      (h), PARSE_OPT_NOARG|(f) }
-#define OPT_SET_INT_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
-                                         (h), PARSE_OPT_NOARG | (f), NULL, (i) }
+#define OPT_BIT_F(s, l, v, h, b, f) { \
+       .type = OPTION_BIT, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_NOARG|(f), \
+       .callback = NULL, \
+       .defval = (b), \
+}
+#define OPT_COUNTUP_F(s, l, v, h, f) { \
+       .type = OPTION_COUNTUP, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_NOARG|(f), \
+}
+#define OPT_SET_INT_F(s, l, v, h, i, f) { \
+       .type = OPTION_SET_INT, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_NOARG | (f), \
+       .defval = (i), \
+}
 #define OPT_BOOL_F(s, l, v, h, f)   OPT_SET_INT_F(s, l, v, h, 1, f)
-#define OPT_CALLBACK_F(s, l, v, a, h, f, cb)                   \
-       { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), (cb) }
-#define OPT_STRING_F(s, l, v, a, h, f)   { OPTION_STRING,  (s), (l), (v), (a), (h), (f) }
-#define OPT_INTEGER_F(s, l, v, h, f)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
+#define OPT_CALLBACK_F(s, l, v, a, h, f, cb) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = (a), \
+       .help = (h), \
+       .flags = (f), \
+       .callback = (cb), \
+}
+#define OPT_STRING_F(s, l, v, a, h, f) { \
+       .type = OPTION_STRING, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = (a), \
+       .help = (h), \
+       .flags = (f), \
+}
+#define OPT_INTEGER_F(s, l, v, h, f) { \
+       .type = OPTION_INTEGER, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = N_("n"), \
+       .help = (h), \
+       .flags = (f), \
+}
 
-#define OPT_END()                   { OPTION_END }
-#define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
+#define OPT_END() { \
+       .type = OPTION_END, \
+}
+#define OPT_GROUP(h) { \
+       .type = OPTION_GROUP, \
+       .help = (h), \
+}
 #define OPT_BIT(s, l, v, h, b)      OPT_BIT_F(s, l, v, h, b, 0)
-#define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
-                                           PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, \
-                                           (set), NULL, (clear) }
-#define OPT_NEGBIT(s, l, v, h, b)   { OPTION_NEGBIT, (s), (l), (v), NULL, \
-                                     (h), PARSE_OPT_NOARG, NULL, (b) }
+#define OPT_BITOP(s, l, v, h, set, clear) { \
+       .type = OPTION_BITOP, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_NOARG|PARSE_OPT_NONEG, \
+       .defval = (set), \
+       .extra = (clear), \
+}
+#define OPT_NEGBIT(s, l, v, h, b) { \
+       .type = OPTION_NEGBIT, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_NOARG, \
+       .defval = (b), \
+}
 #define OPT_COUNTUP(s, l, v, h)     OPT_COUNTUP_F(s, l, v, h, 0)
 #define OPT_SET_INT(s, l, v, h, i)  OPT_SET_INT_F(s, l, v, h, i, 0)
 #define OPT_BOOL(s, l, v, h)        OPT_BOOL_F(s, l, v, h, 0)
-#define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \
-                                     (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
-#define OPT_CMDMODE_F(s, l, v, h, i, f)  { OPTION_SET_INT, (s), (l), (v), NULL, \
-                                     (h), PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), NULL, (i) }
+#define OPT_HIDDEN_BOOL(s, l, v, h) { \
+       .type = OPTION_SET_INT, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \
+       .defval = 1, \
+}
+#define OPT_CMDMODE_F(s, l, v, h, i, f) { \
+       .type = OPTION_SET_INT, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \
+       .defval = (i), \
+}
 #define OPT_CMDMODE(s, l, v, h, i)  OPT_CMDMODE_F(s, l, v, h, i, 0)
 
 #define OPT_INTEGER(s, l, v, h)     OPT_INTEGER_F(s, l, v, h, 0)
-#define OPT_MAGNITUDE(s, l, v, h)   { OPTION_MAGNITUDE, (s), (l), (v), \
-                                     N_("n"), (h), PARSE_OPT_NONEG }
+#define OPT_MAGNITUDE(s, l, v, h) { \
+       .type = OPTION_MAGNITUDE, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = N_("n"), \
+       .help = (h), \
+       .flags = PARSE_OPT_NONEG, \
+}
 #define OPT_STRING(s, l, v, a, h)   OPT_STRING_F(s, l, v, a, h, 0)
-#define OPT_STRING_LIST(s, l, v, a, h) \
-                                   { OPTION_CALLBACK, (s), (l), (v), (a), \
-                                     (h), 0, &parse_opt_string_list }
-#define OPT_UYN(s, l, v, h)         { OPTION_CALLBACK, (s), (l), (v), NULL, \
-                                     (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
-#define OPT_EXPIRY_DATE(s, l, v, h) \
-       { OPTION_CALLBACK, (s), (l), (v), N_("expiry-date"),(h), 0,     \
-         parse_opt_expiry_date_cb }
-#define OPT_CALLBACK(s, l, v, a, h, f) OPT_CALLBACK_F(s, l, v, a, h, 0, f)
-#define OPT_NUMBER_CALLBACK(v, h, f) \
-       { OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
-         PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
-#define OPT_FILENAME(s, l, v, h)    { OPTION_FILENAME, (s), (l), (v), \
-                                      N_("file"), (h) }
-#define OPT_COLOR_FLAG(s, l, v, h) \
-       { OPTION_CALLBACK, (s), (l), (v), N_("when"), (h), PARSE_OPT_OPTARG, \
-               parse_opt_color_flag_cb, (intptr_t)"always" }
-
-#define OPT_NOOP_NOARG(s, l) \
-       { OPTION_CALLBACK, (s), (l), NULL, NULL, \
-         N_("no-op (backward compatibility)"),         \
-         PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
-
-#define OPT_ALIAS(s, l, source_long_name) \
-       { OPTION_ALIAS, (s), (l), (source_long_name) }
+#define OPT_STRING_LIST(s, l, v, a, h) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = (a), \
+       .help = (h), \
+       .callback = &parse_opt_string_list, \
+}
+#define OPT_STRVEC(s, l, v, a, h) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = (a), \
+       .help = (h), \
+       .callback = &parse_opt_strvec, \
+}
+#define OPT_UYN(s, l, v, h) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_NOARG, \
+       .callback = &parse_opt_tertiary, \
+}
+#define OPT_EXPIRY_DATE(s, l, v, h) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = N_("expiry-date"), \
+       .help = (h), \
+       .callback = parse_opt_expiry_date_cb, \
+}
+#define OPT_CALLBACK(s, l, v, a, h, cb) OPT_CALLBACK_F(s, l, v, a, h, 0, cb)
+#define OPT_NUMBER_CALLBACK(v, h, cb) { \
+       .type = OPTION_NUMBER, \
+       .value = (v), \
+       .help = (h), \
+       .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, \
+       .callback = (cb), \
+}
+#define OPT_FILENAME(s, l, v, h) { \
+       .type = OPTION_FILENAME, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = N_("file"), \
+       .help = (h), \
+}
+#define OPT_COLOR_FLAG(s, l, v, h) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = N_("when"), \
+       .help = (h), \
+       .flags = PARSE_OPT_OPTARG, \
+       .callback = parse_opt_color_flag_cb, \
+       .defval = (intptr_t)"always", \
+}
+
+#define OPT_NOOP_NOARG(s, l) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .help = N_("no-op (backward compatibility)"), \
+       .flags = PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, \
+       .callback = parse_opt_noop_cb, \
+}
+
+#define OPT_ALIAS(s, l, source_long_name) { \
+       .type = OPTION_ALIAS, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (source_long_name), \
+}
 
 #define OPT_SUBCOMMAND_F(l, v, fn, f) { \
        .type = OPTION_SUBCOMMAND, \
        .long_name = (l), \
        .value = (v), \
        .flags = (f), \
-       .subcommand_fn = (fn) }
+       .subcommand_fn = (fn), \
+}
 #define OPT_SUBCOMMAND(l, v, fn)    OPT_SUBCOMMAND_F((l), (v), (fn), 0)
 
 /*
@@ -303,6 +445,8 @@ static inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,
 
 /*----- incremental advanced APIs -----*/
 
+struct parse_opt_cmdmode_list;
+
 /*
  * It's okay for the caller to consume argv/argc in the usual way.
  * Other fields of that structure are private to parse-options and should not
@@ -317,7 +461,7 @@ struct parse_opt_ctx_t {
        unsigned has_subcommands;
        const char *prefix;
        const char **alias_groups; /* must be in groups of 3 elements! */
-       struct option *updated_options;
+       struct parse_opt_cmdmode_list *cmdmode_list;
 };
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
@@ -347,10 +491,8 @@ int parse_opt_commits(const struct option *, const char *, int);
 int parse_opt_commit(const struct option *, const char *, int);
 int parse_opt_tertiary(const struct option *, const char *, int);
 int parse_opt_string_list(const struct option *, const char *, int);
+int parse_opt_strvec(const struct option *, const char *, int);
 int parse_opt_noop_cb(const struct option *, const char *, int);
-enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
-                                          const struct option *,
-                                          const char *, int);
 int parse_opt_passthru(const struct option *, const char *, int);
 int parse_opt_passthru_argv(const struct option *, const char *, int);
 /* value is enum branch_track* */
@@ -358,30 +500,80 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
 
 #define OPT__VERBOSE(var, h)  OPT_COUNTUP('v', "verbose", (var), (h))
 #define OPT__QUIET(var, h)    OPT_COUNTUP('q', "quiet",   (var), (h))
-#define OPT__VERBOSITY(var) \
-       { OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \
-         PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
-       { OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \
-         PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
+#define OPT__VERBOSITY(var) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = 'v', \
+       .long_name = "verbose", \
+       .value = (var), \
+       .help = N_("be more verbose"), \
+       .flags = PARSE_OPT_NOARG, \
+       .callback = &parse_opt_verbosity_cb, \
+}, { \
+       .type = OPTION_CALLBACK, \
+       .short_name = 'q', \
+       .long_name = "quiet", \
+       .value = (var), \
+       .help = N_("be more quiet"), \
+       .flags = PARSE_OPT_NOARG, \
+       .callback = &parse_opt_verbosity_cb, \
+}
 #define OPT__DRY_RUN(var, h)  OPT_BOOL('n', "dry-run", (var), (h))
 #define OPT__FORCE(var, h, f) OPT_COUNTUP_F('f', "force",   (var), (h), (f))
-#define OPT__ABBREV(var)  \
-       { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
-         N_("use <n> digits to display object names"), \
-         PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+#define OPT__ABBREV(var) { \
+       .type = OPTION_CALLBACK, \
+       .long_name = "abbrev", \
+       .value = (var), \
+       .argh = N_("n"), \
+       .help = N_("use <n> digits to display object names"), \
+       .flags = PARSE_OPT_OPTARG, \
+       .callback = &parse_opt_abbrev_cb, \
+}
+#define OPT__SUPER_PREFIX(var) \
+       OPT_STRING_F(0, "super-prefix", (var), N_("prefix"), \
+               N_("prefixed path to initial superproject"), PARSE_OPT_HIDDEN)
+
 #define OPT__COLOR(var, h) \
        OPT_COLOR_FLAG(0, "color", (var), (h))
-#define OPT_COLUMN(s, l, v, h) \
-       { OPTION_CALLBACK, (s), (l), (v), N_("style"), (h), PARSE_OPT_OPTARG, parseopt_column_callback }
-#define OPT_PASSTHRU(s, l, v, a, h, f) \
-       { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru }
-#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) \
-       { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru_argv }
-#define _OPT_CONTAINS_OR_WITH(name, variable, help, flag) \
-       { OPTION_CALLBACK, 0, name, (variable), N_("commit"), (help), \
-         PARSE_OPT_LASTARG_DEFAULT | flag, \
-         parse_opt_commits, (intptr_t) "HEAD" \
-       }
+#define OPT_COLUMN(s, l, v, h) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = N_("style"), \
+       .help = (h), \
+       .flags = PARSE_OPT_OPTARG, \
+       .callback = parseopt_column_callback, \
+}
+#define OPT_PASSTHRU(s, l, v, a, h, f) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = (a), \
+       .help = (h), \
+       .flags = (f), \
+       .callback = parse_opt_passthru, \
+}
+#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) { \
+       .type = OPTION_CALLBACK, \
+       .short_name = (s), \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = (a), \
+       .help = (h), \
+       .flags = (f), \
+       .callback = parse_opt_passthru_argv, \
+}
+#define _OPT_CONTAINS_OR_WITH(l, v, h, f) { \
+       .type = OPTION_CALLBACK, \
+       .long_name = (l), \
+       .value = (v), \
+       .argh = N_("commit"), \
+       .help = (h), \
+       .flags = PARSE_OPT_LASTARG_DEFAULT | (f), \
+       .callback = parse_opt_commits, \
+       .defval = (intptr_t) "HEAD", \
+}
 #define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG)
 #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
 #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
@@ -391,4 +583,10 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
 #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
 #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
 
+#define OPT_IPVERSION(v) \
+       OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \
+               TRANSPORT_FAMILY_IPV4, PARSE_OPT_NONEG), \
+       OPT_SET_INT_F('6', "ipv6", (v), N_("use IPv6 addresses only"), \
+               TRANSPORT_FAMILY_IPV6, PARSE_OPT_NONEG)
+
 #endif
diff --git a/parse.c b/parse.c
new file mode 100644 (file)
index 0000000..42d691a
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,182 @@
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "parse.h"
+
+static uintmax_t get_unit_factor(const char *end)
+{
+       if (!*end)
+               return 1;
+       else if (!strcasecmp(end, "k"))
+               return 1024;
+       else if (!strcasecmp(end, "m"))
+               return 1024 * 1024;
+       else if (!strcasecmp(end, "g"))
+               return 1024 * 1024 * 1024;
+       return 0;
+}
+
+int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
+{
+       if (value && *value) {
+               char *end;
+               intmax_t val;
+               intmax_t factor;
+
+               if (max < 0)
+                       BUG("max must be a positive integer");
+
+               errno = 0;
+               val = strtoimax(value, &end, 0);
+               if (errno == ERANGE)
+                       return 0;
+               if (end == value) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               factor = get_unit_factor(end);
+               if (!factor) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               if ((val < 0 && -max / factor > val) ||
+                   (val > 0 && max / factor < val)) {
+                       errno = ERANGE;
+                       return 0;
+               }
+               val *= factor;
+               *ret = val;
+               return 1;
+       }
+       errno = EINVAL;
+       return 0;
+}
+
+static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
+{
+       if (value && *value) {
+               char *end;
+               uintmax_t val;
+               uintmax_t factor;
+
+               /* negative values would be accepted by strtoumax */
+               if (strchr(value, '-')) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               errno = 0;
+               val = strtoumax(value, &end, 0);
+               if (errno == ERANGE)
+                       return 0;
+               if (end == value) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               factor = get_unit_factor(end);
+               if (!factor) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               if (unsigned_mult_overflows(factor, val) ||
+                   factor * val > max) {
+                       errno = ERANGE;
+                       return 0;
+               }
+               val *= factor;
+               *ret = val;
+               return 1;
+       }
+       errno = EINVAL;
+       return 0;
+}
+
+int git_parse_int(const char *value, int *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_int64(const char *value, int64_t *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_ulong(const char *value, unsigned long *ret)
+{
+       uintmax_t tmp;
+       if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_ssize_t(const char *value, ssize_t *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_maybe_bool_text(const char *value)
+{
+       if (!value)
+               return 1;
+       if (!*value)
+               return 0;
+       if (!strcasecmp(value, "true")
+           || !strcasecmp(value, "yes")
+           || !strcasecmp(value, "on"))
+               return 1;
+       if (!strcasecmp(value, "false")
+           || !strcasecmp(value, "no")
+           || !strcasecmp(value, "off"))
+               return 0;
+       return -1;
+}
+
+int git_parse_maybe_bool(const char *value)
+{
+       int v = git_parse_maybe_bool_text(value);
+       if (0 <= v)
+               return v;
+       if (git_parse_int(value, &v))
+               return !!v;
+       return -1;
+}
+
+/*
+ * Parse environment variable 'k' as a boolean (in various
+ * possible spellings); if missing, use the default value 'def'.
+ */
+int git_env_bool(const char *k, int def)
+{
+       const char *v = getenv(k);
+       int val;
+       if (!v)
+               return def;
+       val = git_parse_maybe_bool(v);
+       if (val < 0)
+               die(_("bad boolean environment value '%s' for '%s'"),
+                   v, k);
+       return val;
+}
+
+/*
+ * Parse environment variable 'k' as ulong with possibly a unit
+ * suffix; if missing, use the default value 'val'.
+ */
+unsigned long git_env_ulong(const char *k, unsigned long val)
+{
+       const char *v = getenv(k);
+       if (v && !git_parse_ulong(v, &val))
+               die(_("failed to parse %s"), k);
+       return val;
+}
diff --git a/parse.h b/parse.h
new file mode 100644 (file)
index 0000000..07d2193
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,20 @@
+#ifndef PARSE_H
+#define PARSE_H
+
+int git_parse_signed(const char *value, intmax_t *ret, intmax_t max);
+int git_parse_ssize_t(const char *, ssize_t *);
+int git_parse_ulong(const char *, unsigned long *);
+int git_parse_int(const char *value, int *ret);
+int git_parse_int64(const char *value, int64_t *ret);
+
+/**
+ * Same as `git_config_bool`, except that it returns -1 on error rather
+ * than dying.
+ */
+int git_parse_maybe_bool(const char *);
+int git_parse_maybe_bool_text(const char *value);
+
+int git_env_bool(const char *, int);
+unsigned long git_env_ulong(const char *, unsigned long);
+
+#endif /* PARSE_H */
index 46c6a8f3eab519c88c0732677c9d09703f5882a3..a5683b462c6e7692ceff3218213b9747a9b6cc02 100644 (file)
@@ -1,7 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diff.h"
 #include "commit.h"
-#include "hash-lookup.h"
+#include "hash.h"
+#include "hex.h"
 #include "patch-ids.h"
 
 static int patch_id_defined(struct commit *commit)
@@ -11,7 +12,7 @@ static int patch_id_defined(struct commit *commit)
 }
 
 int commit_patch_id(struct commit *commit, struct diff_options *options,
-                   struct object_id *oid, int diff_header_only, int stable)
+                   struct object_id *oid, int diff_header_only)
 {
        if (!patch_id_defined(commit))
                return -1;
@@ -22,7 +23,7 @@ int commit_patch_id(struct commit *commit, struct diff_options *options,
        else
                diff_root_tree_oid(&commit->object.oid, "", options);
        diffcore_std(options);
-       return diff_flush_patch_id(options, oid, diff_header_only, stable);
+       return diff_flush_patch_id(options, oid, diff_header_only);
 }
 
 /*
@@ -48,11 +49,11 @@ static int patch_id_neq(const void *cmpfn_data,
        b = container_of(entry_or_key, struct patch_id, ent);
 
        if (is_null_oid(&a->patch_id) &&
-           commit_patch_id(a->commit, opt, &a->patch_id, 0, 0))
+           commit_patch_id(a->commit, opt, &a->patch_id, 0))
                return error("Could not get patch ID for %s",
                        oid_to_hex(&a->commit->object.oid));
        if (is_null_oid(&b->patch_id) &&
-           commit_patch_id(b->commit, opt, &b->patch_id, 0, 0))
+           commit_patch_id(b->commit, opt, &b->patch_id, 0))
                return error("Could not get patch ID for %s",
                        oid_to_hex(&b->commit->object.oid));
        return !oideq(&a->patch_id, &b->patch_id);
@@ -82,7 +83,7 @@ static int init_patch_id_entry(struct patch_id *patch,
        struct object_id header_only_patch_id;
 
        patch->commit = commit;
-       if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1, 0))
+       if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1))
                return -1;
 
        hashmap_entry_init(&patch->ent, oidhash(&header_only_patch_id));
index ab6c6a680474c9b2a37725fc9392683ac5a33083..490d739371666ef8f5fc2fec0d5cbea03178865e 100644 (file)
@@ -20,7 +20,7 @@ struct patch_ids {
 };
 
 int commit_patch_id(struct commit *commit, struct diff_options *options,
-                   struct object_id *oid, int, int);
+                   struct object_id *oid, int);
 int init_patch_ids(struct repository *, struct patch_ids *);
 int free_patch_ids(struct patch_ids *);
 
diff --git a/path.c b/path.c
index a3cfcd8a6e95b3d018c55529d0e532a9d48da0ef..67e2690efef897b406d46eab6202b52fc65a55d0 100644 (file)
--- a/path.c
+++ b/path.c
@@ -1,16 +1,21 @@
 /*
  * Utilities for paths and pathnames
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "dir.h"
 #include "worktree.h"
+#include "setup.h"
 #include "submodule-config.h"
 #include "path.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "lockfile.h"
 #include "exec-cmd.h"
 
@@ -347,7 +352,8 @@ static void init_common_trie(void)
  * Helper function for update_common_dir: returns 1 if the dir
  * prefix is common.
  */
-static int check_common(const char *unmatched, void *value, void *baton)
+static int check_common(const char *unmatched, void *value,
+                       void *baton UNUSED)
 {
        struct common_dir *dir = value;
 
@@ -901,7 +907,13 @@ int adjust_shared_perm(const char *path)
        if (S_ISDIR(old_mode)) {
                /* Copy read bits to execute bits */
                new_mode |= (new_mode & 0444) >> 2;
-               new_mode |= FORCE_DIR_SET_GID;
+
+               /*
+                * g+s matters only if any extra access is granted
+                * based on group membership.
+                */
+               if (FORCE_DIR_SET_GID && (new_mode & 060))
+                       new_mode |= FORCE_DIR_SET_GID;
        }
 
        if (((old_mode ^ new_mode) & ~S_IFMT) &&
@@ -1200,6 +1212,26 @@ int normalize_path_copy(char *dst, const char *src)
        return normalize_path_copy_len(dst, src, NULL);
 }
 
+int strbuf_normalize_path(struct strbuf *src)
+{
+       struct strbuf dst = STRBUF_INIT;
+
+       strbuf_grow(&dst, src->len);
+       if (normalize_path_copy(dst.buf, src->buf) < 0) {
+               strbuf_release(&dst);
+               return -1;
+       }
+
+       /*
+        * normalize_path does not tell us the new length, so we have to
+        * compute it by looking for the new NUL it placed
+        */
+       strbuf_setlen(&dst, strlen(dst.buf));
+       strbuf_swap(src, &dst);
+       strbuf_release(&dst);
+       return 0;
+}
+
 /*
  * path = Canonical absolute path
  * prefixes = string_list containing normalized, absolute paths without
diff --git a/path.h b/path.h
index 0a59c85a62eda03acd290fa564b7e197288e26ad..639372edd9ee36127e3b89084f89ee59d12ff60f 100644 (file)
--- a/path.h
+++ b/path.h
@@ -3,6 +3,7 @@
 
 struct repository;
 struct strbuf;
+struct string_list;
 
 /*
  * The result to all functions which return statically allocated memory may be
@@ -179,7 +180,71 @@ const char *git_path_auto_merge(struct repository *r);
 const char *git_path_fetch_head(struct repository *r);
 const char *git_path_shallow(struct repository *r);
 
-
 int ends_with_path_components(const char *path, const char *components);
+int validate_headref(const char *ref);
+
+int adjust_shared_perm(const char *path);
+
+char *interpolate_path(const char *path, int real_home);
+const char *enter_repo(const char *path, int strict);
+const char *remove_leading_path(const char *in, const char *prefix);
+const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
+int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
+int normalize_path_copy(char *dst, const char *src);
+/**
+ * Normalize in-place the path contained in the strbuf. If an error occurs,
+ * the contents of "sb" are left untouched, and -1 is returned.
+ */
+int strbuf_normalize_path(struct strbuf *src);
+int longest_ancestor_length(const char *path, struct string_list *prefixes);
+char *strip_path_suffix(const char *path, const char *suffix);
+int daemon_avoid_alias(const char *path);
+
+/*
+ * These functions match their is_hfs_dotgit() counterparts; see utf8.h for
+ * details.
+ */
+int is_ntfs_dotgit(const char *name);
+int is_ntfs_dotgitmodules(const char *name);
+int is_ntfs_dotgitignore(const char *name);
+int is_ntfs_dotgitattributes(const char *name);
+int is_ntfs_dotmailmap(const char *name);
+
+/*
+ * Returns true iff "str" could be confused as a command-line option when
+ * passed to a sub-program like "ssh". Note that this has nothing to do with
+ * shell-quoting, which should be handled separately; we're assuming here that
+ * the string makes it verbatim to the sub-program.
+ */
+int looks_like_command_line_option(const char *str);
+
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CONFIG_HOME/$subdir/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
+ * "$HOME/.config/$subdir/$filename". Return NULL upon error.
+ */
+char *xdg_config_home_for(const char *subdir, const char *filename);
+
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
+ * "$HOME/.config/git/$filename". Return NULL upon error.
+ */
+char *xdg_config_home(const char *filename);
+
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
+ * "$HOME/.cache/git/$filename". Return NULL upon error.
+ */
+char *xdg_cache_home(const char *filename);
+
+/*
+ * Create a directory and (if share is nonzero) adjust its permissions
+ * according to the shared_repository setting. Only use this for
+ * directories under $GIT_DIR.  Don't use it for working tree
+ * directories.
+ */
+void safe_create_dir(const char *dir, int share);
 
 #endif /* PATH_H */
index 46e77a85fee9d86e6e16e9f466ecfc3db893fd16..2133b9fe60a87c201186dc3f1840276104903f7f 100644 (file)
@@ -1,10 +1,18 @@
-#include "cache.h"
-#include "config.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "parse.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
 #include "pathspec.h"
 #include "attr.h"
+#include "read-cache.h"
+#include "repository.h"
+#include "setup.h"
 #include "strvec.h"
+#include "symlinks.h"
 #include "quote.h"
+#include "wildmatch.h"
 
 /*
  * Finds which of the given pathspecs match items in the index.
@@ -101,16 +109,37 @@ static struct pathspec_magic {
        { PATHSPEC_ATTR,    '\0', "attr" },
 };
 
-static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
+static void prefix_magic(struct strbuf *sb, int prefixlen,
+                        unsigned magic, const char *element)
 {
-       int i;
-       strbuf_addstr(sb, ":(");
-       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
-               if (magic & pathspec_magic[i].bit) {
-                       if (sb->buf[sb->len - 1] != '(')
-                               strbuf_addch(sb, ',');
-                       strbuf_addstr(sb, pathspec_magic[i].name);
+       /* No magic was found in element, just add prefix magic */
+       if (!magic) {
+               strbuf_addf(sb, ":(prefix:%d)", prefixlen);
+               return;
+       }
+
+       /*
+        * At this point, we know that parse_element_magic() was able
+        * to extract some pathspec magic from element. So we know
+        * element is correctly formatted in either shorthand or
+        * longhand form
+        */
+       if (element[1] != '(') {
+               /* Process an element in shorthand form (e.g. ":!/<match>") */
+               strbuf_addstr(sb, ":(");
+               for (int i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
+                       if ((magic & pathspec_magic[i].bit) &&
+                           pathspec_magic[i].mnemonic) {
+                               if (sb->buf[sb->len - 1] != '(')
+                                       strbuf_addch(sb, ',');
+                               strbuf_addstr(sb, pathspec_magic[i].name);
+                       }
                }
+       } else {
+               /* For the longhand form, we copy everything up to the final ')' */
+               size_t len = strchr(element, ')') - element;
+               strbuf_add(sb, element, len);
+       }
        strbuf_addf(sb, ",prefix:%d)", prefixlen);
 }
 
@@ -459,7 +488,12 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
                match = prefix_path_gently(prefix, prefixlen,
                                           &prefixlen, copyfrom);
                if (!match) {
-                       const char *hint_path = get_git_work_tree();
+                       const char *hint_path;
+
+                       if (!have_git_dir())
+                               die(_("'%s' is outside the directory tree"),
+                                   copyfrom);
+                       hint_path = get_git_work_tree();
                        if (!hint_path)
                                hint_path = get_git_dir();
                        die(_("%s: '%s' is outside repository at '%s'"), elt,
@@ -480,7 +514,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
                struct strbuf sb = STRBUF_INIT;
 
                /* Preserve the actual prefix length of each pattern */
-               prefix_magic(&sb, prefixlen, element_magic);
+               prefix_magic(&sb, prefixlen, element_magic, elt);
 
                strbuf_addstr(&sb, match);
                item->original = strbuf_detach(&sb, NULL);
@@ -525,27 +559,32 @@ static int pathspec_item_cmp(const void *a_, const void *b_)
        return strcmp(a->match, b->match);
 }
 
-static void NORETURN unsupported_magic(const char *pattern,
-                                      unsigned magic)
+void pathspec_magic_names(unsigned magic, struct strbuf *out)
 {
-       struct strbuf sb = STRBUF_INIT;
        int i;
        for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
                const struct pathspec_magic *m = pathspec_magic + i;
                if (!(magic & m->bit))
                        continue;
-               if (sb.len)
-                       strbuf_addstr(&sb, ", ");
+               if (out->len)
+                       strbuf_addstr(out, ", ");
 
                if (m->mnemonic)
-                       strbuf_addf(&sb, _("'%s' (mnemonic: '%c')"),
+                       strbuf_addf(out, _("'%s' (mnemonic: '%c')"),
                                    m->name, m->mnemonic);
                else
-                       strbuf_addf(&sb, "'%s'", m->name);
+                       strbuf_addf(out, "'%s'", m->name);
        }
+}
+
+static void NORETURN unsupported_magic(const char *pattern,
+                                      unsigned magic)
+{
+       struct strbuf sb = STRBUF_INIT;
+       pathspec_magic_names(magic, &sb);
        /*
         * We may want to substitute "this command" with a command
-        * name. E.g. when add--interactive dies when running
+        * name. E.g. when "git add -p" or "git add -i" dies when running
         * "checkout -p"
         */
        die(_("%s: pathspec magic not supported by this command: %s"),
@@ -681,8 +720,7 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
        int i, j;
 
        *dst = *src;
-       ALLOC_ARRAY(dst->items, dst->nr);
-       COPY_ARRAY(dst->items, src->items, dst->nr);
+       DUP_ARRAY(dst->items, src->items, dst->nr);
 
        for (i = 0; i < dst->nr; i++) {
                struct pathspec_item *d = &dst->items[i];
@@ -691,8 +729,7 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
                d->match = xstrdup(s->match);
                d->original = xstrdup(s->original);
 
-               ALLOC_ARRAY(d->attr_match, d->attr_match_nr);
-               COPY_ARRAY(d->attr_match, s->attr_match, d->attr_match_nr);
+               DUP_ARRAY(d->attr_match, s->attr_match, d->attr_match_nr);
                for (j = 0; j < d->attr_match_nr; j++) {
                        const char *value = s->attr_match[j].value;
                        d->attr_match[j].value = xstrdup_or_null(value);
index 41f6adfbb421fee745e3717ffd57ff92acfc82b1..fec4399bbc940823c76f689808ee77e8804624bb 100644 (file)
@@ -130,6 +130,14 @@ void parse_pathspec_file(struct pathspec *pathspec,
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
 void clear_pathspec(struct pathspec *);
 
+/*
+ * Add a human-readable string to "out" representing the PATHSPEC_* flags set
+ * in "magic". The result is suitable for error messages, but not for
+ * parsing as pathspec magic itself (you get 'icase' with quotes, not
+ * :(icase)).
+ */
+void pathspec_magic_names(unsigned magic, struct strbuf *out);
+
 static inline int ps_strncmp(const struct pathspec_item *item,
                             const char *s1, const char *s2, size_t n)
 {
@@ -171,6 +179,11 @@ int match_pathspec_attrs(struct index_state *istate,
                         const char *name, int namelen,
                         const struct pathspec_item *item);
 
+int match_pathspec(struct index_state *istate,
+                  const struct pathspec *pathspec,
+                  const char *name, int namelen,
+                  int prefix, char *seen, int is_dir);
+
 /*
  * Determine whether a pathspec will match only entire index entries (non-sparse
  * files and/or entire sparse directories). If the pathspec has the potential to
index d82b71325c6bd492cd7eb0f703b00fa6634c627d..5b97e0315d6f16c9a215b387fb587dba1074bd23 100644 (file)
@@ -1025,7 +1025,7 @@ C<:warndie> handlers added by Paul Evans <leonerd@leonerd.org.uk>
 
 =head1 MAINTAINER
 
-Shlomi Fish, L<http://www.shlomifish.org/> .
+Shlomi Fish, L<https://www.shlomifish.org/> .
 
 =head1 PAST MAINTAINERS
 
index 080cdc2a21d32da831524a088edf0635357c78df..03bf570bf4c852932973f3a6815c2d3dd4802622 100644 (file)
@@ -7,7 +7,7 @@ Git - Perl interface to the Git version control system
 
 package Git;
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
@@ -177,16 +177,27 @@ sub repository {
                -d $opts{Directory} or throw Error::Simple("Directory not found: $opts{Directory} $!");
 
                my $search = Git->repository(WorkingCopy => $opts{Directory});
-               my $dir;
+
+               # This rev-parse will throw an exception if we're not in a
+               # repository, which is what we want, but it's kind of noisy.
+               # Ideally we'd capture stderr and relay it, but doing so is
+               # awkward without depending on it fitting in a pipe buffer. So
+               # we just reproduce a plausible error message ourselves.
+               my $out;
                try {
-                       $dir = $search->command_oneline(['rev-parse', '--git-dir'],
-                                                       STDERR => 0);
+                 # Note that "--is-bare-repository" must come first, as
+                 # --git-dir output could contain newlines.
+                 $out = $search->command([qw(rev-parse --is-bare-repository --git-dir)],
+                                         STDERR => 0);
                } catch Git::Error::Command with {
-                       $dir = undef;
+                       throw Error::Simple("fatal: not a git repository: $opts{Directory}");
                };
 
+               chomp $out;
+               my ($bare, $dir) = split /\n/, $out, 2;
+
                require Cwd;
-               if ($dir) {
+               if ($bare ne 'true') {
                        require File::Spec;
                        File::Spec->file_name_is_absolute($dir) or $dir = $opts{Directory} . '/' . $dir;
                        $opts{Repository} = Cwd::abs_path($dir);
@@ -204,21 +215,6 @@ sub repository {
                        $opts{WorkingSubdir} = $prefix;
 
                } else {
-                       # A bare repository? Let's see...
-                       $dir = $opts{Directory};
-
-                       unless (-d "$dir/refs" and -d "$dir/objects" and -e "$dir/HEAD") {
-                               # Mimic git-rev-parse --git-dir error message:
-                               throw Error::Simple("fatal: Not a git repository: $dir");
-                       }
-                       my $search = Git->repository(Repository => $dir);
-                       try {
-                               $search->command('symbolic-ref', 'HEAD');
-                       } catch Git::Error::Command with {
-                               # Mimic git-rev-parse --git-dir error message:
-                               throw Error::Simple("fatal: Not a git repository: $dir");
-                       }
-
                        $opts{Repository} = Cwd::abs_path($dir);
                }
 
index 895e759c57a9d9c76d677299ff4658796e2930af..5454c3a6d2c433c1456c0d6151c50746e8f2fad4 100644 (file)
@@ -1,5 +1,5 @@
 package Git::I18N;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 BEGIN {
index 0c360bc799860798ade6b2afce6e05b0a567d21f..8c7fa805f97390a47366b0d0058966ee391b9df1 100644 (file)
@@ -1,5 +1,5 @@
 package Git::LoadCPAN;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
index 5d84c202884b7cb7cea576ea1a6f146b2e653ae2..5cecb0fcd6930a1806aafdaacee9312aa7e8212e 100644 (file)
@@ -1,5 +1,5 @@
 package Git::LoadCPAN::Error;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::LoadCPAN (
index 340e88a7a56be37af8b1f437c92ecb52b1b29089..9f808090a66a16c02eba9c5ed84669ec03f1b944 100644 (file)
@@ -1,5 +1,5 @@
 package Git::LoadCPAN::Mail::Address;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::LoadCPAN (
index d144f5168f37adaf54a2f9b768f04ad2a1c20196..d896e6952399b0fa92b249144d61fcd58d2aab67 100644 (file)
@@ -1,5 +1,5 @@
 package Git::Packet;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 BEGIN {
index 6ce2e283c8d18b3de72cbec12957ebd532d25333..7721708ce5d7f6560ba9f84706e9ad840664020a 100644 (file)
@@ -1752,7 +1752,7 @@ sub tie_for_persistent_memoization {
 END {
        # Force cache writeout explicitly instead of waiting for
        # global destruction to avoid segfault in Storable:
-       # http://rt.cpan.org/Public/Bug/Display.html?id=36087
+       # https://rt.cpan.org/Public/Bug/Display.html?id=36087
        unmemoize_svn_mergeinfo_functions();
 }
 
index ce4e73b6833a48119dfc5f36b64daa8e962dc9c4..24479eae4dbe2a44b15a10f86b15bfc6f865bb34 100644 (file)
@@ -1,6 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "copy.h"
 #include "pkt-line.h"
+#include "gettext.h"
+#include "hex.h"
 #include "run-command.h"
+#include "sideband.h"
+#include "trace.h"
+#include "write-or-die.h"
 
 char packet_buffer[LARGE_PACKET_MAX];
 static const char *packet_trace_prefix = "git";
@@ -367,10 +373,14 @@ static int get_packet_data(int fd, char **src_buf, size_t *src_size,
        return ret;
 }
 
-int packet_length(const char lenbuf_hex[4])
+int packet_length(const char lenbuf_hex[4], size_t size)
 {
-       int val = hex2chr(lenbuf_hex);
-       return (val < 0) ? val : (val << 8) | hex2chr(lenbuf_hex + 2);
+       if (size < 4)
+               BUG("buffer too small");
+       return  hexval(lenbuf_hex[0]) << 12 |
+               hexval(lenbuf_hex[1]) <<  8 |
+               hexval(lenbuf_hex[2]) <<  4 |
+               hexval(lenbuf_hex[3]);
 }
 
 static char *find_packfile_uri_path(const char *buffer)
@@ -413,7 +423,7 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
                return PACKET_READ_EOF;
        }
 
-       len = packet_length(linelen);
+       len = packet_length(linelen, sizeof(linelen));
 
        if (len < 0) {
                if (options & PACKET_READ_GENTLE_ON_READ_ERROR)
@@ -453,8 +463,32 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
        }
 
        if ((options & PACKET_READ_CHOMP_NEWLINE) &&
-           len && buffer[len-1] == '\n')
-               len--;
+           len && buffer[len-1] == '\n') {
+               if (options & PACKET_READ_USE_SIDEBAND) {
+                       int band = *buffer & 0xff;
+                       switch (band) {
+                       case 1:
+                               /* Chomp newline for payload */
+                               len--;
+                               break;
+                       case 2:
+                       case 3:
+                               /*
+                                * Do not chomp newline for progress and error
+                                * message.
+                                */
+                               break;
+                       default:
+                               /*
+                                * Bad sideband, let's leave it to
+                                * demultiplex_sideband() to catch this error.
+                                */
+                               break;
+                       }
+               } else {
+                       len--;
+               }
+       }
 
        buffer[len] = 0;
        if (options & PACKET_READ_REDACT_URI_PATH &&
@@ -583,17 +617,19 @@ void packet_reader_init(struct packet_reader *reader, int fd,
        reader->options = options;
        reader->me = "git";
        reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
+       strbuf_init(&reader->scratch, 0);
 }
 
 enum packet_read_status packet_reader_read(struct packet_reader *reader)
 {
-       struct strbuf scratch = STRBUF_INIT;
-
        if (reader->line_peeked) {
                reader->line_peeked = 0;
                return reader->status;
        }
 
+       if (reader->use_sideband)
+               reader->options |= PACKET_READ_USE_SIDEBAND;
+
        /*
         * Consume all progress packets until a primary payload packet is
         * received
@@ -611,7 +647,7 @@ enum packet_read_status packet_reader_read(struct packet_reader *reader)
                        break;
                if (demultiplex_sideband(reader->me, reader->status,
                                         reader->buffer, reader->pktlen, 1,
-                                        &scratch, &sideband_type))
+                                        &reader->scratch, &sideband_type))
                        break;
        }
 
index 79c538b99e477660fb8819d7fd0175daf4520cc5..3b33cc64f34dcc3447f6a6f42f12451dbc1a77a9 100644 (file)
@@ -1,9 +1,7 @@
 #ifndef PKTLINE_H
 #define PKTLINE_H
 
-#include "git-compat-util.h"
 #include "strbuf.h"
-#include "sideband.h"
 
 /*
  * Write a packetized stream, where each line is preceded by
@@ -86,6 +84,7 @@ void packet_fflush(FILE *f);
 #define PACKET_READ_DIE_ON_ERR_PACKET    (1u<<2)
 #define PACKET_READ_GENTLE_ON_READ_ERROR (1u<<3)
 #define PACKET_READ_REDACT_URI_PATH      (1u<<4)
+#define PACKET_READ_USE_SIDEBAND         (1u<<5)
 int packet_read(int fd, char *buffer, unsigned size, int options);
 
 /*
@@ -95,7 +94,7 @@ int packet_read(int fd, char *buffer, unsigned size, int options);
  * If lenbuf_hex contains non-hex characters, return -1. Otherwise, return the
  * numeric value of the length header.
  */
-int packet_length(const char lenbuf_hex[4]);
+int packet_length(const char lenbuf_hex[4], size_t size);
 
 /*
  * Read a packetized line into a buffer like the 'packet_read()' function but
@@ -195,6 +194,9 @@ struct packet_reader {
 
        /* hash algorithm in use */
        const struct git_hash_algo *hash_algo;
+
+       /* hold temporary sideband message */
+       struct strbuf scratch;
 };
 
 /*
@@ -247,4 +249,6 @@ void packet_writer_error(struct packet_writer *writer, const char *fmt, ...);
 void packet_writer_delim(struct packet_writer *writer);
 void packet_writer_flush(struct packet_writer *writer);
 
+void packet_trace_identity(const char *prog);
+
 #endif
index 3e4f897d935918092f3dfd1a05637b7176bd9fe1..ec08aa24add4e3da7c02b990ba1851665825c2b0 100644 (file)
@@ -412,7 +412,7 @@ There are some conventions that l10n contributors must follow:
 - Do not use non-ASCII characters in the subject of a commit.
 
 - The length of commit subject (first line of the commit log) should
-  be less than 50 characters, and the length of other lines of the
+  be no more than 50 characters, and the length of other lines of the
   commit log should be no more than 72 characters.
 
 - Add "Signed-off-by" trailer to your commit log, like other commits
index 5a63397655a271bf5051f53058db864439020b3d..87df7c6962e2b340d3e78ab8135038186dafb560 100644 (file)
--- a/po/TEAMS
+++ b/po/TEAMS
@@ -51,7 +51,7 @@ Repository:   https://github.com/Arusekk/git-po
 Leader:                Arusekk <arek_koz@o2.pl>
 
 Language:      pt_PT (Portuguese - Portugal)
-Repository:    https://codeberg.org/git-pt/Git-PO-pt_PT/
+Repository:    https://gitlab.com/alexandre1985/git-pt/
 Leader:                Daniel Santos <dacs.git@brilhante.top>
 
 Language:      ru (Russian)
@@ -67,16 +67,22 @@ Language:   tr (Turkish)
 Repository:    https://github.com/bitigchi/git-po/
 Leader:                Emir SARI <bitigchi@me.com>
 
+Language:      uk (Ukrainian)
+Repository:    https://github.com/arkid15r/git-ukrainian-l10n/
+Leader:                Arkadii Yakovets <ark@cho.red>
+Members:       Kateryna Golovanova <kate@kgthreads.com>
+
 Language:      vi (Vietnamese)
 Repository:    https://github.com/vnwildman/git/
 Leader:                Trần Ngọc Quân <vnwildman AT gmail.com>
 Members:       Nguyễn Thái Ngọc Duy <pclouds AT gmail.com>
 
 Language:      zh_CN (Simplified Chinese)
-Repository:    https://github.com/fangyi-zhou/git-po/
-Leader:                Fangyi Zhou <me AT fangyi.io>
+Repository:    https://github.com/dyrone/git/
+Leader:                Teng Long <dyroneteng AT gmail.com>
 Members:       Ray Chen <oldsharp AT gmail.com>
                依云 <lilydjwg AT gmail.com>
+               Fangyi Zhou <me AT fangyi.io>
                Jiang Xin <worldhello.net AT gmail.com>
 
 Language:      zh_TW (Traditional Chinese)
index 8b2a936ba0d082a86c80f6e6aee2b3ce917bc9c9..a3fc5b74d8afc62550681297e4aaa66881fd6f1f 100644 (file)
--- a/po/bg.po
+++ b/po/bg.po
@@ -1,7 +1,7 @@
 # Bulgarian translation of git po-file.
-# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Alexander Shopov <ash@kambanaria.org>.
+# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 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.
+# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023.
 # ========================
 # DICTIONARY TO MERGE IN GIT GUI
 # ------------------------
 # island marks граници на групите
 # reflog журнал на указателите
 # hash контролна сума, изчисляване на контролна сума
-# fanout откъс (разперване???)
+# fanout откъс за разпределяне
 # idx - index of pack file, 1 index per 1 packfile
 # midx, multi-pack index - файл с индекса за множество пакети
 # overlay mode - припокриващ режим (при изтеглянe)
 # timestamp времево клеймо
 # bare repository голо хранилище
 # resolve-undo отмяна на разрешените подавания
+# resolve conflict коригирам конфликт
+# resolve reference установяване на обекта, сочен от указателя, проследяване на указателя
+# cannot resolve reference  не може да сее открие към какво сочи указателят
 # maintenance задачи по поддръжка
 # GLE последна грешка в нишката - от GetLatError: https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
 # lookup table таблица със съответствия
 # out of range извън диапазона
 # checksum сума за проверка
 # superproject обхващащ проект
+# scalar repo скаларно хранилище
+# unclean завършвам работа/задача с грешка
+# cache tree кеш за обекти-дървета
+# gitattributes file файл с атрибути на git
+# advertised обявен за наличен
+# superproject свръхпроект
+# rev-index обратен индекс (reverse index)
+# dererging branches раздалечили се клони
+# master/main branch основен клон
+# unborn/orphan branch неродѐн клон (а не несъздаден) - клон без никакви подавания, включително и началното
+# parse анализ, анализирам
+#
 # ------------------------
 # „$var“ - може да не сработва за shell има gettext и eval_gettext - проверка - намират се лесно по „$
 # ------------------------
+# табулация в началото на реда се заменя с четири интервала
+# по подобен начин отстъпът на примерна команда е четири интервала
+# ------------------------
+#
 # FIXME
-# HEAD as a reference vs head of a branch
-# git update-index -h извежда само един ред, а не цялата помощ за опциите
 # git fetch --al работи подобно на --all
+#
+# ----
+#
+# TODO
+# Причастно-страдателни форми (бъде отворен) -> Възвратно-страдателни форми (се отвори)
+# <ТЕРМИН> -> ТЕРМИН
+#
 # ------------------------
 # export PO_FILE=bg.po
 # msgattrib --only-fuzzy  $PO_FILE > todo1.po
 # for i in `sort -u FILES`; do cnt=`grep $i FILES | wc -l`; echo $cnt $i ;done | sort -n
 msgid ""
 msgstr ""
-"Project-Id-Version: git 2.38\n"
+"Project-Id-Version: git 2.43\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-09-28 10:59+0200\n"
-"PO-Revision-Date: 2022-09-28 10:59+0200\n"
+"POT-Creation-Date: 2023-11-17 15:49+0100\n"
+"PO-Revision-Date: 2023-11-18 13:23+0100\n"
 "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
 "Language-Team: Bulgarian <dict@fsa-bg.org>\n"
 "Language: bg\n"
@@ -226,7 +250,7 @@ msgid "Huh (%s)?"
 msgstr "Неуспешен анализ — „%s“."
 
 msgid "could not read index"
-msgstr "индексът не може да бъде прочетен"
+msgstr "индексът не може да се прочете"
 
 msgid "binary"
 msgstr "двоично"
@@ -245,15 +269,15 @@ msgid "could not stage '%s'"
 msgstr "неуспешно добавяне в индекса на „%s“"
 
 msgid "could not write index"
-msgstr "индексът не може да бъде записан"
+msgstr "индексът не може да се запише"
 
-#, c-format, perl-format
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "%d файл обновен\n"
 msgstr[1] "%d файла обновени\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "БЕЛЕЖКА: „%s“ вече не се следи.\n"
 
@@ -265,9 +289,9 @@ msgid "Revert"
 msgstr "Отмяна"
 
 msgid "Could not parse HEAD^{tree}"
-msgstr "Указателят „HEAD^{tree}“ не може да бъде анализиран"
+msgstr "Указателят „HEAD^{tree}“ не може да се анализира"
 
-#, c-format, perl-format
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "%d файл с отменени промѐни\n"
@@ -280,7 +304,7 @@ msgstr "Няма неследени файлове.\n"
 msgid "Add untracked"
 msgstr "Добавяне на неследени"
 
-#, c-format, perl-format
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "%d файл добавен\n"
@@ -368,25 +392,25 @@ msgid "path"
 msgstr "път"
 
 msgid "could not refresh index"
-msgstr "индексът не може да бъде обновен"
+msgstr "индексът не може да се обнови"
 
 #, c-format
 msgid "Bye.\n"
 msgstr "Изход.\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "Добавяне на промяната на правата за достъп [y,n,q,a,d%s,?]? "
+msgstr "Добавяне на промяната на права̀та за достъп [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Добавяне на изтриването [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Добавяне на добавянето [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Добавяне на това парче [y,n,q,a,d%s,?]? "
 
@@ -394,8 +418,8 @@ msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "staging."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"добавено към индекса."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"добави към индекса."
 
 msgid ""
 "y - stage this hunk\n"
@@ -410,19 +434,19 @@ msgstr ""
 "a — добавяне на това и всички следващи парчета от файла в индекса\n"
 "d — без добавяне на това и всички следващи парчета от файла в индекса\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "Скатаване на промяната на правата за достъп [y,n,q,a,d%s,?]? "
+msgstr "Скатаване на промяната на права̀та за достъп [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Скатаване на изтриването [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Скатаване на добавянето [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Скатаване на това парче [y,n,q,a,d%s,?]? "
 
@@ -430,8 +454,8 @@ msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "stashing."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"скътано."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"скатае."
 
 msgid ""
 "y - stash this hunk\n"
@@ -446,19 +470,19 @@ msgstr ""
 "a — скатаване на това и всички следващи парчета от файла\n"
 "d — без скатаване на това и всички следващи парчета от файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "Изваждане на промяната на правата за достъп [y,n,q,a,d%s,?]? "
+msgstr "Изваждане на промяната на права̀та за достъп [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Изваждане на изтриването [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Изваждане на добавянето [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Изваждане на това парче [y,n,q,a,d%s,?]? "
 
@@ -466,8 +490,8 @@ msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "unstaging."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"извадено от индекса."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"извади от индекса."
 
 msgid ""
 "y - unstage this hunk\n"
@@ -482,20 +506,20 @@ msgstr ""
 "a — изваждане на това и всички следващи парчета от файла от индекса\n"
 "d — без изваждане на това и всички следващи парчета от файла от индекса\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr ""
-"Прилагане на промяната на правата за достъп към индекса [y,n,q,a,d%s,?]? "
+"Прилагане на промяната на права̀та за достъп към индекса [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Прилагане на изтриването към индекса [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Прилагане на добавянето към индекса [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Прилагане на това парче към индекса [y,n,q,a,d%s,?]? "
 
@@ -503,8 +527,8 @@ msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "applying."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"набелязано за прилагане."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"набележи за прилагане."
 
 msgid ""
 "y - apply this hunk to index\n"
@@ -519,21 +543,21 @@ msgstr ""
 "a — прилагане на това и всички следващи парчета от файла към индекса\n"
 "d — без прилагане на това и всички следващи парчета от файла към индекса\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Премахване на промяната в правата за достъп от работното дърво [y,n,q,a,d"
-"%s,?]? "
+"Премахване на промяната в права̀та за достъп от работното дърво [y,n,q,a,"
+"d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Премахване на изтриването от работното дърво [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Премахване на добавянето от работното дърво [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "Премахване на парчето от работното дърво [y,n,q,a,d%s,?]? "
 
@@ -541,8 +565,8 @@ msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "discarding."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"набелязано за зануляване."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"набележи за зануляване."
 
 msgid ""
 "y - discard this hunk from worktree\n"
@@ -560,23 +584,23 @@ msgstr ""
 "d — без премахване на това и всички следващи парчета от файла от работното "
 "дърво\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Премахване на промяната в правата за достъп от индекса и работното дърво [y,"
+"Премахване на промяната в права̀та за достъп от индекса и работното дърво [y,"
 "n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Премахване на изтриването от индекса и работното дърво [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Премахване на добавянето от индекса и работното дърво [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Премахване на парчето от индекса и работното дърво [y,n,q,a,d%s,?]? "
 
@@ -596,22 +620,22 @@ msgstr ""
 "d — без премахване на това и всички следващи парчета от файла от индекса и "
 "работното дърво\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Прилагане на промяната в правата за достъп от индекса и работното дърво [y,n,"
+"Прилагане на промяната в права̀та за достъп от индекса и работното дърво [y,n,"
 "q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Прилагане на изтриването от индекса и работното дърво [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Прилагане на добавянето от индекса и работното дърво [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Прилагане на парчето от индекса и работното дърво [y,n,q,a,d%s,?]? "
 
@@ -631,21 +655,21 @@ msgstr ""
 "d — без прилагане на това и всички следващи парчета от файла от индекса и "
 "работното дърво\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Прилагане на промяната в правата за достъп към работното дърво [y,n,q,a,d"
-"%s,?]? "
+"Прилагане на промяната в права̀та за достъп към работното дърво [y,n,q,a,"
+"d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "Прилагане на изтриването към работното дърво [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "Прилагане на добавянето към работното дърво [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "Прилагане на парчето към работното дърво [y,n,q,a,d%s,?]? "
 
@@ -720,17 +744,15 @@ msgstr ""
 "За да пропуснете редовете, започващи с „%c“: заменете знака с „ “ (стават "
 "контекст)\n"
 "За да пропуснете редовете, започващи с „%c“: изтрийте ги.\n"
-"Редовете, които започват с „%c“ ще бъдат пропуснати.\n"
+"Редовете, които започват с „%c“ ще се пропуснат.\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
 "aborted and the hunk is left unchanged.\n"
 msgstr ""
 "Ако е невъзможно чисто прилагане на кода, ще може пак да редактирате.  Ако\n"
-"изтриете всички редове от парчето код, то ще бъде оставено непроменено, а\n"
+"изтриете всички редове от парчето код, то ще се остави непроменено, а\n"
 "редактирането — отказано.\n"
 
 msgid "could not parse hunk header"
@@ -739,20 +761,12 @@ msgstr "заглавната част парчето не може да се а
 msgid "'git apply --cached' failed"
 msgstr "неуспешно изпълнение на „git apply --cached“"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
@@ -829,14 +843,14 @@ msgid "No hunk matches the given pattern"
 msgstr "Никое парче не напасва на регулярния израз"
 
 msgid "Sorry, cannot split this hunk"
-msgstr "Това парче не може да бъде разделено"
+msgstr "Това парче не може да се раздели"
 
 #, c-format
 msgid "Split into %d hunks."
 msgstr "Разделяне на %d парчета."
 
 msgid "Sorry, cannot edit this hunk"
-msgstr "Това парче не може да бъде редактирано"
+msgstr "Това парче не може да се редактира"
 
 msgid "'git apply' failed"
 msgstr "неуспешно изпълнение на „git apply“"
@@ -870,9 +884,8 @@ msgstr "Издърпването е блокирано от неслети фа
 msgid "Reverting is not possible because you have unmerged files."
 msgstr "Отмяната е блокирана от неслети файлове."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "Действието „%s“ е блокирано от неслети файлове."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Пребазирането е блокирано от неслети файлове."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -893,6 +906,23 @@ msgstr "Промѐните трябва да се подадат преди с
 msgid "Exiting because of unfinished merge."
 msgstr "Изход от програмата заради незавършено сливане."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Раздалечили се клони не може да се превъртят.  Ползвайте:\n"
+"\n"
+"    git merge --no-ff\n"
+"\n"
+"или:\n"
+"\n"
+"    git rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Не може да се извърши превъртане, преустановяване на действието."
 
@@ -938,9 +968,8 @@ msgstr ""
 "Бележка: преминаване към „%s“.\n"
 "\n"
 "Указателят „HEAD“ не е свързан.  Може да разглеждате, да правите произволни\n"
-"промѐни и да ги подавате.  Ако изтеглите нещо друго, всички промѐни ще "
-"бъдат\n"
-"забравени и никой клон няма да се промѐни.\n"
+"промѐни и да ги подавате.  Ако изтеглите нещо друго, всички промѐни ще се\n"
+"забравят и никой клон няма да се промѐни.\n"
 "\n"
 "Ако искате да създадете нов клон, за да запазите подаванията си, може да\n"
 "направите това като зададете име на клон към опцията „-c“ на командата\n"
@@ -985,6 +1014,9 @@ msgstr "командният ред завършва с „/“"
 msgid "unclosed quote"
 msgstr "кавичка без еш"
 
+msgid "too many arguments"
+msgstr "прекалено много аргументи"
+
 #, c-format
 msgid "unrecognized whitespace option '%s'"
 msgstr "непозната опция за знаците за интервали „%s“"
@@ -1001,9 +1033,15 @@ msgstr "опциите „%s“ и „%s“ са несъвместими"
 msgid "'%s' outside a repository"
 msgstr "„%s“ извън хранилище"
 
+msgid "failed to read patch"
+msgstr "кръпката не може да се прочете"
+
+msgid "patch too large"
+msgstr "твърде голяма кръпка"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
-msgstr "РегÑ\83лÑ\8fÑ\80ниÑ\8fÑ\82 Ð¸Ð·Ñ\80аз Ð·Ð° Ð²Ñ\80емевоÑ\82о ÐºÐ»ÐµÐ¹Ð¼Ð¾ â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð·Ð° Ð±Ñ\8aде ÐºÐ¾Ð¼Ð¿Ð¸Ð»Ð¸Ñ\80ан"
+msgstr "РегÑ\83лÑ\8fÑ\80ниÑ\8fÑ\82 Ð¸Ð·Ñ\80аз Ð·Ð° Ð²Ñ\80емевоÑ\82о ÐºÐ»ÐµÐ¹Ð¼Ð¾ â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ñ\81е ÐºÐ¾Ð¼Ð¿Ð¸Ð»Ð¸Ñ\80а "
 
 #, c-format
 msgid "regexec returned %d for input: %s"
@@ -1108,11 +1146,11 @@ msgstr "кръпката е с изцяло повредени данни на 
 
 #, c-format
 msgid "unable to read symlink %s"
-msgstr "символната връзка „%s“ не може да бъде прочетена"
+msgstr "символната връзка „%s“ не може да се прочете"
 
 #, c-format
 msgid "unable to open or read %s"
-msgstr "файлът „%s“ не може да бъде отворен или прочетен"
+msgstr "файлът „%s“ не може да се отвори или прочете"
 
 #, c-format
 msgid "invalid start of line: '%c'"
@@ -1165,12 +1203,11 @@ msgstr "кръпката съответства на „%s“, който тря
 #, c-format
 msgid "the necessary postimage %s for '%s' cannot be read"
 msgstr ""
-"необходимият резултат след операцията  — „%s“ за „%s“ не може да бъде "
-"прочетен"
+"необходимият резултат след операцията  — „%s“ за „%s“ не може да се прочете"
 
 #, c-format
 msgid "binary patch does not apply to '%s'"
-msgstr "двоичната кръпка не може да бъде приложена върху „%s“"
+msgstr "двоичната кръпка не може да се приложи върху „%s“"
 
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
@@ -1188,7 +1225,7 @@ msgstr "„%s“ не може да се изтегли"
 
 #, c-format
 msgid "failed to read %s"
-msgstr "файлът „%s“ не може да бъде прочетен"
+msgstr "файлът „%s“ не може да се прочете"
 
 #, c-format
 msgid "reading from '%s' beyond a symbolic link"
@@ -1215,7 +1252,7 @@ msgstr "Тройно сливане…\n"
 
 #, c-format
 msgid "cannot read the current contents of '%s'"
-msgstr "текущото съдържание на „%s“ не може да бъде прочетено"
+msgstr "текущото съдържание на „%s“ не може да се прочете"
 
 #, c-format
 msgid "Failed to perform three-way merge...\n"
@@ -1258,12 +1295,12 @@ msgstr "„%s“: вече съществува в работното дърво
 
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o)"
-msgstr "новите права за достъп (%o) на „%s“ не съвпадат със старите (%o)"
+msgstr "новите права̀ за достъп (%o) на „%s“ не съвпадат със старите (%o)"
 
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
 msgstr ""
-"новите права за достъп (%o) на „%s“ не съвпадат със старите (%o) на „%s“"
+"новите права̀ за достъп (%o) на „%s“ не съвпадат със старите (%o) на „%s“"
 
 #, c-format
 msgid "affected file '%s' is beyond a symbolic link"
@@ -1271,7 +1308,7 @@ msgstr "засегнатият файл „%s“ е след символна в
 
 #, c-format
 msgid "%s: patch does not apply"
-msgstr "Кръпката „%s“ не може да бъде приложена"
+msgstr "Кръпката „%s“ не може да се приложи"
 
 #, c-format
 msgid "Checking patch %s..."
@@ -1351,7 +1388,11 @@ msgstr "съкращаване на името на файла с отхвърл
 
 #, c-format
 msgid "cannot open %s"
-msgstr "„%s“ не може да бъде отворен"
+msgstr "„%s“ не може да се отвори"
+
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "„%s“ не може да се изтрие"
 
 #, c-format
 msgid "Hunk #%d applied cleanly."
@@ -1370,11 +1411,11 @@ msgstr ""
 "На входа няма непразни кръпки (те се приемат при опция „--allow-empty“)"
 
 msgid "unable to read index file"
-msgstr "индексът не може да бъде записан"
+msgstr "индексът не може да се запише"
 
 #, c-format
 msgid "can't open patch '%s': %s"
-msgstr "кръпката „%s“ не може да бъде отворена: %s"
+msgstr "кръпката „%s“ не може да се отвори: %s"
 
 #, c-format
 msgid "squelched %d whitespace error"
@@ -1397,7 +1438,7 @@ msgstr[1] ""
 "Добавени са %d реда след корекцията на грешките в знаците за интервали."
 
 msgid "Unable to write new index file"
-msgstr "Новият индекс не може да бъде записан"
+msgstr "Новият индекс не може да се запише"
 
 msgid "don't apply changes matching the given path"
 msgstr "без прилагане на промѐните напасващи на дадения път"
@@ -1427,7 +1468,7 @@ msgid "instead of applying the patch, see if the patch is applicable"
 msgstr "проверка дали кръпката може да се приложи, без действително прилагане"
 
 msgid "make sure the patch is applicable to the current index"
-msgstr "проверка дали кръпката може да бъде приложена към текущия индекс"
+msgstr "проверка дали кръпката може да се приложи към текущия индекс"
 
 msgid "mark new files with `git add --intent-to-add`"
 msgstr "отбелязване на новите файлове с „git add --intent-to-add“"
@@ -1440,8 +1481,7 @@ msgstr "прилагане на кръпка, която променя и фа
 
 msgid "also apply the patch (use with --stat/--summary/--check)"
 msgstr ""
-"кръпката да бъде приложена.  Опцията се комбинира с „--check“/„--stat“/„--"
-"summary“"
+"кръпката се приложи.  Опцията се комбинира с „--check“/„--stat“/„--summary“"
 
 msgid "attempt three-way merge, fall back on normal patch if that fails"
 msgstr ""
@@ -1497,11 +1537,11 @@ msgstr "да не се връща грешка при празни кръпки"
 
 #, c-format
 msgid "cannot stream blob %s"
-msgstr "обектът-BLOB „%s“ не може да бъде обработен"
+msgstr "обектът-BLOB „%s“ не може да се обработи"
 
 #, c-format
 msgid "unsupported file mode: 0%o (SHA1: %s)"
-msgstr "неподдържани права за достъп до файл: 0%o (SHA1: %s)"
+msgstr "неподдържани права̀ за достъп до файл: 0%o (SHA1: %s)"
 
 #, c-format
 msgid "deflate error (%d)"
@@ -1509,10 +1549,10 @@ msgstr "грешка при декомпресиране с „deflate“ (%d)"
 
 #, c-format
 msgid "unable to start '%s' filter"
-msgstr "филтърът „%s“ не може да бъде стартиран"
+msgstr "филтърът „%s“ не може да се стартира"
 
 msgid "unable to redirect descriptor"
-msgstr "дескрипторът не може да бъде пренасочен"
+msgstr "дескрипторът не може да се пренасочи"
 
 #, c-format
 msgid "'%s' filter reported error"
@@ -1544,7 +1584,11 @@ msgstr "git archive --remote ХРАНИЛИЩЕ [--exec КОМАНДА] --list"
 
 #, c-format
 msgid "cannot read '%s'"
-msgstr "файлът „%s“ не може да бъде прочетен"
+msgstr "файлът „%s“ не може да се прочете"
+
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "пътят „%s“ съвпада с файлове извън текущата директория"
 
 #, c-format
 msgid "pathspec '%s' did not match any files"
@@ -1562,9 +1606,6 @@ msgstr "неправилно име на обект: „%s“"
 msgid "not a tree object: %s"
 msgstr "не е обект-дърво: %s"
 
-msgid "current working directory is untracked"
-msgstr "текущата работна директория не е следена"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Файлът „%s“ липсва"
@@ -1615,6 +1656,12 @@ msgstr "изчитане на „.gitattributes“ в работната дир
 msgid "report archived files on stderr"
 msgstr "извеждане на архивираните файлове на стандартната грешка"
 
+msgid "time"
+msgstr "ВРЕМЕ"
+
+msgid "set modification time of archive entries"
+msgstr "задаване на ВРЕМЕ на промяна на елементите в архива"
+
 msgid "set compression level"
 msgstr "задаване на нивото на компресиране"
 
@@ -1655,6 +1702,13 @@ msgstr "Аргументът не се поддържа за форма̀та 
 msgid "%.*s is not a valid attribute name"
 msgstr "„%.*s“ е неправилно име за атрибут"
 
+msgid "unable to add additional attribute"
+msgstr "не може да се добави нов атрибут"
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "прескачане на прекалено дълъг ред за атрибути: %d"
+
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "%s: командата не е позволена: „%s:%d“"
@@ -1666,6 +1720,23 @@ msgstr ""
 "Отрицателните шаблони се игнорират в атрибутите на git.\n"
 "Ако ви трябва начална удивителна, ползвайте „\\!“."
 
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "неуспешно изпълнение на „fstat“ върху файла за атрибути на git „%s“"
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "прескачане на прекалено големия файл за атрибути на git: „%s“"
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "прескачане на прекалено големия обект-BLOB за атрибути на git: „%s“"
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr ""
+"неправилна стойност за опцията „--attr-source“ или променливата "
+"„GIT_ATTR_SOURCE“"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Неправилно цитирано съдържание във файла „%s“: %s"
@@ -1718,7 +1789,7 @@ msgid ""
 "So we cannot be sure the first %s commit is between %s and %s.\n"
 "We continue anyway."
 msgstr ""
-"базата за сливане между „%s“ и [%s] трябва да бъде прескочена.\n"
+"базата за сливане между „%s“ и [%s] трябва да се прескочи.\n"
 "Не може да сме сигурни, че първото %s подаване е между „%s“ и „%s“.\n"
 "Двоичното търсене продължава."
 
@@ -1732,11 +1803,11 @@ msgstr "необходима е версия „%s“"
 
 #, c-format
 msgid "could not create file '%s'"
-msgstr "файлът „%s“ не може да бъде създаден"
+msgstr "файлът „%s“ не може да се създаде"
 
 #, c-format
 msgid "could not read file '%s'"
-msgstr "файлът „%s“ не може да бъде прочетен"
+msgstr "файлът „%s“ не може да се прочете"
 
 msgid "reading bisect refs failed"
 msgstr "неуспешно прочитане на указателите за двоично търсене"
@@ -1771,9 +1842,6 @@ msgstr[1] "Двоично търсене: остават %d версии сле
 msgid "--contents and --reverse do not blend well."
 msgstr "опциите „--contents“ и „--reverse“ са несъвместими"
 
-msgid "cannot use --contents with final commit object name"
-msgstr "опцията „--contents“ е несъвместима с име на обект от крайно подаване"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "Едновременното задаване на опциите „--reverse“ и „--first-parent“ изисква "
@@ -1794,7 +1862,7 @@ msgstr "няма път на име „%s“ в „%s“"
 
 #, c-format
 msgid "cannot read blob %s for path %s"
-msgstr "обектът-BLOB „%s“ в пътя %s не може да бъде прочетен"
+msgstr "обектът-BLOB „%s“ в пътя %s не може да се прочете"
 
 msgid ""
 "cannot inherit upstream tracking configuration of multiple refs when "
@@ -1821,7 +1889,7 @@ msgid "branch '%s' set up to track:"
 msgstr "клонът „%s“ ще следи:"
 
 msgid "unable to write upstream branch configuration"
-msgstr "настройките за следения клон не може да бъдат записани"
+msgstr "настройките за следения клон не може да се запишат"
 
 msgid ""
 "\n"
@@ -1894,11 +1962,11 @@ msgstr "„%s“ не е позволено име за клон"
 msgid "a branch named '%s' already exists"
 msgstr "вече съществува клон с име „%s“."
 
-# FIXME
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
 msgstr ""
-"не може принудително да обновите клона „%s“, който е изтеглен в пътя „%s“"
+"не може принудително да обновите клона „%s“, който се ползва от работното "
+"дърво в „%s“"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1945,69 +2013,58 @@ msgstr "подмодул „%s“: подмодулът липсва"
 
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
 "Може да обновите подмодулите с командата:\n"
 "\n"
-"    git checkout %s && git submodule update --init"
+"    git checkout --no-recurse-submodules %s && git submodule update --init"
 
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
 msgstr "подмодул „%s“: клонът „%s“ не може да се създаде"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "„%s“ вече е изтеглен в „%s“"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "Указателят „HEAD“ на работното дърво „%s“ не е обновен"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "„%s“ вече се ползва от работното дърво в „%s“"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [ОПЦИЯ…] [--] ПЪТ…"
 
 #, c-format
 msgid "cannot chmod %cx '%s'"
-msgstr "правата на „%2$s“ не може да се зададат да са %1$cx"
-
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "неочакван изходен код при генериране на разлика: %c"
-
-msgid "updating files failed"
-msgstr "неуспешно обновяване на файловете"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "изтриване на „%s“\n"
+msgstr "права̀та на „%2$s“ не може да се зададат да са %1$cx"
 
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Промѐни, които и след обновяването на индекса не са добавени към него:"
 
-msgid "Could not read the index"
-msgstr "Индексът не може да бъде прочетен"
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"Настройката „add.interactive.useBuiltin“ е премахната!\n"
+"За подробности я потърсете в изхода от „git help config“."
 
-msgid "Could not write patch"
-msgstr "Ð\9aÑ\80Ñ\8aпкаÑ\82а Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð·Ð°Ð¿Ð¸Ñ\81ана"
+msgid "could not read the index"
+msgstr "индекÑ\81Ñ\8aÑ\82 Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ñ\81е Ð¿Ñ\80оÑ\87еÑ\82е"
 
 msgid "editing patch failed"
 msgstr "неуспешно редактиране на кръпка"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Ð\9dе Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ñ\81е Ð¿Ð¾Ð»Ñ\83Ñ\87и Ð¸Ð½Ñ\84оÑ\80маÑ\86иÑ\8f Ñ\87Ñ\80ез â\80\9estatâ\80\9c Ð·Ð° Ñ\84айла „%s“"
+msgid "could not stat '%s'"
+msgstr "неÑ\83Ñ\81пеÑ\88но Ð¸Ð·Ð¿Ñ\8aлнение Ð½Ð° â\80\9estatâ\80\9c Ð²Ñ\8aÑ\80Ñ\85Ñ\83 „%s“"
 
-msgid "Empty patch. Aborted."
-msgstr "Ð\9fÑ\80азна ÐºÑ\80Ñ\8aпка, Ð¿Ñ\80еÑ\83Ñ\81Ñ\82ановÑ\8fване Ð½Ð° Ð´ÐµÐ¹Ñ\81Ñ\82виеÑ\82о."
+msgid "empty patch. aborted"
+msgstr "пÑ\80азна ÐºÑ\80Ñ\8aпка, Ð¿Ñ\80еÑ\83Ñ\81Ñ\82ановÑ\8fване Ð½Ð° Ð´ÐµÐ¹Ñ\81Ñ\82виеÑ\82о"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Ð\9aÑ\80Ñ\8aпкаÑ\82а â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¿Ñ\80иложена"
+msgid "could not apply '%s'"
+msgstr "кÑ\80Ñ\8aпкаÑ\82а â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ñ\81е Ð¿Ñ\80иложи"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
-"Следните пътища ще бъдат игнорирани според някой от файловете „.gitignore“:\n"
+"Следните пътища ще се игнорират според някой от файловете „.gitignore“:\n"
 
 msgid "dry run"
 msgstr "пробно изпълнение"
@@ -2034,7 +2091,7 @@ msgid "renormalize EOL of tracked files (implies -u)"
 msgstr "уеднаквяване на знаците за край на файл (включва опцията „-u“)"
 
 msgid "record only the fact that the path will be added later"
-msgstr "отбелязване само на факта, че пътят ще бъде добавен по-късно"
+msgstr "отбелязване само на факта, че пътят ще се добави по-късно"
 
 msgid "add changes from all tracked and untracked files"
 msgstr "добавяне на всички промѐни в следените и неследените файлове"
@@ -2048,7 +2105,7 @@ msgid "don't add, only refresh the index"
 msgstr "без добавяне на нови файлове, само обновяване на индекса"
 
 msgid "just skip files which cannot be added because of errors"
-msgstr "прескачане на файловете, които не може да бъдат добавени поради грешки"
+msgstr "прескачане на файловете, които не може да се добавят поради грешки"
 
 msgid "check if - even missing - files are ignored in dry run"
 msgstr ""
@@ -2137,6 +2194,9 @@ msgstr ""
 msgid "index file corrupt"
 msgstr "файлът с индекса е повреден"
 
+msgid "unable to write new index file"
+msgstr "неуспешно записване на новия индекс"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "неправилно действие „%s“ за „%s“"
@@ -2147,7 +2207,7 @@ msgstr "неправилна стойност за „%s“: „%s“"
 
 #, c-format
 msgid "could not read '%s'"
-msgstr "файлът „%s“ не може да бъде прочетен"
+msgstr "файлът „%s“ не може да се прочете"
 
 msgid "could not parse author script"
 msgstr "скриптът за автор не може да се анализира"
@@ -2173,11 +2233,11 @@ msgstr "неуспешно изпълнение на „fseek“"
 
 #, c-format
 msgid "could not open '%s' for reading"
-msgstr "файлът не може да бъде прочетен: „%s“"
+msgstr "файлът не може да се прочете: „%s“"
 
 #, c-format
 msgid "could not open '%s' for writing"
-msgstr "„%s“ не може да бъде отворен за запис"
+msgstr "„%s“ не може да се отвори за запис"
 
 #, c-format
 msgid "could not parse patch '%s'"
@@ -2185,7 +2245,7 @@ msgstr "кръпката „%s“ не може да се анализира"
 
 msgid "Only one StGIT patch series can be applied at once"
 msgstr ""
-"Само една поредица от кръпки от „StGIT“ може да бъде прилагана в даден момент"
+"Само една поредица от кръпки от „StGIT“ може да се приложи в даден момент"
 
 msgid "invalid timestamp"
 msgstr "неправилна стойност за времево клеймо"
@@ -2197,14 +2257,14 @@ msgid "invalid timezone offset"
 msgstr "неправилно отместване на часовия пояс"
 
 msgid "Patch format detection failed."
-msgstr "Форматът на кръпката не може да бъде определен."
+msgstr "Форматът на кръпката не може да се определи."
 
 #, c-format
 msgid "failed to create directory '%s'"
-msgstr "директорията „%s“ не може да бъде създадена"
+msgstr "директорията „%s“ не може да се създаде"
 
 msgid "Failed to split patches."
-msgstr "Кръпките не може да бъдат разделени."
+msgstr "Кръпките не може да се разделят."
 
 #, c-format
 msgid "When you have resolved this problem, run \"%s --continue\"."
@@ -2249,7 +2309,7 @@ msgstr "грешен ред с идентичност: %.*s"
 
 #, c-format
 msgid "unable to parse commit %s"
-msgstr "подаването не може да бъде анализирано: %s"
+msgstr "подаването не може да се анализира: %s"
 
 msgid "Repository lacks necessary blobs to fall back on 3-way merge."
 msgstr ""
@@ -2279,7 +2339,7 @@ msgid "applying to an empty history"
 msgstr "прилагане върху празна история"
 
 msgid "failed to write commit object"
-msgstr "обектът за подаването не може да бъде записан"
+msgstr "обектът за подаването не може да се запише"
 
 #, c-format
 msgid "cannot resume: %s does not exist."
@@ -2299,12 +2359,11 @@ msgstr ""
 "на всичко:"
 
 msgid "unable to write index file"
-msgstr "индексът не може да бъде записан"
+msgstr "индексът не може да се запише"
 
 #, c-format
 msgid "Dirty index: cannot apply patches (dirty: %s)"
-msgstr ""
-"Индексът не е чист: кръпките не може да бъдат приложени (замърсени са: %s)"
+msgstr "Индексът не е чист: кръпките не може да се приложат (замърсени са: %s)"
 
 #, c-format
 msgid "Skipping: %.*s"
@@ -2356,15 +2415,12 @@ msgstr ""
 "След корекция на конфликтите изпълнете „git add“ върху поправените файлове.\n"
 "За да приемете „изтрити от тях“, изпълнете „git rm“ върху изтритите файлове."
 
-msgid "unable to write new index file"
-msgstr "неуспешно записване на новия индекс"
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "„%s“ не е разпознат като обект."
 
 msgid "failed to clean index"
-msgstr "индексът не може да бъде изчистен"
+msgstr "индексът не може да се изчисти"
 
 msgid ""
 "You seem to have moved HEAD since the last 'am' failure.\n"
@@ -2378,21 +2434,22 @@ msgstr ""
 
 #, c-format
 msgid "failed to read '%s'"
-msgstr "„%s“ не може да бъде прочетен"
-
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "опциите „%s=%s“ и „%s=%s“ са несъвместими"
+msgstr "„%s“ не може да се прочете"
 
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [ОПЦИЯ…] [(ФАЙЛ_С_ПОЩА|ДИРЕКТОРИЯ_С_ПОЩА)…]"
 
 msgid "git am [<options>] (--continue | --skip | --abort)"
-msgstr "git am [ОПЦИЯ…] (--continue | --skip | --abort)"
+msgstr "git am [ОПЦИЯ…] (--continue|--skip|--abort)"
 
 msgid "run interactively"
 msgstr "интерактивна работа"
 
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr ""
+"без изпълнение на куките преди прилагане на кръпка и съобщение преди "
+"прилагане на кръпка"
+
 msgid "historical option -- no-op"
 msgstr "изоставена опция, съществува по исторически причини, нищо не прави"
 
@@ -2421,11 +2478,6 @@ msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr ""
 "подаване на опцията „--keep-cr“ на командата „git-mailsplit“ за формат „mbox“"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"без подаване на опцията „--keep-cr“ на командата „git-mailsplit“ независимо "
-"от „am.keepcr“"
-
 msgid "strip everything before a scissors line"
 msgstr "пропускане на всичко преди реда за отрязване"
 
@@ -2523,7 +2575,7 @@ msgid "git apply [<options>] [<patch>...]"
 msgstr "git apply [ОПЦИЯ…] [КРЪПКА…]"
 
 msgid "could not redirect output"
-msgstr "изходът не може да бъде пренасочен"
+msgstr "изходът не може да се пренасочи"
 
 msgid "git archive: Remote with no URL"
 msgstr "git archive: Липсва адрес за отдалеченото хранилище"
@@ -2542,31 +2594,28 @@ msgstr "git archive: протоколна грешка"
 msgid "git archive: expected a flush"
 msgstr "git archive: очакваше се изчистване на буферите чрез „flush“"
 
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [ПОДАВАНЕ]"
-
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=ЛОШО --term-{old,good}"
-"=ДОБРО] [--no-checkout] [--first-parent] [ЛОШО [ДОБРО…]] [--] [ПЪТ…]"
+"git bisect start [--term-(new,bad)=УПРАВЛЯВАЩА_ДУМА --term-(old,"
+"good)=УПРАВЛЯВАЩА_ДУМА] [--no-checkout] [--first-parent] [ЛОШО [ДОБРО…]] "
+"[--] [ПЪТ…]"
 
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (ЛОШО) [ВЕРСИЯ]"
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [ВЕРСИЯ…]"
 
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (ДОБРО) [ВЕРСИЯ…]"
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(ВЕРСИЯ|ДИАПАЗОН)…]"
 
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay ИМЕ_НА_ФАЙЛ"
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [ПОДАВАНЕ]"
 
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(ВЕРСИЯ|ДИАПАЗОН)…]"
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay ИМЕ_НА_ФАЙЛ"
 
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run КОМАНДА…"
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run КОМАНДА… [АРГУМЕНТ…]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2578,7 +2627,7 @@ msgstr "във файла „%s“ не може да се пише"
 
 #, c-format
 msgid "cannot open file '%s' for reading"
-msgstr "файлът „%s“ не може да бъде отворен за четене"
+msgstr "файлът „%s“ не може да се отвори за четене"
 
 #, c-format
 msgid "'%s' is not a valid term"
@@ -2607,8 +2656,8 @@ msgstr "„%s“ не е подаване"
 msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
 msgstr ""
-"първоначално указаното „%s“ в указателя „HEAD“ не може да бъде\n"
-"изÑ\82еглено.  Пробвайте да изпълните командата „git bisect reset ПОДАВАНЕ“."
+"първоначално указаното „%s“ в указателя „HEAD“ не може да се\n"
+"изÑ\82егли.  Пробвайте да изпълните командата „git bisect reset ПОДАВАНЕ“."
 
 #, c-format
 msgid "Bad bisect_write argument: %s"
@@ -2715,11 +2764,6 @@ msgstr ""
 "Неуспешно преминаване към „%s“.  Изпълнете командата „git bisect start "
 "СЪЩЕСТВУВАЩ_КЛОН“."
 
-msgid "won't bisect on cg-seek'ed tree"
-msgstr ""
-"не може да се търси двоично, когато е изпълнена командата „cg-seek“ от "
-"„cogito“"
-
 msgid "bad HEAD - strange symbolic ref"
 msgstr "Неправилен указател „HEAD“ — необичаен символен указател"
 
@@ -2775,7 +2819,7 @@ msgid "bisect run failed: no command provided."
 msgstr "неуспешно двоично търсене, не е зададена команда."
 
 #, c-format
-msgid "unable to verify '%s' on good revision"
+msgid "unable to verify %s on good revision"
 msgstr "„%s“ не може да провери с добра версия"
 
 #, c-format
@@ -2783,7 +2827,7 @@ msgid "bogus exit code %d for good revision"
 msgstr "неправилен изходен код %d за добро подаване"
 
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
 msgstr ""
 "неуспешно двоично търсене: изходният код от командата „%2$s“ е %1$d — това е "
 "извън интервала [0, 128)"
@@ -2795,76 +2839,48 @@ msgstr "файлът „%s“ не може да бъде отворен за з
 msgid "bisect run cannot continue any more"
 msgstr "двоичното търсене не може да продължи"
 
-#, c-format
 msgid "bisect run success"
 msgstr "успешно двоично търсене"
 
-#, c-format
 msgid "bisect found first bad commit"
 msgstr "двоичното търсене откри първото лошо подаване"
 
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
-msgstr ""
-"неуспешно двоично търсене: „git bisect--helper --bisect-state %s“ завърши с "
-"код за грешка: %d"
-
-msgid "reset the bisection state"
-msgstr "изчистване на състоянието на двоичното търсене"
-
-msgid "check whether bad or good terms exist"
-msgstr "проверка дали съществуват одобряващи/отхвърлящи управляващи думи"
-
-msgid "print out the bisect terms"
-msgstr "извеждане на управляващите думи"
-
-msgid "start the bisect session"
-msgstr "начало на двоично търсене"
-
-msgid "find the next bisection commit"
-msgstr "откриване на следващото подаване при двоично търсене"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr "неуспешно двоично търсене: „git bisect %s“ завърши с код за грешка: %d"
 
-msgid "mark the state of ref (or refs)"
-msgstr "задаване на състоянието на указателя/ите"
-
-msgid "list the bisection steps so far"
-msgstr "извеждане на стъпките на двоичното търсене досега"
-
-msgid "replay the bisection process from the given file"
-msgstr "наново изпълнение на двоичното търсене чрез дадения файл"
-
-msgid "skip some commits for checkout"
-msgstr "прескачане на някои подавания при изтегляне"
-
-msgid "visualize the bisection"
-msgstr "визуализиране на двоичното търсене"
-
-msgid "use <cmd>... to automatically bisect"
-msgstr "за автоматично двоично търсене да се ползва тази КОМАНДА…"
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "„%s“ изисква или 0 аргументи, или едно подаване"
 
-msgid "no log for BISECT_WRITE"
-msgstr "липсва запис за „BISECT_WRITE“"
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "„%s“ изисква 0 или 1 аргумент"
 
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "опцията „--bisect-reset“ изисква или 0 аргументи, или 1 — подаване"
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "„%s“ изисква 0 аргументи"
 
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "опÑ\86иÑ\8fÑ\82а â\80\9e--bisect-termsâ\80\9c Ð¸Ð·Ð¸Ñ\81ква 0 Ð¸Ð»Ð¸ 1 Ð°Ñ\80гÑ\83менÑ\82а"
+msgid "no logfile given"
+msgstr "не Ðµ Ð·Ð°Ð´Ð°Ð´ÐµÐ½ Ð¶Ñ\83Ñ\80нален Ñ\84айл"
 
-msgid "--bisect-next requires 0 arguments"
-msgstr "опцията „--bisect-next“ не приема аргументи"
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "неуспешно изпълнение на „%s“: не е зададена команда."
 
-msgid "--bisect-log requires 0 arguments"
-msgstr "опÑ\86иÑ\8fÑ\82а â\80\9e--bisect-logâ\80\9c Ð½Ðµ Ð¿Ñ\80иема Ð°Ñ\80гÑ\83менÑ\82и"
+msgid "need a command"
+msgstr "необÑ\85одима Ðµ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°"
 
-msgid "no logfile given"
-msgstr "не е зададен журнален файл"
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "непозната команда: „%s“"
 
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [ОПЦИЯ…] [ОПЦИЯ_ЗА_ВЕРСИЯТА…] [ВЕРСИЯ] [--] ФАЙЛ"
 
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [ОПЦИЯ…] [ОПЦИЯ_ЗА_ВЕРСИЯТА…] [ВЕРСИЯ] [--] ФАЙЛ"
+
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "ОПЦИИте_ЗА_ВЕРСИЯТА са документирани в ръководството git-rev-list(1)"
 
@@ -3012,7 +3028,7 @@ msgid "Blaming lines"
 msgstr "Редове с авторство"
 
 msgid "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
-msgstr "git branch [ОПЦИЯ…] [-r | -a] [--merged] [--no-merged]"
+msgstr "git branch [ОПЦИЯ…] [-r|-a] [--merged] [--no-merged]"
 
 msgid ""
 "git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
@@ -3023,68 +3039,76 @@ msgid "git branch [<options>] [-l] [<pattern>...]"
 msgstr "git branch [ОПЦИЯ…] [-l] [ШАБЛОН…]"
 
 msgid "git branch [<options>] [-r] (-d | -D) <branch-name>..."
-msgstr "git branch [ОПЦИЯ…] [-r] (-d | -D) ИМЕ_НА_КЛОН…"
+msgstr "git branch [ОПЦИЯ…] [-r] (-d|-D) ИМЕ_НА_КЛОН…"
 
 msgid "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"
-msgstr "git branch [ОПЦИЯ…] (-m | -M) [СТАР_КЛОН] НОВ_КЛОН"
+msgstr "git branch [ОПЦИЯ…] (-m|-M) [СТАР_КЛОН] НОВ_КЛОН"
 
 msgid "git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"
-msgstr "git branch [ОПЦИЯ…] (-c | -C) [СТАР_КЛОН] НОВ_КЛОН"
+msgstr "git branch [ОПЦИЯ…] (-c|-C) [СТАР_КЛОН] НОВ_КЛОН"
 
 msgid "git branch [<options>] [-r | -a] [--points-at]"
-msgstr "git branch [ОПЦИЯ…] [-r | -a] [--points-at]"
+msgstr "git branch [ОПЦИЯ…] [-r|-a] [--points-at]"
 
 msgid "git branch [<options>] [-r | -a] [--format]"
-msgstr "git branch [ОПЦИЯ…] [-r | -a] [--format]"
+msgstr "git branch [ОПЦИЯ…] [-r|-a] [--format]"
 
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "изтриване на клона „%s“, който е слят към „%s“,\n"
-"    но още не е слят към върха „HEAD“."
+"    но още не е слят към върха „HEAD“"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
 "отказване на изтриване на клона „%s“, който не е слят към\n"
-"    „%s“, но е слят към върха „HEAD“."
+"    „%s“, но е слят към върха „HEAD“"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Ð\9eбекÑ\82Ñ\8aÑ\82-подаване Ð·Ð° â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¾Ñ\82кÑ\80иÑ\82"
+msgid "couldn't look up commit object for '%s'"
+msgstr "обекÑ\82Ñ\8aÑ\82-подаване Ð·Ð° â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ñ\81е Ð¾Ñ\82кÑ\80ие"
 
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
-"Клонът „%s“ не е слят напълно.  Ако сте сигурни, че искате\n"
-"да го изтриете, изпълнете „git branch -D %s“."
+"клонът „%s“ не е слят напълно.  Ако сте сигурни, че искате\n"
+"да го изтриете, изпълнете:\n"
+"\n"
+"    git branch -D %s"
 
-msgid "Update of config-file failed"
-msgstr "Ð\9dеуспешно обновяване на конфигурационния файл"
+msgid "update of config-file failed"
+msgstr "неуспешно обновяване на конфигурационния файл"
 
 msgid "cannot use -a with -d"
 msgstr "опциите „-a“ и „-d“ са несъвместими"
 
-msgid "Couldn't look up commit object for HEAD"
-msgstr "Обектът-подаване, сочен от указателя „HEAD“, не може да бъде открит"
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"не може да изтриете клона „%s“, който се ползва от работното дърво в „%s“"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Не може да изтриете клона „%s“, който е изтеглен в пътя „%s“"
+msgid "remote-tracking branch '%s' not found"
+msgstr "следящият клон „%s“ липсва"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "следящият клон „%s“ не може да бъде открит."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"клонът „%s“ не може да бъде открит.\n"
+"Пробвахте ли опцията „--remote“?"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "клонÑ\8aÑ\82 â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¾Ñ\82кÑ\80иÑ\82."
+msgid "branch '%s' not found"
+msgstr "клонÑ\8aÑ\82 â\80\9e%sâ\80\9c Ð»Ð¸Ð¿Ñ\81ва"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -3105,47 +3129,52 @@ msgid "HEAD (%s) points outside of refs/heads/"
 msgstr "„HEAD“ (%s) сочи извън директорията „refs/heads“"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "Ð\9aлонът „%s“ се пребазира върху „%s“"
+msgid "branch %s is being rebased at %s"
+msgstr "клонът „%s“ се пребазира върху „%s“"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "Търси се двоично в клона „%s“ при „%s“"
+msgid "branch %s is being bisected at %s"
+msgstr "търси се двоично в клона „%s“ при „%s“"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "не може да копирате текущия клон, защото сте извън който и да е клон"
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "Указателят „HEAD“ на работното дърво „%s“ не е обновен"
 
-msgid "cannot rename the current branch while not on any."
-msgstr ""
-"не може да преименувате текущия клон, защото сте извън който и да е клон"
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "неправилно име на клон: „%s“"
+
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "в клона „%s“ все още няма подавания"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Ð\9dепÑ\80авилно Ð¸Ð¼Ðµ Ð½Ð° ÐºÐ»Ð¾Ð½: „%s“"
+msgid "no branch named '%s'"
+msgstr "липÑ\81ва ÐºÐ»Ð¾Ð½ Ð½Ð° Ð¸Ð¼Ðµ „%s“"
 
-msgid "Branch rename failed"
-msgstr "Ð\9dеуспешно преименуване на клон"
+msgid "branch rename failed"
+msgstr "неуспешно преименуване на клон"
 
-msgid "Branch copy failed"
-msgstr "Ð\9dеуспешно копиране на клон"
+msgid "branch copy failed"
+msgstr "неуспешно копиране на клон"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Ð\9aлонът с неправилно име „%s“ е копиран"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "клонът с неправилно име „%s“ е копиран"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Ð\9aлонът с неправилно име „%s“ е преименуван"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "клонът с неправилно име „%s“ е преименуван"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Ð\9aлонът е преименуван на „%s“, но указателят „HEAD“ не е обновен"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "клонът е преименуван на „%s“, но указателят „HEAD“ не е обновен"
 
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "Ð\9aлонът е преименуван, но конфигурационният файл не е обновен"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "клонът е преименуван, но конфигурационният файл не е обновен"
 
-msgid "Branch is copied, but update of config-file failed"
-msgstr "Ð\9aлонът е копиран, но конфигурационният файл не е обновен"
+msgid "branch is copied, but update of config-file failed"
+msgstr "клонът е копиран, но конфигурационният файл не е обновен"
 
 #, c-format
 msgid ""
@@ -3214,6 +3243,9 @@ msgstr ""
 msgid "move/rename a branch, even if target exists"
 msgstr "преместване/преименуване на клон, дори ако има вече клон с такова име"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "без извеждане на нов ред след празен форматиран указател"
+
 msgid "copy a branch and its reflog"
 msgstr "копиране на клон и принадлежащия му журнал на указателите"
 
@@ -3259,8 +3291,8 @@ msgstr "рекурсивно обхождане подмодулите"
 msgid "format to use for the output"
 msgstr "ФОРМАТ за изхода"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Не може да се открие към какво сочи указателят „HEAD“"
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "указателят „HEAD“ не сочи към обект"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "В директорията „refs/heads“ липсва файл „HEAD“"
@@ -3279,19 +3311,18 @@ msgstr ""
 msgid "branch name required"
 msgstr "Необходимо е име на клон"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "Ð\9dе може да зададете описание на несвързан „HEAD“"
+msgid "cannot give description to detached HEAD"
+msgstr "не може да зададете описание на несвързан „HEAD“"
 
 msgid "cannot edit description of more than one branch"
-msgstr "Ð\9dе може да редактирате описанието на повече от един клон едновременно"
+msgstr "не може да редактирате описанието на повече от един клон едновременно"
 
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "В клона „%s“ все още няма подавания."
+msgid "cannot copy the current branch while not on any"
+msgstr "не може да копирате текущия клон, защото сте извън който и да е клон"
 
-#, c-format
-msgid "No branch named '%s'."
-msgstr "Липсва клон на име „%s“."
+msgid "cannot rename the current branch while not on any"
+msgstr ""
+"не може да преименувате текущия клон, защото сте извън който и да е клон"
 
 msgid "too many branches for a copy operation"
 msgstr "прекалено много клони за копиране"
@@ -3304,32 +3335,31 @@ msgstr "прекалено много аргументи към командат
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"Следеното от „HEAD“ не може да се зададе да е „%s“, защото то не сочи към "
-"никой клон."
+"следеното от „HEAD“ не може да се зададе да е „%s“, защото то не сочи към "
+"никой клон"
 
 #, c-format
 msgid "no such branch '%s'"
-msgstr "Ð\9dяма клон на име „%s“."
+msgstr "няма клон на име „%s“."
 
 #, c-format
 msgid "branch '%s' does not exist"
-msgstr "Ð\9dе съществува клон на име „%s“."
+msgstr "не съществува клон на име „%s“."
 
 msgid "too many arguments to unset upstream"
 msgstr "прекалено много аргументи към командата за спиране на следене"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr ""
-"Следеното от „HEAD“ не може да махне, защото то не сочи към никой клон."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr "следеното от „HEAD“ не може да махне, защото то не сочи към никой клон"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "Ð\9dяма информация клонът „%s“ да следи някой друг"
+msgid "branch '%s' has no upstream information"
+msgstr "няма информация клонът „%s“ да следи някой друг"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
 "опциите „-a“ и „-r“ на „git branch“ са несъвместими с име на клон.\n"
@@ -3337,7 +3367,7 @@ msgstr ""
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "опцията „--set-upstream“ вече не се поддържа.  Използвайте „--track“ или „--"
 "set-upstream-to“"
@@ -3358,13 +3388,12 @@ msgstr "библиотека на C: "
 msgid "not run from a git repository - no hooks to show\n"
 msgstr "командата е стартирана извън хранилище на Git, затова няма куки\n"
 
-# FIXME
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory ФАЙЛ] [-s|--suffix ФОРМАТ] [--"
-"diagnose[=РЕЖИМ]]"
+"git bugreport [(-o|--output-directory) ПЪТ] [(-s|--suffix) ФОРМАТ]\n"
+"              [--diagnose[=РЕЖИМ]]"
 
 msgid ""
 "Thank you for filling out a Git bug report!\n"
@@ -3416,6 +3445,10 @@ msgstr "укажете местоположение, в което да се з
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "укажете суфикса на файловете във формат за „strftime“"
 
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "непозната опция „%s“"
+
 #, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "родителските директории на „%s“ не може да бъдат създадени"
@@ -3438,17 +3471,24 @@ msgstr "в „%s“ не може да се пише"
 msgid "Created new report at '%s'.\n"
 msgstr "Новият доклад е създаден в „%s“.\n"
 
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [ОПЦИЯ…] ФАЙЛ АРГУМЕНТ_ЗА_git_rev-list…"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q|--quiet|--progress ]\n"
+"                  [--version=ВЕРСИЯ] ФАЙЛ ОПЦИЯ_ЗА_git-rev-list…"
 
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [ОПЦИЯ…] ФАЙЛ…"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q|--quiet] ФАЙЛ"
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads ФАЙЛ [ИМЕ_НА_УКАЗАТЕЛ…]"
 
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle ФАЙЛ [ИМЕ_НА_УКАЗАТЕЛ…]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] ФАЙЛ [ИМЕ_НА_УКАЗАТЕЛ…]"
+
+msgid "need a <file> argument"
+msgstr "необходим е аргумент ФАЙЛ"
 
 msgid "do not show progress meter"
 msgstr "без извеждане на напредъка"
@@ -3456,12 +3496,11 @@ msgstr "без извеждане на напредъка"
 msgid "show progress meter"
 msgstr "извеждане на напредъка"
 
-msgid "show progress meter during object writing phase"
-msgstr "извеждане Ð½Ð° Ð½Ð°Ð¿Ñ\80едÑ\8aка Ð²Ñ\8aв Ñ\84азаÑ\82а Ð½Ð° Ð·Ð°Ð¿Ð°Ð·Ð²Ð°Ð½Ðµ Ð½Ð° Ð¾Ð±ÐµÐºÑ\82иÑ\82е"
+msgid "historical; same as --progress"
+msgstr "изоÑ\81Ñ\82авена Ð¾Ð¿Ñ\86иÑ\8f, Ñ\81Ñ\8aÑ\89оÑ\82о ÐºÐ°Ñ\82о â\80\9e--progressâ\80\9c"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr ""
-"същото действие като опцията „--all-progress“ при извеждането на напредъка"
+msgid "historical; does nothing"
+msgstr "изоставена опция, нищо не прави"
 
 msgid "specify bundle format version"
 msgstr "версия на пратката"
@@ -3504,10 +3543,6 @@ msgstr "командата „%s“ изисква аргумент"
 msgid "%s takes no arguments"
 msgstr "командата „%s“ не приема аргументи"
 
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "непозната команда: „%s“"
-
 msgid "only one batch option may be specified"
 msgstr "може да укажете само една пакетна опция"
 
@@ -3515,28 +3550,27 @@ msgid "git cat-file <type> <object>"
 msgstr "git cat-file ВИД ОБЕКТ"
 
 msgid "git cat-file (-e | -p) <object>"
-msgstr "git cat-file (-e | -p) ОБЕКТ"
+msgstr "git cat-file (-e|-p) ОБЕКТ"
 
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
-msgstr "git cat-file (-t | -s) [--allow-unknown-type] ОБЕКТ"
+msgstr "git cat-file (-t|-s) [--allow-unknown-type] ОБЕКТ"
 
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
+"git cat-file (--textconv|--filters)\n"
+"             [ВЕРСИЯ:ПЪТ|ДЪРВО|--path=ПЪТ|ДЪРВО ВЕРСИЯ]"
+
+msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [ВЕРСИЯ:ПЪТ|ДЪРВО | --path=ПЪТ|ДЪРВО ВЕРСИЯ]"
+"git cat-file (--batch|--batch-check|--batch-command) [--batch-all-objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv|--filters] [-Z]"
 
 msgid "Check object existence or emit object contents"
 msgstr "Проверка за съществуването на обекта или извеждане на съдържанието му"
@@ -3580,6 +3614,9 @@ msgstr "като „--batch“, но без извеждане на съдърж
 msgid "stdin is NUL-terminated"
 msgstr "стандартният вход да ползва нулевия знак „NUL“ за разделител"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "стандартните вход и изход да ползват нулевия знак „NUL“ за разделител"
+
 msgid "read commands from stdin"
 msgstr "изчитане на командите от стандартния вход"
 
@@ -3646,18 +3683,20 @@ msgstr "опцията „%s“ изисква версия"
 msgid "<object> required with '-%c'"
 msgstr "опцията „-%c“ изисква обект"
 
-msgid "too many arguments"
-msgstr "прекалено много аргументи"
-
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr "в режим с посочен ВИД ОБЕКТ се изискват точно два аргумента, а не %d"
 
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | АТРИБУТ…] [--] ПЪТ…"
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source УКАЗАТЕЛ_КЪМ_ДЪРВО] [-a|--all|АТРИБУТ…] [--] ПЪТ…"
 
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | АТРИБУТ…]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source УКАЗАТЕЛ_КЪМ_ДЪРВО] [-a|--all|АТРИБУТ…]"
 
 msgid "report all attributes set on file"
 msgstr "извеждане на всички атрибути, зададени върху файл"
@@ -3671,6 +3710,12 @@ msgstr "изчитане на имената на файловете от ста
 msgid "terminate input and output records by a NUL character"
 msgstr "разделяне на входните и изходните записи с нулевия знак „NUL“"
 
+msgid "<tree-ish>"
+msgstr "УКАЗАТЕЛ_КЪМ_ДЪРВО"
+
+msgid "which tree-ish to check attributes at"
+msgstr "към кой УКАЗАТЕЛ_КЪМ_ДЪРВО да се премине"
+
 msgid "suppress progress reporting"
 msgstr "без показване на напредъка"
 
@@ -3830,6 +3875,10 @@ msgstr "опцията „%s“ е задължителна, когато „%s
 msgid "'%s' or '%s' cannot be used with %s"
 msgstr "опцията „%3$s“ е несъвместима както с „%1$s“, така и с „%2$s“"
 
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "„%s“, „%s“ и „%s“ са несъвместими с изтеглянето на дърво"
+
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "пътят „%s“ не е слят"
@@ -4120,7 +4169,7 @@ msgstr "липсва име на клон, използвайте опцията
 
 #, c-format
 msgid "could not resolve %s"
-msgstr "â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¾Ñ\82кÑ\80иÑ\82"
+msgstr "â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¿Ñ\80оÑ\81леден"
 
 msgid "invalid path specification"
 msgstr "указан е неправилен път"
@@ -4193,8 +4242,9 @@ msgid "use overlay mode"
 msgstr "използване на припокриващ режим"
 
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
-msgstr "git clean [-d] [-f] [-i] [-n] [-q] [-e ШАБЛОН] [-x | -X] [--] ПЪТ…"
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
+msgstr "git clean [-d] [-f] [-i] [-n] [-q] [-e ШАБЛОН] [-x|-X] [--] [ПЪТ…]"
 
 #, c-format
 msgid "Removing %s\n"
@@ -4258,7 +4308,7 @@ msgstr ""
 "*          — избиране на всички обекти\n"
 "           — (празно) завършване на избирането\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Неправилен избор (%s).\n"
 
@@ -4409,9 +4459,6 @@ msgstr "ДЪЛБОЧИНА"
 msgid "create a shallow clone of that depth"
 msgstr "плитко клониране до тази ДЪЛБОЧИНА"
 
-msgid "time"
-msgstr "ВРЕМЕ"
-
 msgid "create a shallow clone since a specific time"
 msgstr "плитко клониране до момент във времето"
 
@@ -4451,12 +4498,6 @@ msgstr "специфични за сървъра"
 msgid "option to transmit"
 msgstr "опция за пренос"
 
-msgid "use IPv4 addresses only"
-msgstr "само адреси IPv4"
-
-msgid "use IPv6 addresses only"
-msgstr "само адреси IPv6"
-
 msgid "apply partial clone filters to submodules"
 msgstr "прилагане на филтрите за непълно хранилище към подмодулите"
 
@@ -4488,10 +4529,19 @@ msgstr "не може да бъде получена информация чре
 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“"
@@ -4563,10 +4613,6 @@ msgstr "Прекалено много аргументи."
 msgid "You must specify a repository to clone."
 msgstr "Трябва да укажете кое хранилище искате да клонирате."
 
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "опциите „%s“ и „%s %s“ са несъвместими"
-
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
 "exclude"
@@ -4661,6 +4707,9 @@ msgstr ""
 msgid "failed to fetch objects from bundle URI '%s'"
 msgstr "неуспешно доставяне на обекти от пратка на адрес „%s“"
 
+msgid "failed to fetch advertised bundles"
+msgstr "неуспешно доставяне на обявените за налични пратки"
+
 msgid "remote transport reported error"
 msgstr "отдалеченият транспорт върна грешка"
 
@@ -4696,19 +4745,25 @@ msgid "--command must be the first argument"
 msgstr "опцията „--command“ трябва да е първият аргумент"
 
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir ДИР_ОБЕКТИ] [--shallow] [--"
+"git commit-graph verify [--object-dir ДИРЕКТОРИЯ] [--shallow] [--"
 "[no-]progress]"
 
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 msgstr ""
-"git commit-graph write [--object-dir ДИР_ОБЕКТИ] [--append] [--"
-"split[=СТРАТЕГИЯ]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] ОПЦИИ_ЗА_РАЗДЕЛЯНЕ"
+"git commit-graph write [--object-dir ДИРЕКТОРИЯ] [--append]\n"
+"                       [--split[=СТРАТЕГИЯ]] [--reachable|--stdin-packs|--"
+"stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters БРОЙ] [--"
+"[no-]progress]\n"
+"                       ОПЦИИ_ЗА_РАЗДЕЛЯНЕ"
 
 msgid "dir"
 msgstr "директория"
@@ -4718,12 +4773,16 @@ msgstr "ДИРекторията_с_ОБЕКТИ за запазване на г
 
 msgid "if the commit-graph is split, only verify the tip file"
 msgstr ""
-"ако гра̀фа с подаванията е раздробен, да се проверява само файлът на върха"
+"ако графът с подаванията е раздробен, да се проверява само файлът на върха"
 
 #, c-format
 msgid "Could not open commit-graph '%s'"
 msgstr "Графът с подаванията не може да се отвори: „%s“"
 
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "веригата на гра̀фа с подаванията не може да се отвори: „%s“"
+
 #, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "непознат аргумент към „--split“: %s"
@@ -4781,12 +4840,16 @@ msgstr ""
 msgid "Collecting commits from input"
 msgstr "Получаване на подаванията от входа"
 
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree ДЪРВО [(-p РОДИТЕЛ)…]"
+
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p РОДИТЕЛ)…] [-S[ИДЕНТИФИКАТОР]] [(-m СЪОБЩЕНИЕ)…] [(-F "
-"ФАЙЛ)…] ДЪРВО"
+"git commit-tree [(-p РОДИТЕЛ)…] [-S[ИДЕНТИФИКАТОР_НА_КЛЮЧ]] [(-m "
+"СЪОБЩЕНИЕ)…]\n"
+"                [(-F ФАЙЛ)…] ДЪРВО"
 
 #, c-format
 msgid "duplicate parent %s ignored"
@@ -4828,11 +4891,30 @@ msgstr "трябва да е точно едно дърво"
 msgid "git commit-tree: failed to read"
 msgstr "git commit-tree: не може да се прочете"
 
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [ОПЦИЯ…] [--] ПЪТ…"
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a|--interactive|--patch] [-s] [-v] [-u РЕЖИМ] [--amend]\n"
+"           [--dry-run] [(-c|-C|--squash) ПОДАВАНЕ |--fixup [(amend|"
+"reword):]ПОДАВАНЕ)]\n"
+"           [-F ФАЙЛ|-m СЪОБЩЕНИЕ] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=АВТОР]\n"
+"           [--date=ДАТА] [--cleanup=РЕЖИМ] [--[no-]status]\n"
+"           [-i|-o] [--pathspec-from-file=ФАЙЛ> [--pathspec-file-nul]]\n"
+"           [(--trailer ЛЕКСЕМА[(=|:)СТОЙНОСТ])…] [-"
+"S[ИДЕНТИФИКАТОР_НА_КЛЮЧ]]\n"
+"           [--] [ПЪТ…]"
 
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [ОПЦИЯ…] [--] ПЪТ…"
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [ОПЦИЯ…] [--] [ПЪТ…]"
 
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -4892,6 +4974,9 @@ msgstr ""
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "неуспешно обновяване на файловете"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "върховото дърво (HEAD tree object) не може да бъде извадено от пакет"
 
@@ -4908,10 +4993,7 @@ msgid "unable to update temporary index"
 msgstr "временният индекс не може да бъде обновен"
 
 msgid "Failed to update main cache tree"
-msgstr "Дървото на основния кеш не може да бъде обновено"
-
-msgid "unable to write new_index file"
-msgstr "новият индекс (new_index) не може да бъде записан"
+msgstr "Кешът за обектите-дървета не може да бъде обновен"
 
 msgid "cannot do a partial commit during a merge."
 msgstr "по време на сливане не може да се извърши частично подаване."
@@ -4951,7 +5033,7 @@ msgstr ""
 "използвани всички подобни знаци"
 
 #, c-format
-msgid "could not lookup commit %s"
+msgid "could not lookup commit '%s'"
 msgstr "следното подаване не може да бъде открито: %s"
 
 #, c-format
@@ -5324,12 +5406,12 @@ msgstr "Неизвършване на подаване поради празно
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"хранилището е обновено, но новият файл за индекс „new_index“\n"
-"не Ðµ Ð·Ð°Ð¿Ð¸Ñ\81ан.  Ð\9fÑ\80овеÑ\80еÑ\82е Ð´Ð°Ð»Ð¸ Ð´Ð¸Ñ\81кÑ\8aÑ\82 Ð½Ðµ Ðµ Ð¿Ñ\80епÑ\8aлнен Ð¸Ð»Ð¸ Ð½Ðµ Ñ\81Ñ\82е\n"
-"пÑ\80евиÑ\88или Ð´Ð¸Ñ\81коваÑ\82а Ñ\81и ÐºÐ²Ð¾Ñ\82а.  Ð\97а Ð²Ñ\8aзÑ\81Ñ\82ановÑ\8fване Ð¸Ð·Ð¿Ñ\8aлнеÑ\82е:\n"
+"хранилището е обновено, но новият файл за индекс не е записан.\n"
+"Ð\9fÑ\80овеÑ\80еÑ\82е Ð´Ð°Ð»Ð¸ Ð´Ð¸Ñ\81кÑ\8aÑ\82 Ð½Ðµ Ðµ Ð¿Ñ\80епÑ\8aлнен Ð¸Ð»Ð¸ Ð½Ðµ Ñ\81Ñ\82е Ð¿Ñ\80евиÑ\88или\n"
+"дисковата си квота.  За възстановяване изпълнете:\n"
 "\n"
 "    git restore --staged :/"
 
@@ -5613,7 +5695,7 @@ msgid ""
 "\n"
 "\tchmod 0700 %s"
 msgstr ""
-"Правата за достъп до директорията за програмните гнезда са прекалено "
+"Права̀та за достъп до директорията за програмните гнезда са прекалено "
 "свободни —\n"
 "другите потребители може да получат достъп до кешираните ви пароли.  За да\n"
 "коригирате това, изпълнете:\n"
@@ -5638,11 +5720,19 @@ msgstr ""
 "ключалката на хранилището на идентификациите не бе получена в рамките на %d "
 "ms"
 
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [ОПЦИЯ…] [УКАЗАТЕЛ_КЪМ_ПОДАВАНЕ…]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=БРОЙ] "
+"[УКАЗАТЕЛ_КЪМ_ПОДАВАНЕ…]"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=БРОЙ] --dirty[=МАРКЕР]"
 
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [ОПЦИЯ…] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe обект-BLOB"
 
 msgid "head"
 msgstr "основно"
@@ -5766,11 +5856,11 @@ msgid "option '%s' and commit-ishes cannot be used together"
 msgstr "опциите „%s“ и указателите към обекти са несъвместими"
 
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory ФАЙЛ] [-s|--suffix ФОРМАТ] [--"
-"diagnose[=РЕЖИМ]]"
+"git diagnose [(-o|--output-directory) ПЪТ] [(-s|--suffix) ФОРМАТ]\n"
+"             [--diagnose[=РЕЖИМ]]"
 
 msgid "specify a destination for the diagnostics archive"
 msgstr "укажете местоположение на архива с диагностичната информация"
@@ -5788,6 +5878,9 @@ msgstr "опцията „--merge-base“ изисква точно две по
 msgid "'%s': not a regular file or symlink"
 msgstr "„%s“: не е нито обикновен файл, нито символна връзка"
 
+msgid "no merge given, only parents."
+msgstr "липсва сливане, зададени са само родителски подавания."
+
 #, c-format
 msgid "invalid option: %s"
 msgstr "неправилна опция: %s"
@@ -5904,33 +5997,10 @@ msgstr "не е зададена програма за „--tool=ПРОГРАМ
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "не е зададена команда за „--extcmd=КОМАНДА“"
 
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] ОПЦИИ ПРОМЕНЛИВИ"
-
-msgid "default for git_env_*(...) to fall back on"
-msgstr "стандартна, резервна стойност за „git_env_*(…)“"
+msgid "git fast-export [<rev-list-opts>]"
+msgstr "git fast-export [ОПЦИЯ_ЗА_СПИСЪКА_С_ВЕРСИИ…]"
 
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr ""
-"без извеждане на информация — стойността на „git_env_*()“ да е изходен код"
-
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr ""
-"опцията „--default“ изисква булева стойност при „--type=bool“, а не „%s“"
-
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not `"
-"%s`"
-msgstr ""
-"опцията „--default“ изисква целочислена стойност без знак при „--"
-"type=ulong“, а не „%s“"
-
-msgid "git fast-export [<rev-list-opts>]"
-msgstr "git fast-export [ОПЦИЯ_ЗА_СПИСЪКА_С_ВЕРСИИ…]"
-
-msgid "Error: Cannot export nested tags unless --mark-tags is specified."
+msgid "Error: Cannot export nested tags unless --mark-tags is specified."
 msgstr ""
 "Грешка: непреките етикети не се изнасят, освен ако не зададете „--mark-tags“."
 
@@ -6032,7 +6102,7 @@ msgid "git fetch [<options>] <group>"
 msgstr "git fetch [ОПЦИЯ…] ГРУПА"
 
 msgid "git fetch --multiple [<options>] [(<repository> | <group>)...]"
-msgstr "git fetch --multiple [ОПЦИЯ…] [(ХРАНИЛИЩЕ | ГРУПА)…]"
+msgstr "git fetch --multiple [ОПЦИЯ…] [(ХРАНИЛИЩЕ|ГРУПА)…]"
 
 msgid "git fetch --all [<options>]"
 msgstr "git fetch --all [ОПЦИЯ…]"
@@ -6040,118 +6110,13 @@ msgstr "git fetch --all [ОПЦИЯ…]"
 msgid "fetch.parallel cannot be negative"
 msgstr "опцията „fetch.parallel“ трябва да е неотрицателна"
 
-msgid "fetch from all remotes"
-msgstr "доставяне от всички отдалечени хранилища"
-
-msgid "set upstream for git pull/fetch"
-msgstr "задаване на клон за следене за издърпване/доставяне"
-
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "добавяне към „.git/FETCH_HEAD“ вместо замяна"
-
-msgid "use atomic transaction to update references"
-msgstr "изискване на атомарни операции за обновяване на указателите"
-
-msgid "path to upload pack on remote end"
-msgstr "отдалечен път, където да се качи пакетът"
-
-msgid "force overwrite of local reference"
-msgstr "принудително презаписване на локален указател"
-
-msgid "fetch from multiple remotes"
-msgstr "доставяне от множество отдалечени хранилища"
-
-msgid "fetch all tags and associated objects"
-msgstr "доставяне на всички етикети и принадлежащи обекти"
-
-msgid "do not fetch all tags (--no-tags)"
-msgstr "без доставянето на всички етикети „--no-tags“"
-
-msgid "number of submodules fetched in parallel"
-msgstr "брой подмодули доставени паралелно"
-
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr ""
-"промяна на указателя, така че и той, както останалите, да бъде в „refs/"
-"prefetch/“"
-
-msgid "prune remote-tracking branches no longer on remote"
-msgstr "окастряне на клоните следящи вече несъществуващи отдалечени клони"
-
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr ""
-"окастряне на локалните етикети, които вече не съществуват в отдалеченото "
-"хранилище и презаписване на променените"
-
-msgid "on-demand"
-msgstr "ПРИ НУЖДА"
-
-msgid "control recursive fetching of submodules"
-msgstr "управление на рекурсивното доставяне на подмодулите"
-
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "запазване на доставените указатели във файла „FETCH_HEAD“"
-
-msgid "keep downloaded pack"
-msgstr "запазване на изтеглените пакети с обекти"
-
-msgid "allow updating of HEAD ref"
-msgstr "позволяване на обновяването на указателя „HEAD“"
-
-msgid "deepen history of shallow clone"
-msgstr "задълбочаване на историята на плитко хранилище"
-
-msgid "deepen history of shallow repository based on time"
-msgstr "задълбочаване на историята на плитко хранилище до определено време"
-
-msgid "convert to a complete repository"
-msgstr "превръщане в пълно хранилище"
-
-msgid "re-fetch without negotiating common commits"
-msgstr "повторно доставяне без договаряне на общите подавания"
-
-msgid "prepend this to submodule path output"
-msgstr "добавяне на това пред пътя на подмодула"
-
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr ""
-"стандартно рекурсивно изтегляне на подмодулите (файловете с настройки са с "
-"приоритет)"
-
-msgid "accept refs that update .git/shallow"
-msgstr "приемане на указатели, които обновяват „.git/shallow“"
-
-msgid "refmap"
-msgstr "КАРТА_С_УКАЗАТЕЛИ"
-
-msgid "specify fetch refmap"
-msgstr "указване на КАРТАта_С_УКАЗАТЕЛИ за доставяне"
-
-msgid "report that we have only objects reachable from this object"
-msgstr "докладване, че всички обекти може са достижими при започване от този"
-
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr ""
-"без доставяне на пакетни файлове, вместо това да се извеждат предшественици "
-"на договорните върхове"
-
-msgid "run 'maintenance --auto' after fetching"
-msgstr "изпълняване на „maintenance --auto“ след доставяне"
-
-msgid "check for forced-updates on all updated branches"
-msgstr "проверка за принудителни обновявания на всички клони"
-
-msgid "write the commit-graph after fetching"
-msgstr "запазване на гра̀фа с подаванията след доставяне"
-
-msgid "accept refspecs from stdin"
-msgstr "четене на указателите от стандартния вход"
-
 msgid "couldn't find remote ref HEAD"
 msgstr "указателят „HEAD“ в отдалеченото хранилище не може да бъде открит"
 
+#, c-format
+msgid "From %.*s\n"
+msgstr "От %.*s\n"
+
 #, c-format
 msgid "object %s not found"
 msgstr "обектът „%s“ липсва"
@@ -6227,10 +6192,6 @@ msgid "rejected %s because shallow roots are not allowed to be updated"
 msgstr ""
 "отхвърляне на „%s“, защото плитките върхове не може да бъдат обновявани"
 
-#, c-format
-msgid "From %.*s\n"
-msgstr "От %.*s\n"
-
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -6324,6 +6285,115 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr "трябва да укажете име на етикет"
 
+msgid "fetch from all remotes"
+msgstr "доставяне от всички отдалечени хранилища"
+
+msgid "set upstream for git pull/fetch"
+msgstr "задаване на клон за следене за издърпване/доставяне"
+
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "добавяне към „.git/FETCH_HEAD“ вместо замяна"
+
+msgid "use atomic transaction to update references"
+msgstr "изискване на атомарни операции за обновяване на указателите"
+
+msgid "path to upload pack on remote end"
+msgstr "отдалечен път, където да се качи пакетът"
+
+msgid "force overwrite of local reference"
+msgstr "принудително презаписване на локален указател"
+
+msgid "fetch from multiple remotes"
+msgstr "доставяне от множество отдалечени хранилища"
+
+msgid "fetch all tags and associated objects"
+msgstr "доставяне на всички етикети и принадлежащи обекти"
+
+msgid "do not fetch all tags (--no-tags)"
+msgstr "без доставянето на всички етикети „--no-tags“"
+
+msgid "number of submodules fetched in parallel"
+msgstr "брой подмодули доставени паралелно"
+
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"промяна на указателя, така че и той, както останалите, да бъде в „refs/"
+"prefetch/“"
+
+msgid "prune remote-tracking branches no longer on remote"
+msgstr "окастряне на клоните следящи вече несъществуващи отдалечени клони"
+
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr ""
+"окастряне на локалните етикети, които вече не съществуват в отдалеченото "
+"хранилище и презаписване на променените"
+
+msgid "on-demand"
+msgstr "ПРИ НУЖДА"
+
+msgid "control recursive fetching of submodules"
+msgstr "управление на рекурсивното доставяне на подмодулите"
+
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "запазване на доставените указатели във файла „FETCH_HEAD“"
+
+msgid "keep downloaded pack"
+msgstr "запазване на изтеглените пакети с обекти"
+
+msgid "allow updating of HEAD ref"
+msgstr "позволяване на обновяването на указателя „HEAD“"
+
+msgid "deepen history of shallow clone"
+msgstr "задълбочаване на историята на плитко хранилище"
+
+msgid "deepen history of shallow repository based on time"
+msgstr "задълбочаване на историята на плитко хранилище до определено време"
+
+msgid "convert to a complete repository"
+msgstr "превръщане в пълно хранилище"
+
+msgid "re-fetch without negotiating common commits"
+msgstr "повторно доставяне без договаряне на общите подавания"
+
+msgid "prepend this to submodule path output"
+msgstr "добавяне на това пред пътя на подмодула"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"стандартно рекурсивно изтегляне на подмодулите (файловете с настройки са с "
+"приоритет)"
+
+msgid "accept refs that update .git/shallow"
+msgstr "приемане на указатели, които обновяват „.git/shallow“"
+
+msgid "refmap"
+msgstr "КАРТА_С_УКАЗАТЕЛИ"
+
+msgid "specify fetch refmap"
+msgstr "указване на КАРТАта_С_УКАЗАТЕЛИ за доставяне"
+
+msgid "report that we have only objects reachable from this object"
+msgstr "докладване, че всички обекти може са достижими при започване от този"
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+"без доставяне на пакетни файлове, вместо това да се извеждат предшественици "
+"на договорните върхове"
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr "изпълняване на „maintenance --auto“ след доставяне"
+
+msgid "check for forced-updates on all updated branches"
+msgstr "проверка за принудителни обновявания на всички клони"
+
+msgid "write the commit-graph after fetching"
+msgstr "запазване на гра̀фа с подаванията след доставяне"
+
+msgid "accept refspecs from stdin"
+msgstr "четене на указателите от стандартния вход"
+
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr ""
 "Опцията „--negotiate-only“ изисква една или повече опции „--negotiation-"
@@ -6335,6 +6405,10 @@ msgstr "отрицателна дълбочина като аргумент на
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "не може да използвате опцията „--unshallow“ върху пълно хранилище"
 
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "неуспешно доставяне на пратки от „%s“"
+
 msgid "fetch --all does not take a repository argument"
 msgstr "към „git fetch --all“ не може да добавите аргумент-хранилище"
 
@@ -6370,8 +6444,7 @@ msgstr "опцията „--stdin“ поддържа доставяне сам
 
 msgid ""
 "git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"
-msgstr ""
-"git fmt-merge-msg [-m СЪОБЩЕНИЕ] [--log[=БРОЙ] | --no-log] [--file ФАЙЛ]"
+msgstr "git fmt-merge-msg [-m СЪОБЩЕНИЕ] [--log[=БРОЙ]|--no-log] [--file ФАЙЛ]"
 
 msgid "populate log with at most <n> entries from shortlog"
 msgstr ""
@@ -6438,8 +6511,14 @@ msgstr "извеждане само на указателите, които съ
 msgid "print only refs which don't contain the commit"
 msgstr "извеждане само на указателите, които не съдържат това ПОДАВАНЕ"
 
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=НАСТРОЙКА АРГУМЕНТ…"
+msgid "read reference patterns from stdin"
+msgstr "изчитане на шаблоните за указатели от стандартния вход"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "непознат аргумент към опцията „--stdin“"
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=НАСТРОЙКА [--] АРГУМЕНТ…"
 
 msgid "config"
 msgstr "настройка"
@@ -6450,6 +6529,10 @@ msgstr "настройка, която съдържа списък с пътищ
 msgid "missing --config=<config>"
 msgstr "липсва --config=НАСТРОЙКА"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "получена е неправилена настройка „--config=%s“"
+
 msgid "unknown"
 msgstr "непознат"
 
@@ -6594,24 +6677,41 @@ msgstr "%s: несвързаният връх „HEAD“ не сочи към н
 
 #, c-format
 msgid "notice: %s points to an unborn branch (%s)"
-msgstr "пÑ\80едÑ\83пÑ\80еждение: â\80\9e%sâ\80\9c Ñ\81оÑ\87и ÐºÑ\8aм Ð²Ñ\81е Ð¾Ñ\89е Ð½ÐµÑ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ваÑ\89 клон (%s)"
+msgstr "пÑ\80едÑ\83пÑ\80еждение: â\80\9e%sâ\80\9c Ñ\81оÑ\87и ÐºÑ\8aм Ð½ÐµÑ\80одеÌ\80н клон (%s)"
 
-msgid "Checking cache tree"
-msgstr "Проверка на дървото на кеша"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Проверка на кеша на обектите-дървета на „%s“"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "â\80\9e%sâ\80\9c: Ð½ÐµÐ¿Ñ\80авилен Ñ\83казаÑ\82ел Ð·Ð° SHA1 Ð² Ð´Ñ\8aÑ\80воÑ\82о Ð½Ð° ÐºÐµÑ\88а"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "â\80\9e%sâ\80\9c: Ð½ÐµÐ¿Ñ\80авилен Ñ\83казаÑ\82ел Ð·Ð° SHA1 Ð² ÐºÐµÑ\88а Ð½Ð° Ð¾Ð±ÐµÐºÑ\82иÑ\82е-дÑ\8aÑ\80веÑ\82а Ð½Ð° â\80\9e%sâ\80\9c"
 
 msgid "non-tree in cache-tree"
-msgstr "в Ð´Ñ\8aÑ\80воÑ\82о Ð½Ð° ÐºÐµÑ\88а има нещо, което не е дърво"
+msgstr "в ÐºÐµÑ\88а Ð½Ð° Ð¾Ð±ÐµÐºÑ\82иÑ\82е-дÑ\8aÑ\80веÑ\82а има нещо, което не е дърво"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "„%s“: неправилен указател за отмяна на разрешените подавания"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "„%s“: неправилен указател за отмяна на разрешените подавания на „%s“"
 
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [ОПЦИЯ…] [ОБЕКТ…]"
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "обратният индекс на пакета „%s“ не може да бъде зареден"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "неправилен обратен индекс за пакета „%s“"
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [ОБЕКТ…]"
 
 msgid "show unreachable objects"
 msgstr "показване на недостижимите обекти"
@@ -6666,12 +6766,6 @@ msgstr "git fsmonitor--daemon start [ОПЦИЯ…]"
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [ОПЦИЯ…]"
 
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
-
 #, c-format
 msgid "value of '%s' out of range: %d"
 msgstr "дължината на „%s“ e извън диапазона: %d"
@@ -6795,6 +6889,9 @@ msgstr "окастряне на обектите, към които нищо н
 msgid "pack unreferenced objects separately"
 msgstr "пакетиране на обектите, към които нищо не сочи, отделно"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "с „--cruft“ размерът на новите ненужни пакети се ограничава"
+
 msgid "be more thorough (increased runtime)"
 msgstr "изчерпателно търсене на боклука (за сметка на повече време работа)"
 
@@ -6923,8 +7020,20 @@ msgstr "изпълнение на определена задача"
 msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr "опциите „--auto“ и „--schedule=ЧЕСТОТА“ са несъвместими"
 
-msgid "failed to run 'git config'"
-msgstr "неуспешно изпълнение на „git config“"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "неуспешно добавяне на стойност на „%s“ за „%s“"
+
+msgid "return success even if repository was not registered"
+msgstr "успешно завършване, дори ако хранилището не бъде регистрирано"
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "неуспешно изчистване на стойност на „%s“ за „%s“"
+
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "хранилището „%s“ не е регистрирано"
 
 #, c-format
 msgid "failed to expand path '%s'"
@@ -6965,12 +7074,6 @@ msgstr ""
 msgid "'crontab' died"
 msgstr "процесът на „crontab“ умря"
 
-msgid "failed to start systemctl"
-msgstr "неуспешно стартиране на „systemctl“"
-
-msgid "failed to run systemctl"
-msgstr "неуспешно изпълнение на „systemctl“"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "неуспешно изтриване на „%s“"
@@ -6979,6 +7082,12 @@ msgstr "неуспешно изтриване на „%s“"
 msgid "failed to flush '%s'"
 msgstr "грешка при изчистването на буферите при записването на „%s“"
 
+msgid "failed to start systemctl"
+msgstr "неуспешно стартиране на „systemctl“"
+
+msgid "failed to run systemctl"
+msgstr "неуспешно изпълнение на „systemctl“"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "непознат аргумент към „--scheduler“: %s"
@@ -7002,6 +7111,9 @@ msgstr "ПЛАНИРАЩ_МОДУЛ"
 msgid "scheduler to trigger git maintenance run"
 msgstr "ПЛАНИРАЩият_МОДУЛ, който да изпълнява задачите"
 
+msgid "failed to set up maintenance schedule"
+msgstr "неуспешно насрочване на задачите по поддръжка"
+
 msgid "failed to add repo to global config"
 msgstr "неуспешно добавяне на хранилище към файла с глобални настройки"
 
@@ -7077,7 +7189,7 @@ msgstr ""
 msgid "search in subdirectories (default)"
 msgstr "търсене в поддиректориите (стандартно)"
 
-msgid "descend at most <depth> levels"
+msgid "descend at most <n> levels"
 msgstr "навлизане максимално на тази ДЪЛБОЧИНА в дървото"
 
 msgid "use extended POSIX regular expressions"
@@ -7227,11 +7339,14 @@ msgid "both --cached and trees are given"
 msgstr "опцията „--cached“ е несъвместима със задаване на дърво"
 
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t ВИД] [-w] [--path=ФАЙЛ | --no-filters] [--stdin] [--] "
-"ФАЙЛ…"
+"git hash-object [-t ВИД] [-w] [--path=ФАЙЛ|--no-filters]\n"
+"                [--stdin [--literally]] [--] ФАЙЛ…"
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t ВИД] [-w] --stdin-paths [--no-filters]"
 
 msgid "object type"
 msgstr "ВИД на обекта"
@@ -7362,12 +7477,18 @@ msgstr "употреба: %s%s"
 msgid "'git help config' for more information"
 msgstr "За повече информация изпълнете „git help config“"
 
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] КУКА [-- АРГУМЕНТ_ЗА_КУКА…]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=ПЪТ] КУКА [-- АРГУМЕНТ_ЗА_КУКА…]"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "прескачане на заявена КУКА, която липсва"
 
+msgid "file to read into hooks' stdin"
+msgstr "файл за изчитане от стандартния вход на куката"
+
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "неправилен вид на обекта „%s“"
@@ -7587,89 +7708,19 @@ msgstr "опцията „--verify“ изисква име на пакетен
 msgid "fsck error in pack objects"
 msgstr "грешка при проверка с „fsck“ на пакетните обекти"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "не може да се получи информация чрез „stat“ за шаблона „%s“"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "директорията „%s“ не може да бъде отворена"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "връзката „%s“ не може да бъде прочетена"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "не може да се създаде символна връзка „%s“ в „%s“"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "„%s“ не може да се копира в „%s“"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "игнориране на шаблона „%s“"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "няма шаблони в „%s“"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "шаблоните няма да бъдат копирани от „%s“: „%s“"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "неправилно име на първоначалния клон: „%s“"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "файлове от вид %d не се поддържат"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "„%s“ не може да се премести в „%s“"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr ""
-"опит за повторно задаване на първото подаване в хранилището с различна "
-"контролна сума"
-
-#, c-format
-msgid "%s already exists"
-msgstr "Директорията „%s“ вече съществува"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: „--initial-branch=%s“ се пропуска"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr ""
-"Инициализиране наново на съществуващо, споделено хранилище на Git в „%s%s“\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Инициализиране наново на съществуващо хранилище на Git в „%s%s“\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Инициализиране на празно, споделено хранилище на Git в „%s%s“\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Инициализиране на празно хранилище на Git в „%s%s“\n"
-
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=ДИРЕКТОРИЯ_С_ШАБЛОНИ] [--"
-"shared[=ПРАВА]] [ДИРЕКТОРИЯ]"
+"git init [-q|--quiet] [--bare] [--template=ДИРЕКТОРИЯ_С_ШАБЛОНИ]\n"
+"         [--separate-git-dir ДИРЕКТОРИЯ_НА_GIT] [--object-format=ФОРМАТ]\n"
+"         [-b КЛОН|--initial-branch=КЛОН]\n"
+"         [--shared[=ПРАВА̀]] [ДИРЕКТОРИЯ]"
 
 msgid "permissions"
-msgstr "права"
+msgstr "права̀"
 
 msgid "specify that the git repository is to be shared amongst several users"
 msgstr ""
@@ -7709,11 +7760,13 @@ msgid "--separate-git-dir incompatible with bare repository"
 msgstr "опцията „--separate-git-dir“ е несъвместима с голо хранилище"
 
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"ЛЕКСЕМА[(=|:)СТОЙНОСТ])…] [ФАЙЛ…]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (КЛЮЧ|СИНОНИМ)[(=|:)СТОЙНОСТ])…]\n"
+"                       [--parse] [ФАЙЛ…]"
 
 msgid "edit files in place"
 msgstr "директно редактиране на файловете"
@@ -7721,6 +7774,9 @@ msgstr "директно редактиране на файловете"
 msgid "trim empty trailers"
 msgstr "изчистване на празните епилози"
 
+msgid "placement"
+msgstr "местоположение"
+
 msgid "where to place the new trailer"
 msgstr "къде да се постави новият епилог"
 
@@ -7733,17 +7789,19 @@ msgstr "действие при липсващ епилог"
 msgid "output only the trailers"
 msgstr "извеждане само на епилозите"
 
-msgid "do not apply config rules"
-msgstr "без Ð¿Ñ\80илагане Ð½Ð° Ð¿Ñ\80авилаÑ\82а Ð·Ð° Ð½Ð°Ñ\81Ñ\82Ñ\80ойкиÑ\82е"
+msgid "do not apply trailer.* configuration variables"
+msgstr "без Ð¿Ñ\80илагане Ð½Ð° ÐºÐ¾Ð½Ñ\84игÑ\83Ñ\80иÑ\80аÑ\89и Ð¿Ñ\80оменливи, Ð·Ð°Ð²Ñ\8aÑ\80Ñ\88ваÑ\89и Ñ\81 ÐµÐ¿Ð¸Ð»Ð¾Ð³.*"
 
-msgid "join whitespace-continued values"
-msgstr "сливане на стойностите последване от знаци за интервали"
+msgid "reformat multiline trailer values as single-line values"
+msgstr ""
+"форматиране на епилози, които заемат повече от един ред, в стойности на един "
+"ред"
 
-msgid "set parsing options"
-msgstr "опÑ\86ии Ð¿Ñ\80и Ð°Ð½Ð°Ð»Ð¸Ð·"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "пÑ\81евдоним Ð½Ð° ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ\86иÑ\8fÑ\82а â\80\9e--only-trailers --only-input --unfoldâ\80\9c"
 
-msgid "do not treat --- specially"
-msgstr "„---“ няма специално значение"
+msgid "do not treat \"---\" as the end of input"
+msgstr "„---“ не отбелязва края на входа"
 
 msgid "trailer(s) to add"
 msgstr "епилози за добавяне"
@@ -7832,6 +7890,10 @@ msgstr "трябва да зададете точно един диапазон"
 msgid "not a range"
 msgstr "не е диапазон"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "файлът с описанието на клона „%s“ не може да бъде прочетен"
+
 msgid "cover letter needs email format"
 msgstr "придружаващото писмо трябва да е форматирано като е-писмо"
 
@@ -7843,7 +7905,7 @@ msgid "insane in-reply-to: %s"
 msgstr "неправилен формат на заглавната част за отговор „in-reply-to“: %s"
 
 msgid "git format-patch [<options>] [<since> | <revision-range>]"
-msgstr "git format-patch [ОПЦИЯ…] [ОТ | ДИАПАЗОН_НА_ВЕРСИИТЕ]"
+msgstr "git format-patch [ОПЦИЯ…] [ОТ|ДИАПАЗОН_НА_ВЕРСИИТЕ]"
 
 msgid "two output directories?"
 msgstr "може да укажете максимум една директория за изход"
@@ -7934,6 +7996,9 @@ msgstr ""
 "генериране на частите на придружаващото писмо на базата на описанието на "
 "клона"
 
+msgid "use branch description from file"
+msgstr "ползване на описание на клон от файл"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "използване на този „[ПРЕФИКС]“ вместо „[PATCH]“"
 
@@ -8105,6 +8170,10 @@ msgstr ""
 "Следеният отдалечен клон не бе открит, затова изрично задайте "
 "ОТДАЛЕЧЕН_КЛОН.\n"
 
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "не може да се получи информация за обекта „%s“"
+
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "неправилен формат за „ls-tree“: елементът „%s“ не започва с „(“"
@@ -8213,12 +8282,12 @@ msgstr ""
 
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
-"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [ХРÐ\90Ð\9dÐ\98Ð\9bÐ\98ЩÐ\95 [УÐ\9aÐ\90Ð\97Ð\90ТÐ\95Ð\9bâ\80¦]]"
+"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=КОМАНДА]\n"
+"              [-q|--quiet] [--exit-code] [--get-url] [--sort=КЛЮЧ]\n"
+"              [--symref] [ХРÐ\90Ð\9dÐ\98Ð\9bÐ\98ЩÐ\95 [ШÐ\90Ð\91Ð\9bÐ\9eÐ\9d]]"
 
 msgid "do not print remote URL"
 msgstr "без извеждане на адресите на отдалечените хранилища"
@@ -8250,10 +8319,6 @@ msgstr "извеждане на указателя заедно с обекта
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
 msgstr "git ls-tree [ОПЦИЯ…] УКАЗАТЕЛ_КЪМ_ДЪРВО [ПЪТ…]"
 
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "не може да се получи информация за обекта „%s“"
-
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "неправилен формат за „ls-tree“: елементът „%s“ не започва с „(“"
@@ -8347,17 +8412,17 @@ msgid "empty mbox: '%s'"
 msgstr "празна пощенска кутия mbox: „%s“"
 
 msgid "git merge-base [-a | --all] <commit> <commit>..."
-msgstr "git merge-base [-a | --all] ПОДАВАНЕ ПОДАВАНЕ…"
+msgstr "git merge-base [-a|--all] ПОДАВАНЕ ПОДАВАНЕ…"
 
 msgid "git merge-base [-a | --all] --octopus <commit>..."
-msgstr "git merge-base [-a | --all] --octopus ПОДАВАНЕ…"
-
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent ПОДАВАНЕ…"
+msgstr "git merge-base [-a|--all] --octopus ПОДАВАНЕ…"
 
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor ПОДАВАНЕ_1 ПОДАВАНЕ_2"
 
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent ПОДАВАНЕ…"
+
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point УКАЗАТЕЛ [ПОДАВАНЕ]"
 
@@ -8387,6 +8452,9 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "извеждане на резултатите на стандартния изход"
 
+msgid "use object IDs instead of filenames"
+msgstr "ползване на идентификатори на обекти вместо имена на файлове"
+
 msgid "use a diff3 based merge"
 msgstr "сливане на базата на „diff3“"
 
@@ -8411,6 +8479,13 @@ msgstr "без предупреждения при конфликти"
 msgid "set labels for file1/orig-file/file2"
 msgstr "задаване на етикети за ФАЙЛ_1/ОРИГИНАЛ/ФАЙЛ_2"
 
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "обектът „%s“ не съществува"
+
+msgid "Could not write object file"
+msgstr "Файлът с обекти не може да се запише"
+
 #, c-format
 msgid "unknown option %s"
 msgstr "непозната опция: „%s“"
@@ -8430,7 +8505,7 @@ msgstr "поддържа се само сливане на точно две и
 
 #, c-format
 msgid "could not resolve ref '%s'"
-msgstr "Ñ\83казаÑ\82елÑ\8fÑ\82 â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¸Ð·Ñ\82Ñ\80иÑ\82"
+msgstr "Ñ\83казаÑ\82елÑ\8fÑ\82 â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¿Ñ\80оÑ\81леден"
 
 #, c-format
 msgid "Merging %s with %s\n"
@@ -8467,9 +8542,36 @@ msgstr ""
 msgid "allow merging unrelated histories"
 msgstr "позволяване на сливане на независими истории"
 
+msgid "perform multiple merges, one per line of input"
+msgstr "извършване на множество сливания, по едно на ред"
+
+msgid "specify a merge-base for the merge"
+msgstr "база за сливането"
+
+msgid "option=value"
+msgstr "ОПЦИЯ=СТОЙНОСТ"
+
+msgid "option for selected merge strategy"
+msgstr "ОПЦИЯ за избраната стратегия за сливане"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "„--trivial-merge“ е несъвместима с другите опции"
 
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "непозната опция за стратегия: -X%s"
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr "опциите „--merge-base“ и „--stdin“ са несъвместими"
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "входен ред с неправилен формат: „%s“."
+
+#, c-format
+msgid "merging cannot continue; got unclean result of %d"
+msgstr "сливането не може да продължи — %d-тото завърши с грешка"
+
 msgid "git merge [<options>] [<commit>...]"
 msgstr "git merge [ОПЦИЯ…] [ПОДАВАНЕ…]"
 
@@ -8530,12 +8632,6 @@ msgstr "СТРАТЕГИЯ"
 msgid "merge strategy to use"
 msgstr "СТРАТЕГИЯ за сливане, която да се ползва"
 
-msgid "option=value"
-msgstr "ОПЦИЯ=СТОЙНОСТ"
-
-msgid "option for selected merge strategy"
-msgstr "ОПЦИЯ за избраната стратегия за сливане"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "СЪОБЩЕНИЕ при подаването със сливане (при същински сливания)"
 
@@ -8598,10 +8694,6 @@ msgstr "Индексът не може да бъде прочетен"
 msgid "Not handling anything other than two heads merge."
 msgstr "Поддържа се само сливане на точно две истории."
 
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "непозната опция за стратегия: -X%s"
-
 #, c-format
 msgid "unable to write %s"
 msgstr "„%s“ не може да бъде записан"
@@ -8907,8 +8999,8 @@ msgstr "целта съществува"
 msgid "can not move directory into itself"
 msgstr "директория не може да се премести в себе си"
 
-msgid "cannot move directory over file"
-msgstr "директория не може да се премести върху файл"
+msgid "destination already exists"
+msgstr "целта съществува"
 
 msgid "source directory is empty"
 msgstr "първоначалната директория е празна"
@@ -8989,22 +9081,26 @@ msgid "git notes [--ref <notes-ref>] [list [<object>]]"
 msgstr "git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] [list [ОБЕКТ]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] add [-f] [--allow-empty] [-m СЪОБЩЕНИЕ "
-"| -F ФАЙЛ | (-c | -C) ОБЕКТ] [ОБЕКТ]"
+"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m "
+"СЪОБЩЕНИЕ|-F ФАЙЛ|(-c|-C) ОБЕКТ] [ОБЕКТ]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] copy [-f] ОБЕКТ_ИЗТОЧНИК ОБЕКТ_ЦЕЛ"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] append [--allow-empty] [-m СЪОБЩЕНИЕ | "
-"-F ФАЙЛ | (-c | -C) ОБЕКТ] [ОБЕКТ]"
+"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] append [--allow-empty] [--"
+"[no-]separator|--separator=КРАЙ_НА_АБЗАЦ] [--[no-]stripspace] [-m СЪОБЩЕНИЕ "
+"| -F ФАЙЛ|(-c|-C) ОБЕКТ] [ОБЕКТ]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] edit [--allow-empty] [ОБЕКТ]"
@@ -9015,7 +9111,7 @@ msgstr "git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] show [ОБЕКТ]
 msgid ""
 "git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"
 msgstr ""
-"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] merge [-v | -q] [-s СТРАТЕГИЯ] "
+"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] merge [-v|-q] [-s СТРАТЕГИЯ] "
 "УКАЗАТЕЛ_ЗА_БЕЛЕЖКА"
 
 msgid "git notes [--ref <notes-ref>] remove [<object>...]"
@@ -9104,10 +9200,6 @@ msgid "cannot read note data from non-blob object '%s'."
 msgstr ""
 "съдържанието на бележка не може да се вземе от обект, който не е BLOB: „%s“."
 
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "входен ред с неправилен формат: „%s“."
-
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
 msgstr "бележката не може да се копира от „%s“ към „%s“"
@@ -9143,6 +9235,15 @@ msgstr "приемане и на празни бележки"
 msgid "replace existing notes"
 msgstr "замяна на съществуващите бележки"
 
+msgid "<paragraph-break>"
+msgstr "КРАЙ_НА_АБЗАЦ"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "вмъкване на такъв КРАЙ_НА_АБЗАЦ между абзаците"
+
+msgid "remove unnecessary whitespace"
+msgstr "премахване на излишните знаци за интервали"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -9305,15 +9406,14 @@ msgstr "да се използва бележката сочена от този
 msgid "unknown subcommand: `%s'"
 msgstr "непозната подкоманда: „%s“"
 
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects --stdout [ОПЦИЯ…] [< СПИСЪК_С_УКАЗАТЕЛИ | < СПИСЪК_С_ОБЕКТИ]"
+"git pack-objects --stdout [ОПЦИЯ…] [< СПИСЪК_С_УКАЗАТЕЛИ|< СПИСЪК_С_ОБЕКТИ]"
 
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects [ОПЦИЯ…] ПРЕФИКС_НА_ИМЕТО [< СПИСЪК_С_УКАЗАТЕЛИ | < "
+"git pack-objects [ОПЦИЯ…] ПРЕФИКС_НА_ИМЕТО [< СПИСЪК_С_УКАЗАТЕЛИ|< "
 "СПИСЪК_С_ОБЕКТИ]"
 
 #, c-format
@@ -9450,13 +9550,13 @@ msgid "packfile %s cannot be accessed"
 msgstr "пакетният файл „%s“ не може да бъде достъпен"
 
 msgid "Enumerating cruft objects"
-msgstr "Ð\98збÑ\80оÑ\8fване Ð½Ð° Ð¸Ð·Ð»Ð¸Ñ\88ните обекти"
+msgstr "Ð\98збÑ\80оÑ\8fване Ð½Ð° Ð½ÐµÐ½Ñ\83жните обекти"
 
 msgid "unable to add cruft objects"
-msgstr "неÑ\83Ñ\81пеÑ\88но Ð´Ð¾Ð±Ð°Ð²Ñ\8fне Ð½Ð° Ð¸Ð·Ð»Ð¸Ñ\88ни обекти"
+msgstr "неÑ\83Ñ\81пеÑ\88но Ð´Ð¾Ð±Ð°Ð²Ñ\8fне Ð½Ð° Ð½ÐµÐ½Ñ\83жни обекти"
 
 msgid "Traversing cruft objects"
-msgstr "Ð\9eбÑ\85ождане Ð½Ð° Ð¸Ð·Ð»Ð¸Ñ\88ните обекти"
+msgstr "Ð\9eбÑ\85ождане Ð½Ð° Ð½ÐµÐ½Ñ\83жните обекти"
 
 #, c-format
 msgid ""
@@ -9476,7 +9576,7 @@ msgstr ""
 
 msgid "could not load cruft pack .mtimes"
 msgstr ""
-"вÑ\80еменаÑ\82а Ð½Ð° Ð¿Ñ\80омÑ\8fна (.mtimes) Ð½Ð° Ð¿Ð°ÐºÐµÑ\82а Ñ\81 Ð¸Ð·Ð»Ð¸Ñ\88ни файлове не може да се "
+"вÑ\80еменаÑ\82а Ð½Ð° Ð¿Ñ\80омÑ\8fна (.mtimes) Ð½Ð° Ð¿Ð°ÐºÐµÑ\82а Ñ\81 Ð½ÐµÐ½Ñ\83жни файлове не може да се "
 "заредят"
 
 msgid "cannot open pack index"
@@ -9508,6 +9608,13 @@ msgstr "неподдържана версия на индекса „%s“"
 msgid "bad index version '%s'"
 msgstr "неправилна версия на индекса „%s“"
 
+msgid "show progress meter during object writing phase"
+msgstr "извеждане на напредъка във фазата на запазване на обектите"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr ""
+"същото действие като опцията „--all-progress“ при извеждането на напредъка"
+
 msgid "<version>[,<offset>]"
 msgstr "ВЕРСИЯ[,ОТМЕСТВАНЕ]"
 
@@ -9588,10 +9695,10 @@ msgid "unpack unreachable objects newer than <time>"
 msgstr "разпакетиране на недостижимите обекти, които са по-нови от това ВРЕМЕ"
 
 msgid "create a cruft pack"
-msgstr "Ñ\81Ñ\8aздаване Ð½Ð° Ð¿Ð°ÐºÐµÑ\82 Ñ\81 Ð¸Ð·Ð»Ð¸Ñ\88ните обекти"
+msgstr "Ñ\81Ñ\8aздаване Ð½Ð° Ð¿Ð°ÐºÐµÑ\82 Ñ\81 Ð½ÐµÐ½Ñ\83жните обекти"
 
 msgid "expire cruft objects older than <time>"
-msgstr "обÑ\8fвÑ\8fване Ð½Ð° Ð¸Ð·Ð»Ð¸Ñ\88ните обекти по-стари от това ВРЕМЕ за остарели"
+msgstr "обÑ\8fвÑ\8fване Ð½Ð° Ð½ÐµÐ½Ñ\83жните обекти по-стари от това ВРЕМЕ за остарели"
 
 msgid "use the sparse reachability algorithm"
 msgstr "използване на алгоритъм за частична достижимост"
@@ -9669,9 +9776,6 @@ msgstr ""
 "опцията „--thin“не може да се използва за създаване на пакетни файлове с "
 "индекс"
 
-msgid "cannot use --filter without --stdout"
-msgstr "опцията „--filter“ изисква „--stdout“"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "опциите „--filter“ и „--stdin-packs“ са несъвместими"
 
@@ -9685,9 +9789,6 @@ msgstr "вътрешният списък на версии и опцията 
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "опциите „--stdin-packs“ и „--cruft“ са несъвместими"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "опциите „--max-pack-size“ и „--cruft“ са несъвместими"
-
 msgid "Enumerating objects"
 msgstr "Изброяване на обектите"
 
@@ -9712,8 +9813,14 @@ msgstr ""
 "ни известите с е-писмо до пощенския списък:\n"
 "<git@vger.kernel.org>.\n"
 
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [ОПЦИЯ…]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "трябва да добавите и опцията „--i-still-use-this“"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include ШАБЛОН] [--exclude ШАБЛОН]"
 
 msgid "pack everything"
 msgstr "пакетиране на всичко"
@@ -9721,6 +9828,24 @@ msgstr "пакетиране на всичко"
 msgid "prune loose refs (default)"
 msgstr "окастряне на недостижимите указатели (стандартно)"
 
+msgid "references to include"
+msgstr "кои указатели да се включат"
+
+msgid "references to exclude"
+msgstr "кои указатели да се прескочат"
+
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable|--unstable|--verbatim]"
+
+msgid "use the unstable patch-id algorithm"
+msgstr "използване на нестабилния алгоритъм за идентифициране на кръпка"
+
+msgid "use the stable patch-id algorithm"
+msgstr "използване на стабилния алгоритъм за идентифициране на кръпка"
+
+msgid "don't strip whitespace from the patch"
+msgstr "без махане на празните знаци в кръпката"
+
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr "git prune [-n] [-v] [--progress] [--expire ВРЕМЕ] [--] [ВРЪХ…]"
 
@@ -9768,6 +9893,12 @@ msgstr "принудително презаписване на локалния
 msgid "number of submodules pulled in parallel"
 msgstr "брой подмодули издърпани паралелно"
 
+msgid "use IPv4 addresses only"
+msgstr "само адреси IPv4"
+
+msgid "use IPv6 addresses only"
+msgstr "само адреси IPv6"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9869,13 +10000,13 @@ msgstr ""
 "приоритет пред настройките.\n"
 
 msgid "Updating an unborn branch with changes added to the index."
-msgstr "Ð\9eбновÑ\8fване Ð½Ð° Ð²Ñ\81е Ð¾Ñ\89е Ð½ÐµÑ\81Ñ\8aздаден клон с промѐните от индекса"
+msgstr "Ð\9eбновÑ\8fване Ð½Ð° Ð½ÐµÑ\80одеÌ\80н клон с промѐните от индекса"
 
 msgid "pull with rebase"
 msgstr "издърпване с пребазиране"
 
-msgid "please commit or stash them."
-msgstr "трябва да подадете или скатаете промѐните."
+msgid "Please commit or stash them."
+msgstr "Промѐните трябва или да се подадат, или да се скатаят."
 
 #, c-format
 msgid ""
@@ -9913,7 +10044,7 @@ msgstr "Не може да превъртите към повече от еди
 
 msgid "Need to specify how to reconcile divergent branches."
 msgstr ""
-"ТÑ\80Ñ\8fбва Ð´Ð° Ñ\83кажеÑ\82е ÐºÐ°Ðº Ð´Ð° Ñ\81е Ñ\80еÑ\88аваÑ\82 Ñ\80азликиÑ\82е Ð¿Ñ\80и Ñ\80азминаване на клоните."
+"ТÑ\80Ñ\8fбва Ð´Ð° Ñ\83кажеÑ\82е ÐºÐ°Ðº Ð´Ð° Ñ\81е Ñ\80еÑ\88аваÑ\82 Ñ\80азликиÑ\82е Ð¿Ñ\80и Ñ\80аздалеÑ\87аване на клоните."
 
 msgid "cannot rebase with locally recorded submodule modifications"
 msgstr ""
@@ -9938,14 +10069,13 @@ msgstr ""
 
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
-"За да избегнете автоматичното задаване на следени клони, когато името им не "
-"съвпада с това на локалния клон, погледнете документацията за стойността "
+"За да избегнете автоматичното задаване на следени клони, когато името им\n"
+"не съвпада с това на локалния клон, погледнете документацията за стойността\n"
 "„simple“ на настройката „branch.autoSetupMerge“ в „git help config“.\n"
 
 #, c-format
@@ -10034,35 +10164,37 @@ msgstr ""
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Обновяването е отхвърлено, защото върхът на текущия ви клон е преди върха "
-"на\n"
+"Обновяването е отхвърлено, защото върхът на текущия ви клон следва върха на\n"
 "отдалечения клон.  Внесете отдалечените промѐни (напр. с командата „git "
 "pull…“),\n"
-"преди отново да изтласкате промѐните.  За повече информация вижте раздела\n"
-"„Note about fast-forwards“ в страницата от ръководството „git push --help“."
+"преди отново да изтласкате промѐните.\n"
+"За повече информация вижте раздела „Note about fast-forwards“ в страницата "
+"от\n"
+"ръководството „git push --help“."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Обновяването е отхвърлено, защото върхът на изтласквания клон е преди върха\n"
+"Обновяването е отхвърлено, защото върхът на изтласквания клон следва върха\n"
 "на отдалечения клон.  Преминете към клона и внесете отдалечените промѐни "
 "(напр.\n"
-"с командата „git pull…“), преди отново да изтласкате промѐните.  За повече\n"
-"информация погледнете раздела „Note about fast-forwards“ в страницата от\n"
+"с командата „git pull…“), преди отново да изтласкате промѐните.\n"
+"За повече информация вижте раздела „Note about fast-forwards“ в страницата "
+"от\n"
 "ръководството „git push --help“."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Обновяването е отхвърлено, защото хранилището, към което изтласквате, "
@@ -10092,14 +10224,17 @@ msgstr ""
 "да го променѝте да сочи към подобен обект.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Обновяването е отхвърлено, защото върхът на следящия клон е обновяван след\n"
-"последното изтегляне.  Внесете отдалечените промѐни (напр. с командата\n"
-"„git pull…“), преди отново принудително да изтласкате промѐните.\n"
+"Обновяването е отхвърлено, защото върхът на отдалечения клон съдържа "
+"промени\n"
+"след последното изтегляне.  Внесете отдалечените промѐни (напр. с командата\n"
+"„git pull…“), преди отново да изтласкате промѐните.\n"
+"За повече информация вижте раздела „Note about fast-forwards“ в страницата\n"
+"от ръководството „git push --help“."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -10109,6 +10244,13 @@ msgstr "Изтласкване към „%s“\n"
 msgid "failed to push some refs to '%s'"
 msgstr "част от указателите не бяха изтласкани към „%s“"
 
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"рекурсивно обхождане на подмодулите чрез „push.recurseSubmodules=only“.  "
+"Вместо това се ползва ПРИ НУЖДА"
+
 #, c-format
 msgid "invalid value for '%s'"
 msgstr "Неправилна стойност за „%s“"
@@ -10116,8 +10258,8 @@ msgstr "Неправилна стойност за „%s“"
 msgid "repository"
 msgstr "хранилище"
 
-msgid "push all refs"
-msgstr "изтласкване на всички указатели"
+msgid "push all branches"
+msgstr "изтласкване на всички клони"
 
 msgid "mirror all refs"
 msgstr "огледално копие на всички указатели"
@@ -10125,8 +10267,10 @@ msgstr "огледално копие на всички указатели"
 msgid "delete refs"
 msgstr "изтриване на указателите"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "изтласкване на етикетите (несъвместимо с опциите „--all“ и „--mirror“)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"изтласкване на етикетите (несъвместимо с опциите „--all“, „--branches“ и „--"
+"mirror“)"
 
 msgid "force updates"
 msgstr "принудително обновяване"
@@ -10247,13 +10391,15 @@ msgid "need two commit ranges"
 msgstr "необходими са два диапазона с подавания"
 
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=ПРЕФИКС) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=ФАЙЛ] (--empty | "
-"УКАЗАТЕЛ_КЪМ_ДЪРВО_1 [УКАЗАТЕЛ_КЪМ_ДЪРВО_2 [УКАЗАТЕЛ_КЪМ_ДЪРВО_3]])"
+"git read-tree [(-m [--trivial] [--aggressive]|--reset|--prefix=ПРЕФИКС)\n"
+"              [-u|-i]] [--index-output=ФАЙЛ] [--no-sparse-checkout]\n"
+"              (--empty|УКАЗАТЕЛ_КЪМ_ДЪРВО_1 [УКАЗАТЕЛ_КЪМ_ДЪРВО_2 "
+"[УКАЗАТЕЛ_КЪМ_ДЪРВО_3]])"
 
 msgid "write resulting index to <file>"
 msgstr "запазване на индекса в този ФАЙЛ"
@@ -10313,7 +10459,7 @@ msgid ""
 "git rebase [-i] [options] [--exec <cmd>] [--onto <newbase> | --keep-base] "
 "[<upstream> [<branch>]]"
 msgstr ""
-"git rebase [-i] [ОПЦИЯ…] [--exec КОМАНДА] [--onto НОВА_БАЗА | --keep-base] "
+"git rebase [-i] [ОПЦИЯ…] [--exec КОМАНДА] [--onto НОВА_БАЗА|--keep-base] "
 "[КЛОН_ИЗТОЧНИК [КЛОН]]"
 
 msgid ""
@@ -10343,8 +10489,8 @@ msgid "%s requires the merge backend"
 msgstr "„%s“ изисква пребазиране"
 
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "не може да се премине към новата база, зададена с „onto“: „%s“"
+msgid "invalid onto: '%s'"
+msgstr "неправилен указател върху какво да се пребазира: „%s“"
 
 #, c-format
 msgid "invalid orig-head: '%s'"
@@ -10388,18 +10534,34 @@ msgstr ""
 "\n"
 "В резултат те не може да се пребазират."
 
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Неправилен режим за „--rebase-merges“: %s"
+
 #, c-format
 msgid "could not switch to %s"
 msgstr "не може да се премине към „%s“"
 
+msgid "apply options and merge options cannot be used together"
+msgstr "опциите за прилагане и сливане са несъвместими"
+
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
 msgstr ""
 "неправилна стойност „%s“: вариантите са „drop“ (прескачане), "
 "„keep“ (запазване) и „ask“ (питане)"
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"ползването на „--rebase-merges“ с празен низ за аргумент е остаряло и в "
+"бъдеще ще спре да работи. Ползвайте опцията „--rebase-merges“ без аргумент, "
+"за да постигнете същия резултат."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10629,16 +10791,26 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "опцията „C“ очаква число за аргумент"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Неизвестна стратегия: „%s“"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+"опциите за прилагане са несъвместими с „rebase.autoSquash“.  Пробвайте да "
+"добавите опцията „--no-autosquash“"
 
-msgid "--strategy requires --merge or --interactive"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
 msgstr ""
-"опцията „--strategy“ изисква някоя от опциите „--merge“ или „--interactive“"
+"опциите за прилагане са несъвместими с „rebase.rebaseMerges“.  Пробвайте да "
+"добавите опцията „--no-rebase-merges“"
 
-msgid "apply options and merge options cannot be used together"
-msgstr "опциите за прилагане и сливане са несъвместими"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr ""
+"опциите за прилагане са несъвместими с „rebase.updateRefs“.  Пробвайте да "
+"добавите опцията „--no-update-refs“"
 
 #, c-format
 msgid "Unknown rebase backend: %s"
@@ -10664,7 +10836,7 @@ msgstr "не съществува клон/подаване „%s“"
 msgid "No such ref: %s"
 msgstr "Такъв указател няма: %s"
 
-msgid "Could not resolve HEAD to a revision"
+msgid "Could not resolve HEAD to a commit"
 msgstr "Подаването, сочено от указателя „HEAD“, не може да бъде открито"
 
 #, c-format
@@ -10679,9 +10851,6 @@ msgstr "„%s“: изисква се точно една база за преб
 msgid "Does not point to a valid commit '%s'"
 msgstr "Указателят „%s“ не сочи към подаване"
 
-msgid "Please commit or stash them."
-msgstr "Промѐните трябва или да се подадат, или да се скатаят."
-
 msgid "HEAD is up to date."
 msgstr "Указателят „HEAD“ е напълно актуален."
 
@@ -10792,7 +10961,7 @@ msgid ""
 msgstr ""
 "git reflog expire [--expire=ВРЕМЕ] [--expire-unreachable=ВРЕМЕ]\n"
 "                  [--rewrite] [--updateref] [--stale-fix]\n"
-"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
+"                  [--dry-run|-n] [--verbose] [--all [--single-worktree]|"
 "УКАЗАТЕЛ…]"
 
 msgid ""
@@ -10800,7 +10969,7 @@ msgid ""
 "                  [--dry-run | -n] [--verbose] <ref>@{<specifier>}..."
 msgstr ""
 "git reflog delete [--rewrite] [--updateref]\n"
-"                  [--dry-run | -n] [--verbose] УКАЗАТЕЛ@{УТОЧНЕНИЕ}…"
+"                  [--dry-run|-n] [--verbose] УКАЗАТЕЛ@{УТОЧНЕНИЕ}…"
 
 msgid "git reflog exists <ref>"
 msgstr "git reflog exists УКАЗАТЕЛ"
@@ -10868,7 +11037,7 @@ msgid ""
 "git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--"
 "mirror=<fetch|push>] <name> <url>"
 msgstr ""
-"git remote add [-t КЛОН] [-m ОСНОВЕН_КЛОН] [-f] [--tags | --no-tags] [--"
+"git remote add [-t КЛОН] [-m ОСНОВЕН_КЛОН] [-f] [--tags|--no-tags] [--"
 "mirror=<fetch|push>] ИМЕ АДРЕС"
 
 msgid "git remote rename [--[no-]progress] <old> <new>"
@@ -10878,19 +11047,18 @@ msgid "git remote remove <name>"
 msgstr "git remote remove ИМЕ"
 
 msgid "git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"
-msgstr "git remote set-head ИМЕ (-a | --auto | -d | --delete | КЛОН)"
+msgstr "git remote set-head ИМЕ (-a|--auto|-d|--delete|КЛОН)"
 
 msgid "git remote [-v | --verbose] show [-n] <name>"
-msgstr "git remote [-v | --verbose] show [-n] ИМЕ"
+msgstr "git remote [-v|--verbose] show [-n] ИМЕ"
 
 msgid "git remote prune [-n | --dry-run] <name>"
-msgstr "git remote prune [-n | --dry-run] ИМЕ"
+msgstr "git remote prune [-n|--dry-run] ИМЕ"
 
 msgid ""
 "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"
 msgstr ""
-"git remote [-v | --verbose] update [-p | --prune] [(ГРУПА | "
-"ОТДАЛЕЧЕНО_ХРАНИЛИЩЕ)…]"
+"git remote [-v|--verbose] update [-p|--prune] [(ГРУПА|ОТДАЛЕЧЕНО_ХРАНИЛИЩЕ)…]"
 
 msgid "git remote set-branches [--add] <name> <branch>..."
 msgstr "git remote set-branches [--add] ИМЕ КЛОН…"
@@ -10923,7 +11091,7 @@ msgid "git remote prune [<options>] <name>"
 msgstr "git remote prune [ОПЦИЯ…] ИМЕ"
 
 msgid "git remote update [<options>] [<group> | <remote>]..."
-msgstr "git remote update [ОПЦИЯ…] [ГРУПА | ОТДАЛЕЧЕНО_ХРАНИЛИЩЕ…]"
+msgstr "git remote update [ОПЦИЯ…] [ГРУПА|ОТДАЛЕЧЕНО_ХРАНИЛИЩЕ…]"
 
 #, c-format
 msgid "Updating %s"
@@ -10947,11 +11115,12 @@ msgstr "неправилна стойност за „--mirror“: %s"
 msgid "fetch the remote branches"
 msgstr "отдалечените клони не може да бъдат доставени"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "внасяне на всички етикети и принадлежащите им обекти при доставяне"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "може и да не се доставят никакви етикети (чрез опцията „--no-tags“)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"внасяне на всички етикети и принадлежащите им обекти при доставяне\n"
+"или да не се доставят никакви етикети (чрез опцията „--no-tags“)"
 
 msgid "branch(es) to track"
 msgstr "клон/и за следене"
@@ -11346,6 +11515,14 @@ msgstr "временният файл „%s“ не може да бъде от
 msgid "could not close refs snapshot tempfile"
 msgstr "временният файл със снимка на указателите не може да се затвори"
 
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "изтриването на остарялата битова маска „%s“ е невъзможно"
+
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "името на пакетния файл „%s“ не започва с директорията за обекти с „%s“"
+
 msgid "pack everything in a single pack"
 msgstr "пакетиране на всичко в пакет"
 
@@ -11356,15 +11533,15 @@ msgstr ""
 
 msgid "same as -a, pack unreachable cruft objects separately"
 msgstr ""
-"Ñ\81Ñ\8aÑ\89оÑ\82о ÐºÐ°Ñ\82о Ð¾Ð¿Ñ\86иÑ\8fÑ\82а â\80\9e-aâ\80\9c.  Ð\9dедоÑ\81Ñ\82ижимиÑ\82е Ð¸Ð·Ð»Ð¸Ñ\88ни обекти да се пакетират "
+"Ñ\81Ñ\8aÑ\89оÑ\82о ÐºÐ°Ñ\82о Ð¾Ð¿Ñ\86иÑ\8fÑ\82а â\80\9e-aâ\80\9c.  Ð\9dедоÑ\81Ñ\82ижимиÑ\82е Ð½ÐµÐ½Ñ\83жни обекти да се пакетират "
 "отделно"
 
 msgid "approxidate"
 msgstr "евристична дата"
 
-msgid "with -C, expire objects older than this"
+msgid "with --cruft, expire objects older than this"
 msgstr ""
-"с опцията „-C“: обявяване на обектите по-стари от това ВРЕМЕ за остарели"
+"с опцията „--cruft“: обявяване на обектите по-стари от това ВРЕМЕ за остарели"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr ""
@@ -11430,19 +11607,29 @@ 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 "префикс на имената на пакетите за филтрирани обекти"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "пакетите в хранилище с важни обекти не може да се трият"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "опцията „%s“ изисква „%s“"
+
 msgid "Nothing new to pack."
 msgstr "Нищо ново за пакетиране"
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "имеÑ\82о Ð½Ð° Ð¿Ð°ÐºÐµÑ\82ниÑ\8f Ñ\84айл â\80\9e%sâ\80\9c Ð½Ðµ Ð·Ð°Ð¿Ð¾Ñ\87ва Ñ\81 Ð´Ð¸Ñ\80екÑ\82оÑ\80иÑ\8fÑ\82а Ð·Ð° Ð¾Ð±ÐµÐºÑ\82и Ñ\81 „%s“"
+msgid "renaming pack to '%s' failed"
+msgstr "неÑ\83Ñ\81пеÑ\88но Ð¿Ñ\80еименÑ\83ване Ð½Ð° Ð¿Ð°ÐºÐµÑ\82ниÑ\8f Ñ\84айл Ð½Ð° „%s“"
 
 #, c-format
-msgid "missing required file: %s"
-msgstr "липÑ\81ва Ð·Ð°Ð´Ñ\8aлжиÑ\82елниÑ\8f Ñ\84айл â\80\9e%s“"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "Ð\9aомандаÑ\82а â\80\9egit pack-objectsâ\80\9c Ð½Ðµ Ð·Ð°Ð¿Ð¸Ñ\81а Ñ\84айл â\80\9e%sâ\80\9c Ð·Ð° Ð¿Ð°ÐºÐµÑ\82а â\80\9e%s-%s“"
 
 #, c-format
 msgid "could not unlink: %s"
@@ -11636,8 +11823,9 @@ msgstr "опцията „--convert-graft-file“ не приема аргуме
 msgid "only one pattern can be given with -l"
 msgstr "опцията „-l“ приема точно един шаблон"
 
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forget ПЪТ… | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr "git rerere [clear|forget ПЪТ…|diff|status|remaining|gc]"
 
 msgid "register clean resolutions in index"
 msgstr "регистриране на чисти корекции на конфликти в индекса"
@@ -11651,8 +11839,7 @@ msgstr "неуспешно генериране на разлика за „%s
 
 msgid ""
 "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
-msgstr ""
-"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [ПОДАВАНЕ]"
+msgstr "git reset [--mixed|--soft|--hard|--merge|--keep] [-q] [ПОДАВАНЕ]"
 
 msgid "git reset [-q] [<tree-ish>] [--] <pathspec>..."
 msgstr "git reset [-q] [УКАЗАТЕЛ_КЪМ_ДЪРВО] [--] ПЪТИЩА…"
@@ -11843,24 +12030,44 @@ msgstr "опцията „--prefix“ изисква аргумент"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "непознат режим за „--abbrev-ref“: „%s“"
 
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "опциите „--exclude-hidden“ и „--branches“ са несъвместими"
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "опциите „--exclude-hidden“ и „--tags“ са несъвместими"
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "опциите „--exclude-hidden“ и „--remotes“ са несъвместими"
+
 msgid "this operation must be run in a work tree"
 msgstr "тази команда трябва да се изпълни в работно дърво"
 
+msgid "Could not read the index"
+msgstr "Индексът не може да бъде прочетен"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "непознат режим за „--show-object-format“: „%s“"
 
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [ОПЦИЯ…] УКАЗАТЕЛ_КЪМ_ПОДАВАНЕ…"
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m НОМЕР_НА_РОДИТЕЛ] [-s]\n"
+"           [-S[ИДЕНТИФИКАТОР_НА_КЛЮЧ]] ПОДАВАНЕ…"
 
-msgid "git revert <subcommand>"
-msgstr "git revert ПОДКОМАНДА"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue|--skip|--abort|--quit)"
 
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [ОПЦИЯ…] УКАЗАТЕЛ_КЪМ_ПОДАВАНЕ…"
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m НОМЕР_НА_РОДИТЕЛ] [-s] [-x] [--ff]\n"
+"                [-S[ИДЕНТИФИКАТОР_НА_КЛЮЧ]] ПОДАВАНЕ…"
 
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick ПОДКОМАНДА"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue|--skip|--abort|--quit)"
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
@@ -11889,7 +12096,7 @@ msgid "edit the commit message"
 msgstr "редактиране на съобщението при подаване"
 
 msgid "parent-number"
-msgstr "номер на родителя"
+msgstr "номер на родител"
 
 msgid "select mainline parent"
 msgstr "избор на основния родител"
@@ -11921,8 +12128,14 @@ msgstr "неуспешна отмяна"
 msgid "cherry-pick failed"
 msgstr "неуспешно отбиране"
 
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [ОПЦИЯ…] [--] ФАЙЛ…"
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f|--force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=ФАЙЛ [--pathspec-file-nul]]\n"
+"       [--] [ПЪТ…]"
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -12001,16 +12214,21 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=ПАКЕТ]\n"
 "              [--verbose] [--thin] [--atomic]\n"
-"              [ХОСТ:]ДИРЕКТОРИЯ (--all | УКАЗАТЕЛ…])"
+"              [--[no-]signed|--signed=(true|false|if-asked)]\n"
+"              [ХОСТ:]ДИРЕКТОРИЯ (--all|УКАЗАТЕЛ…)"
 
 msgid "remote name"
 msgstr "име на отдалечено хранилище"
 
+msgid "push all refs"
+msgstr "изтласкване на всички указатели"
+
 msgid "use stateless RPC protocol"
 msgstr "използване на протокол без запазване на състоянието за RPC"
 
@@ -12024,13 +12242,14 @@ msgid "git shortlog [<options>] [<revision-range>] [[--] <path>...]"
 msgstr "git shortlog [ОПЦИЯ…] [ДИАПАЗОН_НА_ВЕРСИИТЕ] [[--] [ПЪТ…]]"
 
 msgid "git log --pretty=short | git shortlog [<options>]"
-msgstr "git log --pretty=short | git shortlog [ОПЦИЯ…]"
+msgstr "git log --pretty=short|git shortlog [ОПЦИЯ…]"
 
 msgid "using multiple --group options with stdin is not supported"
 msgstr "повече от една опции „--group“ са несъвместими със стандартния вход"
 
-msgid "using --group=trailer with stdin is not supported"
-msgstr "опцията „--group=trailer“ е несъвместима със стандартния вход"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "„%s“ не поддържа стандартния вход"
 
 #, c-format
 msgid "unknown group type: %s"
@@ -12067,15 +12286,17 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
-"git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
-"                [--current] [--color[=КОГА] | --no-color] [--sparse]\n"
-"                [--more=БРОЙ | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(РЕВИЗИЯ | УКАЗАТЕЛ)…]"
+"git show-branch [-a|--all] [-r|--remotes] [--topo-order|--date-order]\n"
+"                [--current] [--color[=КОГА]|--no-color] [--sparse]\n"
+"                [--more=<n>|--list|--independent|--merge-base]\n"
+"                [--no-name|--sha1-name] [--topics]\n"
+"                [(РЕВИЗИЯ|УКАЗАТЕЛ)…]"
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
-msgstr "git show-branch (-g | --reflog)[=БРОЙ[,БАЗА]] [--list] [УКАЗАТЕЛ]"
+msgstr "git show-branch (-g|--reflog)[=БРОЙ[,БАЗА]] [--list] [УКАЗАТЕЛ]"
 
 #, c-format
 msgid "ignoring %s; cannot handle more than %d ref"
@@ -12118,7 +12339,7 @@ msgid "show refs unreachable from any other ref"
 msgstr "извеждане на недостижимите указатели"
 
 msgid "show commits in topological order"
-msgstr "извеждане Ð½Ð° Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ\8fÑ\82а Ð² Ñ\82опологиÑ\87еÑ\81ка подредба"
+msgstr "извеждане Ð½Ð° Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ\8fÑ\82а Ð² Ñ\82опологиÑ\87на подредба"
 
 msgid "show only commits not on the first branch"
 msgstr "извеждане само на подаванията, които не са от първия клон"
@@ -12128,7 +12349,7 @@ msgstr "извеждане на сливанията, които може да 
 
 msgid "topologically sort, maintaining date order where possible"
 msgstr ""
-"Ñ\82опологиÑ\87еÑ\81ка подредба, при запазване на подредбата по дата, доколкото е\n"
+"Ñ\82опологиÑ\87на подредба, при запазване на подредбата по дата, доколкото е\n"
 "възможно"
 
 msgid "<n>[,<base>]"
@@ -12174,23 +12395,46 @@ msgid "Unknown hash algorithm"
 msgstr "Непознат алгоритъм за контролни суми"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d|--dereference]\n"
+"             [-s|--hash[=БРОЙ]] [--abbrev[=БРОЙ]] [--tags]\n"
+"             [--heads] [--] [ШАБЛОН…]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=ЧИСЛО]] [--abbrev[=ЧИСЛО]] [--tags] [--heads] [--] [ШАБЛОН…]"
+"git show-ref --verify [-q|--quiet] [-d|--dereference]\n"
+"             [-s|--hash[=БРОЙ]] [--abbrev[=БРОЙ]]\n"
+"             [--] [ШАБЛОН…]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=ШАБЛОН]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists УКАЗАТЕЛ"
+
+msgid "reference does not exist"
+msgstr "указателят не съществува"
+
+msgid "failed to look up reference"
+msgstr "соченото от указателя липсва"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "извеждане на етикетите (може да се комбинира с върховете)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "извеждане на върховете (може да се комбинира с етикетите)"
 
+msgid "check for reference existence without resolving"
+msgstr "проверка за съществуване на указател без проследяването му"
+
 msgid "stricter reference checking, requires exact ref path"
-msgstr "строга проверка на указателите, изисква се указател с пълен път"
+msgstr "по-строга проверка за указатели, изисква точен път на указател"
 
 msgid "show the HEAD reference, even if it would be filtered out"
 msgstr "задължително извеждане и на указателя HEAD"
@@ -12211,8 +12455,15 @@ msgstr ""
 "извеждане на указателите приети от стандартния вход, които липсват в "
 "локалното хранилище"
 
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) ОПЦИЯ…"
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "опциите „%s“, „%s“ и „%s“ са несъвместими"
+
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init|list|set|add|reapply|disable|check-rules) ОПЦИЯ…"
 
 msgid "this worktree is not sparse"
 msgstr "това работно дърво не е частично"
@@ -12309,7 +12560,7 @@ msgstr ""
 "ръководството на командата „git-sparse-checkout“)."
 
 msgid "git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"
-msgstr "git sparse-checkout add [--skip-checks] (--stdin | ШАБЛОН…)"
+msgstr "git sparse-checkout add [--skip-checks] (--stdin|ШАБЛОН…)"
 
 msgid ""
 "skip some sanity checks on the given paths that might give false positives"
@@ -12328,7 +12579,7 @@ msgid ""
 "(--stdin | <patterns>)"
 msgstr ""
 "git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] [--skip-checks] "
-"(--stdin | ШАБЛОН…)"
+"(--stdin|ШАБЛОН…)"
 
 msgid "must be in a sparse-checkout to reapply sparsity patterns"
 msgstr ""
@@ -12338,67 +12589,74 @@ msgstr ""
 msgid "error while refreshing working directory"
 msgstr "грешка при обновяване на работната директория"
 
-msgid "git stash list [<options>]"
-msgstr "git stash list [ОПЦИЯ…]"
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks] [--[no-]cone] [--rules-"
+"file ФАЙЛ]"
 
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [ОПЦИЯ…] [СКАТАНО]"
+msgid "terminate input and output files by a NUL character"
+msgstr "разделяне на входните и изходните файлове с нулевия знак „NUL“"
 
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [СКАТАНО]"
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"когато е придружено с „--rules-file“ шаблоните се третират като шаблони в "
+"пътеводен режим"
 
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [СКАТАНО]"
+msgid "use patterns in <file> instead of the current ones."
+msgstr "ползване на шаблоните във ФАЙЛа вместо текущите."
 
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch КЛОН [СКАТАНО]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [ОПЦИЯ_ЗА_ЖУРНАЛ…]"
 
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message СЪОБЩЕНИЕ]\n"
-"          [--pathspec-from-file=ФАЙЛ [--pathspec-file-nul]]\n"
-"          [--] [ПЪТ…]]"
+"git stash show [-u|--include-untracked|--only-untracked] [ОПЦИЯ_ЗА_РАЗЛИКА…] "
+"[СКАТАНО]"
 
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [СЪОБЩЕНИЕ]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q|--quiet] [СКАТАНО]"
 
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
 msgstr "git stash pop [--index] [-q|--quiet] [СКАТАНО]"
 
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
 msgstr "git stash apply [--index] [-q|--quiet] [СКАТАНО]"
 
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch КЛОН [СКАТАНО]"
+
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
 msgstr "git stash store [-m|--message СЪОБЩЕНИЕ] [-q|--quiet] ПОДАВАНЕ"
 
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message СЪОБЩЕНИЕ]\n"
+"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q | --"
+"quiet]\n"
+"          [-u|--include-untracked] [-a|--all] [(-m|--message) СЪОБЩЕНИЕ]\n"
+"          [--pathspec-from-file=ФАЙЛ [--pathspec-file-nul]]\n"
 "          [--] [ПЪТ…]]"
 
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [СЪОБЩЕНИЕ]]"
+"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
+"quiet]\n"
+"          [-u|--include-untracked] [-a|--all] [СЪОБЩЕНИЕ]"
+
+msgid "git stash create [<message>]"
+msgstr "git stash create [СЪОБЩЕНИЕ]"
 
 #, c-format
 msgid "'%s' is not a stash-like commit"
@@ -12774,7 +13032,7 @@ msgstr "премахване на регистрациите на всички 
 
 msgid ""
 "git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"
-msgstr "git submodule deinit [--quiet] [-f | --force] [--all | [--] [ПЪТ…]]"
+msgstr "git submodule deinit [--quiet] [-f|--force] [--all|[--] [ПЪТ…]]"
 
 msgid "Use '--all' if you really want to deinitialize all submodules"
 msgstr "Използвайте „--all“, за да премахнете всички подмодули"
@@ -12875,6 +13133,10 @@ msgstr "Прескачане на неслетия подмодул „%s“"
 msgid "Skipping submodule '%s'"
 msgstr "Прескачане на подмодула „%s“"
 
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "не може да се клонира подмодул „%s“ без адрес"
+
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "Неуспешен опит за клониране на „%s“.  Насрочен е втори опит"
@@ -12973,9 +13235,6 @@ msgstr "рекурсивно обхождане на подмодулите"
 msgid "don't fetch new objects from the remote site"
 msgstr "без доставяне на новите обекти от отдалеченото хранилище"
 
-msgid "path into the working tree"
-msgstr "път към работното дърво"
-
 msgid "use the 'checkout' update strategy (default)"
 msgstr ""
 "използване на стратегията за обновяване „checkout“ (изтегляне — стандартно)"
@@ -13012,27 +13271,12 @@ msgstr ""
 "shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
 "[--] [ПЪТ…]"
 
-msgid "recurse into submodules"
-msgstr "рекурсивно обхождане подмодулите"
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Не може да се открие към какво сочи указателят „HEAD“"
 
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [ОПЦИЯ…] [ПЪТ…]"
 
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr "проверка дали писането във файла „.gitmodules“ е безопасно"
-
-msgid "unset the config in the .gitmodules file"
-msgstr "изтриване на настройка във файла „.gitmodules“"
-
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config ИМЕ [СТОЙНОСТ]"
-
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset ИМЕ"
-
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr "файлът „.gitmodules“ трябва да е в работното дърво"
-
 msgid "suppress output for setting url of a submodule"
 msgstr "без извеждане на информация при задаването на адреса на подмодул"
 
@@ -13112,6 +13356,9 @@ msgstr "Активиране на локалното хранилище за п
 msgid "unable to checkout submodule '%s'"
 msgstr "Подмодулът „%s“ не може да бъде изтеглен"
 
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr "файлът „.gitmodules“ трябва да е в работното дърво"
+
 #, c-format
 msgid "Failed to add submodule '%s'"
 msgstr "Неуспешно добавяне на подмодула „%s“"
@@ -13165,19 +13412,17 @@ msgstr ""
 msgid "'%s' is not a valid submodule name"
 msgstr "„%s“ е неправилно име за подмодул"
 
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "„%s“ не поддържа опцията „--super-prefix“"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper КОМАНДА"
 
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "„%s“ не е подкоманда на „submodule--helper“"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m ПРИЧИНА] ИМЕ УКАЗАТЕЛ"
 
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [ОПЦИЯ…] ИМЕ [УКАЗАТЕЛ]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] ИМЕ"
 
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] ИМЕ"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] ИМЕ"
 
 msgid "suppress error message for non-symbolic (detached) refs"
 msgstr "без извеждане на грешка за несвързани (несимволни) указатели"
@@ -13188,6 +13433,9 @@ msgstr "изтриване на символен указател"
 msgid "shorten ref output"
 msgstr "кратка информация за указателя"
 
+msgid "recursively dereference (default)"
+msgstr "рекурсивно извеждане на идентификатори (стандартно)"
+
 msgid "reason"
 msgstr "причина"
 
@@ -13195,24 +13443,25 @@ msgid "reason of the update"
 msgstr "причина за обновяването"
 
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u ИДЕНТИФИКАТОР_НА_КЛЮЧ] [-f] [-m СЪОБЩЕНИЕ | -F ФАЙЛ]\n"
-"        Ð\95ТÐ\98Ð\9aÐ\95Т [Ð\92РЪХ]"
+"git tag [-a|-s|-u ИДЕНТИФИКАТОР_НА_КЛЮЧ] [-f] [-m СЪОБЩЕНИЕ|-F ФАЙЛ] [-e]\n"
+"        Ð\95ТÐ\98Ð\9aÐ\95Т [Ð\9fÐ\9eÐ\94Ð\90Ð\92Ð\90Ð\9dÐ\95\9eÐ\91Ð\95Ð\9aТ]"
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d ЕТИКЕТ…"
 
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[БРОЙ]] [--contains ПОДАВАНЕ] [--no-contains ПОДАВАНЕ]\n"
-"           [--points-at ОБЕКТ] [--format=ФОРМАТ] [--merged ПОДАВАНЕ]\n"
-"           [--no-merged ПОДАВАНЕ] [ШАБЛОН…]"
+"git tag [-n[БРОЙ]] -l [--contains ПОДАВАНЕ] [--no-contains ПОДАВАНЕ]\n"
+"        [--points-at ОБЕКТ] [--column[=ОПЦИЯ…]|--no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=ФОРМАТ]\n"
+"        [--merged ПОДАВАНЕ] [--no-merged ПОДАВАНЕ] [ШАБЛОН…]"
 
 msgid "git tag -v [--format=<format>] <tagname>..."
 msgstr "git tag -v [--format=ФОРМАТ] ЕТИКЕТ…"
@@ -13511,6 +13760,9 @@ msgstr ""
 msgid "write index in this format"
 msgstr "записване на индекса в този формат"
 
+msgid "report on-disk index format version"
+msgstr "извеждане на версията на форма̀та на индекса на диска"
+
 msgid "enable or disable split index"
 msgstr "включване или изключване на разделянето на индекса"
 
@@ -13536,6 +13788,14 @@ msgstr "отбелязване на файловете, че може да се
 msgid "clear fsmonitor valid bit"
 msgstr "изчистване на флага за следенето чрез файловата система"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "версия на индекс: бе %d, променена на %d"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13614,8 +13874,12 @@ msgstr "изчитане на указателите от стандартния
 msgid "update the info files from scratch"
 msgstr "обновяване на информационните файлове от нулата"
 
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [ОПЦИЯ…] ДИРЕКТОРИЯ"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=БРОЙ] [--stateless-rpc]\n"
+"                [--advertise-refs] ДИРЕКТОРИЯ"
 
 msgid "quit after a single request/response exchange"
 msgstr "изход след първоначалната размяна на заявка и отговор"
@@ -13630,8 +13894,8 @@ msgstr ""
 msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "трансферът да се преустанови след този БРОЙ секунди"
 
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] ПОДАВАНЕ…"
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v|--verbose] [--raw] ПОДАВАНЕ…"
 
 msgid "print commit contents"
 msgstr "извеждане на съдържанието на подаването"
@@ -13639,8 +13903,8 @@ msgstr "извеждане на съдържанието на подаванет
 msgid "print raw gpg status output"
 msgstr "извеждане на необработения изход от състоянието на „gpg“"
 
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] ПАКЕТ…"
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr "git verify-pack [-v|--verbose] [-s|--stat-only] [--] ПАКЕТ.idx…"
 
 msgid "verbose"
 msgstr "извеждане на подробна информация"
@@ -13648,35 +13912,70 @@ msgstr "извеждане на подробна информация"
 msgid "show statistics only"
 msgstr "извеждане само на статистиката"
 
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=ФОРМАТ] ЕТИКЕТ…"
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v|--verbose] [--format=ФОРМАТ] [--raw] ЕТИКЕТ…"
 
 msgid "print tag contents"
 msgstr "извеждане на съдържанието на ЕТИКЕТи"
 
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [ОПЦИЯ…] ПЪТ [УКАЗАТЕЛ_КЪМ_ПОДАВАНЕ]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason НИЗ]]\n"
+"                 [--orphan] [(-b|-B) НОВ_КЛОН] ПЪТ [УКАЗАТЕЛ_КЪМ_ПОДАВАНЕ]"
 
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [ОПЦИЯ…]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v|--porcelain [-z]]"
 
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [ОПЦИЯ…] [ПЪТ]"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason ПРИЧИНА] ФОРМАТ"
 
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move [ДЪРВО] [НОВ_ПЪТ]"
 
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [ОПЦИЯ…]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire ВРЕМЕ]"
 
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [ОПЦИЯ…] [ДЪРВО]"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] ДЪРВО"
 
 msgid "git worktree repair [<path>...]"
 msgstr "git worktree repair [ПЪТ…]"
 
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock [ПЪТ]"
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock ДЪРВО"
+
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Липсва клон-източник, затова се приема „--orphan“"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"За да създадете работно дърво за това хранилище\n"
+"с нов неродѐн клон — който няма дори и начално подаване,\n"
+"ползвайте опцията „--orphan“:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"За да създадете работно дърво за това хранилище\n"
+"с нов неродѐн клон — който няма дори и начално подаване,\n"
+"ползвайте опцията „--orphan“:\n"
+"\n"
+"    git worktree add --orphan %s\n"
 
 #, c-format
 msgid "Removing %s/%s: %s"
@@ -13749,10 +14048,38 @@ msgstr ""
 msgid "Preparing worktree (checking out '%s')"
 msgstr "Приготвяне на работното дърво (изтегляне на „%s“)"
 
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "недостижим обект: неправилен указател: %s"
+
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Подготвяне на работно дърво (указателят „HEAD“ не свързан: %s)"
 
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD сочи към неправилен или неродѐн указател.\n"
+"HEAD path: „%s“\n"
+"HEAD contents: „%s“"
+
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"Не съществуват никакви локални или отдалечени указатели, въпреки че има\n"
+"поне едно следено хранилище. Работата спира.\n"
+"Ползвайте комбинацията „add -f“ за принудително действие или първо "
+"доставете\n"
+"обектите от отдалеченото хранилище"
+
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "опциите „%s“ и „%s“ са несъвместими"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "Изтегляне КЛОНа, дори и да е изтеглен в друго работно дърво"
 
@@ -13762,6 +14089,9 @@ msgstr "създаване на нов клон"
 msgid "create or reset a branch"
 msgstr "създаване или зануляване на клони"
 
+msgid "create unborn/orphaned branch"
+msgstr "създаване на неродѐн клон"
+
 msgid "populate the new working tree"
 msgstr "подготвяне на новото работно дърво"
 
@@ -13781,6 +14111,13 @@ msgstr "опит за напасване на името на новия кло
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "опциите „%s“, „%s“ и „%s“ са несъвместими"
 
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "опциите „%s“ и „%s“ са несъвместими"
+
+msgid "<commit-ish>"
+msgstr "ПОДАВАНЕ"
+
 msgid "added with --lock"
 msgstr "добавена с „--lock“"
 
@@ -13918,23 +14255,60 @@ msgstr "само за изчистване на грешки"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "„core.fsyncMethod = batch“ не се поддържа на тази платформа"
 
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr ""
+"ключът за списък с пратки „%s“ не може да се анализира със стойност „%s“"
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "липсва режим в списъка от пратки „%s“"
+
 msgid "failed to create temporary file"
 msgstr "не може да се създаде временен файл"
 
 msgid "insufficient capabilities"
 msgstr "недостатъчно възможности"
 
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "изтегленият от адрес „%s“ файл не е пратка"
+
+msgid "failed to store maximum creation token"
+msgstr "неуспешно запазване на данните за последно създаденото"
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "непознат режим на пратки от адрес „%s“"
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr ""
+"преминато е ограничениeто за рекурсивно обхождане на адреси на пратки (%d)"
+
 #, c-format
 msgid "failed to download bundle from URI '%s'"
 msgstr "неуспешно изтегляне на пратка от адрес: „%s“"
 
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "Ñ\84айлÑ\8aÑ\82 Ð½Ð° Ð°Ð´Ñ\80еÑ\81 â\80\9e%sâ\80\9c Ð½Ðµ Ðµ Ð¿Ñ\80аÑ\82ка"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "Ñ\84айлÑ\8aÑ\82 Ð½Ð° Ð°Ð´Ñ\80еÑ\81 â\80\9e%sâ\80\9c Ð½Ðµ Ðµ Ð½Ð¸Ñ\82о Ð¿Ñ\80аÑ\82ка, Ð½Ð¸Ñ\82о Ñ\81пиÑ\81Ñ\8aк Ñ\81 Ð¿Ñ\80аÑ\82ки"
 
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "неуспешно разпакетиране на пратка от адрес „%s“"
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: непознат аргумент: %s"
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: след аргументите се очаква изчистване на буферите"
+
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: получен е празен ред"
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: редът не е във формат „КЛЮЧ=СТОЙНОСТ“"
+
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: редът съдържа празен ключ или стойност"
 
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
@@ -13958,6 +14332,13 @@ msgstr "В хранилището липсват следните необход
 msgid "need a repository to verify a bundle"
 msgstr "за проверката на пратка е необходимо хранилище"
 
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"в хранилището на обекти съществуват необходими подавания, които не са "
+"свързани с историята на хранилището"
+
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -13973,6 +14354,14 @@ msgid_plural "The bundle requires these %<PRIuMAX> refs:"
 msgstr[0] "Пратката изисква следния указател:"
 msgstr[1] "Пратката изисква следните %<PRIuMAX> указатели:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Пратката ползва следния алгоритъм за контролни суми „%s“"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Пратката изисква следния филтър: %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "неуспешно дублиране на дескриптора на пратката с „dup“"
 
@@ -14008,6 +14397,10 @@ msgstr "командата „git index-pack“ не завърши успешн
 msgid "terminating chunk id appears earlier than expected"
 msgstr "идентификаторът за краен откъс се явява по-рано от очакваното"
 
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "откъсът с идентификатор %<PRIx32> не е подравнен по %d-байта"
+
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "неправилно отместване на откъс/и %<PRIx64> и %<PRIx64>"
@@ -14062,9 +14455,9 @@ msgstr "Събиране на информация за потребителя 
 msgid "Move objects and refs by archive"
 msgstr "Местене на обекти и указатели по архиви"
 
-msgid "Provide content or type and size information for repository objects"
+msgid "Provide contents or details of repository objects"
 msgstr ""
-"Ð\9fÑ\80едоÑ\81Ñ\82авÑ\8fне Ð½Ð° Ñ\81Ñ\8aдÑ\8aÑ\80жаниеÑ\82о Ð¸Ð»Ð¸ Ð²Ð¸Ð´Ð° Ð¸ Ñ\80азмеÑ\80иÑ\82е на обекти от хранилище"
+"Ð\9fÑ\80едоÑ\81Ñ\82авÑ\8fне Ð½Ð° Ñ\81Ñ\8aдÑ\8aÑ\80жаниеÑ\82о Ð¸Ð»Ð¸ Ð´Ñ\80Ñ\83га Ð¸Ð½Ñ\84оÑ\80маÑ\86иÑ\8f на обекти от хранилище"
 
 msgid "Display gitattributes information"
 msgstr "Извеждане на информацията за атрибутите на git (gitattributes)"
@@ -14163,7 +14556,7 @@ msgstr "Сравняване на обекти-дърво с работното
 
 msgid "Compares the content and mode of blobs found via two tree objects"
 msgstr ""
-"Сравняване на съдържанието и правата за достъп на обектите-BLOB чрез два "
+"Сравняване на съдържанието и права̀та за достъп на обектите-BLOB чрез два "
 "обекта-дърво"
 
 msgid "Show changes using common diff tools"
@@ -14211,10 +14604,9 @@ msgstr "Извеждане на редовете напасващи на шаб
 msgid "A portable graphical interface to Git"
 msgstr "Графичен интерфейс към Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
+msgid "Compute object ID and optionally create an object from a file"
 msgstr ""
-"Изчисляване на идентификатор на обект и евентуално създаване на обект-BLOB "
-"от файл"
+"Изчисляване на идентификатор на обект и евентуално създаване на обект от файл"
 
 msgid "Display help information about Git"
 msgstr "Извеждане на помощта за Git"
@@ -14482,7 +14874,7 @@ msgstr "Проверка на подписите GPG върху етикетит
 msgid "Display version information about Git"
 msgstr "Извеждане на версията на Git"
 
-msgid "Show logs with difference each commit introduces"
+msgid "Show logs with differences each commit introduces"
 msgstr "Извеждане на журнал с разликите, въведени с всяко подаване"
 
 msgid "Manage multiple working trees"
@@ -14521,7 +14913,7 @@ msgstr "Формат на пратките"
 msgid "Chunk-based file formats"
 msgstr "Формати на откъсите"
 
-msgid "Git commit graph format"
+msgid "Git commit-graph format"
 msgstr "Формат на гра̀фа с подаванията"
 
 msgid "Git index format"
@@ -14618,9 +15010,16 @@ msgstr "файлът с гра̀фа на подаванията е твърде
 msgid "commit-graph has no base graphs chunk"
 msgstr "базовият откъс липсва в гра̀фа с подаванията"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "базовият откъс в гра̀фа с подаванията е прекалено малък"
+
 msgid "commit-graph chain does not match"
 msgstr "веригата на гра̀фа с подаванията не съвпада"
 
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "броят подавания в основния граф е прекалено голям: %<PRIuMAX>"
+
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr ""
@@ -14642,6 +15041,9 @@ msgstr ""
 "графът с подаванията изисква генериране на данни за отместването, но такива "
 "липсват"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr "прекалено малко данни за прелелите поколения в гра̀фа с подаванията"
+
 msgid "Loading known commits in commit graph"
 msgstr "Зареждане на познатите подавания в гра̀фа с подаванията"
 
@@ -14684,14 +15086,15 @@ msgid "Finding extra edges in commit graph"
 msgstr "Откриване на още върхове в гра̀фа с подаванията"
 
 msgid "failed to write correct number of base graph ids"
-msgstr "правилният брой на базовите идентификатори не може да се запише"
+msgstr ""
+"правилният брой на идентификаторите в основния граф не може да се запише"
 
 msgid "unable to create temporary graph layer"
 msgstr "не може да бъде създаден временен слой за гра̀фа с подаванията"
 
 #, c-format
 msgid "unable to adjust shared permissions for '%s'"
-msgstr "правата за споделен достъп до „%s“ не може да бъдат зададени"
+msgstr "права̀та за споделен достъп до „%s“ не може да бъдат зададени"
 
 #, c-format
 msgid "Writing out commit graph in %d pass"
@@ -14708,6 +15111,15 @@ msgstr "основният файл на гра̀фа с подаванията
 msgid "failed to rename temporary commit-graph file"
 msgstr "временният файл на гра̀фа с подаванията не може да бъде преименуван"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr ""
+"не може да се слеят графове с %<PRIuMAX> и %<PRIuMAX> подавания (съответно)"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "графът „%s“ не може да се слее, прекалено много подавания: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Търсене на подаванията със сливания"
 
@@ -14734,16 +15146,13 @@ msgstr ""
 #, c-format
 msgid "commit-graph has incorrect fanout value: fanout[%d] = %u != %u"
 msgstr ""
-"непÑ\80авилна Ñ\81Ñ\82ойноÑ\81Ñ\82 Ð·Ð° Ð¾Ñ\82кÑ\8aÑ\81 Ð² Ð³Ñ\80аÌ\80Ñ\84а Ñ\81 Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ\8fÑ\82а: fanout[%d] = %u, Ð° "
-"трябва да е %u"
+"непÑ\80авилна Ñ\81Ñ\82ойноÑ\81Ñ\82 Ð·Ð° Ð¾Ñ\82кÑ\8aÑ\81 Ð·Ð° Ñ\80азпÑ\80еделÑ\8fне Ð² Ð³Ñ\80аÌ\80Ñ\84а Ñ\81 Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ\8fÑ\82а: "
+"fanout[%d] = %u, а трябва да е %u"
 
 #, c-format
 msgid "failed to parse commit %s from commit-graph"
 msgstr "подаване „%s“ в гра̀фа с подаванията не може да се анализира"
 
-msgid "Verifying commits in commit graph"
-msgstr "Проверка на подаванията в гра̀фа"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14768,20 +15177,6 @@ msgstr "родителят на „%s“ в гра̀фа с подаваният
 msgid "commit-graph parent list for commit %s terminates early"
 msgstr "списъкът с родители на „%s“ в гра̀фа с подаванията е прекалено къс"
 
-#, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"номерът на поколението на подаване „%s“ в гра̀фа с подаванията е 0, а другаде "
-"не е"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"номерът на поколението на подаване „%s“ в гра̀фа с подаванията не е 0, а "
-"другаде е"
-
 #, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr ""
@@ -14794,6 +15189,17 @@ msgstr ""
 "датата на подаване на „%s“ в гра̀фа с подаванията е %<PRIuMAX>, а трябва да е "
 "%<PRIuMAX>"
 
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"гра̀фа с подаванията съдържа както нулеви, така и ненулеви поколения (напр. "
+"подаванията „%s“ и „%s“)"
+
+msgid "Verifying commits in commit graph"
+msgstr "Проверка на подаванията в гра̀фа"
+
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s не е подаване!"
@@ -14820,6 +15226,12 @@ msgstr ""
 "\n"
 "    git config advice.graftFileDeprecated false"
 
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"подаването „%s“ присъства в гра̀фа с подаванията, но липсва в базата от данни "
+"за обектите"
+
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr ""
@@ -14888,8 +15300,12 @@ msgstr "необработен случай в „has_worktree_moved“: %d"
 #, c-format
 msgid "health thread wait failed [GLE %ld]"
 msgstr ""
-"грешка в нишката за следене на състоянието [последна грешка в нишката: GLE="
-"%1$ld]"
+"грешка в нишката за следене на състоянието [последна грешка в нишката: "
+"GLE=%1$ld]"
+
+#, c-format
+msgid "Invalid path: %s"
+msgstr "Неправилен път: „%s“"
 
 msgid "Unable to create FSEventStream."
 msgstr "Неуспешно създаване на „FSEventStream“."
@@ -14911,8 +15327,8 @@ msgstr "„%2$s“ не може да се наблюдава [последна
 #, c-format
 msgid "[GLE %ld] could not get longname of '%s'"
 msgstr ""
-"дългото име на „%2$s“ те може да се получи [последна грешка в нишката: GLE="
-"%1$ld]"
+"дългото име на „%2$s“ те може да се получи [последна грешка в нишката: "
+"GLE=%1$ld]"
 
 #, c-format
 msgid "ReadDirectoryChangedW failed on '%s' [GLE %ld]"
@@ -14932,6 +15348,22 @@ msgstr ""
 "промѐните по директориите не може да бъдат прочетени [последна грешка в "
 "нишката: GLE=%ld]"
 
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "неуспешно изпълнение на opendir(„%s“)"
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "неуспешно изпълнение на lstat(„%s“)"
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "неуспешно изпълнение на strbuf_readlink(„%s“)"
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "неуспешно изпълнение на closedir(„%s“)"
+
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
 msgstr ""
@@ -15220,8 +15652,8 @@ msgstr "дължината на съкращаване е извън диапа
 msgid "bad zlib compression level %d"
 msgstr "неправилно ниво на компресиране: %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "настройката „core.commentChar“ трябва да е само един знак"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "настройката „core.commentChar“ трябва да е само един знак от ASCII"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -15262,10 +15694,6 @@ msgstr "указателят „%s“ не сочи към обект-BLOB"
 msgid "unable to resolve config blob '%s'"
 msgstr "обектът-BLOB „%s“ с конфигурации не може да бъде открит"
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "„%s“ не може да бъде анализиран"
-
 msgid "unable to parse command-line config"
 msgstr "неправилни настройки от командния ред"
 
@@ -15324,7 +15752,7 @@ msgstr "неуспешно изпълнение на „mmap“ върху „%s
 
 #, c-format
 msgid "chmod on %s failed"
-msgstr "неуспешна смяна на права с „chmod“ върху „%s“"
+msgstr "неуспешна смяна на права̀ с „chmod“ върху „%s“"
 
 #, c-format
 msgid "could not write config file %s"
@@ -15338,6 +15766,10 @@ msgstr "„%s“ не може да се зададе да е „%s“"
 msgid "invalid section name: %s"
 msgstr "неправилно име на раздел: %s"
 
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "ред %2$<PRIuMAX> в „%1$s“ е прекалено дълъг"
+
 #, c-format
 msgid "missing value for '%s'"
 msgstr "липсва стойност за „%s“"
@@ -15353,7 +15785,7 @@ msgid ""
 msgstr ""
 "Не може да се чете от отдалеченото хранилище.\n"
 "\n"
-"Проверете дали то съществува и дали имате права\n"
+"Проверете дали то съществува и дали имате права̀\n"
 "за достъп."
 
 #, c-format
@@ -15393,6 +15825,17 @@ msgstr "протоколна грешка: неочаквано „%s“"
 msgid "unknown object format '%s' specified by server"
 msgstr "сървърът указа непознат формат на обект: „%s“"
 
+#, c-format
+msgid "error on bundle-uri response line %d: %s"
+msgstr "грешка на ред %d в отговора на командата „bundle-uri“: %s"
+
+msgid "expected flush after bundle-uri listing"
+msgstr ""
+"след изброяването на адресите на пратките се очаква изчистване на буферите"
+
+msgid "expected response end packet after ref listing"
+msgstr "след изброяването на указателите се очаква пакет за край"
+
 #, c-format
 msgid "invalid ls-refs response: %s"
 msgstr "неправилен отговор на „ls-refs“: „%s“"
@@ -15400,9 +15843,6 @@ msgstr "неправилен отговор на „ls-refs“: „%s“"
 msgid "expected flush after ref listing"
 msgstr "след изброяването на указателите се очаква изчистване на буферите"
 
-msgid "expected response end packet after ref listing"
-msgstr "след изброяването на указателите се очаква пакет за край"
-
 #, c-format
 msgid "protocol '%s' is not supported"
 msgstr "протокол „%s“ не се поддържа"
@@ -15738,9 +16178,6 @@ msgstr "неуспешен запис на архива"
 msgid "--merge-base does not work with ranges"
 msgstr "опцията „--merge-base“ не работи с диапазони"
 
-msgid "--merge-base only works with commits"
-msgstr "опцията „--merge-base“ работи само с подавания"
-
 msgid "unable to get HEAD"
 msgstr "Указателят „HEAD“ не може да бъде получен"
 
@@ -15750,6 +16187,12 @@ msgstr "липсва база за сливане"
 msgid "multiple merge bases found"
 msgstr "много бази за сливане"
 
+msgid "cannot compare stdin to a directory"
+msgstr "стандартният вход не може да се сравни с директория"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "именован канал не може да се сравни с директория"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [ОПЦИЯ…] ПЪТ ПЪТ"
 
@@ -15814,6 +16257,13 @@ msgid "external diff died, stopping at %s"
 msgstr ""
 "външната програма за разлики завърши неуспешно.  Спиране на работата при „%s“"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "опцията „--follow“ изисква точно един път"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "магическите пътища не се поддържат от „--follow“: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr "опциите „%s“, „%s“, „%s“ и „%s“ са несъвместими"
@@ -15827,9 +16277,6 @@ msgid ""
 "options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"
 msgstr "опциите „%s“ и „%s“ са несъвместими, използвайте „%s“ с „%s“ и „%s“"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "опцията „--follow“ изисква точно един път"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "неправилна стойност за „--stat“: %s"
@@ -16026,6 +16473,9 @@ msgstr "добавяне на допълнителен префикс за вс
 msgid "do not show any source or destination prefix"
 msgstr "без префикс за източника и целта"
 
+msgid "use default prefixes a/ and b/"
+msgstr "ползване на стандартните префикси „a/“ и „b/“"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "извеждане на контекст между последователните парчета с разлики от указания "
@@ -16332,6 +16782,14 @@ msgstr "директорията на git не може да се мигрира
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "Подсказка: чака се редакторът ви да затвори файла …%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "в „%s“ не може да се пише"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "„%s“ не може да се редактира"
+
 msgid "Filtering content"
 msgstr "Филтриране на съдържанието"
 
@@ -16541,7 +16999,7 @@ msgstr "такъв отдалечен указател няма: %s"
 
 #, c-format
 msgid "Server does not allow request for unadvertised object %s"
-msgstr "Сървърът не позволява заявка за необявен обект „%s“"
+msgstr "СÑ\8aÑ\80вÑ\8aÑ\80Ñ\8aÑ\82 Ð½Ðµ Ð¿Ð¾Ð·Ð²Ð¾Ð»Ñ\8fва Ð·Ð°Ñ\8fвка Ð·Ð° Ð½ÐµÐ¾Ð±Ñ\8fвен Ð·Ð° Ð½Ð°Ð»Ð¸Ñ\87ен Ð¾Ð±ÐµÐºÑ\82 â\80\9e%sâ\80\9c"
 
 #, c-format
 msgid "fsmonitor_ipc__send_query: invalid path '%s'"
@@ -16576,9 +17034,11 @@ msgstr "виртуалното хранилище „%s“ е несъвмест
 
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
-"хранилището „%s“ е несъвместимо с fsmonitor заради липсата на гнезда на unix"
+"директорията за гнезда „%s“ е несъвместима с fsmonitor заради липсата на "
+"гнезда на Unix"
 
 msgid ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
@@ -16586,16 +17046,13 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
-"git [-v | --version] [-h | --help] [-C ПЪТ] [-c ИМЕ=СТОЙНОСТ]\n"
+"git [-v|--version] [-h|--help] [-C ПЪТ] [-c ИМЕ=СТОЙНОСТ]\n"
 "           [--exec-path[=ПЪТ]] [--html-path] [--man-path] [--info-path]\n"
-"           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
-"bare]\n"
+"           [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]\n"
 "           [--git-dir=ПЪТ] [--work-tree=ПЪТ] [--namespace=ИМЕ]\n"
-"           [--super-prefix=ПЪТ] [--config-env=ИМЕ=ПРОМЕНЛИВА_НА_СРЕДАТА]\n"
-"           КОМАНДА [АРГ…]"
+"           [--config-env=ИМЕ=ПРОМЕНЛИВА_НА_СРЕДАТА] КОМАНДА [АРГ…]"
 
 msgid ""
 "'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -16622,10 +17079,6 @@ msgstr "опцията „%s“ изисква директория\n"
 msgid "no namespace given for --namespace\n"
 msgstr "опцията „--namespace“ изисква име\n"
 
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "опцията „--super-prefix“ изисква префикс\n"
-
 #, c-format
 msgid "-c expects a configuration string\n"
 msgstr "опцията „-c“ изисква низ за настройка\n"
@@ -16634,6 +17087,10 @@ msgstr "опцията „-c“ изисква низ за настройка\n"
 msgid "no config key given for --config-env\n"
 msgstr "опцията „--config-env“ изисква ключ\n"
 
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "опцията „--attr-source“ изисква източник на атрибути\n"
+
 #, c-format
 msgid "unknown option: %s\n"
 msgstr "непозната опция: „%s“\n"
@@ -16746,8 +17203,13 @@ msgstr ""
 msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "неуспешно изпълнение на „gpg.ssh.defaultKeyCommand“: %s %s"
 
-msgid "gpg failed to sign the data"
-msgstr "Програмата „gpg“ не подписа данните"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"Програмата „gpg“ не подписа данните:\n"
+"%s"
 
 msgid "user.signingKey needs to be set for ssh signing"
 msgstr ""
@@ -16915,8 +17377,8 @@ msgstr[1] ""
 "\n"
 "Най-близките команди са"
 
-msgid "git version [<options>]"
-msgstr "git version [ОПЦИЯ…]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #, c-format
 msgid "%s: %s - %s"
@@ -16940,7 +17402,7 @@ msgid ""
 "The '%s' hook was ignored because it's not set as executable.\n"
 "You can disable this warning with `git config advice.ignoredHook false`."
 msgstr ""
-"Куката „%s“ се прескача, защото липсват права за изпълнение.\n"
+"Куката „%s“ се прескача, защото липсват права̀ за изпълнение.\n"
 "За да изключите това предупреждение, изпълнете:\n"
 "    git config advice.ignoredHook false"
 
@@ -17174,12 +17636,12 @@ msgstr ""
 "Неуспешно сливане на подмодула „%s“, но са открити множество решения:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "Ð\9dеуспешно вътрешно сливане"
+msgid "failed to execute internal merge"
+msgstr "неуспешно вътрешно сливане"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "„%s“ не може да се добави в базата с данни"
+msgid "unable to add %s to database"
+msgstr "„%s“ не може да се добави в базата от данни"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -17339,7 +17801,7 @@ msgid ""
 "   or update to an existing commit which has merged those changes\n"
 msgstr ""
 " ⁃ преминаване към подмодула (%s), след които или де се слее подаването\n"
-"   „%s“, или да се обновяви към съществуващо подаване, в които\n"
+"   „%s“, или да се обнови към съществуващо подаване, в които\n"
 "   тези промени са слети\n"
 
 #, c-format
@@ -17420,7 +17882,7 @@ msgstr "неуспешно създаване на символната връз
 #, c-format
 msgid "do not know what to do with %06o %s '%s'"
 msgstr ""
-"не е ясно какво да се прави с обекта „%2$s“ (%3$s) с права за достъп „%1$06o“"
+"не е ясно какво да се прави с обекта „%2$s“ (%3$s) с права̀ за достъп „%1$06o“"
 
 #, c-format
 msgid "Fast-forwarding submodule %s to the following commit:"
@@ -17533,8 +17995,8 @@ msgstr ""
 
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
 "КОНФЛИКТ (преименуване/преименуване): „%s“ е преименуван на „%s“ в клон "
 "„%s“, а „%s“ е преименуван на „%s“ в „%s“/%s."
@@ -17628,7 +18090,15 @@ msgid "failed to read the cache"
 msgstr "кешът не може да бъде прочетен"
 
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "неправилен размер на откъс (OID fanout) на индекса за множество пакети"
+msgstr ""
+"неправилен размер на откъса за разпределянето в индекса за множество пакети"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "неправилен размер на откъса за търсенето в индекса за множество пакети"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr ""
+"неправилен размер на откъса за отместванията в индекса за множество пакети"
 
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -17648,17 +18118,25 @@ msgstr ""
 "версията на контролната сума на индекса за множество пакети %u не съвпада с "
 "%u"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "липсва откъс (pack-name) от индекс за множество пакети"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"откъсът за имена на пакети в индекса за множество пакети липсва или е "
+"повреден"
+
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"откъсът за разпределянето в индекса за множество пакети липсва или е повреден"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "липÑ\81ва Ð¾Ñ\82кÑ\8aÑ\81 (OID fanout) Ð¾Ñ\82 Ð¸Ð½Ð´ÐµÐºÑ\81 Ð·Ð° Ð¼Ð½Ð¾Ð¶ÐµÑ\81Ñ\82во Ð¿Ð°ÐºÐµÑ\82и"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr "оÑ\82кÑ\8aÑ\81Ñ\8aÑ\82 Ð·Ð° Ñ\82Ñ\8aÑ\80Ñ\81ене Ð² Ð¸Ð½Ð´ÐµÐºÑ\81 Ð·Ð° Ð¼Ð½Ð¾Ð¶ÐµÑ\81Ñ\82во Ð¿Ð°ÐºÐµÑ\82и Ð»Ð¸Ð¿Ñ\81ва Ð¸Ð»Ð¸ Ðµ Ð¿Ð¾Ð²Ñ\80еден"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "липсва откъс (OID lookup) от индекс за множество пакети"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"откъсът за отмествания в индекс за множество пакети липсва или е повреден"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "липсва откъс за отместванията на обекти от индекс за множество пакети"
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr ""
+"откъсът за име на пакет в индекс за множество пакети липсва или е повреден"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17676,6 +18154,10 @@ msgstr ""
 "индексът за множество пакети съдържа 64-битови отмествания, но размерът на "
 "„off_t“ е недостатъчен"
 
+msgid "multi-pack-index large offset out of bounds"
+msgstr ""
+"стойността на отместването в индекса за множество пакети е извън диапазона"
+
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "пакетният файл „%s“ не може да бъде добавен"
@@ -17749,7 +18231,9 @@ msgid "failed to clear multi-pack-index at %s"
 msgstr "индексът за множество пакети не може да бъде изчистен при „%s“"
 
 msgid "multi-pack-index file exists, but failed to parse"
-msgstr "файлът с индекса за множество пакети, но не може да бъде анализиран"
+msgstr ""
+"файлът с индекса за множество пакети съществува, но не може да бъде "
+"анализиран"
 
 msgid "incorrect checksum"
 msgstr "неправилна сума за проверка"
@@ -17877,10 +18361,6 @@ msgstr ""
 "%s: алтернативните хранилища за обекти се пренебрегват поради прекалено "
 "дълбоко влагане"
 
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "директорията за обекти „%s“ не може да бъде нормализирана"
-
 msgid "unable to fdopen alternates lockfile"
 msgstr "заключващият файл за алтернативите не може да се отвори с „fdopen“"
 
@@ -17939,6 +18419,10 @@ msgstr "непакетираният обект „%s“ е повреден"
 msgid "garbage at end of loose object '%s'"
 msgstr "грешни данни в края на непакетирания обект „%s“"
 
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "непакетираният обект „%s“ не може да се отвори"
+
 #, c-format
 msgid "unable to parse %s header"
 msgstr "заглавната част на „%s“ не може да бъде анализирана"
@@ -17955,17 +18439,13 @@ msgid "header for %s too long, exceeds %d bytes"
 msgstr "заглавната част на „%s“ е прекалено дълга — надхвърля %d байта"
 
 #, c-format
-msgid "failed to read object %s"
-msgstr "обекÑ\82Ñ\8aÑ\82 â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¿Ñ\80оÑ\87еÑ\82ен"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "непакеÑ\82иÑ\80аниÑ\8fÑ\82 Ð¾Ð±ÐµÐºÑ\82 â\80\9e%sâ\80\9c (в â\80\9e%sâ\80\9c) Ðµ Ð¿Ð¾Ð²Ñ\80еден"
 
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "заместителят „%s“ на „%s“ не може да бъде открит"
 
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "непакетираният обект „%s“ (в „%s“) е повреден"
-
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr "пакетираният обект „%s“ (в „%s“) е повреден"
@@ -17976,10 +18456,7 @@ msgstr "файлът „%s“ не може да бъде записан"
 
 #, c-format
 msgid "unable to set permission to '%s'"
-msgstr "правата за достъп до „%s“ не може да бъдат зададени"
-
-msgid "file write error"
-msgstr "грешка при запис на файл"
+msgstr "права̀та за достъп до „%s“ не може да бъдат зададени"
 
 msgid "error when closing loose object file"
 msgstr "грешка при затварянето на файла с непакетиран обект"
@@ -17987,7 +18464,7 @@ msgstr "грешка при затварянето на файла с непак
 #, c-format
 msgid "insufficient permission for adding an object to repository database %s"
 msgstr ""
-"няма права за добавяне на обект към базата от данни на хранилището „%s“"
+"няма права̀ за добавяне на обект към базата от данни на хранилището „%s“"
 
 msgid "unable to create temporary file"
 msgstr "не може да бъде създаден временен файл"
@@ -18027,11 +18504,12 @@ msgstr "директорията „%s“ не може да бъде създа
 msgid "cannot read object for %s"
 msgstr "обектът за „%s“ не може да се прочете"
 
-msgid "corrupt commit"
-msgstr "повредено подаване"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "„fsck“ откри грешка в обект: „%s“"
 
-msgid "corrupt tag"
-msgstr "повÑ\80еден ÐµÑ\82икет"
+msgid "refusing to create malformed object"
+msgstr "не Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ñ\81е Ñ\81Ñ\8aздаде Ñ\81гÑ\80еÑ\88ен Ð¾Ð±ÐµÐºт"
 
 #, c-format
 msgid "read error while indexing %s"
@@ -18302,10 +18780,6 @@ msgstr "неправилно отместване по XOR в пакетен и
 msgid "cannot fstat bitmap file"
 msgstr "не може да се получи информация за файла с битови маски с „fstat“"
 
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "игнориране на излишния файл с битови маски: „%s“"
-
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr ""
 "сумата за проверка се различава във файла с индекса за множество пакети и "
@@ -18381,6 +18855,10 @@ msgstr "„%s“ липсва в пакет „%s“ при отместване
 msgid "unable to get disk usage of '%s'"
 msgstr "използваното място за съхранение на „%s“ не може да бъде получено"
 
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "неправилна сума за проверка за файла с битови маски „%s“"
+
 #, c-format
 msgid "mtimes file %s is too small"
 msgstr "файлът с времето на промяна (mtime) „%s“ е твърде малък"
@@ -18427,6 +18905,18 @@ msgstr ""
 "идентификатор на контролна сума %2$<PRIu32> на файла с обратен индекс „%1$s“ "
 "не се поддържа"
 
+msgid "invalid checksum"
+msgstr "неправилна сума за проверка"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr ""
+"неправилна позиция в обратния индекс при %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr ""
+"неправилен размер на откъс за обратен индекс в индекса за множество пакети"
+
 msgid "cannot both write and verify reverse index"
 msgstr "обратният индекс не може едновременно да се записва и да се проверява"
 
@@ -18436,7 +18926,7 @@ msgstr "не може да се получи информация чрез „st
 
 #, c-format
 msgid "failed to make %s readable"
-msgstr "не може да се дадат права за четене на „%s“"
+msgstr "не може да се дадат права̀ за четене на „%s“"
 
 #, c-format
 msgid "could not write '%s' promisor file"
@@ -18484,14 +18974,6 @@ msgstr "опцията „%s“ изисква някоя от стойност
 msgid "%s requires a value"
 msgstr "опцията „%s“ изисква аргумент"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "опциите „%s“ и „%s“ са несъвместими"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "опцията „%s“ е несъвместима с нещо"
-
 #, c-format
 msgid "%s takes no value"
 msgstr "опцията „%s“ не приема аргументи"
@@ -18505,6 +18987,10 @@ msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr ""
 "„%s“ очаква неотрицателно цяло число, евентуално със суфикс „k“/„m“/„g“"
 
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "опциите „%s“ и „%s“ са несъвместими"
+
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "нееднозначна опция: „%s“ (може да е „--%s%s“ или „--%s%s“)"
@@ -18576,6 +19062,10 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-ЧИСЛО"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "обратното на „--no-%s“"
+
 msgid "expiry-date"
 msgstr "период на валидност/запазване"
 
@@ -18591,6 +19081,9 @@ msgstr "по-малко подробности"
 msgid "use <n> digits to display object names"
 msgstr "да се показват такъв БРОЙ цифри от имената на обектите"
 
+msgid "prefixed path to initial superproject"
+msgstr "път с префикс към първоначалния свръхпроект"
+
 msgid "how to strip spaces and #comments from message"
 msgstr "кои празни знаци и #коментари да се махат от съобщенията"
 
@@ -18603,9 +19096,17 @@ msgstr ""
 "при ползването на „--pathspec-from-file“, пътищата са разделени с нулевия "
 "знак „NUL“"
 
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "неправилна булева стойност „%s“ за „%s“"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "„%s“ не може да бъде анализиран"
+
 #, c-format
 msgid "Could not make %s writable by group"
-msgstr "Не може да се дадат права за запис в директорията „%s“ на групата"
+msgstr "Не може да се дадат права̀ за запис в директорията „%s“ на групата"
 
 msgid "Escape character '\\' not allowed as last character in attr value"
 msgstr ""
@@ -18650,6 +19151,10 @@ msgstr "Магическите пътища „%c“ са без реализа
 msgid "%s: 'literal' and 'glob' are incompatible"
 msgstr "%s: опциите „literal“ и „glob“ са несъвместими"
 
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "„%s“ е извън дървото на директориите"
+
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: „%s“ е извън хранилището при „%s“"
@@ -18741,6 +19246,10 @@ msgid "promisor remote name cannot begin with '/': %s"
 msgstr ""
 "името отдалеченото хранилище-гарант не може за започва със знака „/“: %s"
 
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "„%s“ не може да се достави от гарантиращото хранилище"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info: след аргументите се очаква изчистване на буферите"
 
@@ -18777,6 +19286,13 @@ msgstr "неуспешно търсене на разлика"
 msgid "could not parse log for '%s'"
 msgstr "журналът с подаванията на „%s“ не може да бъде анализиран"
 
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "неправилен ненужен връх: „%s“"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "допълнителните скорошни обекти не може да се изброят"
+
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr ""
@@ -18920,10 +19436,6 @@ msgstr "не може да се запише разделѐн, частиче
 msgid "failed to convert to a sparse-index"
 msgstr "индексът не може да бъде превърнат в частичен"
 
-#, c-format
-msgid "could not stat '%s'"
-msgstr "неуспешно изпълнение на „stat“ върху „%s“"
-
 #, c-format
 msgid "unable to open git dir: %s"
 msgstr "не може да се отвори директорията на git: %s"
@@ -18934,12 +19446,20 @@ msgstr "неуспешно изтриване на „%s“"
 
 #, c-format
 msgid "cannot fix permission bits on '%s'"
-msgstr "правата за достъп до „%s“ не може да бъдат поправени"
+msgstr "права̀та за достъп до „%s“ не може да бъдат поправени"
 
 #, c-format
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: не може да се премине към етап №0"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "неочакван изходен код при генериране на разлика: %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "изтриване на „%s“\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -18989,7 +19509,7 @@ msgstr ""
 " e, edit ПОДАВАНЕ   — прилагане на подаването и спиране при него за още "
 "промѐни\n"
 " s, squash ПОДАВАНЕ — вкарване на подаването в предходното му\n"
-" f, fixup [-C | -c] ПОДАВАНЕ\n"
+" f, fixup [-C|-c] ПОДАВАНЕ\n"
 "                    — вкарване на подаването в предходното му, без смяна на\n"
 "                      съобщението.  С „-C“ се използва само съобщението на\n"
 "                      настоящото, а с „-c“ освен това се отваря редакторът\n"
@@ -19001,7 +19521,7 @@ msgstr ""
 " d, drop ПОДАВАНЕ   — прескачане на подаването\n"
 " l, label ЕТИКЕТ    — задаване на етикет на указаното от HEAD\n"
 " t, reset ЕТИКЕТ    — зануляване на HEAD към ЕТИКЕТа\n"
-" m, merge [-C ПОДАВАНЕ | -c ПОДАВАНЕ] ЕТИКЕТ [# ЕДИН_РЕД]\n"
+" m, merge [-C ПОДАВАНЕ|-c ПОДАВАНЕ] ЕТИКЕТ [# ЕДИН_РЕД]\n"
 "                    — създаване на подаване със сливане със съобщението от\n"
 "                      първоначалното подаване (или съобщението от ЕДИН_РЕД,\n"
 "                      ако не е зададено подаване със сливане.  С опцията\n"
@@ -19105,6 +19625,14 @@ msgstr "назад с %d"
 msgid "ahead %d, behind %d"
 msgstr "напред с %d, назад с %d"
 
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) не приема аргументи"
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "непознат аргумент за „%%(%.*s)“: %s"
+
 #, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "очакван формат: %%(color:ЦВЯТ)"
@@ -19122,41 +19650,37 @@ msgid "Integer value expected refname:rstrip=%s"
 msgstr "очаква се цяло число за „refname:rstrip=%s“"
 
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "непознаÑ\82 Ð°Ñ\80гÑ\83менÑ\82 Ð·Ð° â\80\9e%%(%s)â\80\9c: %s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "оÑ\87аква Ñ\81е %%(trailers:key=Ð\95Ð\9fÐ\98Ð\9bÐ\9eÐ\93)"
 
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) не приема аргументи"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "непознат аргумент „%%(trailers)“: %s"
 
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) не приема аргументи"
+msgid "positive value expected contents:lines=%s"
+msgstr "очаква се положителна стойност за „contents:lines=%s“"
 
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) не приема аргументи"
+msgid "argument expected for %s"
+msgstr "„%s“ изисква аргумент"
 
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "очаква се %%(trailers:key=ЕПИЛОГ)"
+msgid "positive value expected %s=%s"
+msgstr "очаква се положителна стойност за „%s=%s“"
 
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "непознат аргумент „%%(trailers)“: %s"
+msgid "cannot fully parse %s=%s"
+msgstr "„%s=%s“ не може да се анализира докрай"
 
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "очаква се положителна стойност за „contents:lines=%s“"
+msgid "value expected %s="
+msgstr "очаква се стойност за „%s=“"
 
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "очаква се положителна стойност за „%s“ в %%(%s)"
 
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "непозната опция за е-поща: %s"
-
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
 msgstr "очакван формат: %%(align:ШИРОЧИНА,ПОЗИЦИЯ)"
@@ -19169,13 +19693,17 @@ msgstr "непозната позиция: %s"
 msgid "unrecognized width:%s"
 msgstr "непозната широчина: %s"
 
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "непознат аргумент за „%%(%s)“: %s"
+
 #, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "очаква се положителна широчина с лексемата „%%(align)“"
 
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) не приема аргументи"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "очакван формат: %%(ahead-behind:ПОДАВАНЕ)"
 
 #, c-format
 msgid "malformed field name: %.*s"
@@ -19223,6 +19751,9 @@ msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr ""
 "опцията „--format=%.*s“ е несъвместима с „--python“, „--shell“, „--tcl“"
 
+msgid "failed to run 'describe'"
+msgstr "неуспешно изпълнение на „describe“"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(извън клон, пребазиране на „%s“)"
@@ -19284,6 +19815,9 @@ msgstr "КЛЮЧ"
 msgid "field name to sort on"
 msgstr "име на полето, по което да е подредбата"
 
+msgid "exclude refs which match pattern"
+msgstr "прескачана на указателите напасващи на ШАБЛОНа"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "„%s“ не е журнал с подавания"
@@ -19728,13 +20262,14 @@ msgid_plural ""
 "Your branch and '%s' have diverged,\n"
 "and have %d and %d different commits each, respectively.\n"
 msgstr[0] ""
-"Текущият клон се е отделил от „%s“,\n"
+"Текущият клон се е раздалечил от „%s“,\n"
 "двата имат съответно по %d и %d несъвпадащи подавания.\n"
 msgstr[1] ""
-"Текущият клон се е отделил от „%s“,\n"
+"Текущият клон се е раздалечил от „%s“,\n"
 "двата имат съответно по %d и %d несъвпадащи подавания.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
 msgstr "  (слейте отдалечения клон в локалния чрез „git pull“)\n"
 
 #, c-format
@@ -19810,10 +20345,6 @@ msgstr "конфликтът в „%s“ не може да се обнови"
 msgid "no remembered resolution for '%s'"
 msgstr "липсва запазена корекция на конфликт при „%s“"
 
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "„%s“ не може да се изтрие"
-
 #, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Предварителният вариант на „%s“ е обновен"
@@ -19837,6 +20368,13 @@ msgstr "не може да се определи към какво да сочи
 msgid "failed to find tree of %s"
 msgstr "дървото, сочено от „%s“, не може да бъде открито"
 
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "неподдържан раздел за скрити указатели: „%s“"
+
+msgid "--exclude-hidden= passed more than once"
+msgstr "опцията „--exclude-hidden=“ не може да се подава повече от веднъж"
+
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
 msgstr ""
@@ -19850,6 +20388,10 @@ msgstr "подаването „%s“ към опцията „--ancestry-path
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "опцията „--unpacked=ПАКЕТЕН_ФАЙЛ“ вече не се поддържа"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "опциите „%s“ и „--stdin“ са несъвместими"
+
 msgid "your current branch appears to be broken"
 msgstr "Текущият клон е повреден"
 
@@ -19938,8 +20480,15 @@ msgstr "при клониране да се създава пълна работ
 msgid "only download metadata for the branch that will be checked out"
 msgstr "да се свалят метаданните само за изтегляния клон"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [ОПЦИЯ…] [--] ХРАНИЛИЩЕ [ДИРЕКТОРИЯ]"
+msgid "create repository within 'src' directory"
+msgstr "създаване на хранилище в директория „src“"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch ОСНОВЕН_КЛОН] [--full-clone]\n"
+"    [--[no-]src] АДРЕС [ЗАЧИСЛЕНА_ДИРЕКТОРИЯ]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19980,14 +20529,39 @@ msgid "reconfigure all registered enlistments"
 msgstr "пренастройване на всички зачислени директории"
 
 msgid "scalar reconfigure [--all | <enlistment>]"
-msgstr "scalar reconfigure [--all | ЗАЧИСЛЕНА_ДИРЕКТОРИЯ]"
+msgstr "scalar reconfigure [--all|ЗАЧИСЛЕНА_ДИРЕКТОРИЯ]"
 
 msgid "--all or <enlistment>, but not both"
 msgstr "опцията „--all“ и указването на зачислена директория не са съвместими"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "вече няма хранилище на git в „%s“"
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "остарялото скаларно хранилище (scalar.repo) „%s“ не може да се изтрие"
+
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "изтриване на остарялото скаларно хранилище (scalar.repo) „%s“"
+
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "хранилището „%s“ се притежава от друг"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "хранилището в „%s“ е с неправилен формат"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "в „%s“ липсва хранилище на git"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"за да преустановите регистрацията на хранилището в Scalar, изпълнете\n"
+"\n"
+"    git config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -20016,7 +20590,7 @@ msgid "include Git's build options"
 msgstr "включване и на опциите за компилиране на git"
 
 msgid "scalar verbose [-v | --verbose] [--build-options]"
-msgstr "scalar verbose [-v | --verbose] [--build-options]"
+msgstr "scalar verbose [-v|--verbose] [--build-options]"
 
 msgid "-C requires a <directory>"
 msgstr "„-C“ изисква ДИРЕКТОРИЯ"
@@ -20167,10 +20741,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "„%s“ не може да се заключи"
 
-#, c-format
-msgid "could not write to '%s'"
-msgstr "в „%s“ не може да се пише"
-
 #, c-format
 msgid "could not write eol to '%s'"
 msgstr "краят на ред не може да се запише в „%s“"
@@ -20194,7 +20764,7 @@ msgid "%s: Unable to write new index file"
 msgstr "%s: новият индекс не може да бъде запазен"
 
 msgid "unable to update cache tree"
-msgstr "дÑ\8aÑ\80воÑ\82о Ð½Ð° ÐºÐµÑ\88а Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¾Ð±Ð½Ð¾Ð²ÐµÐ½Ð¾"
+msgstr "кеÑ\88Ñ\8aÑ\82 Ð½Ð° Ð¾Ð±ÐµÐºÑ\82иÑ\82е-дÑ\8aÑ\80веÑ\82а Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¾Ð±Ð½Ð¾Ð²ÐµÐ½"
 
 msgid "could not resolve HEAD commit"
 msgstr "подаването, сочено от указателя „HEAD“, не може да бъде открито"
@@ -20427,10 +20997,6 @@ msgstr "неуспешно извличане на съобщението за 
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s: неразпозната стойност за родителското подаване „%s“"
 
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "„%s“ не може да се преименува на „%s“"
-
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "подаването „%s“… не може да бъде отменено: „%s“"
@@ -20451,6 +21017,23 @@ msgstr "git %s: неуспешно изчитане на индекса"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: неуспешно обновяване на индекса"
 
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "„%s“ е неправилен етикет"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "„%s“ е неправилно име на указател"
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr ""
+"командата „update-ref“ изисква пълно име на указател, напр. „refs/heads/%s“"
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "неправилна команда „%.*s“"
+
 #, c-format
 msgid "%s does not accept arguments: '%s'"
 msgstr "„%s“ не приема аргументи: „%s“"
@@ -20509,14 +21092,14 @@ msgstr "в момента вече се извършва отмяна на по
 
 #, c-format
 msgid "try \"git revert (--continue | %s--abort | --quit)\""
-msgstr "използвайте „git revert (--continue | %s--abort | --quit)“"
+msgstr "използвайте „git revert (--continue|%s--abort|--quit)“"
 
 msgid "cherry-pick is already in progress"
 msgstr "в момента вече се извършва отбиране на подавания"
 
 #, c-format
 msgid "try \"git cherry-pick (--continue | %s--abort | --quit)\""
-msgstr "използвайте „git cherry-pick (--continue | %s--abort | --quit)“"
+msgstr "използвайте „git cherry-pick (--continue|%s--abort|--quit)“"
 
 #, c-format
 msgid "could not create sequencer directory '%s'"
@@ -20524,9 +21107,6 @@ msgstr ""
 "директорията за определянето на последователността „%s“ не може да бъде "
 "създадена"
 
-msgid "could not lock HEAD"
-msgstr "указателят „HEAD“ не може да се заключи"
-
 msgid "no cherry-pick or revert in progress"
 msgstr ""
 "в момента не се извършва отбиране на подавания или пребазиране на клона"
@@ -20629,13 +21209,13 @@ msgstr ""
 "    git rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "и променѝ индекса и/или работното дърво\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "и променѝ индекса и/или работното дърво.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
@@ -20652,16 +21232,16 @@ msgstr ""
 msgid "illegal label name: '%.*s'"
 msgstr "неправилно име на етикет: „%.*s“"
 
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "„%s“ не може да бъде открит"
+
 msgid "writing fake root commit"
 msgstr "запазване на фалшиво начално подаване"
 
 msgid "writing squash-onto"
 msgstr "запазване на подаването, в което другите да се вкарат"
 
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "„%s“ не може да бъде открит"
-
 msgid "cannot merge without a current revision"
 msgstr "без текущо подаване не може да се слива"
 
@@ -20786,14 +21366,14 @@ msgstr ""
 "    git rebase --edit-todo\n"
 "    git rebase --continue\n"
 
-#, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Пребазиране (%d/%d)%s"
-
 #, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Спиране при „%s“…  %.*s\n"
 
+#, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Пребазиране (%d/%d)%s"
+
 #, c-format
 msgid "unknown command %d"
 msgstr "непозната команда %d"
@@ -20992,53 +21572,127 @@ msgstr "Текущата работна директория не може да
 msgid "cannot change to '%s'"
 msgstr "не може да се влезе в директорията „%s“"
 
-#, c-format
-msgid "not a git repository (or any of the parent directories): %s"
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr ""
+"нито тази, нито която и да е от по-горните директории, не е хранилище на "
+"git: %s"
+
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"нито тази, нито която и да е от по-горните директории (до точката на "
+"монтиране „%s“), не е хранилище на git.\n"
+"Git работи в рамките на една файлова система, защото променливата на средата "
+"„GIT_DISCOVERY_ACROSS_FILESYSTEM“ не е зададена."
+
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"засечено е проблемно притежание на хранилището „%s“\n"
+"%sЗа да зададете изключение за тази директория, изпълнете:\n"
+"\n"
+"    git config --global --add safe.directory %s"
+
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr ""
+"голото хранилище „%s“ не може да се ползва („safe.bareRepository“ е „%s“)"
+
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"зададеният в „core.sharedRepository“ режим за достъп до файлове е неправилен "
+"(0%.3o).\n"
+"Собственикът на файла трябва да има права̀ за писане и четене."
+
+msgid "fork failed"
+msgstr "неуспешно създаване на процес чрез „fork“"
+
+msgid "setsid failed"
+msgstr "неуспешно изпълнение на „setsid“"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "не може да се получи информация чрез „stat“ за шаблона „%s“"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "директорията „%s“ не може да бъде отворена"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "връзката „%s“ не може да бъде прочетена"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "не може да се създаде символна връзка „%s“ в „%s“"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "„%s“ не може да се копира в „%s“"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "игнориране на шаблона „%s“"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "няма шаблони в „%s“"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "шаблоните няма да бъдат копирани от „%s“: „%s“"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "неправилно име на първоначалния клон: „%s“"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "файлове от вид %d не се поддържат"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "„%s“ не може да се премести в „%s“"
+
+msgid "attempt to reinitialize repository with different hash"
 msgstr ""
-"ниÑ\82о Ñ\82ази, Ð½Ð¸Ñ\82о ÐºÐ¾Ñ\8fÑ\82о Ð¸ Ð´Ð° Ðµ Ð¾Ñ\82 Ð¿Ð¾-гоÑ\80ниÑ\82е Ð´Ð¸Ñ\80екÑ\82оÑ\80ии, Ð½Ðµ Ðµ Ñ\85Ñ\80анилиÑ\89е на "
-"git: %s"
+"опиÑ\82 Ð·Ð° Ð¿Ð¾Ð²Ñ\82оÑ\80но Ð·Ð°Ð´Ð°Ð²Ð°Ð½Ðµ Ð½Ð° Ð¿Ñ\8aÑ\80воÑ\82о Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ðµ Ð² Ñ\85Ñ\80анилиÑ\89еÑ\82о Ñ\81 Ñ\80азлиÑ\87на "
+"контролна сума"
 
 #, c-format
-msgid ""
-"not a git repository (or any parent up to mount point %s)\n"
-"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
-msgstr ""
-"нито тази, нито която и да е от по-горните директории (до точката на "
-"монтиране „%s“), не е хранилище на git.\n"
-"Git работи в рамките на една файлова система, защото променливата на средата "
-"„GIT_DISCOVERY_ACROSS_FILESYSTEM“ не е зададена."
+msgid "%s already exists"
+msgstr "Директорията „%s“ вече съществува"
 
 #, c-format
-msgid ""
-"detected dubious ownership in repository at '%s'\n"
-"%sTo add an exception for this directory, call:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
-msgstr ""
-"засечено е проблемно притежание на хранилището „%s“\n"
-"%sЗа да зададете изключение за тази директория, изпълнете:\n"
-"\n"
-"    git config --global --add safe.directory %s"
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: „--initial-branch=%s“ се пропуска"
 
 #, c-format
-msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgid "Reinitialized existing shared Git repository in %s%s\n"
 msgstr ""
-"голоÑ\82о Ñ\85Ñ\80анилиÑ\89е â\80\9e%sâ\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ñ\81е Ð¿Ð¾Ð»Ð·Ð²Ð° (â\80\9esafe.bareRepositoryâ\80\9c Ðµ â\80\9e%sâ\80\9c)"
+"Ð\98ниÑ\86иализиÑ\80ане Ð½Ð°Ð½Ð¾Ð²Ð¾ Ð½Ð° Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ваÑ\89о, Ñ\81поделено Ñ\85Ñ\80анилиÑ\89е Ð½Ð° Git Ð² â\80\9e%s%sâ\80\9c\n"
 
 #, c-format
-msgid ""
-"problem with core.sharedRepository filemode value (0%.3o).\n"
-"The owner of files must always have read and write permissions."
-msgstr ""
-"зададеният в „core.sharedRepository“ режим за достъп до файлове е неправилен "
-"(0%.3o).\n"
-"Собственикът на файла трябва да има права за писане и четене."
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Инициализиране наново на съществуващо хранилище на Git в „%s%s“\n"
 
-msgid "fork failed"
-msgstr "неуспешно създаване на процес чрез „fork“"
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Инициализиране на празно, споделено хранилище на Git в „%s%s“\n"
 
-msgid "setsid failed"
-msgstr "неуспешно изпълнение на „setsid“"
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Инициализиране на празно хранилище на Git в „%s%s“\n"
 
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -21091,10 +21745,6 @@ msgid_plural "%u bytes/s"
 msgstr[0] "%u байт/сек."
 msgstr[1] "%u байта/сек."
 
-#, c-format
-msgid "could not edit '%s'"
-msgstr "„%s“ не може да се редактира"
-
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "игнориране на подозрително име на подмодул: „%s“"
@@ -21277,6 +21927,27 @@ msgstr "„ls-tree“ завърши с неочакван изходен код
 msgid "failed to lstat '%s'"
 msgstr "не може да бъде получена информация чрез „lstat“ за „%s“"
 
+msgid "no remote configured to get bundle URIs from"
+msgstr "не е настроено отдалечено хранилище за списъците с адреси на пратки"
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "не е зададен никакъв адрес за отдалеченото хранилище„%s“"
+
+msgid "could not get the bundle-uri list"
+msgstr "списъкът с адреси на пратки не може да се получи"
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree ОПЦИЯ… (control|prime|update)"
+
+msgid "clear the cache tree before each iteration"
+msgstr "изчистване на кеша на обектите-дървета преди всяка итерация"
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr ""
+"какъв брой записи в кеша на обектите-дървета да се отбележат като невалидни "
+"(стандартно е 0)"
+
 msgid "unhandled options"
 msgstr "неподдържани опции"
 
@@ -21377,7 +22048,7 @@ msgstr "„%s“ не е обикновен файл"
 
 #, c-format
 msgid "file %s is not writable by user"
-msgstr "„%s“: няма права за записване на файла"
+msgstr "„%s“: няма права̀ за записване на файла"
 
 msgid "could not open temporary file"
 msgstr "временният файл не може да се отвори"
@@ -21625,11 +22296,19 @@ msgstr "Преустановяване на действието."
 msgid "failed to push all needed submodules"
 msgstr "неуспешно изтласкване на всички необходими подмодули"
 
+msgid "bundle-uri operation not supported by protocol"
+msgstr "операцията „bundle-uri“ (адреси на пратки) не се поддържа от протокола"
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr ""
+"спъсъкът с адреси на пратки обявени за налични от сървъра не може да се "
+"получи "
+
 msgid "too-short tree object"
 msgstr "прекалено кратък обект-дърво"
 
 msgid "malformed mode in tree entry"
-msgstr "неправилни права за достъп в запис в дърво"
+msgstr "неправилни права̀ за достъп в запис в дърво"
 
 msgid "empty filename in tree entry"
 msgstr "празно име на файл в запис в дърво"
@@ -22013,6 +22692,9 @@ msgstr "няма достъп до „%s“"
 msgid "unable to get current working directory"
 msgstr "текущата работна директория е недостъпна"
 
+msgid "unable to get random bytes"
+msgstr "не може да се получат случайни байтове"
+
 msgid "Unmerged paths:"
 msgstr "Неслети пътища:"
 
@@ -22373,14 +23055,18 @@ msgstr "Игнорирани файлове"
 
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
 msgstr ""
-"Бяха необходими %.2f секунди за изброяването на неследените файлове.\n"
-"Добавянето на опцията „-uno“ към командата „git status“ ще ускори\n"
-"изпълнението, но ще трябва да добавяте новите файлове ръчно.\n"
-"За повече подробности погледнете „git status help“."
+"Изброяването на неследените файлове отне %.2f секунди, но\n"
+"резултатите са запомнени и може да забързат последващиге изброявания."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "Изброяването на неследените файлове отне %.2f секунди."
+
+msgid "See 'git help status' for information on how to improve this."
+msgstr "Вижте в „git help status“ за начините да подобрите това."
 
 #, c-format
 msgid "Untracked files not listed%s"
@@ -22529,294 +23215,6 @@ msgstr ""
 msgid "Unable to determine absolute path of git directory"
 msgstr "Абсолютният път на работното дърво не може да се определи"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%14s %14s %s"
-
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "%d файл засегнат\n"
-msgstr[1] "%d файла засегнати\n"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде "
-"незабавно\n"
-"добавено към индекса"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде "
-"незабавно\n"
-"скътано"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде "
-"незабавно\n"
-"извадено от индекса."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде "
-"незабавно\n"
-"набелязано за прилагане."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде "
-"незабавно\n"
-"набелязано за зануляване."
-
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "файлът за редактиране на парчето код не може да бъде отворен: „%s“"
-
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"———\n"
-"За да пропуснете редовете започващи с „%s“ — заменете знака със „ “ (стават "
-"контекст)\n"
-"За да пропуснете редовете започващи с „%s“ — изтрийте ги.\n"
-"Редовете, които започват с „%s“ ще бъдат пропуснати.\n"
-
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr ""
-"файлът за редактиране на парчето код не може да бъде отворен за четене: „%s“"
-
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y — добавяне на парчето в индекса\n"
-"n — без добавяне на парчето в индекса\n"
-"q — изход, без добавяне на това и всички оставащи парчета от файла\n"
-"a — добавяне на това и всички следващи парчета от файла в индекса\n"
-"d — без добавяне на това и всички следващи парчета от файла в индекса"
-
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y — скатаване на парчето\n"
-"n — без скатаване на парчето\n"
-"q — изход, без скатаване на това и всички оставащи парчета\n"
-"a — скатаване на това и всички следващи парчета от файла\n"
-"d — без скатаване на това и всички следващи парчета от файла"
-
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y — изваждане на парчето от индекса\n"
-"n — без изваждане на парчето от индекса\n"
-"q — изход, без изваждане на това и всички оставащи парчета от индекса\n"
-"a — изваждане на това и всички следващи парчета от файла от индекса\n"
-"d — без изваждане на това и всички следващи парчета от файла от индекса"
-
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y — прилагане на парчето към индекса\n"
-"n — без прилагане на парчето към индекса\n"
-"q — изход, без прилагане на това и всички оставащи парчета към индекса\n"
-"a — прилагане на това и всички следващи парчета от файла към индекса\n"
-"d — без прилагане на това и всички следващи парчета от файла към индекса"
-
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y — премахване на парчето от работното дърво\n"
-"n — без премахване на парчето от работното дърво\n"
-"q — изход, без премахване на това и всички оставащи парчета от работното "
-"дърво\n"
-"a — премахване на това и всички следващи парчета от файла от работното "
-"дърво\n"
-"d — без премахване на това и всички следващи парчета от файла от работното "
-"дърво"
-
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y — премахване на парчето от индекса и работното дърво\n"
-"n — без премахване на парчето от индекса и работното дърво\n"
-"q — изход, без премахване на това и всички оставащи парчета от индекса и "
-"работното дърво\n"
-"a — премахване на това и всички следващи парчета от файла от индекса и "
-"работното дърво\n"
-"d — без премахване на това и всички следващи парчета от файла от индекса и "
-"работното дърво"
-
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y — прилагане на парчето от индекса и работното дърво\n"
-"n — без прилагане на парчето от индекса и работното дърво\n"
-"q — изход, без прилагане на това и всички оставащи парчета от индекса и "
-"работното дърво\n"
-"a — прилагане на това и всички следващи парчета от файла от индекса и "
-"работното дърво\n"
-"d — без прилагане на това и всички следващи парчета от файла от индекса и "
-"работното дърво"
-
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y — прилагане на парчето към работното дърво\n"
-"n — без прилагане на парчето към работното дърво\n"
-"q — изход, без прилагане на това и всички оставащи парчета\n"
-"a — прилагане на това и всички следващи парчета от файла\n"
-"d — без прилагане на това и всички следващи парчета от файла"
-
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g — избор към кое парче да се премине\n"
-"/ — търсене на парче, напасващо към даден регулярен израз\n"
-"j — без решение за парчето, към следващото парче без решение\n"
-"J — без решение за парчето, към следващото парче\n"
-"k — без решение за парчето, към предишното парче без решение\n"
-"K — без решение за парчето, към предишното парче\n"
-"s — разделяне на текущото парче на по-малки\n"
-"e — ръчно редактиране на текущото парче\n"
-"? — извеждане не помощта\n"
-
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "Избраните парчета не може да се добавят в индекса!\n"
-
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "пренебрегване на неслятото: „%s“\n"
-
-msgid "No other hunks to goto\n"
-msgstr "Няма други парчета\n"
-
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "Неправилен номер: „%s“\n"
-
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "Има само %d парче.\n"
-msgstr[1] "Има само %d парчета.\n"
-
-msgid "No other hunks to search\n"
-msgstr "Няма други парчета за търсене\n"
-
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "Сгрешен регулярен израз „%s“: %s\n"
-
-msgid "No hunk matches the given pattern\n"
-msgstr "Никое парче не напасва на регулярния израз\n"
-
-msgid "No previous hunk\n"
-msgstr "Няма друго парче преди това\n"
-
-msgid "No next hunk\n"
-msgstr "Няма друго парче след това\n"
-
-msgid "Sorry, cannot split this hunk\n"
-msgstr "Това парче не може да бъде разделено\n"
-
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "Разделяне на %d парче.\n"
-msgstr[1] "Разделяне на %d парчета.\n"
-
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "Това парче не може да бъде редактирано\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        — извеждане на пътищата с промѐните\n"
-"update        — добавяне на състоянието на работното дърво към промѐните в\n"
-"                индекса\n"
-"revert        — отмяна на промѐните в индекса към състоянието сочено от "
-"„HEAD“\n"
-"patch         — избиране на парчета код и обновяване поединично\n"
-"diff          — извеждане на разликата между състоянието на соченото от "
-"„HEAD“\n"
-"                и индекса\n"
-"add untracked — добавяне на неследените файлове към промѐните в индекса\n"
-
-msgid "missing --"
-msgstr "„--“ липсва"
-
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "неизвестна стратегия за прилагане на кръпка към „--patch“: „%s“"
-
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "указан е неправилен аргумент „%s“, а се очаква „--“."
-
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr ""
 "разликата между местния часови пояс и GMT съдържа дробна част от минута\n"
@@ -22941,14 +23339,14 @@ msgstr ""
 "\n"
 "Изтрийте всичко, ако не искате да изпратите обобщаващо писмо.\n"
 
-#, perl-format
-msgid "Failed to open %s: %s"
-msgstr "„%s“ не може да се отвори: %s"
-
 #, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "„%s.final“ не може да се отвори: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "„%s“ не може да се отвори: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "Обобщаващото писмо е празно и се прескача\n"
 
@@ -23107,13 +23505,17 @@ msgid "(%s) Could not execute '%s'"
 msgstr "(%s) Не може да бъде се изпълни „%s“"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Ð\94обавÑ\8fне Ð½Ð° â\80\9e%s: %sâ\80\9c Ð¾Ñ\82: â\80\9e%sâ\80\9c\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Ð\9dепÑ\80авилен Ð¸Ð·Ñ\85од Ð¾Ñ\82: â\80\9e%sâ\80\9c."
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) програмният канал не може да се затвори за изпълнението на „%s“"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Добавяне на „%s: %s“ от: „%s“\n"
+
 msgid "cannot send message as 7bit"
 msgstr "съобщението не може да се изпрати чрез 7 битови знаци"
 
index 7a17ed5936046a55c612ad06f34ceba660a3b0f1..d1a4e56c3a59f200b66d46a26bb93474297f7866 100644 (file)
--- a/po/ca.po
+++ b/po/ca.po
@@ -1,17 +1,20 @@
 # Catalan translations for Git.
 # This file is distributed under the same license as the Git package.
 # Alex Henrie <alexhenrie24@gmail.com>, 2014-2016.
-# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2016-2022
+# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2016-2023
 #
-# Terminologia i criteris utilitzats
+# Terminologia
 #
 #   Anglès           |  Català
 #   -----------------+---------------------------------
 #   ahead            |  davant per
 #   amend            |  esmenar
+#   branch           |  branca
 #   broken           |  malmès
 #   bundle           |  farcell
+#   check out        |  agafar
 #   chunk            |  fragment
+#   commit           |  comissió
 #   cover letter     |  carta de presentació
 #   cruft            |  superflu
 #   delta            |  diferència
@@ -26,6 +29,7 @@
 #   hint             |  consell
 #   hook             |  lligam
 #   hunk             |  tros
+#   multi-pack-index |  índex multipaquet
 #   not supported    |  no està admès
 #   pull             |  baixar
 #   push             |  pujar
 #
 # Vegeu també https://git.github.io/htmldocs/gitglossary.html
 #
+# Criteris
+#   - Mantingueu en anglès les referències a seccions de la documentació, ja que no està traduïda.
+#   - Usem la convenció valenciana per a «per / per a», que inclou l'ús de «per a» davant d'infintiu
+#
 msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-09-28 18:55+0200\n"
-"PO-Revision-Date: 2022-09-28 19:00-0600\n"
+"POT-Creation-Date: 2023-11-13 18:55+0100\n"
+"PO-Revision-Date: 2023-11-13 19:00-0600\n"
 "Last-Translator: Jordi Mas i Hernàndez <jmas@softcatala.org>\n"
 "Language-Team: Catalan\n"
 "Language: ca\n"
@@ -105,13 +113,13 @@ msgstr "no s'ha pogut fer «stage» «%s»"
 msgid "could not write index"
 msgstr "no s'ha pogut escriure l'índex"
 
-#, c-format, perl-format
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "actualitzat %d camí\n"
 msgstr[1] "actualitzats %d camins\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "nota: %s està ara sense seguiment.\n"
 
@@ -125,7 +133,7 @@ msgstr "Reverteix"
 msgid "Could not parse HEAD^{tree}"
 msgstr "No s'ha pogut analitzar HEAD^{tree}"
 
-#, c-format, perl-format
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "revertit %d camí\n"
@@ -138,7 +146,7 @@ msgstr "Sense fitxers no seguits.\n"
 msgid "Add untracked"
 msgstr "Afegeix sense seguiment"
 
-#, c-format, perl-format
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "afegit %d camí\n"
@@ -232,19 +240,19 @@ msgstr "no s'ha pogut actualitzar l'índex"
 msgid "Bye.\n"
 msgstr "Adeu.\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "Canvia el mode de «stage» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Suprimeix «stage» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Afegeix a «stage» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Fer un «stage» d'aquest tros [y,n,q,a,d%s,?]? "
 
@@ -268,19 +276,19 @@ msgstr ""
 "a - fes «stage» d'aquest tros i de tota la resta de trossos del fitxer\n"
 "d - no facis «stage» d'aquest tros ni de cap altre restant del fitxer\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "Canvia el mode de «stash» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Suprimeix «stash» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Afegeix a «stash» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Fer un «stash» d'aquest tros [y,n,q,a,d%s,?]? "
 
@@ -304,19 +312,19 @@ msgstr ""
 "a - fes «stash» d'aquest tros i de tota la resta de trossos del fitxer\n"
 "d - no facis «stash» d'aquest tros ni de cap altre restant del fitxer\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "Canvia el mode de «unstage» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Suprimeix «Unstage» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Afegeix a «unstage» [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Fer un «unstage» d'aquest tros [y,n,q,a,d%s,?]? "
 
@@ -340,19 +348,19 @@ msgstr ""
 "a - fes «unstage» d'aquest tros i de tota la resta de trossos del fitxer\n"
 "d - no facis «unstage» d'aquest tros ni de cap altre restant del fitxer\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "Aplica el canvi de mode a l'índex [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Aplica la supressió a l'índex [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Aplica l'addició a l'índex [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Aplica aquest tros a l'índex [y,n,q,a,d%s,?]? "
 
@@ -376,19 +384,19 @@ msgstr ""
 "a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
 "d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "Descarta el canvi de mode de l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Descarta suprimir de l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Descarta l'addició de l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "Descarta aquest tros de l'arbre de treball [y,n,q,a,d%s,?]? "
 
@@ -412,22 +420,22 @@ msgstr ""
 "a - descarta aquest tros i tots els trossos posteriors en el fitxer\n"
 "d - no descartis aquest tros ni cap dels trossos posteriors en el fitxer\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Descarta el canvi de mode de l'índex i de l'arbre de treball [y,n,q,a,"
 "d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Descarta suprimir de l'índex i de l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Descarta l'addició de l'índex i de l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Descarta aquest tros de l'índex i de l'arbre de treball [y,n,q,a,d%s,?]? "
@@ -445,20 +453,20 @@ msgstr ""
 "a - descarta aquest tros i tots els trossos posteriors en el fitxer\n"
 "d - no descartis aquest tros ni cap dels trossos posteriors en el fitxer\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Aplica el canvi de mode a l'índex i a l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Aplica la supressió a l'índex i a l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Aplica l'addició a l'índex i a l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Aplica aquest tros a l'índex i a l'arbre de treball [y,n,q,a,d%s,?]? "
 
@@ -475,19 +483,19 @@ msgstr ""
 "a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
 "d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr "Aplica el canvi de mode a l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "Aplica la supressió a l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "Aplica l'addició a l'arbre de treball [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "Aplica aquest tros a l'arbre de treball [y,n,q,a,d%s,?]? "
 
@@ -564,8 +572,6 @@ msgstr ""
 "Per a eliminar les línies «%c», suprimiu-les.\n"
 "Les línies que comencin per %c s'eliminaran.\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -581,20 +587,11 @@ msgstr "no s'ha pogut analitzar la capçalera del tros"
 msgid "'git apply --cached' failed"
 msgstr "«git apply --cached» ha fallat"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
-#.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
@@ -709,9 +706,8 @@ msgstr "Baixar no és possible perquè teniu fitxers sense fusionar."
 msgid "Reverting is not possible because you have unmerged files."
 msgstr "Revertir no és possible perquè teniu fitxers sense fusionar."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "No és possible %s perquè teniu fitxers sense fusionar."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Fer «rebase» no és possible perquè teniu fitxers sense fusionar."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -733,6 +729,23 @@ msgstr "Cometeu els vostres canvis abans de fusionar."
 msgid "Exiting because of unfinished merge."
 msgstr "S'està sortint a causa d'una fusió no terminada."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Les branques divergents no es poden avançar ràpidament, cal que feu:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"o:\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "No és possible avançar ràpidament, s'està avortant."
 
@@ -818,7 +831,10 @@ msgid "cmdline ends with \\"
 msgstr "la línia d'ordres acaba amb \\"
 
 msgid "unclosed quote"
-msgstr "commetes no tancades"
+msgstr "cometes no tancades"
+
+msgid "too many arguments"
+msgstr "hi ha massa arguments"
 
 #, c-format
 msgid "unrecognized whitespace option '%s'"
@@ -836,6 +852,12 @@ msgstr "les opcions «%s» i «%s» no es poden usar juntes"
 msgid "'%s' outside a repository"
 msgstr "«%s» fora d'un repositori"
 
+msgid "failed to read patch"
+msgstr "s'ha produït un error en llegir el pedaç"
+
+msgid "patch too large"
+msgstr "el pedaç és massa gran"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "No es pot preparar l'expressió regular de marca de temps %s"
@@ -1180,6 +1202,10 @@ msgstr "s'està truncant el nom del fitxer .rej a %.*s.rej"
 msgid "cannot open %s"
 msgstr "no es pot obrir %s"
 
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "no es pot fer «unlink» de «%s»"
+
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "El tros #%d s'ha aplicat netament."
@@ -1371,6 +1397,11 @@ msgstr "git archive --remote <repositori> [--exec <ordre>] --list"
 msgid "cannot read '%s'"
 msgstr "no es pot llegir «%s»"
 
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr ""
+"l'especificació de camí «%s» coincideix amb fitxers fora del directori actual"
+
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "l'especificació de camí «%s» no ha coincidit amb cap fitxer"
@@ -1387,9 +1418,6 @@ msgstr "no és un nom d'objecte vàlid: %s"
 msgid "not a tree object: %s"
 msgstr "no és un objecte d'arbre: %s"
 
-msgid "current working directory is untracked"
-msgstr "no se segueix el directori de treball actual"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Fitxer no trobat: %s"
@@ -1440,6 +1468,12 @@ msgstr "llegeix .gitattributes en el directori de treball"
 msgid "report archived files on stderr"
 msgstr "informa de fitxers arxivats en stderr"
 
+msgid "time"
+msgstr "data"
+
+msgid "set modification time of archive entries"
+msgstr "estableix l'hora de modificació de les entrades de l'arxiu"
+
 msgid "set compression level"
 msgstr "estableix el nivell de compressió"
 
@@ -1480,6 +1514,13 @@ msgstr "Argument no admès per al format «%s»: -%d"
 msgid "%.*s is not a valid attribute name"
 msgstr "%.*s no és un nom d'atribut vàlid"
 
+msgid "unable to add additional attribute"
+msgstr "no s'ha pogut afegir l'atribut addicional"
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "s'ignorarà la línia d'atributs massa llarga %d"
+
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "%s no està permès: %s:%d"
@@ -1491,6 +1532,21 @@ msgstr ""
 "Els patrons negatius s'ignoren en els atributs de git\n"
 "Useu «\\!» per exclamació capdavantera literal."
 
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "no es pot fer fstat gitattributes al fitxer «%s»"
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "s'ignorarà el fitxer «%s» gitattributes per a ser massa gran"
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "s'ignorarà el blob «%s» gitattributes per a ser massa gran"
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "--attr-source incorrecte o GIT_ATTR_SOURCE"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Comentari amb cometes errònies en el fitxer «%s»: %s"
@@ -1587,7 +1643,6 @@ msgstr[1] "(aproximadament %d passos)"
 
 #. TRANSLATORS: the last %s will be replaced with "(roughly %d
 #. steps)" translation.
-#.
 #, c-format
 msgid "Bisecting: %d revision left to test after this %s\n"
 msgid_plural "Bisecting: %d revisions left to test after this %s\n"
@@ -1597,9 +1652,6 @@ msgstr[1] "Bisecant: manquen %d revisions a provar després d'aquesta %s\n"
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents i --reverse no funcionen bé juntes."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "no es pot usar --contents amb el nom d'objecte de la comissió final"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse i --first-parent junts requereixen una última comissió especificada"
@@ -1676,20 +1728,17 @@ msgstr "no s'està seguint: informació ambigua per a la referència «%s»"
 #. TRANSLATORS: This is a line listing a remote with duplicate
 #. refspecs in the advice message below. For RTL languages you'll
 #. probably want to swap the "%s" and leading "  " space around.
-#.
 #. #-#-#-#-#  object-name.c.po  #-#-#-#-#
 #. TRANSLATORS: This is line item of ambiguous object output
 #. from describe_ambiguous_object() above. For RTL languages
 #. you'll probably want to swap the "%s" and leading " " space
 #. around.
-#.
 #, c-format
 msgid "  %s\n"
 msgstr "  %s\n"
 
 #. TRANSLATORS: The second argument is a \n-delimited list of
 #. duplicate refspecs, composed above.
-#.
 #, c-format
 msgid ""
 "There are multiple remotes whose fetch refspecs map to the remote\n"
@@ -1719,8 +1768,10 @@ msgid "a branch named '%s' already exists"
 msgstr "ja existeix una branca amb nom «%s»"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "no es pot forçar l'actualització de la branca «%s», agafada a «%s»"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr ""
+"no es pot forçar l'actualització de la branca «%s» utilitzada per l'arbre de "
+"treball a «%s»"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1770,23 +1821,19 @@ msgstr "submòdul «%s»: no es pot trobar el submòdul"
 
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
-"Podeu provar d'actualitzar els submòduls utilitzant «git checkout %s && git "
-"submodule update --init»"
+"Podeu provar d'actualitzar els submòduls utilitzant «git checkout --no-"
+"recurse-submodules %s && git submodule update --init»"
 
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
 msgstr "submòdul «%s»: no es pot crear la branca: «%s»"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "«%s» ja s'ha agafat a «%s»"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD de l'arbre de treball %s no està actualitzat"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "«%s» ja s'utilitza en l'arbre de treball a «%s»"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<opcions>] [--] <especificació-de-camí>..."
@@ -1795,39 +1842,32 @@ msgstr "git add [<opcions>] [--] <especificació-de-camí>..."
 msgid "cannot chmod %cx '%s'"
 msgstr "no es pot fer chmod %cx «%s»"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "estat de diff inesperat %c"
-
-msgid "updating files failed"
-msgstr "s'ha produït un error en actualitzar els fitxers"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "elimina «%s»\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Canvis «unstaged» després d'actualitzar l'índex:"
 
-msgid "Could not read the index"
-msgstr "No s'ha pogut llegir l'índex"
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"s'ha eliminat la configuració add.interactive.useBuiltin\n"
+"Per a més detalls, vegeu la seva entrada a «git help config»."
 
-msgid "Could not write patch"
-msgstr "No s'ha pogut escriure el pedaç"
+msgid "could not read the index"
+msgstr "no s'ha pogut llegir l'índex"
 
 msgid "editing patch failed"
 msgstr "l'edició del pedaç ha fallat"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "No s'ha pogut fer stat a «%s»"
+msgid "could not stat '%s'"
+msgstr "no s'ha pogut fer stat a «%s»"
 
-msgid "Empty patch. Aborted."
-msgstr "El pedaç és buit. S'ha avortat."
+msgid "empty patch. aborted"
+msgstr "pedaç buit. interromput"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "No s'ha pogut aplicar «%s»"
+msgid "could not apply '%s'"
+msgstr "no s'ha pogut aplicar «%s»"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
@@ -1878,7 +1918,7 @@ msgstr ""
 "comprova si els fitxers, fins i tot els absents, s'ignoren en fer una prova"
 
 msgid "allow updating entries outside of the sparse-checkout cone"
-msgstr "permet actualitzar entrada fora del con del «sparse-checkout»"
+msgstr "permet actualitzar les entrades fora del con del «sparse-checkout»"
 
 msgid "override the executable bit of the listed files"
 msgstr "sobreescriu el bit executable dels fitxers llistats"
@@ -1956,6 +1996,9 @@ msgstr ""
 msgid "index file corrupt"
 msgstr "fitxer d'índex malmès"
 
+msgid "unable to write new index file"
+msgstr "no s'ha pogut escriure un fitxer d'índex nou"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "acció «%s» incorrecta per a «%s»"
@@ -2100,7 +2143,6 @@ msgstr "El cos de la comissió és:"
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
-#.
 #, c-format
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "
 msgstr ""
@@ -2164,9 +2206,6 @@ msgstr ""
 "Podeu executar «git rm» en un fitxer per a acceptar «suprimit per ells» pel "
 "fitxer."
 
-msgid "unable to write new index file"
-msgstr "no s'ha pogut escriure un fitxer d'índex nou"
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "No s'ha pogut analitzar l'objecte «%s»."
@@ -2185,10 +2224,6 @@ msgstr ""
 msgid "failed to read '%s'"
 msgstr "s'ha produït un error en llegir «%s»"
 
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "les opcions «%s=%s» i «%s=%s» no es poden usar juntes"
-
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<opcions>] [(<bústia> | <directori-de-correu>)...]"
 
@@ -2198,6 +2233,9 @@ msgstr "git am [<opcions>] (--continue | --skip | --abort)"
 msgid "run interactively"
 msgstr "executa interactivament"
 
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "evita els lligams pre-applypatch i applypatch-msg"
+
 msgid "historical option -- no-op"
 msgstr "opció històrica -- no-op"
 
@@ -2225,10 +2263,6 @@ msgstr "passa l'indicador -m a git-mailinfo"
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "passa l'indicador --keep-cr a git-mailsplit per al format mbox"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"no passis l'indicador --keep-cr a git-mailsplit independentment d'am.keepcr"
-
 msgid "strip everything before a scissors line"
 msgstr "elimina tot abans d'una línia de tisores"
 
@@ -2341,32 +2375,27 @@ msgstr "git archive: error de protocol"
 msgid "git archive: expected a flush"
 msgstr "git archive: s'esperava una neteja"
 
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<comissió>]"
-
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<rev>]"
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<rev>...]"
 
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<rev>...]"
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<rev>|<range>)...]"
 
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <filename>"
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<comissió>]"
 
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <logfile>"
 
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <ordre>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <cmd> [<arg>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2408,7 +2437,7 @@ msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
 msgstr ""
 "no s'ha pogut agafar la HEAD original «%s». Proveu «git bisect reset "
-"<commit>»."
+"<comissió>»."
 
 #, c-format
 msgid "Bad bisect_write argument: %s"
@@ -2451,7 +2480,6 @@ msgstr "bisecant amb només una comissió %s"
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
-#.
 msgid "Are you sure [Y/n]? "
 msgstr "N'esteu segur [Y/n]? "
 
@@ -2516,9 +2544,6 @@ msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
 msgstr ""
 "l'agafament de «%s» ha fallat. Proveu «git bisect start <branca-vàlida>»."
 
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "no es bisecarà en un arbre en el qual s'ha fet cg-seek"
-
 msgid "bad HEAD - strange symbolic ref"
 msgstr "HEAD incorrecte - referència simbòlica estranya"
 
@@ -2532,7 +2557,6 @@ msgstr "Cal començar per «git bisect start»\n"
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
-#.
 msgid "Do you want me to do it for you [Y/n]? "
 msgstr "Voleu que ho faci per vostè [Y/n]? "
 
@@ -2570,7 +2594,7 @@ msgid "bisect run failed: no command provided."
 msgstr "ha fallat l'execució de bisect: no s'ha proporcionat cap ordre."
 
 #, c-format
-msgid "unable to verify '%s' on good revision"
+msgid "unable to verify %s on good revision"
 msgstr "no s'ha pogut verificar «%s» en una bona revisió"
 
 #, c-format
@@ -2578,10 +2602,10 @@ msgid "bogus exit code %d for good revision"
 msgstr "codi d'error de sortida %d per a una bona revisió"
 
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
 msgstr ""
-"l'execució de la de bisecció ha fallat: codi de sortida %d de «%s» és < 0 o "
-">= 128"
+"l'execució de la de bisecció ha fallat: codi de sortida %d de %s és < 0 o >= "
+"128"
 
 #, c-format
 msgid "cannot open file '%s' for writing"
@@ -2590,76 +2614,50 @@ msgstr "no es pot obrir «%s» per a escriptura"
 msgid "bisect run cannot continue any more"
 msgstr "l'execució de la bisecció no pot continuar més"
 
-#, c-format
 msgid "bisect run success"
 msgstr "execució de bisecció amb èxit"
 
-#, c-format
 msgid "bisect found first bad commit"
 msgstr "la bisecció ha trobat una primera comissió errònia"
 
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
 msgstr ""
-"l'execució de la bisecció ha fallat: «git bisect--helper --bisect-state %s» "
-"ha sortit amb el codi d'error %d"
-
-msgid "reset the bisection state"
-msgstr "restableix l'estat de la bisecció"
-
-msgid "check whether bad or good terms exist"
-msgstr "comprova si existeixen termes correctes o incorrectes"
-
-msgid "print out the bisect terms"
-msgstr "imprimeix els termes de la bisecció"
-
-msgid "start the bisect session"
-msgstr "inicia la sessió bisecció"
-
-msgid "find the next bisection commit"
-msgstr "troba la comissió de bisecció següent"
-
-msgid "mark the state of ref (or refs)"
-msgstr "marca l'estat de la referència o referències"
-
-msgid "list the bisection steps so far"
-msgstr "mostra les passes de la bisecció fins ara"
-
-msgid "replay the bisection process from the given file"
-msgstr "torna a reproduir el procés de bisecció des del fitxer donat"
-
-msgid "skip some commits for checkout"
-msgstr "omet algunes comissions en agafar"
+"ha fallat l'execució del bisect: «git bisect %s» ha sortit amb el codi "
+"d'error %d"
 
-msgid "visualize the bisection"
-msgstr "visualitza la bisecció"
-
-msgid "use <cmd>... to automatically bisect"
-msgstr "useu <cmd>... per a fer una bisecció automàticament"
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "«%s» no requereix cap argument ni comissió"
 
-msgid "no log for BISECT_WRITE"
-msgstr "no hi ha registre per a BISECT_WRITE"
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "%s requereix 0 o 1 arguments"
 
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset no requereix cap argument ni comissió"
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "«%s» requereix 0 arguments"
 
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms requereix 0 o 1 argument"
+msgid "no logfile given"
+msgstr "no s'ha donat cap fitxer de registre"
 
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next no requereix cap argument"
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "«%s» ha fallat: no s'ha proporcionat cap ordre."
 
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log no requereix cap argument"
+msgid "need a command"
+msgstr "cal una subordre"
 
-msgid "no logfile given"
-msgstr "no s'ha donat cap fitxer de registre"
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "ordre desconeguda: «%s»"
 
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<opcions>] [<opcions-de-revisió>] [<revisió>] [--] fitxer"
 
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<opcions>] [<rev-opts>] [<rev>] [--] <fitxer>"
+
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "es documenten les <opcions-de-revisió> en git-rev-list(1)"
 
@@ -2715,7 +2713,7 @@ msgid "show raw timestamp (Default: off)"
 msgstr "mostra la marca de temps en cru (per defecte: desactivat)"
 
 msgid "show long commit SHA1 (Default: off)"
-msgstr "mostra l'SHA1 de comissió llarg (per defecte: desactivat)"
+msgstr "mostra l'SHA1 de la comissió en format llarg (per defecte: desactivat)"
 
 msgid "suppress author name and timestamp (Default: off)"
 msgstr "omet el nom d'autor i la marca de temps (per defecte: desactivat)"
@@ -2779,7 +2777,6 @@ msgstr ""
 #. among various forms of relative timestamps, but
 #. your language may need more or fewer display
 #. columns.
-#.
 msgid "4 years, 11 months ago"
 msgstr "fa 4 anys i 11 mesos"
 
@@ -2799,11 +2796,11 @@ msgid ""
 "git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
 "point>]"
 msgstr ""
-"git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
+"git branch [<opcions>] [-f] [--recurse-submodules] <branch-name> [<start-"
 "point>]"
 
 msgid "git branch [<options>] [-l] [<pattern>...]"
-msgstr "git branch [<options>] [-l] [<pattern>...]"
+msgstr "git branch [<opcions>] [-l] [<patró>...]"
 
 msgid "git branch [<options>] [-r] (-d | -D) <branch-name>..."
 msgstr "git branch [<opcions>] [-r] (-d | -D) <nom-de-branca>..."
@@ -2823,53 +2820,57 @@ msgstr "git branch [<opcions>] [-r | -a] [--format]"
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
-"s'està suprimint la branca «%s» que s'ha\n"
-"         fusionat a «%s», però encara no\n"
-"         s'ha fusionat a HEAD."
+"s'està suprimint la branca «%s» que s'ha fusionat a\n"
+"         «%s», però encara no s'ha fusionat a HEAD"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"no s'està suprimint la branca «%s» que encara no\n"
-"         s'ha fusionat a «%s», encara que està\n"
-"         fusionada a HEAD."
+"no s'està suprimint la branca «%s» que encara no s'ha fusionat a\n"
+"         «%s», encara que s'hagi fusionat a HEAD"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "No s'ha pogut trobar l'objecte de comissió de «%s»"
+msgid "couldn't look up commit object for '%s'"
+msgstr "no s'ha pogut cercar l'objecte de comissió per a «%s»"
 
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
-"La branca «%s» no està totalment fusionada.\n"
-"Si esteu segur que la voleu suprimir, executeu «git branch -D %s»."
+"la branca «%s» no està completament fusionada.\n"
+"Si esteu segur que voleu suprimir-la, executeu «git branch -D %s»"
 
-msgid "Update of config-file failed"
-msgstr "L'actualització del fitxer de configuració ha fallat"
+msgid "update of config-file failed"
+msgstr "ha fallat l'actualització del fitxer de configuració"
 
 msgid "cannot use -a with -d"
 msgstr "no es pot usar -a amb -d"
 
-msgid "Couldn't look up commit object for HEAD"
-msgstr "No s'ha pogut trobar l'objecte de comissió de HEAD"
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"no es pot suprimir la branca «%s» utilitzada per l'arbre de treball a «%s»"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "No es pot suprimir la branca «%s» agafada a «%s»"
+msgid "remote-tracking branch '%s' not found"
+msgstr "no s'ha trobat la branca de seguiment remot «%s»"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "no s'ha trobat la branca amb seguiment remot «%s»."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"no s'ha trobat la branca «%s».\n"
+"Us heu oblidat de --remote?"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "no s'ha trobat la branca «%s»."
+msgid "branch '%s' not found"
+msgstr "no s'ha trobat la branca «%s»"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2890,50 +2891,56 @@ msgid "HEAD (%s) points outside of refs/heads/"
 msgstr "HEAD (%s) apunta fora de refs/heads/"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "S'està fent «rebase» en la branca %s a %s"
+msgid "branch %s is being rebased at %s"
+msgstr "a la branca %s se li està fent a «rebase» a %s"
+
+#, c-format
+msgid "branch %s is being bisected at %s"
+msgstr "la branca %s s'està bisecant a %s"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "La branca %s s'està bisecant a %s"
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD de l'arbre de treball %s no està actualitzat"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "no es pot copiar branca actual mentre no s'és a cap."
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "el nom de la branca no és vàlid: «%s»"
 
-msgid "cannot rename the current branch while not on any."
-msgstr "no es pot canviar el nom de la branca actual mentre no s'és a cap."
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "encara no hi ha cap comissió a la branca «%s»"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Nom de branca no vàlid: «%s»"
+msgid "no branch named '%s'"
+msgstr "no hi ha cap branca anomenada «%s»"
 
-msgid "Branch rename failed"
-msgstr "El canvi de nom de branca ha fallat"
+msgid "branch rename failed"
+msgstr "ha fallat el canvi de nom de la branca"
 
-msgid "Branch copy failed"
-msgstr "La còpia de la branca ha fallat"
+msgid "branch copy failed"
+msgstr "ha fallat la còpia de la branca"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "S'ha creat una còpia d'una branca mal anomenada «%s»"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "s'ha creat una còpia d'una branca mal anomenada «%s»"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "S'ha canviat el nom de la branca mal anomenada «%s»"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "s'ha canviat el nom d'una branca «%s» mal anomenada"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "S'ha canviat el nom de la branca a %s, però HEAD no està actualitzat!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "s'ha canviat el nom de la branca a %s, però HEAD no s'ha actualitzat"
 
-msgid "Branch is renamed, but update of config-file failed"
+msgid "branch is renamed, but update of config-file failed"
 msgstr ""
-"La branca està canviada de nom, però l'actualització del fitxer de "
-"configuració ha fallat"
+"s'ha canviat el nom de la branca, però ha fallat l'actualització del fitxer "
+"de configuració"
 
-msgid "Branch is copied, but update of config-file failed"
+msgid "branch is copied, but update of config-file failed"
 msgstr ""
-"La branca està copiada, però l'actualització del fitxer de configuració ha "
-"fallat"
+"s'ha copiat la branca, però ha fallat l'actualització del fitxer de "
+"configuració"
 
 #, c-format
 msgid ""
@@ -2999,6 +3006,9 @@ msgstr "mou/canvia de nom una branca i el seu registre de referència"
 msgid "move/rename a branch, even if target exists"
 msgstr "mou/canvia de nom una branca, encara que el destí existeixi"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "no emetis cap línia nova després de refs amb format buit"
+
 msgid "copy a branch and its reflog"
 msgstr "copia una branca i el seu registre de referència"
 
@@ -3039,13 +3049,13 @@ msgid "sorting and filtering are case insensitive"
 msgstr "ordenació i filtratge distingeixen entre majúscules i minúscules"
 
 msgid "recurse through submodules"
-msgstr "inclou recursivament als submòduls"
+msgstr "inclou recursivament els submòduls"
 
 msgid "format to use for the output"
 msgstr "format a usar en la sortida"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "S'ha produït un error en resoldre HEAD com a referència vàlida."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "no s'ha pogut resoldre HEAD com a referència vàlida"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD no trobat sota refs/heads!"
@@ -3063,19 +3073,18 @@ msgstr "--recurse-submodules només es pot utilitzar per a crear branques"
 msgid "branch name required"
 msgstr "cal el nom de branca"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "No es pot donar descripció a una HEAD separada"
+msgid "cannot give description to detached HEAD"
+msgstr "no s'ha pogut donar la descripció al HEAD separat"
 
 msgid "cannot edit description of more than one branch"
 msgstr "no es pot editar la descripció de més d'una branca"
 
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Encara no hi ha cap comissió en la branca «%s»."
+msgid "cannot copy the current branch while not on any"
+msgstr "no es pot copiar la branca actual mentre no pertanyi a cap"
 
-#, c-format
-msgid "No branch named '%s'."
-msgstr "No hi ha cap branca amb nom «%s»."
+msgid "cannot rename the current branch while not on any"
+msgstr ""
+"no s'ha pogut canviar el nom de la branca actual mentre no pertanyi a cap"
 
 msgid "too many branches for a copy operation"
 msgstr "hi ha massa branques per a una operació de còpia"
@@ -3088,9 +3097,9 @@ msgstr "hi ha massa arguments per a establir una nova font"
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"no s'ha pogut establir la font de HEAD com a %s quan no assenyala cap branca."
+"no s'ha pogut configurar la font de HEAD a %s quan no apunta a cap branca"
 
 #, c-format
 msgid "no such branch '%s'"
@@ -3103,27 +3112,26 @@ msgstr "la branca «%s» no existeix"
 msgid "too many arguments to unset upstream"
 msgstr "hi ha massa arguments per a desassignar la font"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr ""
-"no s'ha pogut desassignar la font de HEAD perquè no assenyala cap branca."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr "no s'ha pogut desassignar la font del HEAD quan no apunta a cap branca"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "La branca «%s» no té informació de font"
+msgid "branch '%s' has no upstream information"
+msgstr "la branca «%s» no té informació de la font"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"Les opcions -a i -r a «git branch» no prenen un nom de branca.\n"
-"Volíeu usar -a|-r --list <pattern>?"
+"les opcions -a, i -r, a «git branch» no prenen un nom de branca.\n"
+"Volíeu utilitzar: -a|-r --list <patró>?"
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
-"l'opció --set-upstream ja no s'admet. En lloc seu, useu «--track» o «--set-"
-"upstream-to»."
+"l'opció «--set-upstream» ja no és admesa. Utilitzeu en comptes «--track» o "
+"«--set-upstream-to»"
 
 msgid "git version:\n"
 msgstr "versió de git:\n"
@@ -3143,11 +3151,11 @@ msgstr ""
 "no s'està executant en un repositori de git - no hi ha lligams a mostrar\n"
 
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <camí>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 
 msgid ""
 "Thank you for filling out a Git bug report!\n"
@@ -3196,6 +3204,10 @@ msgstr "especifiqueu una destinació per al fitxer de l'informe d'error"
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "especifiqueu un sufix en format strftime per al nom de fitxer"
 
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "argument desconegut «%s»"
+
 #, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "no s'han pogut crear els directoris principals de «%s»"
@@ -3218,17 +3230,24 @@ msgstr "no s'ha pogut escriure a %s"
 msgid "Created new report at '%s'.\n"
 msgstr "S'ha creat un nou informe a «%s».\n"
 
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [<opcions>] <fitxer> <git-rev-list args>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<versió>] <fitxer> <git-rev-list-args>"
 
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [<opcions>] <fitxer>"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <fitxer>"
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads <fitxer> [<refname>...]"
 
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle <fitxer> [<refname>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <fitxer> [<refname>...]"
+
+msgid "need a <file> argument"
+msgstr "necessita un argument <fitxer>"
 
 msgid "do not show progress meter"
 msgstr "no mostris l'indicador de progrés"
@@ -3236,11 +3255,11 @@ msgstr "no mostris l'indicador de progrés"
 msgid "show progress meter"
 msgstr "mostra l'indicador de progrés"
 
-msgid "show progress meter during object writing phase"
-msgstr "mostra l'indicador de progrés durant la fase d'escriptura d'objectes"
+msgid "historical; same as --progress"
+msgstr "històric; el mateix que --progress"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "similar a --all-progress quan l'indicador de progrés es mostra"
+msgid "historical; does nothing"
+msgstr "històric; no fa res"
 
 msgid "specify bundle format version"
 msgstr "especifica la versió del format del farcell"
@@ -3283,45 +3302,41 @@ msgstr "%s requereix arguments"
 msgid "%s takes no arguments"
 msgstr "%s no accepta cap valor"
 
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "ordre desconeguda: «%s»"
-
 msgid "only one batch option may be specified"
 msgstr "només es pot especificar una opció per lots"
 
 msgid "git cat-file <type> <object>"
-msgstr "git cat-file <type> <object>"
+msgstr "git cat-file <tipus> <objecte>"
 
 msgid "git cat-file (-e | -p) <object>"
-msgstr "git cat-file (-e | -p) <object>"
+msgstr "git cat-file (-e | -p) <objecte>"
 
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
-msgstr "git cat-file (-t | -s) [--allow-unknown-type] <object>"
+msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objecte>"
+
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 
 msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
-msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+"             [--textconv | --filters] [-Z]"
 
 msgid "Check object existence or emit object contents"
 msgstr "Comprova l'existència de l'objecte o emet el contingut de l'objecte"
 
 msgid "check if <object> exists"
-msgstr "comprova si <object> existeix"
+msgstr "comprova si <objecte> existeix"
 
 msgid "pretty-print <object> content"
 msgstr "impressió embellida del contingut de l'<objecte>"
@@ -3355,6 +3370,9 @@ msgstr "com a --batch, però no emetis <contents>"
 msgid "stdin is NUL-terminated"
 msgstr "l'entrada és acabada amb NUL"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin i stdout estan terminats amb NUL"
+
 msgid "read commands from stdin"
 msgstr "llegeix les ordres de stdin"
 
@@ -3390,7 +3408,7 @@ msgid "blob|tree"
 msgstr "blob|tree"
 
 msgid "use a <path> for (--textconv | --filters); Not with 'batch'"
-msgstr "useu un <path> per a (--textconv | --filters); no amb «batch»"
+msgstr "useu un <camí> per a (--textconv | --filters); no amb «batch»"
 
 #, c-format
 msgid "'%s=<%s>' needs '%s' or '%s'"
@@ -3416,20 +3434,23 @@ msgstr "<rev> requerida amb «%s»"
 
 #, c-format
 msgid "<object> required with '-%c'"
-msgstr "<object> requerit amb «-%c»"
-
-msgid "too many arguments"
-msgstr "hi ha massa arguments"
+msgstr "<objecte> requerit amb «-%c»"
 
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
-msgstr "només es permeten dos arguments en el mode <type> <object>, no %d"
+msgstr "només es permeten dos arguments en el mode <tipus> <objecte>, no %d"
 
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <atribut>...] [--] <nom-de-camí>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
 
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <atribut>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
 
 msgid "report all attributes set on file"
 msgstr "informa de tots els atributs establerts en el fitxer"
@@ -3443,6 +3464,12 @@ msgstr "llegeix els noms de fitxer de stdin"
 msgid "terminate input and output records by a NUL character"
 msgstr "acaba els registres d'entrada i de sortida amb un caràcter NUL"
 
+msgid "<tree-ish>"
+msgstr "<tree-ish>"
+
+msgid "which tree-ish to check attributes at"
+msgstr "a quin tree-ish s'han de comprovar els atributs"
+
 msgid "suppress progress reporting"
 msgstr "omet els informes de progrés"
 
@@ -3533,7 +3560,7 @@ msgid "git checkout [<options>] [<branch>] -- <file>..."
 msgstr "git checkout [<opcions>] [<branca>] -- <fitxer>..."
 
 msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<options>] [<branch>]"
+msgstr "git switch [<opcions>] [<branca>]"
 
 msgid "git restore [<options>] [--source=<branch>] <file>..."
 msgstr "git restore [<opcions>] [--source=<branca>] <fitxer>..."
@@ -3601,6 +3628,10 @@ msgstr "«%s» s'ha d'utilitzar quan no s'especifica «%s»"
 msgid "'%s' or '%s' cannot be used with %s"
 msgstr "«%s» o «%s» no poden utilitzar-se amb %s"
 
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "«%s», «%s» o «%s» no es poden utilitzar en agafar un arbre"
+
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "el camí «%s» està sense fusionar"
@@ -3955,9 +3986,11 @@ msgid "use overlay mode"
 msgstr "utilitza el mode de superposició"
 
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <patró>] [-x | -X] [--] <camins>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <patró>] [-x | -X] [--] "
+"[<pathspec>...]"
 
 #, c-format
 msgid "Removing %s\n"
@@ -4021,7 +4054,7 @@ msgstr ""
 "*          - tria tots els ítems\n"
 "           - (buit) finalitza la selecció\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Perdó (%s)?\n"
 
@@ -4170,9 +4203,6 @@ msgstr "profunditat"
 msgid "create a shallow clone of that depth"
 msgstr "crea un clon superficial d'aquesta profunditat"
 
-msgid "time"
-msgstr "data"
-
 msgid "create a shallow clone since a specific time"
 msgstr "crea un clon superficial des d'una data específica"
 
@@ -4210,12 +4240,6 @@ msgstr "específic al servidor"
 msgid "option to transmit"
 msgstr "opció a transmetre"
 
-msgid "use IPv4 addresses only"
-msgstr "usa només adreces IPv4"
-
-msgid "use IPv6 addresses only"
-msgstr "usa només adreces IPv6"
-
 msgid "apply partial clone filters to submodules"
 msgstr "aplica els filtres de clonatge parcial als submòduls"
 
@@ -4245,10 +4269,18 @@ msgstr "s'ha produït un error en fer stat a «%s»"
 msgid "%s exists and is not a directory"
 msgstr "%s existeix i no és directori"
 
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "«%s» és un enllaç simbòlic, es rebutja clonar amb --local"
+
 #, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "no s'ha pogut iniciar l'iterador sobre «%s»"
 
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "l'enllaç simbòlic «%s» existeix, es rebutja a clonar amb --local"
+
 #, c-format
 msgid "failed to unlink '%s'"
 msgstr "s'ha produït un error en desenllaçar «%s»"
@@ -4315,10 +4347,6 @@ msgstr "Hi ha massa arguments."
 msgid "You must specify a repository to clone."
 msgstr "Heu d'especificar un repositori per a clonar."
 
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "les opcions «%s» i «%s %s» no es poden usar juntes"
-
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
 "exclude"
@@ -4405,6 +4433,9 @@ msgstr "no s'ha pogut inicialitzar el repositori, s'omet l'URI del paquet"
 msgid "failed to fetch objects from bundle URI '%s'"
 msgstr "no s'han pogut obtenir els objectes de l'URI del paquet «%s»"
 
+msgid "failed to fetch advertised bundles"
+msgstr "no s'han pogut obtenir els paquets anunciats"
+
 msgid "remote transport reported error"
 msgstr "el transport remot ha informat d'un error"
 
@@ -4440,18 +4471,24 @@ msgid "--command must be the first argument"
 msgstr "--command ha de ser el primer argument"
 
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 msgstr ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 
 msgid "dir"
 msgstr "directori"
@@ -4467,6 +4504,10 @@ msgstr ""
 msgid "Could not open commit-graph '%s'"
 msgstr "No s'ha pogut obrir el graf de comissions «%s»"
 
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "no s'ha pogut obrir la cadena «%s» del graf de comissions"
+
 #, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "argument --split no reconegut, %s"
@@ -4520,12 +4561,15 @@ msgstr "usa com a màxim un --reachable, --stdin-commits, o --stdin-packs"
 msgid "Collecting commits from input"
 msgstr "S'estan recollint les comissions de l'entrada"
 
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <tree> [(-p <pare>)...]"
+
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <pare>)...] [-S[<keyid>]] [(-m <missatge>)...]\n"
+"                [(-F <fitxer>)...] <tree>"
 
 #, c-format
 msgid "duplicate parent %s ignored"
@@ -4567,11 +4611,29 @@ msgstr "ha de donar exactament un arbre"
 msgid "git commit-tree: failed to read"
 msgstr "git commit-tree: ha fallat en llegir"
 
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<opcions>] [--] <especificació-de-camí>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <comissió> | --fixup [(amend|"
+"reword):]<comissió>)]\n"
+"           [-F <fitxer> | -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=<fitxer> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
 
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<opcions>] [--] <especificació-de-camí>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<opcions>] [--] [<pathspec>...]"
 
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -4623,6 +4685,9 @@ msgstr ""
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "s'ha produït un error en actualitzar els fitxers"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "s'ha produït un error en desempaquetar l'objecte d'arbre HEAD"
 
@@ -4641,9 +4706,6 @@ msgstr "no s'ha pogut actualitzar l'índex temporal"
 msgid "Failed to update main cache tree"
 msgstr "S'ha produït un error en actualitzar l'arbre principal de memòria cau"
 
-msgid "unable to write new_index file"
-msgstr "no s'ha pogut escriure el fitxer new_index"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "no es pot fer una comissió parcial durant una fusió."
 
@@ -4682,8 +4744,8 @@ msgstr ""
 "no sigui usat en el missatge de comissió actual"
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "no s'ha pogut cercar la comissió %s"
+msgid "could not lookup commit '%s'"
+msgstr "no s'ha pogut cercar la comissió «%s»"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -4945,7 +5007,6 @@ msgstr "reusa el missatge de la comissió especificada"
 
 #. TRANSLATORS: Leave "[(amend|reword):]" as-is,
 #. and only translate <commit>.
-#.
 msgid "[(amend|reword):]commit"
 msgstr "[(amend|reword):]commit"
 
@@ -5046,13 +5107,13 @@ msgstr ""
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
 "s'ha actualitzat el repositori, però no s'ha pogut escriure\n"
-" el fitxer «new_index». Comproveu que el disc no està ple i\n"
-"que la quota no s'ha excedit, i després, feu\n"
-"«git restore --staged :/» per a recuperar-lo."
+"el fitxer d'índex nou. Comproveu que el disc no està ple i\n"
+"la quota no s'ha excedit, i després feu «git restore --staged :/n»\n"
+"per a recuperar-ho."
 
 msgid "git config [<options>]"
 msgstr "git config [<opcions>]"
@@ -5123,7 +5184,7 @@ msgid "remove a section: name"
 msgstr "elimina una secció: nom"
 
 msgid "list all"
-msgstr "llista tots"
+msgstr "llista'ls tots"
 
 msgid "use string equality when comparing values to 'value-pattern'"
 msgstr ""
@@ -5229,7 +5290,7 @@ msgid "writing to stdin is not supported"
 msgstr "no s'admet escriure a stdin"
 
 msgid "writing config blobs is not supported"
-msgstr "no s'ha admet l'escriptura de blobs de configuració"
+msgstr "no s'admet l'escriptura de blobs de configuració"
 
 #, c-format
 msgid ""
@@ -5352,11 +5413,18 @@ msgid "unable to get credential storage lock in %d ms"
 msgstr ""
 "no s'ha pogut obtenir el bloqueig de l'emmagatzematge de credencials en %d ms"
 
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<opcions>] [<comissió>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
 
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<opcions>] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe <blob>"
 
 msgid "head"
 msgstr "davant per"
@@ -5395,7 +5463,7 @@ msgid ""
 "However, there were unannotated tags: try --tags."
 msgstr ""
 "Cap etiqueta anotada pot descriure «%s».\n"
-"No obstant, hi havia etiquetes no anotades: proveu --tags."
+"No obstant això, hi havia etiquetes no anotades: proveu --tags."
 
 #, c-format
 msgid ""
@@ -5479,11 +5547,11 @@ msgid "option '%s' and commit-ishes cannot be used together"
 msgstr "les opcions «%s» i de comissió no es poden usar juntes"
 
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <camí>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 
 msgid "specify a destination for the diagnostics archive"
 msgstr "especifiqueu una destinació per a l'arxiu de diagnòstic"
@@ -5501,6 +5569,9 @@ msgstr "--merge-base només funciona amb dues comissions"
 msgid "'%s': not a regular file or symlink"
 msgstr "«%s»: no és ni fitxer regular ni enllaç simbòlic"
 
+msgid "no merge given, only parents."
+msgstr "no s'ha donat cap fusió, només els pares."
+
 #, c-format
 msgid "invalid option: %s"
 msgstr "opció no vàlida: %s"
@@ -5529,7 +5600,7 @@ msgid "%s...%s: multiple merge bases, using %s"
 msgstr "%s...%s: múltiples bases de fusió, utilitzant %s"
 
 msgid "git difftool [<options>] [<commit> [<commit>]] [--] [<path>...]"
-msgstr "git difftool [<opcions>] [<commit> [<commit>]] [--] [<camí>...]"
+msgstr "git difftool [<opcions>] [<comissió> [<comissió>]] [--] [<camí>...]"
 
 #, c-format
 msgid "could not read symlink %s"
@@ -5616,27 +5687,6 @@ msgstr "no s'ha proporcionat l'<eina> per a --tool=<eina>"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "no s'ha proporcionat l'<ordre> per a --extcmd=<ordre>"
 
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <opcions> <env-var>"
-
-msgid "default for git_env_*(...) to fall back on"
-msgstr "valor per defecte per a git_env_*(...) en cas d'absència"
-
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr "silenciós només utilitza el valor git_env_*() com a codi de sortida"
-
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr "l'opció «--default» espera un valor booleà amb «--type=bool», no «%s»"
-
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not "
-"`%s`"
-msgstr ""
-"l'opció «--default» espera un valor llarg sense signe amb «--type=ulong», no "
-"«%s»"
-
 msgid "git fast-export [<rev-list-opts>]"
 msgstr "git fast-export [<rev-list-opts>]"
 
@@ -5659,7 +5709,7 @@ msgstr "selecciona la gestió de les etiquetes que etiquetin objectes filtrats"
 
 msgid "select handling of commit messages in an alternate encoding"
 msgstr ""
-"selecciona la gestió dels missatges de publicació en una codificació "
+"selecciona la gestió dels missatges de comissió en una codificació "
 "alternativa"
 
 msgid "dump marks to this file"
@@ -5755,122 +5805,16 @@ msgstr "git fetch --all [<opcions>]"
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel no pot ser negatiu"
 
-msgid "fetch from all remotes"
-msgstr "obtén de tots els remots"
+msgid "couldn't find remote ref HEAD"
+msgstr "no s'ha pogut trobar la referència HEAD remota"
 
-msgid "set upstream for git pull/fetch"
-msgstr "estableix la font per a git pull/fetch"
+#, c-format
+msgid "From %.*s\n"
+msgstr "De %.*s\n"
 
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "annexa a .git/FETCH_HEAD en lloc de sobreescriure'l"
-
-msgid "use atomic transaction to update references"
-msgstr "usa una transacció atòmica per a actualitzar les referències"
-
-msgid "path to upload pack on remote end"
-msgstr "camí al qual pujar el paquet al costat remot"
-
-msgid "force overwrite of local reference"
-msgstr "força la sobreescriptura de la referència local"
-
-msgid "fetch from multiple remotes"
-msgstr "obtén de múltiples remots"
-
-msgid "fetch all tags and associated objects"
-msgstr "obtén totes les etiquetes i tots els objectes associats"
-
-msgid "do not fetch all tags (--no-tags)"
-msgstr "no obtinguis les etiquetes (--no-tags)"
-
-msgid "number of submodules fetched in parallel"
-msgstr "nombre de submòduls obtinguts en paral·lel"
-
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr ""
-"modifica l'especificació de referència per a col·locar totes les referències "
-"dins de refs/prefetch/"
-
-msgid "prune remote-tracking branches no longer on remote"
-msgstr "poda les branques amb seguiment remot que ja no estiguin en el remot"
-
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr ""
-"poda les etiquetes locals que ja no existeixen al remot i adjunta les "
-"etiquetes que han canviat"
-
-msgid "on-demand"
-msgstr "sota demanda"
-
-msgid "control recursive fetching of submodules"
-msgstr "controla l'obtenció recursiva de submòduls"
-
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "escriu les referències obtingudes al fitxer FETCH_HEAD"
-
-msgid "keep downloaded pack"
-msgstr "retén el paquet baixat"
-
-msgid "allow updating of HEAD ref"
-msgstr "permet l'actualització de la referència HEAD"
-
-msgid "deepen history of shallow clone"
-msgstr "aprofundeix la història d'un clon superficial"
-
-msgid "deepen history of shallow repository based on time"
-msgstr "aprofundeix la història d'un clon superficial basat en una data"
-
-msgid "convert to a complete repository"
-msgstr "converteix en un repositori complet"
-
-msgid "re-fetch without negotiating common commits"
-msgstr "tornar a obtenir sense negociar comissions comunes"
-
-msgid "prepend this to submodule path output"
-msgstr "anteposa això a la sortida de camí del submòdul"
-
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr ""
-"per defecte per a l'obtenció recursiva de submòduls (prioritat més baixa que "
-"els fitxers de configuració)"
-
-msgid "accept refs that update .git/shallow"
-msgstr "accepta les referències que actualitzin .git/shallow"
-
-msgid "refmap"
-msgstr "mapa de referències"
-
-msgid "specify fetch refmap"
-msgstr "específica l'obtenció del mapa de referències"
-
-msgid "report that we have only objects reachable from this object"
-msgstr "informa que només hi ha objectes abastables des d'aquest objecte"
-
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr ""
-"no obtinguis un fitxer de paquet; en canvi, mostra els avantpassats dels "
-"consells de negociació"
-
-msgid "run 'maintenance --auto' after fetching"
-msgstr "executa «maintenance --auto» després d'obtenir"
-
-msgid "check for forced-updates on all updated branches"
-msgstr ""
-"comprova si hi ha actualitzacions forçades a totes les branques actualitzades"
-
-msgid "write the commit-graph after fetching"
-msgstr "escriu el graf de comissions després de recollir"
-
-msgid "accept refspecs from stdin"
-msgstr "llegeix les especificacions de referència des de stdin"
-
-msgid "couldn't find remote ref HEAD"
-msgstr "no s'ha pogut trobar la referència HEAD remota"
-
-#, c-format
-msgid "object %s not found"
-msgstr "objecte %s no trobat"
+#, c-format
+msgid "object %s not found"
+msgstr "objecte %s no trobat"
 
 msgid "[up to date]"
 msgstr "[al dia]"
@@ -5940,10 +5884,6 @@ msgid "rejected %s because shallow roots are not allowed to be updated"
 msgstr ""
 "s'ha rebutjat %s perquè no es permeten actualitzar les arrels superficials"
 
-#, c-format
-msgid "From %.*s\n"
-msgstr "De %.*s\n"
-
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -6037,6 +5977,116 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr "necessiteu especificar un nom d'etiqueta"
 
+msgid "fetch from all remotes"
+msgstr "obtén de tots els remots"
+
+msgid "set upstream for git pull/fetch"
+msgstr "estableix la font per a git pull/fetch"
+
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "annexa a .git/FETCH_HEAD en lloc de sobreescriure'l"
+
+msgid "use atomic transaction to update references"
+msgstr "usa una transacció atòmica per a actualitzar les referències"
+
+msgid "path to upload pack on remote end"
+msgstr "camí al qual pujar el paquet al costat remot"
+
+msgid "force overwrite of local reference"
+msgstr "força la sobreescriptura de la referència local"
+
+msgid "fetch from multiple remotes"
+msgstr "obtén de múltiples remots"
+
+msgid "fetch all tags and associated objects"
+msgstr "obtén totes les etiquetes i tots els objectes associats"
+
+msgid "do not fetch all tags (--no-tags)"
+msgstr "no obtinguis les etiquetes (--no-tags)"
+
+msgid "number of submodules fetched in parallel"
+msgstr "nombre de submòduls obtinguts en paral·lel"
+
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"modifica l'especificació de referència per a col·locar totes les referències "
+"dins de refs/prefetch/"
+
+msgid "prune remote-tracking branches no longer on remote"
+msgstr "poda les branques amb seguiment remot que ja no estiguin en el remot"
+
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr ""
+"poda les etiquetes locals que ja no existeixen al remot i adjunta les "
+"etiquetes que han canviat"
+
+msgid "on-demand"
+msgstr "sota demanda"
+
+msgid "control recursive fetching of submodules"
+msgstr "controla l'obtenció recursiva de submòduls"
+
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "escriu les referències obtingudes al fitxer FETCH_HEAD"
+
+msgid "keep downloaded pack"
+msgstr "retén el paquet baixat"
+
+msgid "allow updating of HEAD ref"
+msgstr "permet l'actualització de la referència HEAD"
+
+msgid "deepen history of shallow clone"
+msgstr "aprofundeix la història d'un clon superficial"
+
+msgid "deepen history of shallow repository based on time"
+msgstr "aprofundeix la història d'un clon superficial basat en una data"
+
+msgid "convert to a complete repository"
+msgstr "converteix en un repositori complet"
+
+msgid "re-fetch without negotiating common commits"
+msgstr "tornar a obtenir sense negociar comissions comunes"
+
+msgid "prepend this to submodule path output"
+msgstr "anteposa això a la sortida de camí del submòdul"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"per defecte per a l'obtenció recursiva de submòduls (prioritat més baixa que "
+"els fitxers de configuració)"
+
+msgid "accept refs that update .git/shallow"
+msgstr "accepta les referències que actualitzin .git/shallow"
+
+msgid "refmap"
+msgstr "mapa de referències"
+
+msgid "specify fetch refmap"
+msgstr "específica l'obtenció del mapa de referències"
+
+msgid "report that we have only objects reachable from this object"
+msgstr "informa que només hi ha objectes abastables des d'aquest objecte"
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+"no obtinguis un fitxer de paquet; en canvi, mostra els avantpassats dels "
+"consells de negociació"
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr "executa «maintenance --auto» després d'obtenir"
+
+msgid "check for forced-updates on all updated branches"
+msgstr ""
+"comprova si hi ha actualitzacions forçades a totes les branques actualitzades"
+
+msgid "write the commit-graph after fetching"
+msgstr "escriu el graf de comissions després de recollir"
+
+msgid "accept refspecs from stdin"
+msgstr "llegeix les especificacions de referència des de stdin"
+
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only necessita un o més --negotiation-tip=*"
 
@@ -6046,6 +6096,10 @@ msgstr "no s'admet una profunditat negativa en --deepen"
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "--unshallow en un repositori complet no té sentit"
 
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "no s'han pogut obtenir els paquets de «%s»"
+
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all no accepta un argument de repositori"
 
@@ -6108,7 +6162,7 @@ msgid "git for-each-ref [--points-at <object>]"
 msgstr "git for-each-ref [--points-at <objecte>]"
 
 msgid "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"
-msgstr "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"
+msgstr "git for-each-ref [--merged [<comissió>]] [--no-merged [<comissió>]]"
 
 msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"
 msgstr ""
@@ -6149,8 +6203,14 @@ msgstr "imprimeix només les referències que continguin la comissió"
 msgid "print only refs which don't contain the commit"
 msgstr "imprimeix només les referències que no continguin la comissió"
 
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=<config> <command-args>"
+msgid "read reference patterns from stdin"
+msgstr "llegeix els patrons de referència de l'entrada estàndard"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "s'han proporcionat arguments desconeguts amb --stdin"
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<config> [--] <arguments>"
 
 msgid "config"
 msgstr "config"
@@ -6161,6 +6221,10 @@ msgstr "clau de configuració emmagatzemant una llista de camins de repositori"
 msgid "missing --config=<config>"
 msgstr "falta --config=<config>"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "s'ha obtingut una configuració incorrecta --config=%s"
+
 msgid "unknown"
 msgstr "desconegut"
 
@@ -6307,22 +6371,39 @@ msgstr "%s: la HEAD separada no apunta a res"
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "avís: %s apunta a una branca no nascuda (%s)"
 
-msgid "Checking cache tree"
-msgstr "S'està comprovant l'arbre de la memòria cau"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "S'està comprovant l'arbre de la memòria cau %s"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: apuntador sha1 no vàlid a l'arbre de la memòria cau"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: punter sha1 no vàlid a l'arbre de la memòria cau %s"
 
 msgid "non-tree in cache-tree"
 msgstr "un no arbre en l'arbre de la memòria cau"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: el punter sha1 no és vàlid a «resolve-undo»"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: punter sha1 no vàlid a «resolve-undo» de %s"
 
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<opcions>] [<objecte>...]"
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "no s'ha pogut carregar l'índex de reversió per al paquet «%s»"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "rev-index no vàlid per al paquet «%s»"
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<objecte>...]"
 
 msgid "show unreachable objects"
 msgstr "mostra els objectes inabastables"
@@ -6372,16 +6453,10 @@ msgid "invalid parameter: expected sha1, got '%s'"
 msgstr "paràmetre no vàlid: s'esperava sha1, s'ha obtingut «%s»"
 
 msgid "git fsmonitor--daemon start [<options>]"
-msgstr "git fsmonitor--daemon start [<options>]"
+msgstr "git fsmonitor--daemon start [<opcions>]"
 
 msgid "git fsmonitor--daemon run [<options>]"
-msgstr "git fsmonitor--daemon run [<options>]"
-
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
+msgstr "git fsmonitor--daemon run [<opcions>]"
 
 #, c-format
 msgid "value of '%s' out of range: %d"
@@ -6503,6 +6578,9 @@ msgstr "poda objectes sense referència"
 msgid "pack unreferenced objects separately"
 msgstr "empaqueta els objectes no referenciats de forma separada"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "amb --cruft, limiteu la mida dels paquets cruft nous"
+
 msgid "be more thorough (increased runtime)"
 msgstr "sigues més exhaustiu (el temps d'execució augmenta)"
 
@@ -6576,7 +6654,7 @@ msgid "failed to finish 'git pack-objects' process"
 msgstr "no s'ha pogut finalitzar el procés «git pack-objects»"
 
 msgid "failed to write multi-pack-index"
-msgstr "no s'han pogut escriu l'índex del multipaquet"
+msgstr "no s'ha pogut escriure l'índex del multipaquet"
 
 msgid "'git multi-pack-index expire' failed"
 msgstr "ha fallat el venciment de «git multi-pack-index expire»"
@@ -6627,8 +6705,20 @@ msgstr "executa una tasca específica"
 msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr "usa com a màxim un entre --auto i --schedule=<frequency>"
 
-msgid "failed to run 'git config'"
-msgstr "no s'ha pogut executar «git config»"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "no es pot afegir el valor «%s» de «%s»"
+
+msgid "return success even if repository was not registered"
+msgstr "retorna èxit encara que el repositori no estigui registrat"
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "no es pot desassignar el valor «%s» de «%s»"
+
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "el repositori «%s» no està registrat"
 
 #, c-format
 msgid "failed to expand path '%s'"
@@ -6669,12 +6759,6 @@ msgstr ""
 msgid "'crontab' died"
 msgstr "«crontab» ha mort"
 
-msgid "failed to start systemctl"
-msgstr "s'ha produït un error en iniciar systemctl"
-
-msgid "failed to run systemctl"
-msgstr "s'ha produït un error en executar systemctl"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "s'ha produït un error en suprimir «%s»"
@@ -6683,6 +6767,12 @@ msgstr "s'ha produït un error en suprimir «%s»"
 msgid "failed to flush '%s'"
 msgstr "no s'ha pogut buidar «%s»"
 
+msgid "failed to start systemctl"
+msgstr "s'ha produït un error en iniciar systemctl"
+
+msgid "failed to run systemctl"
+msgstr "s'ha produït un error en executar systemctl"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "l'argument --scheduler no reconegut «%s»"
@@ -6706,11 +6796,14 @@ msgstr "planificador"
 msgid "scheduler to trigger git maintenance run"
 msgstr "planificador per a activar l'execució de manteniment del git"
 
+msgid "failed to set up maintenance schedule"
+msgstr "no s'ha pogut configurar la planificació del manteniment"
+
 msgid "failed to add repo to global config"
 msgstr "no s'ha pogut afegir un repositori a la configuració global"
 
 msgid "git maintenance <subcommand> [<options>]"
-msgstr "git maintenance <subcommand> [<options>]"
+msgstr "git maintenance <subcommand> [<opcions>]"
 
 msgid "git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"
 msgstr "git grep [<opcions>] [-e] <patró> [<revisió>...] [[--] <camí>...]"
@@ -6727,7 +6820,6 @@ msgstr "s'ha especificat un nombre de fils no vàlid (%d) per a %s"
 #. TRANSLATORS: %s is the configuration
 #. variable for tweaking threads, currently
 #. grep.threads
-#.
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "no s'admeten fils, s'ignorarà %s"
@@ -6780,8 +6872,8 @@ msgstr "processa els fitxers binaris amb filtres de textconv"
 msgid "search in subdirectories (default)"
 msgstr "cerca als subdirectoris (per defecte)"
 
-msgid "descend at most <depth> levels"
-msgstr "descendeix com a màxim <profunditat> nivells"
+msgid "descend at most <n> levels"
+msgstr "descendeix com a màxim <n> nivells"
 
 msgid "use extended POSIX regular expressions"
 msgstr "usa les expressions regulars POSIX ampliades"
@@ -6922,11 +7014,14 @@ msgid "both --cached and trees are given"
 msgstr "ambdós --cached i arbres venen donats"
 
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <tipus>] [-w] [--path=<fitxer> | --no-filters] [--stdin] "
-"[--] <fitxer>..."
+"git hash-object [-t <tipus>] [-w] [--path=<fitxer> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <fitxer>..."
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <tipus>] [-w] --stdin-paths [--no-filters]"
 
 msgid "object type"
 msgstr "tipus d'objecte"
@@ -7061,12 +7156,19 @@ msgstr "ús: %s%s"
 msgid "'git help config' for more information"
 msgstr "«git help config» per a més informació"
 
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<camí>] <hook-name> [-- <hook-"
+"args>]"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "ignora silenciosament la sol·licitud <hook-name> perduda"
 
+msgid "file to read into hooks' stdin"
+msgstr "fitxer per a llegir a l'entrada estàndard dels lligams"
+
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "hi ha una discordança de tipus d'objecte a %s"
@@ -7285,83 +7387,16 @@ msgstr "s'ha donat --verify sense nom de fitxer de paquet"
 msgid "fsck error in pack objects"
 msgstr "error fsck als objectes del paquet"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "no es pot fer stat en la plantilla «%s»"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "no es pot fer opendir en el directori «%s»"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "no es pot fer readlink en «%s»"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "no es pot fer symlink en «%s» «%s»"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "no es pot copiar «%s» a «%s»"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "s'està ignorant la plantilla %s"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "plantilles no trobades a %s"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "no s'estan copiant plantilles de «%s»: %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "nom de branca inicial no vàlid: «%s»"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "no s'ha pogut gestionar el tipus de fitxer %d"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "no s'ha pogut moure %s a %s"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "s'ha intentat reinicialitzar el repositori amb un resum diferent"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s ja existeix"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "reinicialització: s'ha ignorat --initial-branch=%s"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "S'ha reinicialitzat el repositori compartit existent del Git en %s%s\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "S'ha reinicialitzat el repositori existent del Git en %s%s\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "S'ha inicialitzat un repositori compartit buit del Git en %s%s\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "S'ha inicialitzat un repositori buit del Git en %s%s\n"
-
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<directori-de-plantilla>] [--"
-"shared[=<permisos>]] [<directori>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 
 msgid "permissions"
 msgstr "permisos"
@@ -7403,11 +7438,13 @@ msgid "--separate-git-dir incompatible with bare repository"
 msgstr "--separate-git-dir és incompatible amb un repositori nu"
 
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<testimoni>[(=|:)<valor>])...] [<fitxer>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 
 msgid "edit files in place"
 msgstr "edita els fitxers in situ"
@@ -7415,6 +7452,9 @@ msgstr "edita els fitxers in situ"
 msgid "trim empty trailers"
 msgstr "escurça els remolcs buits"
 
+msgid "placement"
+msgstr "posicionament"
+
 msgid "where to place the new trailer"
 msgstr "on ubicar el «trailer» nou"
 
@@ -7427,17 +7467,18 @@ msgstr "acció si el «trailer» falta"
 msgid "output only the trailers"
 msgstr "mostra només els «trailer»"
 
-msgid "do not apply config rules"
-msgstr "no apliquis les regles de configuració"
+msgid "do not apply trailer.* configuration variables"
+msgstr "no apliquis les variables de configuració trailer.*"
 
-msgid "join whitespace-continued values"
-msgstr "uneix els valors continus amb espais en blanc"
+msgid "reformat multiline trailer values as single-line values"
+msgstr ""
+"reformata els valors del tràiler multilínia com a valors de línia única"
 
-msgid "set parsing options"
-msgstr "estableix les opcions d'anàlisi"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "àlies per a --only-trailers --only-input --unfold"
 
-msgid "do not treat --- specially"
-msgstr "no tractis --- especialment"
+msgid "do not treat \"---\" as the end of input"
+msgstr "no tractis «---» com el final de l'entrada"
 
 msgid "trailer(s) to add"
 msgstr "remolcs a afegir"
@@ -7481,14 +7522,14 @@ msgid ""
 "<file>"
 msgstr ""
 "traça l'evolució del rang de línia <start>,<end> o funcions :<funcname> a "
-"<file>"
+"<fitxer>"
 
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "argument no reconegut: %s"
 
 msgid "-L<range>:<file> cannot be used with pathspec"
-msgstr "-L<range>:<file> no es pot usar amb una especificació de camí"
+msgstr "-L<range>:<fitxer> no es pot usar amb una especificació de camí"
 
 #, c-format
 msgid "Final output: %d %s\n"
@@ -7526,6 +7567,10 @@ msgstr "necessita exactament un interval"
 msgid "not a range"
 msgstr "no és un interval"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "no es pot llegir el fitxer de descripció de la branca «%s»"
+
 msgid "cover letter needs email format"
 msgstr "la carta de presentació necessita un format de correu electrònic"
 
@@ -7627,6 +7672,9 @@ msgstr ""
 "genera parts d'una carta de presentació basant-se en la descripció d'una "
 "branca"
 
+msgid "use branch description from file"
+msgstr "utilitza la descripció de la branca des del fitxer"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "useu [<prefix>] en comptes de [PATCH]"
 
@@ -7789,6 +7837,10 @@ msgstr ""
 "No s'ha pogut trobar una branca remota seguida. Especifiqueu <font> "
 "manualment.\n"
 
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "no s'ha pogut obtenir la informació sobre l'objecte «%s»"
+
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "format incorrecte del ls-files: l'element «%s» no comença amb «(»"
@@ -7857,7 +7909,7 @@ msgid "skip files matching pattern"
 msgstr "omet els fitxers coincidents amb el patró"
 
 msgid "read exclude patterns from <file>"
-msgstr "llegeix els patrons des de <file>"
+msgstr "llegeix els patrons des de <fitxer>"
 
 msgid "read additional per-directory exclude patterns in <file>"
 msgstr "llegeix els patrons addicionals d'exclusió per directori en <fitxer>"
@@ -7896,12 +7948,12 @@ msgstr ""
 
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 
 msgid "do not print remote URL"
 msgstr "no imprimeixis l'URL remot"
@@ -7933,10 +7985,6 @@ msgstr "mostra la referència subjacent a més de l'objecte que assenyali"
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
 msgstr "git ls-tree [<opcions>] <arbre> [<camí>...]"
 
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "no s'ha pogut obtenir la informació sobre l'objecte «%s»"
-
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "format incorrecte del ls-tree: l'element '%s' no comença amb «(»"
@@ -7982,7 +8030,7 @@ msgstr "--format no es pot combinar amb altres opcions d'alteració de format"
 
 #. TRANSLATORS: keep <> in "<" mail ">" info.
 msgid "git mailinfo [<options>] <msg> <patch> < mail >info"
-msgstr "git mailinfo [<options>] <msg> <patch> < mail >info"
+msgstr "git mailinfo [<opcions>] <msg> <pedaç> < mail >info"
 
 msgid "keep subject"
 msgstr "mantén l'assumpte"
@@ -8030,12 +8078,12 @@ msgstr "git merge-base [-a | --all] <comissió> <comissió>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <comissió>..."
 
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <comissió>..."
-
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <comissió> <comissió>"
 
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <comissió>..."
+
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <referència> [<comissió>]"
 
@@ -8065,6 +8113,9 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "envia els resultats a la sortida estàndard"
 
+msgid "use object IDs instead of filenames"
+msgstr "utilitza els ID dels objectes en comptes dels noms de fitxer"
+
 msgid "use a diff3 based merge"
 msgstr "usa una fusió basada en diff3"
 
@@ -8089,6 +8140,13 @@ msgstr "no avisis de conflictes"
 msgid "set labels for file1/orig-file/file2"
 msgstr "estableix les etiquetes per a fitxer1/fitxer-original/fitxer2"
 
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "l'objecte «%s» no existeix"
+
+msgid "Could not write object file"
+msgstr "No s'ha pogut escriure el fitxer de l'objecte"
+
 #, c-format
 msgid "unknown option %s"
 msgstr "opció desconeguda %s"
@@ -8124,7 +8182,7 @@ msgid "failure to merge"
 msgstr "s'ha produït un error en fusionar"
 
 msgid "git merge-tree [--write-tree] [<options>] <branch1> <branch2>"
-msgstr "git merge-tree [--write-tree] [<options>] <branch1> <branch2>"
+msgstr "git merge-tree [--write-tree] [<opcions>] <branch1> <branch2>"
 
 msgid "git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"
 msgstr "git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"
@@ -8144,9 +8202,36 @@ msgstr "llista els noms de fitxer sense modes/oids/stages"
 msgid "allow merging unrelated histories"
 msgstr "permet fusionar històries no relacionades"
 
+msgid "perform multiple merges, one per line of input"
+msgstr "realitza múltiples fusions, una per línia d'entrada"
+
+msgid "specify a merge-base for the merge"
+msgstr "cal especificar una referència base per a la fusió"
+
+msgid "option=value"
+msgstr "opció=valor"
+
+msgid "option for selected merge strategy"
+msgstr "opció per a l'estratègia de fusió seleccionada"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge és incompatible amb totes les altres opcions"
 
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "opció d'estratègia desconeguda: -X%s"
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base és incompatible amb --stdin"
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "línia d'entrada mal formada: «%s»."
+
+#, c-format
+msgid "merging cannot continue; got unclean result of %d"
+msgstr "la fusió no pot continuar; s'ha obtingut un resultat no net de %d"
+
 msgid "git merge [<options>] [<commit>...]"
 msgstr "git merge [<opcions>] [<comissió>...]"
 
@@ -8207,12 +8292,6 @@ msgstr "estratègia"
 msgid "merge strategy to use"
 msgstr "estratègia de fusió a usar"
 
-msgid "option=value"
-msgstr "opció=valor"
-
-msgid "option for selected merge strategy"
-msgstr "opció per a l'estratègia de fusió seleccionada"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "missatge de comissió de fusió (per a una fusió no d'avanç ràpid)"
 
@@ -8272,10 +8351,6 @@ msgstr "No s'ha pogut escriure l'índex."
 msgid "Not handling anything other than two heads merge."
 msgstr "No s'està gestionant res a part de la fusió de dos caps."
 
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "opció d'estratègia desconeguda: -X%s"
-
 #, c-format
 msgid "unable to write %s"
 msgstr "no s'ha pogut escriure %s"
@@ -8492,8 +8567,8 @@ msgid ""
 "git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
 "snapshot=<path>]"
 msgstr ""
-"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
-"snapshot=<path>]"
+"git multi-pack-index [<opcions>] write [--preferred-pack=<pack>][--refs-"
+"snapshot=<camí>]"
 
 msgid "git multi-pack-index [<options>] verify"
 msgstr "git multi-pack-index [<opcions>] verify"
@@ -8575,8 +8650,8 @@ msgstr "el destí existeix"
 msgid "can not move directory into itself"
 msgstr "no es pot moure un directori a dins d'ell mateix"
 
-msgid "cannot move directory over file"
-msgstr "no es pot moure un directori sobre un fitxer"
+msgid "destination already exists"
+msgstr "la destinació ja existeix"
 
 msgid "source directory is empty"
 msgstr "el directori d'origen està buit"
@@ -8622,7 +8697,7 @@ msgid "git name-rev [<options>] --all"
 msgstr "git name-rev [<opcions>] --all"
 
 msgid "git name-rev [<options>] --annotate-stdin"
-msgstr "git name-rev [<options>] --annotate-stdin"
+msgstr "git name-rev [<opcions>] --annotate-stdin"
 
 msgid "print only ref-based names (no object names)"
 msgstr "imprimeix només els noms basats en referències (no els noms d'objecte)"
@@ -8655,22 +8730,26 @@ msgid "git notes [--ref <notes-ref>] [list [<object>]]"
 msgstr "git notes [--ref <referència-de-notes>] [llista [<objecte>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <referència-de-notes>] add [-f] [--allow-empty] [-m "
-"<missatge> | -F <fitxer> | (-c | -C) <objecte>] [<objecte>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <referència-de-notes>] copy [-f] <d'objecte> <a-objecte>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <referència-de-notes>] append [--allow-empty] [-m "
-"<missatge> | -F <fitxer> | (-c | -C) <objecte>] [<objecte>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr ""
@@ -8771,17 +8850,12 @@ msgstr "s'ha produït un error en llegir l'objecte «%s»."
 msgid "cannot read note data from non-blob object '%s'."
 msgstr "no es poden llegir les dades de node de l'objecte no de blob «%s»."
 
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "línia d'entrada mal formada: «%s»."
-
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
 msgstr "s'ha produït un error en copiar les notes de «%s» a «%s»"
 
 #. TRANSLATORS: the first %s will be replaced by a git
 #. notes command: 'add', 'merge', 'remove', etc.
-#.
 #, c-format
 msgid "refusing to %s notes in %s (outside of refs/notes/)"
 msgstr "s'està refusant %s les notes en %s (fora de refs/notes/)"
@@ -8808,6 +8882,15 @@ msgstr "permet l'emmagatzematge d'una nota buida"
 msgid "replace existing notes"
 msgstr "reemplaça les notes existents"
 
+msgid "<paragraph-break>"
+msgstr "<paragraph-break>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "insereix <paragraph-break> entre paràgrafs"
+
+msgid "remove unnecessary whitespace"
+msgstr "elimina l'espai en blanc innecessari"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -8968,17 +9051,13 @@ msgstr "usa les notes de <referència-de-notes>"
 msgid "unknown subcommand: `%s'"
 msgstr "subordre desconeguda: «%s»"
 
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
-msgstr ""
-"git pack-objects --stdout [<opcions>...] [< <llista-de-referències> | < "
-"<llista-de-objectes>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
+msgstr "git pack-objects --stdout [<opcions>] [< <ref-list> | < <object-list>]"
 
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects [<opcions>...] <nom-base> [< <llista-de-referències> | < "
-"<llista-de-objectes>]"
+"git pack-objects [<opcions>] <base-name> [< <ref-list> | < <object-list>]"
 
 #, c-format
 msgid ""
@@ -9170,6 +9249,12 @@ msgstr "versió d'índex no compatible %s"
 msgid "bad index version '%s'"
 msgstr "versió d'índex incorrecta «%s»"
 
+msgid "show progress meter during object writing phase"
+msgstr "mostra l'indicador de progrés durant la fase d'escriptura d'objectes"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "similar a --all-progress quan l'indicador de progrés es mostra"
+
 msgid "<version>[,<offset>]"
 msgstr "<versió>[,<desplaçament>]"
 
@@ -9327,9 +9412,6 @@ msgstr "el límit mínim de mida del paquet és 1 MiB"
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin no es pot utilitzar per a construir un paquet indexable"
 
-msgid "cannot use --filter without --stdout"
-msgstr "no es pot utilitzar --filter sense --stdout"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "no es pot utilitzar --filter sense --stdin-packs"
 
@@ -9342,9 +9424,6 @@ msgstr "no es pot utilitzar la llista de revisió interna amb --cruft"
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "no es pot --stdin-packs amb --cruft"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "no es pot usar --max-pack-size amb --cruft"
-
 msgid "Enumerating objects"
 msgstr "S'estan enumerant els objectes"
 
@@ -9369,8 +9448,14 @@ msgstr ""
 "i feu-nos saber que encara l'useu enviant un correu electrònic\n"
 "a <git@vger.kernel.org>.  Gràcies.\n"
 
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<opcions>]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "es rebutja a executar sense --i-still-use-this"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <patró>] [--exclude <patró>]"
 
 msgid "pack everything"
 msgstr "empaqueta-ho tot"
@@ -9378,6 +9463,24 @@ msgstr "empaqueta-ho tot"
 msgid "prune loose refs (default)"
 msgstr "poda les referències soltes (per defecte)"
 
+msgid "references to include"
+msgstr "referències a incloure"
+
+msgid "references to exclude"
+msgstr "referències a excloure"
+
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+msgid "use the unstable patch-id algorithm"
+msgstr "utilitza l'algorisme inestable de patch-id"
+
+msgid "use the stable patch-id algorithm"
+msgstr "utilitza l'algorisme estable de patch-id"
+
+msgid "don't strip whitespace from the patch"
+msgstr "no eliminis els espais en blanc del pedaç"
+
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr "git prune [-n] [-v] [--progress] [--expire <data>] [--] [<head>...]"
 
@@ -9424,6 +9527,12 @@ msgstr "força la sobreescriptura de la branca local"
 msgid "number of submodules pulled in parallel"
 msgstr "nombre de submòduls baixats en paral·lel"
 
+msgid "use IPv4 addresses only"
+msgstr "usa només adreces IPv4"
+
+msgid "use IPv6 addresses only"
+msgstr "usa només adreces IPv6"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9531,8 +9640,8 @@ msgstr ""
 msgid "pull with rebase"
 msgstr "baixar fent «rebase»"
 
-msgid "please commit or stash them."
-msgstr "cometeu-los o emmagatzemeu-los."
+msgid "Please commit or stash them."
+msgstr "Cometeu-los o emmagatzemeu-los."
 
 #, c-format
 msgid ""
@@ -9597,16 +9706,14 @@ msgstr ""
 
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
 "Per a evitar configurar automàticament les branques font quan el seu nom\n"
-"no coincideix amb el de la branca local, vegeu l'opció «simple» de «branch."
-"autoSetupMerge»\n"
-"a «git help config».\n"
+"no coincideix amb el de la branca local, vegeu l'opció «simple» de\n"
+"«branch.autoSetupMerge» a «git help config».\n"
 
 #, c-format
 msgid ""
@@ -9693,43 +9800,39 @@ msgstr ""
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"S'han rebutjat les actualitzacions perquè el punt de la vostra branca\n"
-"actual està darrere de la seva branca remota corresponent. Integreu\n"
-"els canvis remots (per exemple, «git pull ...») abans de pujar de nou.\n"
-"Vegeu la «Nota sobre avanços ràpids» a «git push --help» per a més "
-"informació."
+"S'han rebutjat les actualitzacions perquè la punta de la branca actual\n"
+"està darrere de la seva contrapart remota. Si voleu integrar els canvis,\n"
+"remots useu «git pull» abans de tornar a pujar.\n"
+"Vegeu «Note about fast-forwards» a «git push --help» per a més detalls."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"S'han rebutjat les actualitzacions perquè un punt de branca pujada\n"
-"està darrere de la seva branca remota corresponent. Agafeu aquesta\n"
-"branca i integreu els canvis remots (per exemple, «git pull ...»)\n"
-"abans de pujar de nou.\n"
-"Vegeu la «Nota sobre avanços ràpids» a «git push --help» per a més "
-"informació."
+"Les actualitzacions s'han rebutjat perquè la branca pujada està darrere\n"
+"de la seva contrapart remota. Si voleu integrar els canvis remots,\n"
+"utilitzeu «git pull» abans de tornar a pujar.\n"
+"Vegeu «Note about fast-forwards» a «git push --help» per a més detalls."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"S'han rebutjat les actualitzacions perquè el remot conté canvis\n"
-"que no teniu localment. Això acostuma a ser causat per un altre repositori\n"
-"que ha pujat a la mateixa referència. Pot ser que primer vulgueu\n"
-"integrar els canvis remots (per exemple, «git pull ...») abans de\n"
-"pujar de nou.\n"
-"Vegeu la «Nota sobre avanços ràpids» a «git push --help» per a més "
-"informació."
+"Les actualitzacions s'han rebutjat perquè el remot conté canvis que no "
+"teniu\n"
+"localment. Això sol ser causat per un altre repositori que a pujat a\n"
+"la mateixa referència. Si voleu integrar els canvis remots, utilitzeu\n"
+"«git pull» abans de tornar a pujar.\n"
+"Vegeu «Note about fast-forwards» a «git push --help» per a més detalls."
 
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr ""
@@ -9746,15 +9849,15 @@ msgstr ""
 "«--force».\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"S'han rebutjat les actualitzacions perquè el punt actual de la vostra\n"
-"branca està darrere de la seva branca remota corresponent. Integreu\n"
-"els canvis localment (per exemple, «git pull ...») abans de forçar\n"
-"una pujada.\n"
+"Les actualitzacions s'han rebutjat perquè la branca de seguiment remot\n"
+"s'ha actualitzat des de l'última baixada. Si voleu integrar els\n"
+"canvis remots, utilitzeu «git pull» abans de tornar a pujar.\n"
+"Vegeu «Note about fast-forwards» a «git push --help» per a més detalls."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -9764,6 +9867,13 @@ msgstr "S'està pujant a %s\n"
 msgid "failed to push some refs to '%s'"
 msgstr "s'ha produït un error en pujar algunes referències a «%s»"
 
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"cerca recursivament en el submòdul amb push.recurseSubmodules=only; "
+"utilitzant «on-demand» en el seu lloc"
+
 #, c-format
 msgid "invalid value for '%s'"
 msgstr "valor no vàlid per a «%s»"
@@ -9771,7 +9881,7 @@ msgstr "valor no vàlid per a «%s»"
 msgid "repository"
 msgstr "repositori"
 
-msgid "push all refs"
+msgid "push all branches"
 msgstr "puja totes les referències"
 
 msgid "mirror all refs"
@@ -9780,8 +9890,8 @@ msgstr "reflecteix totes les referències"
 msgid "delete refs"
 msgstr "suprimeix les referències"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "puja les etiquetes (no es pot usar amb --all o --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr "puja les etiquetes (no es pot usar amb --all, --branches o --mirror)"
 
 msgid "force updates"
 msgstr "força les actualitzacions"
@@ -9899,13 +10009,15 @@ msgid "need two commit ranges"
 msgstr "calen dos rangs de comissió"
 
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<fitxer>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 
 msgid "write resulting index to <file>"
 msgstr "escriu l'índex resultant al <fitxer>"
@@ -9965,13 +10077,14 @@ msgid ""
 "git rebase [-i] [options] [--exec <cmd>] [--onto <newbase> | --keep-base] "
 "[<upstream> [<branch>]]"
 msgstr ""
-"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase> | --keep-base] "
-"[<upstream> [<branch>]]"
+"git rebase [-i] [options] [--exec <ordre>] [--onto <newbase> | --keep-base] "
+"[<upstream> [<branca>]]"
 
 msgid ""
 "git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]"
 msgstr ""
-"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]"
+"git rebase [-i] [options] [--exec <ordre>] [--onto <newbase>] --root "
+"[<branca>]"
 
 #, c-format
 msgid "could not read '%s'."
@@ -9995,8 +10108,8 @@ msgid "%s requires the merge backend"
 msgstr "%s requereix un rerefons de fusió"
 
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "no s'ha pogut obtenir «onto»: «%s»"
+msgid "invalid onto: '%s'"
+msgstr "no vàlid a: «%s»"
 
 #, c-format
 msgid "invalid orig-head: '%s'"
@@ -10043,10 +10156,17 @@ msgstr ""
 "\n"
 "Com a resultat, git no pot fer un «rebase» d'elles."
 
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Mode de fusió de rebase desconegut: %s"
+
 #, c-format
 msgid "could not switch to %s"
 msgstr "no s'ha pogut commutar a %s"
 
+msgid "apply options and merge options cannot be used together"
+msgstr "les opcions apply i merge no es poden usar juntes"
+
 #, c-format
 msgid ""
 "unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
@@ -10054,6 +10174,15 @@ msgid ""
 msgstr ""
 "tipus buit no reconegut «%s»; els valors vàlids són «drop», «keep» i «ask»."
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"--rebase-merges amb un argument de cadena buit està obsolet i deixarà de "
+"funcionar en una versió futura del Git. Utilitzeu --rebase-merges sense un "
+"argument, que fa el mateix."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10257,7 +10386,7 @@ msgid ""
 "and run me again.  I am stopping in case you still have something\n"
 "valuable there.\n"
 msgstr ""
-"Sembla que ja exigeix un directori %s, i em pregunto\n"
+"Sembla que ja existeix un directori %s, i em pregunto\n"
 "si esteu enmig d'un altre «rebase». Si aquest és el cas, proveu\n"
 "\t%s\n"
 "Si no és cas, feu:\n"
@@ -10268,15 +10397,26 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "«switch» «c» espera un valor numèric"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Mode desconegut: %s"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+"les opcions «apply» són incompatibles amb rebase.autoSquash. Considereu "
+"afegir-hi --no-autosquash"
 
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy requereix --merge o --interactive"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+"les opcions «apply» són incompatibles amb rebase.rebaseMerges. Considereu "
+"afegir-hi --no-rebase-merges"
 
-msgid "apply options and merge options cannot be used together"
-msgstr "les opcions apply i merge no es poden usar juntes"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr ""
+"les opcions «apply» són incompatibles amb rebase.updateRefs. Considereu "
+"afegir-hi --no-update-refs"
 
 #, c-format
 msgid "Unknown rebase backend: %s"
@@ -10300,8 +10440,8 @@ msgstr "no existeix aquesta branca o comissió «%s»"
 msgid "No such ref: %s"
 msgstr "No hi ha tal referència: %s"
 
-msgid "Could not resolve HEAD to a revision"
-msgstr "No s'ha pogut resoldre HEAD a una revisió"
+msgid "Could not resolve HEAD to a commit"
+msgstr "No s'ha pogut resoldre HEAD com a una comissió"
 
 #, c-format
 msgid "'%s': need exactly one merge base with branch"
@@ -10315,9 +10455,6 @@ msgstr "«%s»: necessita exactament una base de fusió"
 msgid "Does not point to a valid commit '%s'"
 msgstr "No apunta a una comissió vàlida «%s»"
 
-msgid "Please commit or stash them."
-msgstr "Cometeu-los o emmagatzemeu-los."
-
 msgid "HEAD is up to date."
 msgstr "HEAD està al dia."
 
@@ -10498,7 +10635,7 @@ msgid ""
 "git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--"
 "mirror=<fetch|push>] <name> <url>"
 msgstr ""
-"git remote add [-t <branca>] [-m <mestra>] [-f] [--tags | --no-tags] [--"
+"git remote add [-t <branca>] [-m <master>] [-f] [--tags | --no-tags] [--"
 "mirror=<fetch|push>] <nom> <url>"
 
 msgid "git remote rename [--[no-]progress] <old> <new>"
@@ -10577,11 +10714,12 @@ msgstr "argument de «mirror» desconegut: %s"
 msgid "fetch the remote branches"
 msgstr "obtén les branques remotes"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "en obtenir, importa totes les etiquetes i tots els objectes"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "o no obtinguis cap etiqueta (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"importa totes les etiquetes i objectes associats en obtenir\n"
+"o no obtingueu cap etiqueta (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "branques a seguir"
@@ -10784,7 +10922,6 @@ msgstr "(sense URL)"
 #. TRANSLATORS: the colon ':' should align
 #. with the one in " Fetch URL: %s"
 #. translation.
-#.
 #, c-format
 msgid "  Push  URL: %s"
 msgstr "  URL de pujada: %s"
@@ -10973,6 +11110,14 @@ msgid "could not close refs snapshot tempfile"
 msgstr ""
 "no s'ha pogut tancar el fitxer temporal amb la instantània de referències"
 
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "no s'ha pogut eliminar el mapa de bits estancat: %s"
+
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "el prefix de paquet %s no comença amb objdir %s"
+
 msgid "pack everything in a single pack"
 msgstr "empaqueta-ho tot en un únic paquet"
 
@@ -10987,8 +11132,8 @@ msgstr ""
 msgid "approxidate"
 msgstr "data aproximada"
 
-msgid "with -C, expire objects older than this"
-msgstr "amb -C, venç els objectes més antics que aquest"
+msgid "with --cruft, expire objects older than this"
+msgstr "amb --cruft, vencen els objectes més antics que aquest"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "elimina els paquets redundants, i executeu git-prune-packed"
@@ -11049,19 +11194,33 @@ msgstr "troba una progressió geomètrica amb el factor <N>"
 msgid "write a multi-pack index of the resulting packs"
 msgstr "escriu un índex multipaquet dels paquets resultants"
 
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr ""
+"prefix del paquet per a emmagatzemar un paquet que contingui objectes podats"
+
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr ""
+"prefix del paquet per a emmagatzemar un paquet que contingui objectes "
+"filtrats"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "no es poden suprimir paquets en un repositori d'objectes preciosos"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "l'opció «%s» només es pot utilitzar juntament amb «%s»"
+
 msgid "Nothing new to pack."
 msgstr "Res nou a empaquetar."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "el prefix de paquet %s no comença amb objdir %s"
+msgid "renaming pack to '%s' failed"
+msgstr "el canvi del nom a «%s» ha fallat"
 
 #, c-format
-msgid "missing required file: %s"
-msgstr "falta el fitxer requerit: %s"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr ""
+"els objectes de paquet no han escrit a un fitxer «%s» per al paquet %s-%s"
 
 #, c-format
 msgid "could not unlink: %s"
@@ -11074,7 +11233,7 @@ msgid "git replace [-f] --edit <object>"
 msgstr "git replace [-f] --edit <objecte>"
 
 msgid "git replace [-f] --graft <commit> [<parent>...]"
-msgstr "git replace [-f] --graft <comissió> [<parent>...]"
+msgstr "git replace [-f] --graft <comissió> [<pare>...]"
 
 msgid "git replace -d <object>..."
 msgstr "git replace -d <objecte>..."
@@ -11255,8 +11414,10 @@ msgstr "--convert-graft-file arguments"
 msgid "only one pattern can be given with -l"
 msgstr "només es pot especificar un patró amb -l"
 
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forget <camí>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 
 msgid "register clean resolutions in index"
 msgstr "registra les resolucions netes en l'índex"
@@ -11424,9 +11585,9 @@ msgid ""
 "\n"
 "Run \"git rev-parse --parseopt -h\" for more information on the first usage."
 msgstr ""
-"git rev-parse --parseopt [<options>] -- [<args>...]\n"
+"git rev-parse --parseopt [<opcions>] -- [<args>...]\n"
 "   o bé: git rev-parse --sq-quote [<arg>...]\n"
-"   o bé: git rev-parse [<options>] [<arg>...]\n"
+"   o bé: git rev-parse [<opcions>] [<arg>...]\n"
 "\n"
 "Executeu «git rev-parse --parseopt -h» per a més informació sobre el primer "
 "ús."
@@ -11461,24 +11622,44 @@ msgstr "--prefix requereix un argument"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "mode desconegut per a --abbrev-ref: %s"
 
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden no es pot utilitzar juntament amb --branches"
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden no es pot utilitzar juntament amb --tags"
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden no es pot utilitzar juntament amb --remotes"
+
 msgid "this operation must be run in a work tree"
 msgstr "aquesta operació s'ha d'executar en un arbre de treball"
 
+msgid "Could not read the index"
+msgstr "No s'ha pogut llegir l'índex"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "mode desconegut per a --show-object-format: %s"
 
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<opcions>] <comissió>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<comissió>..."
 
-msgid "git revert <subcommand>"
-msgstr "git revert <subordre>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
 
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<opcions>] <comissió>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <comissió>..."
 
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <subordre>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
@@ -11539,8 +11720,14 @@ msgstr "la reversió ha fallat"
 msgid "cherry-pick failed"
 msgstr "el «cherry pick» ha fallat"
 
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<opcions>] [--] <fitxer>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<fitxer> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -11614,16 +11801,21 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 
 msgid "remote name"
 msgstr "nom del remot"
 
+msgid "push all refs"
+msgstr "puja totes les referències"
+
 msgid "use stateless RPC protocol"
 msgstr "usa el protocol RPC sense estat"
 
@@ -11642,8 +11834,9 @@ msgstr "git log --pretty=short | git shortlog [<opcions>]"
 msgid "using multiple --group options with stdin is not supported"
 msgstr "no s'admet l'ús de múltiples opcions --group amb stdin"
 
-msgid "using --group=trailer with stdin is not supported"
-msgstr "no s'admet l'ús de --group=trailer amb stdin"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "no s'admet l'ús de %s amb stdin"
 
 #, c-format
 msgid "unknown group type: %s"
@@ -11682,12 +11875,14 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<referència>]"
@@ -11787,21 +11982,44 @@ msgid "Unknown hash algorithm"
 msgstr "Algorisme de resum desconegut"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<patró>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<patró>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <ref>"
+
+msgid "reference does not exist"
+msgstr "la referència no existeix"
+
+msgid "failed to look up reference"
+msgstr "s'ha produït en cercar la referència"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "mostra només les etiquetes (es pot combinar amb heads)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "mostra només els caps (es pot combinar amb tags)"
 
+msgid "check for reference existence without resolving"
+msgstr "comprova l'existència de referència sense resoldre"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr ""
 "comprovació de referència més estricta, requereix el camí de referència "
@@ -11822,8 +12040,16 @@ msgstr "no imprimeixis els resultats a stdout (útil amb --verify)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "mostra les referències de stdin que no siguin en el repositori local"
 
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) <opcions>"
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "només es poden donar les opcions «%s», «%s», o «%s»"
+
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<opcions>]"
 
 msgid "this worktree is not sparse"
 msgstr "aquest arbre de treball no és dispers"
@@ -11947,67 +12173,76 @@ msgstr ""
 msgid "error while refreshing working directory"
 msgstr "s'ha produït un error en actualitzar el directori de treball"
 
-msgid "git stash list [<options>]"
-msgstr "git stash list [<opcions>]"
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <fitxer>]"
 
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<opcions>] [<stash>]"
+msgid "terminate input and output files by a NUL character"
+msgstr "acaba els fitxers d'entrada i de sortida amb un caràcter NUL"
 
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<stash>]"
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"quan s'utilitza amb --rules-file, interpreta els patrons com a patrons del "
+"mode con"
 
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
+msgid "use patterns in <file> instead of the current ones."
+msgstr "utilitza patrons en <file> en lloc dels actuals."
 
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <nom-de-branca> [<stash>]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<log-options>]"
 
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <missatge>]\n"
-"          [--pathspec-from-file=<fitxer> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<missatge>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<stash>]"
 
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<stash>]"
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<stash>]"
 
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<stash>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<stash>]"
+
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <nom-de-branca> [<stash>]"
 
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <missatge>] [-q|--quiet] <commit>"
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr ""
+"git stash store [(-m | --message) <missatge>] [-q | --quiet] <comissió>"
 
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <missatge>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<missatge>]\n"
+"          [--pathspec-from-file=<fitxer> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<missatge>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<missatge>]"
+
+msgid "git stash create [<message>]"
+msgstr "git stash create [<missatge>]"
 
 #, c-format
 msgid "'%s' is not a stash-like commit"
@@ -12223,7 +12458,7 @@ msgid "suppress output of entering each submodule command"
 msgstr "omet la sortida en entrar a cada ordre del submòdul"
 
 msgid "recurse into nested submodules"
-msgstr "inclou recursivament els submòduls imbricats"
+msgstr "cerca recursivament als submòduls imbricats"
 
 msgid "git submodule foreach [--quiet] [--recursive] [--] <command>"
 msgstr "git submodule foreach [--quiet] [--recursive] [--] <ordre>"
@@ -12316,7 +12551,7 @@ msgid "limit the summary size"
 msgstr "limita la mida del resum"
 
 msgid "git submodule summary [<options>] [<commit>] [--] [<path>]"
-msgstr "git submodule summary [<options>] [<commit>] [--] [<path>]"
+msgstr "git submodule summary [<opcions>] [<comissió>] [--] [<camí>]"
 
 msgid "could not fetch a revision for HEAD"
 msgstr "no s'ha pogut obtenir una revisió per a HEAD"
@@ -12337,7 +12572,7 @@ msgid "suppress output of synchronizing submodule url"
 msgstr "omet la sortida de la sincronització de l'URL del submòdul"
 
 msgid "git submodule sync [--quiet] [--recursive] [<path>]"
-msgstr "git submodule sync [--quiet] [--recursive] [<path>]"
+msgstr "git submodule sync [--quiet] [--recursive] [<camí>]"
 
 #, c-format
 msgid ""
@@ -12457,9 +12692,9 @@ msgid ""
 "<repository>] [--name <name>] [--depth <depth>] [--single-branch] [--filter "
 "<filter-spec>] --url <url> --path <path>"
 msgstr ""
-"git submodule--helper clone [--prefix=<path>] [--quiet] [--reference "
+"git submodule--helper clone [--prefix=<camí>] [--quiet] [--reference "
 "<repository>] [--name <name>] [--depth <depth>] [--single-branch] [--filter "
-"<filter-spec>] --url <url> --path <path>"
+"<filter-spec>] --url <url> --path <camí>"
 
 #, c-format
 msgid "Invalid update mode '%s' configured for submodule path '%s'"
@@ -12481,6 +12716,10 @@ msgstr "S'està ometent el submòdul no fusionat %s"
 msgid "Skipping submodule '%s'"
 msgstr "S'està ometent el submòdul «%s»"
 
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "no es pot clonar el submòdul «%s» sense un URL"
+
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "S'ha produït un error en clonar «%s». S'ha programat un reintent"
@@ -12579,9 +12818,6 @@ msgstr "recorre els submòduls recursivament"
 msgid "don't fetch new objects from the remote site"
 msgstr "no obtinguis els objectes nous del lloc remot"
 
-msgid "path into the working tree"
-msgstr "camí a l'arbre de treball"
-
 msgid "use the 'checkout' update strategy (default)"
 msgstr "utilitza l'estratègia d'actualització «checkout» (predeterminada)"
 
@@ -12615,34 +12851,19 @@ msgstr ""
 "git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] "
 "[-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-"
 "shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
-"[--] [<path>...]"
+"[--] [<camí>...]"
 
-msgid "recurse into submodules"
-msgstr "inclou recursivament als submòduls"
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "S'ha produït un error en resoldre HEAD com a referència vàlida."
 
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
-msgstr "git submodule absorbgitdirs [<options>] [<path>...]"
-
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr "comprova si és segur escriure al fitxer .gitmodules"
-
-msgid "unset the config in the .gitmodules file"
-msgstr "desconfigura l'opció de configuració al fitxer .gitmodules"
-
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <nom> [<valor>]"
-
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <nom>"
-
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr "assegureu-vos que el fitxer .gitmodules és a l'arbre de treball"
+msgstr "git submodule absorbgitdirs [<opcions>] [<camí>...]"
 
 msgid "suppress output for setting url of a submodule"
 msgstr "omet la sortida en configurar un URL d'un submòdul"
 
 msgid "git submodule set-url [--quiet] <path> <newurl>"
-msgstr "git submodule set-url [--quiet] <path> <newurl>"
+msgstr "git submodule set-url [--quiet] <camí> <newurl>"
 
 msgid "set the default tracking branch to master"
 msgstr "estableix la branca de seguiment per defecte a «master»"
@@ -12651,10 +12872,10 @@ msgid "set the default tracking branch"
 msgstr "estableix la branca de seguiment per defecte"
 
 msgid "git submodule set-branch [-q|--quiet] (-d|--default) <path>"
-msgstr "git submodule set-branch [-q|--quiet] (-d|--default) <path>"
+msgstr "git submodule set-branch [-q|--quiet] (-d|--default) <camí>"
 
 msgid "git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"
-msgstr "git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"
+msgstr "git submodule set-branch [-q|--quiet] (-b|--branch) <branca> <camí>"
 
 msgid "--branch or --default required"
 msgstr "cal --branch o --default"
@@ -12716,6 +12937,9 @@ msgstr "S'està reactivant el directori de git local per al submòdul «%s»\n"
 msgid "unable to checkout submodule '%s'"
 msgstr "no s'ha pogut agafar el submòdul «%s»"
 
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr "assegureu-vos que el fitxer .gitmodules és a l'arbre de treball"
+
 #, c-format
 msgid "Failed to add submodule '%s'"
 msgstr "S'ha produït un error en afegir el submòdul «%s»"
@@ -12753,7 +12977,7 @@ msgstr ""
 "seu camí"
 
 msgid "git submodule add [<options>] [--] <repository> [<path>]"
-msgstr "git submodule add [<options>] [--] <repository> [<path>]"
+msgstr "git submodule add [<opcions>] [--] <repository> [<camí>]"
 
 msgid "Relative path can only be used from the toplevel of the working tree"
 msgstr ""
@@ -12768,19 +12992,17 @@ msgstr "URL de repositori: «%s» ha de ser absolut o començar amb ./|../"
 msgid "'%s' is not a valid submodule name"
 msgstr "«%s» no és un nom de submòdul vàlid"
 
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s no admet --super-prefix"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <command>"
 
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "«%s» no és una subordre vàlida de submodule--helper"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <reason>] <name> <ref>"
 
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<opcions>] <nom> [<referència>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
 
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <nom>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <name>"
 
 msgid "suppress error message for non-symbolic (detached) refs"
 msgstr "omet el missatge d'error de referències no simbòliques (separades)"
@@ -12791,6 +13013,9 @@ msgstr "suprimeix la referència simbòlica"
 msgid "shorten ref output"
 msgstr "escurça la sortida de referències"
 
+msgid "recursively dereference (default)"
+msgstr "desreferencia recursivament (per defecte)"
+
 msgid "reason"
 msgstr "raó"
 
@@ -12798,25 +13023,25 @@ msgid "reason of the update"
 msgstr "raó de l'actualització"
 
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <fitxer>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <fitxer>] [-e]\n"
+"        <tagname> [<comissió> | <objecte>]"
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d <nom-d'etiqueta>..."
 
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <comissió>] [--no-merged <comissió>] "
-"[<patró>...]"
+"git tag [-n[<num>]] -l [--contains <comissió>] [--no-contains <comissió>]\n"
+"        [--points-at <objecte>] [--column[=<opcions>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <comissió>] [--no-merged <comissió>] [<patró>...]"
 
 msgid "git tag -v [--format=<format>] <tagname>..."
 msgstr "git tag -v [--format=<format>] <nom-d'etiqueta>..."
@@ -13105,6 +13330,9 @@ msgstr "(per a porcellanes) oblida't dels conflictes no resolts ni desats"
 msgid "write index in this format"
 msgstr "escriu l'índex en aquest format"
 
+msgid "report on-disk index format version"
+msgstr "informa sobre la versió del format de l'índex del disc"
+
 msgid "enable or disable split index"
 msgstr "habilita o inhabilita l'índex dividit"
 
@@ -13129,6 +13357,14 @@ msgstr "marca els fitxers com a vàlids pel fsmonitor"
 msgid "clear fsmonitor valid bit"
 msgstr "esborra el bit de validesa del fsmonitor"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: era %d, s'ha establert a %d"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13206,8 +13442,12 @@ msgstr "llegeix les actualitzacions des de stdin"
 msgid "update the info files from scratch"
 msgstr "actualitza els fitxers d'informació des de zero"
 
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<opcions>] <directori>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
 
 msgid "quit after a single request/response exchange"
 msgstr "surt després d'un sol intercanvi de sol·licitud/resposta"
@@ -13222,8 +13462,8 @@ msgstr ""
 msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "interromp la transferència després de <n> segons d'inactivitat"
 
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <comissió>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | --verbose] [--raw] <comissió>..."
 
 msgid "print commit contents"
 msgstr "imprimeix els continguts de la comissió"
@@ -13231,8 +13471,8 @@ msgstr "imprimeix els continguts de la comissió"
 msgid "print raw gpg status output"
 msgstr "imprimeix la sortida crua de l'estat gpg"
 
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <paquet>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
 
 msgid "verbose"
 msgstr "detallat"
@@ -13240,35 +13480,70 @@ msgstr "detallat"
 msgid "show statistics only"
 msgstr "mostra només estadístiques"
 
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<format>] <etiqueta>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
 
 msgid "print tag contents"
 msgstr "imprimeix els continguts de l'etiqueta"
 
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<opcions>] <camí> [<commit-ish>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <cadena>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <camí> [<commit-ish>]"
 
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<opcions>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
 
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<opcions>] <camí>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <string>] <worktree>"
 
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <arbre de treball> <camí-nou>"
 
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<opcions>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <expire>]"
 
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<opcions>] <arbre de treball>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <worktree>"
 
 msgid "git worktree repair [<path>...]"
-msgstr "git worktree repair [<path>...]"
+msgstr "git worktree repair [<camí>...]"
+
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <worktree>"
+
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "No hi ha cap branca d'origen possible, inferint «--orphan»"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Si voleu crear un arbre de treball que contingui una branca orfe nova\n"
+"(branca sense comissions) per a aquest repositori, podeu fer-ho\n"
+"utilitzant l'argument --orphan:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
 
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <camí>"
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Si voleu crear un arbre de treball que contingui una branca orfe nova\n"
+"(branca sense comissions) per a aquest repositori, podeu fer-ho\n"
+"utilitzant l'argument --orphan:\n"
+"\n"
+"    git worktree add --orphan %s\n"
 
 #, c-format
 msgid "Removing %s/%s: %s"
@@ -13341,10 +13616,35 @@ msgstr ""
 msgid "Preparing worktree (checking out '%s')"
 msgstr "S'està preparant l'arbre de treball (s'està agafant «%s»)"
 
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "no accessible: referència no vàlida: %s"
+
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "S'està preparant l'arbre de treball (HEAD %s separat)"
 
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD apunta a una referència no vàlida (o òrfena).\n"
+"Camí HEAD: «%s»\n"
+"Contingut HEAD: «%s»"
+
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"No hi ha referències locals o remotes malgrat hi existeix almenys un\n"
+"remot, aturada; useu «add -f» per a anul·lar o obtenir primer un remot"
+
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "les opcions «%s» i «%s» no es poden usar juntes"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "agafa <branca> encara que sigui agafada en altre arbre de treball"
 
@@ -13354,6 +13654,9 @@ msgstr "crea una branca nova"
 msgid "create or reset a branch"
 msgstr "crea o restableix una branca"
 
+msgid "create unborn/orphaned branch"
+msgstr "crea una branca no nascuda/òrfena"
+
 msgid "populate the new working tree"
 msgstr "emplena l'arbre de treball nou"
 
@@ -13375,6 +13678,13 @@ msgstr ""
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "les opcions «%s», «%s», i «%s» no es poden usar juntes"
 
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "les opcions «%s» i «%s» no es poden usar juntes"
+
+msgid "<commit-ish>"
+msgstr "<commit-ish>"
+
 msgid "added with --lock"
 msgstr "afegit amb --lock"
 
@@ -13508,23 +13818,59 @@ msgstr "només útil per a la depuració"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "core.fsyncMethod = batch no és compatible amb aquesta plataforma"
 
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr ""
+"no s'ha pogut analitzar la clau de llista de paquets %s amb el valor «%s»"
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "la llista de farcells a «%s» no té mode"
+
 msgid "failed to create temporary file"
 msgstr "no s'ha pogut crear un fitxer temporal"
 
 msgid "insufficient capabilities"
 msgstr "capacitats insuficients"
 
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "el fitxer baixat de «%s» no és un paquet"
+
+msgid "failed to store maximum creation token"
+msgstr "no s'ha pogut emmagatzemar el testimoni de creació màxim"
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "no s'ha reconegut el model del farcell de l'URI «%s»"
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "s'ha excedit el límit de recursió URI del paquet (%d)"
+
 #, c-format
 msgid "failed to download bundle from URI '%s'"
 msgstr "no s'ha pogut baixar el paquet de l'URI «%s»"
 
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "el fitxer a l'URI «%s» no és farcell"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "el fitxer a l'URI «%s» no és farcell o una llista de farcells"
 
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "s'ha produït un error en desempaquetar el farcell de l'URI «%s»"
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: argument inesperat: «%s»"
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: s'esperava una neteja després dels arguments"
+
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: té una línia buida"
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: la línia no és de la forma «key=value»"
+
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: la línia té una clau o un valor buit"
 
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
@@ -13548,6 +13894,13 @@ msgstr "Al repositori li manquen aquestes comissions prerequerides:"
 msgid "need a repository to verify a bundle"
 msgstr "cal un repositori per a verificar un farcell"
 
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"hi ha algunes comissions requerides al magatzem d'objectes, però no estan "
+"connectades a l'historial del repositori"
+
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -13563,6 +13916,14 @@ msgid_plural "The bundle requires these %<PRIuMAX> refs:"
 msgstr[0] "El farcell requereix aquesta referència:"
 msgstr[1] "El farcell requereix aquestes %<PRIuMAX> referències:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "El farcell utilitza aquest algoritme de resum: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "El farcell utilitza aquest filtre: %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "no s'ha pogut duplicar el descriptor del farcell"
 
@@ -13598,6 +13959,10 @@ msgid "terminating chunk id appears earlier than expected"
 msgstr ""
 "l'identificador de fragment de finalització apareix abans del que s'esperava"
 
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "ID del fragment %<PRIx32> no alineat %d-byte"
+
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "desplaçament incorrecte del fragment %<PRIx64> i %<PRIx64>"
@@ -13652,10 +14017,8 @@ msgstr "Recopila la informació per a l'usuari per a enviar un informe d'error"
 msgid "Move objects and refs by archive"
 msgstr "Mou els objectes i les referències per arxiu"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Proveeix contingut o informació del tipus i mida per als objectes del "
-"repositori"
+msgid "Provide contents or details of repository objects"
+msgstr "Proporcioneu el contingut o els detalls dels objectes del repositori"
 
 msgid "Display gitattributes information"
 msgstr "Mostra la informació de .gitattributes"
@@ -13796,8 +14159,9 @@ msgstr "Imprimeix les línies coincidents amb un patró"
 msgid "A portable graphical interface to Git"
 msgstr "Una interfície gràfica portable per al Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "Calcula l'ID de l'objecte i opcionalment crea un blob des del fitxer"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr ""
+"Calcula l'ID de l'objecte i opcionalment crea un objecte des del fitxer"
 
 msgid "Display help information about Git"
 msgstr "Mostra informació d'ajuda del Git"
@@ -13882,7 +14246,7 @@ msgid "Build a tree-object from ls-tree formatted text"
 msgstr "Construeix un objecte en arbre a partir de text formatat amb ls-tree"
 
 msgid "Write and verify multi-pack-indexes"
-msgstr "Escriu i verifica els índexs dels paquets multipaquet"
+msgstr "Escriu i verifica els índexs multipaquet"
 
 msgid "Move or rename a file, a directory, or a symlink"
 msgstr "Mou o canvia de nom a un fitxer, directori o enllaç simbòlic"
@@ -14073,8 +14437,8 @@ msgstr "Verifica la signatura GPG de les etiquetes"
 msgid "Display version information about Git"
 msgstr "Mostra informació de la versió del Git"
 
-msgid "Show logs with difference each commit introduces"
-msgstr "Mostra registres amb la diferència introduïda per cada comissió"
+msgid "Show logs with differences each commit introduces"
+msgstr "Mostra els registres amb les diferències que introdueix cada comissió"
 
 msgid "Manage multiple working trees"
 msgstr "Gestiona múltiples arbres de treball"
@@ -14112,7 +14476,7 @@ msgstr "El format del fitxer de farcell"
 msgid "Chunk-based file formats"
 msgstr "Formats de fitxer basats en blocs"
 
-msgid "Git commit graph format"
+msgid "Git commit-graph format"
 msgstr "Format de graf de comissions del Git"
 
 msgid "Git index format"
@@ -14212,9 +14576,16 @@ msgstr ""
 msgid "commit-graph has no base graphs chunk"
 msgstr "el fragment del graf de comissions no té grafs de base"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "el fragment de grafs base de la gràfica de comissió és massa petit"
+
 msgid "commit-graph chain does not match"
 msgstr "la cadena del graf de comissions no coincideix"
 
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "el nombre de comissions en el graf base és massa alt: %<PRIuMAX>"
+
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr ""
@@ -14237,6 +14608,11 @@ msgstr ""
 "el graf de comissions requereix dades de generació de desbordaments però no "
 "en té cap"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr ""
+"les dades de generació de desbordament del graf de comissions són massa "
+"petites"
+
 msgid "Loading known commits in commit graph"
 msgstr "S'estan carregant comissions conegudes al graf de comissions"
 
@@ -14310,6 +14686,14 @@ msgid "failed to rename temporary commit-graph file"
 msgstr ""
 "no s'ha pogut canviar el nom del fitxer temporal del graf de comissions"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "no es poden fusionar els gràfics amb %<PRIuMAX>, %<PRIuMAX>entregues"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "no es pot fusionar el graf %s, hi ha massa comissions: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "S'estan escanejant les comissions fusionades"
 
@@ -14344,9 +14728,6 @@ msgid "failed to parse commit %s from commit-graph"
 msgstr ""
 "s'ha produït un error en analitzar la comissió %s del graf de comissions"
 
-msgid "Verifying commits in commit graph"
-msgstr "S'estan verificant les comissions al graf de comissions"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14372,20 +14753,6 @@ msgstr "el pare pel graf de comissions %s és %s != %s"
 msgid "commit-graph parent list for commit %s terminates early"
 msgstr "la llista pare del graf de comissions per %s acaba aviat"
 
-#, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"el graf de comissions té nombre de generació zero per a la comissió %s, però "
-"té no zero en altres llocs"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"el graf de comissions té un nombre de generació diferent de zero per a "
-"comissió %s però té zero en altres llocs"
-
 #, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr ""
@@ -14398,6 +14765,17 @@ msgstr ""
 "la data d'enviament per a la comissió %s en el graf de comissions és "
 "%<PRIuMAX> != %<PRIuMAX>"
 
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"El graf de comissió té tant generacions zero com no nul·les (p. ex., "
+"comissions «%s» i «%s»)"
+
+msgid "Verifying commits in commit graph"
+msgstr "S'estan verificant les comissions al graf de comissions"
+
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s no és una comissió!"
@@ -14421,6 +14799,12 @@ msgstr ""
 "Desactiveu aquest missatge executant\n"
 "«git config advice.graftFileDeprecated false»"
 
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"la comissió %s existeix al graf de comissions però no a la base de dades "
+"d'objectes"
+
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr "La comissió %s té una signatura GPG no fiable, suposadament de %s."
@@ -14485,6 +14869,10 @@ msgstr "cas no gestionat a «has_worktree_moved»: %d"
 msgid "health thread wait failed [GLE %ld]"
 msgstr "ha fallat l'espera del fil de salut [GLE %ld]"
 
+#, c-format
+msgid "Invalid path: %s"
+msgstr "Camí no vàlid: «%s»"
+
 msgid "Unable to create FSEventStream."
 msgstr "No s'ha pogut crear el FSEventStream."
 
@@ -14515,6 +14903,22 @@ msgstr "Ha fallat GetOverlappedResult a «%s» [GLE %ld]"
 msgid "could not read directory changes [GLE %ld]"
 msgstr "no s'han pogut llegir els canvis de directori [GLE %ld]"
 
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "ha fallat opendir(«%s»)"
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "ha fallat lstat(«%s»)"
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "ha fallat strbuf_readlink(«%s»)"
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "ha fallat closedir(«%s»)"
+
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
 msgstr "[GLE %ld] no s'ha pogut obrir per a llegir «%ls»"
@@ -14556,7 +14960,7 @@ msgid "Invalid back reference"
 msgstr "Referència anterior no vàlida"
 
 msgid "Unmatched [ or [^"
-msgstr "[ or [^ no emparellat"
+msgstr "[ o [^ no emparellat"
 
 msgid "Unmatched ( or \\("
 msgstr "( o \\( no emparellat"
@@ -14583,7 +14987,7 @@ msgid "Regular expression too big"
 msgstr "Expressió regular és massa gran"
 
 msgid "Unmatched ) or \\)"
-msgstr ") o \\) no no emparellat"
+msgstr ") o \\) no emparellat"
 
 msgid "No previous regular expression"
 msgstr "No hi ha expressió regular anterior"
@@ -14802,8 +15206,8 @@ msgstr "la longitud d'«abbrev» està fora de rang: %d"
 msgid "bad zlib compression level %d"
 msgstr "nivell de compressió de zlib incorrecte %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar només hauria de ser un caràcter"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar només hauria de ser un caràcter ASCII"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -14841,10 +15245,6 @@ msgstr "la referència «%s» no assenyala a un blob"
 msgid "unable to resolve config blob '%s'"
 msgstr "no s'ha pogut resoldre el blob de configuració: «%s»"
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "s'ha produït un error en analitzar %s"
-
 msgid "unable to parse command-line config"
 msgstr "no s'ha pogut analitzar la configuració de la línia d'ordres"
 
@@ -14915,6 +15315,11 @@ msgstr "no s'ha pogut establir «%s» a «%s»"
 msgid "invalid section name: %s"
 msgstr "nom de secció no vàlida: %s"
 
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"es rebutja treballar amb una línia massa llarga a «%s» a la línia %<PRIuMAX>"
+
 #, c-format
 msgid "missing value for '%s'"
 msgstr "falta el valor per «%s»"
@@ -14971,16 +15376,23 @@ msgid "unknown object format '%s' specified by server"
 msgstr "format d'objecte «%s» especificat pel servidor desconegut"
 
 #, c-format
-msgid "invalid ls-refs response: %s"
-msgstr "resposta de ls-refs no vàlida: %s"
+msgid "error on bundle-uri response line %d: %s"
+msgstr "error a la línia de resposta de bundle-uri %d: %s"
 
-msgid "expected flush after ref listing"
-msgstr "s'esperava una neteja després del llistat de referències"
+msgid "expected flush after bundle-uri listing"
+msgstr "s'esperava un buidatge després del llistat de bundle-uri"
 
 msgid "expected response end packet after ref listing"
 msgstr ""
 "s'esperava un paquet de final de resposta després del llistat de referències"
 
+#, c-format
+msgid "invalid ls-refs response: %s"
+msgstr "resposta de ls-refs no vàlida: %s"
+
+msgid "expected flush after ref listing"
+msgstr "s'esperava una neteja després del llistat de referències"
+
 #, c-format
 msgid "protocol '%s' is not supported"
 msgstr "el protocol «%s» no és compatible"
@@ -15310,9 +15722,6 @@ msgstr "s'ha produït un error en escriure arxiu"
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base no funciona amb intervals"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base només funciona amb comissions"
-
 msgid "unable to get HEAD"
 msgstr "no s'ha pogut obtenir HEAD"
 
@@ -15322,6 +15731,12 @@ msgstr "no s'ha trobat una base de fusió"
 msgid "multiple merge bases found"
 msgstr "s'han trobat múltiples bases de fusió"
 
+msgid "cannot compare stdin to a directory"
+msgstr "no es pot comparar stdin amb un directori"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "no es pot comparar una canonada amb nom amb un directori"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<opcions>] <camí> <camí>"
 
@@ -15354,7 +15769,7 @@ msgid ""
 "unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', "
 "'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"
 msgstr ""
-"el mode «%s» de «color-moved-ws» és desconegut, els valor possibles són "
+"el mode «%s» de «color-moved-ws» és desconegut, els valors possibles són "
 "«ignore-space-change», «ignore-space-at-eol», «ignore-all-space», «allow-"
 "indentation-change»"
 
@@ -15382,6 +15797,13 @@ msgstr ""
 msgid "external diff died, stopping at %s"
 msgstr "el diff external s'ha mort, s'està aturant a %s"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow requereix exactament una especificació de camí"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "el «pathspec» màgic no està suportat per --follow: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr "les opcions «%s», «%s», «%s», i «%s» no es poden usar juntes"
@@ -15396,9 +15818,6 @@ msgid ""
 msgstr ""
 "les opcions «%s» i «%s» no es poden usar juntes, useu «%s» amb «%s» i «%s»"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow requereix exactament una especificació de camí"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "valor --stat no vàlid: %s"
@@ -15591,6 +16010,9 @@ msgstr "afegir un prefix addicional per a cada línia de sortida"
 msgid "do not show any source or destination prefix"
 msgstr "no mostris cap prefix d'origen o destí"
 
+msgid "use default prefixes a/ and b/"
+msgstr "utilitza els prefixos per defecte a/ i b/"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "mostra el context entre trossos de diferència fins al nombre especificat de "
@@ -15902,6 +16324,14 @@ msgstr "no s'ha pogut migrar el directori de «%s» a «%s»"
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "consell: s'està esperant que el vostre editor tanqui el fitxer...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "no s'ha pogut escriure a «%s»"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "no s'ha pogut editar «%s»"
+
 msgid "Filtering content"
 msgstr "S'està filtrant el contingut"
 
@@ -16069,14 +16499,12 @@ msgstr "s'ha produït un error en processar els acks: %d"
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
-#.
 #, c-format
 msgid "expected packfile to be sent after '%s'"
 msgstr "s'esperava que el fitxer de paquet s'enviés després de «%s»"
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
-#.
 #, c-format
 msgid "expected no other sections to be sent after no '%s'"
 msgstr "no s'esperava que cap altra secció s'enviés després de «%s»"
@@ -16147,10 +16575,11 @@ msgstr "el repositori virtual «%s» és incompatible amb fsmonitor"
 
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
-"el repositori «%s» és incompatible amb fsmonitor a causa de la manca de "
-"sòcols Unix"
+"el directori del sòcol «%s» és incompatible amb fsmonitor a causa de la "
+"manca de compatibilitat amb els sòcols Unix"
 
 msgid ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
@@ -16158,16 +16587,14 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
 "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 
 msgid ""
 "'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -16192,10 +16619,6 @@ msgstr "no s'ha especificat un directori per a l'opció «%s»\n"
 msgid "no namespace given for --namespace\n"
 msgstr "no s'ha especificat un nom d'espai per --namespace\n"
 
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "no s'ha especificat cap prefix per a --super-prefix\n"
-
 #, c-format
 msgid "-c expects a configuration string\n"
 msgstr "-c espera una cadena de configuració\n"
@@ -16204,6 +16627,10 @@ msgstr "-c espera una cadena de configuració\n"
 msgid "no config key given for --config-env\n"
 msgstr "no s'ha indicat cap clau de configuració per a --config-env\n"
 
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "no s'ha donat d'atribut font per a --attr-source\n"
+
 #, c-format
 msgid "unknown option: %s\n"
 msgstr "opció desconeguda: %s\n"
@@ -16308,8 +16735,13 @@ msgstr ""
 msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "gpg.ssh.defaultKeyCommand ha fallat: %s %s"
 
-msgid "gpg failed to sign the data"
-msgstr "gpg ha fallat en signar les dades"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg ha fallat en signar les dades:\n"
+"%s"
 
 msgid "user.signingKey needs to be set for ssh signing"
 msgstr "user.signingKey s'ha d'establir per a signar amb ssh"
@@ -16471,8 +16903,8 @@ msgstr[1] ""
 "\n"
 "Les ordres més similars són"
 
-msgid "git version [<options>]"
-msgstr "git version [<opcions>]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #, c-format
 msgid "%s: %s - %s"
@@ -16608,7 +17040,7 @@ msgstr "sparse: s'ha eliminat la implementació de filtres de camí sparse"
 
 #, c-format
 msgid "'%s' for 'object:type=<type>' is not a valid object type"
-msgstr "«%s» per a «object:type=<type>» no és un tipus d'objecte vàlid"
+msgstr "«%s» per a «object:type=<tipus>» no és un tipus d'objecte vàlid"
 
 #, c-format
 msgid "invalid filter-spec '%s'"
@@ -16667,7 +17099,7 @@ msgid ""
 msgstr ""
 "No s'ha pogut crear «%s.lock»: %s.\n"
 "\n"
-"Sembla que un altre procés de git s'està executant en aquest\n"
+"Sembla que un altre procés git s'està executant en aquest\n"
 "repositori, per exemple, un editor obert per «git commit». \n"
 "Assegureu-vos que tots els processos s'hagin acabat i\n"
 "llavors proveu de nou. Si encara falla, pot ser un procés git\n"
@@ -16730,12 +17162,12 @@ msgstr ""
 "solucions possibles:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "S'ha produït un error en executar la fusió interna"
+msgid "failed to execute internal merge"
+msgstr "no s'ha pogut executar la fusió interna"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "No s'ha pogut afegir %s a la base de dades"
+msgid "unable to add %s to database"
+msgstr "no s'ha pogut afegir %s a la base de dades"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -16890,7 +17322,6 @@ msgstr ""
 #. name, and the second argument is the abbreviated id of the
 #. commit that needs to be merged.  For example:
 #.  - go to submodule (mysubmodule), and either merge commit abc1234"
-#.
 #, c-format
 msgid ""
 " - go to submodule (%s), and either merge commit %s\n"
@@ -16925,7 +17356,6 @@ msgstr ""
 
 #. TRANSLATORS: The %s arguments are: 1) tree hash of a merge
 #. base, and 2-3) the trees for the two trees we're merging.
-#.
 #, c-format
 msgid "collecting merge info failed for trees %s, %s, %s"
 msgstr ""
@@ -17184,7 +17614,15 @@ msgid "failed to read the cache"
 msgstr "s'ha produït un error en llegir la memòria cau"
 
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "l'OID de l'índex multipaquet és d'una mida incorrecta"
+msgstr "l'OID «fanout» de l'índex multipaquet és d'una mida incorrecta"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "El fragment de cerca OID índex multipaquet és de mida incorrecta"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr ""
+"el fragment de desplaçament de l'objecte índex multipaquet és d'una mida "
+"incorrecta"
 
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -17205,17 +17643,28 @@ msgid "multi-pack-index hash version %u does not match version %u"
 msgstr ""
 "la versió del resum índex multipaquet %u no coincideix amb la versió %u"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "falta un fragment de nom de paquet necessari al multi-index"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment del nom de paquet requerit de l'índex "
+"multipaquet"
+
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment del «fanout» OID requerit a l'índex "
+"multipaquet"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "falta un fragment «fanout» OID necessari al multi-pack-index"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment de cerca d'OID necessari a l'índex "
+"multipaquet"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "falta un fragment de cerca «fanout» OID necessari al multi-pack-index"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment de l'índex multipaquet dels objectes "
+"requerits"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "falta un fragment necessari dels desplaçaments al multi-pack-index"
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "el fragment de nom de l'índex multipaquet és massa curt"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17232,6 +17681,9 @@ msgstr ""
 "l'índex multipaquet emmagatzema un desplaçament de 64 bits, però off_t és "
 "massa petit"
 
+msgid "multi-pack-index large offset out of bounds"
+msgstr "desplaçament gran de l'índex multipaquet està fora dels límits"
+
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "no s'ha pogut afegir el fitxer de paquet «%s»"
@@ -17399,7 +17851,6 @@ msgstr "S'està refusant reescriure les notes en %s (fora de refs/notes/)"
 #. TRANSLATORS: The first %s is the name of
 #. the environment variable, the second %s is
 #. its value.
-#.
 #, c-format
 msgid "Bad %s value: '%s'"
 msgstr "Valor erroni de %s: «%s»"
@@ -17419,10 +17870,6 @@ msgstr ""
 "%s: s'estan ignorant els emmagatzematges alternatius d'objectes, imbricació "
 "massa profunda"
 
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "no s'ha pogut normalitzar el directori de l'objecte: %s"
-
 msgid "unable to fdopen alternates lockfile"
 msgstr "no s'ha pogut fer «fdopen» al fitxer de bloqueig alternatiu"
 
@@ -17482,6 +17929,10 @@ msgstr "objecte solt corrupte «%s»"
 msgid "garbage at end of loose object '%s'"
 msgstr "brossa al final de l'objecte solt «%s»"
 
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "no s'ha pogut obrir l'objecte solt %s"
+
 #, c-format
 msgid "unable to parse %s header"
 msgstr "no s'ha pogut analitzar la capçalera %s"
@@ -17498,17 +17949,13 @@ msgid "header for %s too long, exceeds %d bytes"
 msgstr "la capçalera per a %s és massa llarga, supera els %d bytes"
 
 #, c-format
-msgid "failed to read object %s"
-msgstr "s'ha produït un error en llegir l'objecte %s"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "l'objecte solt %s (emmagatzemat a %s) és corrupte"
 
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "no s'ha trobat el reemplaçament %s per a %s"
 
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "l'objecte solt %s (emmagatzemat a %s) és corrupte"
-
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr "l'objecte empaquetat %s (emmagatzemat a %s) és corrupte"
@@ -17521,9 +17968,6 @@ msgstr "no s'ha pogut escriure al fitxer %s"
 msgid "unable to set permission to '%s'"
 msgstr "no s'ha pogut establir el permís a «%s»"
 
-msgid "file write error"
-msgstr "s'ha produït un error en escriure al fitxer"
-
 msgid "error when closing loose object file"
 msgstr "error en tancar el fitxer d'objecte solt"
 
@@ -17571,11 +18015,12 @@ msgstr "s'ha produït un error en crear el directori %s"
 msgid "cannot read object for %s"
 msgstr "no es pot llegir l'objecte per a %s"
 
-msgid "corrupt commit"
-msgstr "comissió corrupta"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "l'objecte ha fallat fsck: %s"
 
-msgid "corrupt tag"
-msgstr "etiqueta corrupta"
+msgid "refusing to create malformed object"
+msgstr "es rebutja crear un objecte mal format"
 
 #, c-format
 msgid "read error while indexing %s"
@@ -17624,7 +18069,6 @@ msgstr "no s'han pogut desempaquetar els continguts de %s"
 #. TRANSLATORS: This is a line of ambiguous object
 #. output shown when we cannot look up or parse the
 #. object in question. E.g. "deadbeef [bad object]".
-#.
 #, c-format
 msgid "%s [bad object]"
 msgstr "%s [objecte incorrecte]"
@@ -17633,7 +18077,6 @@ msgstr "%s [objecte incorrecte]"
 #. object output. E.g.:
 #. *
 #.    "deadbeef commit 2021-01-01 - Some Commit Message"
-#.
 #, c-format
 msgid "%s commit %s - %s"
 msgstr "%s comissió %s - %s"
@@ -17648,7 +18091,6 @@ msgstr "%s comissió %s - %s"
 #. *
 #. The third argument is the "tag" string
 #. from object.c.
-#.
 #, c-format
 msgid "%s tag %s - %s"
 msgstr "%s etiqueta %s - %s"
@@ -17658,21 +18100,18 @@ msgstr "%s etiqueta %s - %s"
 #. the tag itself. E.g.:
 #. *
 #.    "deadbeef [bad tag, could not parse it]"
-#.
 #, c-format
 msgid "%s [bad tag, could not parse it]"
 msgstr "%s [etiqueta malmesa, no s'ha pogut analitzar]"
 
 #. TRANSLATORS: This is a line of ambiguous <type>
 #. object output. E.g. "deadbeef tree".
-#.
 #, c-format
 msgid "%s tree"
 msgstr "arbre %s"
 
 #. TRANSLATORS: This is a line of ambiguous <type>
 #. object output. E.g. "deadbeef blob".
-#.
 #, c-format
 msgid "%s blob"
 msgstr "blob %s"
@@ -17684,7 +18123,6 @@ msgstr "l'id d'objecte curt %s és ambigu"
 #. TRANSLATORS: The argument is the list of ambiguous
 #. objects composed in show_ambiguous_object(). See
 #. its "TRANSLATORS" comments for details.
-#.
 #, c-format
 msgid ""
 "The candidates are:\n"
@@ -17770,7 +18208,7 @@ msgstr ""
 
 #, c-format
 msgid "<object>:<path> required, only <object> '%s' given"
-msgstr "<object>:<path> requerit, només s'ha donat <object> «%s»"
+msgstr "<objecte>:<camí> requerit, només s'ha donat <objecte> «%s»"
 
 #, c-format
 msgid "invalid object name '%.*s'."
@@ -17797,7 +18235,7 @@ msgid "hash mismatch %s"
 msgstr "el resum no coincideix %s"
 
 msgid "trying to write commit not in index"
-msgstr "s'està intentant no escriure la publicació a l'índex"
+msgstr "s'està intentant no escriure la comissió a l'índex"
 
 msgid "failed to load bitmap index (corrupted?)"
 msgstr ""
@@ -17844,10 +18282,6 @@ msgstr "el desplaçament XOR a l'índex de mapa de bits no és vàlid"
 msgid "cannot fstat bitmap file"
 msgstr "no es pot fer fstat en el fitxer de mapa de bits"
 
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "s'ignorarà el fitxer extra de mapa de bits: «%s»"
-
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "la suma de verificació no coincideix amb el MIDX i el mapa de bits"
 
@@ -17920,6 +18354,10 @@ msgstr "no s'ha pogut trobar «%s» al paquet «%s» al desplaçament %<PRIuMAX>
 msgid "unable to get disk usage of '%s'"
 msgstr "no s'ha pogut obtenir l'ús del disc de «%s»"
 
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "el fitxer de mapa de bits «%s» té una suma de verificació no vàlida"
+
 #, c-format
 msgid "mtimes file %s is too small"
 msgstr "el fitxer mtimes %s és massa petit"
@@ -17960,6 +18398,18 @@ msgstr "el fitxer d'índex invers %s té la versió %<PRIu32> no admesa"
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "el fitxer d'índex invers %s té un ID de resum %<PRIu32> no admès"
 
+msgid "invalid checksum"
+msgstr "suma de verificació no vàlida"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr ""
+"posició no vàlida de l'índex de reversió a %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr ""
+"el fragment de l'index invers de l'índex multipaquet és de mida incorrecta"
+
 msgid "cannot both write and verify reverse index"
 msgstr "no es pot escriure i verificar l'índex invers"
 
@@ -18012,14 +18462,6 @@ msgstr "l'opció «%s» espera «%s» o «%s»"
 msgid "%s requires a value"
 msgstr "%s requereix un valor"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s és incompatible amb %s"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s: és incompatible amb alguna altra cosa"
-
 #, c-format
 msgid "%s takes no value"
 msgstr "%s no accepta cap valor"
@@ -18032,6 +18474,10 @@ msgstr "%s no és disponible"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s espera un valor enter no negatiu amb un sufix opcional k/m/g"
 
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s és incompatible amb %s"
+
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "opció ambigua: %s (pot ser --%s%s o --%s%s)"
@@ -18068,7 +18514,6 @@ msgstr "ús: %s"
 
 #. TRANSLATORS: the colon here should align with the
 #. one in "usage: %s" translation.
-#.
 #, c-format
 msgid "   or: %s"
 msgstr "   o: %s"
@@ -18091,7 +18536,6 @@ msgstr "   o: %s"
 #. function. The "%s" is a line in the (hopefully already
 #. translated) N_() usage string, which contained embedded
 #. newlines before we split it up.
-#.
 #, c-format
 msgid "%*s%s"
 msgstr "%*s%s"
@@ -18103,6 +18547,10 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-NUM"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "oposat a --no-%s"
+
 msgid "expiry-date"
 msgstr "data-de-caducitat"
 
@@ -18118,6 +18566,9 @@ msgstr "sigues més discret"
 msgid "use <n> digits to display object names"
 msgstr "usa <n> xifres per a mostrar els noms d'objecte"
 
+msgid "prefixed path to initial superproject"
+msgstr "camí prefixat al superprojecte inicial"
+
 msgid "how to strip spaces and #comments from message"
 msgstr "com suprimir els espais i #comentaris del missatge"
 
@@ -18130,6 +18581,14 @@ msgstr ""
 "amb --pathspec-from-file els elements d'especificació del camí estan "
 "separats amb el caràcter NUL"
 
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "el valor «%s» booleà de l'entorn és incorrecte per a «%s»"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "s'ha produït un error en analitzar %s"
+
 #, c-format
 msgid "Could not make %s writable by group"
 msgstr "No s'ha pogut fer %s escrivible pel grup"
@@ -18180,6 +18639,10 @@ msgstr "Màgia d'especificació de camí no implementada «%c» en «%s»"
 msgid "%s: 'literal' and 'glob' are incompatible"
 msgstr "%s: «literal» i «glob» són incompatibles"
 
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "«%s» és fora de l'arbre de directoris"
+
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: «%s» està fora del repositori en «%s»"
@@ -18269,6 +18732,10 @@ msgstr "promisor-remote: no s'ha pogut tancar stdin al subprocés d'obtenció"
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr "el nom remot «promisor» no pot començar amb «/»: %s"
 
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "no s'ha pogut obtenir «%s» del «promisor» remot"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info: s'esperava una neteja després dels arguments"
 
@@ -18304,6 +18771,13 @@ msgstr "s'ha produït un error en generar el diff"
 msgid "could not parse log for '%s'"
 msgstr "no s'ha pogut llegir el fitxer de registre per a «%s»"
 
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "punta extra extra no vàlida: «%s»"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "no s'han pogut enumerar els objectes recents addicionals"
+
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr "no s'afegirà l'àlies «%s»: («%s» ja existeix en l'índex)"
@@ -18435,10 +18909,6 @@ msgstr "no es pot escriure l'índex dividit per a un índex dispers"
 msgid "failed to convert to a sparse-index"
 msgstr "s'ha produït un error en convertir a un índex dispers"
 
-#, c-format
-msgid "could not stat '%s'"
-msgstr "no s'ha pogut fer stat a «%s»"
-
 #, c-format
 msgid "unable to open git dir: %s"
 msgstr "no s'ha pogut obrir el directori git: %s"
@@ -18455,6 +18925,14 @@ msgstr "no s'han pogut corregir els bits de permisos en «%s»"
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: no es pot baixar fins al «stage» #0"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "estat de diff inesperat %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "elimina «%s»\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -18571,7 +19049,7 @@ msgid ""
 "\n"
 msgstr ""
 "\n"
-"No obstant, si elimineu tot, s'avortarà el «rebase».\n"
+"No obstant això, si elimineu tot, s'avortarà el «rebase».\n"
 "\n"
 
 #, c-format
@@ -18622,6 +19100,14 @@ msgstr "darrere per %d"
 msgid "ahead %d, behind %d"
 msgstr "davant per %d, darrere per %d"
 
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) no accepta arguments"
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "argument %%(%.*s) desconegut: %s"
+
 #, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "format esperat: %%(color:<color>)"
@@ -18639,41 +19125,37 @@ msgid "Integer value expected refname:rstrip=%s"
 msgstr "Valor enter esperat pel nom de referència:rstrip=%s"
 
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "argument %%(%s) desconegut: %s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "s'esperava %%(trailers:key=<value>)"
 
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) no accepta arguments"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "argument %%(trailers) desconegut: %s"
 
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) no accepta arguments"
+msgid "positive value expected contents:lines=%s"
+msgstr "valor positiu esperat conté:lines=%s"
 
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) no accepta arguments"
+msgid "argument expected for %s"
+msgstr "s'esperava un argument per a %s"
 
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "s'esperava %%(trailers:key=<value>)"
+msgid "positive value expected %s=%s"
+msgstr "valor positiu esperat %s=%s"
 
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "argument %%(trailers) desconegut: %s"
+msgid "cannot fully parse %s=%s"
+msgstr "no es pot analitzar completament %s=%s"
 
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "valor positiu esperat conté:lines=%s"
+msgid "value expected %s="
+msgstr "s'esperava un valor %s="
 
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "valor positiu esperat «%s» a %%(%s)"
 
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "opció del correu electrònic no reconeguda: «%s»"
-
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
 msgstr "format esperat: %%(align:<amplada>,<posició>)"
@@ -18686,13 +19168,17 @@ msgstr "posició no reconeguda:%s"
 msgid "unrecognized width:%s"
 msgstr "amplada no reconeguda:%s"
 
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "argument %%(%s) desconegut: %s"
+
 #, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "amplada positiva esperada amb l'àtom %%(align)"
 
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) no accepta arguments"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "format esperat: %%(ahead-behind:<committish>)"
 
 #, c-format
 msgid "malformed field name: %.*s"
@@ -18741,6 +19227,9 @@ msgstr "aquesta ordre rebutja l'àtom %%(%.*s)"
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "no es pot usar --format=%.*s amb --python, --shell, --tcl"
 
+msgid "failed to run 'describe'"
+msgstr "no s'ha pogut executar «describe»"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(sense branca, s'està fent «rebase» %s)"
@@ -18802,6 +19291,9 @@ msgstr "clau"
 msgid "field name to sort on"
 msgstr "nom del camp en el qual ordenar"
 
+msgid "exclude refs which match pattern"
+msgstr "exclou refs que coincideixin amb el patró"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "no és un registre de referència: %s"
@@ -19005,7 +19497,7 @@ msgstr "El transport http no admet %s"
 
 msgid "protocol error: expected '<url> <path>', missing space"
 msgstr ""
-"s'ha produït un error de protocol: s'esperava «<url> <path>», falta espai"
+"s'ha produït un error de protocol: s'esperava «<url> <camí>», falta espai"
 
 #, c-format
 msgid "failed to download file at URL '%s'"
@@ -19078,7 +19570,6 @@ msgstr ""
 #. TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
 #. <remote> <src>:<dst>" push, and "being pushed ('%s')" is
 #. the <src>.
-#.
 #, c-format
 msgid ""
 "The destination you provided is not a full refname (i.e.,\n"
@@ -19255,8 +19746,10 @@ msgstr[1] ""
 "La vostra branca i «%s» han divergit,\n"
 "i tenen %d i %d comissions distintes cada una, respectivament.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (useu «git pull» per a fusionar la branca remota amb la vostra)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr ""
+"  (utilitzeu «git pull» si voleu integrar la branca remota amb la vostra)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
@@ -19328,10 +19821,6 @@ msgstr "ha fallat en actualitzar l'estat en conflicte a «%s»"
 msgid "no remembered resolution for '%s'"
 msgstr "no hi ha cap resolució recordada per a «%s»"
 
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "no es pot fer «unlink» de «%s»"
-
 #, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Imatge prèvia actualitzada per a «%s»"
@@ -19354,6 +19843,13 @@ msgstr "no s'ha pogut determinar la revisió de HEAD"
 msgid "failed to find tree of %s"
 msgstr "s'ha produït un error en cercar l'arbre de %s"
 
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "secció d'índex no compatible per a les referències ocultes: %s"
+
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= passat més d'una vegada"
+
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
 msgstr "resolve-undo indica «%s» que manquen"
@@ -19365,6 +19861,10 @@ msgstr "no s'ha pogut obtenir la comissió per a l'argument d'ancestry-path %s"
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<packfile> ja no s'admet"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "opció no vàlida: «%s» en mode --stdin"
+
 msgid "your current branch appears to be broken"
 msgstr "la vostra branca actual sembla malmesa"
 
@@ -19451,8 +19951,15 @@ msgstr "quan es clona, crear un directori de treball complet"
 msgid "only download metadata for the branch that will be checked out"
 msgstr "només baixa les metadades per a la branca que s'agafarà"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<opcions>] [--] <repo> [<dir>]"
+msgid "create repository within 'src' directory"
+msgstr "crea un repositori dins del directori «src»"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19499,8 +20006,32 @@ msgid "--all or <enlistment>, but not both"
 msgstr "--all o <enlistment>, però no ambdós"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "no existeix un repositori de git a: «%s»"
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "no s'ha pogut suprimir el scalar.repo «%s» estancat"
+
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "s'ha eliminat l'scalar.repo estancat «%s»"
+
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "el dipòsit a «%s» té un propietari diferent"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "el dipòsit a «%s» té un problema de format"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "no s'ha trobat el dipòsit a «%s»"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"per a desregistrar aquest dipòsit de l'escalar, executeu\n"
+"\tgit config --global --unset --fixed-value scalar.repo «%s»"
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -19546,7 +20077,7 @@ msgid ""
 "\n"
 "Commands:\n"
 msgstr ""
-"scalar [-C <directory>] [-c <key>=<value>] <command> [<options>]\n"
+"scalar [-C <directory>] [-c <key>=<value>] <command> [<opcions>]\n"
 "\n"
 "Ordres:\n"
 
@@ -19655,10 +20186,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "no s'ha pogut bloquejar «%s»"
 
-#, c-format
-msgid "could not write to '%s'"
-msgstr "no s'ha pogut escriure a «%s»"
-
 #, c-format
 msgid "could not write eol to '%s'"
 msgstr "no s'ha pogut escriure el terminador de línia a «%s»"
@@ -19676,7 +20203,6 @@ msgstr "cometeu els vostres canvis o feu un «stash» per a procedir."
 
 #. TRANSLATORS: %s will be "revert", "cherry-pick" or
 #. "rebase".
-#.
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr "%s: No s'ha pogut escriure un fitxer d'índex nou"
@@ -19853,7 +20379,7 @@ msgid "unknown command: %d"
 msgstr "ordre desconeguda: %d"
 
 msgid "This is the 1st commit message:"
-msgstr "Aquest és el missatge de la 1ra comissió:"
+msgstr "Aquest és el missatge de la 1a comissió:"
 
 #, c-format
 msgid "This is the commit message #%d:"
@@ -19911,10 +20437,6 @@ msgstr "no es pot obtenir el missatge de comissió de %s"
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s: no es pot analitzar la comissió pare %s"
 
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "no s'ha pogut canviar el nom «%s» a «%s»"
-
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "no s'ha pogut revertir %s... %s"
@@ -19935,6 +20457,23 @@ msgstr "git %s: s'ha produït un error en llegir l'índex"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: s'ha produït un error en actualitzar l'índex"
 
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "«%s» no és una etiqueta vàlida"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "«%s» no és un nom de referència vàlid"
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr ""
+"«update-ref» requereix un refname plenament qualificat, p. ex. refs/heads/%s"
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "ordre no vàlida «%.*s»"
+
 #, c-format
 msgid "%s does not accept arguments: '%s'"
 msgstr "%s no accepta arguments: «%s»"
@@ -20005,9 +20544,6 @@ msgstr "intenteu «git cherry-pick (--continue | %s--abort | --quit)»"
 msgid "could not create sequencer directory '%s'"
 msgstr "no s'ha pogut crear el directori de seqüenciador «%s»"
 
-msgid "could not lock HEAD"
-msgstr "no s'ha pogut bloquejar HEAD"
-
 msgid "no cherry-pick or revert in progress"
 msgstr "ni hi ha cap «cherry pick» ni cap reversió en curs"
 
@@ -20102,13 +20638,13 @@ msgstr ""
 " git rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "i ha fet canvis a l'índex i/o l'arbre de treball\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "i ha fet canvis a l'índex i/o a l'arbre de treball.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
@@ -20118,22 +20654,23 @@ msgstr ""
 "però ha deixat canvis a l'índex i/o l'arbre de treball\n"
 "Cometeu o feu «stash» dels vostres canvis, i llavors executeu\n"
 "\n"
-" git rebase --continue\n"
+"  git rebase --continue\n"
+"\n"
 
 #, c-format
 msgid "illegal label name: '%.*s'"
 msgstr "nom d'etiqueta no permès: «%.*s»"
 
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "no s'ha pogut resoldre «%s»"
+
 msgid "writing fake root commit"
 msgstr "s'està escrivint una comissió arrel falsa"
 
 msgid "writing squash-onto"
 msgstr "s'està escrivint «squash-onto»"
 
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "no s'ha pogut resoldre «%s»"
-
 msgid "cannot merge without a current revision"
 msgstr "no es pot fusionar sense una revisió actual"
 
@@ -20256,14 +20793,14 @@ msgstr ""
 "    git rebase --edit-todo\n"
 "    git rebase --continue\n"
 
-#, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "S'està fent «rebase» (%d/%d)%s"
-
 #, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Aturat a %s...  %.*s\n"
 
+#, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "S'està fent «rebase» (%d/%d)%s"
+
 #, c-format
 msgid "unknown command %d"
 msgstr "ordre %d desconeguda"
@@ -20449,60 +20986,131 @@ msgid "cannot come back to cwd"
 msgstr "no es pot tornar al directori de treball actual"
 
 #, c-format
-msgid "failed to stat '%*s%s%s'"
-msgstr "s'ha produït un error en fer stat a «%*s%s%s»"
+msgid "failed to stat '%*s%s%s'"
+msgstr "s'ha produït un error en fer stat a «%*s%s%s»"
+
+msgid "Unable to read current working directory"
+msgstr "No s'ha pogut llegir el directori de treball actual"
+
+#, c-format
+msgid "cannot change to '%s'"
+msgstr "no es pot canviar a «%s»"
+
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "no és un repositori de git (ni cap dels directoris pares): %s"
+
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"no és un repositori de git (ni cap pare fins al punt de muntatge %s)\n"
+"S'atura a la frontera de sistema de fitxers (GIT_DISCOVERY_ACROSS_FILESYSTEM "
+"no està establert)."
+
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"s'ha detectat una propietat dubtosa al repositori a «%s»\n"
+"%sPer a afegir una excepció per a aquest directori, executeu:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr "no es pot utilitzar el dipòsit nu «%s» (safe.bareRepository és «%s»)"
+
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"hi ha un problema amb el valor de mode de fitxer core.sharedRepository "
+"(0%.3o).\n"
+"El propietari dels fitxers sempre ha de tenir permisos de lectura i "
+"escriptura."
+
+msgid "fork failed"
+msgstr "el «fork» ha fallat"
+
+msgid "setsid failed"
+msgstr "«setsid» ha fallat"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "no es pot fer stat en la plantilla «%s»"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "no es pot fer opendir en el directori «%s»"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "no es pot fer readlink en «%s»"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "no es pot fer symlink en «%s» «%s»"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "no es pot copiar «%s» a «%s»"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "s'està ignorant la plantilla %s"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "plantilles no trobades a %s"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "no s'estan copiant plantilles de «%s»: %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "nom de branca inicial no vàlid: «%s»"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "no s'ha pogut gestionar el tipus de fitxer %d"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "no s'ha pogut moure %s a %s"
 
-msgid "Unable to read current working directory"
-msgstr "No s'ha pogut llegir el directori de treball actual"
+msgid "attempt to reinitialize repository with different hash"
+msgstr "s'ha intentat reinicialitzar el repositori amb un resum diferent"
 
 #, c-format
-msgid "cannot change to '%s'"
-msgstr "no es pot canviar a «%s»"
+msgid "%s already exists"
+msgstr "%s ja existeix"
 
 #, c-format
-msgid "not a git repository (or any of the parent directories): %s"
-msgstr "no és un repositori de git (ni cap dels directoris pares): %s"
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "reinicialització: s'ha ignorat --initial-branch=%s"
 
 #, c-format
-msgid ""
-"not a git repository (or any parent up to mount point %s)\n"
-"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
-msgstr ""
-"no és un repositori de git (ni cap pare fins al punt de muntatge %s)\n"
-"S'atura a la frontera de sistema de fitxers (GIT_DISCOVERY_ACROSS_FILESYSTEM "
-"no està establert)."
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "S'ha reinicialitzat el repositori compartit existent del Git en %s%s\n"
 
 #, c-format
-msgid ""
-"detected dubious ownership in repository at '%s'\n"
-"%sTo add an exception for this directory, call:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
-msgstr ""
-"s'ha detectat una propietat dubtosa al repositori a «%s»\n"
-"%sPer a afegir una excepció per a aquest directori, executeu:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "S'ha reinicialitzat el repositori existent del Git en %s%s\n"
 
 #, c-format
-msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr "no es pot utilitzar el dipòsit nu «%s» (safe.bareRepository és «%s»)"
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "S'ha inicialitzat un repositori compartit buit del Git en %s%s\n"
 
 #, c-format
-msgid ""
-"problem with core.sharedRepository filemode value (0%.3o).\n"
-"The owner of files must always have read and write permissions."
-msgstr ""
-"hi ha un problema amb el valor de mode de fitxer core.sharedRepository "
-"(0%.3o).\n"
-"El propietari dels fitxers sempre ha de tenir permisos de lectura i "
-"escriptura."
-
-msgid "fork failed"
-msgstr "el «fork» ha fallat"
-
-msgid "setsid failed"
-msgstr "«setsid» ha fallat"
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "S'ha inicialitzat un repositori buit del Git en %s%s\n"
 
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -20555,10 +21163,6 @@ msgid_plural "%u bytes/s"
 msgstr[0] "%u byte/s"
 msgstr[1] "%u bytes/s"
 
-#, c-format
-msgid "could not edit '%s'"
-msgstr "no s'ha pogut editar «%s»"
-
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "s'està ignorant el nom de submòdul sospitós %s"
@@ -20741,6 +21345,26 @@ msgstr "ls-tree ha retornat un codi de retorn %d no esperat"
 msgid "failed to lstat '%s'"
 msgstr "s'ha produït un error en fer lstat a «%s»"
 
+msgid "no remote configured to get bundle URIs from"
+msgstr "no hi ha cap remot configurat per a obtenir els URI del paquet"
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "el remot «%s» no té cap URL configurat"
+
+msgid "could not get the bundle-uri list"
+msgstr "no s'ha pogut obtenir la llista bundle-uri"
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <opcions> (control|prime|update)"
+
+msgid "clear the cache tree before each iteration"
+msgstr "neteja l'arbre de la memòria cau abans de cada iteració"
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr ""
+"nombre d'entrades a l'arbre de la memòria cau a invalidar (per defecte 0)"
+
 msgid "unhandled options"
 msgstr "opcions no gestionades"
 
@@ -21083,6 +21707,13 @@ msgstr "S'està avortant."
 msgid "failed to push all needed submodules"
 msgstr "no s'han pogut pujar tots els submòduls necessaris"
 
+msgid "bundle-uri operation not supported by protocol"
+msgstr "L'operació bundle-uri no és compatible amb el protocol"
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr ""
+"no s'ha pogut recuperar la llista de paquets d'URI anunciats pel servidor"
+
 msgid "too-short tree object"
 msgstr "objecte d'arbre massa curt"
 
@@ -21480,6 +22111,9 @@ msgstr "no s'ha pogut accedir a «%s»"
 msgid "unable to get current working directory"
 msgstr "no s'ha pogut obtenir el directori de treball actual"
 
+msgid "unable to get random bytes"
+msgstr "no s'han pogut obtenir bytes aleatoris"
+
 msgid "Unmerged paths:"
 msgstr "Camins sense fusionar:"
 
@@ -21520,7 +22154,7 @@ msgstr "  (useu «git add/rm <fitxer>...» per a actualitzar què es cometrà)"
 msgid ""
 "  (use \"git restore <file>...\" to discard changes in working directory)"
 msgstr ""
-"  (useu «git restore <file>...» per a descartar canvis en el directori de "
+"  (useu «git restore <fitxer>...» per a descartar canvis en el directori de "
 "treball)"
 
 msgid "  (commit or discard the untracked or modified content in submodules)"
@@ -21830,14 +22464,20 @@ msgstr "Fitxers ignorats"
 
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
+msgstr ""
+"S'han trigat %.2f segons a enumerar els fitxers sense seguiment,\n"
+"però els resultats es van emmagatzemar a la memòria cau, i les\n"
+"execucions posteriors podrien ser més ràpides."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "S'han trigat %.2f segons a enumerar els fitxers sense seguiment."
+
+msgid "See 'git help status' for information on how to improve this."
 msgstr ""
-"S'ha trigat %.2f segons enumerar els fitxers no seguits.\n"
-"«status -uno» pot accelerar-ho, però heu d'anar amb compte de no\n"
-"oblidar-vos d'afegir fitxers nous vosaltres mateixos (vegeu\n"
-"«git help status»)."
+"Vegeu «git help status» per a obtenir informació sobre com millorar-ho."
 
 #, c-format
 msgid "Untracked files not listed%s"
@@ -21983,276 +22623,6 @@ msgstr ""
 msgid "Unable to determine absolute path of git directory"
 msgstr "No s'ha pogut determinar el camí absolut del directori de git"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %12s %s"
-
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "modificat %d camí\n"
-msgstr[1] "modificat %d camins\n"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-"Si el pedaç s'aplica correctament, el tros editat es marcarà immediatament\n"
-"per «staging»."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-"Si el pedaç s'aplica correctament, el tros editat es marcarà immediatament\n"
-"per «stashing»."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-"Si el pedaç s'aplica correctament, el tros editat es marcarà immediatament\n"
-"per «unstaging»."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-"Si el pedaç s'aplica correctament, el tros editat es marcarà immediatament\n"
-"per a aplicar-se."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-"Si el pedaç s'aplica correctament, el tros editat es marcarà immediatament\n"
-"per a descartar-se."
-
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "s'ha produït un error en escriure al fitxer d'edició del tros: %s"
-
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"Per a eliminar les línies «%s», convertiu-les en línies ' ' (context).\n"
-"Per a eliminar les línies «%s», suprimiu-les.\n"
-"Les línies que comencin per %s s'eliminaran.\n"
-
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "s'ha produït un error en llegir al fitxer d'edició del tros: %s"
-
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - fes «stage» d'aquest tros\n"
-"n - no facis «stage» d'aquest tros\n"
-"q - surt; no facis «stage» d'aquest tros o de cap altre restant\n"
-"a - fes «stage» d'aquest tros i tota la resta de trossos del fitxer\n"
-"d - no facis «stage» d'aquest tros o de cap altre restant del fitxer"
-
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - fes «stash» d'aquest tros\n"
-"n - no facis «stash» d'aquest tros\n"
-"q - surt; no facis «stash» d'aquest tros o de cap altre restant\n"
-"a - fes «stash» d'aquest tros i tota la resta de trossos del fitxer\n"
-"d - no facis «stash» d'aquest tros o de cap altre restant del fitxer"
-
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - fes «unstage» d'aquest tros\n"
-"n - no facis «unstage» d'aquest tros\n"
-"q - surt; no facis «unstage» d'aquest tros o de cap altre restant\n"
-"a - fes «unstage» d'aquest tros i tota la resta de trossos del fitxer\n"
-"d - no facis «unstage» d'aquest tros o de cap altre restant del fitxer"
-
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - aplica aquest tros a l'índex\n"
-"n - no apliquis aquest tros a l'índex\n"
-"q - surt; no apliquis aquest tros ni cap dels pendents\n"
-"a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
-"d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - descarta aquest tros de l'arbre de treball\n"
-"n - no descartis aquest tros des de l'arbre de treball\n"
-"q - surt; no descartis aquest tros ni cap dels pendents\n"
-"a - descarta aquest tros i tots els trossos posteriors en el fitxer\n"
-"d - no descartis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - descarta aquest tros de l'índex i de l'arbre de treball\n"
-"n - no descartis aquest tros des de l'índex i de l'arbre de treball\n"
-"q - surt; no descartis aquest tros ni cap dels pendents\n"
-"a - descarta aquest tros i tots els trossos posteriors en el fitxer\n"
-"d - no descartis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - aplica aquest tros a l'índex i l'arbre de treball\n"
-"n - no apliquis aquest tros des de l'índex i de l'arbre de treball\n"
-"q - surt; no apliquis aquest tros ni cap dels pendents\n"
-"a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
-"d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - aplica aquest tros a l'arbre de treball\n"
-"n - no apliquis aquest tros a l'arbre de treball\n"
-"q - surt; no apliquis aquest tros ni cap dels pendents\n"
-"a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
-"d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - selecciona el tros on voleu anar\n"
-"/ - cerca un tros que coincideixi amb l'expressió regular donada\n"
-"j - deixa aquest tros sense decidir, veure el tros sense decidir següent\n"
-"J - deixa aquest tros sense decidir, veure el tros següent\n"
-"k - deixa aquest tros sense decidir, veure el tros sense decidir anterior\n"
-"K - deixa aquest tros sense decidir, veure el tros anterior\n"
-"s - divideix el tros actual en trossos més petits\n"
-"e - edita manualment el tros actual\n"
-"? - mostra l'ajuda\n"
-
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "Els trossos seleccionats no apliquen a l'índex\n"
-
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "s'està ignorant %s no fusionat\n"
-
-msgid "No other hunks to goto\n"
-msgstr "No hi ha altres trossos on anar-hi\n"
-
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "Número no vàlid: «%s»\n"
-
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "Només %d tros disponible.\n"
-msgstr[1] "Només %d trossos disponibles.\n"
-
-msgid "No other hunks to search\n"
-msgstr "No hi ha cap altre tros a cercar\n"
-
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "Expressió regular de cerca mal formada %s: %s\n"
-
-msgid "No hunk matches the given pattern\n"
-msgstr "No hi ha trossos que coincideixin amb el patró donat\n"
-
-msgid "No previous hunk\n"
-msgstr "Sense tros previ\n"
-
-msgid "No next hunk\n"
-msgstr "No hi ha tros següent\n"
-
-msgid "Sorry, cannot split this hunk\n"
-msgstr "No es pot dividir aquest tros\n"
-
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "Divideix en %d tros.\n"
-msgstr[1] "Divideix en %d trossos.\n"
-
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "No es pot editar aquest tros\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - mostra els camins amb canvis\n"
-"update        - afegeix l'estat de l'arbre de treball al conjunt de canvis "
-"«staged»\n"
-"revert        - reverteix el conjunt de canvis de «staged» a la versió HEAD\n"
-"patch         - selecciona trossos i actualitza'ls selectivament\n"
-"diff          - mostra la diferència entre HEAD i l'índex\n"
-"add untracked - afegeix el contingut dels fitxers no seguits al conjunt de "
-"canvis «staged»\n"
-
-msgid "missing --"
-msgstr "manca --"
-
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "desconegut --patch mode: %s"
-
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "argument %s no vàlid, s'esperava --"
-
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "la zona local difereix de GMT per un interval que no és de minuts\n"
 
@@ -22370,14 +22740,14 @@ msgstr ""
 "\n"
 "Esborreu el contingut del cos si no voleu enviar cap resum.\n"
 
-#, perl-format
-msgid "Failed to open %s: %s"
-msgstr "S'ha produït un error en obrir %s: %s"
-
 #, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "S'ha produït un error en obrir %s.final: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "S'ha produït un error en obrir %s: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "El correu electrònic de resum està buit, s'omet\n"
 
@@ -22533,13 +22903,17 @@ msgid "(%s) Could not execute '%s'"
 msgstr "(%s) no s'ha pogut executar «%s»"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) S'està afegint %s: %s des de: «%s»\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Sortida mal formada de «%s»"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) s'ha produït un error en tancar el conducte «%s»"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) S'està afegint %s: %s des de: «%s»\n"
+
 msgid "cannot send message as 7bit"
 msgstr "no es pot enviar el missatge en 7 bits"
 
@@ -22577,83 +22951,63 @@ msgstr "S'està ometent %s amb el sufix de còpia de seguretat «%s».\n"
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "Esteu segur que voleu enviar %s? [y|N]: "
 
-#~ msgid "(stats|all)"
-#~ msgstr "(stats|all)"
-
-#~ msgid "git maintenance register"
-#~ msgstr "git maintenance register"
-
-#~ msgid "git maintenance unregister"
-#~ msgstr "git maintenance unregister"
-
-#~ msgid "git maintenance stop"
-#~ msgstr "git maintenance stop"
-
 #, c-format
-#~ msgid "could not parse colored hunk header '%.*s'"
-#~ msgstr "no s'ha pogut analitzar la capçalera del tros acolorida «%.*s»"
+#~ msgid "options '%s=%s' and '%s=%s' cannot be used together"
+#~ msgstr "les opcions «%s=%s» i «%s=%s» no es poden usar juntes"
 
 #, c-format
-#~ msgid "Unknown subcommand: %s"
-#~ msgstr "Subordre desconeguda: %s"
-
-#~ msgid "checked out in another worktree"
-#~ msgstr "s'ha agafat en un altre arbre de treball"
+#~ msgid "%s : incompatible with something else"
+#~ msgstr "%s: és incompatible amb alguna altra cosa"
 
-#~ msgid "failed to open stdin of 'crontab'"
-#~ msgstr "s'ha produït un error en obrir stdin de «crontab»"
+#~ msgid "Could not write patch"
+#~ msgstr "No s'ha pogut escriure el pedaç"
 
 #, c-format
-#~ msgid "invalid subcommand: %s"
-#~ msgstr "subordre no vàlida: %s"
-
-#~ msgid "single arg format must be symmetric range"
-#~ msgstr "el format de l'argument únic ha de ser de rang simètric"
-
-#~ msgid "git submodule--helper list [--prefix=<path>] [<path>...]"
-#~ msgstr "git submodule--helper list [--prefix=<camí>] [<camí>...]"
-
-#~ msgid "git submodule--helper name <path>"
-#~ msgstr "git submodule--helper name <camí>"
+#~ msgid "Could not stat '%s'"
+#~ msgstr "No s'ha pogut fer stat a «%s»"
 
 #, c-format
-#~ msgid "failed to get the default remote for submodule '%s'"
-#~ msgstr ""
-#~ "s'ha produït un error en obtenir el remot per defecte pel submòdul «%s»"
+#~ msgid "Cannot delete branch '%s' checked out at '%s'"
+#~ msgstr "No es pot suprimir la branca «%s» agafada a «%s»"
 
-#, c-format
-#~ msgid "Invalid update mode '%s' for submodule path '%s'"
-#~ msgstr "Mode d'actualització «%s» no vàlid per al camí de submòdul «%s»"
+#~ msgid "unable to write new_index file"
+#~ msgstr "no s'ha pogut escriure el fitxer new_index"
 
-#~ msgid "path into the working tree, across nested submodule boundaries"
-#~ msgstr "camí a l'arbre de treball, a través de fronteres de submòduls niats"
+#~ msgid "do not apply config rules"
+#~ msgstr "no apliquis les regles de configuració"
 
-#~ msgid "rebase, merge, checkout or none"
-#~ msgstr "rebase, merge, checkout o none"
+#~ msgid "join whitespace-continued values"
+#~ msgstr "uneix els valors continus amb espais en blanc"
 
-#~ msgid "bad value for update parameter"
-#~ msgstr "valor incorrecte per al paràmetre update"
+#~ msgid "set parsing options"
+#~ msgstr "estableix les opcions d'anàlisi"
 
-#~ msgid "Show three-way merge without touching index"
-#~ msgstr "Mostra la fusió de tres vies sense tocar l'índex"
+#~ msgid "cannot move directory over file"
+#~ msgstr "no es pot moure un directori sobre un fitxer"
 
-# c-format
-#, c-format
-#~ msgid "could not create directory for '%s'"
-#~ msgstr "no s'ha pogut crear el directori per a «%s»"
+#~ msgid "cannot use --filter without --stdout"
+#~ msgstr "no es pot utilitzar --filter sense --stdout"
 
-#, c-format
-#~ msgid "Couldn't start hook '%s'\n"
-#~ msgstr "No s'ha pogut iniciar el lligam «%s»'\n"
+#~ msgid "cannot use --max-pack-size with --cruft"
+#~ msgstr "no es pot usar --max-pack-size amb --cruft"
+
+#~ msgid "--strategy requires --merge or --interactive"
+#~ msgstr "--strategy requereix --merge o --interactive"
 
 #, c-format
 #~ msgid ""
-#~ "Note: %s not up to date and in way of checking out conflicted version; "
-#~ "old copy renamed to %s"
+#~ "commit-graph has generation number zero for commit %s, but non-zero "
+#~ "elsewhere"
 #~ msgstr ""
-#~ "Nota: %s no està actualitzat i en forma de comprovar la versió en "
-#~ "conflicte; còpia antiga reanomenada a %s"
+#~ "el graf de comissions té nombre de generació zero per a la comissió %s, "
+#~ "però té no zero en altres llocs"
+
+#~ msgid "--merge-base only works with commits"
+#~ msgstr "--merge-base només funciona amb comissions"
+
+#~ msgid "scalar clone [<options>] [--] <repo> [<dir>]"
+#~ msgstr "scalar clone [<opcions>] [--] <repositori> [<dir>]"
 
 #, c-format
-#~ msgid "%s: fast-forward"
-#~ msgstr "%s: avanç ràpid"
+#~ msgid "could not rename '%s' to '%s'"
+#~ msgstr "no s'ha pogut canviar el nom «%s» a «%s»"
index f5c9c29e0b2af13eff5f02313cddc7c14dee4531..95a185afd2b211ecdeaf70132cadc7f999a8df68 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -8,8 +8,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-09-28 17:09+0200\n"
-"PO-Revision-Date: 2022-09-28 17:10+0200\n"
+"POT-Creation-Date: 2023-11-09 11:29+0100\n"
+"PO-Revision-Date: 2023-11-10 14:28+0100\n"
 "Last-Translator: Ralf Thielow <ralf.thielow@gmail.com>\n"
 "Language-Team: German\n"
 "Language: de\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n!=1);\n"
-"X-Generator: Poedit 3.1.1\n"
+"X-Generator: Poedit 3.4.1\n"
 
 #, c-format
 msgid "Huh (%s)?"
@@ -45,13 +45,13 @@ msgstr "Konnte '%s' nicht zum Commit vormerken."
 msgid "could not write index"
 msgstr "konnte Index nicht schreiben"
 
-#, c-format, perl-format
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "%d Pfad aktualisiert\n"
 msgstr[1] "%d Pfade aktualisiert\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "Hinweis: %s ist nun unversioniert.\n"
 
@@ -65,7 +65,7 @@ msgstr "Revert"
 msgid "Could not parse HEAD^{tree}"
 msgstr "Konnte HEAD^{tree} nicht parsen."
 
-#, c-format, perl-format
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "%d Pfad wiederhergestellt\n"
@@ -78,7 +78,7 @@ msgstr "Keine unversionierten Dateien.\n"
 msgid "Add untracked"
 msgstr "Unversionierte Dateien hinzufügen"
 
-#, c-format, perl-format
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "%d Pfad hinzugefügt\n"
@@ -172,19 +172,19 @@ msgstr "Index konnte nicht aktualisiert werden"
 msgid "Bye.\n"
 msgstr "Tschüss.\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "Modusänderung der Staging-Area hinzufügen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Löschung der Staging-Area hinzufügen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Ergänzung der Staging-Area hinzufügen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Diesen Patch-Block der Staging-Area hinzufügen [y,n,q,a,d%s,?]? "
 
@@ -210,19 +210,19 @@ msgstr ""
 "d - diesen oder alle weiteren Patch-Blöcke in dieser Datei nicht zum Commit "
 "vormerken\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "Modusänderung stashen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Löschung stashen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Ergänzung stashen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Diesen Patch-Block stashen [y,n,q,a,d%s,?]? "
 
@@ -246,19 +246,19 @@ msgstr ""
 "a - diesen und alle weiteren Patch-Blöcke dieser Datei stashen\n"
 "d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht stashen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "Modusänderung aus der Staging-Area entfernen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Löschung aus der Staging-Area entfernen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Ergänzung aus der Staging-Area entfernen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Diesen Patch-Block aus der Staging-Area entfernen [y,n,q,a,d%s,?]? "
 
@@ -285,19 +285,19 @@ msgstr ""
 "d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht aus Staging-"
 "Area entfernen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "Modusänderung auf Index anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Löschung auf Index anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Ergänzung auf Index anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Diesen Patch-Block auf Index anwenden [y,n,q,a,d%s,?]? "
 
@@ -324,19 +324,19 @@ msgstr ""
 "d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht auf den Index "
 "anwenden\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "Modusänderung im Arbeitsverzeichnis verwerfen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Löschung im Arbeitsverzeichnis verwerfen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Ergänzung im Arbeitsverzeichnis verwerfen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "Diesen Patch-Block im Arbeitsverzeichnis verwerfen [y,n,q,a,d%s,?]? "
 
@@ -363,24 +363,24 @@ msgstr ""
 "d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht im "
 "Arbeitsverzeichnis verwerfen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Modusänderung vom Index und Arbeitsverzeichnis verwerfen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Löschung vom Index und Arbeitsverzeichnis verwerfen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Ergänzung im Index und Arbeitsverzeichnis verwerfen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Diesen Patch-Block vom Index und Arbeitsverzeichnis verwerfen [y,n,q,a,d"
-"%s,?]? "
+"Diesen Patch-Block vom Index und Arbeitsverzeichnis verwerfen [y,n,q,a,"
+"d%s,?]? "
 
 msgid ""
 "y - discard this hunk from index and worktree\n"
@@ -395,24 +395,24 @@ msgstr ""
 "a - diesen und alle weiteren Patch-Blöcke in der Datei verwerfen\n"
 "d - diesen oder alle weiteren Patch-Blöcke in der Datei nicht verwerfen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Modusänderung auf Index und Arbeitsverzeichnis anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Löschung auf Index und Arbeitsverzeichnis anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Ergänzung auf Index und Arbeitsverzeichnis anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Diesen Patch-Block auf Index und Arbeitsverzeichnis anwenden [y,n,q,a,d"
-"%s,?]? "
+"Diesen Patch-Block auf Index und Arbeitsverzeichnis anwenden [y,n,q,a,"
+"d%s,?]? "
 
 msgid ""
 "y - apply this hunk to index and worktree\n"
@@ -428,19 +428,19 @@ msgstr ""
 "a - diesen und alle weiteren Patch-Blöcke in der Datei anwenden\n"
 "d - diesen oder alle weiteren Patch-Blöcke in der Datei nicht anwenden\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr "Modusänderung auf Arbeitsverzeichnis anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "Löschung auf Arbeitsverzeichnis anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "Ergänzung auf Arbeitsverzeichnis anwenden [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Diesen Patch-Block auf das Arbeitsverzeichnis anwenden [y,n,q,a,d%s,?]? "
@@ -519,7 +519,6 @@ msgstr ""
 "Um '%c' Zeilen zu entfernen, löschen Sie diese.\n"
 "Zeilen, die mit %c beginnen, werden entfernt.\n"
 
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -542,12 +541,6 @@ msgstr "'git apply --cached' schlug fehl"
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
@@ -671,9 +664,9 @@ msgid "Reverting is not possible because you have unmerged files."
 msgstr ""
 "Reverten ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "%s ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr ""
+"Rebase ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -695,6 +688,24 @@ msgstr "Bitte committen Sie Ihre Änderungen, bevor Sie mergen."
 msgid "Exiting because of unfinished merge."
 msgstr "Beende wegen nicht abgeschlossenem Merge."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Abweichende Branches können nicht vorgespult werden, benutzen Sie "
+"stattdessen:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"oder:\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Vorspulen nicht möglich, breche ab."
 
@@ -786,6 +797,9 @@ msgstr "Befehlszeile endet mit \\"
 msgid "unclosed quote"
 msgstr "nicht geschlossene Anführungszeichen"
 
+msgid "too many arguments"
+msgstr "zu viele Argumente"
+
 #, c-format
 msgid "unrecognized whitespace option '%s'"
 msgstr "Nicht erkannte Whitespace-Option: '%s'"
@@ -802,6 +816,12 @@ msgstr "die Optionen '%s' und '%s' können nicht gemeinsam verwendet werden"
 msgid "'%s' outside a repository"
 msgstr "'%s' außerhalb eines Repositories"
 
+msgid "failed to read patch"
+msgstr "Patch konnte nicht gelesen werden"
+
+msgid "patch too large"
+msgstr "Patch zu groß"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "Kann regulären Ausdruck für Zeitstempel %s nicht verarbeiten"
@@ -1147,6 +1167,10 @@ msgstr "Verkürze Name von .rej Datei zu %.*s.rej"
 msgid "cannot open %s"
 msgstr "kann '%s' nicht öffnen"
 
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "Kann '%s' nicht löschen."
+
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Patch-Bereich #%d sauber angewendet."
@@ -1344,6 +1368,11 @@ msgstr "git archive --remote <Repository> [--exec <Programm>] --list"
 msgid "cannot read '%s'"
 msgstr "kann '%s' nicht lesen"
 
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr ""
+"Pfadspezifikation '%s' findet Dateien außerhalb des aktuellen Verzeichnisses"
+
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "Pfadspezifikation '%s' stimmt mit keinen Dateien überein"
@@ -1360,9 +1389,6 @@ msgstr "Kein gültiger Objektname: %s"
 msgid "not a tree object: %s"
 msgstr "Kein Tree-Objekt: %s"
 
-msgid "current working directory is untracked"
-msgstr "aktuelles Arbeitsverzeichnis ist unversioniert"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Datei nicht gefunden: %s"
@@ -1413,6 +1439,12 @@ msgstr ".gitattributes aus dem Arbeitsverzeichnis lesen"
 msgid "report archived files on stderr"
 msgstr "archivierte Dateien in der Standard-Fehlerausgabe ausgeben"
 
+msgid "time"
+msgstr "Zeit"
+
+msgid "set modification time of archive entries"
+msgstr "Änderungszeitpunkt von Archiveinträgen festlegen"
+
 msgid "set compression level"
 msgstr "Komprimierungsgrad setzen"
 
@@ -1453,6 +1485,13 @@ msgstr "Argument für Format '%s' nicht unterstützt: -%d"
 msgid "%.*s is not a valid attribute name"
 msgstr "%.*s ist kein gültiger Attributname"
 
+msgid "unable to add additional attribute"
+msgstr "konnte kein zusätzliches Attribut hinzufügen"
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "ignoriere übermäßig lange Attribut-Zeile %d"
+
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "%s nicht erlaubt: %s:%d"
@@ -1464,6 +1503,21 @@ msgstr ""
 "Verneinende Muster werden in Git-Attributen ignoriert.\n"
 "Benutzen Sie '\\!' für führende Ausrufezeichen."
 
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "Kann gitattributes-Datei '%s' nicht lesen"
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "ignoriere übermäßig große gitattributes-Datei '%s'"
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "ignoriere übermäßig großen gitattribute-Blob '%s'"
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "ungültiges --attr-source oder GIT_ATTR_SOURCE"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Ungültiger Inhalt bzgl. Anführungszeichen in Datei '%s': %s"
@@ -1570,10 +1624,6 @@ msgstr[1] "Binäre Suche: danach noch %d Commits zum Testen übrig %s\n"
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents und --reverse funktionieren gemeinsam nicht."
 
-msgid "cannot use --contents with final commit object name"
-msgstr ""
-"kann --contents nicht mit endgültigem Namen des Commit-Objektes benutzen"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse und --first-parent zusammen erfordern die Angabe eines "
@@ -1648,10 +1698,12 @@ msgstr ""
 msgid "not tracking: ambiguous information for ref '%s'"
 msgstr "kein Tracking: mehrdeutige Informationen für Referenz '%s'"
 
+#. #-#-#-#-#  branch.c.po  #-#-#-#-#
 #. TRANSLATORS: This is a line listing a remote with duplicate
 #. refspecs in the advice message below. For RTL languages you'll
 #. probably want to swap the "%s" and leading "  " space around.
 #.
+#. #-#-#-#-#  object-name.c.po  #-#-#-#-#
 #. TRANSLATORS: This is line item of ambiguous object output
 #. from describe_ambiguous_object() above. For RTL languages
 #. you'll probably want to swap the "%s" and leading " " space
@@ -1694,9 +1746,10 @@ msgid "a branch named '%s' already exists"
 msgstr "Branch '%s' existiert bereits"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
 msgstr ""
-"kann Aktualisierung des Branches '%s' nicht erzwingen, ausgecheckt in '%s'"
+"Aktualisierung des vom Arbeitsverzeichnis '%2$s' verwendete Branch '%1$s' "
+"kann nicht erzwungen werden"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1745,23 +1798,19 @@ msgstr "Submodul '%s': Submodul konnte nicht gefunden werden"
 
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
-"Sie können versuchen die Submodule mit 'git checkout %s && git submodule "
-"update --init' zu aktualisieren"
+"Sie können versuchen, die Submodule mit \"git checkout --no-recurse-"
+"submodules %s && git submodule update --init\" zu aktualisieren."
 
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
 msgstr "Submodul '%s': kann Branch nicht erzeugen: '%s'"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' ist bereits in '%s' ausgecheckt"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD des Arbeitsverzeichnisses %s ist nicht aktualisiert"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s' wird bereits von Arbeitsverzeichnis in '%s' verwendet"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<Optionen>] [--] <Pfadspezifikation>..."
@@ -1770,40 +1819,33 @@ msgstr "git add [<Optionen>] [--] <Pfadspezifikation>..."
 msgid "cannot chmod %cx '%s'"
 msgstr "kann chmod %cx '%s' nicht ausführen"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "unerwarteter Differenz-Status %c"
-
-msgid "updating files failed"
-msgstr "Aktualisierung der Dateien fehlgeschlagen"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "lösche '%s'\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr ""
 "Nicht zum Commit vorgemerkte Änderungen nach Aktualisierung der Staging-Area:"
 
-msgid "Could not read the index"
-msgstr "Konnte den Index nicht lesen"
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"Die Einstellung add.interactive.useBuiltin wurde entfernt!\n"
+"Siehe den Eintrag in 'git help config' für Details."
 
-msgid "Could not write patch"
-msgstr "Konnte Patch nicht schreiben"
+msgid "could not read the index"
+msgstr "konnte den Index nicht lesen"
 
 msgid "editing patch failed"
 msgstr "Bearbeitung des Patches fehlgeschlagen"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Konnte Verzeichnis '%s' nicht lesen"
+msgid "could not stat '%s'"
+msgstr "Konnte '%s' nicht lesen."
 
-msgid "Empty patch. Aborted."
-msgstr "Leerer Patch. Abgebrochen."
+msgid "empty patch. aborted"
+msgstr "leerer Patch. Abgebrochen"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Konnte '%s' nicht anwenden."
+msgid "could not apply '%s'"
+msgstr "konnte '%s' nicht anwenden"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
@@ -1942,6 +1984,9 @@ msgstr ""
 msgid "index file corrupt"
 msgstr "Index-Datei beschädigt"
 
+msgid "unable to write new index file"
+msgstr "Konnte neue Index-Datei nicht schreiben."
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "ungültige Aktion '%s' für '%s'"
@@ -2018,8 +2063,8 @@ msgstr ""
 #, c-format
 msgid "If you prefer to skip this patch, run \"%s --skip\" instead."
 msgstr ""
-"Falls Sie diesen Patch auslassen möchten, führen Sie stattdessen \"%s --skip"
-"\" aus."
+"Falls Sie diesen Patch auslassen möchten, führen Sie stattdessen \"%s --"
+"skip\" aus."
 
 #, c-format
 msgid "To record the empty patch as an empty commit, run \"%s --allow-empty\"."
@@ -2156,9 +2201,6 @@ msgstr ""
 "Sie können `git rm` auf Dateien ausführen, um \"von denen gelöscht\" für\n"
 "diese zu akzeptieren."
 
-msgid "unable to write new index file"
-msgstr "Konnte neue Index-Datei nicht schreiben."
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "Konnte Objekt '%s' nicht parsen."
@@ -2177,11 +2219,6 @@ msgstr ""
 msgid "failed to read '%s'"
 msgstr "Fehler beim Lesen von '%s'"
 
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr ""
-"die Optionen '%s=%s' und '%s=%s' können nicht gemeinsam verwendet werden"
-
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<Optionen>] [(<mbox> | <E-Mail-Verzeichnis>)...]"
 
@@ -2191,6 +2228,9 @@ msgstr "git am [<Optionen>] (--continue | --skip | --abort)"
 msgid "run interactively"
 msgstr "interaktiv ausführen"
 
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "Hooks pre-applypatch und applypatch-msg umgehen"
+
 msgid "historical option -- no-op"
 msgstr "historische Option -- kein Effekt"
 
@@ -2218,9 +2258,6 @@ msgstr "-m an git-mailinfo übergeben"
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "--keep-cr an git-mailsplit für mbox-Format übergeben"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr "kein --keep-cr an git-mailsplit übergeben, unabhängig von am.keepcr"
-
 msgid "strip everything before a scissors line"
 msgstr "alles vor einer Scheren-Zeile entfernen"
 
@@ -2332,32 +2369,28 @@ msgstr "git archive: Protokollfehler"
 msgid "git archive: expected a flush"
 msgstr "git archive: erwartete eine Spülung (flush)"
 
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<Commit>]"
-
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<Begriff> --term-{old,"
-"good}=<Begriff>] [--no-checkout] [--first-parent] [<schlecht> [<gut>...]] "
-"[--] [<Pfade>...]"
+"git bisect start [--term-(new|bad)=<Begriff> --term-(old|good)=<Begriff>]    "
+"[--no-checkout] [--first-parent] [<schlecht> [<gut>...]] [--]    "
+"[<Pfadspezifikation>...]"
 
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<Commit>]"
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<Commit>...]"
 
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<Commit>...]"
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<Commit>|<Bereich>)...]"
 
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <Dateiname>"
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<Commit>]"
 
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<Commit>|<Bereich>)...]"
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <Logdatei>"
 
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <Programm>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <Programm> [<Argument>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2504,10 +2537,6 @@ msgstr ""
 "Auschecken von '%s' fehlgeschlagen. Versuchen Sie 'git bisect start "
 "<gültiger-Branch>'."
 
-msgid "won't bisect on cg-seek'ed tree"
-msgstr ""
-"binäre Suche auf einem durch 'cg-seek' geändertem Verzeichnis nicht möglich"
-
 msgid "bad HEAD - strange symbolic ref"
 msgstr "ungültiger HEAD - merkwürdige symbolische Referenz"
 
@@ -2559,16 +2588,16 @@ msgid "bisect run failed: no command provided."
 msgstr "'bisect run' fehlgeschlagen: kein Befehl angegeben."
 
 #, c-format
-msgid "unable to verify '%s' on good revision"
-msgstr "konnte '%s' nicht für guten Commit überprüfen"
+msgid "unable to verify %s on good revision"
+msgstr "kann %s bei gutem Commit nicht verifizieren"
 
 #, c-format
 msgid "bogus exit code %d for good revision"
 msgstr "fehlerhafter Exit-Code %d für guten Commit"
 
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
-msgstr "'bisect run' fehlgeschlagen: Exit-Code %d von '%s' ist < 0 oder >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "bisect-Lauf fehlgeschlagen: Exit-Code %d von %s ist < 0 oder >= 128"
 
 #, c-format
 msgid "cannot open file '%s' for writing"
@@ -2577,76 +2606,49 @@ msgstr "Datei '%s' kann nicht zum Schreiben geöffnet werden"
 msgid "bisect run cannot continue any more"
 msgstr "'bisect run' kann nicht mehr fortgesetzt werden"
 
-#, c-format
 msgid "bisect run success"
 msgstr "'bisect run' erfolgreich ausgeführt"
 
-#, c-format
 msgid "bisect found first bad commit"
 msgstr "binäre Suche fand ersten schlechten Commit"
 
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
 msgstr ""
-"'bisect run' fehlgeschlagen: 'git bisect--helper --bisect-state %s' mit "
-"Fehlercode %d beendet"
-
-msgid "reset the bisection state"
-msgstr "den Zustand der binären Suche zurücksetzen"
-
-msgid "check whether bad or good terms exist"
-msgstr "prüfen, ob Begriffe für gute und schlechte Commits existieren"
-
-msgid "print out the bisect terms"
-msgstr "die Begriffe für die binäre Suche ausgeben"
-
-msgid "start the bisect session"
-msgstr "Sitzung für binäre Suche starten"
-
-msgid "find the next bisection commit"
-msgstr "nächsten Commit für die binäre Suche finden"
-
-msgid "mark the state of ref (or refs)"
-msgstr "den Status der Referenz(en) markieren"
-
-msgid "list the bisection steps so far"
-msgstr "die bisherigen Schritte der binären Suche auflisten"
-
-msgid "replay the bisection process from the given file"
-msgstr "binäre Suche aus der angegebenen Datei wiederholen"
-
-msgid "skip some commits for checkout"
-msgstr "einige Commits für das Auschecken überspringen"
+"bisect-Lauf fehlgeschlagen: 'git bisect %s' wurde mit Fehlercode %d beendet"
 
-msgid "visualize the bisection"
-msgstr "binäre Suche visualisieren"
-
-msgid "use <cmd>... to automatically bisect"
-msgstr "verwende <Programm>... für die automatische binäre Suche"
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "'%s' erfordert entweder kein Argument oder einen Commit"
 
-msgid "no log for BISECT_WRITE"
-msgstr "kein Log für BISECT_WRITE"
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "'%s' erfordert 0 oder 1 Argument"
 
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset benötigt entweder kein Argument oder ein Commit"
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "'%s' erfordert 0 Argumente"
 
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms benötigt 0 oder 1 Argument"
+msgid "no logfile given"
+msgstr "keine Log-Datei angegeben"
 
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next benötigt 0 Argumente"
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "'%s' ist fehlgeschlagen: kein Befehl angegeben."
 
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log benötigt 0 Argumente"
+msgid "need a command"
+msgstr "Befehl benötigt"
 
-msgid "no logfile given"
-msgstr "keine Log-Datei angegeben"
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "unbekannter Befehl: '%s'"
 
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<Optionen>] [<rev-opts>] [<Commit>] [--] <Datei>"
 
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<Optionen>] [<rev-opts>] [<Commit>] [--] <Datei>"
+
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<rev-opts> sind dokumentiert in git-rev-list(1)"
 
@@ -2810,52 +2812,58 @@ msgstr "git branch [<Optionen>] [-r | -a] [--format]"
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
-"entferne Branch '%s', der zusammengeführt wurde mit\n"
-"         '%s', aber noch nicht mit HEAD zusammengeführt wurde."
+"Löschen des Branches '%s', der mit\n"
+"         '%s', aber noch nicht in HEAD zusammengeführt wurde"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
 "entferne Branch '%s' nicht, der noch nicht zusammengeführt wurde mit\n"
-"         '%s', obwohl er mit HEAD zusammengeführt wurde."
+"         '%s', obwohl er mit HEAD zusammengeführt wurde"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Konnte Commit-Objekt für '%s' nicht nachschlagen."
+msgid "couldn't look up commit object for '%s'"
+msgstr "konnte Commit-Objekt für '%s' nicht nachschlagen"
 
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
 "Der Branch '%s' ist nicht vollständig zusammengeführt.\n"
 "Wenn Sie sicher sind diesen Branch zu entfernen, führen Sie 'git branch -D "
 "%s' aus."
 
-msgid "Update of config-file failed"
+msgid "update of config-file failed"
 msgstr "Aktualisierung der Konfigurationsdatei fehlgeschlagen."
 
 msgid "cannot use -a with -d"
 msgstr "kann -a nicht mit -d benutzen"
 
-msgid "Couldn't look up commit object for HEAD"
-msgstr "Konnte Commit-Objekt für HEAD nicht nachschlagen."
-
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Kann Branch '%s' nicht entfernen, ausgecheckt in '%s'."
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"kann Branch '%s' nicht löschen, der in Arbeitsverzeichnis '%s' verwendet wird"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
+msgid "remote-tracking branch '%s' not found"
 msgstr "Remote-Tracking-Branch '%s' nicht gefunden"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "Branch '%s' nicht gefunden."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"Branch '%s' nicht gefunden.\n"
+"Haben Sie --remote vergessen?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "Branch '%s' nicht gefunden"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2876,53 +2884,56 @@ msgid "HEAD (%s) points outside of refs/heads/"
 msgstr "HEAD (%s) wurde nicht unter \"refs/heads/\" gefunden!"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "Branch %s wird auf %s umgesetzt"
+msgid "branch %s is being rebased at %s"
+msgstr "Rebase von Branch %s in %s im Gange"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "Binäre Suche von Branch %s zu %s im Gange"
-
-msgid "cannot copy the current branch while not on any."
-msgstr ""
-"Kann den aktuellen Branch nicht kopieren, solange Sie sich auf keinem "
-"befinden."
+msgid "branch %s is being bisected at %s"
+msgstr "Binäre Suche von Branch %s in %s im Gange"
 
-msgid "cannot rename the current branch while not on any."
-msgstr ""
-"Kann aktuellen Branch nicht umbenennen, solange Sie sich auf keinem befinden."
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD des Arbeitsverzeichnisses %s ist nicht aktualisiert"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
+msgid "invalid branch name: '%s'"
 msgstr "Ungültiger Branchname: '%s'"
 
-msgid "Branch rename failed"
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "Noch kein Commit in Branch '%s'"
+
+#, c-format
+msgid "no branch named '%s'"
+msgstr "kein Branch mit dem Namen '%s'"
+
+msgid "branch rename failed"
 msgstr "Umbenennung des Branches fehlgeschlagen"
 
-msgid "Branch copy failed"
+msgid "branch copy failed"
 msgstr "Kopie des Branches fehlgeschlagen"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Kopie eines falsch benannten Branches '%s' erstellt."
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "Kopie eines falsch benannten Branches '%s' erstellt"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
+msgid "renamed a misnamed branch '%s' away"
 msgstr "falsch benannten Branch '%s' umbenannt"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Branch umbenannt zu %s, aber HEAD ist nicht aktualisiert!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "Branch wird in %s umbenannt, aber HEAD wird nicht aktualisiert"
 
-msgid "Branch is renamed, but update of config-file failed"
+msgid "branch is renamed, but update of config-file failed"
 msgstr ""
-"Branch ist umbenannt, aber die Aktualisierung der Konfigurationsdatei ist "
-"fehlgeschlagen."
+"Branch wurde umbenannt, aber die Aktualisierung der Konfigurationsdatei\n"
+"ist fehlgeschlagen"
 
-msgid "Branch is copied, but update of config-file failed"
+msgid "branch is copied, but update of config-file failed"
 msgstr ""
-"Branch wurde kopiert, aber die Aktualisierung der Konfigurationsdatei ist\n"
-"fehlgeschlagen."
+"Branch wurde kopiert, aber die Aktualisierung der Konfigurationsdatei\n"
+"ist fehlgeschlagen"
 
 #, c-format
 msgid ""
@@ -2989,6 +3000,9 @@ msgid "move/rename a branch, even if target exists"
 msgstr ""
 "einen Branch verschieben/umbenennen, auch wenn das Ziel bereits existiert"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "keinen Zeilenumbruch nach leer formatierten Referenzen ausgeben"
+
 msgid "copy a branch and its reflog"
 msgstr "einen Branch und dessen Reflog kopieren"
 
@@ -3034,8 +3048,8 @@ msgstr "Rekursion in Submodulen durchführen"
 msgid "format to use for the output"
 msgstr "für die Ausgabe zu verwendendes Format"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Konnte HEAD nicht als gültige Referenz auflösen."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "HEAD konnte nicht als gültige Referenz aufgelöst werden"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD wurde nicht unter \"refs/heads\" gefunden!"
@@ -3053,19 +3067,20 @@ msgstr "--recurse-submodules kann nur genutzt werden, um Branches zu erstellen"
 msgid "branch name required"
 msgstr "Branchname erforderlich"
 
-msgid "Cannot give description to detached HEAD"
+msgid "cannot give description to detached HEAD"
 msgstr "zu losgelöstem HEAD kann keine Beschreibung hinterlegt werden"
 
 msgid "cannot edit description of more than one branch"
 msgstr "Beschreibung von mehr als einem Branch kann nicht bearbeitet werden"
 
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Noch kein Commit in Branch '%s'."
+msgid "cannot copy the current branch while not on any"
+msgstr ""
+"Kann den aktuellen Branch nicht kopieren, solange Sie sich auf keinem "
+"befinden."
 
-#, c-format
-msgid "No branch named '%s'."
-msgstr "Branch '%s' nicht vorhanden."
+msgid "cannot rename the current branch while not on any"
+msgstr ""
+"Kann aktuellen Branch nicht umbenennen, solange Sie sich auf keinem befinden."
 
 msgid "too many branches for a copy operation"
 msgstr "zu viele Branches für eine Kopieroperation angegeben"
@@ -3078,10 +3093,10 @@ msgstr "zu viele Argumente angegeben, um Upstream-Branch zu setzen"
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"Konnte keinen neuen Upstream-Branch von HEAD zu %s setzen, da dieser auf\n"
-"keinen Branch zeigt."
+"konnte den Upstream von HEAD nicht auf %s setzen, wenn er auf keinen Zweig "
+"verweist"
 
 #, c-format
 msgid "no such branch '%s'"
@@ -3096,17 +3111,17 @@ msgstr ""
 "zu viele Argumente angegeben, um Konfiguration zu Upstream-Branch zu "
 "entfernen"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr ""
-"Konnte Konfiguration zu Upstream-Branch von HEAD nicht entfernen, da dieser\n"
-"auf keinen Branch zeigt."
+"konnte den Upstream von HEAD nicht aufheben, wenn er nicht auf einen Zweig "
+"verweist"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "Branch '%s' hat keinen Upstream-Branch gesetzt"
+msgid "branch '%s' has no upstream information"
+msgstr "Branch '%s' hat keine Upstream-Informationen"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
 "Die Optionen -a und -r bei 'git branch' können nicht mit einem Branchnamen "
@@ -3115,7 +3130,7 @@ msgstr ""
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "Die '--set-upstream' Option wird nicht länger unterstützt.\n"
 "Bitte benutzen Sie stattdessen '--track' oder '--set-upstream-to'."
@@ -3137,11 +3152,11 @@ msgid "not run from a git repository - no hooks to show\n"
 msgstr "nicht in einem Git-Repository ausgeführt - keine Hooks zum Anzeigen\n"
 
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory <Datei>] [-s|--suffix <Format>] [--"
-"diagnose[=<Modus>]"
+"git bugreport [(-o | --output-directory) <Pfad>] [(-s | --suffix) <Format>]\n"
+"              [--diagnose[=<Modus>]]"
 
 msgid ""
 "Thank you for filling out a Git bug report!\n"
@@ -3194,6 +3209,10 @@ msgstr "einen Zielort für die Fehlerberichtsdatei(en) angeben"
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "ein Suffix im strftime-Format für den/die Dateinamen angeben"
 
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "unbekanntes Argument `%s'"
+
 #, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "konnte vorangehende Verzeichnisse für '%s' nicht erstellen"
@@ -3216,17 +3235,24 @@ msgstr "konnte nicht nach %s schreiben"
 msgid "Created new report at '%s'.\n"
 msgstr "Neuer Bericht unter '%s' erstellt.\n"
 
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [<Optionen>] <Datei> <git-rev-list Argumente>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<Version>] <Datei> <git-rev-list-Argumente>"
 
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [<Optionen>] <Datei>"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <Datei>"
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads <Datei> [<Referenzname>...]"
 
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle <Datei> [<Referenzname>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <Datei> [<Referenzname>...]"
+
+msgid "need a <file> argument"
+msgstr "<Datei> Argument benötigt"
 
 msgid "do not show progress meter"
 msgstr "keine Fortschrittsanzeige anzeigen"
@@ -3234,11 +3260,11 @@ msgstr "keine Fortschrittsanzeige anzeigen"
 msgid "show progress meter"
 msgstr "Fortschrittsanzeige anzeigen"
 
-msgid "show progress meter during object writing phase"
-msgstr "Forschrittsanzeige während des Schreibens von Objekten anzeigen"
+msgid "historical; same as --progress"
+msgstr "historisch; dasselbe wie --progress"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "ähnlich zu --all-progress wenn Fortschrittsanzeige darstellt wird"
+msgid "historical; does nothing"
+msgstr "historisch; kein Effekt"
 
 msgid "specify bundle format version"
 msgstr "Version des Paket-Formats angeben"
@@ -3281,10 +3307,6 @@ msgstr "%s benötigt Argumente"
 msgid "%s takes no arguments"
 msgstr "%s braucht kein Argument"
 
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "unbekannter Befehl: '%s'"
-
 msgid "only one batch option may be specified"
 msgstr "Nur eine Batch-Option erlaubt."
 
@@ -3297,24 +3319,24 @@ msgstr "git cat-file (-e | -p) <Objekt>"
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <Objekt>"
 
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<Commit>:<Pfad|Commit-Referenz> | --path=<Pfad|Commit-"
+"Referenz> <Commit>]"
+
 msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
-msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<Commit>:<Pfad|Commit-Referenz> | --path=<Pfad|Commit-"
-"Referenz> <Commit>]"
+"             [--textconv | --filters] [-Z]"
 
 msgid "Check object existence or emit object contents"
 msgstr "Überprüfen von Objektexistenz oder Ausgeben von Objekt-Inhalten"
@@ -3353,6 +3375,9 @@ msgstr "wie --batch, aber keine Ausgabe von <Inhalten>"
 msgid "stdin is NUL-terminated"
 msgstr "stdin endet mit NUL"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin und stdout sind NUL-beendet"
+
 msgid "read commands from stdin"
 msgstr "Befehle von der Standard-Eingabe lesen"
 
@@ -3419,18 +3444,22 @@ msgstr "<Commit> benötigt mit '%s'"
 msgid "<object> required with '-%c'"
 msgstr "<Objekt> benötigt mit '-%c'"
 
-msgid "too many arguments"
-msgstr "zu viele Argumente"
-
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr "nur zwei Argumente im <Typ> <Objekt> Modus erlaubt, nicht %d"
 
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <Attribut>...] [--] <Pfadname>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <Commit-Referenz>] [-a | --all | <Attribut>...] "
+"[--] <Pfadname>..."
 
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <Attribut>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <Commit-Referenz>] [-a | --all | "
+"<Attribut>...]"
 
 msgid "report all attributes set on file"
 msgstr "alle Attribute einer Datei ausgeben"
@@ -3444,6 +3473,12 @@ msgstr "Dateinamen von der Standard-Eingabe lesen"
 msgid "terminate input and output records by a NUL character"
 msgstr "Einträge von Ein- und Ausgabe mit NUL-Zeichen abschließen"
 
+msgid "<tree-ish>"
+msgstr "<Commit-Referenz>"
+
+msgid "which tree-ish to check attributes at"
+msgstr "in welchem Commit die Attribute zu prüfen sind"
+
 msgid "suppress progress reporting"
 msgstr "Fortschrittsanzeige unterdrücken"
 
@@ -3604,6 +3639,12 @@ msgstr "'%s' kann nur genutzt werden, wenn '%s' nicht verwendet wird"
 msgid "'%s' or '%s' cannot be used with %s"
 msgstr "'%s' oder '%s' kann nicht mit %s verwendet werden"
 
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr ""
+"'%s', '%s' oder '%s' können beim Auschecken aus einem Verzeichnis nicht "
+"verwendet werden"
+
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "Pfad '%s' ist nicht zusammengeführt"
@@ -3968,9 +4009,11 @@ msgid "use overlay mode"
 msgstr "benutze Overlay-Modus"
 
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <Muster>] [-x | -X] [--] <Pfade>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <Muster>] [-x | -X] [--] "
+"[<Pfadspezifikation>...]"
 
 #, c-format
 msgid "Removing %s\n"
@@ -4034,7 +4077,7 @@ msgstr ""
 "*          - alle Elemente auswählen\n"
 "           - (leer) Auswahl beenden\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Wie bitte (%s)?\n"
 
@@ -4184,9 +4227,6 @@ msgid "create a shallow clone of that depth"
 msgstr ""
 "einen Klon mit unvollständiger Historie (shallow) in dieser Tiefe erstellen"
 
-msgid "time"
-msgstr "Zeit"
-
 msgid "create a shallow clone since a specific time"
 msgstr ""
 "einen Klon mit unvollständiger Historie (shallow) seit einer bestimmten "
@@ -4228,12 +4268,6 @@ msgstr "serverspezifisch"
 msgid "option to transmit"
 msgstr "Option übertragen"
 
-msgid "use IPv4 addresses only"
-msgstr "nur IPv4-Adressen benutzen"
-
-msgid "use IPv6 addresses only"
-msgstr "nur IPv6-Adressen benutzen"
-
 msgid "apply partial clone filters to submodules"
 msgstr "partielle Klonfilter auf Submodule anwenden"
 
@@ -4265,10 +4299,20 @@ msgstr "Konnte '%s' nicht lesen"
 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."
@@ -4335,10 +4379,6 @@ msgstr "Zu viele Argumente."
 msgid "You must specify a repository to clone."
 msgstr "Sie müssen ein Repository zum Klonen angeben."
 
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "die Optionen '%s' und '%s %s' können nicht gemeinsam verwendet werden"
-
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
 "exclude"
@@ -4437,6 +4477,9 @@ msgstr "konnte das Repository nicht initialisieren, überspringe Bundle-URI"
 msgid "failed to fetch objects from bundle URI '%s'"
 msgstr "Objekte aus Bundle-URI '%s' konnten nicht abgerufen werden"
 
+msgid "failed to fetch advertised bundles"
+msgstr "angekündigte Bundles konnten nicht abgerufen werden"
+
 msgid "remote transport reported error"
 msgstr "Remoteübertragung meldete Fehler"
 
@@ -4472,19 +4515,25 @@ msgid "--command must be the first argument"
 msgstr "--command muss an erster Stelle stehen"
 
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir <Objektverzeichnis>] [--shallow] [--"
+"git commit-graph verify [--object-dir <Verzeichnis>] [--shallow] [--"
 "[no-]progress]"
 
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 msgstr ""
-"git commit-graph write [--object-dir <Objektverzeichnis>] [--append] [--"
-"split[=<Strategie>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <Anzahl>] [--[no-]progress] <Split-Optionen>"
+"git commit-graph write [--object-dir <Verzeichnis>] [--append]\n"
+"                       [--split[=<Strategie>]] [--reachable | --stdin-packs "
+"| --stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <Split-Optionen>"
 
 msgid "dir"
 msgstr "Verzeichnis"
@@ -4500,6 +4549,10 @@ msgstr ""
 msgid "Could not open commit-graph '%s'"
 msgstr "Konnte Commit-Graph '%s' nicht öffnen."
 
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "konnte die Commit-Graph-Kette '%s' nicht öffnen"
+
 #, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "nicht erkanntes --split Argument, %s"
@@ -4558,12 +4611,15 @@ msgstr ""
 msgid "Collecting commits from input"
 msgstr "Sammle Commits von der Standard-Eingabe"
 
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <Tree-Objekt> [(-p <Elternteil>)...]"
+
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p <Eltern-Commit>)...] [-S[<Key-ID>]] [(-m "
-"<Nachricht>)...] [(-F <Datei>)...] <Tree-Objekt>"
+"git commit-tree [(-p <Elternteil>)...] [-S[<Key-ID>]] [(-m <Nachricht>)...]\n"
+"                [(-F <Datei>)...] <Tree-Objekt>"
 
 #, c-format
 msgid "duplicate parent %s ignored"
@@ -4605,11 +4661,29 @@ msgstr "Brauche genau ein Tree-Objekt."
 msgid "git commit-tree: failed to read"
 msgstr "git commit-tree: Fehler beim Lesen"
 
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<Optionen>] [--] <Pfadspezifikation>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<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"
+"           [--] [<Pfadspezifikation>...]"
 
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<Optionen>] [--] <Pfadspezifikation>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<Optionen>] [--] [<Pfadspezifikation>...]"
 
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -4661,6 +4735,9 @@ msgstr ""
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "Aktualisierung der Dateien fehlgeschlagen"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "Fehler beim Entpacken des Tree-Objektes von HEAD."
 
@@ -4679,9 +4756,6 @@ msgstr "Konnte temporären Index nicht aktualisieren."
 msgid "Failed to update main cache tree"
 msgstr "Konnte Haupt-Cache-Verzeichnis nicht aktualisieren"
 
-msgid "unable to write new_index file"
-msgstr "Konnte new_index Datei nicht schreiben"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "Kann keinen Teil-Commit durchführen, während ein Merge im Gange ist."
 
@@ -4721,8 +4795,8 @@ msgstr ""
 "der aktuellen Commit-Beschreibung verwendet wird."
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "Konnte Commit %s nicht nachschlagen"
+msgid "could not lookup commit '%s'"
+msgstr "konnte Commit '%s' nicht nachschlagen"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -5091,13 +5165,13 @@ msgstr "Commit aufgrund leerer Commit-Beschreibung abgebrochen.\n"
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"Das Repository wurde aktualisiert, aber die \"new_index\"-Datei\n"
+"Das Repository wurde aktualisiert, aber die neue Index-Datei\n"
 "konnte nicht geschrieben werden. Prüfen Sie, dass Ihre Festplatte nicht\n"
 "voll und Ihr Kontingent nicht aufgebraucht ist und führen Sie\n"
-"anschließend \"git restore HEAD --staged :/\" zur Wiederherstellung aus."
+"anschließend \"git restore --staged :/\" zur Wiederherstellung aus."
 
 msgid "git config [<options>]"
 msgstr "git config [<Optionen>]"
@@ -5396,11 +5470,20 @@ msgstr "credential-cache nicht verfügbar; Unix-Socket wird nicht unterstützt"
 msgid "unable to get credential storage lock in %d ms"
 msgstr "konnte Sperre für Zugangsdatenspeicher nicht in %d ms bekommen"
 
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<Optionen>] [<Commit-Angabe>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<Commit-"
+"Angabe>...]"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --"
+"dirty[=<Markierung>]"
 
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<Optionen>] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe <Blob>"
 
 msgid "head"
 msgstr "Branch"
@@ -5511,8 +5594,8 @@ msgstr "Markierung"
 
 msgid "append <mark> on dirty working tree (default: \"-dirty\")"
 msgstr ""
-"<Markierung> bei geändertem Arbeitsverzeichnis anhängen (Standard: \"-dirty"
-"\")"
+"<Markierung> bei geändertem Arbeitsverzeichnis anhängen (Standard: \"-"
+"dirty\")"
 
 msgid "append <mark> on broken working tree (default: \"-broken\")"
 msgstr ""
@@ -5526,11 +5609,11 @@ msgid "option '%s' and commit-ishes cannot be used together"
 msgstr "Option '%s' und Commit-Angaben können nicht gemeinsam verwendet werden"
 
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory <Pfad>] [-s|--suffix <Format>] [--"
-"mode=<Modus>]"
+"git diagnose [(-o | --output-directory) <Pfad>] [(-s | --suffix) <Format>]\n"
+"             [--mode=<Modus>]"
 
 msgid "specify a destination for the diagnostics archive"
 msgstr "einen Zielort für das Diagnosearchiv angeben"
@@ -5548,6 +5631,9 @@ msgstr "--merge-base funktioniert nur mit zwei Commits"
 msgid "'%s': not a regular file or symlink"
 msgstr "'%s': keine reguläre Datei oder symbolische Verknüpfung"
 
+msgid "no merge given, only parents."
+msgstr "kein Merge gegeben, nur Eltern."
+
 #, c-format
 msgid "invalid option: %s"
 msgstr "Ungültige Option: %s"
@@ -5663,33 +5749,10 @@ msgstr "kein <Tool> für --tool=<Tool> angegeben"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "kein <Programm> für --extcmd=<Programm> angegeben"
 
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <Optionen> <Umgebungsvariable>"
-
-msgid "default for git_env_*(...) to fall back on"
-msgstr "Standard für git_env_*(...), um darauf zurückzugreifen"
-
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr "Ausgaben unterdrücken; nur git_env_*() Werte als Exit-Code verwenden"
+msgid "git fast-export [<rev-list-opts>]"
+msgstr "git fast-export [<rev-list-opts>]"
 
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr ""
-"Option `--default' erwartet einen booleschen Wert bei `--type=bool`, nicht `"
-"%s`"
-
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not `"
-"%s`"
-msgstr ""
-"Option `--default' erwartet einen vorzeichenlosen Long-Wert bei `--"
-"type=ulong`, nicht `%s`"
-
-msgid "git fast-export [<rev-list-opts>]"
-msgstr "git fast-export [<rev-list-opts>]"
-
-msgid "Error: Cannot export nested tags unless --mark-tags is specified."
+msgid "Error: Cannot export nested tags unless --mark-tags is specified."
 msgstr ""
 "Fehler: Verschachtelte Tags können nicht exportiert werden, außer --mark-"
 "tags wurde angegeben."
@@ -5799,126 +5862,13 @@ msgstr "git fetch --all [<Optionen>]"
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel kann nicht negativ sein"
 
-msgid "fetch from all remotes"
-msgstr "fordert von allen Remote-Repositories an"
-
-msgid "set upstream for git pull/fetch"
-msgstr "Upstream für \"git pull/fetch\" setzen"
-
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "an .git/FETCH_HEAD anhängen statt zu überschreiben"
-
-msgid "use atomic transaction to update references"
-msgstr "atomare Transaktionen nutzen, um Referenzen zu aktualisieren"
-
-msgid "path to upload pack on remote end"
-msgstr "Pfad des Programms zum Hochladen von Paketen auf der Gegenseite"
-
-msgid "force overwrite of local reference"
-msgstr "das Überschreiben einer lokalen Referenz erzwingen"
-
-msgid "fetch from multiple remotes"
-msgstr "von mehreren Remote-Repositories anfordern"
-
-msgid "fetch all tags and associated objects"
-msgstr "alle Tags und verbundene Objekte anfordern"
-
-msgid "do not fetch all tags (--no-tags)"
-msgstr "nicht alle Tags anfordern (--no-tags)"
-
-msgid "number of submodules fetched in parallel"
-msgstr "Anzahl der parallel anzufordernden Submodule"
-
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr ""
-"Refspec verändern, damit alle Referenzen unter refs/prefetch/ platziert "
-"werden"
-
-msgid "prune remote-tracking branches no longer on remote"
-msgstr ""
-"Remote-Tracking-Branches entfernen, die sich nicht mehr im Remote-Repository "
-"befinden"
-
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr ""
-"lokale Tags entfernen, die sich nicht mehr im Remote-Repository befinden, "
-"und geänderte Tags aktualisieren"
-
-msgid "on-demand"
-msgstr "bei-Bedarf"
-
-msgid "control recursive fetching of submodules"
-msgstr "rekursive Anforderungen von Submodulen kontrollieren"
-
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "schreibe angeforderte Referenzen in die FETCH_HEAD-Datei"
-
-msgid "keep downloaded pack"
-msgstr "heruntergeladenes Paket behalten"
-
-msgid "allow updating of HEAD ref"
-msgstr "Aktualisierung der \"HEAD\"-Referenz erlauben"
-
-msgid "deepen history of shallow clone"
-msgstr ""
-"die Historie eines Klons mit unvollständiger Historie (shallow) vertiefen"
-
-msgid "deepen history of shallow repository based on time"
-msgstr ""
-"die Historie eines Klons mit unvollständiger Historie (shallow) auf "
-"Zeitbasis\n"
-"vertiefen"
-
-msgid "convert to a complete repository"
-msgstr "zu einem vollständigen Repository konvertieren"
-
-msgid "re-fetch without negotiating common commits"
-msgstr "erneutes Abrufen ohne Aushandeln gemeinsamer Commits"
-
-msgid "prepend this to submodule path output"
-msgstr "dies an die Ausgabe der Submodul-Pfade voranstellen"
-
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr ""
-"Standard für die rekursive Anforderung von Submodulen (geringere Priorität\n"
-"als Konfigurationsdateien)"
-
-msgid "accept refs that update .git/shallow"
-msgstr "Referenzen, die .git/shallow aktualisieren, akzeptieren"
-
-msgid "refmap"
-msgstr "Refmap"
-
-msgid "specify fetch refmap"
-msgstr "Refmap für 'fetch' angeben"
-
-msgid "report that we have only objects reachable from this object"
-msgstr ""
-"ausgeben, dass wir nur Objekte haben, die von diesem Objekt aus erreichbar "
-"sind"
-
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr ""
-"keine Packdatei anfordern; stattdessen die Vorgänger der Verhandlungstipps "
-"anzeigen"
-
-msgid "run 'maintenance --auto' after fetching"
-msgstr "führe 'maintenance --auto' nach \"fetch\" aus"
-
-msgid "check for forced-updates on all updated branches"
-msgstr "Prüfe auf erzwungene Aktualisierungen in allen aktualisierten Branches"
-
-msgid "write the commit-graph after fetching"
-msgstr "Schreibe den Commit-Graph nach \"fetch\""
-
-msgid "accept refspecs from stdin"
-msgstr "akzeptiere Refspecs von der Standard-Eingabe"
-
 msgid "couldn't find remote ref HEAD"
 msgstr "konnte Remote-Referenz von HEAD nicht finden"
 
+#, c-format
+msgid "From %.*s\n"
+msgstr "Von %.*s\n"
+
 #, c-format
 msgid "object %s not found"
 msgstr "Objekt %s nicht gefunden"
@@ -5992,10 +5942,6 @@ msgstr ""
 "%s zurückgewiesen, da Root-Commits von Repositories mit unvollständiger\n"
 "Historie (shallow) nicht aktualisiert werden dürfen."
 
-#, c-format
-msgid "From %.*s\n"
-msgstr "Von %.*s\n"
-
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -6088,6 +6034,123 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr "Sie müssen den Namen des Tags angeben"
 
+msgid "fetch from all remotes"
+msgstr "fordert von allen Remote-Repositories an"
+
+msgid "set upstream for git pull/fetch"
+msgstr "Upstream für \"git pull/fetch\" setzen"
+
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "an .git/FETCH_HEAD anhängen statt zu überschreiben"
+
+msgid "use atomic transaction to update references"
+msgstr "atomare Transaktionen nutzen, um Referenzen zu aktualisieren"
+
+msgid "path to upload pack on remote end"
+msgstr "Pfad des Programms zum Hochladen von Paketen auf der Gegenseite"
+
+msgid "force overwrite of local reference"
+msgstr "das Überschreiben einer lokalen Referenz erzwingen"
+
+msgid "fetch from multiple remotes"
+msgstr "von mehreren Remote-Repositories anfordern"
+
+msgid "fetch all tags and associated objects"
+msgstr "alle Tags und verbundene Objekte anfordern"
+
+msgid "do not fetch all tags (--no-tags)"
+msgstr "nicht alle Tags anfordern (--no-tags)"
+
+msgid "number of submodules fetched in parallel"
+msgstr "Anzahl der parallel anzufordernden Submodule"
+
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"Refspec verändern, damit alle Referenzen unter refs/prefetch/ platziert "
+"werden"
+
+msgid "prune remote-tracking branches no longer on remote"
+msgstr ""
+"Remote-Tracking-Branches entfernen, die sich nicht mehr im Remote-Repository "
+"befinden"
+
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr ""
+"lokale Tags entfernen, die sich nicht mehr im Remote-Repository befinden, "
+"und geänderte Tags aktualisieren"
+
+msgid "on-demand"
+msgstr "bei-Bedarf"
+
+msgid "control recursive fetching of submodules"
+msgstr "rekursive Anforderungen von Submodulen kontrollieren"
+
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "schreibe angeforderte Referenzen in die FETCH_HEAD-Datei"
+
+msgid "keep downloaded pack"
+msgstr "heruntergeladenes Paket behalten"
+
+msgid "allow updating of HEAD ref"
+msgstr "Aktualisierung der \"HEAD\"-Referenz erlauben"
+
+msgid "deepen history of shallow clone"
+msgstr ""
+"die Historie eines Klons mit unvollständiger Historie (shallow) vertiefen"
+
+msgid "deepen history of shallow repository based on time"
+msgstr ""
+"die Historie eines Klons mit unvollständiger Historie (shallow) auf "
+"Zeitbasis\n"
+"vertiefen"
+
+msgid "convert to a complete repository"
+msgstr "zu einem vollständigen Repository konvertieren"
+
+msgid "re-fetch without negotiating common commits"
+msgstr "erneutes Abrufen ohne Aushandeln gemeinsamer Commits"
+
+msgid "prepend this to submodule path output"
+msgstr "dies an die Ausgabe der Submodul-Pfade voranstellen"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"Standard für die rekursive Anforderung von Submodulen (geringere Priorität\n"
+"als Konfigurationsdateien)"
+
+msgid "accept refs that update .git/shallow"
+msgstr "Referenzen, die .git/shallow aktualisieren, akzeptieren"
+
+msgid "refmap"
+msgstr "Refmap"
+
+msgid "specify fetch refmap"
+msgstr "Refmap für 'fetch' angeben"
+
+msgid "report that we have only objects reachable from this object"
+msgstr ""
+"ausgeben, dass wir nur Objekte haben, die von diesem Objekt aus erreichbar "
+"sind"
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+"keine Packdatei anfordern; stattdessen die Vorgänger der Verhandlungstipps "
+"anzeigen"
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr "führe 'maintenance --auto' nach \"fetch\" aus"
+
+msgid "check for forced-updates on all updated branches"
+msgstr "Prüfe auf erzwungene Aktualisierungen in allen aktualisierten Branches"
+
+msgid "write the commit-graph after fetching"
+msgstr "Schreibe den Commit-Graph nach \"fetch\""
+
+msgid "accept refspecs from stdin"
+msgstr "akzeptiere Refspecs von der Standard-Eingabe"
+
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only benötigt einen oder mehrere --negotiation-tip=*"
 
@@ -6099,6 +6162,10 @@ msgstr ""
 "--unshallow kann nicht in einem Repository mit vollständiger Historie "
 "verwendet werden"
 
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "Bundles aus '%s' konnten nicht abgerufen werden"
+
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all akzeptiert kein Repository als Argument"
 
@@ -6206,8 +6273,14 @@ msgstr "nur Referenzen ausgeben, die diesen Commit enthalten"
 msgid "print only refs which don't contain the commit"
 msgstr "nur Referenzen ausgeben, die diesen Commit nicht enthalten"
 
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=<Konfiguration> <Befehlsargumente>"
+msgid "read reference patterns from stdin"
+msgstr "Referenzmuster von Standard-Eingabe lesen"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "unbekannte Argumente mit --stdin geliefert"
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<Konfiguration> [--] <Argumente>"
 
 msgid "config"
 msgstr "Konfiguration"
@@ -6218,6 +6291,10 @@ msgstr "Konfigurationsschlüssel für eine Liste von Repository-Pfaden"
 msgid "missing --config=<config>"
 msgstr "Option --config=<Konfiguration> fehlt"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "ungültige Konfiguration --config=%s"
+
 msgid "unknown"
 msgstr "unbekannt"
 
@@ -6364,22 +6441,39 @@ msgstr "%s: losgelöster HEAD zeigt auf nichts"
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "Notiz: %s zeigt auf einen ungeborenen Branch (%s)"
 
-msgid "Checking cache tree"
-msgstr "Prüfe Cache-Verzeichnis"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Prüfe Cache-Verzeichnis von %s"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: Ungültiger SHA1-Zeiger in Cache-Verzeichnis"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: ungültiger SHA1-Zeiger im Cache-Verzeichnis von %s"
 
 msgid "non-tree in cache-tree"
 msgstr "non-tree in Cache-Verzeichnis"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: Ungültiger sha1-Zeiger in resolve-undo"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: ungültiger SHA1-Zeiger in resolve-undo von %s"
 
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<Optionen>] [<Objekt>...]"
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "rev-Index für Pack-Datei '%s' kann nicht geladen werden"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "ungültiger Rev-Index für Pack-Datei '%s'"
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<Objekt>...]"
 
 msgid "show unreachable objects"
 msgstr "unerreichbare Objekte anzeigen"
@@ -6434,12 +6528,6 @@ msgstr "git fsmonitor--daemon start [<Optionen>]"
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [<Optionen>]"
 
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
-
 #, c-format
 msgid "value of '%s' out of range: %d"
 msgstr "Wert von '%s' außerhalb des Bereichs: %d"
@@ -6561,6 +6649,9 @@ msgstr "unreferenzierte Objekte entfernen"
 msgid "pack unreferenced objects separately"
 msgstr "unreferenzierte Objekte separat verpacken"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "mit --cruft, die Größe neuer Cruft-Pakete begrenzen"
+
 msgid "be more thorough (increased runtime)"
 msgstr "mehr Gründlichkeit (erhöht Laufzeit)"
 
@@ -6689,8 +6780,20 @@ msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr ""
 "nutzen Sie höchstens eine der Optionen --auto oder --schedule=<Häufigkeit>"
 
-msgid "failed to run 'git config'"
-msgstr "Fehler beim Ausführen von 'git config'"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "Wert '%s' von '%s' kann nicht hinzugefügt werden"
+
+msgid "return success even if repository was not registered"
+msgstr "Erfolg zurückgeben, auch wenn das Repository nicht registriert wurde"
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "Wert '%s' von '%s' kann nicht zurückgesetzt werden"
+
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "Repository '%s' ist nicht registriert"
 
 #, c-format
 msgid "failed to expand path '%s'"
@@ -6732,12 +6835,6 @@ msgstr ""
 msgid "'crontab' died"
 msgstr "'crontab' abgebrochen"
 
-msgid "failed to start systemctl"
-msgstr "Fehler beim Starten von systemctl"
-
-msgid "failed to run systemctl"
-msgstr "Fehler beim Ausführen von systemctl"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "Fehler beim Löschen von '%s'"
@@ -6746,6 +6843,12 @@ msgstr "Fehler beim Löschen von '%s'"
 msgid "failed to flush '%s'"
 msgstr "Flush bei '%s' fehlgeschlagen."
 
+msgid "failed to start systemctl"
+msgstr "Fehler beim Starten von systemctl"
+
+msgid "failed to run systemctl"
+msgstr "Fehler beim Ausführen von systemctl"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "nicht erkanntes --scheduler Argument '%s'"
@@ -6769,6 +6872,9 @@ msgstr "Scheduler"
 msgid "scheduler to trigger git maintenance run"
 msgstr "Scheduler, um \"git maintenance run\" auzuführen"
 
+msgid "failed to set up maintenance schedule"
+msgstr "Fehler beim Einrichten des Wartungsplans"
+
 msgid "failed to add repo to global config"
 msgstr "Repository konnte nicht zur globalen Konfiguration hinzugefügt werden"
 
@@ -6786,6 +6892,7 @@ msgstr "grep: Fehler beim Erzeugen eines Thread: %s"
 msgid "invalid number of threads specified (%d) for %s"
 msgstr "ungültige Anzahl von Threads (%d) für %s angegeben"
 
+#. #-#-#-#-#  grep.c.po  #-#-#-#-#
 #. TRANSLATORS: %s is the configuration
 #. variable for tweaking threads, currently
 #. grep.threads
@@ -6842,8 +6949,8 @@ msgstr "binäre Dateien mit \"textconv\"-Filtern verarbeiten"
 msgid "search in subdirectories (default)"
 msgstr "in Unterverzeichnissen suchen (Standard)"
 
-msgid "descend at most <depth> levels"
-msgstr "höchstens <Tiefe> Ebenen durchlaufen"
+msgid "descend at most <n> levels"
+msgstr "höchstens <n> Ebenen absteigen"
 
 msgid "use extended POSIX regular expressions"
 msgstr "erweiterte reguläre Ausdrücke aus POSIX verwenden"
@@ -6988,11 +7095,14 @@ msgid "both --cached and trees are given"
 msgstr "--cached und \"Tree\"-Objekte angegeben"
 
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <Art>] [-w] [--path=<Datei> | --no-filters] [--stdin] "
-"[--] <Datei>..."
+"git hash-object [-t <Art>] [-w] [--path=<Datei> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <Datei>..."
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <Art>] [-w] --stdin-paths [--no-filters]"
 
 msgid "object type"
 msgstr "Art des Objektes"
@@ -7127,12 +7237,19 @@ msgstr "Verwendung: %s%s"
 msgid "'git help config' for more information"
 msgstr "'git help config' für weitere Informationen"
 
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] <Hook-Name> [-- <Hook-Argumente>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<Pfad>] <Hook-Name> [-- <Hook-"
+"Argumente>]"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "fehlende Anforderung <Hook-Name> stillschweigend ignorieren"
 
+msgid "file to read into hooks' stdin"
+msgstr "Datei zum Einlesen in das Hook-stdin"
+
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "Objekt-Typen passen bei %s nicht zusammen"
@@ -7350,83 +7467,16 @@ msgstr "--verify wurde ohne Namen der Paketdatei angegeben"
 msgid "fsck error in pack objects"
 msgstr "fsck Fehler beim Packen von Objekten"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "kann Vorlage '%s' nicht lesen"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "kann Verzeichnis '%s' nicht öffnen"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "kann Verweis '%s' nicht lesen"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "kann symbolische Verknüpfung '%s' auf '%s' nicht erstellen"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "kann '%s' nicht nach '%s' kopieren"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "ignoriere Vorlage %s"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "Keine Vorlagen in %s gefunden."
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "kopiere keine Vorlagen von '%s': %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "ungültiger initialer Branchname: '%s'"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "kann nicht mit Dateityp %d umgehen"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "Konnte %s nicht nach %s verschieben"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "Versuch, das Repository mit einem anderen Hash zu reinitialisieren"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s existiert bereits"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "Neu-Initialisierung: --initial-branch=%s ignoriert"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "Bestehendes verteiltes Git-Repository in %s%s neuinitialisiert\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Bestehendes Git-Repository in %s%s neuinitialisiert\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Leeres verteiltes Git-Repository in %s%s initialisiert\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Leeres Git-Repository in %s%s initialisiert\n"
-
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<Vorlagenverzeichnis>] [--"
-"shared[=<Berechtigungen>]] [<Verzeichnis>]"
+"git init [-q | --quiet] [--bare] [--template=<Vorlagenverzeichnis>]\n"
+"         [--separate-git-dir <Git-Verzeichnis>] [--object-format=<Format>]\n"
+"         [-b <Branchname> | --initial-branch=<Branchname>]\n"
+"         [--shared[=<Berechtigungen>]] [<Verzeichnis>]"
 
 msgid "permissions"
 msgstr "Berechtigungen"
@@ -7467,11 +7517,14 @@ msgid "--separate-git-dir incompatible with bare repository"
 msgstr "--separate-git-dir nicht kompatibel mit Bare-Repository"
 
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<Token>[(=|:)<Wert>])...] [<Datei>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<Schlüssel>|<Schlüssel-Alias>)"
+"[(=|:)<Wert>])...]\n"
+"                       [--parse] [<Datei>...]"
 
 msgid "edit files in place"
 msgstr "vorhandene Dateien direkt bearbeiten"
@@ -7479,6 +7532,9 @@ msgstr "vorhandene Dateien direkt bearbeiten"
 msgid "trim empty trailers"
 msgstr "kürzt leere Anhänge"
 
+msgid "placement"
+msgstr "Platzierung"
+
 msgid "where to place the new trailer"
 msgstr "wo der neue Anhang platziert wird"
 
@@ -7491,17 +7547,17 @@ msgstr "Aktion, wenn Anhang fehlt"
 msgid "output only the trailers"
 msgstr "nur Anhänge ausgeben"
 
-msgid "do not apply config rules"
-msgstr "Regeln aus Konfiguration nicht anwenden"
+msgid "do not apply trailer.* configuration variables"
+msgstr "trailer.* Konfigurationsvariablen nicht anwenden"
 
-msgid "join whitespace-continued values"
-msgstr "durch Leerzeichen fortgesetzte Werte verbinden"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "mehrzeilige Trailer als einzeilige Werte umformatieren"
 
-msgid "set parsing options"
-msgstr "Optionen für das Parsen setzen"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "Alias für --only-trailers --only-input --unfold"
 
-msgid "do not treat --- specially"
-msgstr "--- nicht speziell behandeln"
+msgid "do not treat \"---\" as the end of input"
+msgstr "\"---\" nicht als Ende der Eingabe behandeln"
 
 msgid "trailer(s) to add"
 msgstr "Anhang/Anhänge hinzufügen"
@@ -7591,6 +7647,10 @@ msgstr "Brauche genau einen Commit-Bereich."
 msgid "not a range"
 msgstr "Kein Commit-Bereich."
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "konnte Datei mit Branchbeschreibung '%s' nicht lesen"
+
 msgid "cover letter needs email format"
 msgstr "Anschreiben benötigt E-Mail-Format"
 
@@ -7690,6 +7750,9 @@ msgid "generate parts of a cover letter based on a branch's description"
 msgstr ""
 "Erzeuge Teile des Deckblattes basierend auf der Beschreibung des Branches"
 
+msgid "use branch description from file"
+msgstr "Branchbeschreibung aus Datei verwenden"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "nutze [<Präfix>] statt [PATCH]"
 
@@ -7853,6 +7916,10 @@ msgstr ""
 "Konnte gefolgten Remote-Branch nicht finden, bitte geben Sie <Upstream> "
 "manuell an.\n"
 
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "konnte Objekt-Informationen über '%s' nicht bestimmen"
+
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "ungültiges ls-files-Format: Element '%s' fängt nicht mit '(' an"
@@ -7963,12 +8030,12 @@ msgstr ""
 
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<Programm>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<Repository> [<Referenzen>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<Schlüssel>]\n"
+"              [--symref] [<Repository> [<Muster>...]]"
 
 msgid "do not print remote URL"
 msgstr "URL des Remote-Repositories nicht ausgeben"
@@ -8002,10 +8069,6 @@ msgstr "zusätzlich zum Objekt die darauf verweisenden Referenzen anzeigen"
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
 msgstr "git ls-tree [<Optionen>] <Commit-Referenz> [<Pfad>...]"
 
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "konnte Objekt-Informationen über '%s' nicht bestimmen"
-
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "ungültiges ls-tree-Format: Element '%s' fängt nicht mit '(' an"
@@ -8101,12 +8164,12 @@ msgstr "git merge-base [-a | --all] <Commit> <Commit>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <Commit>..."
 
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <Commit>..."
-
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <Commit> <Commit>"
 
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <Commit>..."
+
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <Referenz> [<Commit>]"
 
@@ -8135,6 +8198,9 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "Ergebnisse zur Standard-Ausgabe senden"
 
+msgid "use object IDs instead of filenames"
+msgstr "Objekt-IDs anstelle von Dateinamen verwenden"
+
 msgid "use a diff3 based merge"
 msgstr "einen diff3 basierten Merge verwenden"
 
@@ -8159,6 +8225,13 @@ msgstr "keine Warnung bei Konflikten"
 msgid "set labels for file1/orig-file/file2"
 msgstr "Beschriftung für Datei1/orig-Datei/Datei2 setzen"
 
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "Objekt '%s' existiert nicht"
+
+msgid "Could not write object file"
+msgstr "konnte Objektdatei nicht schreiben"
+
 #, c-format
 msgid "unknown option %s"
 msgstr "unbekannte Option: %s"
@@ -8215,9 +8288,37 @@ msgstr "Dateinamen ohne Modi/Oids/Stufen auflisten"
 msgid "allow merging unrelated histories"
 msgstr "erlaube das Zusammenführen von nicht zusammenhängenden Historien"
 
+msgid "perform multiple merges, one per line of input"
+msgstr "mehrere Merges durchführen, eine pro Eingabezeile"
+
+msgid "specify a merge-base for the merge"
+msgstr "Merge-Basis für den Merge angeben"
+
+msgid "option=value"
+msgstr "Option=Wert"
+
+msgid "option for selected merge strategy"
+msgstr "Option für ausgewählte Merge-Strategie"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge ist mit allen anderen Optionen inkompatibel"
 
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "unbekannte Strategie-Option: -X%s"
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base ist inkompatibel mit --stdin"
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "Fehlerhafte Eingabezeile: '%s'."
+
+#, 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>...]"
 
@@ -8278,12 +8379,6 @@ msgstr "Strategie"
 msgid "merge strategy to use"
 msgstr "zu verwendende Merge-Strategie"
 
-msgid "option=value"
-msgstr "Option=Wert"
-
-msgid "option for selected merge strategy"
-msgstr "Option für ausgewählte Merge-Strategie"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
 "Commit-Beschreibung zusammenführen (für einen Merge, der kein Vorspulen war)"
@@ -8344,10 +8439,6 @@ msgstr "Konnte Index nicht schreiben."
 msgid "Not handling anything other than two heads merge."
 msgstr "Es wird nur der Merge von zwei Branches behandelt."
 
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "unbekannte Strategie-Option: -X%s"
-
 #, c-format
 msgid "unable to write %s"
 msgstr "konnte %s nicht schreiben"
@@ -8653,8 +8744,8 @@ msgstr "Ziel existiert bereits"
 msgid "can not move directory into itself"
 msgstr "kann Verzeichnis nicht in sich selbst verschieben"
 
-msgid "cannot move directory over file"
-msgstr "kann Verzeichnis nicht über Datei verschieben"
+msgid "destination already exists"
+msgstr "Ziel existiert bereits"
 
 msgid "source directory is empty"
 msgstr "Quellverzeichnis ist leer"
@@ -8733,22 +8824,26 @@ msgid "git notes [--ref <notes-ref>] [list [<object>]]"
 msgstr "git notes [--ref <Notiz-Referenz>] [list [<Objekt>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <Notiz-Referenz>] add [-f] [--allow-empty] [-m "
-"<Beschreibung> | -F <Datei> | (-c | -C) <Objekt>] [<Objekt>]"
+"git notes [--ref <Notiz-Referenz>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<Absatz-Unterbrechung>] [--[no-]stripspace] [-m "
+"<Nachricht> | -F <Datei> | (-c | -C) <Objekt>] [<Objekt>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <Notiz-Referenz>] copy [-f] <von-Objekt> <nach-Objekt>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <Notiz-Referenz>] append [--allow-empty] [-m <Beschreibung> "
-"| -F <Datei> | (-c | -C) <Objekt>] [<Objekt>]"
+"git notes [--ref <Notiz-Referenz>] append [--allow-empty] [--"
+"[no-]separator|--separator=<Absatz-Unterbrechnung>] [--[no-]stripspace] [-m "
+"<Nachricht> | -F <Datei> | (-c | -C) <Objekt>] [<Object>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref <Notiz-Referenz>] edit [--allow-empty] [<Objekt>]"
@@ -8848,10 +8943,6 @@ msgstr "Fehler beim Lesen des Objektes '%s'."
 msgid "cannot read note data from non-blob object '%s'."
 msgstr "Kann Notiz-Daten nicht von Nicht-Blob Objekt '%s' lesen."
 
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "Fehlerhafte Eingabezeile: '%s'."
-
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
 msgstr "Fehler beim Kopieren der Notizen von '%s' nach '%s'"
@@ -8887,6 +8978,15 @@ msgstr "Speichern leerer Notiz erlauben"
 msgid "replace existing notes"
 msgstr "existierende Notizen ersetzen"
 
+msgid "<paragraph-break>"
+msgstr "<Absatz-Unterbrechung>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "<Absatz-Unterbrechung> zwischen Absätzen einfügen"
+
+msgid "remove unnecessary whitespace"
+msgstr "unnötigen Whitespace entfernen"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -9049,16 +9149,14 @@ msgstr "Notizen von <Notiz-Referenz> verwenden"
 msgid "unknown subcommand: `%s'"
 msgstr "unbekannter Unterbefehl: `%s'"
 
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects --stdout [<Optionen>...] [< <Referenzliste> | < "
-"<Objektliste>]"
+"git pack-objects --stdout [<Optionen>] [< <Referenzliste> | < <Objektliste>]"
 
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects [<Optionen>...] <Basis-Name> [< <Referenzliste> | < "
+"git pack-objects [<Optionen>] <Basis-Name> [< <Referenzliste> | < "
 "<Objektliste>]"
 
 #, c-format
@@ -9250,6 +9348,12 @@ msgstr "nicht unterstützte Index-Version %s"
 msgid "bad index version '%s'"
 msgstr "ungültige Index-Version '%s'"
 
+msgid "show progress meter during object writing phase"
+msgstr "Forschrittsanzeige während des Schreibens von Objekten anzeigen"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "ähnlich zu --all-progress wenn Fortschrittsanzeige darstellt wird"
+
 msgid "<version>[,<offset>]"
 msgstr "<Version>[,<Offset>]"
 
@@ -9410,9 +9514,6 @@ msgid "--thin cannot be used to build an indexable pack"
 msgstr ""
 "--thin kann nicht benutzt werden, um ein indizierbares Paket zu erstellen."
 
-msgid "cannot use --filter without --stdout"
-msgstr "Kann --filter nicht ohne --stdout benutzen."
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "kann --filter nicht mit --stdin-packs benutzen"
 
@@ -9426,9 +9527,6 @@ msgstr "interne Commit-Liste kann nicht gemeinsam mit --cruft verwendet werden"
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "kann --stdin-packs nicht mit --cruft benutzen"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "kann --max-pack-size nicht mit --cruft benutzen"
-
 msgid "Enumerating objects"
 msgstr "Objekte aufzählen"
 
@@ -9454,8 +9552,14 @@ msgstr ""
 "Sie es immer noch verwenden, indem Sie eine E-Mail an\n"
 "<git@vger.kernel.org> senden. Danke.\n"
 
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<Optionen>]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "Ausführung ohne --i-still-use-this verweigert"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <Muster>] [--exclude <Muster>]"
 
 msgid "pack everything"
 msgstr "alles packen"
@@ -9463,6 +9567,24 @@ msgstr "alles packen"
 msgid "prune loose refs (default)"
 msgstr "lose Referenzen entfernen (Standard)"
 
+msgid "references to include"
+msgstr "einzubeziehende Referenzen"
+
+msgid "references to exclude"
+msgstr "Referenzen zum Ausschluss"
+
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+msgid "use the unstable patch-id algorithm"
+msgstr "den instabilen Patch-ID-Algorithmus verwenden"
+
+msgid "use the stable patch-id algorithm"
+msgstr "den stabilen Patch-ID-Algorithmus verwenden"
+
+msgid "don't strip whitespace from the patch"
+msgstr "keine Leerzeichen aus dem Patch entfernen"
+
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr "git prune [-n] [-v] [--progress] [--expire <Zeit>] [--] [<Branch>...]"
 
@@ -9510,6 +9632,12 @@ msgstr "das Überschreiben von lokalen Branches erzwingen"
 msgid "number of submodules pulled in parallel"
 msgstr "Anzahl der parallel mit 'pull' zu verarbeitenden Submodule"
 
+msgid "use IPv4 addresses only"
+msgstr "nur IPv4-Adressen benutzen"
+
+msgid "use IPv6 addresses only"
+msgstr "nur IPv6-Adressen benutzen"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9624,7 +9752,7 @@ msgstr ""
 msgid "pull with rebase"
 msgstr "Pull mit Rebase"
 
-msgid "please commit or stash them."
+msgid "Please commit or stash them."
 msgstr "Bitte committen Sie die Änderungen oder benutzen Sie \"stash\"."
 
 #, c-format
@@ -9690,13 +9818,12 @@ msgstr ""
 
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
-"Um das automatische Konfigurieren von Upstream-Branches zu verhindern,\n"
+"Um das automatische Konfigurieren eines Upstream-Branches zu verhindern,\n"
 "wenn deren Namen nicht mit dem lokalen Branch übereinstimmen, siehe\n"
 "Option 'simple' bei branch.autoSetupMerge in 'git help config'.\n"
 
@@ -9786,35 +9913,37 @@ msgstr ""
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil die Spitze Ihres aktuellen\n"
-"Branches hinter seinem externen Gegenstück zurückgefallen ist. Führen Sie\n"
-"die externen Änderungen zusammen (z. B. 'git pull ...') bevor Sie \"push\"\n"
-"erneut ausführen.\n"
-"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
-"für weitere Details."
+"Branches hinter seinem externen Gegenstück zurückgefallen ist. Wenn Sie\n"
+"die externen Änderungen integrieren wollen, verwenden Sie 'git pull' bevor\n"
+"Sie erneut push ausführen.\n"
+"Siehe auch den Abschnitt 'Note about fast-forwards' in 'git push --help' "
+"für\n"
+"weitere Informationen."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n"
-"Branches hinter seinem externen Gegenstück zurückgefallen ist. Checken Sie\n"
-"diesen Branch aus und führen Sie die externen Änderungen zusammen\n"
-"(z. B. 'git pull ...') bevor Sie erneut \"push\" ausführen.\n"
-"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
-"für weitere Details."
-
-msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Branches hinter seinem externen Gegenstück zurückgefallen ist. Wenn Sie die\n"
+"externen Änderungen integrieren wollen, verwenden Sie 'git pull'\n"
+"bevor Sie erneut push ausführen.\n"
+"Siehe auch den Abschnitt 'Note about fast-forwards' in 'git push --help' "
+"für\n"
+"weitere Informationen."
+
+msgid ""
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil das Remote-Repository Commits "
@@ -9822,11 +9951,12 @@ msgstr ""
 "die lokal nicht vorhanden sind. Das wird üblicherweise durch einen \"push\" "
 "von\n"
 "Commits auf dieselbe Referenz von einem anderen Repository aus verursacht.\n"
-"Vielleicht müssen Sie die externen Änderungen zusammenführen (z. B. 'git "
-"pull ...')\n"
-"bevor Sie erneut \"push\" ausführen.\n"
-"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
-"für weitere Details."
+"Wenn Sie die externen Änderungen integrieren wollen, verwenden Sie 'git "
+"pull'\n"
+"bevor Sie erneut push ausführen.\n"
+"Siehe auch den Abschnitt 'Note about fast-forwards' in 'git push --help' "
+"für\n"
+"weitere Informationen."
 
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr ""
@@ -9843,15 +9973,21 @@ msgstr ""
 "die Option '--force' zu verwenden.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Aktualisierungen wurden zurückgewiesen, weil die Spitze des Remote-\n"
-"Tracking-Branches seit dem letzen Checkout aktualisiert wurde. Sie möchten\n"
-"diese Änderungen vielleicht lokal integrieren (z. B. 'git pull ...') bevor\n"
-"Sie die Änderungen erzwingen.\n"
+"Aktualisierungen wurden zurückgewiesen, weil die Spitze des Remote-"
+"Tracking-\n"
+"Branches seit dem letzten Auschecken aktualisiert wurde. Wenn Sie die "
+"externen\n"
+"Änderungen integrieren wollen, verwenden Sie 'git pull' bevor Sie erneut "
+"push\n"
+"ausführen.\n"
+"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help' für "
+"weitere\n"
+"Informationen."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -9861,6 +9997,13 @@ msgstr "Push nach %s\n"
 msgid "failed to push some refs to '%s'"
 msgstr "Fehler beim Versenden einiger Referenzen nach '%s'"
 
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"Rekursion in Submodule mit push.recurseSubmodules=only; stattdessen "
+"Verwendung von on-demand"
+
 #, c-format
 msgid "invalid value for '%s'"
 msgstr "ungültiger Wert für '%s'"
@@ -9868,8 +10011,8 @@ msgstr "ungültiger Wert für '%s'"
 msgid "repository"
 msgstr "Repository"
 
-msgid "push all refs"
-msgstr "alle Referenzen versenden"
+msgid "push all branches"
+msgstr "alle Branches versenden"
 
 msgid "mirror all refs"
 msgstr "alle Referenzen spiegeln"
@@ -9877,8 +10020,10 @@ msgstr "alle Referenzen spiegeln"
 msgid "delete refs"
 msgstr "Referenzen löschen"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "Tags versenden (kann nicht mit --all oder --mirror verwendet werden)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"Tags versenden (kann nicht mit --all, --branches oder --mirror verwendet "
+"werden)"
 
 msgid "force updates"
 msgstr "Aktualisierung erzwingen"
@@ -9998,13 +10143,16 @@ msgid "need two commit ranges"
 msgstr "Benötige zwei Commit-Bereiche."
 
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<Präfix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<Datei>] (--empty | "
-"<Commit-Referenz1> [<Commit-Referenz2> [<Commit-Referenz3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<Präfix>)\n"
+"              [-u | -i]] [--index-output=<Datei>] [--no-sparse-checkout]\n"
+"              (--empty | <Commit-Referenz1> [<Commit-Referenz2> [<Commit-"
+"Referenz3>]])"
 
 msgid "write resulting index to <file>"
 msgstr "resultierenden Index nach <Datei> schreiben"
@@ -10095,8 +10243,8 @@ msgid "%s requires the merge backend"
 msgstr "%s erfordert das Merge-Backend"
 
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "Konnte 'onto' nicht bestimmen: '%s'"
+msgid "invalid onto: '%s'"
+msgstr "ungültig auf: '%s'"
 
 #, c-format
 msgid "invalid orig-head: '%s'"
@@ -10144,18 +10292,36 @@ msgstr ""
 "Infolge dessen kann Git auf diesen Revisionen Rebase nicht\n"
 "ausführen."
 
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Unbekannter rebase-merges Modus: %s"
+
 #, c-format
 msgid "could not switch to %s"
 msgstr "Konnte nicht zu %s wechseln."
 
+msgid "apply options and merge options cannot be used together"
+msgstr ""
+"Optionen für \"am\" und Optionen für \"merge\" können nicht gemeinsam "
+"verwendet werden"
+
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
 msgstr ""
 "nicht erkannter leerer Typ '%s'; gültige Werte sind \"drop\", \"keep\", und "
 "\"ask\"."
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"--rebase-merges mit einem leeren String-Argument ist veraltet und wird in "
+"einer zukünftigen Version von Git nicht mehr funktionieren. Verwenden Sie "
+"stattdessen --rebase-merges ohne ein Argument, was dasselbe bewirkt."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10381,17 +10547,26 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "Schalter `C' erwartet einen numerischen Wert."
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Unbekannter Modus: %s"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+"apply-Optionen sind mit rebase.autoSquash nicht kompatibel. Erwägen Sie das "
+"Hinzufügen von --no-autosquash"
 
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy erfordert --merge oder --interactive"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+"apply-Optionen sind nicht kompatibel mit rebase.rebaseMerges. Erwägen Sie "
+"das Hinzufügen von --no-rebase-merges"
 
-msgid "apply options and merge options cannot be used together"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
 msgstr ""
-"Optionen für \"am\" und Optionen für \"merge\" können nicht gemeinsam "
-"verwendet werden"
+"apply-Optionen sind nicht kompatibel mit rebase.updateRefs. Erwägen Sie das "
+"Hinzufügen von --no-update-refs"
 
 #, c-format
 msgid "Unknown rebase backend: %s"
@@ -10415,8 +10590,8 @@ msgstr "Branch/Commit '%s' nicht gefunden"
 msgid "No such ref: %s"
 msgstr "Referenz nicht gefunden: %s"
 
-msgid "Could not resolve HEAD to a revision"
-msgstr "Konnte HEAD zu keinem Commit auflösen."
+msgid "Could not resolve HEAD to a commit"
+msgstr "HEAD konnte nicht in einen Commit aufgelöst werden"
 
 #, c-format
 msgid "'%s': need exactly one merge base with branch"
@@ -10430,9 +10605,6 @@ msgstr "'%s': benötige genau eine Merge-Basis"
 msgid "Does not point to a valid commit '%s'"
 msgstr "'%s' zeigt auf keinen gültigen Commit."
 
-msgid "Please commit or stash them."
-msgstr "Bitte committen Sie die Änderungen oder benutzen Sie \"stash\"."
-
 msgid "HEAD is up to date."
 msgstr "HEAD ist aktuell."
 
@@ -10695,11 +10867,12 @@ msgstr "unbekanntes Argument für Option mirror: %s"
 msgid "fetch the remote branches"
 msgstr "die Remote-Branches anfordern"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "alle Tags und verbundene Objekte beim Anfordern importieren"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "oder fordere gar keine Tags an (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"alle Tags und zugehörigen Objekte beim Abruf importieren\n"
+"oder überhaupt keine Tags abrufen (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "Branch(es) zur Übernahme"
@@ -11094,6 +11267,14 @@ msgstr "konnte temporäre Datei '%s' nicht zum Schreiben öffnen"
 msgid "could not close refs snapshot tempfile"
 msgstr "konnte temporäre Referenzen-Snapshot-Datei nicht schließen"
 
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "konnte veraltete Bitmap nicht entfernen: %s"
+
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "Pack-Präfix %s fängt nicht mit objdir %s an"
+
 msgid "pack everything in a single pack"
 msgstr "alles in eine einzige Pack-Datei packen"
 
@@ -11106,8 +11287,8 @@ msgstr "genau wie -a, unerreichbare überflüssige Objekte separat packen"
 msgid "approxidate"
 msgstr "Datumsangabe"
 
-msgid "with -C, expire objects older than this"
-msgstr "mit -C, Objekte älter als angegeben verfallen lassen"
+msgid "with --cruft, expire objects older than this"
+msgstr "mit --cruft, Objekte verfallen lassen, die älter sind als das"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "redundante Pakete entfernen und \"git-prune-packed\" ausführen"
@@ -11169,19 +11350,29 @@ 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"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "kann Pack-Dateien in precious-objects Repository nicht löschen"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "Option '%s' kann nur zusammen mit '%s' verwendet werden"
+
 msgid "Nothing new to pack."
 msgstr "Nichts Neues zum Packen."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "Pack-Präfix %s fängt nicht mit objdir %s an"
+msgid "renaming pack to '%s' failed"
+msgstr "Umbenennung des Pakets in '%s' fehlgeschlagen"
 
 #, c-format
-msgid "missing required file: %s"
-msgstr "benötigte Datei fehlt: %s"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects hat keine '%s' Datei für Paket %s-%s geschrieben"
 
 #, c-format
 msgid "could not unlink: %s"
@@ -11376,8 +11567,11 @@ msgstr "--convert-graft-file erwartet keine Argumente"
 msgid "only one pattern can be given with -l"
 msgstr "Mit -l kann nur ein Muster angegeben werden"
 
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clean | forget <Pfad>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <Pfadspezifikation>... | diff | status | "
+"remaining | gc]"
 
 msgid "register clean resolutions in index"
 msgstr "saubere Auflösungen im Index registrieren"
@@ -11584,24 +11778,45 @@ msgstr "--prefix benötigt ein Argument"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "unbekannter Modus für --abbrev-ref: %s"
 
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden kann nicht zusammen mit --branches verwendet werden"
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden kann nicht zusammen mit --tags verwendet werden"
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden kann nicht zusammen mit --remotes verwendet werden"
+
 msgid "this operation must be run in a work tree"
 msgstr "Diese Operation muss in einem Arbeitsverzeichnis ausgeführt werden."
 
+msgid "Could not read the index"
+msgstr "Konnte den Index nicht lesen"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "unbekannter Modus für --show-object-format: %s"
 
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<Optionen>] <Commit-Angabe>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <Nummer des Elterncommits>] [-s] [-S[<Key-"
+"ID>]] <Commit>…"
 
-msgid "git revert <subcommand>"
-msgstr "git revert <Unterbefehl>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
 
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<Optionen>] <Commit-Angabe>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <Nummer des Elterncommits>] [-s] [-x] [--"
+"ff]\n"
+"                [-S[<Key-ID>]] <Commit>…"
 
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <Unterbefehl>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
@@ -11662,8 +11877,14 @@ msgstr "\"revert\" fehlgeschlagen"
 msgid "cherry-pick failed"
 msgstr "\"cherry-pick\" fehlgeschlagen"
 
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<Optionen>] [--] <Datei>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<Datei> [--pathspec-file-nul]]\n"
+"       [--] [<Pfadspezifikation>...]"
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -11741,16 +11962,21 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<Host>:]<Verzeichnis> (--all | <Referenz>...)"
 
 msgid "remote name"
 msgstr "Name des Remote-Repositories"
 
+msgid "push all refs"
+msgstr "alle Referenzen versenden"
+
 msgid "use stateless RPC protocol"
 msgstr "zustandsloses RPC-Protokoll verwenden"
 
@@ -11769,9 +11995,9 @@ msgstr "git log --pretty=short | git shortlog [<Optionen>]"
 msgid "using multiple --group options with stdin is not supported"
 msgstr "mehrere Optionen --group mit Standard-Eingabe wird nicht unterstützt"
 
-msgid "using --group=trailer with stdin is not supported"
-msgstr ""
-"Nutzung von --group=trailer mit Standard-Eingabe wird nicht unterstützt"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "die Verwendung von %s mit stdin wird nicht unterstützt"
 
 #, c-format
 msgid "unknown group type: %s"
@@ -11808,12 +12034,14 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
-"                [--current] [--color[=<Wann>] | --no-color] [--sparse]\n"
+"                [--current] [--color[=<wann>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<Commit> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<Commit> | <Glob>)...]"
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g | --reflog)[=<n>[,<Basis>]] [--list] [<Referenz>]"
@@ -11914,21 +12142,44 @@ msgid "Unknown hash algorithm"
 msgstr "Unbekannter Hash-Algorithmus"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<Muster>...]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<Muster>...] "
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<Referenz>...]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<Muster>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <Referenz>"
+
+msgid "reference does not exist"
+msgstr "Referenz nicht vorhanden"
+
+msgid "failed to look up reference"
+msgstr "Fehler beim Nachschlagen der Referenz"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "nur Tags anzeigen (kann mit \"heads\" kombiniert werden)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "nur Branches anzeigen (kann mit \"tags\" kombiniert werden)"
 
+msgid "check for reference existence without resolving"
+msgstr "Prüfung auf Vorhandensein einer Referenz, ohne diese aufzulösen"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr "strengere Referenzprüfung, erfordert exakten Referenzpfad"
 
@@ -11951,8 +12202,16 @@ msgstr ""
 "Referenzen von der Standard-Eingabe anzeigen, die sich nicht im lokalen "
 "Repository befinden"
 
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) <Optionen>"
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "es kann nur eines von '%s', '%s' oder '%s' angegeben werden"
+
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<Optionen>]"
 
 msgid "this worktree is not sparse"
 msgstr "dieses Arbeitsverzeichnis ist nicht partiell"
@@ -12083,67 +12342,76 @@ msgstr ""
 msgid "error while refreshing working directory"
 msgstr "Fehler während der Aktualisierung des Arbeitsverzeichnisses."
 
-msgid "git stash list [<options>]"
-msgstr "git stash list [<Optionen>]"
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <Datei>]"
 
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<Optionen>] [<Stash>]"
+msgid "terminate input and output files by a NUL character"
+msgstr "Eingabe- und Ausgabedateien durch ein NUL-Zeichen abschließen"
 
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<Stash>]"
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"bei Verwendung mit --rules-file werden die Muster als Cone-Modus Muster "
+"interpretiert"
 
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<Stash>]"
+msgid "use patterns in <file> instead of the current ones."
+msgstr "Muster aus <Datei> anstelle der aktuellen Muster verwenden"
 
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <Branch> [<Stash>]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<log-Optionen>]"
 
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <Nachricht>]\n"
-"          [--pathspec-from-file=<Datei> [--pathspec-file-nul]]\n"
-"          [--] [<Pfadspezifikation>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<Diff-"
+"Optionen>] [<Stash>]"
 
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<Nachricht>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<Stash>]"
 
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<Stash>]"
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<Stash>]"
 
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<Stash>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<Stash>]"
 
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <Nachricht>] [-q|--quiet] <Commit>"
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <Branch> [<Stash>]"
+
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr ""
+"git stash store [(-m | --message) <Beschreibung>] [-q | --quiet] <Commit>"
 
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <Nachricht>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<Nachricht>]\n"
+"          [--pathspec-from-file=<Datei> [--pathspec-file-nul]]\n"
 "          [--] [<Pfadspezifikation>...]]"
 
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<Nachricht>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<Beschreibung>]"
+
+msgid "git stash create [<message>]"
+msgstr "git stash create [<Beschreibung>]"
 
 #, c-format
 msgid "'%s' is not a stash-like commit"
@@ -12624,6 +12892,10 @@ msgstr "Überspringe nicht zusammengeführtes Submodul %s"
 msgid "Skipping submodule '%s'"
 msgstr "Überspringe Submodul '%s'"
 
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "kann Submodul '%s' nicht ohne URL klonen"
+
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "Fehler beim Klonen von '%s'. Weiterer Versuch geplant"
@@ -12721,9 +12993,6 @@ msgstr "Submodule rekursiv durchlaufen"
 msgid "don't fetch new objects from the remote site"
 msgstr "keine neuen Objekte von Remote abrufen"
 
-msgid "path into the working tree"
-msgstr "Pfad zum Arbeitsverzeichnis"
-
 msgid "use the 'checkout' update strategy (default)"
 msgstr "die Aktualisierungsstrategie \"checkout\" verwenden (Standard)"
 
@@ -12764,29 +13033,12 @@ msgstr ""
 "shallow] [--reference <Repository>] [--recursive] [--[no-]single-branch] "
 "[--] [<Pfad>...]"
 
-msgid "recurse into submodules"
-msgstr "Rekursion in Submodule durchführen"
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Konnte HEAD nicht als gültige Referenz auflösen."
 
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<Optionen>] [<Pfad>...]"
 
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr "prüfen, ob es sicher ist, in die Datei .gitmodules zu schreiben"
-
-msgid "unset the config in the .gitmodules file"
-msgstr "Konfiguration in der .gitmodules-Datei entfernen"
-
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <name> [<Wert>]"
-
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <Name>"
-
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr ""
-"Bitte stellen Sie sicher, dass sich die Datei .gitmodules im "
-"Arbeitsverzeichnis befindet."
-
 msgid "suppress output for setting url of a submodule"
 msgstr "Ausgaben beim Setzen der URL eines Submoduls unterdrücken"
 
@@ -12867,6 +13119,11 @@ msgstr "Reaktiviere lokales Git-Verzeichnis für Submodul '%s'\n"
 msgid "unable to checkout submodule '%s'"
 msgstr "kann Submodul '%s' nicht auschecken"
 
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr ""
+"Bitte stellen Sie sicher, dass sich die Datei .gitmodules im "
+"Arbeitsverzeichnis befindet."
+
 #, c-format
 msgid "Failed to add submodule '%s'"
 msgstr "Hinzufügen von Submodul '%s' fehlgeschlagen"
@@ -12919,19 +13176,17 @@ msgstr "repo URL: '%s' muss absolut sein oder mit ./|../ beginnen"
 msgid "'%s' is not a valid submodule name"
 msgstr "'%s' ist kein gültiger Submodul-Name"
 
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s unterstützt kein --super-prefix"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <Befehl>"
 
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "'%s' ist kein gültiger Unterbefehl von submodule--helper"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <Grund>] <Name> <Referenz>"
 
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<Optionen>] <Name> [<Referenz>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <Name>"
 
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <Name>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <Name>"
 
 msgid "suppress error message for non-symbolic (detached) refs"
 msgstr ""
@@ -12943,6 +13198,9 @@ msgstr "symbolische Referenzen löschen"
 msgid "shorten ref output"
 msgstr "verkürzte Ausgabe der Referenzen"
 
+msgid "recursively dereference (default)"
+msgstr "rekursives Dereferenzieren (Standard)"
+
 msgid "reason"
 msgstr "Grund"
 
@@ -12950,25 +13208,25 @@ msgid "reason of the update"
 msgstr "Grund für die Aktualisierung"
 
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u <Schlüssel-ID>] [-f] [-m <Beschreibung> | -F <Datei>]\n"
-"        <Tagname> [<Commit>]"
+"git tag [-a | -s | -u <Key-ID>] [-f] [-m <Beschreibung> | -F <Datei>] [-e]\n"
+"        <Tagname> [<Commit> | <Objekt>]"
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d <Tagname>..."
 
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[<Nummer>]] [--contains <Commit>] [--no-contains <Commit>] [--"
-"points-at <Objekt>]\n"
-"        [--format=<format>] [--merged <Commit>] [--no-merged <Commit>] "
-"[<Muster>...]"
+"git tag [-n[<Nummer>]] -l [--contains <Commit>] [--no-contains <Commit>]\n"
+"        [--points-at <Objekt>] [--column[=<Optionen>] | --no-column]\n"
+"        [--create-reflog] [--sort=<Schlüssel>] [--format=<Format>]\n"
+"        [--merged <Commit>] [--no-merged <Commit>] [<Muster>...]"
 
 msgid "git tag -v [--format=<format>] <tagname>..."
 msgstr "git tag -v [--format=<Format>] <Tagname>..."
@@ -13261,6 +13519,9 @@ msgstr "(für Fremdprogramme) keine gespeicherten, nicht aufgelöste Konflikte"
 msgid "write index in this format"
 msgstr "Index-Datei in diesem Format schreiben"
 
+msgid "report on-disk index format version"
+msgstr "Bericht über die Version des Indexformats auf der Festplatte"
+
 msgid "enable or disable split index"
 msgstr "aufgeteilten Index aktivieren oder deaktivieren"
 
@@ -13287,6 +13548,14 @@ msgstr "Dateien als \"fsmonitor valid\" markieren"
 msgid "clear fsmonitor valid bit"
 msgstr "\"fsmonitor valid\"-Bit löschen"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: war %d, wurde auf %d gesetzt"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13367,8 +13636,12 @@ msgstr "Aktualisierungen von der Standard-Eingabe lesen"
 msgid "update the info files from scratch"
 msgstr "die Informationsdateien von Grund auf aktualisieren"
 
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<Optionen>] <Verzeichnis>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
 
 msgid "quit after a single request/response exchange"
 msgstr "nach einem einzigen Request/Response-Austausch beenden"
@@ -13384,8 +13657,8 @@ msgstr ""
 msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "Übertragung nach <n> Sekunden Inaktivität unterbrechen"
 
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <Commit>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | --verbose] [--raw] <Commit>..."
 
 msgid "print commit contents"
 msgstr "Commit-Inhalte ausgeben"
@@ -13393,8 +13666,9 @@ msgstr "Commit-Inhalte ausgeben"
 msgid "print raw gpg status output"
 msgstr "unbearbeitete Ausgabe des Status von gpg ausgeben"
 
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <Paket>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr ""
+"git verify-pack [-v | --verbose] [-s | --stat-only] [--] <Paket>.idx..."
 
 msgid "verbose"
 msgstr "erweiterte Ausgaben"
@@ -13402,35 +13676,76 @@ msgstr "erweiterte Ausgaben"
 msgid "show statistics only"
 msgstr "nur Statistiken anzeigen"
 
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<Format>] <Tag>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<Format>] [--raw] <Tag>..."
 
 msgid "print tag contents"
 msgstr "Tag-Inhalte ausgeben"
 
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<Optionen>] <Pfad> [<Commit-Angabe>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason "
+"<Zeichenkette>]]\n"
+"                 [--orphan] [(-b | -B) <neuer-Branch>] <Pfad> [<Commit-"
+"Angabe>]"
 
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<Optionen>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
 
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<Optionen>] <Pfad>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <Zeichenkette>] <Arbeitsverzeichnis>"
 
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <Arbeitsverzeichnis> <neuer-Pfad>"
 
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<Optionen>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <Zeit>]"
 
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<Optionen>] <Arbeitsverzeichnis>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <Arbeitsverzeichnis>"
 
 msgid "git worktree repair [<path>...]"
 msgstr "git worktree repair [<Pfad>...]"
 
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <Pfad>"
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <Arbeitsverzeichnis>"
+
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Kein möglicher Quell-Branch, der auf '--orphan' schließen lässt"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Wenn Sie ein Arbeitsverzeichnis erstellen möchten, um einen neuen verwaisten "
+"Branch\n"
+"(Branch ohne Commits) für dieses Repository zu erstellen, können Sie dies "
+"mit\n"
+"der Option --orphan tun:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Wenn Sie ein Arbeitsverzeichnis erstellen möchten, um einen neuen verwaisten "
+"Branch\n"
+"(Branch ohne Commits) für dieses Repository zu erstellen, können Sie dies "
+"mit\n"
+"der Option --orphan tun:\n"
+"\n"
+"    git worktree add --orphan %s\n"
 
 #, c-format
 msgid "Removing %s/%s: %s"
@@ -13502,10 +13817,38 @@ msgstr "Bereite Arbeitsverzeichnis vor (setze Branch '%s' um; war bei %s)"
 msgid "Preparing worktree (checking out '%s')"
 msgstr "Bereite Arbeitsverzeichnis vor (checke '%s' aus)"
 
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "unerreichbar: ungültige Referenz: %s"
+
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Bereite Arbeitsverzeichnis vor (losgelöster HEAD %s)"
 
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD zeigt auf eine ungültige (oder verwaiste) Referenz.\n"
+"HEAD-Pfad: '%s'\n"
+"HEAD Inhalte: '%s'"
+
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"Es gibt keine lokalen oder entfernten Referenzen, obwohl mindestens ein "
+"Remote-Repository\n"
+"vorhanden ist. Angehalten. Verwenden Sie 'add -f', um eine entfernte "
+"Referenz zu überschreiben\n"
+"oder rufen Sie diese zuerst ab"
+
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "'%s' und '%s' können nicht zusammen verwendet werden"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
 "<Branch> auschecken, auch wenn dieser bereits in einem anderen "
@@ -13517,6 +13860,9 @@ msgstr "neuen Branch erstellen"
 msgid "create or reset a branch"
 msgstr "Branch erstellen oder umsetzen"
 
+msgid "create unborn/orphaned branch"
+msgstr "ungeborenen/verwaisten Branch erstellen"
+
 msgid "populate the new working tree"
 msgstr "das neue Arbeitsverzeichnis auschecken"
 
@@ -13539,6 +13885,13 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr ""
 "die Optionen '%s', '%s' und '%s' können nicht gemeinsam verwendet werden"
 
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "die Optionen '%s' und '%s' können nicht gemeinsam verwendet werden"
+
+msgid "<commit-ish>"
+msgstr "<Commit-Angabe>"
+
 msgid "added with --lock"
 msgstr "mit --lock hinzugefügt"
 
@@ -13681,23 +14034,58 @@ msgstr "nur nützlich für Fehlersuche"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "core.fsyncMethod = batch wird auf dieser Plattform nicht unterstützt"
 
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "Konnte Bundle-Listenschlüssel %s mit Wert '%s' nicht parsen."
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "Paketliste bei '%s' hat keinen Modus"
+
 msgid "failed to create temporary file"
 msgstr "temporäre Datei kann nicht erstellt werden"
 
 msgid "insufficient capabilities"
 msgstr "unzureichende Fähigkeiten"
 
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "die von '%s' heruntergeladene Datei ist kein Bundle"
+
+msgid "failed to store maximum creation token"
+msgstr "das maximale Erstellungs-Token konnte nicht gespeichert werden"
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "nicht erkannter Bundle-Modus von URI '%s'"
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "Rekursionsgrenze für Bundle-URI überschritten (%d)"
+
 #, c-format
 msgid "failed to download bundle from URI '%s'"
 msgstr "Download des Bundles von URI '%s' fehlgeschlagen"
 
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "Datei unter URI '%s' ist kein Bundle"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "Datei unter URI '%s' ist kein Paket oder keine Paketliste"
 
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "Bundle von URI '%s' konnte nicht entpackt werden"
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: unerwartetes Argument: '%s'"
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: erwarteter Flush nach Argumenten"
+
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: erhielt eine leere Zeile"
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: Zeile hat nicht die Form 'Schlüssel=Wert'"
+
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: Zeile hat leeren Schlüssel oder Wert"
 
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
@@ -13721,6 +14109,13 @@ msgstr "Dem Repository fehlen folgende vorausgesetzte Commits:"
 msgid "need a repository to verify a bundle"
 msgstr "um ein Paket zu überprüfen wird ein Repository benötigt"
 
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"einige vorausgesetzte Commits sind im Objektspeicher vorhanden, aber sind "
+"nicht mit der Repository-Historie verbunden"
+
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -13736,6 +14131,14 @@ msgid_plural "The bundle requires these %<PRIuMAX> refs:"
 msgstr[0] "Das Paket benötigt diese Referenz:"
 msgstr[1] "Das Paket benötigt diese %<PRIuMAX> Referenzen:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Das Paket verwendet diesen Hash-Algorithmus: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Das Paket verwendet diesen Filter: %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "konnte dup für Descriptor des Pakets nicht ausführen"
 
@@ -13770,6 +14173,10 @@ msgstr "Erstellung der Paketindexdatei abgebrochen"
 msgid "terminating chunk id appears earlier than expected"
 msgstr "abschließende Chunk-ID erscheint eher als erwartet"
 
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "Chunk id %<PRIx32> nicht %d-byte-aligned"
+
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "unzulässige(r) Chunk-Offset(s) %<PRIx64> und %<PRIx64>"
@@ -13827,10 +14234,8 @@ msgstr ""
 msgid "Move objects and refs by archive"
 msgstr "Objekte und Referenzen über ein Archiv verteilen"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Inhalt oder Informationen zu Typ und Größe für Repository-Objekte "
-"bereitstellen"
+msgid "Provide contents or details of repository objects"
+msgstr "Bereitstellung von Inhalten oder Details von Repository-Objekten"
 
 msgid "Display gitattributes information"
 msgstr "gitattributes Informationen darstellen"
@@ -13975,9 +14380,10 @@ msgstr "Zeilen darstellen, die einem Muster entsprechen"
 msgid "A portable graphical interface to Git"
 msgstr "eine portable grafische Schnittstelle zu Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
+msgid "Compute object ID and optionally create an object from a file"
 msgstr ""
-"von einer Datei die Objekt-ID berechnen und optional ein Blob erstellen"
+"Berechnung der Objekt-ID und optionales Erstellen eines Objekts aus einer "
+"Datei"
 
 msgid "Display help information about Git"
 msgstr "Hilfsinformationen über Git anzeigen"
@@ -14261,8 +14667,8 @@ msgstr "die GPG-Signatur von Tags prüfen"
 msgid "Display version information about Git"
 msgstr "Versionsinformationen über Git anzeigen"
 
-msgid "Show logs with difference each commit introduces"
-msgstr "Logs mit dem Unterschied, den jeder Commit einführt, anzeigen"
+msgid "Show logs with differences each commit introduces"
+msgstr "Logs mit den Unterschieden anzeigen, den jeder Commit einführt"
 
 msgid "Manage multiple working trees"
 msgstr "mehrere Arbeitsverzeichnisse verwalten"
@@ -14301,8 +14707,8 @@ msgstr "Das Bundle-Dateiformat"
 msgid "Chunk-based file formats"
 msgstr "Chunk-basierte Dateiformate"
 
-msgid "Git commit graph format"
-msgstr "Git Commit Graph Format"
+msgid "Git commit-graph format"
+msgstr "Git Commit-Graph Format"
 
 msgid "Git index format"
 msgstr "Git-Index-Format"
@@ -14398,9 +14804,16 @@ msgstr "Commit-Graph-Datei ist zu klein, um %u Chunks zu enthalten"
 msgid "commit-graph has no base graphs chunk"
 msgstr "Commit-Graph hat keinen Basis-Graph-Chunk"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "Commit-Graph Basis-Graph-Chunk ist zu klein"
+
 msgid "commit-graph chain does not match"
 msgstr "Commit-Graph Verkettung stimmt nicht überein"
 
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "Anzahl der Commits im Basisgraph zu hoch: %<PRIuMAX>"
+
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr "Ungültige Commit-Graph Verkettung: Zeile '%s' ist kein Hash"
@@ -14418,6 +14831,9 @@ msgstr "konnte Commit %s nicht finden"
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "Commit-Graph erfordert Überlaufgenerierungsdaten, aber hat keine"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr "Commit-Graph Überlaufgenerierungsdaten sind zu klein"
+
 msgid "Loading known commits in commit graph"
 msgstr "Lade bekannte Commits in Commit-Graph"
 
@@ -14484,6 +14900,17 @@ msgstr "konnte Basis-Commit-Graph-Datei nicht umbenennen"
 msgid "failed to rename temporary commit-graph file"
 msgstr "konnte temporäre Commit-Graph-Datei nicht umbenennen"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr ""
+"Graphen mit %<PRIuMAX>, %<PRIuMAX> Commits können nicht zusammengeführt "
+"werden"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr ""
+"Graph %s kann nicht zusammengeführt werden, zu viele Commits: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Durchsuche zusammengeführte Commits"
 
@@ -14515,9 +14942,6 @@ msgstr "Commit-Graph hat fehlerhaften Fanout-Wert: fanout[%d] = %u != %u"
 msgid "failed to parse commit %s from commit-graph"
 msgstr "konnte Commit %s von Commit-Graph nicht parsen"
 
-msgid "Verifying commits in commit graph"
-msgstr "Commit in Commit-Graph überprüfen"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14540,20 +14964,6 @@ msgstr "Commit-Graph-Vorgänger für %s ist %s != %s"
 msgid "commit-graph parent list for commit %s terminates early"
 msgstr "Commit-Graph Vorgänger-Liste für Commit %s endet zu früh"
 
-#, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"Commit-Graph hat Generationsnummer null für Commit %s, aber sonst ungleich "
-"null"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"Commit-Graph hat Generationsnummer ungleich null für Commit %s, aber sonst "
-"null"
-
 #, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr "Commit-Graph Erstellung für Commit %s ist %<PRIuMAX> < %<PRIuMAX>"
@@ -14563,6 +14973,17 @@ msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
 msgstr ""
 "Commit-Datum für Commit %s in Commit-Graph ist %<PRIuMAX> != %<PRIuMAX>"
 
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"Commit-Graph hat sowohl Null- als auch Nicht-Null-Generationen (z. B. "
+"Commits '%s' und '%s')"
+
+msgid "Verifying commits in commit graph"
+msgstr "Commit in Commit-Graph überprüfen"
+
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s ist kein Commit!"
@@ -14587,6 +15008,11 @@ msgstr ""
 "Sie können diese Meldung unterdrücken, indem Sie\n"
 "\"git config advice.graftFileDeprecated false\" ausführen."
 
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"Commit %s existiert im Commit-Graphen, aber nicht in der Objektdatenbank"
+
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr ""
@@ -14653,6 +15079,10 @@ msgstr "unbehandelter Fall in 'has_worktree_moved': %d"
 msgid "health thread wait failed [GLE %ld]"
 msgstr "Warten des health Thread fehlgeschlagen [GLE %ld]"
 
+#, c-format
+msgid "Invalid path: %s"
+msgstr "Ungültiger Pfad: %s"
+
 msgid "Unable to create FSEventStream."
 msgstr "Konnte FSEventStream nicht erstellen."
 
@@ -14683,6 +15113,22 @@ msgstr "GetOverlappedResult fehlgeschlagen auf '%s' [GLE %ld]"
 msgid "could not read directory changes [GLE %ld]"
 msgstr "konnte Verzeichnisveränderungen nicht lesen [GLE %ld]"
 
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "opendir('%s') fehlgeschlagen"
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "lstat('%s') fehlgeschlagen"
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "strbuf_readlink('%s') fehlgeschlagen"
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "closedir('%s') fehlgeschlagen"
+
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
 msgstr "[GLE %ld] '%ls' kann nicht zum Lesen geöffnet werden"
@@ -14977,8 +15423,8 @@ msgstr "Länge für Abkürzung von Commit-IDs außerhalb des Bereichs: %d"
 msgid "bad zlib compression level %d"
 msgstr "ungültiger zlib Komprimierungsgrad %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar sollte nur ein Zeichen sein"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar sollte nur ein ASCII-Zeichen sein"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -15015,10 +15461,6 @@ msgstr "Referenz '%s' zeigt auf keinen Blob."
 msgid "unable to resolve config blob '%s'"
 msgstr "Konnte Blob '%s' für Konfiguration nicht auflösen."
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "Fehler beim Parsen von %s."
-
 msgid "unable to parse command-line config"
 msgstr ""
 "Konnte die über die Befehlszeile angegebene Konfiguration nicht parsen."
@@ -15095,6 +15537,11 @@ msgstr "Konnte '%s' nicht zu '%s' setzen."
 msgid "invalid section name: %s"
 msgstr "Ungültiger Sektionsname: %s"
 
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"Ausführung mit übermäßig langer Zeile in '%s' bei Zeile %<PRIuMAX> verweigert"
+
 #, c-format
 msgid "missing value for '%s'"
 msgstr "Fehlender Wert für '%s'"
@@ -15152,6 +15599,16 @@ msgstr "Protokollfehler: unerwartetes '%s'"
 msgid "unknown object format '%s' specified by server"
 msgstr "unbekanntes Objekt-Format '%s' vom Server angegeben"
 
+#, c-format
+msgid "error on bundle-uri response line %d: %s"
+msgstr "Fehler in der bundle-uri-Antwortzeile %d: %s"
+
+msgid "expected flush after bundle-uri listing"
+msgstr "erwartete Flush nach Bundle-uri-Auflistung"
+
+msgid "expected response end packet after ref listing"
+msgstr "Antwort-Endpaket nach Auflistung der Referenzen erwartet"
+
 #, c-format
 msgid "invalid ls-refs response: %s"
 msgstr "ungültige ls-refs Antwort: %s"
@@ -15159,9 +15616,6 @@ msgstr "ungültige ls-refs Antwort: %s"
 msgid "expected flush after ref listing"
 msgstr "Flush nach Auflistung der Referenzen erwartet"
 
-msgid "expected response end packet after ref listing"
-msgstr "Antwort-Endpaket nach Auflistung der Referenzen erwartet"
-
 #, c-format
 msgid "protocol '%s' is not supported"
 msgstr "Protokoll '%s' wird nicht unterstützt"
@@ -15486,9 +15940,6 @@ msgstr "Schreiben des Archivs fehlgeschlagen"
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base funktioniert nicht mit Bereichen"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base funktioniert nur mit Commits"
-
 msgid "unable to get HEAD"
 msgstr "konnte HEAD nicht bekommen"
 
@@ -15498,6 +15949,12 @@ msgstr "keine Merge-Basis gefunden"
 msgid "multiple merge bases found"
 msgstr "mehrere Merge-Basen gefunden"
 
+msgid "cannot compare stdin to a directory"
+msgstr "kann stdin nicht mit einem Verzeichnis vergleichen"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "kann eine benannte Pipe nicht mit einem Verzeichnis vergleichen"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<Optionen>] <Pfad> <Pfad>"
 
@@ -15555,6 +16012,13 @@ msgstr ""
 msgid "external diff died, stopping at %s"
 msgstr "externes Diff-Programm unerwartet beendet, angehalten bei %s"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow erfordert genau eine Pfadspezifikation"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "Magie von Pfadspezifikationen wird von --follow nicht unterstützt: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr ""
@@ -15574,9 +16038,6 @@ msgstr ""
 "die Optionen '%s' und '%s' können nicht gemeinsam verwendet werden, nutzen "
 "Sie '%s' mit '%s' und '%s'"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow erfordert genau eine Pfadspezifikation"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "Ungültiger --stat Wert: %s"
@@ -15769,6 +16230,9 @@ msgstr "einen zusätzlichen Präfix bei jeder Ausgabezeile voranstellen"
 msgid "do not show any source or destination prefix"
 msgstr "keine Quell- oder Ziel-Präfixe anzeigen"
 
+msgid "use default prefixes a/ and b/"
+msgstr "Standardpräfixe a/ und b/ verwenden"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "Kontext zwischen Unterschied-Blöcken bis zur angegebenen Anzahl von Zeilen "
@@ -16083,6 +16547,14 @@ msgstr "Konnte Git-Verzeichnis nicht von '%s' nach '%s' migrieren"
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "Hinweis: Warte auf das Schließen der Datei durch Ihren Editor...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "Konnte nicht nach '%s' schreiben."
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "Konnte '%s' nicht editieren."
+
 msgid "Filtering content"
 msgstr "Filtere Inhalt"
 
@@ -16326,9 +16798,11 @@ msgstr "virtuelles Repository '%s' ist inkompatibel mit fsmonitor"
 
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
-"Repository '%s' is inkompatibel mit fsmonitor wegen fehlender Unix sockets"
+"Socket-Verzeichnis '%s' ist mit fsmonitor inkompatibel, da es keine Unix-"
+"Sockets unterstützt"
 
 msgid ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
@@ -16336,16 +16810,14 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 "git [-v | --version] [-h | --help] [-C <Pfad>] [-c <Name>=<Wert>]\n"
 "           [--exec-path[=<Pfad>]] [--html-path] [--man-path] [--info-path]\n"
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<Pfad>] [--work-tree=<Pfad>] [--namespace=<Name>]\n"
-"           [--super-prefix=<Pfad>] [--config-env=<Name>=<Variable>]\n"
-"           <Befehl> [<Argumente>]"
+"           [--config-env=<Name>=<Umgebungsvariable>] <Befehl> [<Argumente>]"
 
 msgid ""
 "'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -16371,10 +16843,6 @@ msgstr "kein Verzeichnis für Option '%s' angegeben\n"
 msgid "no namespace given for --namespace\n"
 msgstr "Kein Namespace für --namespace angegeben.\n"
 
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "Kein Präfix für --super-prefix angegeben.\n"
-
 #, c-format
 msgid "-c expects a configuration string\n"
 msgstr "-c erwartet einen Konfigurationsstring.\n"
@@ -16383,6 +16851,10 @@ msgstr "-c erwartet einen Konfigurationsstring.\n"
 msgid "no config key given for --config-env\n"
 msgstr "kein Konfigurationsschlüssel für --config-env angegeben\n"
 
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "keine Attributquelle für --attr-source angegeben\n"
+
 #, c-format
 msgid "unknown option: %s\n"
 msgstr "Unbekannte Option: %s\n"
@@ -16489,8 +16961,13 @@ msgstr ""
 msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "gpg.ssh.defaultKeyCommand fehlgeschlagen: %s %s"
 
-msgid "gpg failed to sign the data"
-msgstr "gpg beim Signieren der Daten fehlgeschlagen"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg konnte die Daten nicht signieren:\n"
+"%s"
 
 msgid "user.signingKey needs to be set for ssh signing"
 msgstr "user.signingKey muss für die SSH-Signatur gesetzt sein"
@@ -16653,8 +17130,8 @@ msgstr[1] ""
 "\n"
 "Die ähnlichsten Befehle sind"
 
-msgid "git version [<options>]"
-msgstr "git version [<Optionen>]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #, c-format
 msgid "%s: %s - %s"
@@ -16909,12 +17386,12 @@ msgstr ""
 "sind vorhanden:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
+msgid "failed to execute internal merge"
 msgstr "Fehler bei Ausführung des internen Merges"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "Konnte %s nicht zur Datenbank hinzufügen"
+msgid "unable to add %s to database"
+msgstr "konnte %s nicht zur Datenbank hinzufügen"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -17070,7 +17547,7 @@ msgstr ""
 #. conflict in a submodule. The first argument is the submodule
 #. name, and the second argument is the abbreviated id of the
 #. commit that needs to be merged.  For example:
-#. - go to submodule (mysubmodule), and either merge commit abc1234"
+#.  - go to submodule (mysubmodule), and either merge commit abc1234"
 #.
 #, c-format
 msgid ""
@@ -17272,8 +17749,8 @@ msgstr ""
 
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
 "KONFLIKT (umbenennen/umbenennen): Benenne um \"%s\"->\"%s\" in Branch \"%s\" "
 "und \"%s\"->\"%s\" in Branch \"%s\"%s"
@@ -17371,6 +17848,12 @@ msgstr "Lesen des Zwischenspeichers fehlgeschlagen"
 msgid "multi-pack-index OID fanout is of the wrong size"
 msgstr "Multi-Pack-Index OID fanout hat die falsche Größe"
 
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "multi-pack-index OID-Lookup-Chunk hat die falsche Größe"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "multi-pack-index Object-Offset-Chunk hat die falsche Größe"
+
 #, c-format
 msgid "multi-pack-index file %s is too small"
 msgstr "Multi-Pack-Index-Datei %s ist zu klein."
@@ -17388,17 +17871,24 @@ msgstr "Multi-Pack-Index-Version %d nicht erkannt."
 msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "Multi-Pack-Index Hash-Version %u stimmt nicht mit Version %u überein"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "Multi-Pack-Index fehlt erforderlicher Pack-Namen Chunk"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"multi-pack-index erforderlicher Pack-Name Chunk fehlt oder ist beschädigt"
+
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"multi-pack-index erforderlicher OID-Fanout-Chunk fehlt oder ist beschädigt"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "Multi-Pack-Index fehlt erforderlicher OID fanout Chunk"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"multi-pack-index erforderlicher OID Lookup Chunk fehlt oder ist beschädigt"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "Multi-Pack-Index fehlt erforderlicher OID lookup Chunk"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"multi-pack-index benötigte Objekt Offsets Chunk fehlt oder ist beschädigt"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "Multi-Pack-Index fehlt erforderlicher Objekt offset Chunk"
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "multi-pack-index Pack-Name Chunk ist zu klein"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17412,6 +17902,9 @@ msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr ""
 "Multi-Pack-Index speichert einen 64-Bit Offset, aber off_t ist zu klein"
 
+msgid "multi-pack-index large offset out of bounds"
+msgstr "multi-pack-index großer Offset außerhalb der Grenzen"
+
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "Fehler beim Hinzufügen von Packdatei '%s'"
@@ -17601,10 +18094,6 @@ msgstr "Konnte alternativen Objektpfad '%s' nicht normalisieren."
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s: ignoriere alternative Objektspeicher - Verschachtelung zu tief"
 
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "Konnte Objektverzeichnis '%s' nicht normalisieren."
-
 msgid "unable to fdopen alternates lockfile"
 msgstr "Konnte fdopen nicht auf Lock-Datei für \"alternates\" aufrufen."
 
@@ -17667,6 +18156,10 @@ msgstr "Fehlerhaftes loses Objekt '%s'."
 msgid "garbage at end of loose object '%s'"
 msgstr "Nutzlose Daten am Ende von losem Objekt '%s'."
 
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "loses Objekt %s kann nicht geöffnet werden"
+
 #, c-format
 msgid "unable to parse %s header"
 msgstr "Konnte %s Kopfbereich nicht parsen."
@@ -17683,17 +18176,13 @@ msgid "header for %s too long, exceeds %d bytes"
 msgstr "Header für %s zu lang, überschreitet %d Bytes"
 
 #, c-format
-msgid "failed to read object %s"
-msgstr "Konnte Objekt %s nicht lesen."
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "Loses Objekt %s (gespeichert in %s) ist beschädigt."
 
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "Ersetzung %s für %s nicht gefunden."
 
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "Loses Objekt %s (gespeichert in %s) ist beschädigt."
-
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr "Gepacktes Objekt %s (gespeichert in %s) ist beschädigt."
@@ -17706,9 +18195,6 @@ msgstr "Konnte Datei %s nicht schreiben."
 msgid "unable to set permission to '%s'"
 msgstr "Konnte Zugriffsberechtigung auf '%s' nicht setzen."
 
-msgid "file write error"
-msgstr "Fehler beim Schreiben einer Datei."
-
 msgid "error when closing loose object file"
 msgstr "Fehler beim Schließen der Datei für lose Objekte."
 
@@ -17756,11 +18242,12 @@ msgstr "Verzeichnis %s kann nicht erstellt werden"
 msgid "cannot read object for %s"
 msgstr "Kann Objekt für %s nicht lesen."
 
-msgid "corrupt commit"
-msgstr "fehlerhafter Commit"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "fsck schlägt bei Objekt fehl: %s"
 
-msgid "corrupt tag"
-msgstr "fehlerhaftes Tag"
+msgid "refusing to create malformed object"
+msgstr "verweigere Erstellung eines ungültigen Objekts"
 
 #, c-format
 msgid "read error while indexing %s"
@@ -17817,7 +18304,7 @@ msgstr "%s [ungültiges Objekt]"
 #. TRANSLATORS: This is a line of ambiguous commit
 #. object output. E.g.:
 #. *
-#. "deadbeef commit 2021-01-01 - Some Commit Message"
+#.    "deadbeef commit 2021-01-01 - Some Commit Message"
 #.
 #, c-format
 msgid "%s commit %s - %s"
@@ -17826,7 +18313,7 @@ msgstr "%s Commit %s - %s"
 #. TRANSLATORS: This is a line of ambiguous
 #. tag object output. E.g.:
 #. *
-#. "deadbeef tag 2022-01-01 - Some Tag Message"
+#.    "deadbeef tag 2022-01-01 - Some Tag Message"
 #. *
 #. The second argument is the YYYY-MM-DD found
 #. in the tag.
@@ -17842,7 +18329,7 @@ msgstr "%s Tag %s - %s"
 #. tag object output where we couldn't parse
 #. the tag itself. E.g.:
 #. *
-#. "deadbeef [bad tag, could not parse it]"
+#.    "deadbeef [bad tag, could not parse it]"
 #.
 #, c-format
 msgid "%s [bad tag, could not parse it]"
@@ -18028,10 +18515,6 @@ msgstr "ungültiger XOR-Offset im Bitmap-Pack-Index"
 msgid "cannot fstat bitmap file"
 msgstr "kann Bitmap-Datei nicht lesen"
 
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "ignoriere zusätzliche Bitmap-Datei: '%s'"
-
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "Prüfsumme stimmt in MIDX und Bitmap nicht überein"
 
@@ -18101,6 +18584,10 @@ msgstr "konnte '%s' in Paket '%s' bei Offset %<PRIuMAX> nicht finden"
 msgid "unable to get disk usage of '%s'"
 msgstr "Festplattennutzung von '%s' kann nicht abgerufen werden"
 
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "Bitmap-Datei '%s' hat eine ungültige Prüfsumme"
+
 #, c-format
 msgid "mtimes file %s is too small"
 msgstr "mtimes-Datei %s ist zu klein"
@@ -18141,6 +18628,16 @@ msgstr "Reverse-Index-Datei %s hat nicht unterstützte Version %<PRIu32>"
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "Reverse-Index-Datei %s hat nicht unterstützte Hash-ID %<PRIu32>"
 
+msgid "invalid checksum"
+msgstr "ungültige Prüfsumme"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "ungültige rev-index Position bei %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "multi-pack-index Reverse-Index Chunk hat die falsche Größe"
+
 msgid "cannot both write and verify reverse index"
 msgstr ""
 "Reverse-Index kann nicht gleichzeitig geschrieben und verifiziert werden"
@@ -18192,14 +18689,6 @@ msgstr "Option `%s' erwartet \"%s\" oder \"%s\""
 msgid "%s requires a value"
 msgstr "%s erfordert einen Wert."
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s ist inkompatibel mit %s."
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s: inkompatibel mit etwas anderem"
-
 #, c-format
 msgid "%s takes no value"
 msgstr "%s erwartet keinen Wert"
@@ -18214,6 +18703,10 @@ msgstr ""
 "%s erwartet einen nicht-negativen Integer-Wert mit einem optionalen k/m/g "
 "Suffix"
 
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s ist inkompatibel mit %s."
+
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "Mehrdeutige Option: %s (kann --%s%s oder --%s%s sein)"
@@ -18285,6 +18778,10 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-NUM"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "Gegenteil von --no-%s"
+
 msgid "expiry-date"
 msgstr "Verfallsdatum"
 
@@ -18300,6 +18797,9 @@ msgstr "weniger Ausgaben"
 msgid "use <n> digits to display object names"
 msgstr "benutze <Anzahl> Ziffern zur Anzeige von Objektnamen"
 
+msgid "prefixed path to initial superproject"
+msgstr "vorangestellter Pfad zum ursprünglichen Superprojekt"
+
 msgid "how to strip spaces and #comments from message"
 msgstr ""
 "wie Leerzeichen und #Kommentare von der Beschreibung getrennt werden sollen"
@@ -18312,6 +18812,14 @@ msgid ""
 msgstr ""
 "Mit der Option --pathspec-from-file sind Pfade durch NUL-Zeichen getrennt"
 
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "falscher boolescher Wert von Umgebungsvariable '%s' für '%s'"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "Fehler beim Parsen von %s."
+
 #, c-format
 msgid "Could not make %s writable by group"
 msgstr "Konnte Gruppenschreibrecht für %s nicht setzen."
@@ -18360,6 +18868,10 @@ msgstr "nicht unterstützte Pfadspezifikationsangabe '%c' in '%s'"
 msgid "%s: 'literal' and 'glob' are incompatible"
 msgstr "%s: 'literal' und 'glob' sind inkompatibel"
 
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s' liegt außerhalb des Verzeichnisbaums"
+
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: '%s' liegt außerhalb des Repositories von '%s'"
@@ -18450,6 +18962,10 @@ msgstr ""
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr "Promisor-Remote-Name kann nicht mit '/' beginnen: %s"
 
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "konnte %s nicht von Promisor-Remote abrufen"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info: erwartete Flush nach Argumenten"
 
@@ -18485,6 +19001,13 @@ msgstr "Fehler beim Generieren des Diffs."
 msgid "could not parse log for '%s'"
 msgstr "Konnte Log für '%s' nicht parsen."
 
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "ungültiger zusätzlicher Schrotthinweis: '%s'"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "zusätzliche neue Objekte konnten nicht aufgezählt werden"
+
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr ""
@@ -18618,10 +19141,6 @@ msgstr "kann aufgeteilten Index nicht für ein Sparse-Index schreiben"
 msgid "failed to convert to a sparse-index"
 msgstr "Konvertierung zu einem Sparse-Index fehlgeschlagen"
 
-#, c-format
-msgid "could not stat '%s'"
-msgstr "Konnte '%s' nicht lesen."
-
 #, c-format
 msgid "unable to open git dir: %s"
 msgstr "konnte Git-Verzeichnis nicht öffnen: %s"
@@ -18638,6 +19157,14 @@ msgstr "Konnte Zugriffsberechtigung auf '%s' nicht setzen."
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: Kann nicht auf Stufe #0 wechseln."
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "unerwarteter Differenz-Status %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "lösche '%s'\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -18802,6 +19329,14 @@ msgstr "%d hinterher"
 msgid "ahead %d, behind %d"
 msgstr "%d voraus, %d hinterher"
 
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) nimmt keine Argumente entgegen"
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "nicht erkanntes %%(%.*s) Argument: %s"
+
 #, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "Erwartetes Format: %%(color:<Farbe>)"
@@ -18819,41 +19354,37 @@ msgid "Integer value expected refname:rstrip=%s"
 msgstr "Positiver Wert erwartet refname:rstrip=%s"
 
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "nicht erkanntes %%(%s) Argument: %s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "%%(trailers:key=<Wert>) erwartet"
 
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) akzeptiert keine Argumente"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "unbekanntes %%(trailers) Argument: %s"
 
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) akzeptiert keine Argumente"
+msgid "positive value expected contents:lines=%s"
+msgstr "Positiver Wert erwartet contents:lines=%s"
 
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) akzeptiert keine Argumente"
+msgid "argument expected for %s"
+msgstr "Argument erwartet für %s"
 
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "%%(trailers:key=<Wert>) erwartet"
+msgid "positive value expected %s=%s"
+msgstr "positiver Wert erwartet %s=%s"
 
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "unbekanntes %%(trailers) Argument: %s"
+msgid "cannot fully parse %s=%s"
+msgstr "kann %s=%s nicht vollständig parsen"
 
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "Positiver Wert erwartet contents:lines=%s"
+msgid "value expected %s="
+msgstr "Wert erwartet %s="
 
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "positiver Wert erwartet '%s' in %%(%s)"
 
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "nicht erkannte E-Mail Option: %s"
-
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
 msgstr "erwartetes Format: %%(align:<Breite>,<Position>)"
@@ -18866,13 +19397,17 @@ msgstr "nicht erkannte Position:%s"
 msgid "unrecognized width:%s"
 msgstr "nicht erkannte Breite:%s"
 
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "nicht erkanntes %%(%s) Argument: %s"
+
 #, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "Positive Breitenangabe für %%(align) erwartet"
 
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) akzeptiert keine Argumente"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "erwartetes Format: %%(ahead-behind:<Commit>)"
 
 #, c-format
 msgid "malformed field name: %.*s"
@@ -18920,6 +19455,9 @@ msgstr "dieser Befehl lehnt Atom ab %%(%.*s)"
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s kann nicht mit --python, --shell, --tcl verwendet werden"
 
+msgid "failed to run 'describe'"
+msgstr "'describe' konnte nicht ausgeführt werden"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(kein Branch, Rebase von %s)"
@@ -18981,6 +19519,9 @@ msgstr "Schüssel"
 msgid "field name to sort on"
 msgstr "sortiere nach diesem Feld"
 
+msgid "exclude refs which match pattern"
+msgstr "Ausschluss von Referenzen, die dem Muster entsprechen"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "Kein Reflog: %s"
@@ -19438,10 +19979,11 @@ msgstr[1] ""
 "Ihr Branch und '%s' sind divergiert,\n"
 "und haben jeweils %d und %d unterschiedliche Commits.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
 msgstr ""
-"  (benutzen Sie \"git pull\", um Ihren Branch mit dem Remote-Branch "
-"zusammenzuführen)\n"
+"  (verwenden Sie \"git pull\", wenn Sie den Remote-Branch in Ihren "
+"integrieren wollen)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
@@ -19513,10 +20055,6 @@ msgstr "Fehler beim Aktualisieren des Konflikt-Status in '%s'."
 msgid "no remembered resolution for '%s'"
 msgstr "Keine aufgezeichnete Konfliktauflösung für '%s'."
 
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "Kann '%s' nicht löschen."
-
 #, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Preimage für '%s' aktualisiert."
@@ -19539,6 +20077,13 @@ msgstr "Konnte HEAD-Commit nicht bestimmen."
 msgid "failed to find tree of %s"
 msgstr "Fehler beim Finden des \"Tree\"-Objektes von %s."
 
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "nicht unterstützter Abschnitt für versteckte Referenzen: %s"
+
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= mehr als einmal übergeben"
+
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
 msgstr "resolve-undo zeichnet `%s` auf, das fehlt"
@@ -19550,6 +20095,10 @@ msgstr "konnte kein Commit für das Argument ancestry-path %s erhalten"
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<Pack-Datei> wird nicht länger unterstützt"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "ungültige Option '%s' im Modus --stdin"
+
 msgid "your current branch appears to be broken"
 msgstr "Ihr aktueller Branch scheint fehlerhaft zu sein."
 
@@ -19636,8 +20185,15 @@ msgstr "vollständiges Arbeitsverzeichnis beim Klonen erstellen"
 msgid "only download metadata for the branch that will be checked out"
 msgstr "lade nur Metadaten des Branches herunter, der ausgecheckt wird"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<Optionen>] [--] <Repository> [<Verzeichnis>]"
+msgid "create repository within 'src' directory"
+msgstr "Repository im Verzeichnis 'src' erstellen"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <Haupt-Branch>] [--full-clone]\n"
+"\t[--[no-]src] <URL> [<Eintragung>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19684,8 +20240,32 @@ msgid "--all or <enlistment>, but not both"
 msgstr "--all oder <Eintragung>, aber nicht beides"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "Git-Repository entfernt in '%s'"
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "konnte veraltetes scalar.repo '%s' nicht entfernen"
+
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "veraltetes scalar.repo '%s' entfernt"
+
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "Repository bei '%s' hat anderen Eigentümer"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "Repository bei '%s' hat ein Formatproblem"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "Kein Repository in '%s' gefunden"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"um dieses Repository von Scalar abzumelden, führen Sie aus\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -19840,10 +20420,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "Konnte '%s' nicht sperren"
 
-#, c-format
-msgid "could not write to '%s'"
-msgstr "Konnte nicht nach '%s' schreiben."
-
 #, c-format
 msgid "could not write eol to '%s'"
 msgstr "Konnte EOL nicht nach '%s' schreiben."
@@ -20099,10 +20675,6 @@ msgstr "Kann keine Commit-Beschreibung für %s bekommen."
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s: kann Eltern-Commit %s nicht parsen"
 
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "Konnte '%s' nicht zu '%s' umbenennen."
-
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "Konnte \"revert\" nicht auf %s... (%s) ausführen"
@@ -20123,6 +20695,24 @@ msgstr "git %s: Fehler beim Lesen des Index"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: Fehler beim Aktualisieren des Index"
 
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "'%s' ist keine gültige Beschriftung"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "'%s' ist kein gültiger Referenzname"
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr ""
+"update-ref erfordert einen vollständig qualifizierten Referenznamen, z. B. "
+"refs/heads/%s"
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "ungültiger Befehl '%.*s'"
+
 #, c-format
 msgid "%s does not accept arguments: '%s'"
 msgstr "%s akzeptiert keine Argumente: '%s'"
@@ -20194,9 +20784,6 @@ msgstr "versuchen Sie \"git cherry-pick (--continue | %s--abort | --quit)\""
 msgid "could not create sequencer directory '%s'"
 msgstr "konnte \"sequencer\"-Verzeichnis '%s' nicht erstellen"
 
-msgid "could not lock HEAD"
-msgstr "konnte HEAD nicht sperren"
-
 msgid "no cherry-pick or revert in progress"
 msgstr "kein \"cherry-pick\" oder \"revert\" im Gange"
 
@@ -20293,20 +20880,20 @@ msgstr ""
 "\n"
 "ausführen.\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "Der Index und/oder das Arbeitsverzeichnis wurde geändert.\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "und Änderungen am Index und/oder am Arbeitsverzeichnis vorgenommen.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "Ausführung erfolgreich: %s\n"
-"Aber Änderungen in Index oder Arbeitsverzeichnis verblieben.\n"
+"Aber es sind Änderungen im Index oder Arbeitsverzeichnis verblieben.\n"
 "Committen Sie Ihre Änderungen oder benutzen Sie \"stash\".\n"
 "Führen Sie dann aus:\n"
 "\n"
@@ -20317,16 +20904,16 @@ msgstr ""
 msgid "illegal label name: '%.*s'"
 msgstr "unerlaubter Beschriftungsname: '%.*s'"
 
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "konnte '%s' nicht auflösen"
+
 msgid "writing fake root commit"
 msgstr "unechten Root-Commit schreiben"
 
 msgid "writing squash-onto"
 msgstr "squash-onto schreiben"
 
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "konnte '%s' nicht auflösen"
-
 msgid "cannot merge without a current revision"
 msgstr "kann nicht ohne einen aktuellen Commit mergen"
 
@@ -20449,14 +21036,14 @@ msgstr ""
 "    git rebase --edit-todo\n"
 "    git rebase --continue\n"
 
-#, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Rebase (%d/%d)%s"
-
 #, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Angehalten bei %s... %.*s\n"
 
+#, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Rebase (%d/%d)%s"
+
 #, c-format
 msgid "unknown command %d"
 msgstr "Unbekannter Befehl %d"
@@ -20646,59 +21233,130 @@ msgid "cannot come back to cwd"
 msgstr "Kann nicht zum aktuellen Arbeitsverzeichnis zurückwechseln."
 
 #, c-format
-msgid "failed to stat '%*s%s%s'"
-msgstr "Konnte '%*s%s%s' nicht lesen."
+msgid "failed to stat '%*s%s%s'"
+msgstr "Konnte '%*s%s%s' nicht lesen."
+
+msgid "Unable to read current working directory"
+msgstr "Konnte aktuelles Arbeitsverzeichnis nicht lesen."
+
+#, c-format
+msgid "cannot change to '%s'"
+msgstr "Kann nicht nach '%s' wechseln."
+
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "Kein Git-Repository (oder irgendeines der Elternverzeichnisse): %s"
+
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"Kein Git-Repository (oder irgendein Elternverzeichnis bis zum Einhängepunkt "
+"%s)\n"
+"Stoppe bei Dateisystemgrenze (GIT_DISCOVERY_ACROSS_FILESYSTEM nicht gesetzt)."
+
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"dubiose Besitzverhältnisse im Repository bei '%s' entdeckt\n"
+"%sUm eine Ausnahme für dieses Verzeichnis hinzuzufügen, rufen Sie auf:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr ""
+"kann Bare-Repository '%s' nicht verwenden (safe.bareRepository ist '%s')"
+
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"Problem mit Wert für Dateimodus (0%.3o) von core.sharedRepository.\n"
+"Der Besitzer der Dateien muss immer Lese- und Schreibrechte haben."
+
+msgid "fork failed"
+msgstr "fork fehlgeschlagen"
+
+msgid "setsid failed"
+msgstr "setsid fehlgeschlagen"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "kann Vorlage '%s' nicht lesen"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "kann Verzeichnis '%s' nicht öffnen"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "kann Verweis '%s' nicht lesen"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "kann symbolische Verknüpfung '%s' auf '%s' nicht erstellen"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "kann '%s' nicht nach '%s' kopieren"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "ignoriere Vorlage %s"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "keine Vorlagen in %s gefunden"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "kopiere keine Vorlagen von '%s': %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "ungültiger initialer Branchname: '%s'"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "kann nicht mit Dateityp %d umgehen"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "konnte %s nicht nach %s verschieben"
 
-msgid "Unable to read current working directory"
-msgstr "Konnte aktuelles Arbeitsverzeichnis nicht lesen."
+msgid "attempt to reinitialize repository with different hash"
+msgstr "Versuch, das Repository mit einem anderen Hash zu reinitialisieren"
 
 #, c-format
-msgid "cannot change to '%s'"
-msgstr "Kann nicht nach '%s' wechseln."
+msgid "%s already exists"
+msgstr "%s existiert bereits"
 
 #, c-format
-msgid "not a git repository (or any of the parent directories): %s"
-msgstr "Kein Git-Repository (oder irgendeines der Elternverzeichnisse): %s"
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "Neu-Initialisierung: --initial-branch=%s ignoriert"
 
 #, c-format
-msgid ""
-"not a git repository (or any parent up to mount point %s)\n"
-"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
-msgstr ""
-"Kein Git-Repository (oder irgendein Elternverzeichnis bis zum Einhängepunkt "
-"%s)\n"
-"Stoppe bei Dateisystemgrenze (GIT_DISCOVERY_ACROSS_FILESYSTEM nicht gesetzt)."
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Bestehendes verteiltes Git-Repository in %s%s neuinitialisiert\n"
 
 #, c-format
-msgid ""
-"detected dubious ownership in repository at '%s'\n"
-"%sTo add an exception for this directory, call:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
-msgstr ""
-"dubiose Besitzverhältnisse im Repository bei '%s' entdeckt\n"
-"%sUm eine Ausnahme für dieses Verzeichnis hinzuzufügen, rufen Sie auf:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Bestehendes Git-Repository in %s%s neuinitialisiert\n"
 
 #, c-format
-msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr ""
-"kann Bare-Repository '%s' nicht verwenden (safe.bareRepository ist '%s')"
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Leeres verteiltes Git-Repository in %s%s initialisiert\n"
 
 #, c-format
-msgid ""
-"problem with core.sharedRepository filemode value (0%.3o).\n"
-"The owner of files must always have read and write permissions."
-msgstr ""
-"Problem mit Wert für Dateimodus (0%.3o) von core.sharedRepository.\n"
-"Der Besitzer der Dateien muss immer Lese- und Schreibrechte haben."
-
-msgid "fork failed"
-msgstr "fork fehlgeschlagen"
-
-msgid "setsid failed"
-msgstr "setsid fehlgeschlagen"
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Leeres Git-Repository in %s%s initialisiert\n"
 
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -20751,10 +21409,6 @@ msgid_plural "%u bytes/s"
 msgstr[0] "%u Byte/s"
 msgstr[1] "%u Bytes/s"
 
-#, c-format
-msgid "could not edit '%s'"
-msgstr "Konnte '%s' nicht editieren."
-
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "Ignoriere verdächtigen Submodulnamen: %s"
@@ -20936,6 +21590,27 @@ msgstr "ls-tree mit unerwartetem Rückgabewert %d beendet"
 msgid "failed to lstat '%s'"
 msgstr "'lstat' für '%s' fehlgeschlagen"
 
+msgid "no remote configured to get bundle URIs from"
+msgstr "kein Remote-Repository zum Erhalten von Bundle-URIs konfiguriert"
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "Remote-Repository '%s' hat keine konfigurierte URL"
+
+msgid "could not get the bundle-uri list"
+msgstr "konnte die Bundle-uri-Liste nicht erhalten"
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <Optionen> (control|prime|update)"
+
+msgid "clear the cache tree before each iteration"
+msgstr "das Cache-Verzeichnis vor jeder Iteration löschen"
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr ""
+"Anzahl der Einträge im Cache-Verzeichnis, die ungültig gemacht werden sollen "
+"(Standardwert 0)"
+
 msgid "unhandled options"
 msgstr "unbehandelte Optionen"
 
@@ -21280,6 +21955,12 @@ msgstr "Abbruch."
 msgid "failed to push all needed submodules"
 msgstr "Fehler beim Versand aller erforderlichen Submodule."
 
+msgid "bundle-uri operation not supported by protocol"
+msgstr "bundle-uri Operation wird vom Protokoll nicht unterstützt"
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "konnte die vom Server angekündigte bundle-uri-Liste nicht abrufen"
+
 msgid "too-short tree object"
 msgstr "zu kurzes Tree-Objekt"
 
@@ -21687,6 +22368,9 @@ msgstr "konnte nicht auf '%s' zugreifen"
 msgid "unable to get current working directory"
 msgstr "konnte aktuelles Arbeitsverzeichnis nicht bekommen"
 
+msgid "unable to get random bytes"
+msgstr "konnte keine Zufallsbytes abrufen"
+
 msgid "Unmerged paths:"
 msgstr "Nicht zusammengeführte Pfade:"
 
@@ -21970,8 +22654,8 @@ msgstr "Sie führen gerade \"cherry-pick\" von Commit %s aus."
 
 msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
 msgstr ""
-"  (beheben Sie die Konflikte und führen Sie dann \"git cherry-pick --continue"
-"\" aus)"
+"  (beheben Sie die Konflikte und führen Sie dann \"git cherry-pick --"
+"continue\" aus)"
 
 msgid "  (run \"git cherry-pick --continue\" to continue)"
 msgstr "  (Führen Sie \"git cherry-pick --continue\" aus, um weiterzumachen)"
@@ -22067,13 +22751,22 @@ msgstr "Ignorierte Dateien"
 
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
+msgstr ""
+"Es dauerte %.2f Sekunden, um die unversionierten Dateien aufzuzählen,\n"
+"aber die Ergebnisse wurden zwischengespeichert, sodass spätere Durchläufe "
+"schneller sein können."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr ""
+"Es hat %.2f Sekunden gedauert, um die unversionierten Dateien aufzuzählen."
+
+msgid "See 'git help status' for information on how to improve this."
 msgstr ""
-"Es dauerte %.2f Sekunden die unversionierten Dateien zu bestimmen.\n"
-"'status -uno' könnte das beschleunigen, aber Sie müssen darauf achten,\n"
-"neue Dateien selbstständig hinzuzufügen (siehe 'git help status')."
+"Siehe 'git help status' für Informationen darüber, wie man dies verbessern "
+"kann."
 
 #, c-format
 msgid "Untracked files not listed%s"
@@ -22233,289 +22926,6 @@ msgstr ""
 msgid "Unable to determine absolute path of git directory"
 msgstr "Konnte absoluten Pfad des Git-Verzeichnisses nicht bestimmen"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%28s %25s %s"
-
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "%d Pfad angefasst\n"
-msgstr[1] "%d Pfade angefasst\n"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-"Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-"Patch-Block direkt zum Hinzufügen zur Staging-Area markiert."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-"Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-"Patch-Block direkt zum Hinzufügen zum Stash markiert."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-"Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-"Patch-Block direkt zum Entfernen aus der Staging-Area markiert."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-"Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-"Patch-Block direkt zum Anwenden markiert."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-"Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-"Patch-Block direkt zum Verwerfen markiert."
-
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr ""
-"Fehler beim Öffnen von Editier-Datei eines Patch-Blocks zum Schreiben: %s"
-
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"Um '%s' Zeilen zu entfernen, machen Sie aus diesen ' ' Zeilen (Kontext).\n"
-"Um '%s' Zeilen zu entfernen, löschen Sie diese.\n"
-"Zeilen, die mit %s beginnen, werden entfernt.\n"
-
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "Fehler beim Öffnen von Editier-Datei eines Patch-Blocks zum Lesen: %s"
-
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - diesen Patch-Block zum Commit vormerken\n"
-"n - diesen Patch-Block nicht zum Commit vormerken\n"
-"q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht zum Commit "
-"vormerken\n"
-"a - diesen und alle weiteren Patch-Blöcke dieser Datei zum Commit vormerken\n"
-"d - diesen oder alle weiteren Patch-Blöcke in dieser Datei nicht zum Commit "
-"vormerken"
-
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - diesen Patch-Block stashen\n"
-"n - diesen Patch-Block nicht stashen\n"
-"q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht stashen\n"
-"a - diesen und alle weiteren Patch-Blöcke dieser Datei stashen\n"
-"d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht stashen"
-
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - diesen Patch-Block unstashen\n"
-"n - diesen Patch-Block nicht unstashen\n"
-"q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht unstashen\n"
-"a - diesen und alle weiteren Patch-Blöcke dieser Datei unstashen\n"
-"d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht unstashen"
-
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - diesen Patch-Block auf den Index anwenden\n"
-"n - diesen Patch-Block nicht auf den Index anwenden\n"
-"q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht auf den Index "
-"anwenden\n"
-"a - diesen und alle weiteren Patch-Blöcke dieser Datei auf den Index "
-"anwenden\n"
-"d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht auf den Index "
-"anwenden"
-
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - diesen Patch-Block im Arbeitsverzeichnis verwerfen\n"
-"n - diesen Patch-Block im Arbeitsverzeichnis nicht verwerfen\n"
-"q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht im "
-"Arbeitsverzeichnis verwerfen\n"
-"a - diesen und alle weiteren Patch-Blöcke dieser Datei im Arbeitsverzeichnis "
-"verwerfen\n"
-"d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht im "
-"Arbeitsverzeichnis verwerfen"
-
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - diesen Patch-Block im Index und Arbeitsverzeichnis verwerfen\n"
-"n - diesen Patch-Block nicht im Index und Arbeitsverzeichnis verwerfen\n"
-"q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht im Index und "
-"Arbeitsverzeichnis verwerfen\n"
-"a - diesen und alle weiteren Patch-Blöcke in der Datei verwerfen\n"
-"d - diesen oder alle weiteren Patch-Blöcke in der Datei nicht verwerfen"
-
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - diesen Patch-Block im Index und auf Arbeitsverzeichnis anwenden\n"
-"n - diesen Patch-Block nicht im Index und auf Arbeitsverzeichnis anwenden\n"
-"q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht anwenden\n"
-"a - diesen und alle weiteren Patch-Blöcke in der Datei anwenden\n"
-"d - diesen oder alle weiteren Patch-Blöcke in der Datei nicht anwenden"
-
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - diesen Patch-Block auf das Arbeitsverzeichnis anwenden\n"
-"n - diesen Patch-Block nicht auf das Arbeitsverzeichnis anwenden\n"
-"q - Beenden; diesen und alle verbleibenden Patch-Blöcke nicht anwenden\n"
-"a - diesen und alle weiteren Patch-Blöcke in der Datei anwenden\n"
-"d - diesen und alle weiteren Patch-Blöcke in der Datei nicht anwenden"
-
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - Patch-Block zum Hinspringen auswählen\n"
-"/ - nach Patch-Block suchen, der gegebenem regulärem Ausdruck entspricht\n"
-"j - diesen Patch-Block unbestimmt lassen, nächsten unbestimmten Patch-Block "
-"anzeigen\n"
-"J - diesen Patch-Block unbestimmt lassen, nächsten Patch-Block anzeigen\n"
-"k - diesen Patch-Block unbestimmt lassen, vorherigen unbestimmten Patch-"
-"Block anzeigen\n"
-"K - diesen Patch-Block unbestimmt lassen, vorherigen Patch-Block anzeigen\n"
-"s - aktuellen Patch-Block in kleinere Patch-Blöcke aufteilen\n"
-"e - aktuellen Patch-Block manuell editieren\n"
-"? - Hilfe anzeigen\n"
-
-msgid "The selected hunks do not apply to the index!\n"
-msgstr ""
-"Die ausgewählten Patch-Blöcke können nicht auf den Index angewendet werden!\n"
-
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "ignoriere nicht zusammengeführte Datei: %s\n"
-
-msgid "No other hunks to goto\n"
-msgstr "Keine anderen Patch-Blöcke verbleibend\n"
-
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "Ungültige Nummer: '%s'\n"
-
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "Entschuldigung, nur %d Patch-Block verfügbar.\n"
-msgstr[1] "Entschuldigung, nur %d Patch-Blöcke verfügbar.\n"
-
-msgid "No other hunks to search\n"
-msgstr "Keine anderen Patch-Blöcke zum Durchsuchen\n"
-
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "Fehlerhafter regulärer Ausdruck für Suche %s: %s\n"
-
-msgid "No hunk matches the given pattern\n"
-msgstr "Kein Patch-Block entspricht dem angegebenen Muster\n"
-
-msgid "No previous hunk\n"
-msgstr "Kein vorheriger Patch-Block\n"
-
-msgid "No next hunk\n"
-msgstr "Kein folgender Patch-Block\n"
-
-msgid "Sorry, cannot split this hunk\n"
-msgstr "Entschuldigung, kann diesen Patch-Block nicht aufteilen.\n"
-
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "In %d Patch-Block aufgeteilt.\n"
-msgstr[1] "In %d Patch-Blöcke aufgeteilt.\n"
-
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "Entschuldigung, kann diesen Patch-Block nicht bearbeiten.\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - Pfade mit Änderungen anzeigen\n"
-"update        - Zustand des Arbeitsverzeichnisses den zum Commit "
-"vorgemerkten Änderungen hinzufügen\n"
-"revert        - zum Commit vorgemerkte Änderungen auf HEAD Version "
-"zurücksetzen\n"
-"patch         - Patch-Blöcke auswählen und selektiv aktualisieren\n"
-"diff          - Unterschiede zwischen HEAD und Index anzeigen\n"
-"add untracked - Inhalte von unversionierten Dateien zum Commit vormerken\n"
-
-msgid "missing --"
-msgstr "-- fehlt"
-
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "Unbekannter --patch Modus: %s"
-
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "ungültiges Argument %s, erwarte --"
-
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr ""
 "lokale Zeitzone unterscheidet sich von GMT nicht um ein Minutenintervall\n"
@@ -22642,14 +23052,14 @@ msgstr ""
 "Leeren Sie den Inhalt des Bodys, wenn Sie keine Zusammenfassung senden "
 "möchten.\n"
 
-#, perl-format
-msgid "Failed to open %s: %s"
-msgstr "Fehler beim Öffnen von %s: %s"
-
 #, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "Fehler beim Öffnen von %s.final: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "Fehler beim Öffnen von %s: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "E-Mail mit Zusammenfassung ist leer, wird ausgelassen\n"
 
@@ -22811,13 +23221,17 @@ msgid "(%s) Could not execute '%s'"
 msgstr "(%s) Konnte '%s' nicht ausführen"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Füge %s: %s hinzu von: '%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Fehlerhafte Ausgabe von '%s'"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) Fehler beim Schließen der Pipe nach '%s'"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Füge %s: %s hinzu von: '%s'\n"
+
 msgid "cannot send message as 7bit"
 msgstr "kann Nachricht nicht als 7bit versenden"
 
index b29d34100204664e4716badf519f9ac320f831dd..ee2e610ef1469c351f9d7825319bc68a71e73e9c 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -1,5 +1,5 @@
 # French translations for Git.
-# Copyright (C) 2019 Jean-Noël Avila <jn.avila@free.fr>
+# Copyright (C) 2023 Jean-Noël Avila <jn.avila@free.fr>
 # This file is distributed under the same license as the Git package.
 # Jean-Noël Avila <jn.avila@free.fr>, 2013-2019.
 # Sébastien Helleu <flashcode@flashtux.org>, 2013.
@@ -21,6 +21,7 @@
 #   bypass           |  éviter d'utiliser
 #   to checkout      |  extraire
 #   cherry-pick      |  picorer
+#   chunk            |  tronçon
 #   to commit        |  valider
 #   commit-ish       |  commit ou apparenté
 #   config file      |  fichier de configuration
@@ -29,6 +30,7 @@
 #   debugging        |  débogage
 #   to deflate       |  compresser
 #   email            |  courriel
+#   enlistment       |  enrôlement
 #   entry            |  élément
 #   fanout           |  dispersion
 #   fast-forward     |  avance rapide
@@ -48,7 +50,7 @@
 #   pack             |  paquet
 #   patches          |  patchs
 #   pattern          |  motif
-#   to prune         |  éliminer
+#   to prune         |  élaguer
 #   to push          |  pousser
 #   to rebase        |  rebaser
 #   scheduler        |  planificateur
@@ -78,8 +80,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-09-28 21:43+0200\n"
-"PO-Revision-Date: 2022-09-28 21:45+0200\n"
+"POT-Creation-Date: 2023-11-08 04:57+0000\n"
+"PO-Revision-Date: 2023-11-11 10:00+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"
@@ -115,13 +117,13 @@ msgstr "impossible d'indexer '%s'"
 msgid "could not write index"
 msgstr "impossible d'écrire l'index"
 
-#, c-format, perl-format
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "%d chemin mis à jour\n"
 msgstr[1] "%d chemins mis à jour\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "note : %s n'est plus suivi à présent.\n"
 
@@ -135,7 +137,7 @@ msgstr "Inverser"
 msgid "Could not parse HEAD^{tree}"
 msgstr "Impossible d'analyser HEAD^{tree}"
 
-#, c-format, perl-format
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "%d chemin inversé\n"
@@ -148,7 +150,7 @@ msgstr "Aucun Fichier non suivi.\n"
 msgid "Add untracked"
 msgstr "Ajouter un fichier non-suivi"
 
-#, c-format, perl-format
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "%d chemin ajouté\n"
@@ -245,19 +247,19 @@ msgstr "impossible de rafraîchir l'index"
 msgid "Bye.\n"
 msgstr "Au revoir.\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "Indexer le changement de mode [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Indexer la suppression [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Indexer l'ajout [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Indexer cette section [y,n,q,a,d%s,?] ? "
 
@@ -281,19 +283,19 @@ msgstr ""
 "a - indexer cette section et toutes les suivantes de ce fichier\n"
 "d - ne pas indexer cette section ni les suivantes de ce fichier\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "Remiser le changement de mode [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Remiser la suppression [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Remiser l'ajout [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Remiser cette section [y,n,q,a,d%s,?] ? "
 
@@ -317,19 +319,19 @@ msgstr ""
 "a - remiser cette section et toutes les suivantes de ce fichier\n"
 "d - ne pas remiser cette section ni les suivantes de ce fichier\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "Désindexer le changement de mode [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Désindexer la suppression [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Désindexer l'ajout [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Désindexer cette section [y,n,q,a,d%s,?] ? "
 
@@ -353,19 +355,19 @@ msgstr ""
 "a - désindexer cette section et toutes les suivantes de ce fichier\n"
 "d - ne pas désindexer cette section ni les suivantes de ce fichier\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "Appliquer le changement de mode à l'index [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Appliquer la suppression à l'index [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Appliquer l'ajout à l'index [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Appliquer cette section à l'index [y,n,q,a,d%s,?] ? "
 
@@ -389,19 +391,19 @@ msgstr ""
 "a - appliquer cette section et toutes les suivantes de ce fichier\n"
 "d - ne pas appliquer cette section ni les suivantes de ce fichier\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "Abandonner le changement de mode dans l'arbre [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Abandonner la suppression dans l'arbre [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Abandonner l'ajout dans l'arbre [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "Abandonner cette section dans l'arbre [y,n,q,a,d%s,?] ? "
 
@@ -425,20 +427,20 @@ msgstr ""
 "a - supprimer cette section et toutes les suivantes de ce fichier\n"
 "d - ne pas supprimer cette section ni les suivantes de ce fichier\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Abandonner le changement de mode dans l'index et l'arbre [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Abandonner la suppression de l'index et de l'arbre [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Abandonner l'ajout de l'index et de l'arbre [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Supprimer la section dans l'index et l'arbre de travail [y,n,q,a,d%s,?] ? "
@@ -456,24 +458,24 @@ msgstr ""
 "a - éliminer cette section et toutes les suivantes de ce fichier\n"
 "d - ne pas éliminer cette section ni les suivantes de ce fichier\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Appliquer le changement de mode dans l'index et l'arbre de travail [y,n,q,a,"
 "d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Appliquer la suppression dans l'index et l'arbre de travail [y,n,q,a,"
 "d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Appliquer l'ajout dans l'index et l'arbre de travail [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Appliquer la section à l'index et l'arbre de travail [y,n,q,a,d%s,?] ? "
@@ -491,20 +493,20 @@ msgstr ""
 "a - appliquer cette section et toutes les suivantes de ce fichier\n"
 "d - ne pas appliquer cette section ni les suivantes de ce fichier\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Appliquer le changement de mode dans l'arbre de travail [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "Appliquer la suppression dans l'arbre de travail [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "Appliquer l'ajout dans l'arbre de travail [y,n,q,a,d%s,?] ? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "Appliquer la section à l'arbre de travail [y,n,q,a,d%s,?] ? "
 
@@ -581,8 +583,6 @@ msgstr ""
 "Pour éliminer les lignes '%c', effacez-les.\n"
 "Les lignes commençant par %c seront éliminées.\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -598,24 +598,16 @@ msgstr "impossible d'analyser l'entête de section"
 msgid "'git apply --cached' failed"
 msgstr "'git apply --cached' a échoué"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
-"Votre section éditée ne s'applique pas. L'éditer à nouveau (\"no\" "
+"Votre section éditée ne s'applique pas. L'éditer à nouveau (\"n\" "
 "l'élimine !) [y|n] ? "
 
 msgid "The selected hunks do not apply to the index!"
@@ -726,9 +718,8 @@ msgstr "Impossible de tirer car vous avez des fichiers non fusionnés."
 msgid "Reverting is not possible because you have unmerged files."
 msgstr "Impossible d'annuler car vous avez des fichiers non fusionnés."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "%s n'est pas possible car vous avez des fichiers non fusionnés."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Impossible de rebaser, car vous avez des fichiers non fusionnés."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -749,6 +740,24 @@ msgstr "Veuillez valider vos changements avant de fusionner."
 msgid "Exiting because of unfinished merge."
 msgstr "Abandon à cause d'une fusion non terminée."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Des branches divergentes ne peuvent pas être gérées en avance rapide, vous "
+"devez soit :\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"ou :\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Pas possible d'avancer rapidement, abandon."
 
@@ -841,6 +850,9 @@ msgstr "cmdline se termine par \\"
 msgid "unclosed quote"
 msgstr "citation non fermée"
 
+msgid "too many arguments"
+msgstr "trop d'arguments"
+
 #, c-format
 msgid "unrecognized whitespace option '%s'"
 msgstr "option d'espace non reconnue '%s'"
@@ -857,6 +869,12 @@ msgstr "les options '%s' et '%s' ne peuvent pas être utilisées ensemble"
 msgid "'%s' outside a repository"
 msgstr "'%s' hors d'un dépôt"
 
+msgid "failed to read patch"
+msgstr "impossible de lire la rustine"
+
+msgid "patch too large"
+msgstr "la rustine est trop grosse"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "Impossible de préparer la regexp d'horodatage %s"
@@ -1206,6 +1224,10 @@ msgstr "troncature du nom de fichier .rej en %.*s.rej"
 msgid "cannot open %s"
 msgstr "impossible d'ouvrir %s"
 
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "impossible de délier '%s'"
+
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Section n°%d appliquée proprement."
@@ -1397,6 +1419,12 @@ msgstr "git archive --remote <dépôt> [--exec <commande>] --list"
 msgid "cannot read '%s'"
 msgstr "impossible de lire '%s'"
 
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr ""
+"le spécificateur de chemin '%s' correspond à des fichiers hors du répertoire "
+"actuel"
+
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "le chemin '%s' ne correspond à aucun fichier"
@@ -1413,9 +1441,6 @@ msgstr "nom d'objet invalide : %s"
 msgid "not a tree object: %s"
 msgstr "objet arbre invalide : %s"
 
-msgid "current working directory is untracked"
-msgstr "l'arbre de travail actuel est non-suivi"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Fichier non trouvé : %s"
@@ -1466,6 +1491,12 @@ msgstr "lire .gitattributes dans le répertoire de travail"
 msgid "report archived files on stderr"
 msgstr "afficher les fichiers archivés sur stderr"
 
+msgid "time"
+msgstr "heure"
+
+msgid "set modification time of archive entries"
+msgstr "régler la date de modification des entrées de l'archive"
+
 msgid "set compression level"
 msgstr "régler le niveau de compression"
 
@@ -1506,6 +1537,13 @@ msgstr "Argument non supporté pour le format '%s' : -%d"
 msgid "%.*s is not a valid attribute name"
 msgstr "%.*s n'est pas un nom valide d'attribut"
 
+msgid "unable to add additional attribute"
+msgstr "Impossible d'ajouter l'attribut additionnel"
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "ligne d'attribute trop longue ignorée %d"
+
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "%s non permis : %s : %d"
@@ -1517,6 +1555,21 @@ msgstr ""
 "Les motifs de négation sont ignorés dans les attributs git\n"
 "Utilisez '\\!' pour un point d'exclamation littéral."
 
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "impossible de fstat le fichier gitattributes '%s'"
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "fichier gitattributes trop gros ignoré '%s'"
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "blob gitattributes trop gros ignoré '%s'"
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "mauvais --attr-source ou GIT_ATTR_SOURCE"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Contenu mal cité dans le fichier '%s' : %s"
@@ -1623,9 +1676,6 @@ msgstr[1] "Bissection : %d révisions à tester après ceci %s\n"
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents et --reverse ne font pas bon ménage."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "on ne peut pas utiliser --contents avec un nom d'objet commit final"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse et --first-parent ensemble nécessitent la spécification d'un "
@@ -1744,9 +1794,10 @@ msgid "a branch named '%s' already exists"
 msgstr "Une branche nommée '%s' existe déjà"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
 msgstr ""
-"impossible de forcer la mise à jour de la branche '%s' extraite dans '%s'"
+"impossible de forcer la mise à jour de la branche '%s' utilisée par l'arbre-"
+"de-travail dans '%s'"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1795,23 +1846,19 @@ msgstr "sous-module '%s' : impossible de trouver le sous-module"
 
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
 "Vous pouvez essayer de mettre à jour les sous-modules en utilisant 'git "
-"checkout %s && git submodule update --init'"
+"checkout --no-recurse-submodules %s && git submodule update --init'"
 
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
 msgstr "sous-module '%s' : impossible de créer la branche '%s'"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' est déjà extrait dans '%s'"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "la HEAD de la copie de travail %s n'est pas mise à jour"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s' est déjà utilisé par l'arbre-de-travail dans '%s'"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<options>] [--] <chemin>..."
@@ -1820,39 +1867,32 @@ msgstr "git add [<options>] [--] <chemin>..."
 msgid "cannot chmod %cx '%s'"
 msgstr "impossible de chmod %cx '%s'"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "état de diff inattendu %c"
-
-msgid "updating files failed"
-msgstr "échec de la mise à jour des fichiers"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "suppression de '%s'\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Modifications non indexées après rafraîchissement de l'index :"
 
-msgid "Could not read the index"
-msgstr "Impossible de lire l'index"
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"le réglage add.interactive.useBuiltin a été supprimé !\n"
+"Référez-vous à cette entrée dans 'git help config' pour plus de détails."
 
-msgid "Could not write patch"
-msgstr "Impossible d'écrire le patch"
+msgid "could not read the index"
+msgstr "impossible de lire l'index"
 
 msgid "editing patch failed"
 msgstr "échec de l'édition du patch"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Stat de '%s' impossible"
+msgid "could not stat '%s'"
+msgstr "impossible de stat '%s'"
 
-msgid "Empty patch. Aborted."
-msgstr "Patch vide. Abandon."
+msgid "empty patch. aborted"
+msgstr "rustine vide. abandon"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Impossible d'appliquer '%s'"
+msgid "could not apply '%s'"
+msgstr "impossible d'appliquer '%s'"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
@@ -1986,6 +2026,9 @@ msgstr ""
 msgid "index file corrupt"
 msgstr "fichier d'index corrompu"
 
+msgid "unable to write new index file"
+msgstr "impossible d'écrire le nouveau fichier d'index"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "action invalide '%s' pour '%s'"
@@ -2195,9 +2238,6 @@ msgstr ""
 "Vous pouvez lancer 'git rm' sur un fichier \"supprimé par eux\" pour "
 "accepter son état."
 
-msgid "unable to write new index file"
-msgstr "impossible d'écrire le nouveau fichier d'index"
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "Impossible d'analyser l'objet '%s'."
@@ -2216,10 +2256,6 @@ msgstr ""
 msgid "failed to read '%s'"
 msgstr "échec de la lecture de '%s'"
 
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "les options '%s=%s' et '%s=%s' ne peuvent pas être utilisées ensemble"
-
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<options>] [(<mbox> | <Maildir>)...]"
 
@@ -2229,6 +2265,9 @@ msgstr "git am [<options>] (--continue | --skip | --abort)"
 msgid "run interactively"
 msgstr "exécution interactive"
 
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "court-circuiter les crochets pre-applypatch and applypatch-msg"
+
 msgid "historical option -- no-op"
 msgstr "option historique -- no-op"
 
@@ -2256,10 +2295,6 @@ msgstr "passer l'option -m à git-mailinfo"
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "passer l'option --keep-cr à git-mailsplit fpour le format mbox"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"ne pas passer l'option --keep-cr à git-mailsplit indépendamment de am.keepcr"
-
 msgid "strip everything before a scissors line"
 msgstr "retirer tout le contenu avant la ligne des ciseaux"
 
@@ -2373,32 +2408,28 @@ msgstr "git archive : erreur de protocole"
 msgid "git archive: expected a flush"
 msgstr "git archive : vidage attendu"
 
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<commit>]"
-
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<terme> --term-{old,good}"
-"=<terme>][--no-checkout] [--first-parent] [<mauvais> [<bon>...]] [--] "
-"[<chemins>...]"
+"git bisect start [--term-{new,bad}=<terme> --term-{old,good}=<terme>]    [--"
+"no-checkout] [--first-parent] [<mauvais> [<bon>...]] [--]    [<spéc-de-"
+"chemin>...]"
 
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<rév>]"
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<rév>...]"
 
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<rév>...]"
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<rév>|<plage>)...]"
 
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <nom-de-fichier>"
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<commit>]"
 
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<rév>|<plage>)...]"
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <fichier-journal>"
 
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <cmd>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <cmd>  [<arg>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2545,9 +2576,6 @@ msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
 msgstr ""
 "l'extraction de '%s' a échoué. Essayez 'git bisect start <branche-valide>'."
 
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "refus de bissecter sur un arbre 'cg-seeked'"
-
 msgid "bad HEAD - strange symbolic ref"
 msgstr "mauvaise HEAD - référence symbolique douteuse"
 
@@ -2599,16 +2627,16 @@ msgid "bisect run failed: no command provided."
 msgstr "la bissection a échoué : aucune commande fournie."
 
 #, c-format
-msgid "unable to verify '%s' on good revision"
-msgstr "impossible de vérifier '%s' sur une bonne révision"
+msgid "unable to verify %s on good revision"
+msgstr "impossible de vérifier %s sur une bonne révision"
 
 #, c-format
 msgid "bogus exit code %d for good revision"
 msgstr "code de sortie %d erroné pour une bonne révision"
 
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
-msgstr "la bissection a échoué : le code retour %d de '%s' est < 0 ou >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "la bissection a échoué : le code retour %d de %s est < 0 ou >= 128"
 
 #, c-format
 msgid "cannot open file '%s' for writing"
@@ -2617,76 +2645,48 @@ msgstr "impossible d'ouvrir '%s' en écriture"
 msgid "bisect run cannot continue any more"
 msgstr "la bissection ne peut plus continuer"
 
-#, c-format
 msgid "bisect run success"
 msgstr "succès de la bissection"
 
-#, c-format
 msgid "bisect found first bad commit"
 msgstr "la bissection a trouvé le premier mauvais commit"
 
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
-msgstr ""
-"la bissection a échoué : 'git bisect--helper --bisect-state %s' a retourné "
-"le code erreur %d"
-
-msgid "reset the bisection state"
-msgstr "réinitialiser l'état de la bissection"
-
-msgid "check whether bad or good terms exist"
-msgstr "vérifier si les termes bons ou mauvais existent"
-
-msgid "print out the bisect terms"
-msgstr "afficher les termes de bissection"
-
-msgid "start the bisect session"
-msgstr "démarrer une session de bissection"
-
-msgid "find the next bisection commit"
-msgstr "trouver le prochain commit de bissection"
-
-msgid "mark the state of ref (or refs)"
-msgstr "marquer l'état d'une références (ou plusieurs)"
-
-msgid "list the bisection steps so far"
-msgstr "lister les étapes de bissection jusqu'ici"
-
-msgid "replay the bisection process from the given file"
-msgstr "rejouer le processus de bissection depuis le fichier fourni"
-
-msgid "skip some commits for checkout"
-msgstr "sauter certains commits pour l'extraction"
-
-msgid "visualize the bisection"
-msgstr "visualiser la bissection"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr "la bissection a échoué : 'git bisect %s' a retourné le code erreur %d"
 
-msgid "use <cmd>... to automatically bisect"
-msgstr "utiliser <cmd>... pour bissecter automatiquement"
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "%s supporte soit aucun argument, soit un commit"
 
-msgid "no log for BISECT_WRITE"
-msgstr "pas de journal pour BISECT_WRITE"
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "'%s' nécessite 0 ou 1 argument"
 
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset supporte soit aucun argument, soit un commit"
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "'%s' n'accepte aucun argument"
 
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms exige 0 ou 1 argument"
+msgid "no logfile given"
+msgstr "pas de fichier de log donné"
 
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next exige 0 argument"
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "'%s' a échoué : aucune commande fournie."
 
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log exige 0 argument"
+msgid "need a command"
+msgstr "commande requise"
 
-msgid "no logfile given"
-msgstr "pas de fichier de log donné"
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "commande inconnue : '%s'"
 
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<options>] [<rev-opts>] [<rev>] [--] <fichier>"
 
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<options>] [<options-de-rev>] [<rev>] [--] <fichier>"
+
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<options-de-révision> sont documentés dans git-rev-list(1)"
 
@@ -2856,51 +2856,58 @@ msgstr "git branch [<options>] [-r | -a] [--format]"
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "suppression de la branche '%s' qui a été fusionnée dans\n"
-"         '%s', mais pas dans HEAD."
+"         '%s', mais pas encore dans HEAD"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"branche '%s' non supprimée car elle n'a pas été fusionnée dans\n"
-"         '%s', même si elle est fusionnée dans HEAD."
+"branche '%s' non supprimée car elle n'a pas encore été fusionnée dans\n"
+"         '%s', même si elle est fusionnée dans HEAD"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Impossible de rechercher l'objet commit pour '%s'"
+msgid "couldn't look up commit object for '%s'"
+msgstr "impossible de rechercher l'objet commit pour '%s'"
 
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
-"La branche '%s' n'est pas totalement fusionnée.\n"
-"Si vous souhaitez réellement la supprimer, lancez 'git branch -D %s'."
+"la branche '%s' n'est pas totalement fusionnée.\n"
+"Si vous souhaitez réellement la supprimer, lancez 'git branch -D %s'"
 
-msgid "Update of config-file failed"
-msgstr "Ã\89chec de la mise à jour du fichier de configuration"
+msgid "update of config-file failed"
+msgstr "échec de la mise à jour du fichier de configuration"
 
 msgid "cannot use -a with -d"
 msgstr "impossible d'utiliser -a avec -d"
 
-msgid "Couldn't look up commit object for HEAD"
-msgstr "Impossible de rechercher l'objet commit pour HEAD"
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"impossible de supprimer la branche '%s' utilisée par l'arbre-de-travail dans "
+"'%s'"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Impossible de supprimer la branche '%s' extraite dans '%s'"
+msgid "remote-tracking branch '%s' not found"
+msgstr "branche de suivi '%s' non trouvée"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "branche de suivi '%s' non trouvée."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"branche '%s' non trouvée.\n"
+"Avez-vous oublié --remote ?"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "branche '%s' non trouvée."
+msgid "branch '%s' not found"
+msgstr "branche '%s' non trouvée"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2921,49 +2928,55 @@ msgid "HEAD (%s) points outside of refs/heads/"
 msgstr "HEAD (%s) pointe hors de refs/heads/"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "La branche %s est en cours de rebasage sur %s"
+msgid "branch %s is being rebased at %s"
+msgstr "la branche %s est en cours de rebasage sur %s"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "La branche %s est en cours de bissection sur %s"
+msgid "branch %s is being bisected at %s"
+msgstr "la branche %s est en cours de bissection sur %s"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "impossible de copier la branche actuelle, il n'y en a pas."
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "la HEAD de la copie de travail %s n'est pas mise à jour"
+
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "nom de branche invalide : '%s'"
 
-msgid "cannot rename the current branch while not on any."
-msgstr "impossible de renommer la branche actuelle, il n'y en a pas."
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "aucun commit encore sur la branche '%s'"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Nom de branche invalide : '%s'"
+msgid "no branch named '%s'"
+msgstr "aucune branche nommée '%s'"
 
-msgid "Branch rename failed"
-msgstr "Ã\89chec de renommage de la branche"
+msgid "branch rename failed"
+msgstr "échec de renommage de la branche"
 
-msgid "Branch copy failed"
-msgstr "Ã\89chec de copie de la branche"
+msgid "branch copy failed"
+msgstr "échec de copie de la branche"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Création d'une copie d'une branche mal nommée '%s'"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "création d'une copie d'une branche mal nommée '%s'"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Renommage d'une branche mal nommée '%s'"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "renommage d'une branche mal nommée '%s'"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "La branche a été renommée en %s, mais HEAD n'est pas mise à jour !"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "la branche a été renommée en %s, mais HEAD n'est pas mise à jour"
 
-msgid "Branch is renamed, but update of config-file failed"
+msgid "branch is renamed, but update of config-file failed"
 msgstr ""
-"La branche est renommée, mais la mise à jour du fichier de configuration a "
+"la branche est renommée, mais la mise à jour du fichier de configuration a "
 "échoué"
 
-msgid "Branch is copied, but update of config-file failed"
+msgid "branch is copied, but update of config-file failed"
 msgstr ""
-"La branche est copiée, mais la mise à jour du fichier de configuration a "
+"la branche est copiée, mais la mise à jour du fichier de configuration a "
 "échoué"
 
 #, c-format
@@ -3030,6 +3043,9 @@ msgstr "déplacer/renommer une branche et son reflog"
 msgid "move/rename a branch, even if target exists"
 msgstr "déplacer/renommer une branche, même si la cible existe"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "ne pas générer de nouvelle ligne après des réfs formatées vides"
+
 msgid "copy a branch and its reflog"
 msgstr "copier une branche et son reflog"
 
@@ -3075,8 +3091,8 @@ msgstr "parcourir récursivement les sous-modules"
 msgid "format to use for the output"
 msgstr "format à utiliser pour la sortie"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Ã\89chec de résolution de HEAD comme référence valide."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "échec de résolution de HEAD comme référence valide"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD non trouvée sous refs/heads !"
@@ -3094,19 +3110,17 @@ msgstr "--recurse-submodules ne peut être utilisé que pour créer des branches
 msgid "branch name required"
 msgstr "le nom de branche est requis"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "Impossible de décrire une HEAD détachée"
+msgid "cannot give description to detached HEAD"
+msgstr "impossible de décrire une HEAD détachée"
 
 msgid "cannot edit description of more than one branch"
 msgstr "impossible d'éditer la description de plus d'une branche"
 
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Aucun commit sur la branche '%s'."
+msgid "cannot copy the current branch while not on any"
+msgstr "impossible de copier la branche actuelle, il n'y en a pas"
 
-#, c-format
-msgid "No branch named '%s'."
-msgstr "Aucune branche nommée '%s'."
+msgid "cannot rename the current branch while not on any"
+msgstr "impossible de renommer la branche actuelle, il n'y en a pas"
 
 msgid "too many branches for a copy operation"
 msgstr "trop de branches pour une opération de copie"
@@ -3119,10 +3133,10 @@ msgstr "trop d'arguments pour spécifier une branche amont"
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
 "impossible de spécifier une branche amont de HEAD par %s qui ne pointe sur "
-"aucune branche."
+"aucune branche"
 
 #, c-format
 msgid "no such branch '%s'"
@@ -3135,29 +3149,29 @@ msgstr "la branche '%s' n'existe pas"
 msgid "too many arguments to unset upstream"
 msgstr "trop d'arguments pour désactiver un amont"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr ""
 "impossible de désactiver une branche amont de HEAD quand elle ne pointe sur "
-"aucune branche."
+"aucune branche"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "La branche '%s' n'a aucune information de branche amont"
+msgid "branch '%s' has no upstream information"
+msgstr "la branche '%s' n'a aucune information de branche amont"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"Les options -a et -r de 'git branch' n'ont pas de sens avec un nom de "
+"les options -a et -r de 'git branch' n'ont pas de sens avec un nom de "
 "branche.\n"
 "Vouliez-vous plutôt dire -a|-r --list <motif> ?"
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "l'option '--set-upstream' est obsolète. Utilisez '--track' ou '--set-"
-"upstream-to' à la place."
+"upstream-to' à la place"
 
 msgid "git version:\n"
 msgstr "version git ::\n"
@@ -3176,11 +3190,12 @@ msgid "not run from a git repository - no hooks to show\n"
 msgstr "lancé hors d'un dépôt git - aucun crochet à montrer\n"
 
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory <fichier>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]]"
+"git bugreport [(-o | --output-directory) <chemin>] [(-s | --suffix) "
+"<format>]\n"
+"              [--diagnose[=<mode>]]"
 
 msgid ""
 "Thank you for filling out a Git bug report!\n"
@@ -3233,6 +3248,10 @@ msgstr "spécifier la destination du/des fichier(s) de rapport de bogue"
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "spécifier une suffixe au format strftime pour le(s) nom(s) de fichier"
 
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "argument inconnu '%s'"
+
 #, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "impossible de créer les répertoires de premier niveau pour '%s'"
@@ -3255,17 +3274,24 @@ msgstr "impossible d'écrire dans %s"
 msgid "Created new report at '%s'.\n"
 msgstr "Nouveau rapport créé à '%s'.\n"
 
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [<options>] <fichier> <args git-rev-list>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <fichier> <args-de-git-rev-list>"
 
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [<options>] <fichier>"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <fichier>"
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads <fichier> [<nom-de-ref>...]"
 
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle <fichier> [<nom-de-ref>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <fichier> [<nom-de-ref>...]"
+
+msgid "need a <file> argument"
+msgstr "requiert un argument <fichier>"
 
 msgid "do not show progress meter"
 msgstr "ne pas afficher la barre de progression"
@@ -3273,11 +3299,11 @@ msgstr "ne pas afficher la barre de progression"
 msgid "show progress meter"
 msgstr "afficher la barre de progression"
 
-msgid "show progress meter during object writing phase"
-msgstr "afficher la barre de progression durant la phase d'écrite des objets"
+msgid "historical; same as --progress"
+msgstr "option historique ; identique à --progress"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "similaire à --all-progress quand la barre de progression est affichée"
+msgid "historical; does nothing"
+msgstr "option historique ; ne fait rien"
 
 msgid "specify bundle format version"
 msgstr "spécifier la version du format de colis"
@@ -3320,10 +3346,6 @@ msgstr "%s nécessite des arguments"
 msgid "%s takes no arguments"
 msgstr "%s n'accepte aucune argument"
 
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "commande inconnue : %s"
-
 msgid "only one batch option may be specified"
 msgstr "une seule option de traitement ne peut être spécifiée à la fois"
 
@@ -3336,23 +3358,23 @@ msgstr "git cat-file (-e | -p) <objet>"
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objet>"
 
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<chemin|arbresque> | --path=<chemin|arbresque> <rev>]"
+
 msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
-msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<chemin|arbresque> | --path=<chemin|arbresque> <rev>]"
+"             [--textconv | --filters] [-Z]"
 
 msgid "Check object existence or emit object contents"
 msgstr "Vérifie l'existence d'un objet ou émettre le contenu de l'objet"
@@ -3390,6 +3412,9 @@ msgstr "comme --batch, mais ne pas émettre <contenu>"
 msgid "stdin is NUL-terminated"
 msgstr "l'entrée se termine par NUL"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "l'entrée et la sortie standard se terminent par NUL"
+
 msgid "read commands from stdin"
 msgstr "lire les commandes depuis l'entrée standard"
 
@@ -3456,18 +3481,21 @@ msgstr "<rev> nécessaire avec '%s'"
 msgid "<object> required with '-%c'"
 msgstr "<objet> nécessaire avec '-%c'"
 
-msgid "too many arguments"
-msgstr "trop d'arguments"
-
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr "deux arguments seulement permis dans le mode <type> <objet>, pas %d"
 
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <attr>...] [--] <chemin>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <arbre-esque>] [-a | --all | <attr>...] [--] "
+"<chemin>..."
 
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <attr>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <arbre-esque>] [-a | --all | <attr>...]"
 
 msgid "report all attributes set on file"
 msgstr "afficher tous les attributs associés au fichier"
@@ -3482,6 +3510,12 @@ msgid "terminate input and output records by a NUL character"
 msgstr ""
 "terminer les enregistrements en entrée et en sortie par un caractère NUL"
 
+msgid "<tree-ish>"
+msgstr "<arbre-esque>"
+
+msgid "which tree-ish to check attributes at"
+msgstr "à quel <arbre-esque> vérifier les attributs"
+
 msgid "suppress progress reporting"
 msgstr "supprimer l'état d'avancement"
 
@@ -3642,6 +3676,12 @@ msgstr "'%s' ne peut pas être utilisé quand '%s' n'est pas spécifié"
 msgid "'%s' or '%s' cannot be used with %s"
 msgstr "'%s' ou '%s' ne peut pas être utilisé avec %s"
 
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr ""
+"'%s', '%s' ou '%s' ne peuvent pas être utilisés lors de l'extraction d'un "
+"arbre"
+
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "le chemin '%s' n'est pas fusionné"
@@ -4001,9 +4041,11 @@ msgid "use overlay mode"
 msgstr "utiliser le mode de superposition"
 
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <motif>] [-x | -X] [--] <chemins>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <motif>] [-x | -X] [--] [<spec-de-"
+"chemins>...]"
 
 #, c-format
 msgid "Removing %s\n"
@@ -4067,7 +4109,7 @@ msgstr ""
 "*          - choisir tous les éléments\n"
 "           - (vide) terminer la sélection\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Hein (%s) ?\n"
 
@@ -4216,9 +4258,6 @@ msgstr "profondeur"
 msgid "create a shallow clone of that depth"
 msgstr "créer un clone superficiel de cette profondeur"
 
-msgid "time"
-msgstr "heure"
-
 msgid "create a shallow clone since a specific time"
 msgstr "créer un clone superficiel depuis une date spécifique"
 
@@ -4258,12 +4297,6 @@ msgstr "spécifique au serveur"
 msgid "option to transmit"
 msgstr "option à transmettre"
 
-msgid "use IPv4 addresses only"
-msgstr "n'utiliser que des adresses IPv4"
-
-msgid "use IPv6 addresses only"
-msgstr "n'utiliser que des adresses IPv6"
-
 msgid "apply partial clone filters to submodules"
 msgstr "appliquer les filtres de clone partiel aux sous-modules"
 
@@ -4296,10 +4329,18 @@ msgstr "échec du stat de '%s'"
 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'"
@@ -4366,10 +4407,6 @@ msgstr "Trop d'arguments."
 msgid "You must specify a repository to clone."
 msgstr "Vous devez spécifier un dépôt à cloner."
 
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "les options '%s' et '%s %s' ne peuvent pas être utilisées ensemble"
-
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
 "exclude"
@@ -4461,6 +4498,9 @@ msgstr "échec lors de l'initialisation du dépôt, URI du paquet ignoré"
 msgid "failed to fetch objects from bundle URI '%s'"
 msgstr "impossible de récupérer les objets depuis l'URI de paquet '%s'"
 
+msgid "failed to fetch advertised bundles"
+msgstr "échec de récupération des colis annoncés"
+
 msgid "remote transport reported error"
 msgstr "le transport distant a retourné une erreur"
 
@@ -4496,18 +4536,24 @@ msgid "--command must be the first argument"
 msgstr "--command doit être le premier argument"
 
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <rép>] [--shallow] [--[no-]progress]"
 
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 msgstr ""
-"git commit-graph write [--object-dir <répertoire-d'objet>] [--append] [--"
-"split[=<stratégie>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <options de division>"
+"git commit-graph write [--object-dir <rép>] [--append]\n"
+"                       [--split[=<stratégie>]] [--reachable | --stdin-packs "
+"| --stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <options de division>"
 
 msgid "dir"
 msgstr "répertoire"
@@ -4523,6 +4569,10 @@ msgstr ""
 msgid "Could not open commit-graph '%s'"
 msgstr "Impossible d'ouvrir le graphe de commit '%s'"
 
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "impossible d'ouvrir le graphe de commit '%s'"
+
 #, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "argument de --split non reconnu, %s"
@@ -4577,12 +4627,15 @@ msgstr "utilisez un seul parmi --reachable, --stdin-commits ou --stdin-packs"
 msgid "Collecting commits from input"
 msgstr "Collecte des commits depuis l'entrée"
 
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <arbre> [(-p <parent>)...]"
+
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p <parent>)...] [-S[<idclé>]] [(-m <message>)...] [(-F "
-"<fichier>)...] <arbre>"
+"git commit-tree [(-p <parent>)...] [-S[<idclé>]] [(-m <message>)...]\n"
+"                [(-F <fichier>)...] <arbre>"
 
 #, c-format
 msgid "duplicate parent %s ignored"
@@ -4624,11 +4677,29 @@ msgstr "exactement un arbre obligatoire"
 msgid "git commit-tree: failed to read"
 msgstr "git commit-tree : échec de la lecture"
 
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<options>] [--] <spécification-de-chemin>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<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=<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"
+"           [--] [<spéc-de-chemin>...]"
 
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<options>] [--] <spécification-de-chemin>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<options>] [--] [<spécification-de-chemin>...]"
 
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -4681,6 +4752,9 @@ msgstr ""
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "échec de la mise à jour des fichiers"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "échec du dépaquetage de l'objet arbre HEAD"
 
@@ -4699,9 +4773,6 @@ msgstr "impossible de mettre à jour l'index temporaire"
 msgid "Failed to update main cache tree"
 msgstr "Impossible de mettre à jour l'arbre de cache principal"
 
-msgid "unable to write new_index file"
-msgstr "impossible d'écrire le fichier new_index"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "impossible de faire une validation partielle pendant une fusion."
 
@@ -4740,8 +4811,8 @@ msgstr ""
 "qui n'est pas utilisé dans le message de validation actuel"
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "impossible de rechercher le commit %s"
+msgid "could not lookup commit '%s'"
+msgstr "impossible de rechercher le commit '%s'"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -5111,11 +5182,11 @@ msgstr ""
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"le dépôt a été mis à jour, mais impossible d'écrire le fichier\n"
-"new_index. Vérifiez que le disque n'est pas plein ou que le quota\n"
+"le dépôt a été mis à jour, mais impossible d'écrire le nouveau fichier\n"
+"d'index. Vérifiez que le disque n'est pas plein ou que le quota\n"
 "n'a pas été dépassé, puis lancez \"git restore --staged :/\" pour réparer."
 
 msgid "git config [<options>]"
@@ -5412,11 +5483,18 @@ msgstr "credential-cache non disponible ; pas de gestion des socket unix"
 msgid "unable to get credential storage lock in %d ms"
 msgstr "impossible d'accéder au verrou de stockage d'identification en %d ms"
 
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<options>] <commit ou apparenté>*"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-esque>...]"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<marque>]"
 
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<options>] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe <blob>"
 
 msgid "head"
 msgstr "tête"
@@ -5543,11 +5621,11 @@ msgstr ""
 "l'option '%s' et des commit-esques ne peuvent pas être utilisées ensemble"
 
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory <fichier>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [-o | --output-directory <fichier>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 
 msgid "specify a destination for the diagnostics archive"
 msgstr "spécifier la destination de l'archive de diagnostique"
@@ -5565,6 +5643,9 @@ msgstr "--merge-base ne fonctionne qu'avec deux commits"
 msgid "'%s': not a regular file or symlink"
 msgstr "'%s' : n'est pas un fichier régulier ni un lien symbolique"
 
+msgid "no merge given, only parents."
+msgstr "pas de fusion fournie, seulement des parents."
+
 #, c-format
 msgid "invalid option: %s"
 msgstr "option invalide : %s"
@@ -5679,30 +5760,6 @@ msgstr "pas d'<outil> spécifié pour --tool=<outil>"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "pas de <commande> spécifié pour --extcmd=<commande>"
 
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <options> <var d'env>"
-
-msgid "default for git_env_*(...) to fall back on"
-msgstr "valeur par défaut pour git_env_*(...) en cas d'absence"
-
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr ""
-"mode silencieux n'utilisant la valeur de git_env_*() que pour le code de "
-"sortie"
-
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr ""
-"l'option `--default` attend une valeur booléenne avec `--type=bool`, pas `%s`"
-
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not "
-"`%s`"
-msgstr ""
-"l'option `--default` attend une valeur entier long non signé avec `--"
-"type=ulong`, pas `%s`"
-
 msgid "git fast-export [<rev-list-opts>]"
 msgstr "git fast-export [<options-de-liste-de-révisions>]"
 
@@ -5821,128 +5878,19 @@ msgstr "git fetch --all [<options>]"
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel ne peut pas être négatif"
 
-msgid "fetch from all remotes"
-msgstr "récupérer depuis tous les dépôts distants"
+msgid "couldn't find remote ref HEAD"
+msgstr "impossible de trouver la référence HEAD distante"
 
-msgid "set upstream for git pull/fetch"
-msgstr "définir la branche amont pour git pull/fetch"
+#, c-format
+msgid "From %.*s\n"
+msgstr "Depuis %.*s\n"
 
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "ajouter à .git/FETCH_HEAD au lieu de l'écraser"
+#, c-format
+msgid "object %s not found"
+msgstr "objet %s non trouvé"
 
-msgid "use atomic transaction to update references"
-msgstr "utiliser une transaction atomique pour mettre à jour les références"
-
-msgid "path to upload pack on remote end"
-msgstr "chemin vers lequel télécharger le paquet sur le poste distant"
-
-msgid "force overwrite of local reference"
-msgstr "forcer l'écrasement de la branche locale"
-
-msgid "fetch from multiple remotes"
-msgstr "récupérer depuis plusieurs dépôts distants"
-
-msgid "fetch all tags and associated objects"
-msgstr "récupérer toutes les étiquettes et leurs objets associés"
-
-msgid "do not fetch all tags (--no-tags)"
-msgstr "ne pas récupérer toutes les étiquettes (--no-tags)"
-
-msgid "number of submodules fetched in parallel"
-msgstr "nombre de sous-modules récupérés en parallèle"
-
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr ""
-"modifier le spécificateur de référence pour placer les références dans refs/"
-"prefetch/"
-
-msgid "prune remote-tracking branches no longer on remote"
-msgstr ""
-"éliminer les branches de suivi distant si la branche n'existe plus dans le "
-"dépôt distant"
-
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr ""
-"éliminer les étiquettes locales qui ont disparu du dépôt distant et qui "
-"encombrent les étiquettes modifiées"
-
-msgid "on-demand"
-msgstr "à la demande"
-
-msgid "control recursive fetching of submodules"
-msgstr "contrôler la récupération récursive dans les sous-modules"
-
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "écrire les références récupérées dans le fichier FETCH_HEAD"
-
-msgid "keep downloaded pack"
-msgstr "conserver le paquet téléchargé"
-
-msgid "allow updating of HEAD ref"
-msgstr "permettre la mise à jour de la référence HEAD"
-
-msgid "deepen history of shallow clone"
-msgstr "approfondir l'historique d'un clone superficiel"
-
-msgid "deepen history of shallow repository based on time"
-msgstr "approfondir l'historique d'un clone superficiel en fonction d'une date"
-
-msgid "convert to a complete repository"
-msgstr "convertir en un dépôt complet"
-
-msgid "re-fetch without negotiating common commits"
-msgstr "re-récupérer sans négocier les commits communs"
-
-msgid "prepend this to submodule path output"
-msgstr "préfixer ceci à la sortie du chemin du sous-module"
-
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr ""
-"par défaut pour la récupération récursive de sous-modules (priorité plus "
-"basse que les fichiers de config)"
-
-msgid "accept refs that update .git/shallow"
-msgstr "accepter les références qui mettent à jour .git/shallow"
-
-msgid "refmap"
-msgstr "correspondance de référence"
-
-msgid "specify fetch refmap"
-msgstr "spécifier une correspondance de référence pour la récupération"
-
-msgid "report that we have only objects reachable from this object"
-msgstr "rapporte que nous n'avons que des objets joignables depuis cet objet"
-
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr ""
-"ne pas récupérer le fichier paquet ; à la place, afficher les ancêtres des "
-"sommets de négociation"
-
-msgid "run 'maintenance --auto' after fetching"
-msgstr "lancer 'maintenance --auto' après la récupération"
-
-msgid "check for forced-updates on all updated branches"
-msgstr ""
-"vérifier les mises à jour forcées (forced-updates) sur toutes les branches "
-"mises à jour"
-
-msgid "write the commit-graph after fetching"
-msgstr "écrire le graphe de commits après le rapatriement"
-
-msgid "accept refspecs from stdin"
-msgstr "lire les spécificateurs de référence depuis l'entrée standard"
-
-msgid "couldn't find remote ref HEAD"
-msgstr "impossible de trouver la référence HEAD distante"
-
-#, c-format
-msgid "object %s not found"
-msgstr "objet %s non trouvé"
-
-msgid "[up to date]"
-msgstr "[à jour]"
+msgid "[up to date]"
+msgstr "[à jour]"
 
 msgid "[rejected]"
 msgstr "[rejeté]"
@@ -6008,10 +5956,6 @@ msgstr ""
 "%s rejeté parce que les  mises à jour de racines superficielles ne sont pas "
 "permises"
 
-#, c-format
-msgid "From %.*s\n"
-msgstr "Depuis %.*s\n"
-
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -6103,6 +6047,119 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr "Vous devez spécifier un nom d'étiquette"
 
+msgid "fetch from all remotes"
+msgstr "récupérer depuis tous les dépôts distants"
+
+msgid "set upstream for git pull/fetch"
+msgstr "définir la branche amont pour git pull/fetch"
+
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "ajouter à .git/FETCH_HEAD au lieu de l'écraser"
+
+msgid "use atomic transaction to update references"
+msgstr "utiliser une transaction atomique pour mettre à jour les références"
+
+msgid "path to upload pack on remote end"
+msgstr "chemin vers lequel télécharger le paquet sur le poste distant"
+
+msgid "force overwrite of local reference"
+msgstr "forcer l'écrasement de la branche locale"
+
+msgid "fetch from multiple remotes"
+msgstr "récupérer depuis plusieurs dépôts distants"
+
+msgid "fetch all tags and associated objects"
+msgstr "récupérer toutes les étiquettes et leurs objets associés"
+
+msgid "do not fetch all tags (--no-tags)"
+msgstr "ne pas récupérer toutes les étiquettes (--no-tags)"
+
+msgid "number of submodules fetched in parallel"
+msgstr "nombre de sous-modules récupérés en parallèle"
+
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"modifier le spécificateur de référence pour placer les références dans refs/"
+"prefetch/"
+
+msgid "prune remote-tracking branches no longer on remote"
+msgstr ""
+"élaguer les branches de suivi distant si la branche n'existe plus dans le "
+"dépôt distant"
+
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr ""
+"élaguer les étiquettes locales qui ont disparu du dépôt distant et qui "
+"encombrent les étiquettes modifiées"
+
+msgid "on-demand"
+msgstr "à la demande"
+
+msgid "control recursive fetching of submodules"
+msgstr "contrôler la récupération récursive dans les sous-modules"
+
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "écrire les références récupérées dans le fichier FETCH_HEAD"
+
+msgid "keep downloaded pack"
+msgstr "conserver le paquet téléchargé"
+
+msgid "allow updating of HEAD ref"
+msgstr "permettre la mise à jour de la référence HEAD"
+
+msgid "deepen history of shallow clone"
+msgstr "approfondir l'historique d'un clone superficiel"
+
+msgid "deepen history of shallow repository based on time"
+msgstr "approfondir l'historique d'un clone superficiel en fonction d'une date"
+
+msgid "convert to a complete repository"
+msgstr "convertir en un dépôt complet"
+
+msgid "re-fetch without negotiating common commits"
+msgstr "re-récupérer sans négocier les commits communs"
+
+msgid "prepend this to submodule path output"
+msgstr "préfixer ceci à la sortie du chemin du sous-module"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"par défaut pour la récupération récursive de sous-modules (priorité plus "
+"basse que les fichiers de config)"
+
+msgid "accept refs that update .git/shallow"
+msgstr "accepter les références qui mettent à jour .git/shallow"
+
+msgid "refmap"
+msgstr "correspondance de référence"
+
+msgid "specify fetch refmap"
+msgstr "spécifier une correspondance de référence pour la récupération"
+
+msgid "report that we have only objects reachable from this object"
+msgstr "rapporte que nous n'avons que des objets joignables depuis cet objet"
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+"ne pas récupérer le fichier paquet ; à la place, afficher les ancêtres des "
+"sommets de négociation"
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr "lancer 'maintenance --auto' après la récupération"
+
+msgid "check for forced-updates on all updated branches"
+msgstr ""
+"vérifier les mises à jour forcées (forced-updates) sur toutes les branches "
+"mises à jour"
+
+msgid "write the commit-graph after fetching"
+msgstr "écrire le graphe de commits après le rapatriement"
+
+msgid "accept refspecs from stdin"
+msgstr "lire les spécificateurs de référence depuis l'entrée standard"
+
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only nécessite au moins un --negotiation-tip=*"
 
@@ -6112,6 +6169,10 @@ msgstr "une profondeur négative dans --deepen n'est pas supportée"
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "--unshallow sur un dépôt complet n'a pas de sens"
 
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "échec de récupération des colis depuis '%s'"
+
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all n'accepte pas d'argument de dépôt"
 
@@ -6214,8 +6275,14 @@ msgstr "afficher seulement les références qui contiennent le commit"
 msgid "print only refs which don't contain the commit"
 msgstr "afficher seulement les références qui ne contiennent pas le commit"
 
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=<config> <arguments-de-commande>"
+msgid "read reference patterns from stdin"
+msgstr "lire les motifs de références depuis l'entrée standard"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "arguments inconnus fournis avec l'option --stdin"
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<config> [--] <arguments>"
 
 msgid "config"
 msgstr "config"
@@ -6226,6 +6293,10 @@ msgstr "clé de config qui stocke la liste des chemins de dépôts"
 msgid "missing --config=<config>"
 msgstr "--config=<config> manquant"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "config incorrecte --config=%s"
+
 msgid "unknown"
 msgstr "inconnu"
 
@@ -6372,22 +6443,39 @@ msgstr "%s : la HEAD détachée ne pointe sur rien"
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "note : %s pointe sur une branche non-née (%s)"
 
-msgid "Checking cache tree"
-msgstr "Vérification de l'arbre cache"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Vérification de l'arbre cache de %s"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s : pointer sha1 invalide dans l'arbre de cache"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s : pointer sha1 invalide dans l'arbre de cache de %s"
 
 msgid "non-tree in cache-tree"
 msgstr "non-arbre dans l'arbre de cache"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s : pointeur sha1 invalide dans resolve-undo"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s : pointeur sha1 invalide dans resolve-undo de %s"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "impossible de charger le rev-index pour le paquet '%s'"
 
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<options>] [<objet>...]"
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "rev-index invalide pour le paquet '%s'"
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<objec>...]"
 
 msgid "show unreachable objects"
 msgstr "afficher les objets inaccessibles"
@@ -6443,12 +6531,6 @@ msgstr "git fsmonitor--daemon start [<options>]"
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [<options>]"
 
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
-
 #, c-format
 msgid "value of '%s' out of range: %d"
 msgstr "valeur de '%s' hors de gamme : %d"
@@ -6565,11 +6647,14 @@ msgstr ""
 "%s"
 
 msgid "prune unreferenced objects"
-msgstr "éliminer les objets non référencés"
+msgstr "élaguer les objets non référencés"
 
 msgid "pack unreferenced objects separately"
 msgstr "empaqueter les objets non référencés séparément"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "avec --cruft, limiter la taille des nouveaux paquets déchets"
+
 msgid "be more thorough (increased runtime)"
 msgstr "être plus consciencieux (durée de traitement allongée)"
 
@@ -6694,8 +6779,20 @@ msgstr "lancer une tâche spécifique"
 msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr "--auto et --schedule=<fréquence> sont mutuellement exclusifs"
 
-msgid "failed to run 'git config'"
-msgstr "échec du lancement de 'git config'"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "impossible d'ajouter la valeur '%s' de '%s'"
+
+msgid "return success even if repository was not registered"
+msgstr "renvoyer un succès même si le dépôt n'était pas enregistré"
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "impossible de retirer la valeur '%s' de '%s'"
+
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "le dépôt '%s' n'est pas enregistré"
 
 #, c-format
 msgid "failed to expand path '%s'"
@@ -6737,12 +6834,6 @@ msgstr ""
 msgid "'crontab' died"
 msgstr "'crontab' est mort"
 
-msgid "failed to start systemctl"
-msgstr "échec du démarrage de systemctl"
-
-msgid "failed to run systemctl"
-msgstr "échec pour lancer systemctl"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "échec de la suppression de '%s'"
@@ -6751,6 +6842,12 @@ msgstr "échec de la suppression de '%s'"
 msgid "failed to flush '%s'"
 msgstr "échec du flush de '%s'"
 
+msgid "failed to start systemctl"
+msgstr "échec du démarrage de systemctl"
+
+msgid "failed to run systemctl"
+msgstr "échec pour lancer systemctl"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "argument '%s' de --scheduler non reconnu"
@@ -6776,6 +6873,9 @@ msgstr "planificateur"
 msgid "scheduler to trigger git maintenance run"
 msgstr "planificateur qui lancera les maintenances git"
 
+msgid "failed to set up maintenance schedule"
+msgstr "impossible de mettre en place la planification de maintenance"
+
 msgid "failed to add repo to global config"
 msgstr "échec de l'ajout du dépôt à la config globale"
 
@@ -6850,8 +6950,8 @@ msgstr "traiter les fichiers binaires avec les filtres textconv"
 msgid "search in subdirectories (default)"
 msgstr "rechercher dans les sous-répertoires (défaut)"
 
-msgid "descend at most <depth> levels"
-msgstr "descendre au plus de <profondeur> dans l'arborescence"
+msgid "descend at most <n> levels"
+msgstr "descendre au plus de <n> niveaux"
 
 msgid "use extended POSIX regular expressions"
 msgstr "utiliser des expressions régulières étendues POSIX"
@@ -6996,11 +7096,14 @@ msgid "both --cached and trees are given"
 msgstr "--cached et des arbres sont fournis en même temps"
 
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <type>] [-w] [--path=<fichier> | --no-filters] [--stdin] "
-"[--] <fichier>..."
+"git hash-object [-t <type>] [-w] [--path=<fichier> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <fichier>..."
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
 
 msgid "object type"
 msgstr "type d'objet"
@@ -7133,13 +7236,19 @@ msgstr "usage : %s%s"
 msgid "'git help config' for more information"
 msgstr "'git help config' pour plus d'information"
 
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
 msgstr ""
-"git hook run [--ignore-missing] <nom-de-crochet> [-- <arguments-de-crochet>]"
+"git hook run [--ignore-missing] [--to-stdin=<chemin>] <nom-de-crochet> [-- "
+"<arguments-de-crochet>]"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "ignorer silencieusement le <nom-de-crochet> requis manquant"
 
+msgid "file to read into hooks' stdin"
+msgstr "fichier à la lire dans l'entrée standard du crochet"
+
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "type d'objet non correspondant à %s"
@@ -7357,83 +7466,16 @@ msgstr "--verify sans nom de fichier paquet donné"
 msgid "fsck error in pack objects"
 msgstr "erreur de fsck dans les objets paquets"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "impossible de faire un stat du modèle '%s'"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "impossible d'ouvrir le répertoire '%s'"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "impossible de lire le lien '%s'"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "impossible de créer un lien symbolique de '%s' '%s'"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "impossible de copier '%s' vers '%s'"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "modèle %s ignoré"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "modèles non trouvés dans %s"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "pas de copie des modèles depuis '%s' : %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "nom de branche initiale invalide : '%s'"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "impossible de traiter le fichier de type %d"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "impossible de déplacer %s vers %s"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "essai de réinitialisation du dépôt avec une empreinte différente"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s existe déjà"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-initialisation : --initial-branch=%s ignoré"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "Dépôt Git existant partagé réinitialisé dans %s%s\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Dépôt Git existant réinitialisé dans %s%s\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Dépôt Git vide partagé initialisé dans %s%s\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Dépôt Git vide initialisé dans %s%s\n"
-
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<répertoire-modèle>] [--"
-"shared[=<permissions>]] [<répertoire>]"
+"git init [-q | --quiet] [--bare] [--template=<répertoire-modèle>]\n"
+"         [--separate-git-dir <rép-git>] [--object-format=<format>]\\n\"\n"
+"         [-b <nom-de-branche> | --initial-branch=<nom-de-branche>]\\n\"\n"
+"         [--shared[=<permissions>]] [<répertoire>]"
 
 msgid "permissions"
 msgstr "permissions"
@@ -7474,11 +7516,14 @@ msgid "--separate-git-dir incompatible with bare repository"
 msgstr "--separate-git-dir est incompatible avec un dépôt nu"
 
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<symbole>[(=|:)<valeur>])...] [<fichier>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<clé>|<alias-de-clé>)"
+"[(=|:)<valeur>])...]\n"
+"                       [--parse] [<fichier>...]"
 
 msgid "edit files in place"
 msgstr "éditer les fichiers sur place"
@@ -7486,6 +7531,9 @@ msgstr "éditer les fichiers sur place"
 msgid "trim empty trailers"
 msgstr "éliminer les lignes de fin vides"
 
+msgid "placement"
+msgstr "placement"
+
 msgid "where to place the new trailer"
 msgstr "où placer les nouvelles lignes terminales"
 
@@ -7498,17 +7546,19 @@ msgstr "action si les lignes terminales manquent"
 msgid "output only the trailers"
 msgstr "éliminer les lignes terminales vides"
 
-msgid "do not apply config rules"
-msgstr "ne pas appliquer les règles de la configuration"
+msgid "do not apply trailer.* configuration variables"
+msgstr "ne pas appliquer les variables de configuration trailer.*"
 
-msgid "join whitespace-continued values"
-msgstr "joindre les valeurs continuées avec des caractères blancs"
+msgid "reformat multiline trailer values as single-line values"
+msgstr ""
+"reformatter les valeurs de ligne terminales multi-lignes en valeurs sur une "
+"seule ligne"
 
-msgid "set parsing options"
-msgstr "paramètres d'analyse"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "alias pour --only-trailers --only-input --unfold"
 
-msgid "do not treat --- specially"
-msgstr "ne pas traiter spécialement ---"
+msgid "do not treat \"---\" as the end of input"
+msgstr "ne pas traiter \"---\" comme la fin d'entrée"
 
 msgid "trailer(s) to add"
 msgstr "ligne(s) de fin à ajouter"
@@ -7598,6 +7648,10 @@ msgstr "exactement une plage nécessaire"
 msgid "not a range"
 msgstr "ceci n'est pas une plage"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "lecture du fichier de description de branche '%s' impossible"
+
 msgid "cover letter needs email format"
 msgstr "la lettre de motivation doit être au format courriel"
 
@@ -7704,6 +7758,9 @@ msgstr ""
 "générer des parties de la lettre d'introduction à partir de la description "
 "de la branche"
 
+msgid "use branch description from file"
+msgstr "utiliser la description de branche dans le fichier"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "utiliser [<préfixe>] au lieu de [PATCH]"
 
@@ -7869,6 +7926,10 @@ msgstr ""
 "Impossible de trouver une branche distante suivie, merci de spécifier "
 "<branche_amont> manuellement.\n"
 
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "impossible d'obtenir l'information d'objet pour '%s'"
+
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "mauvais format ls-files : l'élément '%s' ne commence pas par '('"
@@ -7952,7 +8013,7 @@ msgid "if any <file> is not in the index, treat this as an error"
 msgstr "si un <fichier> n'est pas dans l'index, traiter cela comme une erreur"
 
 msgid "tree-ish"
-msgstr "arbre ou apparenté"
+msgstr "arbre-esque"
 
 msgid "pretend that paths removed since <tree-ish> are still present"
 msgstr ""
@@ -7977,12 +8038,12 @@ msgstr ""
 
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<dépôt> [<f>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<clé>]\n"
+"              [--symref] [<dépôt> [<motif>...]]"
 
 msgid "do not print remote URL"
 msgstr "ne pas afficher les URL distantes"
@@ -8016,10 +8077,6 @@ msgstr "afficher la réf sous-jacente en plus de l'objet pointé par elle"
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
 msgstr "git ls-tree [<options>] <arbre ou apparenté> [<chemin>...]"
 
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "impossible d'obtenir l'information d'objet pour '%s'"
-
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "mauvais format ls-tree : l'élément '%s' ne commence pas par '('"
@@ -8116,12 +8173,12 @@ msgstr "git merge-base [-a | --all] <commit> <commit>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <commit>..."
 
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <validation>..."
-
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <validation> <validation>"
 
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <validation>..."
+
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <référence> [<validation>]"
 
@@ -8150,6 +8207,9 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "envoyer les résultats sur la sortie standard"
 
+msgid "use object IDs instead of filenames"
+msgstr "utiliser les IDs d'objet au lieu de noms de fichier"
+
 msgid "use a diff3 based merge"
 msgstr "utiliser une fusion basée sur diff3"
 
@@ -8174,6 +8234,13 @@ msgstr "ne pas avertir à propos des conflits"
 msgid "set labels for file1/orig-file/file2"
 msgstr "définir les labels pour fichier1/fichier-orig/fichier2"
 
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "l'objet '%s' n'existe pas"
+
+msgid "Could not write object file"
+msgstr "impossible d'écrire le fichier d'objet"
+
 #, c-format
 msgid "unknown option %s"
 msgstr "option inconnue %s"
@@ -8229,9 +8296,36 @@ msgstr "lister les noms de fichier sans modes/oids/indexation"
 msgid "allow merging unrelated histories"
 msgstr "permettre la fusion d'historiques sans rapport"
 
+msgid "perform multiple merges, one per line of input"
+msgstr "réaliser des fusions multiples, une par ligne d'entrée"
+
+msgid "specify a merge-base for the merge"
+msgstr "spécifier une base de fusion pour la fusion"
+
+msgid "option=value"
+msgstr "option=valeur"
+
+msgid "option for selected merge strategy"
+msgstr "option pour la stratégie de fusion sélectionnée"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge est incompatible avec d'autres options"
 
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "option de stratégie inconnue : -X%s"
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base est incompatible avec --stdin"
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "ligne en entrée malformée : '%s'."
+
+#, 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>...]"
 
@@ -8292,12 +8386,6 @@ msgstr "stratégie"
 msgid "merge strategy to use"
 msgstr "stratégie de fusion à utiliser"
 
-msgid "option=value"
-msgstr "option=valeur"
-
-msgid "option for selected merge strategy"
-msgstr "option pour la stratégie de fusion sélectionnée"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
 "message de validation de la fusion (pour une fusion sans avance rapide)"
@@ -8358,10 +8446,6 @@ msgstr "Impossible d'écrire l'index."
 msgid "Not handling anything other than two heads merge."
 msgstr "Impossible de gérer autre chose que la fusion de deux têtes."
 
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "option de stratégie inconnue : -X%s"
-
 #, c-format
 msgid "unable to write %s"
 msgstr "impossible d'écrire %s"
@@ -8663,8 +8747,8 @@ msgstr "la destination existe"
 msgid "can not move directory into itself"
 msgstr "impossible de déplacer un répertoire dans lui-même"
 
-msgid "cannot move directory over file"
-msgstr "impossible de déplacer un répertoire sur un fichier"
+msgid "destination already exists"
+msgstr "la destination existe déjà"
 
 msgid "source directory is empty"
 msgstr "le répertoire source est vide"
@@ -8745,22 +8829,26 @@ msgid "git notes [--ref <notes-ref>] [list [<object>]]"
 msgstr "git notes [--ref <références-notes>] [list [<object>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <références-notes>] add [-f] [--allow-empty] [-m <message> "
-"| -F <fichier> | (-c | -C) <objet>] [<objet>]"
+"git notes [--ref <référence-notes>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<coupure-paragraphe>] [--[no-]stripspace] [-m "
+"<message> | -F <fichier> | (-c | -C) <objet>] [<objet>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <références-notes>] copy [-f] <depuis-objet> <vers-objet>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <références-notes>] append [--allow-empty] [-m <message> | -"
-"F <fichier> | (-c | -C) <objet>] [<objet>]"
+"git notes [--ref <références-notes>] append [--allow-empty] [--"
+"[no-]separator|--separator=<coupure-paragraphe>] [--[no-]stripspace]-m "
+"<message> | -F <fichier> | (-c | -C) <objet>] [<objet>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref <références-notes>] edit [--allow-empty] [<objet>]"
@@ -8859,10 +8947,6 @@ msgstr "impossible de lire l'objet '%s'."
 msgid "cannot read note data from non-blob object '%s'."
 msgstr "impossible de lire les informations de note d'un objet non-blob '%s'."
 
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "ligne en entrée malformée : '%s'."
-
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
 msgstr "impossible de copier les notes de '%s' vers '%s'"
@@ -8896,6 +8980,15 @@ msgstr "permettre de stocker une note vide"
 msgid "replace existing notes"
 msgstr "remplacer les notes existantes"
 
+msgid "<paragraph-break>"
+msgstr "<séparateur-paragraphe>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "insérer <séparateur-paragraphe> entre les paragraphes"
+
+msgid "remove unnecessary whitespace"
+msgstr "retirer les espaces inutiles"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -9055,16 +9148,15 @@ msgstr "utiliser les notes depuis <références-notes>"
 msgid "unknown subcommand: `%s'"
 msgstr "sous-commande inconnue : `%s'"
 
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects --stdout [options...] [< <liste-références> | < <liste-"
+"git pack-objects --stdout [<options>] [< <liste-références> | < <liste-"
 "objets>]"
 
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects [options...] base-name [< <liste-références> | < <liste-"
+"git pack-objects [<options>] <nom-de-base> [< <liste-références> | < <liste-"
 "objets>]"
 
 #, c-format
@@ -9256,6 +9348,12 @@ msgstr "version d'index non supportée %s"
 msgid "bad index version '%s'"
 msgstr "mauvaise version d'index '%s'"
 
+msgid "show progress meter during object writing phase"
+msgstr "afficher la barre de progression durant la phase d'écrite des objets"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "similaire à --all-progress quand la barre de progression est affichée"
+
 msgid "<version>[,<offset>]"
 msgstr "<version>[,<décalage>]"
 
@@ -9410,9 +9508,6 @@ msgstr "la taille limite minimale d'un paquet est 1 MiB"
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin ne peut pas être utilisé pour construire un paquet indexable"
 
-msgid "cannot use --filter without --stdout"
-msgstr "impossible d'utiliser --filter sans --stdout"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "impossible d'utiliser --filter avec --stdin-packs"
 
@@ -9426,9 +9521,6 @@ msgstr "impossible d'utiliser une liste interne de révisions avec --cruft"
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "impossible d'utiliser --stdin-packs avec --cruft"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "impossible d'utiliser --max-pack-size avec --cruft"
-
 msgid "Enumerating objects"
 msgstr "Énumération des objets"
 
@@ -9453,20 +9545,44 @@ msgstr ""
 "sur la ligne de commande pour nous avertir par\n"
 "un courriel à <git@vger.kernel.org>. Merci.\n"
 
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<options>]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "refus de lancer sans --i-still-use-this"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <motif>] [--exclude <motif>]"
 
 msgid "pack everything"
 msgstr "empaqueter tout"
 
 msgid "prune loose refs (default)"
-msgstr "éliminer les références perdues (défaut)"
+msgstr "élaguer les références perdues (défaut)"
+
+msgid "references to include"
+msgstr "références à inclure"
+
+msgid "references to exclude"
+msgstr "références à exclure"
+
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+msgid "use the unstable patch-id algorithm"
+msgstr "utiliser l'algorithme instable patch-id"
+
+msgid "use the stable patch-id algorithm"
+msgstr "utiliser l'algorithme stable patch-id"
+
+msgid "don't strip whitespace from the patch"
+msgstr "ne pas retirer les espaces blancs de la rustine"
 
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr "git prune [-n] [-v] [--progress] [--expire <heure>] [--] [<head>…]"
 
 msgid "report pruned objects"
-msgstr "afficher les objets éliminés"
+msgstr "afficher les objets élagués"
 
 msgid "expire objects older than <time>"
 msgstr "faire expirer les objets plus vieux que <heure>"
@@ -9475,7 +9591,7 @@ msgid "limit traversal to objects outside promisor packfiles"
 msgstr "limiter la traversée aux objets hors des fichiers paquets prometteurs"
 
 msgid "cannot prune in a precious-objects repo"
-msgstr "impossible de nettoyer dans un dépôt d'objets précieux"
+msgstr "impossible d'élaguer dans un dépôt d'objets précieux"
 
 msgid "git pull [<options>] [<repository> [<refspec>...]]"
 msgstr "git pull [<options>] [<dépôt> [<spécification-de-référence>...]]"
@@ -9507,6 +9623,12 @@ msgstr "forcer l'écrasement de la branche locale"
 msgid "number of submodules pulled in parallel"
 msgstr "nombre de sous-modules tirés en parallèle"
 
+msgid "use IPv4 addresses only"
+msgstr "n'utiliser que des adresses IPv4"
+
+msgid "use IPv6 addresses only"
+msgstr "n'utiliser que des adresses IPv6"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9617,8 +9739,8 @@ msgstr ""
 msgid "pull with rebase"
 msgstr "tirer avec un rebasage"
 
-msgid "please commit or stash them."
-msgstr "veuillez les valider ou les remiser."
+msgid "Please commit or stash them."
+msgstr "Veuillez les valider ou les remiser."
 
 #, c-format
 msgid ""
@@ -9682,9 +9804,8 @@ msgstr ""
 
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
@@ -9778,46 +9899,43 @@ msgstr ""
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Les mises à jour ont été rejetées car la pointe de la branche courante est "
+"Les mises à jour ont été rejetées car le sommet de la branche actuelle est "
 "derrière\n"
-"son homologue distant. Intégrez les changements distants (par exemple 'git "
-"pull ...')\n"
-"avant de pousser à nouveau.\n"
+"son homologue distant. Si vous souhaitez intégrer les changements distants,\n"
+"utilisez 'git pull' avant de pousser à nouveau.\n"
 "Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus "
 "d'information."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Les mises à jour ont été rejetées car la pointe de la branche courante est "
+"Les mises à jour ont été rejetées, car le sommet d'une branche poussée est "
 "derrière\n"
-"son homologue distant. Extrayez cette branche et intégrez les changements "
-"distants\n"
-"(par exemple 'git pull ...') avant de pousser à nouveau.\n"
+"son homologue distant. Si vous souhaitez intégrer les changements distants,\n"
+"\"utilisez 'git pull' avant de pousser à nouveau.\n"
 "Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus "
 "d'information."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Les mises à jour ont été rejetées car la branche distante contient du "
-"travail que\n"
-"vous n'avez pas en local. Ceci est généralement causé par un autre dépôt "
-"poussé\n"
-"vers la même référence. Vous pourriez intégrer d'abord les changements "
-"distants\n"
-"(par exemple 'git pull ...') avant de pousser à nouveau.\n"
+"Les mises à jour ont été rejetées car le distant contient du travail que "
+"vous\n"
+"n'avez pas localement. La cause probable est que quelqu'un a déjà poussé sur "
+"la même réf.\n"
+"depuis un autre dépôt. Si vous souhaitez intégrer les changements distants,\n"
+"\"utilisez 'git pull' avant de pousser à nouveau.\n"
 "Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus "
 "d'information."
 
@@ -9838,15 +9956,18 @@ msgstr ""
 "vers un objet qui n'est pas un commit, sans utiliser l'option '--force'.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Les mises à jour ont été rejetées, car la pointe de la branche\n"
-"de suivi a été mise à jour depuis la dernière extraction. Intégrez\n"
-"ces changements localement (par exemple 'git pull ...') avant de\n"
-"forcer à nouveau une mise à jour.\n"
+"Les mises à jour ont été rejetées, car le sommet de la branche de suivi à "
+"distance a\n"
+"été mis à jour depuis la dernière extraction. Si vous souhaitez intégrer les "
+"changements distants,\n"
+"\"utilisez 'git pull' avant de pousser à nouveau.\n"
+"Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus "
+"d'information."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -9856,6 +9977,13 @@ msgstr "Poussée vers %s\n"
 msgid "failed to push some refs to '%s'"
 msgstr "impossible de pousser des références vers '%s'"
 
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"la récursion dans le sous-module avec push.recurseSubmodules=only ; "
+"utilisation de on-demande à la place"
+
 #, c-format
 msgid "invalid value for '%s'"
 msgstr "valeur invalide pour '%s'"
@@ -9863,8 +9991,8 @@ msgstr "valeur invalide pour '%s'"
 msgid "repository"
 msgstr "dépôt"
 
-msgid "push all refs"
-msgstr "pousser toutes les références"
+msgid "push all branches"
+msgstr "pousser toutes les branches"
 
 msgid "mirror all refs"
 msgstr "refléter toutes les références"
@@ -9872,9 +10000,10 @@ msgstr "refléter toutes les références"
 msgid "delete refs"
 msgstr "supprimer les références"
 
-msgid "push tags (can't be used with --all or --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
 msgstr ""
-"pousser les étiquettes (ne peut pas être utilisé avec --all ou --mirror)"
+"pousser les étiquettes (ne peut pas être utilisé avec --all, --branches ou --"
+"mirror)"
 
 msgid "force updates"
 msgstr "forcer les mises à jour"
@@ -9901,7 +10030,7 @@ msgid "set upstream for git pull/status"
 msgstr "définir la branche amont pour git pull/status"
 
 msgid "prune locally removed refs"
-msgstr "éliminer les références locales supprimées"
+msgstr "élaguer les références locales supprimées"
 
 msgid "bypass pre-push hook"
 msgstr "éviter d'utiliser le crochet pre-push"
@@ -9996,14 +10125,15 @@ msgid "need two commit ranges"
 msgstr "plage entre deux commits requise"
 
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
 "git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
-"prefix=<préfixe>) [-u | -i]] [--no-sparse-checkout] [--index-"
-"output=<fichier>] (--empty | <arbre-esque1> [<arbre-esque2> [<arbre-"
-"esque3>]])"
+"prefix=<préfixe>)\n"
+"              [-u | -i]] [--index-output=<fichier>] [--no-sparse-checkout]\n"
+"              (--empty | <arbre-esque1> [<arbre-esque2> [<arbre-esque3>]])"
 
 msgid "write resulting index to <file>"
 msgstr "écrire l'index résultant dans <fichier>"
@@ -10094,8 +10224,8 @@ msgid "%s requires the merge backend"
 msgstr "%s requiert un moteur de fusion"
 
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "impossible d'accéder 'onto' : '%s'"
+msgid "invalid onto: '%s'"
+msgstr "destination invalide : '%s'"
 
 #, c-format
 msgid "invalid orig-head: '%s'"
@@ -10140,10 +10270,18 @@ msgstr ""
 "\n"
 "Résultat, git ne peut pas les rebaser."
 
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Mode de rebase-merges inconnu : %s"
+
 #, c-format
 msgid "could not switch to %s"
 msgstr "impossible de basculer vers %s"
 
+msgid "apply options and merge options cannot be used together"
+msgstr ""
+"Les options d'apply et celles de merge ne peuvent pas être utilisées ensemble"
+
 #, c-format
 msgid ""
 "unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
@@ -10152,6 +10290,15 @@ msgstr ""
 "type vide non connu '%s' ; les valeurs valides sont \"drop\" (abandonner), "
 "\"keep\" (garder) et \"ask\" (demander)."
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"--rebase-merges avec un argument chaîne vide est obsolète et cessera de "
+"fonctionner dans une version future de Git. Utilisez à la place --rebase-"
+"merges sans argument, qui a le même effet."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10372,16 +10519,26 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "l'option `C' attend un valeur numérique"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Mode inconnu : %s"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+"les options d'application sont incompatibles avec rebase.autoSquash. "
+"Considérez l'ajout de --no-autosquash"
 
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy requiert --merge ou --interactive"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+"les options d'application sont incompatibles avec rebase.rebaseMerges. "
+"Considérez l'ajout de --no-rebase-merges"
 
-msgid "apply options and merge options cannot be used together"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
 msgstr ""
-"Les options d'apply et celles de merge ne peuvent pas être utilisées ensemble"
+"les options d'application sont incompatibles avec rebase.updateRefs. "
+"Considérez l'ajout de --no-update-refs"
 
 #, c-format
 msgid "Unknown rebase backend: %s"
@@ -10405,8 +10562,8 @@ msgstr "pas de branche ou commit '%s'"
 msgid "No such ref: %s"
 msgstr "Référence inexistante : %s"
 
-msgid "Could not resolve HEAD to a revision"
-msgstr "Impossible de résoudre le commit HEAD vers un révision"
+msgid "Could not resolve HEAD to a commit"
+msgstr "impossible de résoudre HEAD en un commit"
 
 #, c-format
 msgid "'%s': need exactly one merge base with branch"
@@ -10420,9 +10577,6 @@ msgstr "'%s': exactement une base de fusion nécessaire"
 msgid "Does not point to a valid commit '%s'"
 msgstr "Ne pointe pas sur une validation valide : '%s'"
 
-msgid "Please commit or stash them."
-msgstr "Veuillez les valider ou les remiser."
-
 msgid "HEAD is up to date."
 msgstr "HEAD est à jour."
 
@@ -10682,12 +10836,13 @@ msgstr "argument miroir inconnu : %s"
 msgid "fetch the remote branches"
 msgstr "rapatrier les branches distantes"
 
-msgid "import all tags and associated objects when fetching"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
 msgstr ""
-"importer toutes les étiquettes et les objets associés lors du rapatriement"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "ou ne rapatrier aucune étiquette (--no-tags)"
+"importer toutes les étiquettes et les objets associés lors de la "
+"récupération\n"
+"ou ne récupérer aucune étiquette (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "branche(s) à suivre"
@@ -10981,14 +11136,14 @@ msgstr "URL : %s"
 
 #, c-format
 msgid " * [would prune] %s"
-msgstr " * [serait éliminé] %s"
+msgstr " * [élaguerait] %s"
 
 #, c-format
 msgid " * [pruned] %s"
-msgstr " * [éliminé] %s"
+msgstr " * [élagué] %s"
 
 msgid "prune remotes after fetching"
-msgstr "éliminer les distants après le rapatriement"
+msgstr "élagué les distants après la récupération"
 
 #, c-format
 msgid "No such remote '%s'"
@@ -11082,6 +11237,14 @@ msgstr "impossible d'ouvrir le fichier temporaire %s en écriture"
 msgid "could not close refs snapshot tempfile"
 msgstr "impossible de fermer le fichier temporaire d'instantané des réfs"
 
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "impossible de revenir la bitmap obsolète : %s"
+
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "le préfixe %s ne commence pas avec objdir %s"
+
 msgid "pack everything in a single pack"
 msgstr "empaqueter tout dans un seul paquet"
 
@@ -11095,8 +11258,8 @@ msgstr ""
 msgid "approxidate"
 msgstr "date approximative"
 
-msgid "with -C, expire objects older than this"
-msgstr "avec -C, faire expirer les objets plus vieux que celui-ci"
+msgid "with --cruft, expire objects older than this"
+msgstr "avec --cruft, faire expirer les objets plus vieux que celui-ci"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "supprimer les paquets redondants et lancer git-prune-packed"
@@ -11156,19 +11319,29 @@ 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"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "impossible de supprimer les paquets dans un dépôt d'objets précieux"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "l'option '%s' ne peut être utilisé qu'avec '%s'"
+
 msgid "Nothing new to pack."
 msgstr "Rien de neuf à empaqueter."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "le préfixe %s ne commence pas avec objdir %s"
+msgid "renaming pack to '%s' failed"
+msgstr "le renommage du paquet en '%s' a échoué"
 
 #, c-format
-msgid "missing required file: %s"
-msgstr "fichier nécessaire manquant : %s"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects n'a pas écrit un fichier '%s' pour la paquet %s-%s"
 
 #, c-format
 msgid "could not unlink: %s"
@@ -11362,9 +11535,10 @@ msgstr "--convert-graft-file ne supporte aucun argument"
 msgid "only one pattern can be given with -l"
 msgstr "-l n'accepte qu'un motifs"
 
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 msgstr ""
-"git rerere [clear | forget <chemin>... | status | remaining | diff | gc]"
+"git rerere [clear | forget <chemin>... | diff | status | remaining | gc]"
 
 msgid "register clean resolutions in index"
 msgstr "enregistrer des résolutions propres dans l'index"
@@ -11572,24 +11746,44 @@ msgstr "--prefix exige un argument"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "mode inconnu pour --abbrev-ref : %s"
 
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden ne peut être utilisé avec --branches"
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden ne peut pas être utilisé avec --tags"
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden ne peut pas être utilisé avec --remotes"
+
 msgid "this operation must be run in a work tree"
 msgstr "cette opération doit être effectuée dans un arbre de travail"
 
+msgid "Could not read the index"
+msgstr "Impossible de lire l'index"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "mode inconnu pour --show-object-format : %s"
 
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<options>] <commit ou apparenté>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <numéro-de-parent>] [-s] [-S[<id-clé>]] "
+"<commit>..."
 
-msgid "git revert <subcommand>"
-msgstr "git revert <sous-commande>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
 
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<options>] <commit ou apparenté>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <numéro-de-parent>] [-s] [-x] [--ff]\n"
+"                [-S[<clé-id>]] <commit>..."
 
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <sous-commande>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
@@ -11650,8 +11844,14 @@ msgstr "revert a échoué"
 msgid "cherry-pick failed"
 msgstr "le picorage a échoué"
 
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<options>] [--] <fichier>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<fichier> [--pathspec-file-nul]]\n"
+"       [--] [<spéc-de-chemin>...]"
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -11726,16 +11926,21 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true | false | if-asked)]\n"
 "              [<hôte>:]<répertoire> (--all | <réf>...)"
 
 msgid "remote name"
 msgstr "nom distant"
 
+msgid "push all refs"
+msgstr "pousser toutes les références"
+
 msgid "use stateless RPC protocol"
 msgstr "utiliser un protocole RPC sans état"
 
@@ -11755,8 +11960,9 @@ msgid "using multiple --group options with stdin is not supported"
 msgstr ""
 "l'utilisation de plusieurs options --group avec stdin n'est pas supportée"
 
-msgid "using --group=trailer with stdin is not supported"
-msgstr "l'utilisation de --group=trailer avec stdin n'est pas supportée"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "l'utilisation de %s avec stdin n'est pas supportée"
 
 #, c-format
 msgid "unknown group type: %s"
@@ -11795,12 +12001,14 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<quand>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rév> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rév> | <glob>)...]"
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<référence>]"
@@ -11902,21 +12110,44 @@ msgid "Unknown hash algorithm"
 msgstr "Algorithme d'empreinte inconnu"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<motif>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<motif>...]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<motif>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <réf>"
+
+msgid "reference does not exist"
+msgstr "la référence n'existe pas"
+
+msgid "failed to look up reference"
+msgstr "échec de la recherche de la référence"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "afficher seulement les étiquettes (peut être combiné avec heads)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "afficher seulement les têtes (peut être combiné avec tags)"
 
+msgid "check for reference existence without resolving"
+msgstr "vérifier l'existence de la référence sans la résoudre"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr ""
 "vérification de référence plus stricte, nécessite un chemin de référence "
@@ -11940,8 +12171,16 @@ msgstr ""
 "afficher les références de l'entrée standard qui ne sont pas dans le dépôt "
 "local"
 
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "les options '%s', '%s' et '%s' sont mutuellement exclusives"
+
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 
 msgid "this worktree is not sparse"
 msgstr "cet arbre de travail n'est pas clairsemé"
@@ -12071,67 +12310,75 @@ msgstr ""
 msgid "error while refreshing working directory"
 msgstr "erreur lors du rafraîchissement du répertoire de travail"
 
-msgid "git stash list [<options>]"
-msgstr "git stash list [<options>]"
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <fichier>]"
 
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<options>] [<remise>]"
+msgid "terminate input and output files by a NUL character"
+msgstr "terminer les fichiers en entrée et en sortie par un caractère NUL"
 
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<remise>]"
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"quand utilisé avec --rules-file, interpréter les motifs comme des motifs en "
+"mode cone"
 
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<remise>]"
+msgid "use patterns in <file> instead of the current ones."
+msgstr "utiliser les motifs dans <fichier> plutôt que les actuels."
 
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <nom-de-branche> [<remise>]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<options-de-log>]"
 
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<fichier> [--pathspec-file-nul]]\n"
-"          [--] [<spécificateur-de-chemin>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<options-de-"
+"diff>] [<stash>]"
 
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<remise>]"
 
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<remise>]"
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<remise>]"
 
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<remise>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<remise>]"
+
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <nom-de-branche> [<remise>]"
 
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <message>] [-q|--quiet] <remise>"
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr "git stash store [(-m | --message) <message>] [-q | --quiet] <remise>"
 
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<fichier> [--pathspec-file-nul]]\n"
 "          [--] [<spécificateur-de-chemin>...]]"
 
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
+
+msgid "git stash create [<message>]"
+msgstr "git stash create [<message>]"
 
 #, c-format
 msgid "'%s' is not a stash-like commit"
@@ -12287,7 +12534,7 @@ msgid "stash staged changes only"
 msgstr "remiser seulement les modifications indexées"
 
 msgid "stash in patch mode"
-msgstr "remiser une mode rustine"
+msgstr "remiser en mode rustine"
 
 msgid "quiet mode"
 msgstr "mode silencieux"
@@ -12613,6 +12860,10 @@ msgstr "Sous-module non fusionné %s non traité"
 msgid "Skipping submodule '%s'"
 msgstr "Sous-module '%s' non traité"
 
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "impossible de cloner le sous-module '%s' sans une URL"
+
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "Impossible de cloner '%s'. Réessai prévu"
@@ -12712,9 +12963,6 @@ msgstr "traverser les sous-modules récursivement"
 msgid "don't fetch new objects from the remote site"
 msgstr "ne pas récupérer les nouveaux objets depuis le site distant"
 
-msgid "path into the working tree"
-msgstr "chemin dans la copie de travail"
-
 msgid "use the 'checkout' update strategy (default)"
 msgstr "utiliser la stratégie de mise à jour 'checkout' (valeur par défaut)"
 
@@ -12750,28 +12998,12 @@ msgstr ""
 "[no-]recommend-shallow] [--reference <dépôt>] [--recursive] [--[no-]single-"
 "branch] [--] [<chemin>...]"
 
-msgid "recurse into submodules"
-msgstr "parcourir récursivement les sous-modules"
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Échec de résolution de HEAD comme référence valide."
 
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<options>] [<chemin>...]"
 
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr "vérifier si écrire dans le fichier .gitmodules est sur"
-
-msgid "unset the config in the .gitmodules file"
-msgstr "désactiver la configuration dans le fichier .gitmodules"
-
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config name [<valeur>]"
-
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <nom>"
-
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr ""
-"veuillez vous assurer que le fichier .gitmodules est dans l'arbre de travail"
-
 msgid "suppress output for setting url of a submodule"
 msgstr "supprimer la sortie lors du paramétrage de l'url d'un sous-module"
 
@@ -12851,6 +13083,10 @@ msgstr "Réactivation du répertoire git local pour le sous-module '%s'\n"
 msgid "unable to checkout submodule '%s'"
 msgstr "Impossible d'extraire le sous-module '%s'"
 
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr ""
+"veuillez vous assurer que le fichier .gitmodules est dans l'arbre de travail"
+
 #, c-format
 msgid "Failed to add submodule '%s'"
 msgstr "Échec d'ajout du sous-module '%s'"
@@ -12903,19 +13139,17 @@ msgstr "l'URL de dépôt : '%s' doit être absolu ou commencer par ./|../"
 msgid "'%s' is not a valid submodule name"
 msgstr "'%s' n'est pas un nom valide de sous-module"
 
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s ne gère pas --super-prefix"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <commande>"
 
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "'%s' n'est pas une sous-commande valide de submodule--helper"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <raison>] <nom> <réf>"
 
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<options>] <nom> [<référence>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <nom>"
 
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <nom>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <nom>"
 
 msgid "suppress error message for non-symbolic (detached) refs"
 msgstr ""
@@ -12927,6 +13161,9 @@ msgstr "supprimer la référence symbolique"
 msgid "shorten ref output"
 msgstr "raccourcir l'affichage de la référence"
 
+msgid "recursively dereference (default)"
+msgstr "déréférencer récursivement (défaut)"
+
 msgid "reason"
 msgstr "raison"
 
@@ -12934,25 +13171,25 @@ msgid "reason of the update"
 msgstr "raison de la mise à jour"
 
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u <id-clé>] [-f] [-m <msg> | -F <fichier>]\n"
-"        <nom-d-étiquette> [<tête>]"
+"git tag [-a | -s | -u <id-clé>] [-f] [-m <msg> | -F <fichier>] [-e]\n"
+"        <nom-d-étiquette> [<commit> | <objet>]"
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d <nom-d-étiquette>..."
 
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <objet>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<motif>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <objet>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<clé>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<motif>...]"
 
 msgid "git tag -v [--format=<format>] <tagname>..."
 msgstr "git tag -v [--format=<format>] <nom-d-étiquette>..."
@@ -13242,6 +13479,9 @@ msgstr "(pour porcelaines) oublier les conflits sauvés et non résolus"
 msgid "write index in this format"
 msgstr "écrire l'index dans ce format"
 
+msgid "report on-disk index format version"
+msgstr "afficher la version du format d'index sur disque"
+
 msgid "enable or disable split index"
 msgstr "activer ou désactiver l'index scindé"
 
@@ -13267,6 +13507,14 @@ msgstr "marquer les fichiers comme valides pour fsmonitor"
 msgid "clear fsmonitor valid bit"
 msgstr "effacer le bit de validité fsmonitor"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version : précédemment %d, mis à %d"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13345,8 +13593,12 @@ msgstr "lire les mises à jour depuis l'entrée standard"
 msgid "update the info files from scratch"
 msgstr "mettre à jour les fichiers d'information à partir de zéro"
 
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<options>] <répertoire>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <répertoire>"
 
 msgid "quit after a single request/response exchange"
 msgstr "quitter après un unique échange requête/réponse"
@@ -13361,8 +13613,8 @@ msgstr ""
 msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "interrompre le transfert après <n> secondes d'inactivité"
 
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <commit>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | --verbose] [--raw] <commit>..."
 
 msgid "print commit contents"
 msgstr "afficher le contenu du commit"
@@ -13370,8 +13622,9 @@ msgstr "afficher le contenu du commit"
 msgid "print raw gpg status output"
 msgstr "afficher les messages bruts de gpg"
 
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr ""
+"git verify-pack [-v | --verbose] [-s | --stat-only] [--] <paquet>.idx..."
 
 msgid "verbose"
 msgstr "verbeux"
@@ -13379,42 +13632,79 @@ msgstr "verbeux"
 msgid "show statistics only"
 msgstr "afficher seulement les statistiques"
 
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<format>] <étiquette>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr ""
+"git verify-tag [-v | --verbose] [--format=<format>] [--raw] <étiquette>..."
 
 msgid "print tag contents"
 msgstr "afficher le contenu de l'étiquette"
 
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<options>] <chemin> [<commit>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <chaîne>]]\n"
+"                 [--orphan] [(-b | -B) <nouvelle-branche>] <chemin> [<commit-"
+"esque>]"
 
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<options>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
 
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<options>] <chemin>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <chaîne>] <arbre-de-travail>"
 
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <arbre-de-travail> <nouveau-chemin>"
 
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<options>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <date>]"
 
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<options>] <arbre-de-travail>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <arbre-de-travail>"
 
 msgid "git worktree repair [<path>...]"
 msgstr "git worktree repair [<chemin>...]"
 
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <chemin>"
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <arbre-de-travail>"
+
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Aucune branche source possible, activation de '--orphan'"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Si vous vouliez créer un arbre-de-travail contenant une nouvelle branche\n"
+"orpheline (une branche sans commit) pour ce dépôt, vous pouvez le faire\n"
+"en utilisant le drapeau --orphan :\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Si vous vouliez créer un arbre-de-travail contenant une nouvelle branche\n"
+"orpheline (une branche sans commit) pour ce dépôt, vous pouvez le faire\n"
+"en utilisant le drapeau --orphan :\n"
+"\n"
+"    git worktree add --orphan %s\n"
 
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "Suppression de %s/%s : %s"
 
 msgid "report pruned working trees"
-msgstr "afficher les arbres de travail éliminés"
+msgstr "afficher les arbres de travail élagués"
 
 msgid "expire working trees older than <time>"
 msgstr "faire expirer les arbres de travail plus vieux que <temps>"
@@ -13480,10 +13770,37 @@ msgstr ""
 msgid "Preparing worktree (checking out '%s')"
 msgstr "Préparation de l'arbre de travail (extraction de '%s')"
 
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "non joignable : référence invalide : %s"
+
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Préparation de l'arbre de travail (HEAD détachée %s)"
 
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD pointe sur une référence invalide (ou orpheline).\n"
+"chemin de HEAD '%s'\n"
+"contenu de HEAD : '%s'"
+
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"Aucune réf locale ou distant n'existe malgré la présence d'au moins un "
+"distant,\n"
+"on arrête ; utilisez 'add -f' pour passe outre ou récupérer le distant en "
+"premier"
+
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "'%s' et '%s' ne peuvent pas être utilisées ensemble"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
 "extraire la <branche> même si elle est déjà extraite dans une autre copie de "
@@ -13495,6 +13812,9 @@ msgstr "créer une nouvelle branche"
 msgid "create or reset a branch"
 msgstr "créer ou réinitialiser une branche"
 
+msgid "create unborn/orphaned branch"
+msgstr "créer une branche non née/orpheline"
+
 msgid "populate the new working tree"
 msgstr "remplissage de la nouvelle copie de travail"
 
@@ -13514,6 +13834,13 @@ msgstr "essayer de nommer la nouvelle branche comme la branche amont"
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "les options '%s', '%s' et '%s' ne peuvent pas être utilisées ensemble"
 
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "les options '%s' et '%s' ne peuvent pas être utilisées ensemble"
+
+msgid "<commit-ish>"
+msgstr "<commit-esque>"
+
 msgid "added with --lock"
 msgstr "ajouté avec --lock"
 
@@ -13654,23 +13981,58 @@ msgstr "seulement utile pour le débogage"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "core.fsyncMethod = batch non géré sur cette plateforme"
 
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "impossible d'analyser la clé de liste de colis %s avec la valeur '%s'"
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "la liste de colis n'a pas de mode à '%s'"
+
 msgid "failed to create temporary file"
 msgstr "impossible de créer un fichier temporaire"
 
 msgid "insufficient capabilities"
 msgstr "capacités insuffisantes"
 
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "le fichier téléchargé depuis '%s' n'est pas un colis"
+
+msgid "failed to store maximum creation token"
+msgstr "échec de stockage du jeton de création maximum"
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "mode de colisage non reconnu depuis l'URI '%s'"
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "limite de récursion d'URI de colis dépassée (%d)"
+
 #, c-format
 msgid "failed to download bundle from URI '%s'"
 msgstr "impossible de télécharger le colis depuis l'URI '%s'"
 
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "le fichier à l'URI '%s' n'est pas un colis"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "le fichier à l'URI '%s' n'est pas un colis ou une liste de colis"
 
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "échec pour ouvrir le colis de l'URI '%s'"
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri :argument inattendu : '%s'"
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri : vidage attendu après les arguments"
+
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri : ligne vide rencontrée"
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri : la ligne n'est pas de la forme 'clé=valeur'"
+
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri : la ligne a une clé ou une valeur vide"
 
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
@@ -13694,6 +14056,13 @@ msgstr "Le dépôt ne dispose pas des commits prérequis suivants :"
 msgid "need a repository to verify a bundle"
 msgstr "la vérification d'un colis requiert un dépôt"
 
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"des commits prérequis existent dans le stock d'objets, mais ne sont pas "
+"connectés à l'historique du dépôt"
+
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -13709,6 +14078,14 @@ msgid_plural "The bundle requires these %<PRIuMAX> refs:"
 msgstr[0] "Le colis exige cette référence :"
 msgstr[1] "Le colis exige ces %<PRIuMAX> références :"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "algorithme d'empreinte du colis : %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Le colis utilise ce filtre : %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "impossible de dupliquer le descripteur de liasse"
 
@@ -13743,17 +14120,21 @@ msgstr "l'index de groupe a disparu"
 msgid "terminating chunk id appears earlier than expected"
 msgstr "l'identifiant de terminaison de tronçon apparaît plus tôt qu'attendu"
 
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "id de tronçon %<PRIx32> non alignés sur %d octets"
+
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
-msgstr "décalage(s) de section incorrect(s) %<PRIx64> et %<PRIx64>"
+msgstr "décalage(s) de tronçon incorrect(s) %<PRIx64> et %<PRIx64>"
 
 #, c-format
 msgid "duplicate chunk ID %<PRIx32> found"
-msgstr "ID de section dupliqué %<PRIx32>"
+msgstr "ID de tronçon dupliqué %<PRIx32>"
 
 #, c-format
 msgid "final chunk has non-zero id %<PRIx32>"
-msgstr "la section finale a un id non nul %<PRIx32>"
+msgstr "le tronçon final a un id non nul %<PRIx32>"
 
 msgid "invalid hash version"
 msgstr "version d'empreinte invalide"
@@ -13798,10 +14179,8 @@ msgstr ""
 msgid "Move objects and refs by archive"
 msgstr "Déplacer les objets et références par archive"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Fournir le contenu ou l'information de type et taille pour les objets du "
-"dépôt"
+msgid "Provide contents or details of repository objects"
+msgstr "Fournir le contenu ou les détails des objets du dépôt"
 
 msgid "Display gitattributes information"
 msgstr "Afficher les informations gitattributes"
@@ -13944,9 +14323,9 @@ msgstr "Afficher les lignes correspondant à un motif"
 msgid "A portable graphical interface to Git"
 msgstr "Une interface graphique portable pour Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
+msgid "Compute object ID and optionally create an object from a file"
 msgstr ""
-"Calculer l'ID d'objet et créer optionnellement un blob depuis un fichier"
+"Calculer l'ID d'objet et créer optionnellement un objet depuis un fichier"
 
 msgid "Display help information about Git"
 msgstr "Afficher l'information d'aide à propos de Git"
@@ -14062,8 +14441,7 @@ msgid "Compute unique ID for a patch"
 msgstr "Calculer l'ID unique d'un patch"
 
 msgid "Prune all unreachable objects from the object database"
-msgstr ""
-"Éliminer les objets inatteignables depuis la base de données des objets"
+msgstr "Élaguer les objets inatteignables depuis la base de données des objets"
 
 msgid "Remove extra objects that are already in pack files"
 msgstr "Éliminer les objets qui sont déjà présents dans les fichiers pack"
@@ -14226,7 +14604,7 @@ msgstr "Vérifier la signature GPG d'étiquettes"
 msgid "Display version information about Git"
 msgstr "Afficher l'information de version à propos de Git"
 
-msgid "Show logs with difference each commit introduces"
+msgid "Show logs with differences each commit introduces"
 msgstr "Afficher les journaux avec la différence que chaque commit introduit"
 
 msgid "Manage multiple working trees"
@@ -14263,10 +14641,10 @@ msgid "The bundle file format"
 msgstr "le format du fichier de colis"
 
 msgid "Chunk-based file formats"
-msgstr "format de fichier avec des sections"
+msgstr "format de fichier avec des tronçons"
 
-msgid "Git commit graph format"
-msgstr "format de graphe de commit de Gti"
+msgid "Git commit-graph format"
+msgstr "format de graphe de commit de Git"
 
 msgid "Git index format"
 msgstr "format d'index Git"
@@ -14361,14 +14739,21 @@ msgstr ""
 
 #, c-format
 msgid "commit-graph file is too small to hold %u chunks"
-msgstr "le graphe de commit est trop petit pour contenir %u sections"
+msgstr "le graphe de commit est trop petit pour contenir %u tronçons"
 
 msgid "commit-graph has no base graphs chunk"
-msgstr "le graphe de commit n'a pas de section de graphes de base"
+msgstr "le graphe de commit n'a pas de tronçon de graphes de base"
+
+msgid "commit-graph base graphs chunk is too small"
+msgstr "le tronçon de graphes de base du graphe de commit est trop petit"
 
 msgid "commit-graph chain does not match"
 msgstr "la chaîne de graphe de commit ne correspond pas"
 
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "nombre de commits dans le graphe de base trop haut : %<PRIuMAX>"
+
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr ""
@@ -14391,6 +14776,11 @@ msgstr ""
 "le graphe de commits requiert des données de génération de débordement mais "
 "n'en contient pas"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr ""
+"les données de génération de débordement du graphe de commits sont trop "
+"petites"
+
 msgid "Loading known commits in commit graph"
 msgstr "Lecture des commits connus dans un graphe de commit"
 
@@ -14460,6 +14850,15 @@ msgstr "échec du renommage du fichier de graphe de commits"
 msgid "failed to rename temporary commit-graph file"
 msgstr "impossible de renommer le fichier temporaire de graphe de commits"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr ""
+"impossible de fusionner des graphes avec %<PRIuMAX>, %<PRIuMAX> commits"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "impossible de fusionner le graphe %s, trop de commits : %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Analyse des commits de fusion"
 
@@ -14492,9 +14891,6 @@ msgstr ""
 msgid "failed to parse commit %s from commit-graph"
 msgstr "échec de l'analyse le commit %s depuis le graphe de commits"
 
-msgid "Verifying commits in commit graph"
-msgstr "Verification des commits dans le graphe de commits"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14521,20 +14917,6 @@ msgid "commit-graph parent list for commit %s terminates early"
 msgstr ""
 "la liste de parents du graphe de commit pour le commit %s se termine trop tôt"
 
-#, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"le graphe de commit a un numéro de génération nul pour le commit %s, mais "
-"non-nul ailleurs"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"le graphe de commit a un numéro de génération non-nul pour le commit %s, "
-"mais nul ailleurs"
-
 #, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr ""
@@ -14547,6 +14929,17 @@ msgstr ""
 "la date de validation pour le commit %s dans le graphe de commit est "
 "%<PRIuMAX> != %<PRIuMAX>"
 
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"le graphe de commit a des numéros de génération à la fois nuls et non-nuls "
+"(par ex. les commits %s et %s)"
+
+msgid "Verifying commits in commit graph"
+msgstr "Verification des commits dans le graphe de commits"
+
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s n'est pas un commit !"
@@ -14570,6 +14963,12 @@ msgstr ""
 "Supprimez ce message en lançant\n"
 "\"git config advice.graftFileDeprecated false\""
 
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"le commit '%s' existe dans la graphe de commit mais pas dans la base de "
+"données d'objets"
+
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr "La validation %s a une signature GPG non fiable, prétendument par %s."
@@ -14634,6 +15033,10 @@ msgstr "cas non géré dans 'has_worktree_moved': %d"
 msgid "health thread wait failed [GLE %ld]"
 msgstr "l'attente du fil de santé a échoué [GLE %ld]"
 
+#, c-format
+msgid "Invalid path: %s"
+msgstr "chemin invalide : '%s'"
+
 msgid "Unable to create FSEventStream."
 msgstr "impossible de créer FSEVEntStream."
 
@@ -14664,6 +15067,22 @@ msgstr "GetOverlappedResult a échoué sur '%s' [GLE %ld]"
 msgid "could not read directory changes [GLE %ld]"
 msgstr "impossible de lire les modifications du répertoire [GLE %ld]"
 
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "échec de opendir(%s)"
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "échec de lstat('%s')"
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "échec de strbuf_readlink('%s')"
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "échec de closedir('%s')"
+
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
 msgstr "[GLE %ld] impossible d'ouvrir pour lire '%ls'"
@@ -14960,8 +15379,8 @@ msgstr "longueur d'abbrev hors plage : %d"
 msgid "bad zlib compression level %d"
 msgstr "niveau de compression zlib incorrect %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar ne devrait être qu'un unique caractère"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar ne devrait être qu'un unique caractère ASCII"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -14997,10 +15416,6 @@ msgstr "la référence '%s' ne pointe pas sur un blob"
 msgid "unable to resolve config blob '%s'"
 msgstr "impossible de résoudre le blob de config '%s'"
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "échec de l'analyse de %s"
-
 msgid "unable to parse command-line config"
 msgstr "lecture de la configuration de ligne de commande impossible"
 
@@ -15075,6 +15490,12 @@ msgstr "impossible de régler '%s' à '%s'"
 msgid "invalid section name: %s"
 msgstr "nom de section invalide : %s"
 
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"refus de travailler avec des lignes trop longues dans '%s' à la ligne "
+"%<PRIuMAX>"
+
 #, c-format
 msgid "missing value for '%s'"
 msgstr "valeur manquante pour '%s'"
@@ -15129,6 +15550,16 @@ msgstr "erreur de protocole : '%s' attendu"
 msgid "unknown object format '%s' specified by server"
 msgstr "format d'objet spécifié par le serveur inconnu '%s'"
 
+#, c-format
+msgid "error on bundle-uri response line %d: %s"
+msgstr "erreur sur la ligne %d de réponse bundle-uri : %s"
+
+msgid "expected flush after bundle-uri listing"
+msgstr "vidage attendu après le listage des bundle-uri"
+
+msgid "expected response end packet after ref listing"
+msgstr "paquet de fin de réponse attendu après le listage de références"
+
 #, c-format
 msgid "invalid ls-refs response: %s"
 msgstr "réponse à ls-ref invalide : %s"
@@ -15136,9 +15567,6 @@ msgstr "réponse à ls-ref invalide : %s"
 msgid "expected flush after ref listing"
 msgstr "vidage attendu après le listage de références"
 
-msgid "expected response end packet after ref listing"
-msgstr "paquet de fin de réponse attendu après le listage de références"
-
 #, c-format
 msgid "protocol '%s' is not supported"
 msgstr "le protocole '%s' n'est pas supporté"
@@ -15470,9 +15898,6 @@ msgstr "impossible d'écrire l'archive"
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base ne fonctionne pas avec des plages"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base ne fonctionne qu'avec des commits"
-
 msgid "unable to get HEAD"
 msgstr "impossible d'acquérir HEAD"
 
@@ -15482,6 +15907,12 @@ msgstr "aucune base de fusion trouvée"
 msgid "multiple merge bases found"
 msgstr "bases multiples de fusion trouvées"
 
+msgid "cannot compare stdin to a directory"
+msgstr "impossible de comparer stdin à un répertoire"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "impossible de réparer un tuyau nommé à un répertoire"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<options>] <chemin> <chemin>"
 
@@ -15541,6 +15972,14 @@ msgstr ""
 msgid "external diff died, stopping at %s"
 msgstr "l'application de diff externe a disparu, arrêt à %s"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow a besoin d'une spécification de chemin unique"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr ""
+"le spécificateur magique de chemin n'est pas pris en charge par --follow : %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr ""
@@ -15559,9 +15998,6 @@ msgstr ""
 "les options '%s' et '%s' ne peuvent pas être utilisées ensemble, utilisez "
 "'%s' avec '%s' et '%s'"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow a besoin d'une spécification de chemin unique"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "valeur invalide de --stat : %s"
@@ -15757,6 +16193,9 @@ msgstr "préfixer toutes les lignes en sortie avec la chaîne indiquée"
 msgid "do not show any source or destination prefix"
 msgstr "n'afficher aucun préfixe, ni de source, ni de destination"
 
+msgid "use default prefixes a/ and b/"
+msgstr "utiliser les préfixes par défaut a/ et b/"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "afficher le contexte entre les sections à concurrence du nombre de ligne "
@@ -16078,6 +16517,14 @@ msgstr ""
 "suggestion : en attente de la fermeture du fichier par votre éditeur de "
 "texte…%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "impossible d'écrire dans '%s'"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "impossible d'éditer '%s'"
+
 msgid "Filtering content"
 msgstr "Filtrage du contenu"
 
@@ -16302,11 +16749,11 @@ msgstr "fsmonitor--daemon n'est pas en cours d'exécution"
 
 #, c-format
 msgid "could not send '%s' command to fsmonitor--daemon"
-msgstr "impossible de lancer la commmand '%s' à fsmonitor--daemon"
+msgstr "impossible de lancer la commande '%s' à fsmonitor--daemon"
 
 #, c-format
 msgid "bare repository '%s' is incompatible with fsmonitor"
-msgstr "le dépôit nu '%s' est incompatible avec fsmonitor"
+msgstr "le dépôt nu '%s' est incompatible avec fsmonitor"
 
 #, c-format
 msgid "repository '%s' is incompatible with fsmonitor due to errors"
@@ -16322,9 +16769,11 @@ msgstr "le dépôt virtuel '%s' est incompatible avec fsmonitor"
 
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
-"le dépôt '%s' est incompatible avec fsmonitor par manque de sockets Unix"
+"le répertoire de socket '%s' est incompatible avec fsmonitor par manque de "
+"sockets Unix"
 
 msgid ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
@@ -16332,18 +16781,16 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
-"git [--version] [-h | --help] [-C <chemin>] [-c <nom>=<valeur>]\n"
+"git [-v | --version] [-h | --help] [-C <chemin>] [-c <nom>=<valeur>]\n"
 "           [--exec-path[=<chemin>]] [--html-path] [--man-path] [--info-"
 "path]\n"
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<chemin>] [--work-tree=<chemin>] [--namespace=<nom>]\n"
-"           [--super-prefix=<chemin>] [--config-env=<nom>=<variable-d-"
-"environnement>]\n"
-"           <commande> [<args>]"
+"           [--config-env=<nom>=<variable-d-environnement>] <commande> "
+"[<args>]"
 
 msgid ""
 "'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -16368,10 +16815,6 @@ msgstr "aucun répertoire fourni pour l'option '%s'\n"
 msgid "no namespace given for --namespace\n"
 msgstr "aucun espace de nom fournit pour --namespace\n"
 
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "aucun préfixe fourni pour --super-prefix\n"
-
 #, c-format
 msgid "-c expects a configuration string\n"
 msgstr "-c requiert une chaîne de configuration\n"
@@ -16380,6 +16823,10 @@ msgstr "-c requiert une chaîne de configuration\n"
 msgid "no config key given for --config-env\n"
 msgstr "aucune clé de configuration fournie pour --config-env\n"
 
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "aucune source d'attribut fournie pour --attr-source\n"
+
 #, c-format
 msgid "unknown option: %s\n"
 msgstr "option inconnue : %s\n"
@@ -16484,8 +16931,13 @@ msgstr ""
 msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "gpg.ssh.defaultKeyCommand a échoué : %s %s"
 
-msgid "gpg failed to sign the data"
-msgstr "gpg n'a pas pu signer les données"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg n'a pas pu signer les données :\n"
+"%s"
 
 msgid "user.signingKey needs to be set for ssh signing"
 msgstr "user.signingKey doit être configuré pour signer avec ssh"
@@ -16650,8 +17102,8 @@ msgstr[1] ""
 "\n"
 "Les commandes les plus ressemblantes sont"
 
-msgid "git version [<options>]"
-msgstr "git version [<options>]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #, c-format
 msgid "%s: %s - %s"
@@ -16909,12 +17361,12 @@ msgstr ""
 "existent :\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "Ã\89chec à l'exécution de la fusion interne"
+msgid "failed to execute internal merge"
+msgstr "échec à l'exécution de la fusion interne"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "Impossible d'ajouter %s à la base de données"
+msgid "unable to add %s to database"
+msgstr "impossible d'ajouter %s à la base de données"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -17356,6 +17808,14 @@ msgstr "impossible de lire le cache"
 msgid "multi-pack-index OID fanout is of the wrong size"
 msgstr "l'étalement de l'OID d'index multi-paquet n'a pas la bonne taille"
 
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr ""
+"le tronçon de recherche de l'OID d'index multi-paquet n'a pas la bonne taille"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr ""
+"le tronçon de décalage de l'OID d'index multi-paquet n'a pas la bonne taille"
+
 #, c-format
 msgid "multi-pack-index file %s is too small"
 msgstr "le fichier d'index multi-paquet %s est trop petit"
@@ -17376,17 +17836,28 @@ msgstr ""
 "la version d'empreinte d'index multi-paquet %u ne correspond pas à la "
 "version %u"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "index multi-paquet manque de tronçon de nom de paquet"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"le tronçon de nom de paquet requis par l'index multi-paquet est manquant ou "
+"corrompu"
+
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"le tronçon d'étalement OID requis de l'index multi-paquet est manquant ou "
+"corrompu"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "index multi-paquet manque de tronçon de d'étalement OID requis"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"le tronçon de recherche OID requis de l'index multi-paquet est manquant ou "
+"corrompu"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "index multi-paquet manque de tronçon de recherche OID requis"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"le tronçon de décalage d'objet requis de l'index multi-paquet est manquant "
+"ou corrompu"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "index multi-paquet manque de tronçon de décalage d'objet requis"
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "le tronçon de nom de paquet de l'index multi-paquet est trop petit"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17399,7 +17870,10 @@ msgstr "mauvais pack-int-id : %u (%u paquets au total)"
 
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr ""
-"l'index multi-paquet stock un décalage en 64-bit, mais off_t est trop petit"
+"l'index multi-paquet stocke un décalage en 64-bit, mais off_t est trop petit"
+
+msgid "multi-pack-index large offset out of bounds"
+msgstr "le grand décalage de l'index-multi-paquet est hors limite"
 
 #, c-format
 msgid "failed to add packfile '%s'"
@@ -17584,10 +18058,6 @@ msgstr "impossible de normaliser le chemin d'objet alternatif : %s"
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s : magasins d'objets alternatifs ignorés, récursion trop profonde"
 
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "impossible de normaliser le répertoire d'objet : %s"
-
 msgid "unable to fdopen alternates lockfile"
 msgstr "impossible d'ouvrir (fdopen) le fichier verrou des alternatives"
 
@@ -17647,6 +18117,10 @@ msgstr "objet libre corrompu '%s'"
 msgid "garbage at end of loose object '%s'"
 msgstr "données incorrectes à la fin de l'objet libre '%s'"
 
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "impossible d'ouvrir l'objet libre %s"
+
 #, c-format
 msgid "unable to parse %s header"
 msgstr "impossible d'analyser l'entête %s"
@@ -17663,17 +18137,13 @@ msgid "header for %s too long, exceeds %d bytes"
 msgstr "entête de %s trop long, attendu %d octets"
 
 #, c-format
-msgid "failed to read object %s"
-msgstr "impossible de lire l'objet %s"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "l'objet libre %s (stocké dans %s) est corrompu"
 
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "remplacement %s non trouvé pour %s"
 
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "l'objet libre %s (stocké dans %s) est corrompu"
-
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr "l'objet empaqueté %s (stocké dans %s) est corrompu"
@@ -17686,9 +18156,6 @@ msgstr "impossible d'écrire le fichier %s"
 msgid "unable to set permission to '%s'"
 msgstr "impossible de régler les droits de '%s'"
 
-msgid "file write error"
-msgstr "erreur d'écriture d'un fichier"
-
 msgid "error when closing loose object file"
 msgstr "erreur en fermeture du fichier d'objet esseulé"
 
@@ -17735,11 +18202,12 @@ msgstr "échec de la création du répertoire %s"
 msgid "cannot read object for %s"
 msgstr "impossible de lire l'objet pour %s"
 
-msgid "corrupt commit"
-msgstr "commit corrompu"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "l'objet est en échec de fsck : %s"
 
-msgid "corrupt tag"
-msgstr "étiquette corrompue"
+msgid "refusing to create malformed object"
+msgstr "Refus de créer un objet malformé"
 
 #, c-format
 msgid "read error while indexing %s"
@@ -18008,10 +18476,6 @@ msgstr "décalage XOR invalide dans l'index de paquet en bitmap"
 msgid "cannot fstat bitmap file"
 msgstr "impossible d'obtenir le statut (fstat) du fichier de bitmap"
 
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "fichier bitmap extra ignoré : '%s'"
-
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "la somme de contrôle ne correspond pas entre MIDX et bitmap"
 
@@ -18080,6 +18544,10 @@ msgstr "impossible de trouver '%s' dans le paquet '%s' à l'offset %<PRIuMAX>"
 msgid "unable to get disk usage of '%s'"
 msgstr "impossible de récupérer l'utilisation du disque de '%s'"
 
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "le fichier bitmap '%s' a une somme de contrôle invalide"
+
 #, c-format
 msgid "mtimes file %s is too small"
 msgstr "le fichier de mtimes %s est trop petit"
@@ -18120,6 +18588,17 @@ msgstr "le fichier d'index inverse %s a une version non gérée %<PRIu32>"
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "le fichier d'index inverse %s a un id d'empreinte non géré %<PRIu32>"
 
+msgid "invalid checksum"
+msgstr "somme de contrôle invalide"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "position de rev-index invalide à %<PRIu64> : %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr ""
+"le tronçon d'index inversé de l'index multi-paquet n'a pas la bonne taille"
+
 msgid "cannot both write and verify reverse index"
 msgstr "impossible de lire et vérifier à la fois l'index inverse"
 
@@ -18171,14 +18650,6 @@ msgstr "l'option '%s' attend \"%s\" ou \"%s\""
 msgid "%s requires a value"
 msgstr "%s a besoin d'une valeur"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s est incompatible avec %s"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s est incompatible avec toute autre option"
-
 #, c-format
 msgid "%s takes no value"
 msgstr "%s n'accepte aucune valeur"
@@ -18191,6 +18662,10 @@ msgstr "%s n'est pas disponible"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s attend une valeur entière non négative avec une suffixe k/m/g"
 
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s est incompatible avec %s"
+
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "option ambigüe : %s (devrait être --%s%s ou --%s%s)"
@@ -18262,6 +18737,10 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-NUM"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "opposé de --no-%s"
+
 msgid "expiry-date"
 msgstr "date-d'expiration"
 
@@ -18277,6 +18756,9 @@ msgstr "être plus silencieux"
 msgid "use <n> digits to display object names"
 msgstr "utiliser <n> chiffres pour afficher les noms des objets"
 
+msgid "prefixed path to initial superproject"
+msgstr "chemin préfixé vers le superprojet initial"
+
 msgid "how to strip spaces and #comments from message"
 msgstr "comment éliminer les espaces et les commentaires # du message"
 
@@ -18289,6 +18771,14 @@ msgstr ""
 "avec --pathspec-from-file, les spécificateurs de chemin sont séparés par un "
 "caractère NUL"
 
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "valeur booléenne d'environnement invalide '%s' pour '%s'"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "échec de l'analyse de %s"
+
 #, c-format
 msgid "Could not make %s writable by group"
 msgstr "Impossible de rendre %s inscriptible pour le groupe"
@@ -18339,6 +18829,10 @@ msgstr "Spécificateur magique '%c' non implémenté dans '%s'"
 msgid "%s: 'literal' and 'glob' are incompatible"
 msgstr "%s : 'literal' et 'glob' sont incompatibles"
 
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s' est hors de l'arbre de répertoire"
+
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s : '%s' est hors du dépôt à '%s'"
@@ -18433,6 +18927,10 @@ msgstr ""
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr "un nom de prometteur distant ne peut pas commencer par '/' : %s"
 
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "impossible de récupérer %s depuis le distant de prometteur"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info : vidage attendu après les arguments"
 
@@ -18468,6 +18966,13 @@ msgstr "échec de la génération de diff"
 msgid "could not parse log for '%s'"
 msgstr "impossible d'analyser le journal pour '%s'"
 
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "sommet supplémentaire dégénéré invalide : '%s'"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "impossible d'énumérer les objets récents additionnels"
+
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr "pas d'ajout d'alias de fichier '%s'(« %s » existe déjà dans l'index)"
@@ -18600,10 +19105,6 @@ msgstr "impossible d'écrire un index scindé pour un index clairsemé"
 msgid "failed to convert to a sparse-index"
 msgstr "échec de conversion d'un index clairsemé"
 
-#, c-format
-msgid "could not stat '%s'"
-msgstr "impossible de stat '%s'"
-
 #, c-format
 msgid "unable to open git dir: %s"
 msgstr "impossible d'ouvrir le répertoire git : %s"
@@ -18620,6 +19121,14 @@ msgstr "impossible de régler les bits de droit de '%s'"
 msgid "%s: cannot drop to stage #0"
 msgstr "%s : impossible de revenir à l'étape 0"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "état de diff inattendu %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "suppression de '%s'\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -18786,6 +19295,14 @@ msgstr "en retard de %d"
 msgid "ahead %d, behind %d"
 msgstr "en avance de %d, en retard de %d"
 
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) n'accepte pas d'argument"
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "argument %%(%.*s) non reconnu : %s"
+
 #, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "format attendu : %%(color:<couleur>)"
@@ -18803,41 +19320,37 @@ msgid "Integer value expected refname:rstrip=%s"
 msgstr "Valeur entière attendue refname:rstrip=%s"
 
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "argument %%(%s) non reconnu : %s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "%%(trailers:key=<value>) attendu"
 
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) n'accepte pas d'argument"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "argument %%(trailers) inconnu : %s"
 
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) n'accepte pas d'argument"
+msgid "positive value expected contents:lines=%s"
+msgstr "valeur positive attendue contents:lines=%s"
 
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) n'accepte pas d'argument"
+msgid "argument expected for %s"
+msgstr "argument attendu pour %s"
 
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "%%(trailers:key=<value>) attendu"
+msgid "positive value expected %s=%s"
+msgstr "valeur positive attendue %s=%s"
 
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "argument %%(trailers) inconnu : %s"
+msgid "cannot fully parse %s=%s"
+msgstr "impossible d'analyser complètement %s=%s"
 
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "valeur positive attendue contents:lines=%s"
+msgid "value expected %s="
+msgstr "valeur attendue %s="
 
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "valeur positive attendue '%s' dans %%(%s)"
 
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "option de courriel non reconnue : %s"
-
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
 msgstr "format attendu : %%(align:<largeur>,<position>)"
@@ -18850,13 +19363,17 @@ msgstr "position non reconnue : %s"
 msgid "unrecognized width:%s"
 msgstr "largeur non reconnue : %s"
 
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "argument %%(%s) non reconnu : %s"
+
 #, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "valeur positive attendue avec l'atome %%(align)"
 
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) n'accepte pas d'argument"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "format attendu : %%(ahead-behind:<commit-esque>)"
 
 #, c-format
 msgid "malformed field name: %.*s"
@@ -18904,6 +19421,9 @@ msgstr "cette commande rejette l'atome %%(%.*s)"
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s ne peut pas être utilisé avec --python, --shell, --tcl"
 
+msgid "failed to run 'describe'"
+msgstr "échec pour lancer 'describe'"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(aucune branche, rebasage de %s)"
@@ -18965,6 +19485,9 @@ msgstr "clé"
 msgid "field name to sort on"
 msgstr "nom du champ servant à trier"
 
+msgid "exclude refs which match pattern"
+msgstr "exclure les références correspondant à <motif>"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "'%s' n'est pas un journal de références"
@@ -19418,9 +19941,10 @@ msgstr[1] ""
 "Votre branche et '%s' ont divergé,\n"
 "et ont %d et %d commits différents chacune respectivement.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
 msgstr ""
-"  (utilisez \"git pull\" pour fusionner la branche distante dans la vôtre)\n"
+"  (utilisez \"git pull\" pour intégrer la branche distante avec la vôtre)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
@@ -19492,10 +20016,6 @@ msgstr "échec de la mise à jour de l'état en conflit dans '%s'"
 msgid "no remembered resolution for '%s'"
 msgstr "aucune résolution enregistrée pour '%s'"
 
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "impossible de délier '%s'"
-
 #, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Pré-image mise à jour pour '%s'"
@@ -19518,6 +20038,13 @@ msgstr "impossible de déterminer la révision HEAD"
 msgid "failed to find tree of %s"
 msgstr "impossible de trouver l'arbre de %s"
 
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "section non géree pour les réfs cachées : %s"
+
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= présent plus d'une fois"
+
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
 msgstr "resolve-undo enregistre `%s` qui manque"
@@ -19530,6 +20057,10 @@ msgstr ""
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<fichier-paquet> n'est plus géré"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "option invalide '%s' en mode --stdin"
+
 msgid "your current branch appears to be broken"
 msgstr "votre branche actuelle semble cassée"
 
@@ -19616,8 +20147,16 @@ msgstr "lors d'un clonage, créer un répertoire de travail complet"
 msgid "only download metadata for the branch that will be checked out"
 msgstr "ne télécharger les méta-données que pour la branche qui sera extraite"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<options>] [--] <dépôt> [<répertoire>]"
+msgid "create repository within 'src' directory"
+msgstr "Créer un dépôt dans le repertoire 'src'"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <branche-principale>] [--full-"
+"clone]\n"
+"\t[--[no-]src] <url> [<enrôlement>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19664,8 +20203,32 @@ msgid "--all or <enlistment>, but not both"
 msgstr "--all ou <enrôlement>, mais pas les deux"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "dépôt git parti dans '%s'"
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "impossible de supprimé le scalar.repo obsolète '%s'"
+
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "suppression du scalar.repo obsolète '%s'"
+
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "le dépôt dans '%s' a un propriétaire différent"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "le dépôt dans '%s' a un problème de format"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "dépôt non trouvé dans '%s'"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"pour désinscrire ce dépôt de Scalar, lancez\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -19819,10 +20382,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "impossible de verrouiller '%s'"
 
-#, c-format
-msgid "could not write to '%s'"
-msgstr "impossible d'écrire dans '%s'"
-
 #, c-format
 msgid "could not write eol to '%s'"
 msgstr "impossible d'écrire la fin de ligne dans '%s'"
@@ -20076,10 +20635,6 @@ msgstr "impossible d'obtenir un message de validation pour %s"
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s : impossible d'analyser le commit parent %s"
 
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "impossible de renommer '%s' en '%s'"
-
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "impossible d'annuler %s... %s"
@@ -20100,6 +20655,24 @@ msgstr "git %s : échec à la lecture de l'index"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s : échec du rafraîchissement de l'index"
 
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "'%s' n'est pas un label valide"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "'%s' n'est pas un nom valide de référence"
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr ""
+"update-ref requiert un nom de référence totalement qualifié par ex. refs/"
+"heads/%s"
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "commande '%.*s' invalide"
+
 #, c-format
 msgid "%s does not accept arguments: '%s'"
 msgstr "%s n'accepte pas d'argument : '%s'"
@@ -20170,9 +20743,6 @@ msgstr "essayez \"git cherry-pick (--continue | %s--abort | --quit)\""
 msgid "could not create sequencer directory '%s'"
 msgstr "impossible de créer le répertoire de séquenceur '%s'"
 
-msgid "could not lock HEAD"
-msgstr "impossible de verrouiller HEAD"
-
 msgid "no cherry-pick or revert in progress"
 msgstr "aucun picorage ou retour en cours"
 
@@ -20268,20 +20838,20 @@ msgstr ""
 "git rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "et a mis à jour l'index ou l'arbre de travail\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "et a mis à jour l'index ou l'arbre de travail.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "l'exécution a réussi : %s\n"
-"mais a laissé des modifications dans l'index ou la copie de travail\n"
+"mais a laissé des modifications dans l'index ou la copie de travail.\n"
 "Validez ou remisez vos modification, puis lancez\n"
 "\n"
 "  git rebase --continue\n"
@@ -20291,16 +20861,16 @@ msgstr ""
 msgid "illegal label name: '%.*s'"
 msgstr "nom de label illégal '%.*s'"
 
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "impossible de résoudre '%s'"
+
 msgid "writing fake root commit"
 msgstr "écriture d'un commit racine bidon"
 
 msgid "writing squash-onto"
 msgstr "écriture de 'écraser-sur'"
 
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "impossible de résoudre '%s'"
-
 msgid "cannot merge without a current revision"
 msgstr "impossible de fusionner avec une révision courante"
 
@@ -20422,14 +20992,14 @@ msgstr ""
 "    git rebase --edit-todo\n"
 "    git rebase --continue\n"
 
-#, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Rebasage (%d/%d)%s"
-
 #, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Arrêt à %s... %.*s\n"
 
+#, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Rebasage (%d/%d)%s"
+
 #, c-format
 msgid "unknown command %d"
 msgstr "commande inconnue %d"
@@ -20629,49 +21199,120 @@ msgid "cannot change to '%s'"
 msgstr "impossible de modifier en '%s'"
 
 #, c-format
-msgid "not a git repository (or any of the parent directories): %s"
-msgstr "ni ceci ni aucun de ses répertoires parents n'est un dépôt git : %s"
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "ni ceci ni aucun de ses répertoires parents n'est un dépôt git : %s"
+
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"ni ceci ni aucun de ses répertoires parents (jusqu'au point de montage %s) "
+"n'est un dépôt git\n"
+"Arrêt à la limite du système de fichiers (GIT_DISCOVERY_ACROSS_FILESYSTEM "
+"n'est pas défini)."
+
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"propriétaire douteux détecté dans le dépôt à '%s'\n"
+"%sPour ajouter une exception pour ce dépôt, lancez :\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr "impossible d'utiliser le dépôt nu '%s' (safe.bareRepository vaut '%s')"
+
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"problème avec la valeur de core.sharedRepository (0%.3o).\n"
+"Le propriétaire des fichiers doit toujours avoir les droits en lecture et "
+"écriture."
+
+msgid "fork failed"
+msgstr "échec de la bifurcation"
+
+msgid "setsid failed"
+msgstr "échec du setsid"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "impossible de faire un stat du modèle '%s'"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "impossible d'ouvrir le répertoire '%s'"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "impossible de lire le lien '%s'"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "impossible de créer un lien symbolique de '%s' '%s'"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "impossible de copier '%s' vers '%s'"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "modèle %s ignoré"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "modèles non trouvés dans %s"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "pas de copie des modèles depuis '%s' : %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "nom de branche initiale invalide : '%s'"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "impossible de traiter le fichier de type %d"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "impossible de déplacer %s vers %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "essai de réinitialisation du dépôt avec une empreinte différente"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s existe déjà"
 
 #, c-format
-msgid ""
-"not a git repository (or any parent up to mount point %s)\n"
-"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
-msgstr ""
-"ni ceci ni aucun de ses répertoires parents (jusqu'au point de montage %s) "
-"n'est un dépôt git\n"
-"Arrêt à la limite du système de fichiers (GIT_DISCOVERY_ACROSS_FILESYSTEM "
-"n'est pas défini)."
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-initialisation : --initial-branch=%s ignoré"
 
 #, c-format
-msgid ""
-"detected dubious ownership in repository at '%s'\n"
-"%sTo add an exception for this directory, call:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
-msgstr ""
-"propriétaire douteux détecté dans le dépôt à '%s'\n"
-"%sPour ajouter une exception pour ce dépôt, lancez :\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Dépôt Git existant partagé réinitialisé dans %s%s\n"
 
 #, c-format
-msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr "impossible d'utiliser le dépôt nu '%s' (safe.bareRepository vaut '%s')"
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Dépôt Git existant réinitialisé dans %s%s\n"
 
 #, c-format
-msgid ""
-"problem with core.sharedRepository filemode value (0%.3o).\n"
-"The owner of files must always have read and write permissions."
-msgstr ""
-"problème avec la valeur de core.sharedRepository (0%.3o).\n"
-"Le propriétaire des fichiers doit toujours avoir les droits en lecture et "
-"écriture."
-
-msgid "fork failed"
-msgstr "échec de la bifurcation"
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Dépôt Git vide partagé initialisé dans %s%s\n"
 
-msgid "setsid failed"
-msgstr "échec du setsid"
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Dépôt Git vide initialisé dans %s%s\n"
 
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -20724,10 +21365,6 @@ msgid_plural "%u bytes/s"
 msgstr[0] "%u octet/s"
 msgstr[1] "%u octets/s"
 
-#, c-format
-msgid "could not edit '%s'"
-msgstr "impossible d'éditer '%s'"
-
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "nom de sous-module suspicieux %s ignoré"
@@ -20911,6 +21548,25 @@ msgstr "ls-tree a renvoyé un code de retour inattendu %d"
 msgid "failed to lstat '%s'"
 msgstr "échec du lstat de '%s'"
 
+msgid "no remote configured to get bundle URIs from"
+msgstr "aucun distant configuré depuis lequel récupérer des URIs de colis"
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "le distant '%s' n'a pas d'URL configuré"
+
+msgid "could not get the bundle-uri list"
+msgstr "impossible d'avoir la liste de bundle-uris"
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <options> (control|prime|update)"
+
+msgid "clear the cache tree before each iteration"
+msgstr "effacer l'arbre de cache avant chaque itération"
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr "nombre d'entrées dans l'arbre de cache à invalider (par défaut, 0)"
+
 msgid "unhandled options"
 msgstr "options non gérées"
 
@@ -21261,6 +21917,13 @@ msgstr "Abandon."
 msgid "failed to push all needed submodules"
 msgstr "échec de la poussée de tous les sous-modules nécessaires"
 
+msgid "bundle-uri operation not supported by protocol"
+msgstr "opération bundle-uri non supportée par le protocole"
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr ""
+"impossible de récupérer la liste de bundle-uris annoncée par le serveur"
+
 msgid "too-short tree object"
 msgstr "objet arbre trop court"
 
@@ -21661,6 +22324,9 @@ msgstr "impossible d'accéder à '%s'"
 msgid "unable to get current working directory"
 msgstr "impossible d'accéder au répertoire de travail courant"
 
+msgid "unable to get random bytes"
+msgstr "impossible d'acquérir des octets aléatoires"
+
 msgid "Unmerged paths:"
 msgstr "Chemins non fusionnés :"
 
@@ -22014,14 +22680,19 @@ msgstr "Fichiers ignorés"
 
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
 msgstr ""
-"L'énumération des fichiers non suivis a duré %.2f secondes. 'status -uno'\n"
-"peut l'accélérer, mais vous devez alors faire attention à ne pas\n"
-"oublier d'ajouter les nouveaux fichiers par vous-même (voir 'git help "
-"status')."
+"L'énumération des fichiers non-suivis a pris %.2f secondes,\n"
+"mais les resultats ont été mis en cache, et les lancements suivants seront "
+"plus rapides."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "L'énumération des fichiers non-suivis a pris %.2f secondes."
+
+msgid "See 'git help status' for information on how to improve this."
+msgstr "Voir 'git help status' pour tout information pour améliorer ceci."
 
 #, c-format
 msgid "Untracked files not listed%s"
@@ -22172,275 +22843,6 @@ msgstr ""
 msgid "Unable to determine absolute path of git directory"
 msgstr "Impossible de déterminer le chemin absolu du répertoire git"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %s12s %s"
-
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "%d chemin touché\n"
-msgstr[1] "%d chemins touchés\n"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-"Si le patch s'applique proprement, la section éditée sera\n"
-"immédiatement marquée comme indexée."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-"Si le patch s'applique proprement, la section éditée sera\n"
-"immédiatement marquée comme remisée."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-"Si le patch s'applique proprement, la section éditée sera\n"
-"immédiatement marquée comme desindexée."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-"Si le patch s'applique proprement, la section éditée sera\n"
-"immédiatement marquée comme appliquée."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-"Si le patch s'applique proprement, la section éditée sera\n"
-"immédiatement marquée comme éliminée."
-
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "impossible d'ouvrir le fichier d'édition de section en écriture : %s"
-
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"Pour éliminer les lignes '%s', rendez-les ' ' (contexte).\n"
-"Pour éliminer les lignes '%s', effacez-les.\n"
-"Les lignes commençant par %s seront éliminées.\n"
-
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "échec de l'ouverture du fichier d'édition de section en lecture : %s"
-
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - indexer cette section\n"
-"n - ne pas indexer cette section\n"
-"q - quitter ; ne pas indexer cette section ni les autres restantes\n"
-"a - indexer cette section et toutes les suivantes de ce fichier\n"
-"d - ne pas indexer cette section ni les suivantes de ce fichier"
-
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - remiser cette section\n"
-"n - ne pas remiser cette section\n"
-"q - quitter ; ne pas remiser cette section ni les autres restantes\n"
-"a - remiser cette section et toutes les suivantes de ce fichier\n"
-"d - ne pas remiser cette section ni les suivantes de ce fichier"
-
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - désindexer cette section\n"
-"n - ne pas désindexer cette section\n"
-"q - quitter ; ne pas désindexer cette section ni les autres restantes\n"
-"a - désindexer cette section et toutes les suivantes de ce fichier\n"
-"d - ne pas désindexer cette section ni les suivantes de ce fichier"
-
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - appliquer cette section\n"
-"n - ne pas appliquer cette section\n"
-"q - quitter ; ne pas appliquer cette section ni les autres restantes\n"
-"a - appliquer cette section et toutes les suivantes de ce fichier\n"
-"d - ne pas appliquer cette section ni les suivantes de ce fichier"
-
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - supprimer cette section\n"
-"n - ne pas supprimer cette section\n"
-"q - quitter ; ne pas supprimer cette section ni les autres restantes\n"
-"a - supprimer cette section et toutes les suivantes de ce fichier\n"
-"d - ne pas supprimer cette section ni les suivantes de ce fichier"
-
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - éliminer cette section de l'index et de l'arbre de travail\n"
-"n - ne pas éliminer cette section\n"
-"q - quitter ; ne pas éliminer cette section ni les autres restantes\n"
-"a - éliminer cette section et toutes les suivantes de ce fichier\n"
-"d - ne pas éliminer cette section ni les suivantes de ce fichier"
-
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - appliquer cette section à l'index et à l'arbre de travail\n"
-"n - ne pas appliquer cette section\n"
-"q - quitter ; ne pas appliquer cette section ni les autres restantes\n"
-"a - appliquer cette section et toutes les suivantes de ce fichier\n"
-"d - ne pas appliquer cette section ni les suivantes de ce fichier"
-
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - appliquer cette section à l'arbre de travail\n"
-"n - ne pas appliquer cette section\n"
-"q - quitter ; ne pas appliquer cette section ni les autres restantes\n"
-"a - appliquer cette section et toutes les suivantes de ce fichier\n"
-"d - ne pas appliquer cette section ni les suivantes de ce fichier"
-
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - selectionner une section et s'y rendre\n"
-"/ - rechercher une section correspondant à une regex donnée\n"
-"j - laisser cette section non décidée et aller à la suivante non-décidée\n"
-"J - laisser cette section non décidée et aller à la suivante\n"
-"k - laisser cette section non décidée et aller à la précédente non-décidée\n"
-"K - laisser cette section non décidée et aller à la précédente\n"
-"s - découper la section en sections plus petites\n"
-"e - éditer manuellement la section actuelle\n"
-"? - afficher l'aide\n"
-
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "Les sections sélectionnées ne s'applique pas à l'index !\n"
-
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "fichier non-fusionné ignoré : %s\n"
-
-msgid "No other hunks to goto\n"
-msgstr "Aucune autre section à atteindre\n"
-
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "Nombre invalide : '%s'\n"
-
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "Désolé, %d seule section disponible.\n"
-msgstr[1] "Désolé, Seulement %d sections disponibles.\n"
-
-msgid "No other hunks to search\n"
-msgstr "Aucune autre section à rechercher\n"
-
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "Regex de recherche malformée %s : %s\n"
-
-msgid "No hunk matches the given pattern\n"
-msgstr "Aucune section ne correspond au motif donné\n"
-
-msgid "No previous hunk\n"
-msgstr "Pas de section précédente\n"
-
-msgid "No next hunk\n"
-msgstr "Pas de section suivante\n"
-
-msgid "Sorry, cannot split this hunk\n"
-msgstr "Désolé, impossible de découper cette section\n"
-
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "Découpée en %d section.\n"
-msgstr[1] "Découpée en %d sections.\n"
-
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "Désolé, impossible d'éditer cette section\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - montrer les chemins modifiés\n"
-"update        - ajouter l'état de l'arbre de travail aux modifications à "
-"indexer\n"
-"revert        - faire revenir les modifications à indexer à la version HEAD\n"
-"patch         - sélectionner les sections et mettre à jour sélectivement\n"
-"diff          - visualiser les diff entre HEAD et l'index\n"
-"add untracked - ajouter les fichiers non-suivis aux modifications à indexer\n"
-
-msgid "missing --"
-msgstr "-- manquant"
-
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "mode de --patch inconnu : %s"
-
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "argument invalide %s, -- attendu"
-
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr ""
 "la zone locale diffère du GMT par un intervalle supérieur à une minute\n"
@@ -22563,14 +22965,14 @@ msgstr ""
 "\n"
 "Effacez le corps si vous ne souhaitez pas envoyer un résumé.\n"
 
-#, perl-format
-msgid "Failed to open %s: %s"
-msgstr "Échec à l'ouverture de %s : %s"
-
 #, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "Échec à l'ouverture de %s.final : %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "Échec à l'ouverture de %s : %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "Le courriel de résumé étant vide, il a été ignoré\n"
 
@@ -22724,13 +23126,17 @@ msgid "(%s) Could not execute '%s'"
 msgstr "(%s) Impossible d'exécuter '%s'"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Ajout de %s : %s depuis : '%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Sortie malformée depuis '%s'"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) échec de la fermeture du pipe vers '%s'"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Ajout de %s : %s depuis : '%s'\n"
+
 msgid "cannot send message as 7bit"
 msgstr "impossible d'envoyer un message comme 7bit"
 
@@ -22768,87 +23174,103 @@ msgstr "%s sauté avec un suffix de sauvegarde '%s'.\n"
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "Souhaitez-vous réellement envoyer %s ?[y|N] : "
 
-#~ msgid "(stats|all)"
-#~ msgstr "(stats|all)"
-
-#~ msgid "git maintenance register"
-#~ msgstr "git maintenance register"
+#, c-format
+#~ msgid "options '%s=%s' and '%s=%s' cannot be used together"
+#~ msgstr ""
+#~ "les options '%s=%s' et '%s=%s' ne peuvent pas être utilisées ensemble"
 
-#~ msgid "git maintenance unregister"
-#~ msgstr "git maintenance unregister"
+#, c-format
+#~ msgid "%s : incompatible with something else"
+#~ msgstr "%s est incompatible avec toute autre option"
 
-#~ msgid "git maintenance stop"
-#~ msgstr "git maintenance stop"
+#~ msgid "Could not write patch"
+#~ msgstr "Impossible d'écrire le patch"
 
 #, c-format
-#~ msgid "could not parse colored hunk header '%.*s'"
-#~ msgstr "impossible d'analyser l'entête coloré de section '%.*s'"
+#~ msgid "Could not stat '%s'"
+#~ msgstr "Stat de '%s' impossible"
 
 #, c-format
-#~ msgid "Unknown subcommand: %s"
-#~ msgstr "Sous-commande inconnue : %s"
+#~ msgid "Cannot delete branch '%s' checked out at '%s'"
+#~ msgstr "Impossible de supprimer la branche '%s' extraite dans '%s'"
 
-#~ msgid "checked out in another worktree"
-#~ msgstr "extrait dans un autre arbre de travail"
+#~ msgid "unable to write new_index file"
+#~ msgstr "impossible d'écrire le fichier new_index"
 
-#~ msgid "failed to open stdin of 'crontab'"
-#~ msgstr "échec à l'ouverture de stdin de 'crontab'"
+#~ msgid "do not apply config rules"
+#~ msgstr "ne pas appliquer les règles de la configuration"
 
-#, c-format
-#~ msgid "invalid subcommand: %s"
-#~ msgstr "sous-commande invalide : %s"
+#~ msgid "join whitespace-continued values"
+#~ msgstr "joindre les valeurs continuées avec des caractères blancs"
 
-#~ msgid "single arg format must be symmetric range"
-#~ msgstr "un format d'argument unique doit être une plage symétrique"
+#~ msgid "set parsing options"
+#~ msgstr "paramètres d'analyse"
 
-#~ msgid "git submodule--helper list [--prefix=<path>] [<path>...]"
-#~ msgstr "git submodule--helper list [--prefix=<chemin>] [<chemin>...]"
+#~ msgid "cannot move directory over file"
+#~ msgstr "impossible de déplacer un répertoire sur un fichier"
 
-#~ msgid "git submodule--helper name <path>"
-#~ msgstr "git submodule--helper <nom> <chemin>"
+#~ msgid "cannot use --filter without --stdout"
+#~ msgstr "impossible d'utiliser --filter sans --stdout"
 
-#, c-format
-#~ msgid "failed to get the default remote for submodule '%s'"
-#~ msgstr ""
-#~ "échec d'obtention du dépôt distant par défaut pour le sous-module '%s'"
+#~ msgid "cannot use --max-pack-size with --cruft"
+#~ msgstr "impossible d'utiliser --max-pack-size avec --cruft"
 
-#, c-format
-#~ msgid "Invalid update mode '%s' for submodule path '%s'"
-#~ msgstr ""
-#~ "Mode de mise à jour '%s' invalide pour le chemin de sous-module '%s'"
+#~ msgid "--strategy requires --merge or --interactive"
+#~ msgstr "--strategy requiert --merge ou --interactive"
 
-#~ msgid "path into the working tree, across nested submodule boundaries"
+#, c-format
+#~ msgid ""
+#~ "commit-graph has generation number zero for commit %s, but non-zero "
+#~ "elsewhere"
 #~ msgstr ""
-#~ "chemin dans la copie de travail, traversant les frontières de sous-modules"
+#~ "le graphe de commit a un numéro de génération nul pour le commit %s, mais "
+#~ "non-nul ailleurs"
 
-#~ msgid "rebase, merge, checkout or none"
-#~ msgstr "valeurs possibles : rebase, merge, checkout ou none"
+#~ msgid "--merge-base only works with commits"
+#~ msgstr "--merge-base ne fonctionne qu'avec des commits"
 
-#~ msgid "bad value for update parameter"
-#~ msgstr "valeur invalide pour la mise à jour du paramètre"
-
-#~ msgid "Show three-way merge without touching index"
-#~ msgstr "Afficher la fusion à trois points sans modifier l'index"
+#~ msgid "scalar clone [<options>] [--] <repo> [<dir>]"
+#~ msgstr "scalar clone [<options>] [--] <dépôt> [<répertoire>]"
 
 #, c-format
-#~ msgid "could not create directory for '%s'"
-#~ msgstr "impossible de créer le répertoire pour '%s'"
+#~ msgid "could not rename '%s' to '%s'"
+#~ msgstr "impossible de renommer '%s' en '%s'"
 
 #, c-format
-#~ msgid "Couldn't start hook '%s'\n"
-#~ msgstr "impossible de démarrer le crochet '%s'\n"
+#~ msgid "It is not possible to %s because you have unmerged files."
+#~ msgstr "%s n'est pas possible car vous avez des fichiers non fusionnés."
+
+#~ msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
+#~ msgstr ""
+#~ "ne pas passer l'option --keep-cr à git-mailsplit indépendamment de am."
+#~ "keepcr"
 
-#, c-format
 #~ msgid ""
-#~ "Note: %s not up to date and in way of checking out conflicted version; "
-#~ "old copy renamed to %s"
+#~ "Updates were rejected because the tip of the remote-tracking\n"
+#~ "branch has been updated since the last checkout. You may want\n"
+#~ "to integrate those changes locally (e.g., 'git pull ...')\n"
+#~ "before forcing an update.\n"
 #~ msgstr ""
-#~ "Note :%s pas à jour et au milieu de l'extraction d'une version "
-#~ "conflictuelle ; la vielle copie a été renommée en %s"
+#~ "Les mises à jour ont été rejetées, car la pointe de la branche\n"
+#~ "de suivi a été mise à jour depuis la dernière extraction. Intégrez\n"
+#~ "ces changements localement (par exemple 'git pull ...') avant de\n"
+#~ "forcer à nouveau une mise à jour.\n"
+
+#~ msgid "or do not fetch any tag at all (--no-tags)"
+#~ msgstr "ou ne rapatrier aucune étiquette (--no-tags)"
+
+#~ msgid "current working directory is untracked"
+#~ msgstr "l'arbre de travail actuel est non-suivi"
+
+#~ msgid "cannot use --contents with final commit object name"
+#~ msgstr "on ne peut pas utiliser --contents avec un nom d'objet commit final"
+
+#~ msgid "please commit or stash them."
+#~ msgstr "veuillez les valider ou les remiser."
 
 #, c-format
-#~ msgid "%s: fast-forward"
-#~ msgstr "%s : avance rapide"
+#~ msgid "Unknown mode: %s"
+#~ msgstr "Mode inconnu : %s"
 
-#~ msgid "--preserve-merges was replaced by --rebase-merges"
-#~ msgstr "--preserve-merges a été remplacé par --rebase-merges"
+#~ msgid "could not lock HEAD"
+#~ msgstr "impossible de verrouiller HEAD"
index 7ce5acb674ced672d168b21029d0cadf29000924..9698367606beb8480d967e616eb8ac960d773807 100644 (file)
--- a/po/id.po
+++ b/po/id.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-09-28 14:31+0700\n"
-"PO-Revision-Date: 2022-09-28 14:50+0700\n"
+"POT-Creation-Date: 2023-11-12 20:04+0700\n"
+"PO-Revision-Date: 2023-11-12 20:30+0700\n"
 "Last-Translator: Bagas Sanjaya <bagasdotme@gmail.com>\n"
 "Language-Team: Indonesian\n"
 "Language: id\n"
@@ -26,19 +26,19 @@ msgstr "Huh (%s)?"
 msgid "could not read index"
 msgstr "tidak dapat membaca indeks"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "binary"
 msgstr "biner"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "nothing"
 msgstr "tidak ada"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "unchanged"
 msgstr "tak berubah"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Update"
 msgstr "Perbarui"
 
@@ -51,15 +51,15 @@ msgstr "tidak dapat menggelar '%s'"
 msgid "could not write index"
 msgstr "tidak dapat menulis indeks"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "%d jalur diperbarui\n"
 msgstr[1] "%d jalur diperbarui\n"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "catatan: %s sekarang tak terlacak.\n"
 
@@ -68,7 +68,7 @@ msgstr "catatan: %s sekarang tak terlacak.\n"
 msgid "make_cache_entry failed for path '%s'"
 msgstr "make_cache_entry gagal untuk jalur '%s'"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Revert"
 msgstr "Kembalikan"
 
@@ -76,24 +76,24 @@ msgstr "Kembalikan"
 msgid "Could not parse HEAD^{tree}"
 msgstr "Tidak dapat menguraikan HEAD^{tree}"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "%d jalur dikembalikan\n"
 msgstr[1] "%d jalur dikembalikan\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 #, c-format
 msgid "No untracked files.\n"
 msgstr "Tidak ada berkas tak terlacak.\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Add untracked"
 msgstr "Tambahkan tak terlacak"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "%d jalur ditambahkan\n"
@@ -104,21 +104,21 @@ msgstr[1] "%d jalur ditambahkan\n"
 msgid "ignoring unmerged: %s"
 msgstr "mengabaikan tak tergabung: %s"
 
-#: add-interactive.c add-patch.c git-add--interactive.perl
+#: add-interactive.c add-patch.c
 #, c-format
 msgid "Only binary files changed.\n"
 msgstr "Hanya berkas biner yang berubah.\n"
 
-#: add-interactive.c add-patch.c git-add--interactive.perl
+#: add-interactive.c add-patch.c
 #, c-format
 msgid "No changes.\n"
 msgstr "Tidak ada perubahan.\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Patch update"
 msgstr "Pembaruan tambalan"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Review diff"
 msgstr "Tinjau diff"
 
@@ -186,25 +186,25 @@ msgstr "pilih item bernomor"
 msgid "(empty) select nothing"
 msgstr "(empty) tidak pilih apapun"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 msgid "*** Commands ***"
 msgstr "*** Perintah ***"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 msgid "What now"
 msgstr "Apa sekarang"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "staged"
 msgstr "tergelar"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "unstaged"
 msgstr "tak tergelar"
 
 #: add-interactive.c apply.c builtin/am.c builtin/bugreport.c builtin/clone.c
-#: builtin/diagnose.c builtin/fetch.c builtin/merge.c builtin/pull.c
-#: builtin/submodule--helper.c git-add--interactive.perl
+#: builtin/diagnose.c builtin/fetch.c builtin/hook.c builtin/merge.c
+#: builtin/pull.c builtin/submodule--helper.c
 msgid "path"
 msgstr "jalur"
 
@@ -212,28 +212,28 @@ msgstr "jalur"
 msgid "could not refresh index"
 msgstr "tidak dapat menyegarkan indeks"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 #, c-format
 msgid "Bye.\n"
 msgstr "Sampai jumpa.\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "Gelar perubahan mode [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Gelar penghapusan [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Gelar penambahan [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Gelar bingkah ini [y,n,q,a,d%s,?]? "
 
@@ -259,23 +259,23 @@ msgstr ""
 "a - gelar bingkah ini dan semua bingkah selanjutnya dalam berkas\n"
 "d - jangan gelar bingkah ini atau bingkah selanjutnya dalam berkas\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "Stase perubahan mode [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Stase penghapusan [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Stase penambahan [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Stase bingkah ini [y,n,q,a,d%s,?]? "
 
@@ -301,23 +301,23 @@ msgstr ""
 "a - stase bingkah ini dan semua bingkah selanjutnya dalam berkas\n"
 "d - jangan stase bingkah ini atau bingkah selanjutnya dalam berkas\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "Batal gelar perubahan mode [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Batal gelar penghapusan [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Batal gelar penambahan [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Batal gelar bingkah ini [y,n,q,a,d%s,?]? "
 
@@ -343,23 +343,23 @@ msgstr ""
 "a - batal gelar bingkah ini dan semua bingkah selanjutnya dalam berkas\n"
 "d - jangan batal gelar bingkah ini atau bingkah selanjutnya dalam berkas\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "Terapkan perubahan mode ke indeks [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Terapkan penghapusan ke indeks [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Terapkan penambahan ke indeks [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Terapkan bingkah ini ke indeks [y,n,q,a,d%s,?]? "
 
@@ -385,23 +385,23 @@ msgstr ""
 "a - terapkan bingkah ini dan semua bingkah selanjutnya dalam berkas\n"
 "d - jangan terapkan bingkah ini atau bingkah selanjutnya dalam berkas\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "Buang perubahan mode dari pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Buang penghapusan dari pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Buang penambahan dari pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "Buang bingkah ini dari pohon kerja [y,n,q,a,d%s,?]? "
 
@@ -427,23 +427,23 @@ msgstr ""
 "a - buang hunk ini dan semua bingkah selanjutnya dalam berkas\n"
 "d - jangan buang hunk ini atau bingkah selanjutnya dalam berkas\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Buang perubahan mode dari indeks dan pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Buang penghapusan dari indeks dan pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Buang penambahan dari indeks dan pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Buang bingkah ini dari indeks dan pohon kerja [y,n,q,a,d%s,?]? "
 
@@ -461,23 +461,23 @@ msgstr ""
 "a - buang bingkah ini dan semua bingkah selanjutnya dalam berkas\n"
 "d - jangan buang bingkah ini atau bingkah selanjutnya dalam berkas\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Terapkan perubahan mode pada indeks dan pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Terapkan penghapusan pada indeks dan pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Terapkan penambahan pada indeks dan pohon kerja [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Terapkan bingkah ini pada indeks dan pohon kerja [y,n,q,a,d%s,?]? "
 
@@ -495,23 +495,23 @@ msgstr ""
 "a - terapkan bingkah ini dan semua bingkah selanjutnya dalam berkas\n"
 "d - jangan terapkan bingkah ini atau bingkah selanjutnya dalam berkas\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr "Terapkan perubahan mode pada pohon kerja [y,n,q,a,d%s,?]?"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "Terapkan penghapusan pada pohon kerja [y,n,q,a,d%s,?]?"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "Terapkan penambahan pada pohon kerja [y,n,q,a,d%s,?]?"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "Terapkan bingkah ini pada pohon kerja [y,n,q,a,d%s,?]?"
 
@@ -581,7 +581,7 @@ msgstr ""
 "tidak berakhir dengan:\n"
 "%.*s"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Manual hunk edit mode -- see bottom for a quick guide.\n"
 msgstr "Mode sunting bingkah manual -- lihat dibawah untuk panduan cepat.\n"
 
@@ -598,9 +598,7 @@ msgstr ""
 "Untuk menghapus baris '%c', hapuslahnya.\n"
 "Baris yang diawali dengan %c akan dihapus.\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -618,21 +616,13 @@ msgstr "tidak dapat menguraikan kepala bingkah"
 msgid "'git apply --cached' failed"
 msgstr "'git apply --cached' gagal"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
@@ -643,11 +633,11 @@ msgstr ""
 msgid "The selected hunks do not apply to the index!"
 msgstr "Bingkah yang dipilih tidak diterapkan pada indeks!"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Apply them to the worktree anyway? "
 msgstr "Tetap terapkan pada pohon kerja? "
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Nothing was applied.\n"
 msgstr "Tidak ada yang diterapkan.\n"
 
@@ -685,11 +675,11 @@ msgstr "Tidak ada bingkah selanjutnya"
 msgid "No other hunks to goto"
 msgstr "Tidak ada bingkah lainnya untuk dikunjungi"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "go to which hunk (<ret> to see more)? "
 msgstr "pergi ke bingkah yang mana (<ret> untuk lihat lebih)? "
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "go to which hunk? "
 msgstr "pergi ke bingkah yang mana?"
 
@@ -709,7 +699,7 @@ msgstr[1] "Maaf, hanya %d bingkah yang tersedia."
 msgid "No other hunks to search"
 msgstr "Tidak ada bingkah lainnya untuk dicari"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "search for regex? "
 msgstr "cari untuk regex? "
 
@@ -774,9 +764,8 @@ msgid "Reverting is not possible because you have unmerged files."
 msgstr "Pembalikkan tidak mungkin sebab Anda punya berkas tak tergabung."
 
 #: advice.c
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "Tidak mungkin untuk %s sebab Anda punya berkas tak tergabung."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Pendasaran ulang tidak mungkin sebab Anda punya berkas tak tergabung."
 
 #: advice.c
 msgid ""
@@ -802,6 +791,25 @@ msgstr "Mohon komit perubahan Anda sebelum menggabungkan."
 msgid "Exiting because of unfinished merge."
 msgstr "Keluar karena penggabungan belum selesai."
 
+#: advice.c
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Cabang yang menyebar tidak dapat dimajucepatkan, Anda perlu salah satu "
+"dari:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"atau:\n"
+"\n"
+"\tgit rebase\n"
+
 #: advice.c
 msgid "Not possible to fast-forward, aborting."
 msgstr "Tidak mungkin untuk maju cepat, batalkan."
@@ -900,6 +908,11 @@ msgstr "baris perintah diakhiri dengan \\"
 msgid "unclosed quote"
 msgstr "tanda kutip tak ditutup"
 
+#: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
+#: builtin/receive-pack.c builtin/tag.c
+msgid "too many arguments"
+msgstr "terlalu banyak argumen"
+
 #: apply.c
 #, c-format
 msgid "unrecognized whitespace option '%s'"
@@ -910,14 +923,15 @@ msgstr "opsi spasi putih tidak dikenal '%s'"
 msgid "unrecognized whitespace ignore option '%s'"
 msgstr "opsi abai spasi putih tidak dikenal '%s'"
 
-#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout.c
-#: builtin/clone.c builtin/commit.c builtin/describe.c builtin/diff-tree.c
-#: builtin/difftool.c builtin/fast-export.c builtin/fetch.c builtin/help.c
-#: builtin/index-pack.c builtin/init-db.c builtin/log.c builtin/ls-files.c
-#: builtin/merge-base.c builtin/merge.c builtin/pack-objects.c builtin/push.c
-#: builtin/rebase.c builtin/repack.c builtin/reset.c builtin/rev-list.c
-#: builtin/show-branch.c builtin/stash.c builtin/submodule--helper.c
-#: builtin/tag.c builtin/worktree.c parse-options.c range-diff.c revision.c
+#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
+#: builtin/checkout.c builtin/clone.c builtin/commit.c builtin/describe.c
+#: builtin/diff-tree.c builtin/difftool.c builtin/fast-export.c builtin/fetch.c
+#: builtin/help.c builtin/index-pack.c builtin/init-db.c builtin/log.c
+#: builtin/ls-files.c builtin/merge-base.c builtin/merge.c
+#: builtin/pack-objects.c builtin/push.c builtin/rebase.c builtin/repack.c
+#: builtin/reset.c builtin/rev-list.c builtin/show-branch.c builtin/stash.c
+#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c
+#: range-diff.c revision.c
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
 msgstr "Opsi '%s' dan '%s' tidak dapat digunakan bersamaan"
@@ -927,6 +941,14 @@ msgstr "Opsi '%s' dan '%s' tidak dapat digunakan bersamaan"
 msgid "'%s' outside a repository"
 msgstr "'%s' di luar repositori"
 
+#: apply.c
+msgid "failed to read patch"
+msgstr "gagal membaca tambalan"
+
+#: apply.c
+msgid "patch too large"
+msgstr "tambalan terlalu besar"
+
 #: apply.c
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
@@ -1303,7 +1325,7 @@ msgstr ""
 msgid "unable to add cache entry for %s"
 msgstr "tidak dapat menambahkan entri tembolok untuk %s"
 
-#: apply.c builtin/bisect--helper.c builtin/gc.c
+#: apply.c builtin/bisect.c builtin/gc.c
 #, c-format
 msgid "failed to write to '%s'"
 msgstr "gagal menulis ke '%s'"
@@ -1344,6 +1366,11 @@ msgstr "memotong nama berkas .rej ke %.*s.rej"
 msgid "cannot open %s"
 msgstr "tidak dapat membuka %s"
 
+#: apply.c rerere.c
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "tidak dapat batal taut '%s'"
+
 #: apply.c
 #, c-format
 msgid "Hunk #%d applied cleanly."
@@ -1364,7 +1391,7 @@ msgid "No valid patches in input (allow with \"--allow-empty\")"
 msgstr ""
 "Tidak ada tambalan valid dalam masukan (perbolehkan dengan \"--allow-empty\")"
 
-#: apply.c
+#: apply.c t/helper/test-cache-tree.c
 msgid "unable to read index file"
 msgstr "tidak dapa membaca berkas indeks"
 
@@ -1394,7 +1421,7 @@ msgid_plural "%d lines applied after fixing whitespace errors."
 msgstr[0] "%d baris diterapkan setelah memperbaiki kesalahan spasi putih."
 msgstr[1] "%d baris diterapkan setelah memperbaiki kesalahan spasi putih."
 
-#: apply.c builtin/add.c builtin/mv.c builtin/rm.c
+#: apply.c builtin/mv.c builtin/rm.c
 msgid "Unable to write new index file"
 msgstr "Tidak dapat menulis berkas indeks baru"
 
@@ -1584,6 +1611,11 @@ msgstr "git archive --remote <repo> [--exec <perintah>] --list"
 msgid "cannot read '%s'"
 msgstr "tidak dapat membaca '%s'"
 
+#: archive.c
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "spek jalur '%s' mencocoki berkas di luar direktori saat ini"
+
 #: archive.c builtin/add.c builtin/rm.c
 #, c-format
 msgid "pathspec '%s' did not match any files"
@@ -1599,15 +1631,11 @@ msgstr "tidak ada referensi seperti: %.*s"
 msgid "not a valid object name: %s"
 msgstr "bukan nama objek valid: %s"
 
-#: archive.c
+#: archive.c t/helper/test-cache-tree.c
 #, c-format
 msgid "not a tree object: %s"
 msgstr "bukan objek pohon: %s"
 
-#: archive.c
-msgid "current working directory is untracked"
-msgstr "direktori kerja saat ini tak terlacak"
-
 #: archive.c
 #, c-format
 msgid "File not found: %s"
@@ -1641,7 +1669,7 @@ msgstr "fmt"
 msgid "archive format"
 msgstr "format arsip"
 
-#: archive.c builtin/log.c
+#: archive.c builtin/log.c parse-options.h
 msgid "prefix"
 msgstr "prefiks"
 
@@ -1650,7 +1678,7 @@ msgid "prepend prefix to each pathname in the archive"
 msgstr "tambahkan prefiks di depan setiap nama jalur dalam arsip"
 
 #: archive.c builtin/blame.c builtin/commit-tree.c builtin/config.c
-#: builtin/fast-export.c builtin/grep.c builtin/hash-object.c
+#: builtin/fast-export.c builtin/gc.c builtin/grep.c builtin/hash-object.c
 #: builtin/ls-files.c builtin/notes.c builtin/read-tree.c parse-options.h
 msgid "file"
 msgstr "berkas"
@@ -1675,6 +1703,15 @@ msgstr "baca .gitattributes dalam direktori kerja"
 msgid "report archived files on stderr"
 msgstr "laporkan berkas terarsip ke error standar"
 
+#: archive.c builtin/clone.c builtin/fetch.c builtin/pack-objects.c
+#: builtin/pull.c
+msgid "time"
+msgstr "waktu"
+
+#: archive.c
+msgid "set modification time of archive entries"
+msgstr "setel waktu modifikasi entri arsip"
+
 #: archive.c
 msgid "set compression level"
 msgstr "setel level kompresi"
@@ -1730,6 +1767,15 @@ msgstr "Argumen tidak didukung untuk format '%s': -%d"
 msgid "%.*s is not a valid attribute name"
 msgstr "%.*s bukan sebuah nama atribut valid"
 
+#: attr.c
+msgid "unable to add additional attribute"
+msgstr "Tidak dapat menambahkan atribut tambahan"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "mengabaikan baris atribut ke-%d yang terlalu panjang"
+
 #: attr.c
 #, c-format
 msgid "%s not allowed: %s:%d"
@@ -1743,6 +1789,25 @@ msgstr ""
 "Pola negatif diabaikan di atribut git\n"
 "Gunakan '\\!' untuk tanda seru awal literal."
 
+#: attr.c
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "tidak dapat menulis berkas gitattributes '%s'"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "mengabaikan berkas gitattributes yang terlalu besar '%s'"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "mengabaikan blob gitattributes '%s' yang terlalu besar"
+
+#: attr.c
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "--attr-source atau GIT_ATTR_SOURCE jelek"
+
 #: bisect.c
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
@@ -1866,18 +1931,14 @@ msgstr[1] "Membagi dua: %d revisi tersisa untuk diuji setelah ini %s\n"
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents dan --reverse tidak dapat dipadu dengan baik."
 
-#: blame.c
-msgid "cannot use --contents with final commit object name"
-msgstr "tidak dapat menggunakan --contents dengan nama objek komit final"
-
 #: blame.c
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse dan --first-parent bersama-sama butuh komit terbaru yang disebutkan"
 
 #: blame.c builtin/commit.c builtin/log.c builtin/merge.c
-#: builtin/pack-objects.c builtin/shortlog.c bundle.c midx.c pack-bitmap.c
-#: ref-filter.c remote.c sequencer.c submodule.c
+#: builtin/pack-objects.c builtin/shortlog.c midx.c pack-bitmap.c remote.c
+#: sequencer.c submodule.c
 msgid "revision walk setup failed"
 msgstr "persiapan jalan revisi gagal"
 
@@ -2009,8 +2070,10 @@ msgstr "sebuah cabang bernama '%s' sudah ada"
 
 #: branch.c
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "tidak dapat memperbarui paksa cabang '%s' yang ter-check out pada '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr ""
+"tidak dapat memperbarui paksa cabang '%s' yang yang digunakan oleh pohon "
+"kerja pada '%s'"
 
 #: branch.c
 #, c-format
@@ -2067,11 +2130,11 @@ msgstr "submodul '%s': tidak dapat menemukan submodul"
 #: branch.c
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
-"Anda dapat mencoba memperbarui submodul dengan 'git checkout %s && git "
-"submodule update --init'"
+"Anda dapat mencoba memperbarui submodul dengan 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 
 #: branch.c
 #, c-format
@@ -2080,13 +2143,8 @@ msgstr "submodul '%s': tidak dapat membuat cabang '%s'"
 
 #: branch.c
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' sudah di-checkout pada '%s'"
-
-#: branch.c
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD dari pohon kerja %s tidak diperbarui"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s' sudah digunakan oleh pohon kerja di '%s'"
 
 #: builtin/add.c
 msgid "git add [<options>] [--] <pathspec>..."
@@ -2097,49 +2155,39 @@ msgstr "git add [<opsi>] [--] <pathspec>..."
 msgid "cannot chmod %cx '%s'"
 msgstr "tidak dapat chmod %cx '%s'"
 
-#: builtin/add.c
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "status diff tak diharapkan %c"
-
-#: builtin/add.c builtin/commit.c
-msgid "updating files failed"
-msgstr "gagal memperbarui berkas"
-
-#: builtin/add.c
-#, c-format
-msgid "remove '%s'\n"
-msgstr "hapus '%s'\n"
-
 #: builtin/add.c
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Perubahan tak tergelar setelah menyegarkan indeks:"
 
-#: builtin/add.c builtin/rev-parse.c
-msgid "Could not read the index"
-msgstr "Tidak dapat membaca indeks"
+#: builtin/add.c
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"setelan add.interactive.useBuiltin sudah dihapus!\n"
+"Selengkapnya lihat entrinya di 'git help config'."
 
 #: builtin/add.c
-msgid "Could not write patch"
-msgstr "Tidak dapat menulis tambalan"
+msgid "could not read the index"
+msgstr "tidak dapat membaca indeks"
 
 #: builtin/add.c
 msgid "editing patch failed"
 msgstr "Gagal menyunting tambalan"
 
-#: builtin/add.c
+#: builtin/add.c read-cache.c
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Tidak dapat men-stat '%s'"
+msgid "could not stat '%s'"
+msgstr "tidak dapat men-stat '%s'"
 
 #: builtin/add.c
-msgid "Empty patch. Aborted."
-msgstr "Tambalan kosong. Dibatalkan."
+msgid "empty patch. aborted"
+msgstr "tambalan kosong, dibatalkan"
 
 #: builtin/add.c
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Tidak dapat terapkan '%s'"
+msgid "could not apply '%s'"
+msgstr "tidak dapat menerapkan '%s'"
 
 #: builtin/add.c
 msgid "The following paths are ignored by one of your .gitignore files:\n"
@@ -2300,6 +2348,11 @@ msgstr ""
 msgid "index file corrupt"
 msgstr "berkas indeks rusak"
 
+#: builtin/add.c builtin/am.c builtin/checkout.c builtin/clone.c
+#: builtin/commit.c builtin/stash.c merge.c rerere.c
+msgid "unable to write new index file"
+msgstr "gagal menulis berkas indeks baru"
+
 #: builtin/am.c builtin/mailinfo.c mailinfo.c
 #, c-format
 msgid "bad action '%s' for '%s'"
@@ -2350,7 +2403,7 @@ msgstr "fseek gagal"
 msgid "could not open '%s' for reading"
 msgstr "tidak dapat membuka '%s' untuk dibaca"
 
-#: builtin/am.c builtin/rebase.c sequencer.c strbuf.c wrapper.c
+#: builtin/am.c builtin/rebase.c editor.c sequencer.c wrapper.c
 #, c-format
 msgid "could not open '%s' for writing"
 msgstr "tidak dapat membuka '%s' untuk ditulis"
@@ -2563,11 +2616,6 @@ msgstr ""
 "Anda mungkin jalankan `git rm` pada berkas untuk menerima \"penghapusan oleh "
 "mereka\" untuk itu."
 
-#: builtin/am.c builtin/checkout.c builtin/clone.c builtin/stash.c merge.c
-#: rerere.c
-msgid "unable to write new index file"
-msgstr "gagal menulis berkas indeks baru"
-
 #: builtin/am.c builtin/reset.c
 #, c-format
 msgid "Could not parse object '%s'."
@@ -2585,16 +2633,11 @@ msgstr ""
 "Sepertinya Anda telah memindahkan HEAD sejak kegagalan 'am' terakhir.\n"
 "Tidak memutar ulang ke ORIG_HEAD"
 
-#: builtin/am.c builtin/bisect--helper.c worktree.c
+#: builtin/am.c builtin/bisect.c worktree.c
 #, c-format
 msgid "failed to read '%s'"
 msgstr "gagal membaca '%s'"
 
-#: builtin/am.c
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "Opsi '%s=%s' dan '%s=%s' tidak dapat digunakan bersamaan"
-
 #: builtin/am.c
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<opsi>] [(<mbox> | <Maildir>)...]"
@@ -2607,6 +2650,10 @@ msgstr "git am [<opsi>] (--continue | --skip | --abort)"
 msgid "run interactively"
 msgstr "jalankan secara interaktif"
 
+#: builtin/am.c
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "lewati kail pre-applypatch dan applypatch-msg"
+
 #: builtin/am.c
 msgid "historical option -- no-op"
 msgstr "opsi bersejarah -- no-op"
@@ -2644,11 +2691,6 @@ msgstr "lewatkan opsi -m ke git-mailinfo"
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "lewatkan opsi --keep-cr ke git-mailsplit untuk format mbox"
 
-#: builtin/am.c
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"jangan lewatkan opsi --keep-cr ke git-mailsplit tak bergantung pada am.keepcr"
-
 #: builtin/am.c
 msgid "strip everything before a scissors line"
 msgstr "copot semuanya sebelum garis gunting"
@@ -2798,112 +2840,106 @@ msgstr "git archive: kesalahan protokol"
 msgid "git archive: expected a flush"
 msgstr "git archive: sebuah bilasan diharapkan"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<komit>]"
-
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<istilah> --term-{old,"
-"good}=<istilah>] [--no-checkout] [--first-parent] [<jelek> [<bagus>...]] "
-"[--] [<jalur>...]"
+"git bisect start [--term-(new,bad)=<istilah> --term-(old, good)=<istilah>] "
+"[--no-checkout] [--first-parent] [<jelek> [<bagus>...]] [--] [<jalur>...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<revisi>]"
+#: builtin/bisect.c
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<revisi>...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<revisi>...]"
+#: builtin/bisect.c
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<revisi>|<rentang>)...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <nama berkas>"
+#: builtin/bisect.c
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<komit>]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<revisi>|<rentang>)...]"
+#: builtin/bisect.c
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <berkas log>"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <perintah>..."
+#: builtin/bisect.c
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <perintah> [<argumen>...]"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
 msgstr "tidak dapat membuka berkas '%s' dalam mode '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "could not write to file '%s'"
 msgstr "tidak dapat menulis ke berkas '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' for reading"
 msgstr "tidak dapat membuka berkas '%s' untuk dibaca"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' is not a valid term"
 msgstr "'%s' bukan istilah yang valid"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "can't use the builtin command '%s' as a term"
 msgstr "tidak dapat menggunakan perintah bawaan '%s' sebagai istilah"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "can't change the meaning of the term '%s'"
 msgstr "tidak dapat mengubah makna istilah '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "please use two different terms"
 msgstr "mohon gunakan dua istilah yang berbeda"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "We are not bisecting.\n"
 msgstr "Kami tidak sedang membagi dua.\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' is not a valid commit"
 msgstr "'%s' bukan sebuah komit yang valid"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
 msgstr ""
 "tidak dapat men-checkout HEAD asli '%s'. Coba 'git bisect reset <komit>'."
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad bisect_write argument: %s"
 msgstr "argument bisect_write jelek: %s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "couldn't get the oid of the rev '%s'"
 msgstr "tidak dapat mendapatkan oid revisi '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "couldn't open the file '%s'"
 msgstr "tidak dapat membuka berkas '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Invalid command: you're currently in a %s/%s bisect"
 msgstr "Perintah tidak valid: sekarang Anda berada dalam pembagian dua %s/%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "You need to give me at least one %s and %s revision.\n"
@@ -2912,7 +2948,7 @@ msgstr ""
 "Anda perlu berikan saya setidaknya satu revisi %s dan %s.\n"
 "Untuk itu Anda dapat menggunakan \"git bisect %s\" dan \"git bisect %s\"."
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "You need to start by \"git bisect start\".\n"
@@ -2923,7 +2959,7 @@ msgstr ""
 "Lalu Anda perlu berikan saya setidaknya satu revisi %s dan %s.\n"
 "Untuk itu Anda dapat menggunakan \"git bisect %s\" dan \"git bisect %s\"."
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "bisecting only with a %s commit"
 msgstr "membagi dua hanya dengan sebuah komit %s"
@@ -2932,30 +2968,30 @@ msgstr "membagi dua hanya dengan sebuah komit %s"
 #. translation. The program will only accept English input
 #. at this point.
 #.
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Are you sure [Y/n]? "
 msgstr "Anda yakin [Y/n]?"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "status: waiting for both good and bad commits\n"
 msgstr "status: menunggu komit bagus dan jelek\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "status: waiting for bad commit, %d good commit known\n"
 msgid_plural "status: waiting for bad commit, %d good commits known\n"
 msgstr[0] "status: menunggu komit jelek, %d komit bagus diketahui\n"
 msgstr[1] "status: menunggu komit jelek, %d komit bagus diketahui\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "status: waiting for good commit(s), bad commit known\n"
 msgstr "status: menunggu komit bagus, komit jelek diketahui\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "no terms defined"
 msgstr "tidak ada istilah yang didefinisikan"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "Your current terms are %s for the old state\n"
@@ -2964,7 +3000,7 @@ msgstr ""
 "Istilah Anda saat ini adalah %s untuk keadaan lama\n"
 "dan %s untuk keadaan baru.\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "invalid argument %s for 'git bisect terms'.\n"
@@ -2973,52 +3009,48 @@ msgstr ""
 "argumen %s tidak valid untuk 'git bisect terms'.\n"
 "Opsi yang didukung adalah: --term-good|--term-old dan --term-bad|--term-new."
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "revision walk setup failed\n"
 msgstr "setup jalan revisi gagal\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "could not open '%s' for appending"
 msgstr "tidak dapat membuka '%s' untuk menambahkan"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "'' is not a valid term"
 msgstr "'' bukan istilah yang valid"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "unrecognized option: '%s'"
 msgstr "opsi tidak dikenal: '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' does not appear to be a valid revision"
 msgstr "'%s' sepertinya bukan revisi valid"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bad HEAD - I need a HEAD"
 msgstr "HEAD jelek - saya butuh HEAD"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
 msgstr "gagal men-checkout '%s'. Coba 'git bisect start <cabang valid>'."
 
-#: builtin/bisect--helper.c
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "tidak akan membagi dua pada pohon yang di-cg-seek"
-
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bad HEAD - strange symbolic ref"
 msgstr "HEAD jelek - referensi simbolik aneh"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "invalid ref: '%s'"
 msgstr "referensi tidak valid: '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "You need to start by \"git bisect start\"\n"
 msgstr "Anda perlu memulai dengan \"git bisect start\"\n"
 
@@ -3026,167 +3058,130 @@ msgstr "Anda perlu memulai dengan \"git bisect start\"\n"
 #. translation. The program will only accept English input
 #. at this point.
 #.
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Do you want me to do it for you [Y/n]? "
 msgstr "Anda ingin saya melakukannya untuk Anda [Y/n]"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Please call `--bisect-state` with at least one argument"
 msgstr "Mohon panggil `--bisect-state` dengan setidaknya satu argumen"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'git bisect %s' can take only one argument."
 msgstr "'git bisect %s' hanya dapat mengambil satu argumen."
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad rev input: %s"
 msgstr "Masukan revisi jelek: %s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad rev input (not a commit): %s"
 msgstr "Masukan revisi jelek (bukan sebuah komit): %s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "We are not bisecting."
 msgstr "Kami tidak sedang membagi dua."
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s'?? what are you talking about?"
 msgstr "'%s'?? Anda bilang tentang apa?"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot read file '%s' for replaying"
 msgstr "tidak dapat membuka berkas '%s' untuk memainkan ulang"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "running %s\n"
 msgstr "menjalankan %s\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bisect run failed: no command provided."
 msgstr "bisect run gagal: tidak ada perintah yang diberikan"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid "unable to verify '%s' on good revision"
-msgstr "tidak dapat memverifikasi '%s' pada revisi bagus"
+msgid "unable to verify %s on good revision"
+msgstr "tidak dapat memverifikasi %s pada revisi bagus"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "bogus exit code %d for good revision"
 msgstr "kode keluar gadungan %d untuk revisi bagu"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
-msgstr "bisect run gagal: kode keluar %d dari '%s' < 0 atau >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "bisect run gagal: kode keluar %d dari %s < 0 atau >= 128"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' for writing"
 msgstr "tidak dapat membuka berkas '%s' untuk ditulis"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bisect run cannot continue any more"
 msgstr "bisect run tidak dapat dilanjutkan lagi"
 
-#: builtin/bisect--helper.c
-#, c-format
+#: builtin/bisect.c
 msgid "bisect run success"
 msgstr "bisect run sukses"
 
-#: builtin/bisect--helper.c
-#, c-format
+#: builtin/bisect.c
 msgid "bisect found first bad commit"
 msgstr "pembagian dua menemukan komit jelek pertama"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
-msgstr ""
-"bisect run gagal: 'git bisect--helper --bisect-state %s' keluar dengan kode "
-"keluar %d"
-
-#: builtin/bisect--helper.c
-msgid "reset the bisection state"
-msgstr "setel ulang keadaan pembagian dua"
-
-#: builtin/bisect--helper.c
-msgid "check whether bad or good terms exist"
-msgstr "periksa apakah ada istilah jelek atau bagus"
-
-#: builtin/bisect--helper.c
-msgid "print out the bisect terms"
-msgstr "cetak istilah pembagian dua"
-
-#: builtin/bisect--helper.c
-msgid "start the bisect session"
-msgstr "mulai sesi pembagian dua"
-
-#: builtin/bisect--helper.c
-msgid "find the next bisection commit"
-msgstr "temukan komit pembagian dua berikutnya"
-
-#: builtin/bisect--helper.c
-msgid "mark the state of ref (or refs)"
-msgstr "tandai keadaan referensi"
-
-#: builtin/bisect--helper.c
-msgid "list the bisection steps so far"
-msgstr "daftar langkah pembagian dua sejauh ini"
-
-#: builtin/bisect--helper.c
-msgid "replay the bisection process from the given file"
-msgstr "mainkan ulang proses pembagian dua dari berkas yang diberikan"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr "bisect run gagal: 'git bisect %s' keluar dengan kode keluar %d"
 
-#: builtin/bisect--helper.c
-msgid "skip some commits for checkout"
-msgstr "lewati beberapa komit untuk checkout"
-
-#: builtin/bisect--helper.c
-msgid "visualize the bisection"
-msgstr "visualisasikan pembagian dua"
-
-#: builtin/bisect--helper.c
-msgid "use <cmd>... to automatically bisect"
-msgstr "gunakan <cmd>... untuk bagi dua otomatis."
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "'%s' perlu baik tanpa argumen atau sebuah komit"
 
-#: builtin/bisect--helper.c
-msgid "no log for BISECT_WRITE"
-msgstr "tidak ada log untuk BISECT_WRITE"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "%s perlu sebuah argumen"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset butuh baik tanpa argumen atau sebuah komit"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "'%s' butuh tanpa argumen"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms butuh 0 atau 1 argumen"
+#: builtin/bisect.c
+msgid "no logfile given"
+msgstr "tidak ada berkas log yang diberikan"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next butuh 0 argumen"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "'%s' gagal: tidak ada perintah yang diberikan."
 
-#: builtin/bisect--helper.c
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log butuh 0 argumen"
+#: builtin/bisect.c
+msgid "need a command"
+msgstr "butuh sebuah perintah"
 
-#: builtin/bisect--helper.c
-msgid "no logfile given"
-msgstr "tidak ada berkas log yang diberikan"
+#: builtin/bisect.c builtin/cat-file.c
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "perintah tidak dikenal: '%s'"
 
 #: builtin/blame.c
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<opsi>] [<opsi revisi>] [<revisi>] [--] <berkas>"
 
+#: builtin/blame.c
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<opsi>] [<opsi revisi>] [<revisi>] [--] <berkas>"
+
 #: builtin/blame.c
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<opsi revisi> didokumentasikan dalam git-rev-list(1)"
@@ -3390,60 +3385,66 @@ msgstr "git branch [<opsi>] [-r | -a] [--format]"
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "menghapus cabang '%s' yang sudah digabungkan ke\n"
-"         '%s', tapi belum digabungkan ke HEAD."
+"         '%s', tapi belum digabungkan ke HEAD"
 
 #: builtin/branch.c
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
 "tidak menghapus cabang '%s' yang belum digabungkan ke\n"
-"         '%s', walaupun tergabung ke HEAD."
+"         '%s', walaupun tergabung ke HEAD"
 
 #: builtin/branch.c
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Tidak dapat mencari objek komit untuk '%s'"
+msgid "couldn't look up commit object for '%s'"
+msgstr "tidak dapat mencari objek komit untuk '%s'"
 
 #: builtin/branch.c
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
-"Cabang '%s' belum sepenuhnya tergabung.\n"
-"Kalau Anda yakin ingin menghapusnya, jalankan 'git branch -D %s'."
+"cabang '%s' belum sepenuhnya tergabung.\n"
+"kalau Anda yakin ingin menghapusnya, jalankan 'git branch -D %s'"
 
 #: builtin/branch.c
-msgid "Update of config-file failed"
-msgstr "Pembaruan berkas konfigurasi gagal"
+msgid "update of config-file failed"
+msgstr "pembaruan berkas konfigurasi gagal"
 
 #: builtin/branch.c
 msgid "cannot use -a with -d"
 msgstr "tidak dapat gunakan -a dengan -d"
 
 #: builtin/branch.c
-msgid "Couldn't look up commit object for HEAD"
-msgstr "Tidak dapat mencari objek komit untuk HEAD"
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"tidak dapat menghapus cabang '%s' yang digunakan oleh pohon kerja di '%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Tidak dapat menghapus cabang '%s' yang ter-checkout pada '%s'"
+msgid "remote-tracking branch '%s' not found"
+msgstr "cabang pelacak remote '%s' tidak ditemukan"
 
 #: builtin/branch.c
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "cabang pelacak remote '%s' tidak ditemukan."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"cabang '%s' tidak ditemukan.\n"
+"Mungkin anda lupa menambahkan --remote?"
 
 #: builtin/branch.c
 #, c-format
-msgid "branch '%s' not found."
-msgstr "cabang '%s' tidak ditemukan."
+msgid "branch '%s' not found"
+msgstr "cabang '%s' tidak ditemukan"
 
 #: builtin/branch.c
 #, c-format
@@ -3470,57 +3471,64 @@ msgstr "HEAD (%s) merujuk diluar refs/heads/"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "Cabang %s sedang didasarkan ulang pada %s"
+msgid "branch %s is being rebased at %s"
+msgstr "cabang %s sedang didasarkan ulang pada %s"
+
+#: builtin/branch.c
+#, c-format
+msgid "branch %s is being bisected at %s"
+msgstr "cabang %s sedang dibagi dua pada %s"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "Cabang %s sedang dibagi dua pada %s"
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD dari pohon kerja %s tidak diperbarui"
 
 #: builtin/branch.c
-msgid "cannot copy the current branch while not on any."
-msgstr "tidak dapat menyalin cabang saat ini ketika tidak ada."
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "nama cabang tidak valid: '%s'"
 
 #: builtin/branch.c
-msgid "cannot rename the current branch while not on any."
-msgstr "tidak dapat mengganti nama cabang saat ini ketika tidak ada."
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "belum ada komit pada cabang '%s'."
 
 #: builtin/branch.c
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Nama cabang tidak valid: '%s'"
+msgid "no branch named '%s'"
+msgstr "tidak ada cabang bernama '%s'"
 
 #: builtin/branch.c
-msgid "Branch rename failed"
-msgstr "Penggantian nama cabang gagal"
+msgid "branch rename failed"
+msgstr "penggantian nama cabang gagal"
 
 #: builtin/branch.c
-msgid "Branch copy failed"
-msgstr "Penyalinan cabang gagal"
+msgid "branch copy failed"
+msgstr "penyalinan cabang gagal"
 
 #: builtin/branch.c
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Salinan cabang salah nama '%s' dibuat"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "salinan cabang salah nama '%s' dibuat"
 
 #: builtin/branch.c
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Cabang salah nama '%s' berganti nama"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "cabang salah nama '%s' berganti nama"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Cabang berganti nama ke %s, tapi HEAD tidak diperbarui!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "cabang berganti nama ke %s, tapi HEAD tidak diperbarui"
 
 #: builtin/branch.c
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "Cabang berganti nama, tapi pembaruan berkas konfigurasi gagal"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "cabang berganti nama, tapi pembaruan berkas konfigurasi gagal"
 
 #: builtin/branch.c
-msgid "Branch is copied, but update of config-file failed"
-msgstr "Cabang disalin, tapi pembaruan berkas konfigurasi gagal"
+msgid "branch is copied, but update of config-file failed"
+msgstr "cabang disalin, tapi pembaruan berkas konfigurasi gagal"
 
 #: builtin/branch.c
 #, c-format
@@ -3605,6 +3613,10 @@ msgstr "pindah/ganti nama cabang dan reflog-nya"
 msgid "move/rename a branch, even if target exists"
 msgstr "pindah/ganti nama cabang, walaupun target ada"
 
+#: builtin/branch.c builtin/for-each-ref.c builtin/tag.c
+msgid "do not output a newline after empty formatted refs"
+msgstr "jangan keluarkan baris baru setelah referensi terformat kosong"
+
 #: builtin/branch.c
 msgid "copy a branch and its reflog"
 msgstr "salin cabang dan reflog-nya"
@@ -3666,9 +3678,9 @@ msgstr "rekursi melalui submodul"
 msgid "format to use for the output"
 msgstr "format yang digunakan untuk keluaran"
 
-#: builtin/branch.c builtin/submodule--helper.c submodule.c
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Gagal menguraikan HEAD sebagai referensi valid."
+#: builtin/branch.c
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "gagal menguraikan HEAD sebagai referensi valid."
 
 #: builtin/branch.c builtin/clone.c
 msgid "HEAD not found below refs/heads!"
@@ -3691,22 +3703,20 @@ msgid "branch name required"
 msgstr "nama cabang diperlukan"
 
 #: builtin/branch.c
-msgid "Cannot give description to detached HEAD"
-msgstr "Tidak dapat memberikan deskripsi ke HEAD terpisah"
+msgid "cannot give description to detached HEAD"
+msgstr "tidak dapat memberikan deskripsi ke HEAD terpisah"
 
 #: builtin/branch.c
 msgid "cannot edit description of more than one branch"
 msgstr "tidak dapat menyunting deskripsi lebih dari satu cabang"
 
 #: builtin/branch.c
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Belum ada komit pada cabang '%s'."
+msgid "cannot copy the current branch while not on any"
+msgstr "tidak dapat menyalin cabang saat ini ketika tidak ada"
 
 #: builtin/branch.c
-#, c-format
-msgid "No branch named '%s'."
-msgstr "Tidak ada cabang bernama '%s'."
+msgid "cannot rename the current branch while not on any"
+msgstr "tidak dapat mengganti nama cabang saat ini ketika tidak ada"
 
 #: builtin/branch.c
 msgid "too many branches for a copy operation"
@@ -3723,10 +3733,9 @@ msgstr "terlalu banyak argumen untuk menyetel hulu baru"
 #: builtin/branch.c
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"tidak dapat menyetel hulu HEAD ke %s ketika itu tak menunjuk pada cabang "
-"apapun."
+"tidak dapat menyetel hulu HEAD ke %s ketika tak menunjuk pada cabang apapun"
 
 #: builtin/branch.c
 #, c-format
@@ -3743,31 +3752,30 @@ msgid "too many arguments to unset upstream"
 msgstr "terlalu banyak argumen untuk batal-setel hulu"
 
 #: builtin/branch.c
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr ""
-"tidak dapat membatal-setel hulu HEAD ketika itu tak menunjuk pada cabang "
-"apapun."
+"tidak dapat membatal-setel hulu HEAD ketika tak menunjuk pada cabang apapun"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "Cabang '%s' tidak ada informasi hulu"
+msgid "branch '%s' has no upstream information"
+msgstr "cabang '%s' tidak ada informasi hulu"
 
 #: builtin/branch.c
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"Opsi -a dan -r tidak mengambil nama cabang.\n"
-"Mungkin maksud Anda gunakan: -a|-r --list <pola>?"
+"opsi -a dan -r tidak mengambil nama cabang.\n"
+"Mungkin maksud Anda menggunakan: -a|-r --list <pola>?"
 
 #: builtin/branch.c
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "opsi '--set-upstream' tidak lagi didukung. Mohon gunakan '--track' atau '--"
-"set-upstream-to' sebagai gantinya."
+"set-upstream-to' sebagai gantinya"
 
 #: builtin/bugreport.c
 msgid "git version:\n"
@@ -3794,11 +3802,12 @@ msgstr ""
 
 #: builtin/bugreport.c
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory <berkas>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]]"
+"git bugreport [(-o | --output-directory) <berkas>] [(-s |-- suffix) "
+"<format>]\n"
+"              [--diagnose[=<mode>]]"
 
 #: builtin/bugreport.c
 msgid ""
@@ -3852,6 +3861,11 @@ msgstr "sebutkan tujuan untuk berkas(-berkas) laporan bug"
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "sebutkan akhiran format strftime untuk nama(-nama) berkas"
 
+#: builtin/bugreport.c
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "argumen tidak dikenal `%s'"
+
 #: builtin/bugreport.c builtin/diagnose.c
 #, c-format
 msgid "could not create leading directories for '%s'"
@@ -3881,20 +3895,28 @@ msgid "Created new report at '%s'.\n"
 msgstr "Laporan baru dibuat pada '%s'.\n"
 
 #: builtin/bundle.c
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [<opsi>] <berkas> <argumen git-rev-list>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<versi>] <berkas> <argumen git-rev-list>"
 
 #: builtin/bundle.c
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [<opsi>] <berkas>"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <berkas>"
 
 #: builtin/bundle.c
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads <berkas> [<nama referensi>...]"
 
 #: builtin/bundle.c
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle <berkas> [<nama referensi>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <berkas> [<nama referensi>...]"
+
+#: builtin/bundle.c
+msgid "need a <file> argument"
+msgstr "butuh sebuah argumen <berkas>"
 
 #: builtin/bundle.c builtin/pack-objects.c
 msgid "do not show progress meter"
@@ -3904,13 +3926,13 @@ msgstr "jangan perlihatkan meteran perkembangan"
 msgid "show progress meter"
 msgstr "perlihatkan meteran perkembangan"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "show progress meter during object writing phase"
-msgstr "perlihatkan meteran perkembangan saat fase penulisan objek"
+#: builtin/bundle.c
+msgid "historical; same as --progress"
+msgstr "opsi bersejarah; sama dengan --progress"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "sama seperti --all-progress ketika meteran perkembangan diperlihatkan"
+#: builtin/bundle.c
+msgid "historical; does nothing"
+msgstr "opsi bersejarah: tidak apa-apa"
 
 #: builtin/bundle.c
 msgid "specify bundle format version"
@@ -3965,11 +3987,6 @@ msgstr "%s butuh sebuah argumen"
 msgid "%s takes no arguments"
 msgstr "%s tidak mengambil argumen"
 
-#: builtin/cat-file.c
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "perintah tidak dikenal: '%s'"
-
 #: builtin/cat-file.c
 msgid "only one batch option may be specified"
 msgstr "hanya satu opsi setumpuk yang mungkin disebutkan"
@@ -3986,26 +4003,26 @@ msgstr "git cat-file (-e | -p) <objek>"
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objek>"
 
+#: builtin/cat-file.c
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<revisi>:<jalur|mirip-pohon> | --path=<jalur|mirip-pohon>] "
+"<revisi>"
+
 #: builtin/cat-file.c
 msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-#: builtin/cat-file.c
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
-msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<revisi>:<jalur|mirip-pohon> | --path=<jalur|mirip-pohon>] "
-"<revisi>"
+"             [--textconv | --filters] [-z]"
 
 #: builtin/cat-file.c
 msgid "Check object existence or emit object contents"
@@ -4055,6 +4072,10 @@ msgstr "seperti --batch, tapi jangan keluarkan <isi>"
 msgid "stdin is NUL-terminated"
 msgstr "stdin diakhiri dengan NUL"
 
+#: builtin/cat-file.c
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin dan stdout diakhiri dengan NUL"
+
 #: builtin/cat-file.c
 msgid "read commands from stdin"
 msgstr "baca perintah dari masukan standar"
@@ -4138,11 +4159,6 @@ msgstr "<revisi> diperlukan dengan '%s'"
 msgid "<object> required with '-%c'"
 msgstr "<objek> diperlukan dengan '-%c'"
 
-#: builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
-msgid "too many arguments"
-msgstr "terlalu banyak argumen"
-
 #: builtin/cat-file.c
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
@@ -4150,12 +4166,19 @@ msgstr ""
 "hanya dua argumen yang diperbolehkan di dalam mode <tipe> <objek>, bukan %d"
 
 #: builtin/check-attr.c
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <atribut>...] [--] <nama jalur>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [source <mirip-pohon> [-a | --all | <atribut>...] [--] <nama "
+"jalur>..."
 
 #: builtin/check-attr.c
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <attribut>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <mirip-pohon>] [-a | --all | "
+"<attribut>...]"
 
 #: builtin/check-attr.c
 msgid "report all attributes set on file"
@@ -4173,6 +4196,14 @@ msgstr "baca nama berkas dari masukan standar"
 msgid "terminate input and output records by a NUL character"
 msgstr "akhiri masukan dan keluarkan rekaman oleh sebuah karakter NUL"
 
+#: builtin/check-attr.c
+msgid "<tree-ish>"
+msgstr "<mirip-pohon>"
+
+#: builtin/check-attr.c
+msgid "which tree-ish to check attributes at"
+msgstr "mirip-pohon mana yang diperiksa atributnya"
+
 #: builtin/check-ignore.c builtin/checkout.c builtin/gc.c builtin/worktree.c
 msgid "suppress progress reporting"
 msgstr "padamkan pelaporan kemajuan"
@@ -4379,6 +4410,11 @@ msgstr "'%s' harus disebutkan ketika '%s' tidak disebutkan"
 msgid "'%s' or '%s' cannot be used with %s"
 msgstr "'%s' atau '%s' tidak dapat digunakan untuk %s"
 
+#: builtin/checkout.c
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "'%s', '%s', atau '%s' tidak dapat digunakan ketika men-checkout pohon"
+
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' is unmerged"
@@ -4583,8 +4619,8 @@ msgid ""
 "Consider \"git merge --quit\" or \"git worktree add\"."
 msgstr ""
 "tidak dapat mengganti cabang saat penggabungan\n"
-"Pertimbangkan untuk menggunakan \"git merge --quit\" atau \"git worktree add"
-"\"."
+"Pertimbangkan untuk menggunakan \"git merge --quit\" atau \"git worktree "
+"add\"."
 
 #: builtin/checkout.c
 msgid ""
@@ -4600,8 +4636,8 @@ msgid ""
 "Consider \"git rebase --quit\" or \"git worktree add\"."
 msgstr ""
 "tidak dapat mengganti cabang saat pendasaran ulang\n"
-"Pertimbangkan untuk menggunakan \"git rebase --quit\" atau \"git worktree add"
-"\"."
+"Pertimbangkan untuk menggunakan \"git rebase --quit\" atau \"git worktree "
+"add\"."
 
 #: builtin/checkout.c
 msgid ""
@@ -4618,8 +4654,8 @@ msgid ""
 "Consider \"git revert --quit\" or \"git worktree add\"."
 msgstr ""
 "tidak dapat mengganti cabang saat pembalikan\n"
-"Pertimbangkan untuk menggunakan \"git revert --quit\" atau \"git worktree add"
-"\"."
+"Pertimbangkan untuk menggunakan \"git revert --quit\" atau \"git worktree "
+"add\"."
 
 #: builtin/checkout.c
 msgid "you are switching branch while bisecting"
@@ -4810,9 +4846,11 @@ msgstr "gunakan mode hamparan"
 
 #: builtin/clean.c
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pola>] [-x | -X] [--] <jalur>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pola>] [-x | -X] [--] [<spek "
+"jalur>...]"
 
 #: builtin/clean.c
 #, c-format
@@ -4852,7 +4890,7 @@ msgstr "Menolak menghapus direktori kerja saat ini\n"
 msgid "Would refuse to remove current working directory\n"
 msgstr "Akan menolak menghapus direktori kerja saat ini\n"
 
-#: builtin/clean.c git-add--interactive.perl
+#: builtin/clean.c
 #, c-format
 msgid ""
 "Prompt help:\n"
@@ -4865,7 +4903,7 @@ msgstr ""
 "foo        - pilih item berdasarkan prefiks unik\n"
 "           - (kosong) tidak pilih apa-apa\n"
 
-#: builtin/clean.c git-add--interactive.perl
+#: builtin/clean.c
 #, c-format
 msgid ""
 "Prompt help:\n"
@@ -4886,8 +4924,8 @@ msgstr ""
 "*          - pilih semua item\n"
 "           - (kosong) selesai memilih\n"
 
-#: builtin/clean.c git-add--interactive.perl
-#, c-format, perl-format
+#: builtin/clean.c
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Huh (%s)?\n"
 
@@ -4956,7 +4994,8 @@ msgid "remove whole directories"
 msgstr "hapus keseluruhan direktori"
 
 #: builtin/clean.c builtin/describe.c builtin/grep.c builtin/log.c
-#: builtin/ls-files.c builtin/name-rev.c builtin/show-ref.c
+#: builtin/ls-files.c builtin/name-rev.c builtin/pack-refs.c builtin/show-ref.c
+#: ref-filter.h
 msgid "pattern"
 msgstr "pola"
 
@@ -5070,7 +5109,7 @@ msgstr "checkout <cabang> daripada HEAD remote"
 msgid "path to git-upload-pack on the remote"
 msgstr "jalur ke git-upload-pack pada remote"
 
-#: builtin/clone.c builtin/fetch.c builtin/grep.c builtin/pull.c
+#: builtin/clone.c builtin/fetch.c builtin/pull.c
 msgid "depth"
 msgstr "kedalaman"
 
@@ -5078,10 +5117,6 @@ msgstr "kedalaman"
 msgid "create a shallow clone of that depth"
 msgstr "buat klon dangkal sedalam kedalaman tersebut"
 
-#: builtin/clone.c builtin/fetch.c builtin/pack-objects.c builtin/pull.c
-msgid "time"
-msgstr "waktu"
-
 #: builtin/clone.c
 msgid "create a shallow clone since a specific time"
 msgstr "buat klon dangkal sejak waktu yang disebutkan"
@@ -5132,14 +5167,6 @@ msgstr "spesifik ke server"
 msgid "option to transmit"
 msgstr "opsi untuk transmisi"
 
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv4 addresses only"
-msgstr "gunakan hanya alamat IPv4"
-
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv6 addresses only"
-msgstr "gunakan hanya alamat IPv6"
-
 #: builtin/clone.c
 msgid "apply partial clone filters to submodules"
 msgstr "terapkan saringan kloning parsial ke submodul"
@@ -5176,11 +5203,21 @@ msgstr "gagal men-stat '%s'"
 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'"
@@ -5262,11 +5299,6 @@ msgstr "Terlalu banyak argumen."
 msgid "You must specify a repository to clone."
 msgstr "Anda harus sebutkan repositori untuk diklon."
 
-#: builtin/clone.c
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "opsi '%s' dan '%s %s' tidak dapat digunakan bersamaan"
-
 #: builtin/clone.c
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
@@ -5376,6 +5408,10 @@ msgstr "gagal menginisialisasi repo, melewatkan URI bundel"
 msgid "failed to fetch objects from bundle URI '%s'"
 msgstr "gagal mengambil objek dari URI bundel '%s'"
 
+#: builtin/clone.c
+msgid "failed to fetch advertised bundles"
+msgstr "gagal mengambil bundel yang diiklankan"
+
 #: builtin/clone.c
 msgid "remote transport reported error"
 msgstr "transportasi remote melaporkan kesalahan"
@@ -5423,22 +5459,28 @@ msgstr "--command harus menjadi argumen pertama"
 
 #: builtin/commit-graph.c
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir <direktori objek>] [--shallow] [--"
+"git commit-graph verify [--object-dir <direktori>] [--shallow] [--"
 "[no-]progress]"
 
 #: builtin/commit-graph.c
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
-msgstr ""
-"git commit-graph write [--object-dir <direktori objek>] [--append] [--"
-"split[=<strategi>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[-no-]progrss] <opsi pemisah>"
-
-#: builtin/commit-graph.c builtin/fetch.c builtin/log.c
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
+msgstr ""
+"git commit-graph write [--object-dir <direktori>] [--append]\n"
+"                       [--split[=<strategi>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--[-"
+"no-]progress]\n"
+"                       <opsi pemisahan>"
+
+#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
 msgid "dir"
 msgstr "direktori"
 
@@ -5455,6 +5497,11 @@ msgstr "jika grafik komit terpisah, hanya verifikasi berkas ujung"
 msgid "Could not open commit-graph '%s'"
 msgstr "Tidak dapat membuka grafik komit '%s'"
 
+#: builtin/commit-graph.c
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "tidak dapat membuka grafik komit '%s'"
+
 #: builtin/commit-graph.c
 #, c-format
 msgid "unrecognized --split argument, %s"
@@ -5525,13 +5572,17 @@ msgstr ""
 msgid "Collecting commits from input"
 msgstr "Mengumpulkan komit dari masukan"
 
+#: builtin/commit-tree.c
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <pohon> [(-p <induk>)...]"
+
 #: builtin/commit-tree.c
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p <induk>)...] [-S[<id kunci>]] [(-m <pesan>)...] [(-F "
-"<berkas>)...] <pohon>"
+"git commit-tree [(-p <induk>)...] [-S[<id kunci>]] [(-m <pesan>)...]\n"
+"                [(-F <berkas>)...] <pohon>"
 
 #: builtin/commit-tree.c
 #, c-format
@@ -5588,12 +5639,31 @@ msgid "git commit-tree: failed to read"
 msgstr "git commit-tree: gagal membaca"
 
 #: builtin/commit.c
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<opsi>] [--] <spek jalur>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <komit> | --fixup [(amend|"
+"reword):]<komit>)]\n"
+"           [-F <berkas> | -m <pesan>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--"
+"author=<pengarang>]\n"
+"           [--date=<tanggal>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<berkas> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<nilai>])...] [-S[<id kunci>]]\n"
+"           [--] [<spek jalur>...]"
 
 #: builtin/commit.c
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<opsi>] [--] <spek jalur>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<opsi>] [--] [<spek jalur>...]"
 
 #: builtin/commit.c
 msgid ""
@@ -5649,6 +5719,10 @@ msgstr ""
 "    git cherry-pick --skip\n"
 "\n"
 
+#: builtin/commit.c read-cache.c
+msgid "updating files failed"
+msgstr "gagal memperbarui berkas"
+
 #: builtin/commit.c
 msgid "failed to unpack HEAD tree object"
 msgstr "gagal membuka objek pohon HEAD"
@@ -5673,10 +5747,6 @@ msgstr "tidak dapat memperbarui indeks sementara"
 msgid "Failed to update main cache tree"
 msgstr "gagal memperbarui tembolok pohon utama"
 
-#: builtin/commit.c
-msgid "unable to write new_index file"
-msgstr "tidak dapat menulis berkas new_index"
-
 #: builtin/commit.c
 msgid "cannot do a partial commit during a merge."
 msgstr "tidak dapat melakukan komit sebagian selama penggabungan."
@@ -5724,10 +5794,10 @@ msgstr ""
 "tidak dapat memilih karakter komentar yang tidak terpakai\n"
 "dalam pesan komit saat ini"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/merge-tree.c
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "tidak dapat mencari komit %s"
+msgid "could not lookup commit '%s'"
+msgstr "tidak dapat mencari komit '%s'"
 
 #: builtin/commit.c builtin/shortlog.c
 #, c-format
@@ -5947,7 +6017,7 @@ msgstr "hitung nilai didepan/dibelakang penuh"
 msgid "version"
 msgstr "versi"
 
-#: builtin/commit.c builtin/push.c builtin/worktree.c
+#: builtin/commit.c builtin/fetch.c builtin/push.c builtin/worktree.c
 msgid "machine-readable output"
 msgstr "keluaran yang dapat dibaca mesin"
 
@@ -6032,7 +6102,7 @@ msgstr "tangal"
 msgid "override date for commit"
 msgstr "timpa tanggal komit"
 
-#: builtin/commit.c parse-options.h ref-filter.h
+#: builtin/commit.c builtin/merge-tree.c parse-options.h ref-filter.h
 msgid "commit"
 msgstr "komit"
 
@@ -6175,18 +6245,18 @@ msgstr "Batalkan komit karena badan pesan komit kosong.\n"
 #: builtin/commit.c
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
 "repositori sudah diperbarui, tetapi tidak dapat menulis\n"
-"berkas new_index. Periksa bahwa disk tidak penuh dan kuota\n"
+"berkas indeks baru. Periksa bahwa disk tidak penuh dan kuota\n"
 "tidak terlampaui, lalu \"git restore --staged :/\" untuk pulihkan."
 
 #: builtin/config.c
 msgid "git config [<options>]"
 msgstr "git config [<opsi>]"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 #, c-format
 msgid "unrecognized --type argument, %s"
 msgstr "argumen --type tidak dikenal %s"
@@ -6215,7 +6285,7 @@ msgstr "gunakan berkas konfigurasi repositori"
 msgid "use per-worktree config file"
 msgstr "gunakan berkas konfigurasi per pohon kerja"
 
-#: builtin/config.c
+#: builtin/config.c builtin/gc.c
 msgid "use given config file"
 msgstr "gunakan berkas konfigurasi yang diberikan"
 
@@ -6295,11 +6365,11 @@ msgstr "temukan setelan warna: slot [stdout-is-tty]"
 msgid "Type"
 msgstr "Tipe"
 
-#: builtin/config.c builtin/env--helper.c builtin/hash-object.c
+#: builtin/config.c builtin/hash-object.c
 msgid "type"
 msgstr "tipe"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 msgid "value is given this type"
 msgstr "Nilai diberikan tipe ini"
 
@@ -6354,7 +6424,7 @@ msgstr ""
 "perlihatkan cakupan konfigurasi (pohon kerja, lokal, global, sistem, "
 "perintah)"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 msgid "value"
 msgstr "nilai"
 
@@ -6439,7 +6509,7 @@ msgstr "--blob hanya dapat digunakan di dalam repositori git"
 msgid "--worktree can only be used inside a git repository"
 msgstr "--worktree hanya dapat digunakan di dalam repositori git"
 
-#: builtin/config.c
+#: builtin/config.c builtin/gc.c
 msgid "$HOME not set"
 msgstr "$HOME tak disetel"
 
@@ -6553,12 +6623,20 @@ msgstr ""
 "tidak dapat mendapatkan kunci penyimpanan kredensial dalam %d milidetik"
 
 #: builtin/describe.c
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<opsi>] [<mirip-komit>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<mirip-komit>...]"
+
+#: builtin/describe.c
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<penanda>]"
 
 #: builtin/describe.c
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<opsi>] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe <blob>"
 
 #: builtin/describe.c
 msgid "head"
@@ -6713,11 +6791,11 @@ msgstr "opsi '%s' dan mirip-komit tidak dapat digunakan bersamaan"
 
 #: builtin/diagnose.c
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory <jalur>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <jalur>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 
 #: builtin/diagnose.c
 msgid "specify a destination for the diagnostics archive"
@@ -6740,6 +6818,10 @@ msgstr "--merge-base hanya bekerja dengan dua komit"
 msgid "'%s': not a regular file or symlink"
 msgstr "'%s': bukan berkas reguler atau tautan simbolik"
 
+#: builtin/diff.c
+msgid "no merge given, only parents."
+msgstr "tidak ada penggabungan yang diberikan, hanya induk."
+
 #: builtin/diff.c
 #, c-format
 msgid "invalid option: %s"
@@ -6885,34 +6967,6 @@ msgstr "tidak ada <alat> yang diberikan untuk --tool=<alat>"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "tidak ada <perintah> yang diberikan untuk --extcmd=<perintah>"
 
-#: builtin/env--helper.c
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <opsi> <variabel lingkungan>"
-
-#: builtin/env--helper.c
-msgid "default for git_env_*(...) to fall back on"
-msgstr "asali untuk git_env_*(...) agar kembali"
-
-#: builtin/env--helper.c
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr "jadi diam; hanya gunakan nilai git_env_*() sebagai kode keluar"
-
-#: builtin/env--helper.c
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr ""
-"opsi `--default' mengharapkan sebuah nilai boolean dengan `--type=bool`, "
-"bukan `%s`"
-
-#: builtin/env--helper.c
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not `"
-"%s`"
-msgstr ""
-"opsi `--default' mengharapkan sebuah nilai unsigned long dengan `--"
-"type=ulong`, bukan `%s`"
-
 #: builtin/fast-export.c
 msgid "git fast-export [<rev-list-opts>]"
 msgstr "git fast-export [<opsi rev-list>]"
@@ -7058,150 +7112,15 @@ msgstr "git fetch --all [<opsi>]"
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel tidak dapat bernilai negatif"
 
-#: builtin/fetch.c builtin/pull.c
-msgid "fetch from all remotes"
-msgstr "ambil dari semua remote"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "set upstream for git pull/fetch"
-msgstr "setel hulu untuk git pull/fetch"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "tambah ke .git/FETCH_HEAD daripada timpa"
-
-#: builtin/fetch.c
-msgid "use atomic transaction to update references"
-msgstr "gunakan transaksi atomik untuk memperbarui referensi"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "path to upload pack on remote end"
-msgstr "jalur ke paket unggah pada sisi remote"
-
-#: builtin/fetch.c
-msgid "force overwrite of local reference"
-msgstr "paksa timpa referensi lokal"
-
-#: builtin/fetch.c
-msgid "fetch from multiple remotes"
-msgstr "ambil dari banyak remote"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "fetch all tags and associated objects"
-msgstr "ambil semua tag dan objek yang bersesuaian"
-
-#: builtin/fetch.c
-msgid "do not fetch all tags (--no-tags)"
-msgstr "jangan ambil semua tag (--no-tags)"
-
-#: builtin/fetch.c
-msgid "number of submodules fetched in parallel"
-msgstr "jumlah submodul yang diambil secara bersamaan"
-
-#: builtin/fetch.c
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr ""
-"modifikasi spek referensi untuk tempatkan semua referensi di dalam refs/"
-"prefetch/"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "prune remote-tracking branches no longer on remote"
-msgstr "buang cabang pelacak remote yang tidak ada pada remote"
-
-#: builtin/fetch.c
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr "buang tag lokal yang tidak ada pada remote dan klob tag yang berubah"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "on-demand"
-msgstr "sesuai permintaan"
-
-#: builtin/fetch.c
-msgid "control recursive fetching of submodules"
-msgstr "kontrol pengambilan submodul rekursif"
-
-#: builtin/fetch.c
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "tulis referensi yang diambil ke berkas FETCH_HEAD"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "keep downloaded pack"
-msgstr "simpan paket yang diunduh"
-
-#: builtin/fetch.c
-msgid "allow updating of HEAD ref"
-msgstr "bolehkan perbarui referensi HEAD"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "deepen history of shallow clone"
-msgstr "perdalam riwayat klon dangkal"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "deepen history of shallow repository based on time"
-msgstr "perdalam riwayat repositori dangkal berdasarkan waktu"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "convert to a complete repository"
-msgstr "ubah ke repositori penuh"
-
-#: builtin/fetch.c
-msgid "re-fetch without negotiating common commits"
-msgstr "ambil ulang tanpa menegosiasikan komit yang sama"
-
-#: builtin/fetch.c
-msgid "prepend this to submodule path output"
-msgstr "tambahkan ini ke jalur keluaran submodul"
-
-#: builtin/fetch.c
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr ""
-"default untuk ambil submodul secara rekursif (prioritas lebih rendah "
-"dariberkas konfigurasi)"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "accept refs that update .git/shallow"
-msgstr "terima referensi yang memperbarui .git/shallow"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "refmap"
-msgstr "peta referensi"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "specify fetch refmap"
-msgstr "sebutkan ambil peta referensi"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "report that we have only objects reachable from this object"
-msgstr ""
-"laporkan bahwa kami hanya punya object yang bisa dicapai dari objek ini"
-
-#: builtin/fetch.c
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr ""
-"jangan ambil berkas pak; sebagai gantinya cetak leluhur dari ujung negosiasi"
-
-#: builtin/fetch.c
-msgid "run 'maintenance --auto' after fetching"
-msgstr "lakukan 'maintenance --auto' setelah pengambilan"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "check for forced-updates on all updated branches"
-msgstr "periksa pembaruan terpaksa pada semua cabang"
-
-#: builtin/fetch.c
-msgid "write the commit-graph after fetching"
-msgstr "tulis grafik komit setelah pengambilan"
-
-#: builtin/fetch.c
-msgid "accept refspecs from stdin"
-msgstr "terima spek referensi dari masukan standar"
-
 #: builtin/fetch.c
 msgid "couldn't find remote ref HEAD"
 msgstr "tidak dapat menemukan referensi remote HEAD"
 
+#: builtin/fetch.c
+#, c-format
+msgid "From %.*s\n"
+msgstr "Dari %.*s\n"
+
 #: builtin/fetch.c
 #, c-format
 msgid "object %s not found"
@@ -7291,11 +7210,6 @@ msgstr "%s tidak mengirim semua objek yang diperlukan\n"
 msgid "rejected %s because shallow roots are not allowed to be updated"
 msgstr "tolak %s karena akar dangkal tidak diperkenankan untuk diperbarui"
 
-#: builtin/fetch.c
-#, c-format
-msgid "From %.*s\n"
-msgstr "Dari %.*s\n"
-
 #: builtin/fetch.c
 #, c-format
 msgid ""
@@ -7408,9 +7322,149 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr "Anda perlu sebutkan sebuah nama tag"
 
-#: builtin/fetch.c
-msgid "--negotiate-only needs one or more --negotiation-tip=*"
-msgstr "--negotiate-only perlu satu atau lebih --negotiation-tip=*"
+#: builtin/fetch.c builtin/pull.c
+msgid "fetch from all remotes"
+msgstr "ambil dari semua remote"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "set upstream for git pull/fetch"
+msgstr "setel hulu untuk git pull/fetch"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "tambah ke .git/FETCH_HEAD daripada timpa"
+
+#: builtin/fetch.c
+msgid "use atomic transaction to update references"
+msgstr "gunakan transaksi atomik untuk memperbarui referensi"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "path to upload pack on remote end"
+msgstr "jalur ke paket unggah pada sisi remote"
+
+#: builtin/fetch.c
+msgid "force overwrite of local reference"
+msgstr "paksa timpa referensi lokal"
+
+#: builtin/fetch.c
+msgid "fetch from multiple remotes"
+msgstr "ambil dari banyak remote"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "fetch all tags and associated objects"
+msgstr "ambil semua tag dan objek yang bersesuaian"
+
+#: builtin/fetch.c
+msgid "do not fetch all tags (--no-tags)"
+msgstr "jangan ambil semua tag (--no-tags)"
+
+#: builtin/fetch.c
+msgid "number of submodules fetched in parallel"
+msgstr "jumlah submodul yang diambil secara bersamaan"
+
+#: builtin/fetch.c
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"modifikasi spek referensi untuk tempatkan semua referensi di dalam refs/"
+"prefetch/"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "prune remote-tracking branches no longer on remote"
+msgstr "buang cabang pelacak remote yang tidak ada pada remote"
+
+#: builtin/fetch.c
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr "buang tag lokal yang tidak ada pada remote dan klob tag yang berubah"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "on-demand"
+msgstr "sesuai permintaan"
+
+#: builtin/fetch.c
+msgid "control recursive fetching of submodules"
+msgstr "kontrol pengambilan submodul rekursif"
+
+#: builtin/fetch.c
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "tulis referensi yang diambil ke berkas FETCH_HEAD"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "keep downloaded pack"
+msgstr "simpan paket yang diunduh"
+
+#: builtin/fetch.c
+msgid "allow updating of HEAD ref"
+msgstr "bolehkan perbarui referensi HEAD"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "deepen history of shallow clone"
+msgstr "perdalam riwayat klon dangkal"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "deepen history of shallow repository based on time"
+msgstr "perdalam riwayat repositori dangkal berdasarkan waktu"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "convert to a complete repository"
+msgstr "ubah ke repositori penuh"
+
+#: builtin/fetch.c
+msgid "re-fetch without negotiating common commits"
+msgstr "ambil ulang tanpa menegosiasikan komit yang sama"
+
+#: builtin/fetch.c
+msgid "prepend this to submodule path output"
+msgstr "tambahkan ini ke jalur keluaran submodul"
+
+#: builtin/fetch.c
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"default untuk ambil submodul secara rekursif (prioritas lebih rendah "
+"dariberkas konfigurasi)"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "accept refs that update .git/shallow"
+msgstr "terima referensi yang memperbarui .git/shallow"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "refmap"
+msgstr "peta referensi"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "specify fetch refmap"
+msgstr "sebutkan ambil peta referensi"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "report that we have only objects reachable from this object"
+msgstr ""
+"laporkan bahwa kami hanya punya object yang bisa dicapai dari objek ini"
+
+#: builtin/fetch.c
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+"jangan ambil berkas pak; sebagai gantinya cetak leluhur dari ujung negosiasi"
+
+#: builtin/fetch.c
+msgid "run 'maintenance --auto' after fetching"
+msgstr "lakukan 'maintenance --auto' setelah pengambilan"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "check for forced-updates on all updated branches"
+msgstr "periksa pembaruan terpaksa pada semua cabang"
+
+#: builtin/fetch.c
+msgid "write the commit-graph after fetching"
+msgstr "tulis grafik komit setelah pengambilan"
+
+#: builtin/fetch.c
+msgid "accept refspecs from stdin"
+msgstr "terima spek referensi dari masukan standar"
+
+#: builtin/fetch.c
+msgid "--negotiate-only needs one or more --negotiation-tip=*"
+msgstr "--negotiate-only perlu satu atau lebih --negotiation-tip=*"
 
 #: builtin/fetch.c
 msgid "negative depth in --deepen is not supported"
@@ -7420,6 +7474,11 @@ msgstr "kedalaman negatif pada --deepen tidak didukung"
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "--unshallow pada repositori penuh tidak masuk akal"
 
+#: builtin/fetch.c
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "gagal mengambil bundel dari '%s'"
+
 #: builtin/fetch.c
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all tidak mengambil argumen repositori"
@@ -7551,9 +7610,17 @@ msgstr "hanya cetak referensi yang berisi komit"
 msgid "print only refs which don't contain the commit"
 msgstr "hanya cetak referensi yang tidak berisi komit"
 
+#: builtin/for-each-ref.c
+msgid "read reference patterns from stdin"
+msgstr "baca pola referensi dari masukan standar"
+
+#: builtin/for-each-ref.c
+msgid "unknown arguments supplied with --stdin"
+msgstr "argumen tidak dikenal diberikan dengan --stdin"
+
 #: builtin/for-each-repo.c
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=<konfigurasi> <argumen perintah>"
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<konfigurasi> [--] <argumen perintah>"
 
 #: builtin/for-each-repo.c
 msgid "config"
@@ -7567,6 +7634,11 @@ msgstr "kunci konfigurasi yang menampung daftar jalur repositori"
 msgid "missing --config=<config>"
 msgstr "kehilangan --config=<config>"
 
+#: builtin/for-each-repo.c
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "dapat konfigurasi jelek --config=%s"
+
 #: builtin/fsck.c
 msgid "unknown"
 msgstr "tidak dikenal"
@@ -7752,13 +7824,14 @@ msgid "notice: %s points to an unborn branch (%s)"
 msgstr "catatan: %s menunjuk ke cabang yang belum lahir (%s)"
 
 #: builtin/fsck.c
-msgid "Checking cache tree"
-msgstr "Memeriksa pohon tembolok"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Memeriksa pohon tembolok %s"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: penunjuk sha1 tidak valid pada pohon tembolok"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: penunjuk sha1 tidak valid pada pohon tembolok %s"
 
 #: builtin/fsck.c
 msgid "non-tree in cache-tree"
@@ -7766,12 +7839,30 @@ msgstr "bukan pohon pada pohon tembolok"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: penunjuk sha1 tidak valid di resolve-undo"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: penunjuk sha1 tidak valid di resolve-undo %s"
+
+#: builtin/fsck.c
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "tidak dapat memuat indeks balik untuk pak '%s'"
+
+#: builtin/fsck.c
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "indeks balik tidak valid untuk pak '%s'"
 
 #: builtin/fsck.c
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<opsi>] [<objek>...]"
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<objek>...]"
 
 #: builtin/fsck.c
 msgid "show unreachable objects"
@@ -7843,14 +7934,6 @@ msgstr "git fsmonitor--daemon start [<opsi>]"
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [<opsi>]"
 
-#: builtin/fsmonitor--daemon.c
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-#: builtin/fsmonitor--daemon.c
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
-
 #: builtin/fsmonitor--daemon.c
 #, c-format
 msgid "value of '%s' out of range: %d"
@@ -7952,7 +8035,7 @@ msgstr ""
 msgid "invalid 'ipc-threads' value (%d)"
 msgstr "nilai 'ipc-threads' tidak valid (%d)"
 
-#: builtin/fsmonitor--daemon.c
+#: builtin/fsmonitor--daemon.c t/helper/test-cache-tree.c
 #, c-format
 msgid "Unhandled subcommand '%s'"
 msgstr "Subperintah tidak tertangani '%s'"
@@ -7975,7 +8058,7 @@ msgstr "Gagal men-fstat %s: %s"
 msgid "failed to parse '%s' value '%s'"
 msgstr "gagal menguraikan nilai '%s' '%s'"
 
-#: builtin/gc.c builtin/init-db.c
+#: builtin/gc.c setup.c
 #, c-format
 msgid "cannot stat '%s'"
 msgstr "tidak dapat men-stat '%s'"
@@ -8003,6 +8086,10 @@ msgstr "pangkas objek tak tereferensi"
 msgid "pack unreferenced objects separately"
 msgstr "pak objek tak terujuk secara terpisah"
 
+#: builtin/gc.c builtin/repack.c
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "batasi ukuran pak sisa dengan --cruft"
+
 #: builtin/gc.c
 msgid "be more thorough (increased runtime)"
 msgstr "jadi lebih cermat (waktu yang dijalankan bertambah)"
@@ -8157,8 +8244,23 @@ msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr "gunakan paling banyak satu dari --auto dan --schedule=<frekuensi>"
 
 #: builtin/gc.c
-msgid "failed to run 'git config'"
-msgstr "gagal menjalankan 'git config'"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "tidak dapat menambahkan nilai '%s' dari '%s'"
+
+#: builtin/gc.c
+msgid "return success even if repository was not registered"
+msgstr "kembalikan sukses bahkan jika repositori tidak terdaftar"
+
+#: builtin/gc.c
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "tidak dapat membatal-setel nilai '%s' dari '%s'"
+
+#: builtin/gc.c
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "repositori '%s' tidak terdaftar"
 
 #: builtin/gc.c
 #, c-format
@@ -8209,14 +8311,6 @@ msgstr ""
 msgid "'crontab' died"
 msgstr "'crontab' mati"
 
-#: builtin/gc.c
-msgid "failed to start systemctl"
-msgstr "gagal memulai systemctl"
-
-#: builtin/gc.c
-msgid "failed to run systemctl"
-msgstr "gagal menjalankan systemctl"
-
 #: builtin/gc.c builtin/worktree.c
 #, c-format
 msgid "failed to delete '%s'"
@@ -8227,6 +8321,14 @@ msgstr "gagal menghapus '%s'"
 msgid "failed to flush '%s'"
 msgstr "gagal membilas '%s'"
 
+#: builtin/gc.c
+msgid "failed to start systemctl"
+msgstr "gagal memulai systemctl"
+
+#: builtin/gc.c
+msgid "failed to run systemctl"
+msgstr "gagal menjalankan systemctl"
+
 #: builtin/gc.c
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
@@ -8257,6 +8359,10 @@ msgstr "penjadwal"
 msgid "scheduler to trigger git maintenance run"
 msgstr "penjadwal untuk memicu git maintenance run"
 
+#: builtin/gc.c
+msgid "failed to set up maintenance schedule"
+msgstr "gagal mempersiapkan jadwal pemeliharaan"
+
 #: builtin/gc.c
 msgid "failed to add repo to global config"
 msgstr "gagal menambahkan repositori ke konfigurasi global"
@@ -8353,7 +8459,7 @@ msgid "search in subdirectories (default)"
 msgstr "cari dalam subdirektori (asali)"
 
 #: builtin/grep.c
-msgid "descend at most <depth> levels"
+msgid "descend at most <n> levels"
 msgstr "turun paling banyak <kedalaman> tingkat"
 
 #: builtin/grep.c
@@ -8537,11 +8643,15 @@ msgstr "baik --cached dan pohon diberikan"
 
 #: builtin/hash-object.c
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <tipe>] [-w] [--path=<berkas> | --no-filters] [--stdin] "
-"[--] <berkas>..."
+"git hash-object [-t <tipe>] [-w] [--path=<berkas> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <berkas>..."
+
+#: builtin/hash-object.c
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <tipe>] [-w] --stdin-paths [--no-filters]"
 
 #: builtin/hash-object.c
 msgid "object type"
@@ -8709,13 +8819,21 @@ msgid "'git help config' for more information"
 msgstr "'git help config' untuk informasi lebih lanjut"
 
 #: builtin/hook.c
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] <nama kait> [-- <argumen kait>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<jalur>] <nama kait> [-- "
+"<argumen kait>]"
 
 #: builtin/hook.c
 msgid "silently ignore missing requested <hook-name>"
 msgstr "diam-diam abaikan <nama kait> yang diminta yang hilang"
 
+#: builtin/hook.c
+msgid "file to read into hooks' stdin"
+msgstr "gagal membaca masukan standar kait"
+
 #: builtin/index-pack.c
 #, c-format
 msgid "object type mismatch at %s"
@@ -8972,7 +9090,7 @@ msgstr "tidak dapat kembali ke direktori kerja saat ini"
 msgid "bad %s"
 msgstr "%s jelek"
 
-#: builtin/index-pack.c builtin/init-db.c
+#: builtin/index-pack.c builtin/init-db.c setup.c
 #, c-format
 msgid "unknown hash algorithm '%s'"
 msgstr "algoritma hash tak dikenal '%s'"
@@ -8989,102 +9107,17 @@ msgstr "--verify tanpa nama berkas paket diberikan"
 msgid "fsck error in pack objects"
 msgstr "kesalahan fsck dalam objek paket"
 
-#: builtin/init-db.c
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "tidak dapat men-stat templat '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "tidak dapat membuka direktori '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "tidak dapat membaca tautan '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "tidak dapat menautkan simbolik '%s' '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "tidak dapat menyalin '%s' ke '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "ignoring template %s"
-msgstr "mengabaikan templat %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "templates not found in %s"
-msgstr "templat tidak ditemukan di %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "tidak menyalin templat dari '%s': %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "nama cabang asal salah: '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "tidak dapat menangani tipe berkas %d"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "tidak dapat memindahkan %s ke %s"
-
-#: builtin/init-db.c
-msgid "attempt to reinitialize repository with different hash"
-msgstr "mencoba menginisialisasi ulang repositori dengan hash yang berbeda"
-
-#: builtin/init-db.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s sudah ada"
-
-#: builtin/init-db.c
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: --initial-branch=%s diabaikan"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "Repositori berbagi Git yang sudah ada diinisialisasi ulang di %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Repositori Git diinisialisasi ulang di %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Repositori berbagi Git kosong diinisialisasi di %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Repositori Git kosong dinisialisasi di %s%s\n"
-
 #: builtin/init-db.c
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<direktori-templat>][--"
-"shared[=<perizinan>]] [<direktori>]"
+"git init [-q | --quiet] [--bare] [--template=<direktori templat>]\n"
+"         [--separate-git-dir <direktori git>] [--object-format=<format>]\n"
+"         [-b <nama cabang> | --initial-branch=<nama cabang>]\n"
+"         [--shared[=<perizinan>]] [<direktori>]"
 
 #: builtin/init-db.c
 msgid "permissions"
@@ -9137,11 +9170,14 @@ msgstr "--separate-git-dir tidak kompatibel dengan repositori bare"
 
 #: builtin/interpret-trailers.c
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<nilai>])...] [<berkas>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<kunci>|<alias kunci>)"
+"[(=|:)<nilai>])...]\n"
+"                       [--parse] [<berkas>...]"
 
 #: builtin/interpret-trailers.c
 msgid "edit files in place"
@@ -9151,6 +9187,10 @@ msgstr "sunting berkas di tempat"
 msgid "trim empty trailers"
 msgstr "pangkas trailer kosong"
 
+#: builtin/interpret-trailers.c
+msgid "placement"
+msgstr "penempatan"
+
 #: builtin/interpret-trailers.c
 msgid "where to place the new trailer"
 msgstr "dimana trailer baru ditempatkan"
@@ -9168,20 +9208,20 @@ msgid "output only the trailers"
 msgstr "keluarkan hanya trailer"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply config rules"
-msgstr "jangan terapkan aturan konfigurasi"
+msgid "do not apply trailer.* configuration variables"
+msgstr "jangan terapkan variabel konfigurasi trailer.*"
 
 #: builtin/interpret-trailers.c
-msgid "join whitespace-continued values"
-msgstr "gabungkan nilai yang dilanjutkan oleh spasi"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "format ulang nilai trailer multibaris sebagai nilai satu baris."
 
 #: builtin/interpret-trailers.c
-msgid "set parsing options"
-msgstr "setel opsi penguraian"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "alias untuk --only-trailers --only-input --unfold"
 
 #: builtin/interpret-trailers.c
-msgid "do not treat --- specially"
-msgstr "jangan memperlakukan khusus ---"
+msgid "do not treat \"---\" as the end of input"
+msgstr "jangan perlakukan \"---\" sebagai ujung masukan"
 
 #: builtin/interpret-trailers.c
 msgid "trailer(s) to add"
@@ -9295,6 +9335,11 @@ msgstr "butuh tepatnya satu rentang"
 msgid "not a range"
 msgstr "bukan sebuah rentang"
 
+#: builtin/log.c
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "tidak dapat membaca berkas deskripsi cabang '%s'"
+
 #: builtin/log.c
 msgid "cover letter needs email format"
 msgstr "sampul surat butuh format email"
@@ -9423,6 +9468,10 @@ msgstr "cover-from-description-mode"
 msgid "generate parts of a cover letter based on a branch's description"
 msgstr "buat bagian dari sampul surat berdasarkan deskripsi cabang"
 
+#: builtin/log.c
+msgid "use branch description from file"
+msgstr "gunakan deskripsi cabang dari berkas"
+
 #: builtin/log.c
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "gunakan [<prefix>] daripada [PATCH]"
@@ -9632,6 +9681,11 @@ msgstr ""
 "Tidak dapat menemukan cabang remote terlacak, mohon sebutkan <hulu>\n"
 "secara manual.\n"
 
+#: builtin/ls-files.c builtin/ls-tree.c
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "tidak dapat mendapatkan info objek tentang '%s'"
+
 #: builtin/ls-files.c
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
@@ -9771,12 +9825,12 @@ msgstr ""
 #: builtin/ls-remote.c
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repositori> [<referensi>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<kunci>]\n"
+"              [--symref] [<repositori> [<pola>...]]"
 
 #: builtin/ls-remote.c
 msgid "do not print remote URL"
@@ -9816,15 +9870,10 @@ msgid "show underlying ref in addition to the object pointed by it"
 msgstr "perlihatkan referensi pokok selain objek yang ditunjuk olehnya"
 
 #: builtin/ls-tree.c
-msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
-msgstr "git ls-tree [<opsi>] <mirip-pohon> [<jalur>...]"
-
-#: builtin/ls-tree.c
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "tidak dapat mendapatkan info objek tentang '%s'"
-
-#: builtin/ls-tree.c
+msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
+msgstr "git ls-tree [<opsi>] <mirip-pohon> [<jalur>...]"
+
+#: builtin/ls-tree.c
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "format ls-tree jelek: elemen '%s' tidak dimulai dengan '('"
@@ -9947,14 +9996,14 @@ msgstr "git merge-base [-a | --all] <komit> <komit>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <komit>..."
 
-#: builtin/merge-base.c
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <komit>..."
-
 #: builtin/merge-base.c
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <komit> <komit>"
 
+#: builtin/merge-base.c
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <komit>..."
+
 #: builtin/merge-base.c
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <referensi> [<komit>]"
@@ -9991,6 +10040,10 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "kirim hasil ke keluaran standar"
 
+#: builtin/merge-file.c
+msgid "use object IDs instead of filenames"
+msgstr "gunakan ID objek daripada nama berkas"
+
 #: builtin/merge-file.c
 msgid "use a diff3 based merge"
 msgstr "gunakan penggabungan berdasarkan diff3"
@@ -10023,6 +10076,15 @@ msgstr "jangan peringatkan tentang konflik"
 msgid "set labels for file1/orig-file/file2"
 msgstr "setel label untuk file1/orig-file/file2"
 
+#: builtin/merge-file.c
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "objek '%s' tidak ada"
+
+#: builtin/merge-file.c
+msgid "Could not write object file"
+msgstr "Tidak dapat menulis berkas objek"
+
 #: builtin/merge-recursive.c
 #, c-format
 msgid "unknown option %s"
@@ -10094,10 +10156,45 @@ msgstr "daftar nama berkas tanpa mode/oid/tahap"
 msgid "allow merging unrelated histories"
 msgstr "perbolehkan penggabungan riwayat yang tak terkait"
 
+#: builtin/merge-tree.c
+msgid "perform multiple merges, one per line of input"
+msgstr "lakukan banyak penggabungan, satu per baris masukan"
+
+#: builtin/merge-tree.c
+msgid "specify a merge-base for the merge"
+msgstr "harus menyebutkan sebuah dasar penggabungan untuk penggabungan"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option=value"
+msgstr "opsi=nilai"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option for selected merge strategy"
+msgstr "opsi untuk strategi penggabungan yang dipilih"
+
 #: builtin/merge-tree.c
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge tidak kompatibel dengan semua opsi lainnya"
 
+#: builtin/merge-tree.c builtin/merge.c
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "opsi strategi tidak dikenal: -X%s"
+
+#: builtin/merge-tree.c
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base tidak kompatibel dengan --stdin"
+
+#: builtin/merge-tree.c builtin/notes.c
+#, c-format
+msgid "malformed input line: '%s'."
+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>...]"
@@ -10176,14 +10273,6 @@ msgstr "strategi"
 msgid "merge strategy to use"
 msgstr "strategi penggabungan yang digunakan"
 
-#: builtin/merge.c builtin/pull.c
-msgid "option=value"
-msgstr "opsi=nilai"
-
-#: builtin/merge.c builtin/pull.c
-msgid "option for selected merge strategy"
-msgstr "opsi untuk strategi penggabungan yang dipilih"
-
 #: builtin/merge.c
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "pesan komit penggabungan (untuk penggabungan bukan maju cepat)"
@@ -10261,11 +10350,6 @@ msgstr "Tidak dapat menulis indeks."
 msgid "Not handling anything other than two heads merge."
 msgstr "Tak tangani apapun selain penggabungan dua kepala."
 
-#: builtin/merge.c
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "opsi strategi tidak dikenal: -X%s"
-
 #: builtin/merge.c t/helper/test-fast-rebase.c
 #, c-format
 msgid "unable to write %s"
@@ -10342,7 +10426,7 @@ msgstr "Tidak ada cabang pelacak remote untuk %s dari %s"
 msgid "Bad value '%s' in environment '%s'"
 msgstr "Nilai jelek '%s' dalam lingkungan '%s'"
 
-#: builtin/merge.c read-cache.c strbuf.c wrapper.c
+#: builtin/merge.c editor.c read-cache.c wrapper.c
 #, c-format
 msgid "could not close '%s'"
 msgstr "tidak dapat menutup '%s'"
@@ -10634,8 +10718,8 @@ msgid "can not move directory into itself"
 msgstr "tidak dapat memindahkan direktori ke dirinya sendiri"
 
 #: builtin/mv.c
-msgid "cannot move directory over file"
-msgstr "tidak dapat memindahkan direktori ke berkas"
+msgid "destination already exists"
+msgstr "tujuan sudah ada"
 
 #: builtin/mv.c
 msgid "source directory is empty"
@@ -10680,7 +10764,7 @@ msgstr "%s, source=%s, destination=%s"
 msgid "Renaming %s to %s\n"
 msgstr "Mengganti nama %s ke %s\n"
 
-#: builtin/mv.c builtin/remote.c builtin/repack.c
+#: builtin/mv.c builtin/remote.c
 #, c-format
 msgid "renaming '%s' failed"
 msgstr "gagal mengganti nama '%s'"
@@ -10739,11 +10823,13 @@ msgstr "git notes [--ref <referensi catan>] [list [<objek>]]"
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <referensi catatan>] add [-f] [--allow-empty] [-m <pesan | -"
-"F <berkas> | (-c | -C) <objek>] [<objek>]"
+"git notes [--ref <referensi catatan>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<pemisah paragraf>] [--[no-]stripspace] [-m "
+"<pesan | -F <berkas> | (-c | -C) <objek>] [<objek>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
@@ -10752,11 +10838,13 @@ msgstr ""
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <referensi catatan>] append [--alow-empty] [-m <pesan> | -F "
-"<berkas> | (-c | -C) <objek>] [<objek>]"
+"git notes [--ref <referensi catatan>] append [--alow-empty] [--"
+"[no]separator|--separator=<pemisah paragraf>] [--[no-]stripspace] [-m "
+"<pesan> | -F <berkas> | (-c | -C) <objek>] [<objek>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
@@ -10884,11 +10972,6 @@ msgstr "gagal membaca objek '%s'."
 msgid "cannot read note data from non-blob object '%s'."
 msgstr "gagal membaca data catatan dari objek bukan blob '%s'."
 
-#: builtin/notes.c
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "baris masukan jelek: '%s'."
-
 #: builtin/notes.c
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
@@ -10931,6 +11014,18 @@ msgstr "perbolehkan menyimpan catatan kosong"
 msgid "replace existing notes"
 msgstr "timpa catatan yang sudah ada"
 
+#: builtin/notes.c
+msgid "<paragraph-break>"
+msgstr "<pemisah paragraf>"
+
+#: builtin/notes.c
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "masukkan <pemisah paragraf> di antara paragraf"
+
+#: builtin/notes.c
+msgid "remove unnecessary whitespace"
+msgstr "hapus spasi yang tidak diperlukan"
+
 #: builtin/notes.c
 #, c-format
 msgid ""
@@ -11130,15 +11225,14 @@ msgid "unknown subcommand: `%s'"
 msgstr "subperintah tidak dikenal: `%s'"
 
 #: builtin/pack-objects.c
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects --stdout [<opsi>...] [< <daftar referensi>| < <daftar-"
+"git pack-objects --stdout [<opsi>...] [< <daftar referensi> | < <daftar-"
 "objek>]"
 
 #: builtin/pack-objects.c
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
 "git pack-objects [<opsi>...] <nama dasar> [< <daftar referensi> | < <daftar-"
 "objek>]"
@@ -11204,7 +11298,7 @@ msgstr "gagal menulis indeks bitmap"
 msgid "wrote %<PRIu32> objects while expecting %<PRIu32>"
 msgstr "%<PRIu32> objek ditulish ketika mengharapkan %<PRIu32>"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/repack.c
 msgid "disabling bitmap writing, as some objects are not being packed"
 msgstr "menonaktifkan penulisan bitmap, saat beberapa objek tidak sedang dipak"
 
@@ -11372,6 +11466,14 @@ msgstr "versi indeks tidak didukung %s"
 msgid "bad index version '%s'"
 msgstr "versi indeks jelek '%s'"
 
+#: builtin/pack-objects.c
+msgid "show progress meter during object writing phase"
+msgstr "perlihatkan meteran perkembangan saat fase penulisan objek"
+
+#: builtin/pack-objects.c
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "sama seperti --all-progress ketika meteran perkembangan diperlihatkan"
+
 #: builtin/pack-objects.c
 msgid "<version>[,<offset>]"
 msgstr "<versi>[,<offset>]"
@@ -11568,10 +11670,6 @@ msgid "--thin cannot be used to build an indexable pack"
 msgstr ""
 "--thin tidak dapat digunakan untuk membangun sebuah pak yang dapat diindeks"
 
-#: builtin/pack-objects.c
-msgid "cannot use --filter without --stdout"
-msgstr "tidak dapat menggunakan --filter tanpa --stdout"
-
 #: builtin/pack-objects.c
 msgid "cannot use --filter with --stdin-packs"
 msgstr "tidak dapat menggunakan --filter dengan --stdin-packs"
@@ -11588,10 +11686,6 @@ msgstr "tidak dapat menggunakan daftar revisi internal dengan --cruft"
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "tidak dapat menggunakan --stdin-packs dengan --cruft"
 
-#: builtin/pack-objects.c
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "tidak dapat menggunakan --max-pack-size dengan --cruft"
-
 #: builtin/pack-objects.c
 msgid "Enumerating objects"
 msgstr "Menghitung objek"
@@ -11619,9 +11713,16 @@ msgstr ""
 "Anda masih menggunakannya dengan mengirimkan surel ke\n"
 "<git@vger.kernel.org>. Terima kasih.\n"
 
+#: builtin/pack-redundant.c
+msgid "refusing to run without --i-still-use-this"
+msgstr "menolak menjalankan tanpa --i-still-use-this"
+
 #: builtin/pack-refs.c
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<opsi>]"
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <pola>] [--exclude <pola>]"
 
 #: builtin/pack-refs.c
 msgid "pack everything"
@@ -11631,6 +11732,30 @@ msgstr "pak semuanya"
 msgid "prune loose refs (default)"
 msgstr "pangkas referensi longgar (asali)"
 
+#: builtin/pack-refs.c
+msgid "references to include"
+msgstr "referensi untuk ditambahkan"
+
+#: builtin/pack-refs.c
+msgid "references to exclude"
+msgstr "referensi untuk dikecualikan"
+
+#: builtin/patch-id.c
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+#: builtin/patch-id.c
+msgid "use the unstable patch-id algorithm"
+msgstr "gunakan algoritma id tambalan tidak stabil"
+
+#: builtin/patch-id.c
+msgid "use the stable patch-id algorithm"
+msgstr "gunakan algoritma id tambalan stabil"
+
+#: builtin/patch-id.c
+msgid "don't strip whitespace from the patch"
+msgstr "jangan kupas spasi dari tambalan"
+
 #: builtin/prune.c
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr "git prune [-n] [-v] [--progress] [--expire <waktu>] [--] [<kepala>...]"
@@ -11691,6 +11816,14 @@ msgstr "paksa timpa cabang lokal"
 msgid "number of submodules pulled in parallel"
 msgstr "nomor submodul ditarik dalam paralel"
 
+#: builtin/pull.c parse-options.h
+msgid "use IPv4 addresses only"
+msgstr "gunakan hanya alamat IPv4"
+
+#: builtin/pull.c parse-options.h
+msgid "use IPv6 addresses only"
+msgstr "gunakan hanya alamat IPv6"
+
 #: builtin/pull.c
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
@@ -11819,9 +11952,9 @@ msgstr ""
 msgid "pull with rebase"
 msgstr "tarik dengan pendasaran ulang"
 
-#: builtin/pull.c
-msgid "please commit or stash them."
-msgstr "mohon komit atau stase."
+#: builtin/pull.c builtin/rebase.c
+msgid "Please commit or stash them."
+msgstr "Mohon komit atau stase."
 
 #: builtin/pull.c
 #, c-format
@@ -11895,14 +12028,13 @@ msgstr ""
 #: builtin/push.c
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
 "Untuk menghindari konfigurasi cabang hulu otomatis ketika namanya\n"
-"tidak cocok dengan cabang lokal, lihat opsi 'simple' dari branch."
+"tidak akan cocok dengan cabang lokal, lihat opsi 'simple' dari branch."
 "autoSetupMerge\n"
 "di 'git help config'.\n"
 
@@ -11997,39 +12129,39 @@ msgstr ""
 #: builtin/push.c
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Pembaruan ditolak karena ujung dari cabang Anda saat ini di belakang\n"
-"pasangan remotenya. Integrasikan perubahan remote (seperti\n"
-"'git pull') sebelum dorong lagi.\n"
+"pasangan remotenya. Jika Anda ingin mengintegrasikan perubahan remote,\n"
+"lakukan 'git pull' sebelum mendorong lagi.\n"
 "Lihat 'Note about fast-forwards' di 'git push --help' untuk selengkapnya."
 
 #: builtin/push.c
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Pembaruan ditolak karena ujung dari cabang yang didorong di belakang\n"
-"pasangan remotenya. Check out cabang itu dan integrasikan perubahan\n"
-"remote (seperti 'git pull') sebelum dorong lagi.\n"
+"pasangan remotenya. Jika anda ingin mengintegrasikan perubahan\n"
+"remote, lakukan 'git pull' sebelum mendorong lagi.\n"
 "Lihat 'Note about fast-forwards' di 'git push --help' untuk selengkapnya."
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Pembaruan ditolak karena remote berisi kerja yang Anda tidak punya\n"
+"Pembaruan ditolak karena remote berisi karya yang Anda tidak punya\n"
 "di lokal. Ini biasanya disebabkan repositori yang lain dorong ke\n"
-"referensi yang sama. Mungkin Anda ingin integrasikan terlebih dahulu\n"
-"perubahan remote (seperti 'git pull') sebelum dorong lagi.\n"
+"referensi yang sama. Jika Anda ingin mengintegrasikan perubahan remote\n"
+"lakukan 'git pull' sebelum mendorong lagi.\n"
 "Lihat 'Note about fast-forwards' di 'git push --help' untuk selengkapnya."
 
 #: builtin/push.c
@@ -12048,15 +12180,15 @@ msgstr ""
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Pembaruan ditolak karena ujung dari cabang pelacak remote\n"
-"sudah diperbarui sejak checkout terakhir. Mungkin Anda ingin\n"
-"integrasikan perubahan tersebut ke lokal (seperti 'git pull...')\n"
-"sebelum memaksa pembaruan.\n"
+"Pembaruan ditolak karena ujung dari cabang Anda saat ini sudah diperbarui\n"
+"sejak checkout terakhir. Jika Anda ingin mengintegrasikan perubahan remote,\n"
+"lakukan 'git pull' sebelum mendorong lagi.\n"
+"Lihat 'Note about fast-forwards' di 'git push --help' untuk selengkapnya."
 
 #: builtin/push.c
 #, c-format
@@ -12068,6 +12200,14 @@ msgstr "Mendorong ke %s\n"
 msgid "failed to push some refs to '%s'"
 msgstr "gagal dorong beberapa referensi ke '%s'"
 
+#: builtin/push.c
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"mengulangi ke dalam submodul dengan push.recurseSubmodules=only; menggunakan "
+"on-demand sebagai gantinya"
+
 #: builtin/push.c builtin/send-pack.c submodule-config.c
 #, c-format
 msgid "invalid value for '%s'"
@@ -12077,9 +12217,9 @@ msgstr "nilai tidak valid untuk '%s'"
 msgid "repository"
 msgstr "repositori"
 
-#: builtin/push.c builtin/send-pack.c
-msgid "push all refs"
-msgstr "dorong semua referensi"
+#: builtin/push.c
+msgid "push all branches"
+msgstr "dorong semua cabang"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "mirror all refs"
@@ -12090,8 +12230,10 @@ msgid "delete refs"
 msgstr "hapus referensi"
 
 #: builtin/push.c
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "dorong tag (tidak dapat digunakan dengan --all atau --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"dorong tag (tidak dapat digunakan bersamaan dengan --all, --branches, atau --"
+"mirror)"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "force updates"
@@ -12149,7 +12291,7 @@ msgstr "minta transaksi atomik pada sisi remote"
 msgid "--delete doesn't make sense without any refs"
 msgstr "--delete tidak masuk akal tanpa referensi"
 
-#: builtin/push.c
+#: builtin/push.c t/helper/test-bundle-uri.c
 #, c-format
 msgid "bad repository '%s'"
 msgstr "repositori jelek '%s'"
@@ -12242,14 +12384,15 @@ msgstr "butuh dua rentang komit"
 
 #: builtin/read-tree.c
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
 "git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
-"prefix=<prefiks>) [-u | -i]] [--no-sparse-checkout] [--index-"
-"output=<berkas>] (--empty | <mirip-pohon 1> [<mirip-pohon 2> <mirip-pohon "
-"3>])"
+"prefix=<awalan>)\n"
+"              [-u | -i]] [--index-output=<berkas>] [--no-sparse-checkout]\n"
+"              (--empty | <mirip pohon 1> [<mirip pohon 2> [mirip pohon 3]])"
 
 #: builtin/read-tree.c
 msgid "write resulting index to <file>"
@@ -12368,8 +12511,8 @@ msgstr "%s butuh tulang belakang penggabungan"
 
 #: builtin/rebase.c
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "tidak dapat mendapatkan 'ke': '%s'"
+msgid "invalid onto: '%s'"
+msgstr "kepada tidak valid: '%s'"
 
 #: builtin/rebase.c
 #, c-format
@@ -12420,20 +12563,39 @@ msgstr ""
 "\n"
 "Hasilnya git tidak dapat mendasarkan ulang."
 
+#: builtin/rebase.c
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Mode rebase-merges tidak dikenal: %s"
+
 #: builtin/rebase.c
 #, c-format
 msgid "could not switch to %s"
 msgstr "tidak dapat mengganti ke %s"
 
+#: builtin/rebase.c
+msgid "apply options and merge options cannot be used together"
+msgstr "opsi apply dan opsi merge tidak dapat digunakan bersamaan"
+
 #: builtin/rebase.c
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
 msgstr ""
 "tipe kosong tak dikenali '%s'; nilai yang valid adalah \"drop\", \"keep\", "
 "dan \"ask\"."
 
+#: builtin/rebase.c
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"--rebase-merges dengan argumen untai kosong usang dan akan berhenti bekerja "
+"pada versi Git berikutnya. Sebagai gantinya, gunakan --rebase-merges tanpa "
+"argumen, yang melakukan hal yang sama."
+
 #: builtin/rebase.c
 #, c-format
 msgid ""
@@ -12701,17 +12863,28 @@ msgid "switch `C' expects a numerical value"
 msgstr "tombol `C' harap nilai numerik"
 
 #: builtin/rebase.c
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Mode tidak dikenal: %s"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+"opsi penerapan tidak kompatibel dengan rebase.autoSquash. "
+"Pertimbangkanmenambahkan --no-autosquash"
 
 #: builtin/rebase.c
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy butuh --merge atau --interactive"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+"opsi penerapan tidak kompatibel dengan rebase.rebaseMerges. "
+"Pertimbangkanmenambahkan --no-rebase-merges"
 
 #: builtin/rebase.c
-msgid "apply options and merge options cannot be used together"
-msgstr "opsi apply dan opsi merge tidak dapat digunakan bersamaan"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr ""
+"opsi penerapan tidak kompatibel dengan rebase.updateRefs. "
+"Pertimbangkanmenambahkan --no-update-refs"
 
 #: builtin/rebase.c
 #, c-format
@@ -12742,8 +12915,8 @@ msgid "No such ref: %s"
 msgstr "Tidak ada referensi seperti: %s"
 
 #: builtin/rebase.c
-msgid "Could not resolve HEAD to a revision"
-msgstr "Tidak dapat menguraikan HEAD ke sebuah revisi"
+msgid "Could not resolve HEAD to a commit"
+msgstr "tidak dapat menguraikan komit HEAD ke sebuah komit"
 
 #: builtin/rebase.c
 #, c-format
@@ -12760,10 +12933,6 @@ msgstr "'%s': butuh tepatnya satu dasar penggabungan"
 msgid "Does not point to a valid commit '%s'"
 msgstr "Tidak menunjuk pada komit yang valid '%s'"
 
-#: builtin/rebase.c
-msgid "Please commit or stash them."
-msgstr "Mohon komit atau stase."
-
 #: builtin/rebase.c
 msgid "HEAD is up to date."
 msgstr "HEAD terbaru."
@@ -13075,12 +13244,12 @@ msgid "fetch the remote branches"
 msgstr "ambil cabang remote"
 
 #: builtin/remote.c
-msgid "import all tags and associated objects when fetching"
-msgstr "impor semua tag dan objek yang terkait ketika mengambil"
-
-#: builtin/remote.c
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "atau jangan mengambil tag apapun (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"impor semua tag dan objek yang terkait ketika mengambil atau jangan\n"
+"mengambil tag apapun (--no-tags)"
 
 #: builtin/remote.c
 msgid "branch(es) to track"
@@ -13557,6 +13726,16 @@ msgstr "tidak dapat membuka berkas sementara '%s' untuk ditulis"
 msgid "could not close refs snapshot tempfile"
 msgstr "tidak dapat menutup berkas sementara jepretan referensi"
 
+#: builtin/repack.c
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "tidak dapt memindahkan bitmap basi: %s"
+
+#: builtin/repack.c
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "nama berkas paket %s tidak diawali dengan %s"
+
 #: builtin/repack.c
 msgid "pack everything in a single pack"
 msgstr "pak semuanya dalam satu pak"
@@ -13574,8 +13753,8 @@ msgid "approxidate"
 msgstr "tanggal aproksimasi"
 
 #: builtin/repack.c
-msgid "with -C, expire objects older than this"
-msgstr "dengan -C, jangan kadaluarsakan objek lebih lama dari ini"
+msgid "with --cruft, expire objects older than this"
+msgstr "dengan --cruft, kadaluarsakan objek yang lebih tua dari ini"
 
 #: builtin/repack.c
 msgid "remove redundant packs, and run git-prune-packed"
@@ -13655,25 +13834,38 @@ msgstr "temukan deret geometri dengan faktor <N>"
 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"
+
 #: builtin/repack.c
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "tidak dapat menghapus pak dalam repositori objek berharga"
 
+#: builtin/repack.c
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "opsi '%s' tidak dapat digunakan bersamaan dengan '%s'"
+
 #: builtin/repack.c
 msgid "Nothing new to pack."
 msgstr "Tidak ada yang baru untuk dipak."
 
 #: builtin/repack.c
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "nama berkas paket %s tidak diawali dengan %s"
+msgid "renaming pack to '%s' failed"
+msgstr "gagal mengganti nama pak ke '%s'"
 
 #: builtin/repack.c
 #, c-format
-msgid "missing required file: %s"
-msgstr "berkas yang diperlukan hilang: %s"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects tidak menulis berkas '%s' untuk pak %s-%s"
 
-#: builtin/repack.c
+#: builtin/repack.c sequencer.c
 #, c-format
 msgid "could not unlink: %s"
 msgstr "tidak dapat membatal taut: %s"
@@ -13917,8 +14109,10 @@ msgid "only one pattern can be given with -l"
 msgstr "hanya satu pola yang dapat diberikan dengan -l"
 
 #: builtin/rerere.c
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forge <jalur>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <spek jalur>... | diff | status | remaining | gc]"
 
 #: builtin/rerere.c
 msgid "register clean resolutions in index"
@@ -14176,30 +14370,54 @@ msgstr "--prefix butuh sebuah argumen"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "mode untuk --abbrev-ref tidak dikenal: %s"
 
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden tidak dapat digunakan bersamaan dengan --branches"
+
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden tidak dapat digunakan bersamaan dengan --tags"
+
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden tidak dapat digunakan dengan --remotes"
+
 #: builtin/rev-parse.c setup.c
 msgid "this operation must be run in a work tree"
 msgstr "operasi ini harus dijalankan di dalam pohon kerja"
 
+#: builtin/rev-parse.c
+msgid "Could not read the index"
+msgstr "Tidak dapat membaca indeks"
+
 #: builtin/rev-parse.c
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "mode untuk --show-object-format tidak dikenal: %s"
 
 #: builtin/revert.c
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<opsi>] <mirip-komit>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <nomor induk>] [-s] [-S[<id kunci>]] "
+"<komit>..."
 
 #: builtin/revert.c
-msgid "git revert <subcommand>"
-msgstr "git revert <subperintah>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
 
 #: builtin/revert.c
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<opsi>] <mirip-komit>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <nomor induk>] [-s] [-x] [--ff]\n"
+"                [-S[<id kunci>]] <komit>..."
 
 #: builtin/revert.c
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <subperintah>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
 
 #: builtin/revert.c
 #, c-format
@@ -14280,8 +14498,14 @@ msgid "cherry-pick failed"
 msgstr "pemetikan ceri gagal"
 
 #: builtin/rm.c
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<opsi>] [--] <berkas>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<berkas> [--pathspec-file-nul]]\n"
+"       [--] [<spek jalur>...]"
 
 #: builtin/rm.c
 msgid ""
@@ -14369,17 +14593,23 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>\n"
-"]              [--verbose] [--thin] [--atomic]\n"
+"              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<tuan rumah>:]<direktori> (--all | <referensi>...)"
 
 #: builtin/send-pack.c
 msgid "remote name"
 msgstr "nama remote"
 
+#: builtin/send-pack.c
+msgid "push all refs"
+msgstr "dorong semua referensi"
+
 #: builtin/send-pack.c
 msgid "use stateless RPC protocol"
 msgstr "gunakan protokol RPC nirkeadaan"
@@ -14405,8 +14635,9 @@ msgid "using multiple --group options with stdin is not supported"
 msgstr "menggunakan banyak opsi --group dengan masukan standar tidak didukung"
 
 #: builtin/shortlog.c
-msgid "using --group=trailer with stdin is not supported"
-msgstr "mengguanakn --group=trailer dengan stdin tidak didukung"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "menggunakan %s dengan masukan standar tidak didukung"
 
 #: builtin/shortlog.c
 #, c-format
@@ -14454,12 +14685,14 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<kapan>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<revisi> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<revisi> | <glob>)...]"
 
 #: builtin/show-branch.c
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
@@ -14589,16 +14822,40 @@ msgstr "algoritma hash tidak dikenal"
 
 #: builtin/show-ref.c
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pola>...]"
+
+#: builtin/show-ref.c
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pola>...]"
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pola>...]"
 
 #: builtin/show-ref.c
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<pola>]"
 
+#: builtin/show-ref.c
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <referensi>"
+
+#: builtin/show-ref.c
+msgid "reference does not exist"
+msgstr "referensi tidak ada"
+
+#: builtin/show-ref.c
+msgid "failed to look up reference"
+msgstr "gagal mencari referensi"
+
 #: builtin/show-ref.c
 msgid "only show tags (can be combined with heads)"
 msgstr "hanya perlihatkan tag (bisa dikombinasikan dengan kepala)"
@@ -14607,6 +14864,10 @@ msgstr "hanya perlihatkan tag (bisa dikombinasikan dengan kepala)"
 msgid "only show heads (can be combined with tags)"
 msgstr "hanya perlihatkan kepala (bisa dikombinasikan dengan tag)"
 
+#: builtin/show-ref.c
+msgid "check for reference existence without resolving"
+msgstr "periksa adanya referensi tanpa penguraian"
+
 #: builtin/show-ref.c
 msgid "stricter reference checking, requires exact ref path"
 msgstr "pemeriksaan referensi lebih ketat, butuh jalur referensi eksak"
@@ -14633,9 +14894,18 @@ msgstr ""
 "perlihatkan referensi dari masukan standar yang tidak ada dalam repositori "
 "lokal"
 
+#: builtin/show-ref.c
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "hanya salah satu dari '%s', '%s', dan '%s' dapat diberikan"
+
 #: builtin/sparse-checkout.c
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) <opsi>"
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<opsi>]"
 
 #: builtin/sparse-checkout.c
 msgid "this worktree is not sparse"
@@ -14786,77 +15056,88 @@ msgstr ""
 msgid "error while refreshing working directory"
 msgstr "kesalahan saat menyegarkan direktori kerja"
 
-#: builtin/stash.c
-msgid "git stash list [<options>]"
-msgstr "git stash list [<opsi>]"
+#: builtin/sparse-checkout.c
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no]-cone] [--rules-"
+"file <file>]"
 
-#: builtin/stash.c
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<opsi>] [<stase>]"
+#: builtin/sparse-checkout.c
+msgid "terminate input and output files by a NUL character"
+msgstr "akhiri berkas masukan dan keluaran oleh satu karakter NUL"
 
-#: builtin/stash.c
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<stase>]"
+#: builtin/sparse-checkout.c
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"ketika digunakan dengan --rules-file tafsirkan pola sebagai pola mode kerucut"
 
-#: builtin/stash.c
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<stase>]"
+#: builtin/sparse-checkout.c
+msgid "use patterns in <file> instead of the current ones."
+msgstr "gunakan pola di dalam <berkas> daripada yang saat ini."
 
 #: builtin/stash.c
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <nama cabang> [<stase>]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<opsi log>]"
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index]\n"
-"          [-q|--quiet] [-u|--include-untracked] [-a|--all]\n"
-"          [-m|--message <pesan>] [--pathspec-from-file=<berkas>\n"
-"          [--pathspec-file-nul]] [--] [<spek jalur>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<opsi diff>] "
+"[<stase>]"
 
 #: builtin/stash.c
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index]\n"
-"          [-q|--quiet] [-u|--include-untracked] [-a|--all] [<pesan>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<stase>]"
+
+#: builtin/stash.c
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<stase>]"
 
 #: builtin/stash.c
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<stase>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<stase>]"
 
 #: builtin/stash.c
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<stase>]"
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <nama cabang> [<stase>]"
 
 #: builtin/stash.c
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <pesan>] [-q|--quiet] <komit>"
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr "git stash store [(-m | --message) <pesan>] [-q|--quiet] <komit>"
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <pesan>]\n"
-"          [--] [<spek jalur>...]"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<pesan>]\n"
+"          [--pathspec-from-file=<berkas> [--pathspec-file-nul]]\n"
+"          [--] [<spek jalur>...]]"
 
 #: builtin/stash.c
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<pesan>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<pesan>]"
+
+#: builtin/stash.c
+msgid "git stash create [<message>]"
+msgstr "git stash create [<pesan>]"
 
 #: builtin/stash.c
 #, c-format
@@ -15444,6 +15725,11 @@ msgstr "Melewati submodul tak tergabung %s"
 msgid "Skipping submodule '%s'"
 msgstr "Melewati submodul '%s'"
 
+#: builtin/submodule--helper.c
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "tidak dapat mengkloning submodul '%s' tanpa URL"
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
@@ -15564,10 +15850,6 @@ msgstr "lintasi submodul secara rekursif"
 msgid "don't fetch new objects from the remote site"
 msgstr "jangan ambil objek baru dari situs remote"
 
-#: builtin/submodule--helper.c
-msgid "path into the working tree"
-msgstr "jalur ke dalam pohon kerja"
-
 #: builtin/submodule--helper.c
 msgid "use the 'checkout' update strategy (default)"
 msgstr "gunakan strategi pembaruan 'checkout' (asali)"
@@ -15613,34 +15895,14 @@ msgstr ""
 "shallow] [--reference <repositori>] [--recursive] [--[no-]single-branch] "
 "[--] [<jalur>...]"
 
-#: builtin/submodule--helper.c
-msgid "recurse into submodules"
-msgstr "rekursi ke dalam submodul"
+#: builtin/submodule--helper.c submodule.c
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Gagal menguraikan HEAD sebagai referensi valid."
 
 #: builtin/submodule--helper.c
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<opsi>] [<jalur>...]"
 
-#: builtin/submodule--helper.c
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr "periksa apakah itu aman untuk menulis ke berkas .gitmodules"
-
-#: builtin/submodule--helper.c
-msgid "unset the config in the .gitmodules file"
-msgstr "batal setel konfigurasi dalam berkas .gitmodules"
-
-#: builtin/submodule--helper.c
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <nama> [<nilai>]"
-
-#: builtin/submodule--helper.c
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <nama>"
-
-#: builtin/submodule--helper.c
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr "mohom pastikan berkas .gitmodules di dalam pohon kerja"
-
 #: builtin/submodule--helper.c
 msgid "suppress output for setting url of a submodule"
 msgstr "sembunyikan keluaran penyetelan url submodule"
@@ -15736,6 +15998,10 @@ msgstr "Mengaktifkan ulang direktori git lokal untuk submodul '%s'\n"
 msgid "unable to checkout submodule '%s'"
 msgstr "Tidak dapat men-checkout submodul '%s'"
 
+#: builtin/submodule--helper.c
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr "mohom pastikan berkas .gitmodules di dalam pohon kerja"
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "Failed to add submodule '%s'"
@@ -15798,23 +16064,21 @@ msgstr "URL repo: '%s' harus absolut atau diawali dengan ./|../"
 msgid "'%s' is not a valid submodule name"
 msgstr "'%s' bukan nama submodul yang valid"
 
-#: builtin/submodule--helper.c git.c
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s tidak mendukung --super-prefix"
-
 #: builtin/submodule--helper.c
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "'%s' bukan subperintah submodule--helper valid"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <nama>"
+
+#: builtin/symbolic-ref.c
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <alasan>] <nama> <referensi>"
 
 #: builtin/symbolic-ref.c
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<opsi>] <nama> [<referensi>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <nama>"
 
 #: builtin/symbolic-ref.c
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <nama>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <nama>"
 
 #: builtin/symbolic-ref.c
 msgid "suppress error message for non-symbolic (detached) refs"
@@ -15828,6 +16092,10 @@ msgstr "hapus referensi simbolik"
 msgid "shorten ref output"
 msgstr "pendekkan keluaran referensi"
 
+#: builtin/symbolic-ref.c
+msgid "recursively dereference (default)"
+msgstr "derefensi secara rekursif (asali)"
+
 #: builtin/symbolic-ref.c builtin/update-ref.c
 msgid "reason"
 msgstr "alasan"
@@ -15838,11 +16106,11 @@ msgstr "alasan pembaruan"
 
 #: builtin/tag.c
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u <id kunci>] [-f] [-m <pesan | -F <berkas>]\n"
-"        <nama tag> [<kepala>]"
+"git tag [-a | -s | -u <id kunci>] [-f] [-m <pesan> | -F <berkas>] [-e]\n"
+"        <nama tag> [<komit> | <objek>]"
 
 #: builtin/tag.c
 msgid "git tag -d <tagname>..."
@@ -15850,15 +16118,15 @@ msgstr "git tag -d <nama tag>..."
 
 #: builtin/tag.c
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[<angka>]] [--contains <komit>] [--no-contains <komit>] [--"
-"points-at <objek>]\n"
-"        [--format=<format>] [--merged <komit>] [--no-merged <komit>] "
-"[<pola>...]"
+"git tag [-n[<angka>]] -l [--contains <komit>] [--no-contains <komit>]\n"
+"        [--points-at <objeck>] [--column[=<opsi>] | --no-column]\n"
+"        [--create-reflog] [--sort=<kunci>] [--format=<format>]\n"
+"        [--merged <komit>] [--no-merged <komit>] [<pola>...]"
 
 #: builtin/tag.c
 msgid "git tag -v [--format=<format>] <tagname>..."
@@ -16212,6 +16480,10 @@ msgstr "(untuk porselen) lupakan konflik tak terselesaikan yang disimpan"
 msgid "write index in this format"
 msgstr "tulis indeks dalam format ini"
 
+#: builtin/update-index.c
+msgid "report on-disk index format version"
+msgstr "laporkan versi format indeks pada-disk"
+
 #: builtin/update-index.c
 msgid "enable or disable split index"
 msgstr "aktifkan atau nonaktifkan indeks terpisah"
@@ -16244,6 +16516,16 @@ msgstr "tandai berkas sebagai fsmonitor valid"
 msgid "clear fsmonitor valid bit"
 msgstr "bersihkan bita fsmonitor valid"
 
+#: builtin/update-index.c
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#: builtin/update-index.c
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: sebelumnya %d, disetel ke %d"
+
 #: builtin/update-index.c
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
@@ -16340,8 +16622,12 @@ msgid "update the info files from scratch"
 msgstr "perbarui berkas info dari awal"
 
 #: builtin/upload-pack.c
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<opsi>] <direktori>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <direktori>"
 
 #: builtin/upload-pack.c t/helper/test-serve-v2.c
 msgid "quit after a single request/response exchange"
@@ -16360,8 +16646,8 @@ msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "interupsi transfer setelah <n> detik niraktivitas"
 
 #: builtin/verify-commit.c
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <komit>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | --verbose] [--raw] <komit>..."
 
 #: builtin/verify-commit.c
 msgid "print commit contents"
@@ -16372,8 +16658,8 @@ msgid "print raw gpg status output"
 msgstr "cetak keluaran status gpg mentah"
 
 #: builtin/verify-pack.c
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <pak>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pak>.idx..."
 
 #: builtin/verify-pack.c
 msgid "verbose"
@@ -16384,44 +16670,80 @@ msgid "show statistics only"
 msgstr "hanya perlihatkan statistik"
 
 #: builtin/verify-tag.c
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
 
 #: builtin/verify-tag.c
 msgid "print tag contents"
 msgstr "cetak isi tag"
 
 #: builtin/worktree.c
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<opsi>] <jalur> [<mirip komit>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <untai>]]\n"
+"                 [--orphan] [(-b | -B) <cabang baru>] <jalur> [<mirip komit>]"
 
 #: builtin/worktree.c
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<opsi>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]"
 
 #: builtin/worktree.c
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<opsi>] <jalur>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <untai>] <pohon kerja>"
 
 #: builtin/worktree.c
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <pohon kerja> <jalur baru>"
 
 #: builtin/worktree.c
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<opsi>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <kadaluarsa>]"
 
 #: builtin/worktree.c
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<opsi>] <pohon kerja>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <pohon kerja>"
 
 #: builtin/worktree.c
 msgid "git worktree repair [<path>...]"
 msgstr "git worktree repair [<jalur>...]"
 
 #: builtin/worktree.c
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <jalur>"
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <worktree>"
+
+#: builtin/worktree.c
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Tidak ada cabang sumber yang mungkin, menyimpulkan '--orphan'"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Jika maksud Anda ingin membuat pohon kerja berisi sebuah cabang yatim baru\n"
+"(cabang tanpa komit) untuk repositori ini, Anda dapat melakukannya dengan\n"
+"opsi --orphan:\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Jika maksud Anda ingin membuat pohon kerja berisi sebuah cabang yatim baru\n"
+"(cabang tanpa komit) untuk repositori ini, Anda dapat melakukannya dengan\n"
+"opsi --orphan:\n"
+"    git worktree add --orphan %s\n"
 
 #: builtin/worktree.c
 #, c-format
@@ -16506,11 +16828,41 @@ msgstr ""
 msgid "Preparing worktree (checking out '%s')"
 msgstr "Menyiapkan pohon kerja (men-checkout '%s')"
 
+#: builtin/worktree.c
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "tidak dapat dicapat: referensi tidak valid: %s"
+
 #: builtin/worktree.c
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Menyiapkan pohon kerja (HEAD terpisah %s)"
 
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD menunjuk pada referensi tidak valid (atau yatim).\n"
+"Jalur HEAD: '%s'\n"
+"Isi HEAD: '%s'"
+
+#: builtin/worktree.c
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"Tidak ada referensi lokal atau remote yang ada meskipun salah satu remote\n"
+"ada, berhenti; gunakan 'add -f' untuk menimpa atau mengambil remote\n"
+"terlebih dahulu"
+
+#: builtin/worktree.c
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "'%s' dan '%s' tidak dapat digunakan bersamaan"
+
 #: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
@@ -16524,6 +16876,10 @@ msgstr "buat sebuah cabang baru"
 msgid "create or reset a branch"
 msgstr "buat atau setel ulang sebuah cabang"
 
+#: builtin/worktree.c
+msgid "create unborn/orphaned branch"
+msgstr "buat cabang belum lahir/yatim"
+
 #: builtin/worktree.c
 msgid "populate the new working tree"
 msgstr "isikan pohon kerja baru"
@@ -16549,6 +16905,15 @@ msgstr "coba cocokkan nama cabang baru dengan sebuah cabang pelacakan remote"
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "Opsi '%s', '%s', dan '%s' tidak dapat digunakan bersamaan"
 
+#: builtin/worktree.c
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "Opsi '%s', dan '%s' tidak dapat digunakan bersamaan"
+
+#: builtin/worktree.c
+msgid "<commit-ish>"
+msgstr "<mirip-komit>"
+
 #: builtin/worktree.c
 msgid "added with --lock"
 msgstr "tambahkan dengan --lock"
@@ -16712,6 +17077,16 @@ msgstr "hanya berguna untuk penirkutuan"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "core.fsyncMethod = batch tidak didukung pada platform ini"
 
+#: bundle-uri.c
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "tidak dapat mengurai kunci daftar bundel %s dengan nilai '%s'"
+
+#: bundle-uri.c
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "daftar bundel pada '%s' tidak punya mode"
+
 #: bundle-uri.c
 msgid "failed to create temporary file"
 msgstr "tidak dapat membuat berkas sementara"
@@ -16720,6 +17095,25 @@ msgstr "tidak dapat membuat berkas sementara"
 msgid "insufficient capabilities"
 msgstr "tidak cukup kemampuan"
 
+#: bundle-uri.c
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "berkas yang diunduh dari '%s' bukan sebuah bundel"
+
+#: bundle-uri.c
+msgid "failed to store maximum creation token"
+msgstr "gagal menyimpan token pembuatan maksimum"
+
+#: bundle-uri.c
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "mode bundel tidak dikenal dari URI '%s'"
+
+#: bundle-uri.c
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "batas rekursi URI bundel (%d) terlewati"
+
 #: bundle-uri.c
 #, c-format
 msgid "failed to download bundle from URI '%s'"
@@ -16727,13 +17121,29 @@ msgstr "gagal mengunduh bundel dari URI '%s'"
 
 #: bundle-uri.c
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "berkas pada URI '%s' bukan sebuah bundle"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "berkas pada URI '%s' bukan sebuah bundel atau daftar bundel"
 
 #: bundle-uri.c
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "gagal membongkar bundel dari URI '%s'"
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: argumen tidak diharapkan: '%s'"
+
+#: bundle-uri.c
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: bilasan diharapkan setelah argumen"
+
+#: bundle-uri.c
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: dapat satu baris kosong"
+
+#: bundle-uri.c
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: baris bukan berbentuk 'kunci=nilai'"
+
+#: bundle-uri.c
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: baris berisi kunci atau nilai kosong"
 
 #: bundle.c
 #, c-format
@@ -16763,6 +17173,14 @@ msgstr "Repositori kekurangan komit prasyarat berikut:"
 msgid "need a repository to verify a bundle"
 msgstr "perlu sebuah repositori untuk verifikasi bundel"
 
+#: bundle.c
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"beberapa komit prasyarat ada pada penyimpanan objek, tetapi tidak terhubung "
+"ke riwayat repositori"
+
 #: bundle.c
 #, c-format
 msgid "The bundle contains this ref:"
@@ -16781,6 +17199,16 @@ msgid_plural "The bundle requires these %<PRIuMAX> refs:"
 msgstr[0] "Bundel membutuhkan referensi ini:"
 msgstr[1] "Bundel membutuhkan %<PRIuMAX> referensi berikut:"
 
+#: bundle.c
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Bundel menggunakan algoritma hash ini: %s"
+
+#: bundle.c
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Bundel menggunakan penyaring ini: %s"
+
 #: bundle.c
 msgid "unable to dup bundle descriptor"
 msgstr "tidak dapat men-dup pendeskripsi bundel"
@@ -16825,6 +17253,11 @@ msgstr "index-pack mati"
 msgid "terminating chunk id appears earlier than expected"
 msgstr "id bingkah pengakhiran muncul lebih awal dari yang diharapkan"
 
+#: chunk-format.c
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "id bingkah %<PRIx32> tidak terata %d-bita"
+
 #: chunk-format.c
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
@@ -16896,9 +17329,8 @@ msgid "Move objects and refs by archive"
 msgstr "Pindahkan objek dan referensi oleh arsip"
 
 #: command-list.h
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Sediakan isi atau informasi tipe dan ukuran berkas untuk objek repositori"
+msgid "Provide contents or details of repository objects"
+msgstr "Sediakan isi atau detail objek repositori"
 
 #: command-list.h
 msgid "Display gitattributes information"
@@ -17084,8 +17516,8 @@ msgid "A portable graphical interface to Git"
 msgstr "Sebuah antarmuka grafis Git portabel"
 
 #: command-list.h
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "Hitung ID objek dan buat blob dari berkas (opsional)"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "Hitung ID objek dan buat objeck dari berkas (opsional)"
 
 #: command-list.h
 msgid "Display help information about Git"
@@ -17444,8 +17876,8 @@ msgid "Display version information about Git"
 msgstr "Perlihatkan info versi Git"
 
 #: command-list.h
-msgid "Show logs with difference each commit introduces"
-msgstr "Perlihatkan log dengan perbedaan yang dimasukkan setiap komit"
+msgid "Show logs with differences each commit introduces"
+msgstr "Perlihatkan log dengan perbedaan yang diperkenalkan pada setiap komit"
 
 #: command-list.h
 msgid "Manage multiple working trees"
@@ -17496,7 +17928,7 @@ msgid "Chunk-based file formats"
 msgstr "Berkas format berbasis bingkah"
 
 #: command-list.h
-msgid "Git commit graph format"
+msgid "Git commit-graph format"
 msgstr "Format grafik komit Git"
 
 #: command-list.h
@@ -17623,10 +18055,19 @@ msgstr "berkas grafik komit terlalu kecil untuk menyimpan %u bingkah"
 msgid "commit-graph has no base graphs chunk"
 msgstr "grafik komit tidak punya bingkah grafik dasar"
 
+#: commit-graph.c
+msgid "commit-graph base graphs chunk is too small"
+msgstr "bingkah grafik komit dasar terlalu kecil"
+
 #: commit-graph.c
 msgid "commit-graph chain does not match"
 msgstr "rantai grafik komit tidak cocok"
 
+#: commit-graph.c
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "jumlah komit pada grafik dasar terlalu tinggi: %<PRIuMAX>"
+
 #: commit-graph.c
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
@@ -17649,6 +18090,10 @@ msgstr "tidak dapat menemukan komit %s"
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "grafik komit memerlukan pembuatan data meluap tapi tidak punya"
 
+#: commit-graph.c
+msgid "commit-graph overflow generation data is too small"
+msgstr "data generasi luapan grafik komit terlalu kecil"
+
 #: commit-graph.c
 msgid "Loading known commits in commit graph"
 msgstr "Memuat komit yang dikenal di grafik komit"
@@ -17734,6 +18179,16 @@ msgstr "gagal menamai ulang berkas grafik komit dasar"
 msgid "failed to rename temporary commit-graph file"
 msgstr "gagal menamai ulang berkas grafik komit sementara"
 
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "tidak dapat menggabungkan grafik dengan %<PRIuMAX>, %<PRIuMAX> komit"
+
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "tidak dapat menggabungkan grafik %s, terlalu banyak komit: %<PRIuMAX>"
+
 #: commit-graph.c
 msgid "Scanning merged commits"
 msgstr "Memindai komit tergabung"
@@ -17769,10 +18224,6 @@ msgstr "grafik komit punya nilai kipas keluar salah: fanout[%d] = %u != %u"
 msgid "failed to parse commit %s from commit-graph"
 msgstr "gagal menguraikan komit %s dari grafik komit"
 
-#: commit-graph.c
-msgid "Verifying commits in commit graph"
-msgstr "Memverifikasi komit di dalam grafik komit"
-
 #: commit-graph.c
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
@@ -17798,22 +18249,6 @@ msgstr "induk grafik komit untuk %s adalah %s != %s"
 msgid "commit-graph parent list for commit %s terminates early"
 msgstr "daftar induk grafik komit untuk komit %s berakhir lebih awal"
 
-#: commit-graph.c
-#, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"grafik komit punya angka pembuatan nol untuk komit %s, tapi bukan nol di "
-"tempat lain"
-
-#: commit-graph.c
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"grafik komit punya angka pembuatan bukan nol untuk komit %s, tapi nol di "
-"tempat lain"
-
 #: commit-graph.c
 #, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
@@ -17826,6 +18261,19 @@ msgstr ""
 "tanggal komit untuk komit %s di dalam grafik komit yaitu %<PRIuMAX> != "
 "%<PRIuMAX>"
 
+#: commit-graph.c
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"grafik komit punya baik generasi nol dan bukan nol (seperti komit '%s' dan "
+"'%s')"
+
+#: commit-graph.c
+msgid "Verifying commits in commit graph"
+msgstr "Memverifikasi komit di dalam grafik komit"
+
 #: commit.c
 #, c-format
 msgid "%s %s is not a commit!"
@@ -17851,6 +18299,12 @@ msgstr ""
 "Matikan pesan ini dengan menjalankan\n"
 "\"git config advice.graftFileDeprecated false\""
 
+#: commit.c
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"komit %s ada pada grafik komit tapi tidak ada di dalam basis data objek"
+
 #: commit.c
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
@@ -17929,6 +18383,11 @@ msgstr "kasus tak tertangani di 'has_worktree_moved': %d"
 msgid "health thread wait failed [GLE %ld]"
 msgstr "antri utas kesehatan gagal [GLE %ld]"
 
+#: compat/fsmonitor/fsm-ipc-darwin.c
+#, c-format
+msgid "Invalid path: %s"
+msgstr "Jalur tidak valid: %s"
+
 #: compat/fsmonitor/fsm-listen-darwin.c
 msgid "Unable to create FSEventStream."
 msgstr "tidak dapat membuat FSEventStream."
@@ -17967,12 +18426,32 @@ msgstr "GetOverlappedResult gagal pada '%s' [GLE %ld]"
 msgid "could not read directory changes [GLE %ld]"
 msgstr "tidak dapat membaca perubahan direktori [GLE %ld]"
 
-#: compat/fsmonitor/fsm-settings-win32.c
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "opendir('%s') gagal"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "lstat('%s') gagal"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "strbuf_readlink('%s') gagal"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "closedir('%s') gagal"
+
+#: compat/fsmonitor/fsm-path-utils-win32.c
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
 msgstr "[GLE %ld] tidak dapat membuka untuk dibaca '%ls'"
 
-#: compat/fsmonitor/fsm-settings-win32.c
+#: compat/fsmonitor/fsm-path-utils-win32.c
 #, c-format
 msgid "[GLE %ld] unable to get protocol information for '%ls'"
 msgstr "[GLE %ld] tidak dapat mendapatkan informasi protokol untuk '%ls'"
@@ -18328,8 +18807,8 @@ msgid "bad zlib compression level %d"
 msgstr "level kompresi zlib jelek %d"
 
 #: config.c
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar harusnya hanya satu karakter"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar seharusnya hanya satu karakter ASCII"
 
 #: config.c
 #, c-format
@@ -18374,11 +18853,6 @@ msgstr "referensi '%s' tidak menunjuk pada sebuah blob"
 msgid "unable to resolve config blob '%s'"
 msgstr "tidak dapat menguraikan blob konfigurasi '%s'"
 
-#: config.c
-#, c-format
-msgid "failed to parse %s"
-msgstr "gagal menguraikan %s"
-
 #: config.c
 msgid "unable to parse command-line config"
 msgstr "gagal menguraikan konfigurasi baris perintah"
@@ -18467,6 +18941,13 @@ msgstr "tidak dapat menyetel '%s' ke '%s'"
 msgid "invalid section name: %s"
 msgstr "nama bagian tidak valid: %s"
 
+#: config.c
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"menolak bekerja dengan baris yang terlalu panjang di '%s' pada baris "
+"%<PRIuMAX>"
+
 #: config.c
 #, c-format
 msgid "missing value for '%s'"
@@ -18535,17 +19016,26 @@ msgstr "format objek tidak dikenal '%s' disebutkan oleh peladen"
 
 #: connect.c
 #, c-format
-msgid "invalid ls-refs response: %s"
-msgstr "jawaban ls-refs tidak valid: %s"
+msgid "error on bundle-uri response line %d: %s"
+msgstr "kesalahan pada baris tanggapan bundle-uri ke-%d: %s"
 
 #: connect.c
-msgid "expected flush after ref listing"
-msgstr "bilasan diharapkan setelah penyebutan referensi"
+msgid "expected flush after bundle-uri listing"
+msgstr "bilasan diharapkan setelah pendaftaran bundle-uri"
 
 #: connect.c
 msgid "expected response end packet after ref listing"
 msgstr "jawaban akhir paket diharapkan setelah penyebutan referensi"
 
+#: connect.c
+#, c-format
+msgid "invalid ls-refs response: %s"
+msgstr "jawaban ls-refs tidak valid: %s"
+
+#: connect.c
+msgid "expected flush after ref listing"
+msgstr "bilasan diharapkan setelah penyebutan referensi"
+
 #: connect.c
 #, c-format
 msgid "protocol '%s' is not supported"
@@ -18942,10 +19432,6 @@ msgstr "gagal menulis arsip"
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base tidak bekerja dengan rentang"
 
-#: diff-lib.c
-msgid "--merge-base only works with commits"
-msgstr "--merge-base hanya bekerja dengan komit"
-
 #: diff-lib.c
 msgid "unable to get HEAD"
 msgstr "tidak dapat mendapatkan HEAD"
@@ -18958,6 +19444,14 @@ msgstr "dasar penggabungan tidak ditemukan"
 msgid "multiple merge bases found"
 msgstr "banyak dasar penggabungan ditemukan"
 
+#: diff-no-index.c
+msgid "cannot compare stdin to a directory"
+msgstr "tidak dapat membandingkan masukan standar dan sebuah direktori"
+
+#: diff-no-index.c
+msgid "cannot compare a named pipe to a directory"
+msgstr "tidak dapat membandingkan pipa bernama dan sebuah direktori"
+
 #: diff-no-index.c
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<opsi>] <jalur> <jalur>"
@@ -19025,6 +19519,15 @@ msgstr ""
 msgid "external diff died, stopping at %s"
 msgstr "diff eksternal mati, berhenti pada %s"
 
+#: diff.c
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow butuh tepatnya satu spek jalur"
+
+#: diff.c
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "spek jalur ajaib tidak didukung oleh --follow: %s"
+
 #: diff.c parse-options.c
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
@@ -19044,10 +19547,6 @@ msgstr ""
 "opsi '%s' dan '%s' tidak dapat digunakan bersamaan, gunakan '%s' dengan '%s' "
 "dan '%s'"
 
-#: diff.c
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow butuh tepatnya satu spek jalur"
-
 #: diff.c
 #, c-format
 msgid "invalid --stat value: %s"
@@ -19289,6 +19788,10 @@ msgstr "tambah depan prefiks tambahan pada setiap baris keluaran"
 msgid "do not show any source or destination prefix"
 msgstr "jangan perlihatkan prefiks sumber atau tujuan apapun"
 
+#: diff.c
+msgid "use default prefixes a/ and b/"
+msgstr "gunakan awalan asali a/ dan b/"
+
 #: diff.c
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
@@ -19673,6 +20176,16 @@ msgstr "tidak dapat memigrasikan direktori git dari '%s' ke '%s'"
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "petunjuk: Menunggu penyunting Anda untuk menutup berkas...%c"
 
+#: editor.c sequencer.c wrapper.c
+#, c-format
+msgid "could not write to '%s'"
+msgstr "tidak dapat menulis ke '%s'"
+
+#: editor.c
+#, c-format
+msgid "could not edit '%s'"
+msgstr "tidak dapat menyunting '%s'"
+
 #: entry.c
 msgid "Filtering content"
 msgstr "Menyaring isi"
@@ -19983,10 +20496,11 @@ msgstr "repositori virtual '%s' tidka kompatibel dengan fsmonitor"
 #: fsmonitor-settings.c
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
-"repositori '%s' tidak kompatibel dengan fsmonitor karena kekurangan soket "
-"Unix"
+"direktori soket '%s' tidak kompatibel dengan fsmonitor karena kekurangan "
+"dukungan soket Unix"
 
 #: git.c
 msgid ""
@@ -19995,16 +20509,14 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 "git [-v| --version] [-h | --help] [-C <jalur>] [-c <nama>=<nilai>]\n"
 "           [--exec-path[=<jalur>]] [--html-path] [--man-path] [--info-path]\n"
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<jalur>] [--work-tree=<jalur>] [--namespace=<nama>]\n"
-"           [--super-prefix=<jalur>] [--config-env=<nama>=<variabel "
-"lingkungan>]\n"
+"           [--config-env=<nama>=<variabel lingkungan>]\n"
 "           <perintah> [<argumen>]"
 
 #: git.c
@@ -20034,11 +20546,6 @@ msgstr "tidak ada direktori yang diberikan untuk opsi '%s'\n"
 msgid "no namespace given for --namespace\n"
 msgstr "tidak ada ruang nama yang diberikan untuk --namespace\n"
 
-#: git.c
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "tidak ada prefiks yang diberikan untuk --super-prefix\n"
-
 #: git.c
 #, c-format
 msgid "-c expects a configuration string\n"
@@ -20049,6 +20556,11 @@ msgstr "-c mengharapkan sebuah untai konfigurasi\n"
 msgid "no config key given for --config-env\n"
 msgstr "tidak ada kunci konfigurasi yang diberikan untuk --config-env\n"
 
+#: git.c
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "tidak ada atribut yang diberikan untuk --attr-source\n"
+
 #: git.c
 #, c-format
 msgid "unknown option: %s\n"
@@ -20177,8 +20689,13 @@ msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "gpg.ssh.defaultKeyCommand gagal: %s %s"
 
 #: gpg-interface.c
-msgid "gpg failed to sign the data"
-msgstr "gpg gagal menandatangani data"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg gagal menandatangani data:\n"
+"%s"
 
 #: gpg-interface.c
 msgid "user.signingKey needs to be set for ssh signing"
@@ -20380,8 +20897,8 @@ msgstr[1] ""
 "Perintah paling mirip adalah"
 
 #: help.c
-msgid "git version [<options>]"
-msgstr "git version [<opsi>]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #: help.c
 #, c-format
@@ -20683,13 +21200,13 @@ msgstr ""
 "%s"
 
 #: merge-ort.c merge-recursive.c
-msgid "Failed to execute internal merge"
-msgstr "Gagal menjalankan penggabungan internal"
+msgid "failed to execute internal merge"
+msgstr "gagal menjalankan penggabungan internal"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "Tidak dapat menambahkan %s ke basis data"
+msgid "unable to add %s to database"
+msgstr "tidak dapat menambahkan %s ke basis data"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
@@ -21095,8 +21612,8 @@ msgstr ""
 #: merge-recursive.c
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
 "KONFLIK (penamaan ulang/penamaan ulang): Penamaan ulang \"%s\"->\"%s\" di "
 "dalam cabang \"%s\" penamaan ulang \"%s\"->\"%s\" di \"%s\"%s"
@@ -21212,6 +21729,14 @@ msgstr "gagal membaca tembolok"
 msgid "multi-pack-index OID fanout is of the wrong size"
 msgstr "OID kipas-keluar indeks multipak salah ukuran"
 
+#: midx.c
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "bingkah pencarian OID kipas-keluar indeks multipak salah ukuran"
+
+#: midx.c
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "bingkah offset OID kipas-keluar indeks multipak salah ukuran"
+
 #: midx.c
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -21234,20 +21759,26 @@ msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "versi hash indeks multipak %u tidak cocok dengan versi %u"
 
 #: midx.c
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "indeks multipak kehilangan bingkah pack-name yang diperlukan"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "bingkah nama pak indeks multipak yang diperlukan hilang atau rusak"
+
+#: midx.c
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"bingkah OID kipas-keluar indeks multipak yang diperlukan hilang atau rusak"
 
 #: midx.c
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "indeks multipak kehilangan bingkah OID kipas keluar yang diperlukan"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"bingkah pencarian OID indeks multipak yang diperlukan  hilang atau rusak"
 
 #: midx.c
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "indeks multipak kehilangan bingkah pencarian OID yang diperlukan"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr "bingkah offset objek indeks multipak yang diperlukan hilang atau rusak"
 
 #: midx.c
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "indeks multipak kehilangan bingkah offset objek yang diperlukan"
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "bingkah nama pak indeks multipak terlalu kecil"
 
 #: midx.c
 #, c-format
@@ -21263,6 +21794,10 @@ msgstr "pack-int-id jelek: %u (total pak %u)"
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr "indeks multipak simpan offset 64-bit, tapi off_t terlalu kecil"
 
+#: midx.c
+msgid "multi-pack-index large offset out of bounds"
+msgstr "offset indeks multipak besar di luar jangkauan"
+
 #: midx.c
 #, c-format
 msgid "failed to add packfile '%s'"
@@ -21492,11 +22027,6 @@ msgstr "tidak dapat menormalisasikan jalur objek alternatif: %s"
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s: mengabaikan penyimpanan objek alternatif, bersarang terlalu dalam"
 
-#: object-file.c
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "tidak dapat menormalisasikan direktori objek: %s"
-
 #: object-file.c
 msgid "unable to fdopen alternates lockfile"
 msgstr "tidak dapat men-fdopen berkas kunci alternatif"
@@ -21570,6 +22100,11 @@ msgstr "objek longgar '%s' rusak"
 msgid "garbage at end of loose object '%s'"
 msgstr "sampah pada ujung berkas objek '%s'"
 
+#: object-file.c
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "tidak dapat membuka objek longgar %s"
+
 #: object-file.c
 #, c-format
 msgid "unable to parse %s header"
@@ -21591,19 +22126,14 @@ msgstr "kepala untuk %s terlalu panjang, melebihi %d bita"
 
 #: object-file.c
 #, c-format
-msgid "failed to read object %s"
-msgstr "gagal membaca objek %s"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "objek longgar %s (disimpan di %s) rusak"
 
 #: object-file.c
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "pengganti %s tidak ditemukan untuk %s"
 
-#: object-file.c
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "objek longgar %s (disimpan di %s) rusak"
-
 #: object-file.c
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
@@ -21619,10 +22149,6 @@ msgstr "tidak dapat menulis berkas %s"
 msgid "unable to set permission to '%s'"
 msgstr "tidak dapat menyetel perizinan ke '%s'"
 
-#: object-file.c
-msgid "file write error"
-msgstr "kesalahan menulis berkas"
-
 #: object-file.c
 msgid "error when closing loose object file"
 msgstr "kesalahan saat menutup berkas objek longgar"
@@ -21683,12 +22209,13 @@ msgid "cannot read object for %s"
 msgstr "tidak dapat membaca objek untuk %s"
 
 #: object-file.c
-msgid "corrupt commit"
-msgstr "komit rusak"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "fsck objek gagal: %s"
 
 #: object-file.c
-msgid "corrupt tag"
-msgstr "tag rusak"
+msgid "refusing to create malformed object"
+msgstr "menolak membuat objek jelek"
 
 #: object-file.c
 #, c-format
@@ -22002,11 +22529,6 @@ msgstr "offset XOR tidak valid di indeks pak bitmap"
 msgid "cannot fstat bitmap file"
 msgstr "tidak dapat fstat berkas bitmap"
 
-#: pack-bitmap.c
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "mengabaikan berkas bitmap tambahan: '%s'"
-
 #: pack-bitmap.c
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "checksum tidak cocok di MIDX dan bitmap"
@@ -22090,6 +22612,11 @@ msgstr "tidak dapat menemukan %s di dalam pak '%s' pada offset %<PRIuMAX>"
 msgid "unable to get disk usage of '%s'"
 msgstr "tidak dapat mendapatkan penggunaan disk dari '%s'"
 
+#: pack-bitmap.c
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "checksum berkas bitmap '%s' tidak valid"
+
 #: pack-mtimes.c
 #, c-format
 msgid "mtimes file %s is too small"
@@ -22140,6 +22667,19 @@ msgstr "berkas indeks balik %s punya versi tidak didukung %<PRIu32>"
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "berkas indeks balik %s punya id hash tidak didukung %<PRIu32>"
 
+#: pack-revindex.c
+msgid "invalid checksum"
+msgstr "checksum tidak valid"
+
+#: pack-revindex.c
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "posisi indeks balik tidak valid pada %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+#: pack-revindex.c
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "bingkah indeks balik multipak salah ukuran"
+
 #: pack-write.c
 msgid "cannot both write and verify reverse index"
 msgstr "tidak dapat kedua-duanya menulis dan memverifikasi indeks balik"
@@ -22196,22 +22736,12 @@ msgstr "nama objek rusak '%s'"
 #: parse-options-cb.c
 #, c-format
 msgid "option `%s' expects \"%s\" or \"%s\""
-msgstr "opsi `%s' mengharapkan \"%s\" atau \"%s\""
-
-#: parse-options.c
-#, c-format
-msgid "%s requires a value"
-msgstr "%s butuh sebuah nilai"
-
-#: parse-options.c
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s tidak kompatibel dengan %s"
+msgstr "opsi `%s' mengharapkan \"%s\" atau \"%s\""
 
 #: parse-options.c
 #, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s : tidak kompatibel dengan sesuatu yang lain"
+msgid "%s requires a value"
+msgstr "%s butuh sebuah nilai"
 
 #: parse-options.c
 #, c-format
@@ -22230,6 +22760,11 @@ msgstr ""
 "%s mengharapkan nilai bilangan bulat non negatif dengan akhiran opsional k/m/"
 "g"
 
+#: parse-options.c
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s tidak kompatibel dengan %s"
+
 #: parse-options.c
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
@@ -22314,6 +22849,11 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-NUM"
 
+#: parse-options.c
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "lawan dari --no-%s"
+
 #: parse-options.h
 msgid "expiry-date"
 msgstr "tanggal kadaluarsa"
@@ -22334,6 +22874,10 @@ msgstr "jadi lebih dian"
 msgid "use <n> digits to display object names"
 msgstr "gunakan <n> digit untuk menampilkan nama objek"
 
+#: parse-options.h
+msgid "prefixed path to initial superproject"
+msgstr "jalur terprefiks ke superproyek awal"
+
 #: parse-options.h
 msgid "how to strip spaces and #comments from message"
 msgstr "bagaimana cara mengupas spasi dan #komentar dari pesan"
@@ -22348,6 +22892,16 @@ msgid ""
 msgstr ""
 "dengan --pathspec-from-file, elemen spek jalur dipisahkan dengan karakter NUL"
 
+#: parse.c
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "nilai lingkungan boolean '%s' jelek untuk '%s'"
+
+#: parse.c
+#, c-format
+msgid "failed to parse %s"
+msgstr "gagal menguraikan %s"
+
 #: path.c
 #, c-format
 msgid "Could not make %s writable by group"
@@ -22408,6 +22962,11 @@ msgstr "Spek jalur ajaib '%c' tidak diterapkan di '%s'"
 msgid "%s: 'literal' and 'glob' are incompatible"
 msgstr "%s: 'literal' dan 'glob' tidak kompatibel"
 
+#: pathspec.c
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s' di luar pohon direktori"
+
 #: pathspec.c
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
@@ -22520,6 +23079,11 @@ msgstr ""
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr "nama remote penjanji tidak dapat diawali dengan '/': %s"
 
+#: promisor-remote.c
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "tidak dapat mengambil %s dari remote penjanji"
+
 #: protocol-caps.c
 msgid "object-info: expected flush after arguments"
 msgstr "object-info: bilasan diharapkan setelah argumen"
@@ -22564,6 +23128,15 @@ msgstr "gagal membuat diff"
 msgid "could not parse log for '%s'"
 msgstr "tidak dapat menguraikan log untuk '%s'"
 
+#: reachable.c
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "ujung sampah tambahan tidak valid: '%s'"
+
+#: reachable.c
+msgid "unable to enumerate additional recent objects"
+msgstr "tidak dapat menghitung objeck terkini tambahan"
+
 #: read-cache.c
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
@@ -22729,11 +23302,6 @@ msgstr "tidak dapat membuat indeks terpisah untuk indeks jarang"
 msgid "failed to convert to a sparse-index"
 msgstr "gagal mengubah ke indeks jarang"
 
-#: read-cache.c
-#, c-format
-msgid "could not stat '%s'"
-msgstr "tidak dapat men-stat '%s'"
-
 #: read-cache.c
 #, c-format
 msgid "unable to open git dir: %s"
@@ -22754,6 +23322,16 @@ msgstr "tidak dapat memperbaiki bit perizinan pada '%s'"
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: tidak dapat menurunkan ke tahap #0"
 
+#: read-cache.c
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "status diff tak diharapkan %c"
+
+#: read-cache.c
+#, c-format
+msgid "remove '%s'\n"
+msgstr "hapus '%s'\n"
+
 #: rebase-interactive.c
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
@@ -22930,6 +23508,16 @@ msgstr "di belakang %d"
 msgid "ahead %d, behind %d"
 msgstr "di depan %d, di belakang %d"
 
+#: ref-filter.c
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) tidak mengambil argumen"
+
+#: ref-filter.c
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "argumen %%(%.*s) tidak dikenal: %s"
+
 #: ref-filter.c
 #, c-format
 msgid "expected format: %%(color:<color>)"
@@ -22952,49 +23540,44 @@ msgstr "Nilai bilangan bulat diharapkan refname:rstrip=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "argumen %%(%s) tidak dikenal: %s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "diharapkan %%(trailers:key=<nilai>)"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) tidak mengambil argumen"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "argumen %%(trailers) tidak dikenal: %s"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) tidak mengambil argumen"
+msgid "positive value expected contents:lines=%s"
+msgstr "nilai positif diharapkan contents:lines=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) tidak mengambil argumen"
+msgid "argument expected for %s"
+msgstr "argumen diharapkan untuk %s"
 
 #: ref-filter.c
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "diharapkan %%(trailers:key=<nilai>)"
+msgid "positive value expected %s=%s"
+msgstr "nilai positif %s=%s diharapkan"
 
 #: ref-filter.c
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "argumen %%(trailers) tidak dikenal: %s"
+msgid "cannot fully parse %s=%s"
+msgstr "tidak dapat menguraikan penuh %s=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "nilai positif diharapkan contents:lines=%s"
+msgid "value expected %s="
+msgstr "nilai %s= diharapkan"
 
 #: ref-filter.c
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "nilai positif '%s' diharapkan di %%(%s)"
 
-#: ref-filter.c
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "opsi surel tidak dikenal: %s"
-
 #: ref-filter.c
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
@@ -23010,6 +23593,11 @@ msgstr "posisi tidak dikenal: %s"
 msgid "unrecognized width:%s"
 msgstr "lebar tidak dikenal: %s"
 
+#: ref-filter.c
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "argumen %%(%s) tidak dikenal: %s"
+
 #: ref-filter.c
 #, c-format
 msgid "positive width expected with the %%(align) atom"
@@ -23017,8 +23605,8 @@ msgstr "lebar positif diharapkan dengan atom %%(align)"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) tidak mengambil argumen"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "format yang diharapkan: %%(ahead-behind:<mirip komit>)"
 
 #: ref-filter.c
 #, c-format
@@ -23077,6 +23665,10 @@ msgstr "perintah ini menolak atom %%(%.*s)"
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s tidak dapat digunakan dengan --python, --shell, --tcl"
 
+#: ref-filter.c
+msgid "failed to run 'describe'"
+msgstr "gagal menjalankan 'describe'"
+
 #: ref-filter.c
 #, c-format
 msgid "(no branch, rebasing %s)"
@@ -23154,6 +23746,10 @@ msgstr "kunci"
 msgid "field name to sort on"
 msgstr "nama bidang untuk diurutkan"
 
+#: ref-filter.h
+msgid "exclude refs which match pattern"
+msgstr "hanya gunakan referensi yang cocok dengan pola"
+
 #: reflog.c
 #, c-format
 msgid "not a reflog: %s"
@@ -23694,8 +24290,11 @@ msgstr[1] ""
 "dan masing-masing punya %d dan %d komit berbeda.\n"
 
 #: remote.c
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (gunakan \"git pull\" untuk gabungkan cabang remote ke milik Anda)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr ""
+"  (gunakan \"git pull\" jika Anda ingin mengintegrasikan cabang remote pada\n"
+"   milik Anda)\n"
 
 #: remote.c
 #, c-format
@@ -23785,11 +24384,6 @@ msgstr "gagal memperbarui keadaan konflik di '%s'"
 msgid "no remembered resolution for '%s'"
 msgstr "tidak ada resolusi yang diingat untuk '%s'"
 
-#: rerere.c
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "tidak dapat batal taut '%s'"
-
 #: rerere.c
 #, c-format
 msgid "Updated preimage for '%s'"
@@ -23819,6 +24413,15 @@ msgstr "tidak dapat menentukan revisi HEAD"
 msgid "failed to find tree of %s"
 msgstr "gagal menemukan pohon %s"
 
+#: revision.c
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "seksi tidak didukung untuk referensi tersembunyi: %s"
+
+#: revision.c
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= dilewatkan lebih dari sekali"
+
 #: revision.c
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
@@ -23833,6 +24436,11 @@ msgstr "tidak dapat mendapatkan komit untuk argumen jalur leluhur '%s'"
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<berkas pak> tidak didukung lagi"
 
+#: revision.c
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "opsi tidak valid: '%s' di mode --stdin"
+
 #: revision.c
 msgid "your current branch appears to be broken"
 msgstr "sepertinya cabang Anda saat ini rusak"
@@ -23945,8 +24553,16 @@ msgid "only download metadata for the branch that will be checked out"
 msgstr "hanya unduh metadata untuk cabang yang akan dicheckout"
 
 #: scalar.c
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<options>] [--] <repositori> [<direktori>]"
+msgid "create repository within 'src' directory"
+msgstr "salin repositori di dalam direktori 'src'"
+
+#: scalar.c
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <cabang utama>] [--full-clone]\n"
+"\t[--[-no-]src] <url> [<pendaftaran>]"
 
 #: scalar.c
 #, c-format
@@ -24007,8 +24623,37 @@ msgstr "--all atau <pendaftaran>, tetapi bukan kedua-duanya"
 
 #: scalar.c
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "repositori git pergi di '%s'"
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "tidak dapat menghapus scalar.repo basi '%s'"
+
+#: scalar.c
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "menghapus scalar.repo basi '%s'"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "repositori pada '%s' berpemilik lain"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "repositori pada '%s' ada masalah format"
+
+#: scalar.c
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "repositori tidak ditemukan di '%s'"
+
+#: scalar.c
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"untuk mencabut repositori ini dari Scalar, jalankan\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 #: scalar.c
 msgid ""
@@ -24192,11 +24837,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "tidak dapat mengunci '%s'"
 
-#: sequencer.c strbuf.c wrapper.c
-#, c-format
-msgid "could not write to '%s'"
-msgstr "tidak dapat menulis ke '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not write eol to '%s'"
@@ -24497,11 +25137,6 @@ msgstr "tidak dapat mendapatkan pesan komit untuk %s"
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s: tidak dapat menguraikan komit induk %s"
 
-#: sequencer.c
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "tidak dapat menamai ulang '%s' ke '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not revert %s... %s"
@@ -24527,6 +25162,28 @@ msgstr "git %s: gagal membaca indeks"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: gagal menyegarkan indeks"
 
+#: sequencer.c
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "'%s' bukan label valid"
+
+#: sequencer.c
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "'%s' bukan nama referensi valid"
+
+#: sequencer.c
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr ""
+"update-ref memerlukan nama referensi terkualifikasi penuh, misalnya refs/"
+"heads/%s"
+
+#: sequencer.c
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "perintah tidak valid '%.*s'"
+
 #: sequencer.c
 #, c-format
 msgid "%s does not accept arguments: '%s'"
@@ -24617,10 +25274,6 @@ msgstr "coba \"git cherry-pick (--continue | %s--abort | --quit)\""
 msgid "could not create sequencer directory '%s'"
 msgstr "tidak dapat membuat direktori pembaris '%s'"
 
-#: sequencer.c
-msgid "could not lock HEAD"
-msgstr "tidak dapat mengunci HEAD"
-
 #: sequencer.c
 msgid "no cherry-pick or revert in progress"
 msgstr "tidak ada pemetikan ceri atau pembalikkan yang sedang berjalan"
@@ -24737,21 +25390,21 @@ msgstr ""
 "\n"
 
 #: sequencer.c
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "dan buat perubahan ke indeks dan/atau pohon kerja\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "dan buat perubahan ke indeks dan/atau pohon kerja.\n"
 
 #: sequencer.c
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "eksekusi berhasil: %s\n"
-"tapi meninggalkan perubahan ke indeks dan/atau pohon kerja\n"
+"tapi meninggalkan perubahan ke indeks dan/atau pohon kerja.\n"
 "Komit atau stase perubahan Anda, lalu jalankan\n"
 "\n"
 "  git rebase --continue\n"
@@ -24762,6 +25415,11 @@ msgstr ""
 msgid "illegal label name: '%.*s'"
 msgstr "nama label ilegal: '%.*s'"
 
+#: sequencer.c
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "tidak dapat menguraikan '%s'"
+
 #: sequencer.c
 msgid "writing fake root commit"
 msgstr "menulis komit akar palsu"
@@ -24770,11 +25428,6 @@ msgstr "menulis komit akar palsu"
 msgid "writing squash-onto"
 msgstr "menulis squash-onto"
 
-#: sequencer.c
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "tidak dapat menguraikan '%s'"
-
 #: sequencer.c
 msgid "cannot merge without a current revision"
 msgstr "tidak dapat menggabungkan tanpa revisi saat ini"
@@ -24920,13 +25573,13 @@ msgstr ""
 
 #: sequencer.c
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Mendasarkan ulang (%d/%d)%s"
+msgid "Stopped at %s...  %.*s\n"
+msgstr "Berhenti pada %s... %.*s\n"
 
 #: sequencer.c
 #, c-format
-msgid "Stopped at %s...  %.*s\n"
-msgstr "Berhenti pada %s... %.*s\n"
+msgid "Rebasing (%d/%d)%s"
+msgstr "Mendasarkan ulang (%d/%d)%s"
 
 #: sequencer.c
 #, c-format
@@ -25168,55 +25821,144 @@ msgid "cannot change to '%s'"
 msgstr "tidak dapat berganti ke '%s'"
 
 #: setup.c
-#, c-format
-msgid "not a git repository (or any of the parent directories): %s"
-msgstr "bukan sebuah repositori git (atau direktori induk apapun): %s"
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "bukan sebuah repositori git (atau direktori induk apapun): %s"
+
+#: setup.c
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"bukan sebuah repositori git (atau induk apapun hingga ke titik pasang %s)\n"
+"Berhenti pada perbatasan sistem berkas (GIT_DISCOVERY_ACROSS_FILESYSTEM "
+"tidak disetel)."
+
+#: setup.c
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"perizinan meragukan terdeteksi di dalam repositori pada '%s'\n"
+"%sUntuk menambahkan pengecualian untuk direktori ini, panggil:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#: setup.c
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr ""
+"tidak dapat menggunakan repositori bare '%s' (safe.bareRepository yaitu '%s')"
+
+#: setup.c
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"masalah dengan nilai mode berkas core.sharedRepository (0%.3o).\n"
+"Pemilik berkas harus selalu punya perizinan baca dan tulis."
+
+#: setup.c
+msgid "fork failed"
+msgstr "penggarpuan gagal"
+
+#: setup.c
+msgid "setsid failed"
+msgstr "setsid gagal"
+
+#: setup.c
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "tidak dapat men-stat templat '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "tidak dapat membuka direktori '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "tidak dapat membaca tautan '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "tidak dapat menautkan simbolik '%s' '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "tidak dapat menyalin '%s' ke '%s'"
+
+#: setup.c
+#, c-format
+msgid "ignoring template %s"
+msgstr "mengabaikan templat %s"
+
+#: setup.c
+#, c-format
+msgid "templates not found in %s"
+msgstr "templat tidak ditemukan di %s"
+
+#: setup.c
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "tidak menyalin templat dari '%s': %s"
+
+#: setup.c
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "nama cabang asal salah: '%s'"
+
+#: setup.c
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "tidak dapat menangani tipe berkas %d"
+
+#: setup.c
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "tidak dapat memindahkan %s ke %s"
+
+#: setup.c
+msgid "attempt to reinitialize repository with different hash"
+msgstr "mencoba menginisialisasi ulang repositori dengan hash yang berbeda"
 
 #: setup.c
 #, c-format
-msgid ""
-"not a git repository (or any parent up to mount point %s)\n"
-"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
-msgstr ""
-"bukan sebuah repositori git (atau induk apapun hingga ke titik pasang %s)\n"
-"Berhenti pada perbatasan sistem berkas (GIT_DISCOVERY_ACROSS_FILESYSTEM "
-"tidak disetel)."
+msgid "%s already exists"
+msgstr "%s sudah ada"
 
 #: setup.c
 #, c-format
-msgid ""
-"detected dubious ownership in repository at '%s'\n"
-"%sTo add an exception for this directory, call:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
-msgstr ""
-"perizinan meragukan terdeteksi di dalam repositori pada '%s'\n"
-"%sUntuk menambahkan pengecualian untuk direktori ini, panggil:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: --initial-branch=%s diabaikan"
 
 #: setup.c
 #, c-format
-msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr ""
-"tidak dapat menggunakan repositori bare '%s' (safe.bareRepository yaitu '%s')"
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Repositori berbagi Git yang sudah ada diinisialisasi ulang di %s%s\n"
 
 #: setup.c
 #, c-format
-msgid ""
-"problem with core.sharedRepository filemode value (0%.3o).\n"
-"The owner of files must always have read and write permissions."
-msgstr ""
-"masalah dengan nilai mode berkas core.sharedRepository (0%.3o).\n"
-"Pemilik berkas harus selalu punya perizinan baca dan tulis."
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Repositori Git diinisialisasi ulang di %s%s\n"
 
 #: setup.c
-msgid "fork failed"
-msgstr "penggarpuan gagal"
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Repositori berbagi Git kosong diinisialisasi di %s%s\n"
 
 #: setup.c
-msgid "setsid failed"
-msgstr "setsid gagal"
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Repositori Git kosong dinisialisasi di %s%s\n"
 
 #: sparse-index.c
 #, c-format
@@ -25279,11 +26021,6 @@ msgid_plural "%u bytes/s"
 msgstr[0] "%u bita/detik"
 msgstr[1] "%u bita/detik"
 
-#: strbuf.c
-#, c-format
-msgid "could not edit '%s'"
-msgstr "tidak dapat menyunting '%s'"
-
 #: submodule-config.c
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
@@ -25502,100 +26239,129 @@ msgstr "ls-tree kembalikan kode kembali %d yang tak diharapkan"
 msgid "failed to lstat '%s'"
 msgstr "gagal men-lstat '%s'"
 
+#: t/helper/test-bundle-uri.c
+msgid "no remote configured to get bundle URIs from"
+msgstr "tidak ada remote yang dikonfigurasi untuk mendapatkan URI bundel"
+
+#: t/helper/test-bundle-uri.c
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "remote '%s' tidak punya URL terkonfigurasi"
+
+#: t/helper/test-bundle-uri.c
+msgid "could not get the bundle-uri list"
+msgstr "tidak dapat mendapatkan daftar bundle-uri"
+
+#: t/helper/test-cache-tree.c
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <opsi> (control|prime|update)"
+
+#: t/helper/test-cache-tree.c
+msgid "clear the cache tree before each iteration"
+msgstr "bersihkan pohon tembolok sebelum setiap iterasi"
+
+#: t/helper/test-cache-tree.c
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr "jumlah entri di dalam pohon tembolok untuk dinirvalidasi (asali 0)"
+
 #: t/helper/test-fast-rebase.c
 msgid "unhandled options"
-msgstr ""
+msgstr "opsi tak tertangani"
 
 #: t/helper/test-fast-rebase.c
 msgid "error preparing revisions"
-msgstr ""
+msgstr "kesalahan menyiapkan revisi"
 
 #: t/helper/test-reach.c
 #, c-format
 msgid "commit %s is not marked reachable"
-msgstr ""
+msgstr "komit %s tidak ditandai sebagai dapat dicapai"
 
 #: t/helper/test-reach.c
 msgid "too many commits marked reachable"
-msgstr ""
+msgstr "terlalu banyak komit yang ditandai sebagai dapat dicapai"
 
 #: t/helper/test-serve-v2.c
 msgid "test-tool serve-v2 [<options>]"
-msgstr ""
+msgstr "test-tool serve-v2 [<opsi>]"
 
 #: t/helper/test-serve-v2.c
 msgid "exit immediately after advertising capabilities"
-msgstr ""
+msgstr "langsung keluar setelah mengiklankan kemampuan"
 
 #: t/helper/test-simple-ipc.c
 msgid "test-helper simple-ipc is-active    [<name>] [<options>]"
-msgstr ""
+msgstr "test-helper simple-ipc is-active    [<nama>] [<opsi>]"
 
 #: t/helper/test-simple-ipc.c
 msgid "test-helper simple-ipc run-daemon   [<name>] [<threads>]"
-msgstr ""
+msgstr "test-helper simple-ipc run-daemon   [<nama>] [<utas>]"
 
 #: t/helper/test-simple-ipc.c
 msgid "test-helper simple-ipc start-daemon [<name>] [<threads>] [<max-wait>]"
 msgstr ""
+"test-helper simple-ipc start-daemon [<nama>] [<utas>] [<tunggu maksimum>]"
 
 #: t/helper/test-simple-ipc.c
 msgid "test-helper simple-ipc stop-daemon  [<name>] [<max-wait>]"
-msgstr ""
+msgstr "test-helper simple-ipc stop-daemon  [<nama> [<tunggu maksimum>]"
 
 #: t/helper/test-simple-ipc.c
 msgid "test-helper simple-ipc send         [<name>] [<token>]"
-msgstr ""
+msgstr "test-helper simple-ipc send         [<nama>] [<token>]"
 
 #: t/helper/test-simple-ipc.c
 msgid "test-helper simple-ipc sendbytes    [<name>] [<bytecount>] [<byte>]"
 msgstr ""
+"test-helper simple-ipc sendbytes    [<nama>] [<hitungan bita>] [<bita>]"
 
 #: t/helper/test-simple-ipc.c
 msgid ""
 "test-helper simple-ipc multiple     [<name>] [<threads>] [<bytecount>] "
 "[<batchsize>]"
 msgstr ""
+"test-helper simple-ipc multiple     [<nama>] [<utas>] [<hitungan bita>] "
+"[<ukuran batch>]"
 
 #: t/helper/test-simple-ipc.c
 msgid "name or pathname of unix domain socket"
-msgstr ""
+msgstr "nama atau nama jalur soket domain unix"
 
 #: t/helper/test-simple-ipc.c
 msgid "named-pipe name"
-msgstr ""
+msgstr "nama pipa bernama"
 
 #: t/helper/test-simple-ipc.c
 msgid "number of threads in server thread pool"
-msgstr ""
+msgstr "jumlah utas pada kolam utas peladen"
 
 #: t/helper/test-simple-ipc.c
 msgid "seconds to wait for daemon to start or stop"
-msgstr ""
+msgstr "waktu menunggu daemon dimulai atau dihentikan (dalam detik)"
 
 #: t/helper/test-simple-ipc.c
 msgid "number of bytes"
-msgstr ""
+msgstr "jumlah bita"
 
 #: t/helper/test-simple-ipc.c
 msgid "number of requests per thread"
-msgstr ""
+msgstr "jumlah permintaan tiap utas"
 
 #: t/helper/test-simple-ipc.c
 msgid "byte"
-msgstr ""
+msgstr "bita"
 
 #: t/helper/test-simple-ipc.c
 msgid "ballast character"
-msgstr ""
+msgstr "karakter pemberat"
 
 #: t/helper/test-simple-ipc.c
 msgid "token"
-msgstr ""
+msgstr "token"
 
 #: t/helper/test-simple-ipc.c
 msgid "command token to send to the server"
-msgstr ""
+msgstr "token perintah untuk dikirim ke peladen"
 
 #: trailer.c
 #, c-format
@@ -25927,6 +26693,14 @@ msgstr "Membatalkan."
 msgid "failed to push all needed submodules"
 msgstr "gagal mendorong semua submodul yang dibutuhkan"
 
+#: transport.c
+msgid "bundle-uri operation not supported by protocol"
+msgstr "operasi bundle-uri tidak didukung oleh protokol"
+
+#: transport.c
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "tidak dapat menerima daftar bundle-uri teriklankan server"
+
 #: tree-walk.c
 msgid "too-short tree object"
 msgstr "objek pohon terlalu pendek"
@@ -26387,6 +27161,10 @@ msgstr "tidak dapat mengakses '%s'"
 msgid "unable to get current working directory"
 msgstr "tidak dapat mendapatkan direktori kerja saat ini"
 
+#: wrapper.c
+msgid "unable to get random bytes"
+msgstr "tidak dapat mendapatkan bita acak"
+
 #: wt-status.c
 msgid "Unmerged paths:"
 msgstr "Jalur yang tak tergabung:"
@@ -26831,13 +27609,22 @@ msgstr "Berkas yang diabaikan"
 #: wt-status.c
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
+msgstr ""
+"Butuh %.2f detik untuk mengenumerasikan berkas tak terlacak,\n"
+"tapi hasilnya ditembolokkan, dan invokasi berikutnya mungkin\n"
+"lebih cepat."
+
+#: wt-status.c
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "Butuh %.2f detik untuk mengenumerasikan berkas tak terlacak."
+
+#: wt-status.c
+msgid "See 'git help status' for information on how to improve this."
 msgstr ""
-"Butuh %.2f detik untuk menghitung berkas tak terlacak. 'status -uno'\n"
-"mungkin bisa mempercepat, tapi Anda harus berhati-hati jangan sampai lupa\n"
-"untuk tambahkan berkas baru sendiri (lihat 'git help status')."
+"Lihat 'git help status' untuk informasi bagaimana meningkatkankeadaan ini."
 
 #: wt-status.c
 #, c-format
@@ -27016,241 +27803,6 @@ msgstr "Anda perlu menjalankan perintah ini dari level atas dari pohon kerja."
 msgid "Unable to determine absolute path of git directory"
 msgstr "Tidak dapat menentukan jalur absolut direktori git"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#: git-add--interactive.perl
-#, perl-format
-msgid "%12s %12s %s"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] ""
-msgstr[1] ""
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid "The selected hunks do not apply to the index!\n"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid "No other hunks to goto\n"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] ""
-msgstr[1] ""
-
-#: git-add--interactive.perl
-msgid "No other hunks to search\n"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid "No hunk matches the given pattern\n"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid "No previous hunk\n"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid "No next hunk\n"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid "Sorry, cannot split this hunk\n"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] ""
-msgstr[1] ""
-
-#: git-add--interactive.perl
-msgid "Sorry, cannot edit this hunk\n"
-msgstr ""
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-#: git-add--interactive.perl
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-
-#: git-add--interactive.perl
-msgid "missing --"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr ""
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr ""
-
 #: git-send-email.perl
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "zona lokal berbeda dari GMT oleh selang non-menit\n"
@@ -27392,13 +27944,13 @@ msgstr ""
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "Gagal membuka %s: %s"
+msgid "Failed to open %s.final: %s"
+msgstr "Gagal membuka %s.final: %s"
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s.final: %s"
-msgstr "Gagal membuka %s.final: %s"
+msgid "Failed to open %s: %s"
+msgstr "Gagal membuka %s: %s"
 
 #: git-send-email.perl
 msgid "Summary email is empty, skipping it\n"
@@ -27583,14 +28135,19 @@ msgstr "(%s) Tidak dapat menjalankan '%s'"
 
 #: git-send-email.perl
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Menambahkan %s: %s dari: '%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Baris masukan salah format: '%s'"
 
 #: git-send-email.perl
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) gagal menutup pipa ke '%s'"
 
+#: git-send-email.perl
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Menambahkan %s: %s dari: '%s'\n"
+
 #: git-send-email.perl
 msgid "cannot send message as 7bit"
 msgstr "tidak dapat mengirim pesan sebagai 7bit"
index 803208c4d52d855f36dc9fe493dc95a6c75390e9..3e56eb546eadf320c95d9a358c337ae22beb421f 100644 (file)
--- a/po/ru.po
+++ b/po/ru.po
@@ -4,28 +4,28 @@
 #
 # Translators:
 # Alexander Golubev <fatzer2@gmail.com>, 2020
-# Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2022
+# Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2023
 # insolor, 2014
 # insolor, 2014
 # Sergey Alyoshin <alyoshin.s@gmail.com>, 2020-2021
 # Sergey Kuznetsov <votkinsk@gmail.com>, 2021-2022
-# Чук Таблицоменделеев <aurum444an@gmail.com>, 2019
+# Чук Таблицоменделеев (Aurum79) <aurum444an@gmail.com>, 2019
 msgid ""
 msgstr ""
 "Project-Id-Version: Git Russian Localization Project\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-06-26 20:30+0800\n"
+"POT-Creation-Date: 2023-06-30 12:55+0300\n"
 "PO-Revision-Date: 2014-02-18 23:30+0000\n"
-"Last-Translator: Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2022\n"
-"Language-Team: Russian (http://www.transifex.com/djm00n/git-po-ru/language/"
+"Last-Translator: Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2023\n"
+"Language-Team: Russian (http://app.transifex.com/djm00n/git-po-ru/language/"
 "ru/)\n"
-"Language: ru\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
-"%100>=11 && n%100<=14)? 2 : 3);\n"
+"Language: ru\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || "
+"(n%100>=11 && n%100<=14)? 2 : 3);\n"
 
 #, c-format
 msgid "Huh (%s)?"
@@ -53,7 +53,7 @@ msgstr " не удалось проиндексировать «%s»"
 msgid "could not write index"
 msgstr "не удалось записать индекс"
 
-#, c-format, perl-format
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "обновлён %d путь\n"
@@ -61,7 +61,7 @@ msgstr[1] "обновлено %d пути\n"
 msgstr[2] "обновлено %d путей\n"
 msgstr[3] "обновлено %d пути\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "примечание: %s теперь неотслеживаемый.\n"
 
@@ -75,7 +75,7 @@ msgstr "Обратить изменения"
 msgid "Could not parse HEAD^{tree}"
 msgstr "Не удалось разобрать HEAD^{tree}"
 
-#, c-format, perl-format
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "обращены изменения %d пути\n"
@@ -90,7 +90,7 @@ msgstr "Нет неотслеживаемых файлов.\n"
 msgid "Add untracked"
 msgstr "Добавить неотслеживаемый"
 
-#, c-format, perl-format
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "добавлен %d путь\n"
@@ -188,19 +188,19 @@ msgstr "не удалось обновить индекс"
 msgid "Bye.\n"
 msgstr "До свидания.\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "Проиндексировать изменение режима доступа [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Проиндексировать удаление [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Добавить к индексу [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Индексировать этот блок [y,n,q,a,d%s,?]? "
 
@@ -224,19 +224,19 @@ msgstr ""
 "a - индексировать этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "Спрятать изменение режима доступа [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Спрятать удаление файла [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Добавить к спрятанному [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Спрятать этот блок [y,n,q,a,d%s,?]? "
 
@@ -259,19 +259,19 @@ msgstr ""
 "a - спрятать этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "Убрать изменения режима доступа из индекса [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Убрать удаление из индекса [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Убрать из индекса [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Убрать из индекса этот блок [y,n,q,a,d%s,?]? "
 
@@ -289,25 +289,25 @@ msgid ""
 "a - unstage this hunk and all later hunks in the file\n"
 "d - do not unstage this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - убрать из индекса этот блок\n"
+"y - убрать этот блок из индекса\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - убрать из индекса этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "Применить изменение режима доступа к индексу [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Применить удаление к индексу [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Применить добавление к индексу [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Принять этот блок в индекс [y,n,q,a,d%s,?]? "
 
@@ -331,21 +331,21 @@ msgstr ""
 "a - принять этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить изменения режима доступа в рабочем каталоге [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить удаление в рабочем каталоге [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить добавления из рабочего каталога [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "Ð\9eÑ\82бÑ\80оÑ\81иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð¸Ð· Ñ\80абоÑ\87его Ð´ÐµÑ\80ева [y,n,q,a,d%s,?]?"
+msgstr "Ð\9eÑ\82бÑ\80оÑ\81иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð¸Ð· Ñ\80абоÑ\87его ÐºÐ°Ñ\82алога [y,n,q,a,d%s,?]?"
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -360,29 +360,29 @@ msgid ""
 "a - discard this hunk and all later hunks in the file\n"
 "d - do not discard this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - Ð¾Ñ\82бÑ\80оÑ\81иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð¸Ð· Ñ\80абоÑ\87его Ð´ÐµÑ\80ева\n"
+"y - Ð¾Ñ\82бÑ\80оÑ\81иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð¸Ð· Ñ\80абоÑ\87его ÐºÐ°Ñ\82алога\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - отбросить этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Отменить изменения режима доступа в индексе и рабочем каталоге [y,n,q,a,d"
-"%s,?]? "
+"Отменить изменения режима доступа в индексе и рабочем каталоге [y,n,q,a,"
+"d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить удаление в индексе и рабочем каталоге [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить добавление в индекс и рабочий каталог [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Ð\9eÑ\82бÑ\80оÑ\81иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð¸Ð· Ð¸Ð½Ð´ÐµÐºÑ\81а Ñ\80абоÑ\87его Ð´ÐµÑ\80ева [y,n,q,a,d%s,?]? "
+msgstr "Ð\9eÑ\82бÑ\80оÑ\81иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð¸Ð· Ð¸Ð½Ð´ÐµÐºÑ\81а Ñ\80абоÑ\87его ÐºÐ°Ñ\82алога [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - discard this hunk from index and worktree\n"
@@ -391,27 +391,27 @@ msgid ""
 "a - discard this hunk and all later hunks in the file\n"
 "d - do not discard this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - Ð¾Ñ\82бÑ\80оÑ\81иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð¸Ð· Ñ\80абоÑ\87его Ð´ÐµÑ\80ева\n"
+"y - Ð¾Ñ\82бÑ\80оÑ\81иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð¸Ð· Ñ\80абоÑ\87его ÐºÐ°Ñ\82алога\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - отбросить этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
-"Применить изменения режима доступа к индексу и рабочему каталогу [y,n,q,a,d"
-"%s,?]? "
+"Применить изменения режима доступа к индексу и рабочему каталогу [y,n,q,a,"
+"d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Применить удаление к индексу и рабочему каталогу [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Применить добавление к индексу и рабочему каталогу [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Принять этот блок в индекс и рабочий каталог [y,n,q,a,d%s,?]? "
 
@@ -422,12 +422,28 @@ msgid ""
 "a - apply this hunk and all later hunks in the file\n"
 "d - do not apply this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - Ð¿Ñ\80инÑ\8fÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð² Ð¸Ð½Ð´ÐµÐºÑ\81 Ð¸ Ñ\80абоÑ\87ее Ð´ÐµÑ\80ево\n"
+"y - Ð¿Ñ\80инÑ\8fÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð² Ð¸Ð½Ð´ÐµÐºÑ\81 Ð¸ Ñ\80абоÑ\87ий ÐºÐ°Ñ\82алог\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - принять этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
+#, c-format
+msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
+msgstr ""
+
+#, c-format
+msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
+msgstr ""
+
+#, c-format
+msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
+msgstr ""
+
+#, c-format
+msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
+msgstr "Принять этот блок в рабочий каталог [y,n,q,a,d%s,?]? "
+
 msgid ""
 "y - apply this hunk to worktree\n"
 "n - do not apply this hunk to worktree\n"
@@ -435,7 +451,7 @@ msgid ""
 "a - apply this hunk and all later hunks in the file\n"
 "d - do not apply this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - Ð¿Ñ\80инÑ\8fÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð² Ñ\80абоÑ\87ее Ð´ÐµÑ\80ево\n"
+"y - Ð¿Ñ\80инÑ\8fÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 Ð±Ð»Ð¾Ðº Ð² Ñ\80абоÑ\87ий ÐºÐ°Ñ\82алог\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - принять этот и остальные блоки файла\n"
@@ -445,10 +461,6 @@ msgstr ""
 msgid "could not parse hunk header '%.*s'"
 msgstr "не удалось разобрать заголовок блока изменений «%.*s»"
 
-#, c-format
-msgid "could not parse colored hunk header '%.*s'"
-msgstr "не удалось разобрать цветной заголовок блока изменений «%.*s»"
-
 msgid "could not parse diff"
 msgstr "не удалось разобрать список изменений (diff)"
 
@@ -457,15 +469,18 @@ msgstr "не удалось разобрать цветной список из
 
 #, c-format
 msgid "failed to run '%s'"
-msgstr ""
+msgstr "не удалось запустить «%s»"
 
 msgid "mismatched output from interactive.diffFilter"
-msgstr ""
+msgstr "несовпадение вывода из interactive.diffFilter"
 
 msgid ""
 "Your filter must maintain a one-to-one correspondence\n"
 "between its input and output lines."
 msgstr ""
+"Ваш фильтр должен поддерживать соответствие\n"
+"один-к-одному между его входными и выходными\n"
+"строками."
 
 #, c-format
 msgid ""
@@ -504,8 +519,6 @@ msgstr ""
 "Чтобы убрать «%c»-строки, удалите их.\n"
 "Строки, начинающиеся с %cбудут удалены.\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -517,25 +530,16 @@ msgstr ""
 "и блок останется без изменений.\n"
 
 msgid "could not parse hunk header"
-msgstr ""
+msgstr "не удалось разобрать заголовок блока изменений"
 
 msgid "'git apply --cached' failed"
-msgstr ""
+msgstr "сбой при выполнении «git apply --cached»"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
-#.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
@@ -543,7 +547,7 @@ msgstr ""
 "«нет»!) [y/n]? "
 
 msgid "The selected hunks do not apply to the index!"
-msgstr ""
+msgstr "Выбранные блоки не применяются без ошибок к индексу!"
 
 msgid "Apply them to the worktree anyway? "
 msgstr "Все равно применить их к рабочему каталогу? "
@@ -562,15 +566,24 @@ msgid ""
 "e - manually edit the current hunk\n"
 "? - print help\n"
 msgstr ""
+"j - не принимать решение по этому блоку, перейти на следующий нерешенный\n"
+"J - не принимать решение по этому блоку, перейти на следующий\n"
+"k - не принимать решение по этому блоку, перейти на предыдущий нерешенный\n"
+"K - не принимать решение по этому блоку, перейти на предыдущий\n"
+"g - выбрать блок изменений на который нужно перейти\n"
+"/ - поиск блока изменений с помощью регулярного выражения\n"
+"s - разделить текущий блок на блоки меньшего размера\n"
+"e - вручную отредактировать текущий блок\n"
+"? - вывести справку\n"
 
 msgid "No previous hunk"
-msgstr ""
+msgstr "Нет предыдущего блока"
 
 msgid "No next hunk"
-msgstr ""
+msgstr "Не следующего блока"
 
 msgid "No other hunks to goto"
-msgstr ""
+msgstr "Нет других блоков для перехода"
 
 msgid "go to which hunk (<ret> to see more)? "
 msgstr "на какой блок перейти (нажмите <ввод> чтобы увидеть еще)? "
@@ -580,41 +593,41 @@ msgstr "на какой блок перейти? "
 
 #, c-format
 msgid "Invalid number: '%s'"
-msgstr ""
+msgstr "Недопустимый номер: «%s»"
 
 #, c-format
 msgid "Sorry, only %d hunk available."
 msgid_plural "Sorry, only %d hunks available."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Простите, но только %d блок изменений доступнен."
+msgstr[1] "Простите, но только %d блока изменений доступно."
+msgstr[2] "Простите, но только %d блоков изменений доступно."
+msgstr[3] "Простите, но только %d блока изменений доступно."
 
 msgid "No other hunks to search"
-msgstr ""
+msgstr "Нет других блоков для поиска"
 
 msgid "search for regex? "
 msgstr "искать с помощью регулярного выражения? "
 
 #, c-format
 msgid "Malformed search regexp %s: %s"
-msgstr ""
+msgstr "Регулярное выражение для поиска в неправильном формате %s: %s"
 
 msgid "No hunk matches the given pattern"
-msgstr ""
+msgstr "Не найдены блоки, которые соответствуют указанному шаблону"
 
 msgid "Sorry, cannot split this hunk"
 msgstr ""
 
 #, c-format
 msgid "Split into %d hunks."
-msgstr ""
+msgstr "Разбито на %d блока изменений."
 
 msgid "Sorry, cannot edit this hunk"
 msgstr ""
 
 msgid "'git apply' failed"
-msgstr ""
+msgstr "сбой при выполнении «git apply»"
 
 #, c-format
 msgid ""
@@ -666,6 +679,16 @@ msgstr "Перед слиянием, выполните коммит ваших
 msgid "Exiting because of unfinished merge."
 msgstr "Выход из-за незавершенного слияния."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Быстрая перемотка невозможна, отменяем."
 
@@ -724,19 +747,35 @@ msgstr ""
 "advice.detachedHead в значение false\n"
 "\n"
 
+#, c-format
+msgid ""
+"The following paths have been moved outside the\n"
+"sparse-checkout definition but are not sparse due to local\n"
+"modifications.\n"
+msgstr ""
+
+msgid ""
+"To correct the sparsity of these paths, do the following:\n"
+"* Use \"git add --sparse <paths>\" to update the index\n"
+"* Use \"git sparse-checkout reapply\" to apply the sparsity rules"
+msgstr ""
+
 msgid "cmdline ends with \\"
 msgstr "командная строка заканчивается символом \\"
 
 msgid "unclosed quote"
 msgstr "пропущена закрывающая кавычка"
 
+msgid "too many arguments"
+msgstr ""
+
 #, c-format
 msgid "unrecognized whitespace option '%s'"
-msgstr "неопознанная опция для пробелов «%s»"
+msgstr "неопознанный параметр для пробелов «%s»"
 
 #, c-format
 msgid "unrecognized whitespace ignore option '%s'"
-msgstr "неопознанная опция для игнорирования пробелов «%s»"
+msgstr "неопознанный параметр для игнорирования пробелов «%s»"
 
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
@@ -1100,6 +1139,10 @@ msgstr "усечение имени .rej файла до %.*s.rej"
 msgid "cannot open %s"
 msgstr "не удалось открыть %s"
 
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr ""
+
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Блок №%d применен без ошибок."
@@ -1255,6 +1298,10 @@ msgstr "не удалось создать поток двоичного объ
 msgid "unsupported file mode: 0%o (SHA1: %s)"
 msgstr "неподдерживаемый режим доступа к файлу: 0%o (SHA1: %s)"
 
+#, c-format
+msgid "deflate error (%d)"
+msgstr "ошибка сжатия (%d)"
+
 #, c-format
 msgid "unable to start '%s' filter"
 msgstr "не удалось запустить фильтр «%s»"
@@ -1274,10 +1321,6 @@ msgstr "путь не является действительным UTF-8: %s"
 msgid "path too long (%d chars, SHA1: %s): %s"
 msgstr "путь слишком длинный (%d символов, SHA1: %s): %s"
 
-#, c-format
-msgid "deflate error (%d)"
-msgstr "ошибка сжатия (%d)"
-
 #, c-format
 msgid "timestamp too large for this system: %<PRIuMAX>"
 msgstr "отметка времени слишком большая для этой системы: %<PRIuMAX>"
@@ -1297,6 +1340,10 @@ msgstr "git archive --remote <репозиторий> [--exec <команда>]
 msgid "cannot read '%s'"
 msgstr "не удалось прочитать «%s»"
 
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr ""
+
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "спецификатор пути «%s» не соответствует ни одному файлу"
@@ -1313,9 +1360,6 @@ msgstr "недопустимое имя объекта: %s"
 msgid "not a tree object: %s"
 msgstr "недействительный объект дерева: %s"
 
-msgid "current working directory is untracked"
-msgstr "текущий рабочий каталог не отслеживается"
-
 #, c-format
 msgid "File not found: %s"
 msgstr ""
@@ -1366,6 +1410,12 @@ msgstr "читать .gitattributes в рабочем каталоге"
 msgid "report archived files on stderr"
 msgstr "отчет об архивированных файлах в stderr"
 
+msgid "time"
+msgstr "время"
+
+msgid "set modification time of archive entries"
+msgstr ""
+
 msgid "set compression level"
 msgstr ""
 
@@ -1406,6 +1456,13 @@ msgstr "Аргумент не поддерживается для формата
 msgid "%.*s is not a valid attribute name"
 msgstr "%.*s не является допустимым именем атрибута"
 
+msgid "unable to add additional attribute"
+msgstr ""
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr ""
+
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "%s не разрешено: %s:%d"
@@ -1418,6 +1475,21 @@ msgstr ""
 "Используйте «\\!» для буквального использования символа в значении "
 "«восклицательный знак»."
 
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr ""
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr ""
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr ""
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr ""
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Плохое содержимое файла «%s»: %s"
@@ -1514,7 +1586,6 @@ msgstr[3] "(примерно %d шагов)"
 
 #. TRANSLATORS: the last %s will be replaced with "(roughly %d
 #. steps)" translation.
-#.
 #, c-format
 msgid "Bisecting: %d revision left to test after this %s\n"
 msgid_plural "Bisecting: %d revisions left to test after this %s\n"
@@ -1526,9 +1597,6 @@ msgstr[3] "Бинарный поиск: %d редакций осталось п
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents и --reverse не очень сочетаются."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "нельзя использовать --contents с указанием финального имени объекта"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "при --reverse и --first-parent вместе нужно указывать конкретный последний "
@@ -1597,20 +1665,17 @@ msgstr ""
 #. TRANSLATORS: This is a line listing a remote with duplicate
 #. refspecs in the advice message below. For RTL languages you'll
 #. probably want to swap the "%s" and leading "  " space around.
-#.
 #. #-#-#-#-#  object-name.c.po  #-#-#-#-#
 #. TRANSLATORS: This is line item of ambiguous object output
 #. from describe_ambiguous_object() above. For RTL languages
 #. you'll probably want to swap the "%s" and leading " " space
 #. around.
-#.
 #, c-format
 msgid "  %s\n"
 msgstr ""
 
 #. TRANSLATORS: The second argument is a \n-delimited list of
 #. duplicate refspecs, composed above.
-#.
 #, c-format
 msgid ""
 "There are multiple remotes whose fetch refspecs map to the remote\n"
@@ -1680,8 +1745,8 @@ msgstr ""
 
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
 
 #, c-format
@@ -1692,10 +1757,6 @@ msgstr ""
 msgid "'%s' is already checked out at '%s'"
 msgstr "«%s» уже находится на «%s»"
 
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD рабочего каталога %s не обновлён"
-
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<опции>] [--] <спецификатор-пути>..."
 
@@ -1717,6 +1778,11 @@ msgstr "удалить «%s»\n"
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Непроиндексированные изменения после обновления индекса:"
 
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+
 msgid "Could not read the index"
 msgstr "Не удалось прочитать индекс"
 
@@ -2006,7 +2072,6 @@ msgstr "Тело коммита:"
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
-#.
 #, c-format
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "
 msgstr ""
@@ -2103,6 +2168,9 @@ msgstr "git am [<опции>] (--continue | --skip | --abort)"
 msgid "run interactively"
 msgstr "запустить в интерактивном режиме"
 
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr ""
+
 msgid "historical option -- no-op"
 msgstr "историческая опция — ничего не делает"
 
@@ -2245,31 +2313,24 @@ msgstr "git archive: ошибка протокола"
 msgid "git archive: expected a flush"
 msgstr "git archive: ожидался сброс буфера"
 
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr ""
-
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<термин> --term-{old,"
-"good}=<термин>] [--no-checkout] [--first-parent] [<плохая-редация> [<хорошая-"
-"редация>...]] [--] [<пути>...]"
 
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
+msgid "git bisect (good|bad) [<rev>...]"
 msgstr ""
 
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
+msgid "git bisect skip [(<rev>|<range>)...]"
 msgstr ""
 
-msgid "git bisect--helper --bisect-replay <filename>"
+msgid "git bisect reset [<commit>]"
 msgstr ""
 
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
+msgid "git bisect replay <logfile>"
 msgstr ""
 
-msgid "git bisect--helper --bisect-run <cmd>..."
+msgid "git bisect run <cmd>..."
 msgstr ""
 
 #, c-format
@@ -2348,7 +2409,6 @@ msgstr ""
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
-#.
 msgid "Are you sure [Y/n]? "
 msgstr "Вы уверены [Y - да/n - нет]? "
 
@@ -2406,9 +2466,6 @@ msgstr ""
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
 msgstr ""
 
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "нельзя выполнить двоичный поиск на дереве после cg-seek"
-
 msgid "bad HEAD - strange symbolic ref"
 msgstr ""
 
@@ -2422,7 +2479,6 @@ msgstr ""
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
-#.
 msgid "Do you want me to do it for you [Y/n]? "
 msgstr "Вы уверены, что хотите, чтобы я сделал это [Y] - да/[n] - нет? "
 
@@ -2460,7 +2516,7 @@ msgid "bisect run failed: no command provided."
 msgstr "сбой при выполнении двоичного поиска: не передана команда."
 
 #, c-format
-msgid "unable to verify '%s' on good revision"
+msgid "unable to verify %s on good revision"
 msgstr ""
 
 #, c-format
@@ -2468,7 +2524,7 @@ msgid "bogus exit code %d for good revision"
 msgstr ""
 
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
 msgstr ""
 
 #, c-format
@@ -2478,74 +2534,48 @@ msgstr ""
 msgid "bisect run cannot continue any more"
 msgstr "bisect run больше не может продолжать"
 
-#, c-format
 msgid "bisect run success"
 msgstr "bisect run выполнен успешно"
 
-#, c-format
 msgid "bisect found first bad commit"
 msgstr ""
 
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
-msgstr ""
-
-msgid "reset the bisection state"
-msgstr ""
-
-msgid "check whether bad or good terms exist"
-msgstr ""
-
-msgid "print out the bisect terms"
-msgstr ""
-
-msgid "start the bisect session"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
 msgstr ""
 
-msgid "find the next bisection commit"
-msgstr ""
-
-msgid "mark the state of ref (or refs)"
-msgstr ""
-
-msgid "list the bisection steps so far"
-msgstr ""
-
-msgid "replay the bisection process from the given file"
-msgstr ""
-
-msgid "skip some commits for checkout"
-msgstr ""
-
-msgid "visualize the bisection"
-msgstr ""
-
-msgid "use <cmd>... to automatically bisect"
+#, c-format
+msgid "'%s' requires either no argument or a commit"
 msgstr ""
 
-msgid "no log for BISECT_WRITE"
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
 msgstr ""
 
-msgid "--bisect-reset requires either no argument or a commit"
+#, c-format
+msgid "'%s' requires 0 arguments"
 msgstr ""
 
-msgid "--bisect-terms requires 0 or 1 argument"
+msgid "no logfile given"
 msgstr ""
 
-msgid "--bisect-next requires 0 arguments"
+#, c-format
+msgid "'%s' failed: no command provided."
 msgstr ""
 
-msgid "--bisect-log requires 0 arguments"
+msgid "need a command"
 msgstr ""
 
-msgid "no logfile given"
+#, c-format
+msgid "unknown command: '%s'"
 msgstr ""
 
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<опции>] [<опции-редакции>] [<редакция>] [--] <файл>"
 
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr ""
+
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<опции-rev-list> документированы в git-rev-list(1)"
 
@@ -2659,7 +2689,6 @@ msgstr ""
 #. among various forms of relative timestamps, but
 #. your language may need more or fewer display
 #. columns.
-#.
 msgid "4 years, 11 months ago"
 msgstr "4 года и 11 месяцев назад"
 
@@ -2734,9 +2763,6 @@ msgstr "Не удалось обновить файл конфигурации"
 msgid "cannot use -a with -d"
 msgstr "нельзя использовать одновременно ключи -a и -d"
 
-msgid "Couldn't look up commit object for HEAD"
-msgstr "Не удалось найти объект коммита для HEAD"
-
 #, c-format
 msgid "Cannot delete branch '%s' checked out at '%s'"
 msgstr "Нельзя удалить ветку «%s» т.к. она активна на «%s»"
@@ -2745,6 +2771,12 @@ msgstr "Нельзя удалить ветку «%s» т.к. она активн
 msgid "remote-tracking branch '%s' not found."
 msgstr "внешняя отслеживаемая ветка «%s» не найдена."
 
+#, c-format
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+
 #, c-format
 msgid "branch '%s' not found."
 msgstr "ветка «%s» не найдена."
@@ -2775,20 +2807,22 @@ msgstr "Производится перемещение ветки %s на %s"
 msgid "Branch %s is being bisected at %s"
 msgstr "Производится двоичный поиск в ветке %s на %s"
 
-msgid "cannot copy the current branch while not on any."
-msgstr ""
-"невозможно скопировать текущую ветку, если вы не находитесь ни на одной из "
-"веток."
-
-msgid "cannot rename the current branch while not on any."
-msgstr ""
-"невозможно переименовать текущую ветку, если вы не находитесь ни на одной из "
-"них."
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD рабочего каталога %s не обновлён"
 
 #, c-format
 msgid "Invalid branch name: '%s'"
 msgstr "Недействительное имя ветки: «%s»"
 
+#, c-format
+msgid "No commit on branch '%s' yet."
+msgstr "Еще нет коммита на ветке «%s»."
+
+#, c-format
+msgid "No branch named '%s'."
+msgstr "Нет ветки с именем «%s»."
+
 msgid "Branch rename failed"
 msgstr "Сбой переименования ветки"
 
@@ -2877,6 +2911,9 @@ msgstr "переместить/переименовать ветки и ее ж
 msgid "move/rename a branch, even if target exists"
 msgstr "переместить/переименовать ветку, даже если целевое имя уже существует"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr ""
+
 msgid "copy a branch and its reflog"
 msgstr "скопировать ветку и её журнал ссылок"
 
@@ -2945,13 +2982,15 @@ msgstr "Нельзя дать описание отсоединённому ук
 msgid "cannot edit description of more than one branch"
 msgstr "нельзя изменить описание более одной ветки за раз"
 
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Еще нет коммита на ветке «%s»."
+msgid "cannot copy the current branch while not on any."
+msgstr ""
+"невозможно скопировать текущую ветку, если вы не находитесь ни на одной из "
+"веток."
 
-#, c-format
-msgid "No branch named '%s'."
-msgstr "Нет ветки с именем «%s»."
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+"невозможно переименовать текущую ветку, если вы не находитесь ни на одной из "
+"них."
 
 msgid "too many branches for a copy operation"
 msgstr "слишком много веток для операции копирования"
@@ -3017,7 +3056,9 @@ msgstr ""
 msgid "not run from a git repository - no hooks to show\n"
 msgstr ""
 
-msgid "git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"
+msgid ""
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
 
 msgid ""
@@ -3038,16 +3079,27 @@ msgid ""
 "You can delete any lines you don't wish to share.\n"
 msgstr ""
 
-msgid "specify a destination for the bugreport file"
+msgid "mode"
+msgstr "режим"
+
+msgid ""
+"create an additional zip archive of detailed diagnostics (default 'stats')"
 msgstr ""
 
-msgid "specify a strftime format suffix for the filename"
+msgid "specify a destination for the bugreport file(s)"
+msgstr ""
+
+msgid "specify a strftime format suffix for the filename(s)"
 msgstr ""
 
 #, c-format
 msgid "could not create leading directories for '%s'"
 msgstr ""
 
+#, c-format
+msgid "unable to create diagnostics archive %s"
+msgstr ""
+
 msgid "System Info"
 msgstr ""
 
@@ -3062,16 +3114,21 @@ msgstr ""
 msgid "Created new report at '%s'.\n"
 msgstr ""
 
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
 
-msgid "git bundle verify [<options>] <file>"
+msgid "git bundle verify [-q | --quiet] <file>"
 msgstr ""
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr ""
 
-msgid "git bundle unbundle <file> [<refname>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr ""
+
+msgid "need a <file> argument"
 msgstr ""
 
 msgid "do not show progress meter"
@@ -3080,11 +3137,11 @@ msgstr "не выводить прогресс выполнения"
 msgid "show progress meter"
 msgstr "показать прогресс выполнения"
 
-msgid "show progress meter during object writing phase"
-msgstr "показать прогресс выполнения во время записи объектов"
+msgid "historical; same as --progress"
+msgstr ""
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "похоже на --all-progress при включенном прогрессе выполнения"
+msgid "historical; does nothing"
+msgstr ""
 
 msgid "specify bundle format version"
 msgstr ""
@@ -3105,10 +3162,6 @@ msgstr "Требуется репозиторий для распаковки."
 msgid "Unbundling objects"
 msgstr ""
 
-#, c-format
-msgid "Unknown subcommand: %s"
-msgstr "Неизвестная подкоманда: %s"
-
 #, c-format
 msgid "cannot read object %s '%s'"
 msgstr "невозможно прочитать объект %s «%s»"
@@ -3131,10 +3184,6 @@ msgstr ""
 msgid "%s takes no arguments"
 msgstr ""
 
-#, c-format
-msgid "unknown command: '%s'"
-msgstr ""
-
 msgid "only one batch option may be specified"
 msgstr ""
 
@@ -3151,7 +3200,7 @@ msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-z]"
 msgstr ""
 
 msgid ""
@@ -3180,6 +3229,9 @@ msgstr "показать размер объекта"
 msgid "allow -s and -t to work with broken/corrupt objects"
 msgstr "разрешить -s и -t работать с повреждёнными объектами"
 
+msgid "use mail map file"
+msgstr ""
+
 msgid "Batch objects requested on stdin (or --batch-all-objects)"
 msgstr ""
 
@@ -3189,6 +3241,9 @@ msgstr ""
 msgid "like --batch, but don't emit <contents>"
 msgstr ""
 
+msgid "stdin is NUL-terminated"
+msgstr ""
+
 msgid "read commands from stdin"
 msgstr ""
 
@@ -3250,18 +3305,18 @@ msgstr ""
 msgid "<object> required with '-%c'"
 msgstr ""
 
-msgid "too many arguments"
-msgstr ""
-
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr ""
 
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <атрибут>...] [--] <путь>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
 
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <атрибут>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
 
 msgid "report all attributes set on file"
 msgstr "вывести все атрибуты установленные для файла"
@@ -3275,6 +3330,12 @@ msgstr "прочитать имена файлов из стандартного
 msgid "terminate input and output records by a NUL character"
 msgstr "окончание ввода и вывода записей по НУЛЕВОМУ символу"
 
+msgid "<tree-ish>"
+msgstr ""
+
+msgid "which tree-ish to check attributes at"
+msgstr ""
+
 msgid "suppress progress reporting"
 msgstr "не выводить прогресс выполнения"
 
@@ -3787,9 +3848,9 @@ msgid "use overlay mode"
 msgstr ""
 
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <шаблон>] [-x | -X] [--] <пути>..."
 
 #, c-format
 msgid "Removing %s\n"
@@ -3853,7 +3914,7 @@ msgstr ""
 "*          - выбрать все элементы\n"
 "           - (пусто) завершить выделение\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Хм (%s)?\n"
 
@@ -4006,9 +4067,6 @@ msgstr "глубина"
 msgid "create a shallow clone of that depth"
 msgstr "сделать частичный клон указанной глубины"
 
-msgid "time"
-msgstr "время"
-
 msgid "create a shallow clone since a specific time"
 msgstr "сделать частичный клон до определенного времени"
 
@@ -4062,6 +4120,12 @@ msgstr ""
 msgid "initialize sparse-checkout file to include only files at root"
 msgstr ""
 
+msgid "uri"
+msgstr ""
+
+msgid "a URI for downloading bundles before fetching from origin remote"
+msgstr ""
+
 #, c-format
 msgid "info: Could not add alternate for '%s': %s\n"
 msgstr "информация: Не удалось добавить альтернативу для «%s»: %s\n"
@@ -4074,10 +4138,18 @@ msgstr "не удалось выполнить stat «%s»"
 msgid "%s exists and is not a directory"
 msgstr "%s уже существует и не является каталогом"
 
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr ""
+
 #, c-format
 msgid "failed to start iterator over '%s'"
 msgstr ""
 
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr ""
+
 #, c-format
 msgid "failed to unlink '%s'"
 msgstr "сбой отсоединения «%s»"
@@ -4124,10 +4196,8 @@ msgstr "не удалось обновить %s"
 msgid "failed to initialize sparse-checkout"
 msgstr ""
 
-msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
 msgstr ""
-"внешний HEAD ссылается на несуществующую ссылку, нельзя переключиться на "
-"такое состояние.\n"
 
 msgid "unable to checkout working tree"
 msgstr "не удалось переключиться на версию в рабочем каталоге"
@@ -4147,8 +4217,9 @@ msgstr "Слишком много аргументов."
 msgid "You must specify a repository to clone."
 msgstr "Вы должны указать репозиторий для клонирования."
 
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
+msgid ""
+"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
+"exclude"
 msgstr ""
 
 #, c-format
@@ -4227,6 +4298,16 @@ msgstr "--local игнорируется"
 msgid "cannot clone from filtered bundle"
 msgstr ""
 
+msgid "failed to initialize the repo, skipping bundle URI"
+msgstr ""
+
+#, c-format
+msgid "failed to fetch objects from bundle URI '%s'"
+msgstr ""
+
+msgid "failed to fetch advertised bundles"
+msgstr ""
+
 msgid "remote transport reported error"
 msgstr ""
 
@@ -4262,13 +4343,16 @@ msgid "--command must be the first argument"
 msgstr "параметр --command должен быть первым"
 
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
 
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 msgstr ""
 
 msgid "dir"
@@ -4337,13 +4421,12 @@ msgstr ""
 msgid "Collecting commits from input"
 msgstr ""
 
-#, c-format
-msgid "unrecognized subcommand: %s"
+msgid "git commit-tree <tree> [(-p <parent>)...]"
 msgstr ""
 
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
 
 #, c-format
@@ -4386,11 +4469,20 @@ msgstr "необходимо указать одно дерево"
 msgid "git commit-tree: failed to read"
 msgstr "git commit-tree: сбой при чтении"
 
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<опции>] [--] <спецификатор-пути>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
 
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<опции>] [--] <спецификатор-пути>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr ""
 
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -4672,9 +4764,6 @@ msgstr "показать статус в длинном формате (по у
 msgid "terminate entries with NUL"
 msgstr "завершать записи НУЛЕВЫМ байтом"
 
-msgid "mode"
-msgstr "режим"
-
 msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
 msgstr ""
 "показать неотслеживаемые файлы, опциональные режимы: all (все), normal (как "
@@ -4747,7 +4836,6 @@ msgstr "использовать сообщение указанного ком
 
 #. TRANSLATORS: Leave "[(amend|reword):]" as-is,
 #. and only translate <commit>.
-#.
 msgid "[(amend|reword):]commit"
 msgstr ""
 
@@ -5136,11 +5224,16 @@ msgstr ""
 msgid "unable to get credential storage lock in %d ms"
 msgstr ""
 
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<опции>] [<указатель-коммита>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
 
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<опции>] --dirty"
+msgid "git describe <blob>"
+msgstr ""
 
 msgid "head"
 msgstr "указатель на ветку"
@@ -5262,6 +5355,20 @@ msgstr "Имена не найдены, не могу ничего описат
 msgid "option '%s' and commit-ishes cannot be used together"
 msgstr ""
 
+msgid ""
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
+msgstr ""
+
+msgid "specify a destination for the diagnostics archive"
+msgstr ""
+
+msgid "specify a strftime format suffix for the filename"
+msgstr ""
+
+msgid "specify the content of the diagnostic archive"
+msgstr ""
+
 msgid "--merge-base only works with two commits"
 msgstr ""
 
@@ -5269,6 +5376,9 @@ msgstr ""
 msgid "'%s': not a regular file or symlink"
 msgstr "«%s»: не является обычным файлом или символьной ссылкой"
 
+msgid "no merge given, only parents."
+msgstr ""
+
 #, c-format
 msgid "invalid option: %s"
 msgstr "неправильный параметр: %s"
@@ -5380,25 +5490,6 @@ msgstr "не передана <утилита> для --tool=<утилита>"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "не передана <команда> для --extcmd=<команда>"
 
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr ""
-
-msgid "default for git_env_*(...) to fall back on"
-msgstr ""
-
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr ""
-
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr ""
-
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not `"
-"%s`"
-msgstr ""
-
 msgid "git fast-export [<rev-list-opts>]"
 msgstr ""
 
@@ -5507,113 +5598,13 @@ msgstr "git fetch --all [<опции>]"
 msgid "fetch.parallel cannot be negative"
 msgstr ""
 
-msgid "fetch from all remotes"
-msgstr "извлечь со всех внешних репозиториев"
-
-msgid "set upstream for git pull/fetch"
-msgstr ""
-
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "дописать к .git/FETCH_HEAD вместо перезаписи"
-
-msgid "use atomic transaction to update references"
-msgstr ""
-
-msgid "path to upload pack on remote end"
-msgstr "путь к программе упаковки пакета на машине с внешним репозиторием"
-
-msgid "force overwrite of local reference"
-msgstr ""
-
-msgid "fetch from multiple remotes"
-msgstr "извлечь с нескольких внешних репозиториев"
-
-msgid "fetch all tags and associated objects"
-msgstr "извлечь все метки и связанные объекты"
-
-msgid "do not fetch all tags (--no-tags)"
-msgstr "не извлекать все метки (--no-tags)"
-
-msgid "number of submodules fetched in parallel"
-msgstr "количество подмодулей, которые будут скачаны парралельно"
-
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr ""
-
-msgid "prune remote-tracking branches no longer on remote"
-msgstr ""
-"почистить отслеживаемые внешние ветки, которых уже нет на внешнем репозитории"
-
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr ""
-
-msgid "on-demand"
-msgstr "по требованию"
-
-msgid "control recursive fetching of submodules"
-msgstr "управление рекурсивным извлечением подмодулей"
-
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr ""
-
-msgid "keep downloaded pack"
-msgstr "оставить загруженный пакет данных"
-
-msgid "allow updating of HEAD ref"
-msgstr "разрешить обновление указателя HEAD"
-
-msgid "deepen history of shallow clone"
-msgstr "углубить историю частичного клона"
-
-msgid "deepen history of shallow repository based on time"
-msgstr "углубить историю частичного клона основываясь на времени"
-
-msgid "convert to a complete repository"
-msgstr "преобразовать в полный репозиторий"
-
-msgid "re-fetch without negotiating common commits"
-msgstr ""
-
-msgid "prepend this to submodule path output"
-msgstr "присоединять это спереди к выводу путей подмодуля"
-
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr ""
-"настроить по умолчанию рекурсивное извлечение подмодулей (более низкий "
-"приоритет, чем файлы конфигурации)"
-
-msgid "accept refs that update .git/shallow"
-msgstr "принимать ссылки, которые обновляют .git/shallow"
-
-msgid "refmap"
-msgstr "соответствие-ссылок"
-
-msgid "specify fetch refmap"
-msgstr "указать соответствие ссылок при извлечении"
-
-msgid "report that we have only objects reachable from this object"
-msgstr ""
-
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr ""
-
-msgid "run 'maintenance --auto' after fetching"
-msgstr ""
-
-msgid "check for forced-updates on all updated branches"
-msgstr ""
-
-msgid "write the commit-graph after fetching"
-msgstr ""
-
-msgid "accept refspecs from stdin"
-msgstr ""
-
 msgid "couldn't find remote ref HEAD"
 msgstr ""
 
+#, c-format
+msgid "From %.*s\n"
+msgstr "Из %.*s\n"
+
 #, c-format
 msgid "object %s not found"
 msgstr "объект %s не найден"
@@ -5624,10 +5615,7 @@ msgstr "[актуально]"
 msgid "[rejected]"
 msgstr "[отклонено]"
 
-msgid "can't fetch in current branch"
-msgstr "нельзя извлечь текущую ветку"
-
-msgid "checked out in another worktree"
+msgid "can't fetch into checked-out branch"
 msgstr ""
 
 msgid "[tag update]"
@@ -5680,10 +5668,6 @@ msgstr "%s не отправил все необходимые объекты\n"
 msgid "rejected %s because shallow roots are not allowed to be updated"
 msgstr ""
 
-#, c-format
-msgid "From %.*s\n"
-msgstr "Из %.*s\n"
-
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -5769,72 +5753,180 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr ""
 
-msgid "--negotiate-only needs one or more --negotiation-tip=*"
-msgstr ""
+msgid "fetch from all remotes"
+msgstr "извлечь со всех внешних репозиториев"
 
-msgid "negative depth in --deepen is not supported"
+msgid "set upstream for git pull/fetch"
 msgstr ""
 
-msgid "--unshallow on a complete repository does not make sense"
-msgstr "--unshallow не имеет смысла на полном репозитории"
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "дописать к .git/FETCH_HEAD вместо перезаписи"
 
-msgid "fetch --all does not take a repository argument"
-msgstr "fetch --all не принимает имя репозитория как аргумент"
+msgid "use atomic transaction to update references"
+msgstr ""
 
-msgid "fetch --all does not make sense with refspecs"
-msgstr "fetch --all не имеет смысла при указании спецификаторов ссылок"
+msgid "path to upload pack on remote end"
+msgstr "путь к программе упаковки пакета на машине с внешним репозиторием"
 
-#, c-format
-msgid "no such remote or remote group: %s"
+msgid "force overwrite of local reference"
 msgstr ""
 
-msgid "fetching a group and specifying refspecs does not make sense"
-msgstr ""
+msgid "fetch from multiple remotes"
+msgstr "извлечь с нескольких внешних репозиториев"
 
-msgid "must supply remote when using --negotiate-only"
-msgstr ""
+msgid "fetch all tags and associated objects"
+msgstr "извлечь все метки и связанные объекты"
 
-msgid "protocol does not support --negotiate-only, exiting"
-msgstr ""
+msgid "do not fetch all tags (--no-tags)"
+msgstr "не извлекать все метки (--no-tags)"
 
-msgid ""
-"--filter can only be used with the remote configured in extensions."
-"partialclone"
-msgstr ""
+msgid "number of submodules fetched in parallel"
+msgstr "количество подмодулей, которые будут скачаны парралельно"
 
-msgid "--atomic can only be used when fetching from one remote"
+msgid "modify the refspec to place all refs within refs/prefetch/"
 msgstr ""
 
-msgid "--stdin can only be used when fetching from one remote"
+msgid "prune remote-tracking branches no longer on remote"
 msgstr ""
+"почистить отслеживаемые внешние ветки, которых уже нет на внешнем репозитории"
 
-msgid ""
-"git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"
+msgid "prune local tags no longer on remote and clobber changed tags"
 msgstr ""
-"git fmt-merge-msg [-m <сообщение>] [--log[=<n>] | --no-log] [--file <файл>]"
-
-msgid "populate log with at most <n> entries from shortlog"
-msgstr "отправить в журнал <n> записей из короткого журнала"
-
-msgid "alias for --log (deprecated)"
-msgstr "сокращение для --log (устаревшее)"
 
-msgid "text"
-msgstr "текст"
+msgid "on-demand"
+msgstr "по требованию"
 
-msgid "use <text> as start of message"
-msgstr "использовать <текст> как начальное сообщение"
+msgid "control recursive fetching of submodules"
+msgstr "управление рекурсивным извлечением подмодулей"
 
-msgid "use <name> instead of the real target branch"
+msgid "write fetched references to the FETCH_HEAD file"
 msgstr ""
 
-msgid "file to read from"
-msgstr "файл для чтения"
+msgid "keep downloaded pack"
+msgstr "оставить загруженный пакет данных"
 
-msgid "git for-each-ref [<options>] [<pattern>]"
-msgstr "git for-each-ref [<опции>] [<шаблон>]"
+msgid "allow updating of HEAD ref"
+msgstr "разрешить обновление указателя HEAD"
 
-msgid "git for-each-ref [--points-at <object>]"
+msgid "deepen history of shallow clone"
+msgstr "углубить историю частичного клона"
+
+msgid "deepen history of shallow repository based on time"
+msgstr "углубить историю частичного клона основываясь на времени"
+
+msgid "convert to a complete repository"
+msgstr "преобразовать в полный репозиторий"
+
+msgid "re-fetch without negotiating common commits"
+msgstr ""
+
+msgid "prepend this to submodule path output"
+msgstr "присоединять это спереди к выводу путей подмодуля"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"настроить по умолчанию рекурсивное извлечение подмодулей (более низкий "
+"приоритет, чем файлы конфигурации)"
+
+msgid "accept refs that update .git/shallow"
+msgstr "принимать ссылки, которые обновляют .git/shallow"
+
+msgid "refmap"
+msgstr "соответствие-ссылок"
+
+msgid "specify fetch refmap"
+msgstr "указать соответствие ссылок при извлечении"
+
+msgid "report that we have only objects reachable from this object"
+msgstr ""
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr ""
+
+msgid "check for forced-updates on all updated branches"
+msgstr ""
+
+msgid "write the commit-graph after fetching"
+msgstr ""
+
+msgid "accept refspecs from stdin"
+msgstr ""
+
+msgid "--negotiate-only needs one or more --negotiation-tip=*"
+msgstr ""
+
+msgid "negative depth in --deepen is not supported"
+msgstr ""
+
+msgid "--unshallow on a complete repository does not make sense"
+msgstr "--unshallow не имеет смысла на полном репозитории"
+
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr ""
+
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all не принимает имя репозитория как аргумент"
+
+msgid "fetch --all does not make sense with refspecs"
+msgstr "fetch --all не имеет смысла при указании спецификаторов ссылок"
+
+#, c-format
+msgid "no such remote or remote group: %s"
+msgstr ""
+
+msgid "fetching a group and specifying refspecs does not make sense"
+msgstr ""
+
+msgid "must supply remote when using --negotiate-only"
+msgstr ""
+
+msgid "protocol does not support --negotiate-only, exiting"
+msgstr ""
+
+msgid ""
+"--filter can only be used with the remote configured in extensions."
+"partialclone"
+msgstr ""
+
+msgid "--atomic can only be used when fetching from one remote"
+msgstr ""
+
+msgid "--stdin can only be used when fetching from one remote"
+msgstr ""
+
+msgid ""
+"git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"
+msgstr ""
+"git fmt-merge-msg [-m <сообщение>] [--log[=<n>] | --no-log] [--file <файл>]"
+
+msgid "populate log with at most <n> entries from shortlog"
+msgstr "отправить в журнал <n> записей из короткого журнала"
+
+msgid "alias for --log (deprecated)"
+msgstr "сокращение для --log (устаревшее)"
+
+msgid "text"
+msgstr "текст"
+
+msgid "use <text> as start of message"
+msgstr "использовать <текст> как начальное сообщение"
+
+msgid "use <name> instead of the real target branch"
+msgstr ""
+
+msgid "file to read from"
+msgstr "файл для чтения"
+
+msgid "git for-each-ref [<options>] [<pattern>]"
+msgstr "git for-each-ref [<опции>] [<шаблон>]"
+
+msgid "git for-each-ref [--points-at <object>]"
 msgstr "git for-each-ref [--points-at <объект>]"
 
 msgid "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"
@@ -5878,7 +5970,13 @@ msgstr "вывод только ссылок, которые содержат к
 msgid "print only refs which don't contain the commit"
 msgstr "вывод только ссылок, которые не содержат коммит"
 
-msgid "git for-each-repo --config=<config> <command-args>"
+msgid "read reference patterns from stdin"
+msgstr ""
+
+msgid "unknown arguments supplied with --stdin"
+msgstr ""
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr ""
 
 msgid "config"
@@ -5890,6 +5988,10 @@ msgstr ""
 msgid "missing --config=<config>"
 msgstr ""
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr ""
+
 msgid "unknown"
 msgstr ""
 
@@ -6034,18 +6136,35 @@ msgstr "%s: отсоединённый указатель HEAD ни на что
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr ""
 
-msgid "Checking cache tree"
+#, c-format
+msgid "Checking cache tree of %s"
 msgstr ""
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
 msgstr ""
 
 msgid "non-tree in cache-tree"
 msgstr ""
 
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<опции>] [<объект>...]"
+#, c-format
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr ""
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr ""
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr ""
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
 
 msgid "show unreachable objects"
 msgstr "показать недоступные объекты"
@@ -6100,12 +6219,6 @@ msgstr ""
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr ""
 
-msgid "git fsmonitor--daemon stop"
-msgstr ""
-
-msgid "git fsmonitor--daemon status"
-msgstr ""
-
 #, c-format
 msgid "value of '%s' out of range: %d"
 msgstr ""
@@ -6342,7 +6455,19 @@ msgstr ""
 msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr ""
 
-msgid "failed to run 'git config'"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr ""
+
+msgid "return success even if repository was not registered"
+msgstr ""
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr ""
+
+#, c-format
+msgid "repository '%s' is not registered"
 msgstr ""
 
 #, c-format
@@ -6369,10 +6494,13 @@ msgstr ""
 msgid "failed to run 'crontab -l'; your system might not support 'cron'"
 msgstr ""
 
-msgid "failed to run 'crontab'; your system might not support 'cron'"
+msgid "failed to create crontab temporary file"
+msgstr ""
+
+msgid "failed to open temporary file"
 msgstr ""
 
-msgid "failed to open stdin of 'crontab'"
+msgid "failed to run 'crontab'; your system might not support 'cron'"
 msgstr ""
 
 msgid "'crontab' died"
@@ -6421,10 +6549,6 @@ msgstr ""
 msgid "git maintenance <subcommand> [<options>]"
 msgstr ""
 
-#, c-format
-msgid "invalid subcommand: %s"
-msgstr ""
-
 msgid "git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"
 msgstr "git grep [<опции>] [-e] <шаблон> [<редакция>...] [[--] <путь>...]"
 
@@ -6440,7 +6564,6 @@ msgstr "указано недопустимое количество поток
 #. TRANSLATORS: %s is the configuration
 #. variable for tweaking threads, currently
 #. grep.threads
-#.
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "нет поддержки потоков, игнорирование %s"
@@ -6595,6 +6718,9 @@ msgstr "показать совпадающие файлы с помощью п
 msgid "allow calling of grep(1) (ignored by this build)"
 msgstr "разрешить вызов grep(1) (игнорируется в этой сборке)"
 
+msgid "maximum number of results per file"
+msgstr ""
+
 msgid "no pattern given"
 msgstr ""
 
@@ -6630,11 +6756,12 @@ msgid "both --cached and trees are given"
 msgstr ""
 
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
+msgstr ""
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
 msgstr ""
-"git hash-object [-t <тип>] [-w] [--path=<файл> | --no-filters] [--stdin] "
-"[--] <файл>..."
 
 msgid "object type"
 msgstr "тип объекта"
@@ -6683,10 +6810,16 @@ msgstr ""
 msgid "print list of useful guides"
 msgstr "вывести список полезных руководств"
 
+msgid "print list of user-facing repository, command and file interfaces"
+msgstr ""
+
+msgid "print list of file formats, protocols and other developer interfaces"
+msgstr ""
+
 msgid "print all configuration variable names"
 msgstr ""
 
-msgid "git help [[-i|--info] [-m|--man] [-w|--web]] [<command>]"
+msgid "git help [[-i|--info] [-m|--man] [-w|--web]] [<command>|<doc>]"
 msgstr ""
 
 #, c-format
@@ -6756,12 +6889,17 @@ msgstr "использование: %s%s"
 msgid "'git help config' for more information"
 msgstr ""
 
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
 msgstr ""
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr ""
 
+msgid "file to read into hooks' stdin"
+msgstr ""
+
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "несоответствие типа объекта на %s"
@@ -7065,11 +7203,11 @@ msgid "Initialized empty Git repository in %s%s\n"
 msgstr "Инициализирован пустой репозиторий Git в %s%s\n"
 
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<каталог-шаблонов>] [--"
-"shared[=<права-доступа>]] [<каталог>]"
 
 msgid "permissions"
 msgstr "права-доступа"
@@ -7111,11 +7249,10 @@ msgid "--separate-git-dir incompatible with bare repository"
 msgstr ""
 
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<ключ>[(=|:)<значение>])...] [<файл>...]"
 
 msgid "edit files in place"
 msgstr "редактировать файлы на месте"
@@ -7172,7 +7309,7 @@ msgstr "не выводить список изменений"
 msgid "show source"
 msgstr "показать источник"
 
-msgid "use mail map file"
+msgid "clear all previously-defined decoration filters"
 msgstr ""
 
 msgid "only decorate refs that match <pattern>"
@@ -7422,6 +7559,9 @@ msgstr ""
 msgid "percentage by which creation is weighted"
 msgstr ""
 
+msgid "show in-body From: even if identical to the e-mail header"
+msgstr ""
+
 #, c-format
 msgid "invalid ident line: %s"
 msgstr "неправильная строка идентификации: %s"
@@ -7482,6 +7622,18 @@ msgstr ""
 "Не удалось найти отслеживаемую внешнюю ветку, укажите <вышестоящую-ветку> "
 "вручную.\n"
 
+#, c-format
+msgid "bad ls-files format: element '%s' does not start with '('"
+msgstr ""
+
+#, c-format
+msgid "bad ls-files format: element '%s' does not end in ')'"
+msgstr ""
+
+#, c-format
+msgid "bad ls-files format: %%%.*s"
+msgstr ""
+
 msgid "git ls-files [<options>] [<file>...]"
 msgstr "git ls-files [<опции>] [<файл>...]"
 
@@ -7568,10 +7720,15 @@ msgstr ""
 msgid "show sparse directories in the presence of a sparse index"
 msgstr ""
 
+msgid ""
+"--format cannot be used with -s, -o, -k, -t, --resolve-undo, --deduplicate, "
+"--eol"
+msgstr ""
+
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 
 msgid "do not print remote URL"
@@ -7704,12 +7861,12 @@ msgstr "git merge-base [-a | --all] <коммит> <коммит>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <коммит>..."
 
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <коммит>..."
-
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <коммит> <коммит>"
 
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <коммит>..."
+
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <ссылка> [<коммит>]"
 
@@ -7789,6 +7946,56 @@ msgstr "не удалось распознать ссылку «%s»"
 msgid "Merging %s with %s\n"
 msgstr "Слияние %s и %s\n"
 
+msgid "not something we can merge"
+msgstr "не является тем, что можно слить"
+
+msgid "refusing to merge unrelated histories"
+msgstr "отказ слияния несвязанных историй изменений"
+
+msgid "failure to merge"
+msgstr ""
+
+msgid "git merge-tree [--write-tree] [<options>] <branch1> <branch2>"
+msgstr ""
+
+msgid "git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"
+msgstr ""
+
+msgid "do a real merge instead of a trivial merge"
+msgstr ""
+
+msgid "do a trivial merge only"
+msgstr ""
+
+msgid "also show informational/conflict messages"
+msgstr ""
+
+msgid "list filenames without modes/oids/stages"
+msgstr ""
+
+msgid "allow merging unrelated histories"
+msgstr "разрешить слияние несвязанных историй изменений"
+
+msgid "perform multiple merges, one per line of input"
+msgstr ""
+
+msgid "specify a merge-base for the merge"
+msgstr ""
+
+msgid "--trivial-merge is incompatible with all other options"
+msgstr ""
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr ""
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "неправильная введенная строка: «%s»."
+
+#, c-format
+msgid "merging cannot continue; got unclean result of %d"
+msgstr ""
+
 msgid "git merge [<options>] [<commit>...]"
 msgstr "git merge [<опции>] [<коммит>...]"
 
@@ -7870,9 +8077,6 @@ msgstr ""
 msgid "continue the current in-progress merge"
 msgstr "продолжить выполнение происходящего слияния"
 
-msgid "allow merging unrelated histories"
-msgstr "разрешить слияние несвязанных историй изменений"
-
 msgid "bypass pre-merge-commit and commit-msg hooks"
 msgstr ""
 
@@ -7994,9 +8198,6 @@ msgstr "не удалось закрыть «%s»"
 msgid "not something we can merge in %s: %s"
 msgstr "не является тем, что можно слить в %s: %s"
 
-msgid "not something we can merge"
-msgstr "не является тем, что можно слить"
-
 msgid "--abort expects no arguments"
 msgstr "опция --abort не принимает аргументы"
 
@@ -8046,13 +8247,16 @@ msgstr "%s не является тем, что можно слить"
 msgid "Can merge only exactly one commit into empty head"
 msgstr "Можно только один коммит в пустую ветку."
 
-msgid "refusing to merge unrelated histories"
-msgstr "отказ слияния несвязанных историй изменений"
-
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr "Обновление %s..%s\n"
 
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by merge:\n"
+"  %s"
+msgstr ""
+
 #, c-format
 msgid "Trying really trivial in-index merge...\n"
 msgstr "Попытка тривиального слияния в индексе...\n"
@@ -8087,6 +8291,10 @@ msgstr ""
 "Автоматическое слияние прошло успешно; как и запрашивали, остановлено перед "
 "выполнением коммита\n"
 
+#, c-format
+msgid "When finished, apply stashed changes with `git stash pop`\n"
+msgstr ""
+
 #, c-format
 msgid "warning: tag input does not pass fsck: %s"
 msgstr ""
@@ -8201,6 +8409,9 @@ msgstr "Проверка переименования из «%s» в «%s»\n"
 msgid "bad source"
 msgstr "плохой источник"
 
+msgid "destination exists"
+msgstr "целевой путь уже существует"
+
 msgid "can not move directory into itself"
 msgstr "нельзя переместить каталог в самого себя"
 
@@ -8216,9 +8427,6 @@ msgstr "не под версионным контролем"
 msgid "conflicted"
 msgstr ""
 
-msgid "destination exists"
-msgstr "целевой путь уже существует"
-
 #, c-format
 msgid "overwriting '%s'"
 msgstr "перезапись «%s»"
@@ -8232,6 +8440,9 @@ msgstr "несколько источников для одного целево
 msgid "destination directory does not exist"
 msgstr "целевой каталог не существует"
 
+msgid "destination exists in the index"
+msgstr ""
+
 #, c-format
 msgid "%s, source=%s, destination=%s"
 msgstr "%s, откуда=%s, куда=%s"
@@ -8398,17 +8609,12 @@ msgstr "не удалось прочитать объект «%s»."
 msgid "cannot read note data from non-blob object '%s'."
 msgstr "не удалось прочитать данные заметки из недвоичного объекта «%s»."
 
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "неправильная введенная строка: «%s»."
-
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
 msgstr "не удалось скопировать заметку из «%s» в «%s»"
 
 #. TRANSLATORS: the first %s will be replaced by a git
 #. notes command: 'add', 'merge', 'remove', etc.
-#.
 #, c-format
 msgid "refusing to %s notes in %s (outside of refs/notes/)"
 msgstr "отказ в перезаписи %s заметок в %s (за пределами refs/notes/)"
@@ -8590,20 +8796,15 @@ msgid "use notes from <notes-ref>"
 msgstr "использовать заметку из <ссылка-на-заметку>"
 
 #, c-format
-msgid "unknown subcommand: %s"
-msgstr "неизвестная подкоманда: %s"
+msgid "unknown subcommand: `%s'"
+msgstr ""
 
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects --stdout [<опции>...] [< <список-ссылок> | < <список-"
-"объектов>]"
 
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects [<опции>...] <имя-базы> [< <список-ссылок> | < <список-"
-"объектов>]"
 
 #, c-format
 msgid ""
@@ -8782,6 +8983,12 @@ msgstr "неподдерживаемая версия индекса %s"
 msgid "bad index version '%s'"
 msgstr "плохая версия индекса «%s»"
 
+msgid "show progress meter during object writing phase"
+msgstr "показать прогресс выполнения во время записи объектов"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "похоже на --all-progress при включенном прогрессе выполнения"
+
 msgid "<version>[,<offset>]"
 msgstr ""
 
@@ -8967,8 +9174,11 @@ msgid ""
 "to <git@vger.kernel.org>.  Thanks.\n"
 msgstr ""
 
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<опции>]"
+msgid "refusing to run without --i-still-use-this"
+msgstr ""
+
+msgid "git pack-refs [--all] [--no-prune]"
+msgstr ""
 
 msgid "pack everything"
 msgstr "паковать всё"
@@ -8976,6 +9186,18 @@ msgstr "паковать всё"
 msgid "prune loose refs (default)"
 msgstr "почистить слабые ссылки (по умолчанию)"
 
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr ""
+
+msgid "use the unstable patch-id algorithm"
+msgstr ""
+
+msgid "use the stable patch-id algorithm"
+msgstr ""
+
+msgid "don't strip whitespace from the patch"
+msgstr ""
+
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr ""
 "git prune [-n] [-v] [--progress] [--expire <время>] [--] [<редакция>...]"
@@ -9114,8 +9336,8 @@ msgstr ""
 msgid "pull with rebase"
 msgstr "получение с перемещением"
 
-msgid "please commit or stash them."
-msgstr "сделайте коммит или спрячьте их."
+msgid "Please commit or stash them."
+msgstr "Сделайте коммит или спрячьте их."
 
 #, c-format
 msgid ""
@@ -9175,9 +9397,8 @@ msgstr ""
 
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 
@@ -9321,6 +9542,11 @@ msgstr "Отправка в %s\n"
 msgid "failed to push some refs to '%s'"
 msgstr "не удалось отправить некоторые ссылки в «%s»"
 
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+
 #, c-format
 msgid "invalid value for '%s'"
 msgstr ""
@@ -9328,8 +9554,8 @@ msgstr ""
 msgid "repository"
 msgstr "репозиторий"
 
-msgid "push all refs"
-msgstr "отправить все ссылки"
+msgid "push all branches"
+msgstr ""
 
 msgid "mirror all refs"
 msgstr "сделать зеркало всех ссылок"
@@ -9337,8 +9563,8 @@ msgstr "сделать зеркало всех ссылок"
 msgid "delete refs"
 msgstr "удалить ссылки"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "отправить метки (нельзя использовать вместе с --all или --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
 
 msgid "force updates"
 msgstr "принудительное обновление"
@@ -9440,20 +9666,26 @@ msgstr ""
 msgid "only emit output related to the second range"
 msgstr ""
 
+#, c-format
+msgid "not a revision: '%s'"
+msgstr ""
+
 #, c-format
 msgid "not a commit range: '%s'"
 msgstr ""
 
-msgid "single arg format must be symmetric range"
+#, c-format
+msgid "not a symmetric range: '%s'"
 msgstr ""
 
 msgid "need two commit ranges"
 msgstr ""
 
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
 
 msgid "write resulting index to <file>"
@@ -9542,7 +9774,7 @@ msgid "%s requires the merge backend"
 msgstr ""
 
 #, c-format
-msgid "could not get 'onto': '%s'"
+msgid "invalid onto: '%s'"
 msgstr ""
 
 #, c-format
@@ -9581,14 +9813,27 @@ msgid ""
 "As a result, git cannot rebase them."
 msgstr ""
 
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr ""
+
 #, c-format
 msgid "could not switch to %s"
 msgstr ""
 
+msgid "apply options and merge options cannot be used together"
+msgstr ""
+
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
+msgstr ""
+
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
 msgstr ""
 
 #, c-format
@@ -9693,6 +9938,9 @@ msgstr ""
 msgid "move commits that begin with squash!/fixup! under -i"
 msgstr ""
 
+msgid "update branches that point to commits that are being rebased"
+msgstr ""
+
 msgid "add exec lines after each commit of the editable list"
 msgstr ""
 
@@ -9779,14 +10027,22 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr ""
 
-#, c-format
-msgid "Unknown mode: %s"
+msgid "--strategy requires --merge or --interactive"
 msgstr ""
 
-msgid "--strategy requires --merge or --interactive"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
 msgstr ""
 
-msgid "apply options and merge options cannot be used together"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
 msgstr ""
 
 #, c-format
@@ -9811,7 +10067,7 @@ msgstr ""
 msgid "No such ref: %s"
 msgstr "Нет такой ссылки: %s"
 
-msgid "Could not resolve HEAD to a revision"
+msgid "Could not resolve HEAD to a commit"
 msgstr ""
 
 #, c-format
@@ -9826,9 +10082,6 @@ msgstr ""
 msgid "Does not point to a valid commit '%s'"
 msgstr ""
 
-msgid "Please commit or stash them."
-msgstr "Сделайте коммит или спрячьте их."
-
 msgid "HEAD is up to date."
 msgstr "HEAD уже в актуальном состоянии."
 
@@ -10203,6 +10456,9 @@ msgstr " новая (следующее извлечение сохранит е
 msgid " tracked"
 msgstr " отслеживается"
 
+msgid " skipped"
+msgstr ""
+
 msgid " stale (use 'git remote prune' to remove)"
 msgstr " недействительна (используйте «git remote prune», чтобы удалить)"
 
@@ -10287,7 +10543,6 @@ msgstr "(нет URL)"
 #. TRANSLATORS: the colon ':' should align
 #. with the one in " Fetch URL: %s"
 #. translation.
-#.
 #, c-format
 msgid "  Push  URL: %s"
 msgstr "    URL для отправки: %s"
@@ -10472,6 +10727,10 @@ msgstr ""
 msgid "could not close refs snapshot tempfile"
 msgstr ""
 
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr ""
+
 msgid "pack everything in a single pack"
 msgstr "упаковать всё в один пакет"
 
@@ -10484,7 +10743,7 @@ msgstr ""
 msgid "approxidate"
 msgstr "примерная-дата"
 
-msgid "with -C, expire objects older than this"
+msgid "with --cruft, expire objects older than this"
 msgstr ""
 
 msgid "remove redundant packs, and run git-prune-packed"
@@ -10544,6 +10803,9 @@ msgstr ""
 msgid "write a multi-pack index of the resulting packs"
 msgstr ""
 
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr ""
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "нельзя удалять пакеты в precious-objects репозитории"
 
@@ -10555,7 +10817,11 @@ msgid "pack prefix %s does not begin with objdir %s"
 msgstr ""
 
 #, c-format
-msgid "missing required file: %s"
+msgid "renaming pack to '%s' failed"
+msgstr ""
+
+#, c-format
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
 msgstr ""
 
 #, c-format
@@ -10743,8 +11009,9 @@ msgstr ""
 msgid "only one pattern can be given with -l"
 msgstr ""
 
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forget <путь>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
 
 msgid "register clean resolutions in index"
 msgstr "записать чистые разрешения конфликтов в индекс"
@@ -10866,6 +11133,10 @@ msgstr "Не удалось записать новый файл индекса.
 msgid "unable to get disk usage of %s"
 msgstr ""
 
+#, c-format
+msgid "invalid value for '%s': '%s', the only allowed format is '%s'"
+msgstr ""
+
 msgid "rev-list does not support display of notes"
 msgstr "rev-list не поддерживает отображение заметок"
 
@@ -10891,6 +11162,9 @@ msgstr ""
 msgid "no usage string given before the `--' separator"
 msgstr ""
 
+msgid "missing opt-spec before option flags"
+msgstr ""
+
 msgid "Needed a single revision"
 msgstr ""
 
@@ -10938,6 +11212,15 @@ msgstr ""
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr ""
 
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr ""
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr ""
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr ""
+
 msgid "this operation must be run in a work tree"
 msgstr ""
 
@@ -10945,17 +11228,21 @@ msgstr ""
 msgid "unknown mode for --show-object-format: %s"
 msgstr ""
 
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<опции>] <указатель-коммита>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
 
-msgid "git revert <subcommand>"
-msgstr "git revert <подкоманда>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr ""
 
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<опции>] <указатель-коммита>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
 
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <подкоманда>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr ""
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
@@ -11017,8 +11304,11 @@ msgstr "сбой обращения изменений коммита"
 msgid "cherry-pick failed"
 msgstr "сбой при копировании коммита"
 
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<опции>] [--] <файл>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -11101,12 +11391,16 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 
 msgid "remote name"
 msgstr "имя внешнего репозитория"
 
+msgid "push all refs"
+msgstr "отправить все ссылки"
+
 msgid "use stateless RPC protocol"
 msgstr "протокол без сохранения состояния для RPC"
 
@@ -11125,7 +11419,8 @@ msgstr "git log --pretty=short | git shortlog [<опции>]"
 msgid "using multiple --group options with stdin is not supported"
 msgstr ""
 
-msgid "using --group=trailer with stdin is not supported"
+#, c-format
+msgid "using %s with stdin is not supported"
 msgstr ""
 
 #, c-format
@@ -11163,7 +11458,8 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
@@ -11270,11 +11566,10 @@ msgid "Unknown hash algorithm"
 msgstr ""
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<шаблон>...]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<шаблон>]"
@@ -11305,7 +11600,9 @@ msgid "show refs from stdin that aren't in local repository"
 msgstr ""
 "вывести ссылки со стандартного ввода, которых нет в локальном репозитории"
 
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
 
 msgid "this worktree is not sparse"
@@ -11413,57 +11710,59 @@ msgstr ""
 msgid "error while refreshing working directory"
 msgstr ""
 
-msgid "git stash list [<options>]"
-msgstr "git stash list [<опции>]"
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
 
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<опциии>] [<спрятанные-изменения>]"
+msgid "terminate input and output files by a NUL character"
+msgstr ""
 
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<спрятанные-изменения>]"
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
 
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
+msgid "use patterns in <file> instead of the current ones."
 msgstr ""
-"git stash ( pop | apply ) [--index] [-q|--quiet] [<спрятанные-изменения>]"
 
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <имя-ветки> [<спрятанные-изменения>]"
+msgid "git stash list [<log-options>]"
+msgstr ""
 
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
 
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr ""
+
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
 msgstr ""
 
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<спрятанные-изменения>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr ""
 
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<спрятанные-изменения>]"
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <имя-ветки> [<спрятанные-изменения>]"
 
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <сообщение>] [-q|--quiet] <коммит>"
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr ""
 
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <сообщение>]\n"
-"          [--] [<спецификатор-пути>...]]"
 
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
+msgstr ""
+
+msgid "git stash create [<message>]"
 msgstr ""
 
 #, c-format
@@ -11636,18 +11935,16 @@ msgstr "добавить перед каждой строкой символ к
 msgid "Expecting a full ref name, got %s"
 msgstr "Ожидалось полное имя ссылки, а получено %s"
 
+#, c-format
+msgid "could not get a repository handle for submodule '%s'"
+msgstr ""
+
 #, c-format
 msgid ""
 "could not look up configuration '%s'. Assuming this repository is its own "
 "authoritative upstream."
 msgstr ""
 
-msgid "alternative anchor for relative paths"
-msgstr "альтернативный символ для относительных путей"
-
-msgid "git submodule--helper list [--prefix=<path>] [<path>...]"
-msgstr "git submodule--helper list [--prefix=<путь>] [<путь>...]"
-
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "URL для подмодуля по пути «%s» не найден в .gitmodules"
@@ -11675,7 +11972,7 @@ msgstr ""
 msgid "recurse into nested submodules"
 msgstr "проходить вглубь вложенных подмодулей"
 
-msgid "git submodule--helper foreach [--quiet] [--recursive] [--] <command>"
+msgid "git submodule foreach [--quiet] [--recursive] [--] <command>"
 msgstr ""
 
 #, c-format
@@ -11697,7 +11994,7 @@ msgstr "Сбой регистрации режима обновления для
 msgid "suppress output for initializing a submodule"
 msgstr ""
 
-msgid "git submodule--helper init [<options>] [<path>]"
+msgid "git submodule init [<options>] [<path>]"
 msgstr ""
 
 #, c-format
@@ -11723,9 +12020,6 @@ msgstr ""
 msgid "git submodule status [--quiet] [--cached] [--recursive] [<path>...]"
 msgstr "git submodule status [--quiet] [--cached] [--recursive] [<путь>...]"
 
-msgid "git submodule--helper name <path>"
-msgstr "git submodule--helper name <путь>"
-
 #, c-format
 msgid "* %s %s(blob)->%s(submodule)"
 msgstr ""
@@ -11758,7 +12052,7 @@ msgstr ""
 msgid "limit the summary size"
 msgstr ""
 
-msgid "git submodule--helper summary [<options>] [<commit>] [--] [<path>]"
+msgid "git submodule summary [<options>] [<commit>] [--] [<path>]"
 msgstr ""
 
 msgid "could not fetch a revision for HEAD"
@@ -11772,10 +12066,6 @@ msgstr ""
 msgid "failed to register url for submodule path '%s'"
 msgstr ""
 
-#, c-format
-msgid "failed to get the default remote for submodule '%s'"
-msgstr ""
-
 #, c-format
 msgid "failed to update remote for submodule '%s'"
 msgstr ""
@@ -11783,8 +12073,8 @@ msgstr ""
 msgid "suppress output of synchronizing submodule url"
 msgstr ""
 
-msgid "git submodule--helper sync [--quiet] [--recursive] [<path>]"
-msgstr "git submodule--helper sync [--quiet] [--recursive] [<путь>]"
+msgid "git submodule sync [--quiet] [--recursive] [<path>]"
+msgstr ""
 
 #, c-format
 msgid ""
@@ -11837,6 +12127,10 @@ msgid ""
 "'--reference-if-able' instead of '--reference'."
 msgstr ""
 
+#, c-format
+msgid "could not get a repository handle for gitdir '%s'"
+msgstr ""
+
 #, c-format
 msgid "submodule '%s' cannot add alternate: %s"
 msgstr "подмодулю «%s» не удалось добавить альтернативу: %s"
@@ -11866,6 +12160,9 @@ msgstr ""
 msgid "could not get submodule directory for '%s'"
 msgstr "не удалось получить каталог для подмодуля «%s»"
 
+msgid "alternative anchor for relative paths"
+msgstr "альтернативный символ для относительных путей"
+
 msgid "where the new submodule will be cloned to"
 msgstr "куда должен быть склонирован новый подмодуль"
 
@@ -11890,10 +12187,6 @@ msgid ""
 "<filter-spec>] --url <url> --path <path>"
 msgstr ""
 
-#, c-format
-msgid "Invalid update mode '%s' for submodule path '%s'"
-msgstr ""
-
 #, c-format
 msgid "Invalid update mode '%s' configured for submodule path '%s'"
 msgstr ""
@@ -11963,6 +12256,10 @@ msgid ""
 "of that commit failed."
 msgstr ""
 
+#, c-format
+msgid "could not initialize submodule at path '%s'"
+msgstr ""
+
 #, c-format
 msgid ""
 "Submodule (%s) branch configured to inherit branch from superproject, but "
@@ -11971,10 +12268,6 @@ msgstr ""
 "Ветка подмодуля (%s) настроена на наследование ветки из родительского "
 "проекта, но он не находится ни на одной ветке"
 
-#, c-format
-msgid "could not get a repository handle for submodule '%s'"
-msgstr ""
-
 #, c-format
 msgid "Unable to find current revision in submodule path '%s'"
 msgstr ""
@@ -12006,14 +12299,14 @@ msgstr ""
 msgid "don't fetch new objects from the remote site"
 msgstr ""
 
-msgid "path into the working tree"
-msgstr "путь в рабочем каталоге"
+msgid "use the 'checkout' update strategy (default)"
+msgstr ""
 
-msgid "path into the working tree, across nested submodule boundaries"
-msgstr "путь в рабочем каталоге, в пределах границ подмодуля"
+msgid "use the 'merge' update strategy"
+msgstr ""
 
-msgid "rebase, merge, checkout or none"
-msgstr "rebase, merge, checkout или none"
+msgid "use the 'rebase' update strategy"
+msgstr ""
 
 msgid "create a shallow clone truncated to the specified number of revisions"
 msgstr ""
@@ -12029,6 +12322,9 @@ msgstr ""
 msgid "don't print cloning progress"
 msgstr "вы выводить прогресс клонирования"
 
+msgid "disallow cloning into non-empty directory, implies --init"
+msgstr ""
+
 msgid ""
 "git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] "
 "[-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-"
@@ -12036,34 +12332,13 @@ msgid ""
 "[--] [<path>...]"
 msgstr ""
 
-msgid "bad value for update parameter"
-msgstr "плохое значение для параметра update"
-
-msgid "recurse into submodules"
-msgstr "рекурсивно по подмодулям"
-
-msgid "git submodule--helper absorb-git-dirs [<options>] [<path>...]"
-msgstr "git submodule--helper absorb-git-dirs [<опции>] [<путь>...]"
-
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr ""
-
-msgid "unset the config in the .gitmodules file"
-msgstr ""
-
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <имя> [<значение>]"
-
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <имя>"
-
-msgid "please make sure that the .gitmodules file is in the working tree"
+msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr ""
 
 msgid "suppress output for setting url of a submodule"
 msgstr ""
 
-msgid "git submodule--helper set-url [--quiet] <path> <newurl>"
+msgid "git submodule set-url [--quiet] <path> <newurl>"
 msgstr ""
 
 msgid "set the default tracking branch to master"
@@ -12072,11 +12347,10 @@ msgstr ""
 msgid "set the default tracking branch"
 msgstr ""
 
-msgid "git submodule--helper set-branch [-q|--quiet] (-d|--default) <path>"
+msgid "git submodule set-branch [-q|--quiet] (-d|--default) <path>"
 msgstr ""
 
-msgid ""
-"git submodule--helper set-branch [-q|--quiet] (-b|--branch) <branch> <path>"
+msgid "git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"
 msgstr ""
 
 msgid "--branch or --default required"
@@ -12130,6 +12404,9 @@ msgstr ""
 msgid "unable to checkout submodule '%s'"
 msgstr ""
 
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr ""
+
 #, c-format
 msgid "Failed to add submodule '%s'"
 msgstr ""
@@ -12164,7 +12441,7 @@ msgid ""
 "path"
 msgstr ""
 
-msgid "git submodule--helper add [<options>] [--] <repository> [<path>]"
+msgid "git submodule add [<options>] [--] <repository> [<path>]"
 msgstr ""
 
 msgid "Relative path can only be used from the toplevel of the working tree"
@@ -12180,19 +12457,17 @@ msgstr ""
 msgid "'%s' is not a valid submodule name"
 msgstr ""
 
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s не поддерживает параметр --super-prefix"
+msgid "git submodule--helper <command>"
+msgstr ""
 
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "«%s» не является подкомандой submodule--helper"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr ""
 
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<опции>] <имя> [<ссылка>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr ""
 
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <имя>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr ""
 
 msgid "suppress error message for non-symbolic (detached) refs"
 msgstr ""
@@ -12204,6 +12479,9 @@ msgstr "удалить символьные ссылки"
 msgid "shorten ref output"
 msgstr "укороченный вывод ссылок"
 
+msgid "recursively dereference (default)"
+msgstr ""
+
 msgid "reason"
 msgstr "причина"
 
@@ -12211,18 +12489,18 @@ msgid "reason of the update"
 msgstr "причина обновления"
 
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d <имя-метки>..."
 
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
 
 msgid "git tag -v [--format=<format>] <tagname>..."
@@ -12363,6 +12641,16 @@ msgstr "Метка «%s» обновлена (была %s)\n"
 msgid "pack exceeds maximum allowed size"
 msgstr "размер пакета превышает максимальный допустимый"
 
+msgid "failed to write object in stream"
+msgstr ""
+
+#, c-format
+msgid "inflate returned (%d)"
+msgstr ""
+
+msgid "invalid blob object from stream"
+msgstr ""
+
 msgid "Unpacking objects"
 msgstr "Распаковка объектов"
 
@@ -12592,8 +12880,10 @@ msgstr "прочитать обновления из стандартного в
 msgid "update the info files from scratch"
 msgstr "обновить информацию о серверах с нуля"
 
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<опции>] <каталог>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
 
 msgid "quit after a single request/response exchange"
 msgstr "выход после обмена одним запросом/ответом"
@@ -12607,8 +12897,8 @@ msgstr "не проверять <каталог>/.git/ если <каталог>
 msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "прервать передачу после <кол> секунд простоя"
 
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <коммит>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr ""
 
 msgid "print commit contents"
 msgstr "вывести содержимое коммита"
@@ -12616,8 +12906,8 @@ msgstr "вывести содержимое коммита"
 msgid "print raw gpg status output"
 msgstr "выводить сырой вывод статуса от gpg"
 
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <пакет>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr ""
 
 msgid "verbose"
 msgstr "быть многословнее"
@@ -12625,35 +12915,37 @@ msgstr "быть многословнее"
 msgid "show statistics only"
 msgstr "вывести только статистику"
 
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<формат>] <метка>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr ""
 
 msgid "print tag contents"
 msgstr "вывести содержимое метки"
 
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<опции>] <путь> [<указатель-коммита>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [-b <new-branch>] <path> [<commit-ish>]"
+msgstr ""
 
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<опции>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr ""
 
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<опции>] <путь>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr ""
 
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <рабочий-каталог> <новый-путь>"
 
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<опции>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr ""
 
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<опции>] <рабочий-каталог>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr ""
 
 msgid "git worktree repair [<path>...]"
 msgstr ""
 
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <путь>"
+msgid "git worktree unlock <worktree>"
+msgstr ""
 
 #, c-format
 msgid "Removing %s/%s: %s"
@@ -12722,7 +13014,7 @@ msgstr "Готовится рабочая копия (отсоединённый
 
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
-"переключиться на <ветка> даже если она уже активна в другом рабочесм каталоге"
+"переключиться на <ветка> даже если она уже активна в другом рабочем каталоге"
 
 msgid "create a new branch"
 msgstr "создать новую ветку"
@@ -12866,6 +13158,62 @@ msgstr "вывести объект дерева для подкаталога 
 msgid "only useful for debugging"
 msgstr "используется только при отладке"
 
+msgid "core.fsyncMethod = batch is unsupported on this platform"
+msgstr ""
+
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr ""
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr ""
+
+msgid "failed to create temporary file"
+msgstr ""
+
+msgid "insufficient capabilities"
+msgstr ""
+
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr ""
+
+msgid "failed to store maximum creation token"
+msgstr ""
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr ""
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr ""
+
+#, c-format
+msgid "failed to download bundle from URI '%s'"
+msgstr ""
+
+#, c-format
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr ""
+
+#, c-format
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr ""
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr ""
+
+msgid "bundle-uri: got an empty line"
+msgstr ""
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr ""
+
+msgid "bundle-uri: line has empty key or value"
+msgstr ""
+
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
 msgstr ""
@@ -12888,6 +13236,11 @@ msgstr "В репозитории отсутствуют необходимые
 msgid "need a repository to verify a bundle"
 msgstr ""
 
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -13072,6 +13425,9 @@ msgstr "Очень простой сервер для Git репозиторие
 msgid "Give an object a human readable name based on an available ref"
 msgstr "Присвоение объекту удобочитаемое имя на основе доступной ссылки"
 
+msgid "Generate a zip archive of diagnostic information"
+msgstr ""
+
 msgid "Show changes between commits, commit and working tree, etc"
 msgstr "Вывод разницы между коммитами, коммитом и рабочим каталогом и т.д."
 
@@ -13205,8 +13561,8 @@ msgid "The standard helper program to use with git-merge-index"
 msgstr ""
 "Стандартная программа-помощник для использования совместно с git-merge-index"
 
-msgid "Show three-way merge without touching index"
-msgstr "Вывод трёхходового слияние без затрагивания индекса"
+msgid "Perform merge without touching index or working tree"
+msgstr ""
 
 msgid "Run merge conflict resolution tools to resolve merge conflicts"
 msgstr "Запуск инструментов разрешения конфликтов слияния"
@@ -13405,6 +13761,9 @@ msgstr "Проверка файлов упакованных архивов Git"
 msgid "Check the GPG signature of tags"
 msgstr "Проверка подписи GPG меток"
 
+msgid "Display version information about Git"
+msgstr ""
+
 msgid "Show logs with difference each commit introduces"
 msgstr "Вывод журнала с изменениями, которые вводил каждый из коммитов"
 
@@ -13438,6 +13797,24 @@ msgstr "Полезный минимум команд для каждодневн
 msgid "Frequently asked questions about using Git"
 msgstr ""
 
+msgid "The bundle file format"
+msgstr ""
+
+msgid "Chunk-based file formats"
+msgstr ""
+
+msgid "Git commit-graph format"
+msgstr ""
+
+msgid "Git index format"
+msgstr ""
+
+msgid "Git pack format"
+msgstr ""
+
+msgid "Git cryptographic signature formats"
+msgstr ""
+
 msgid "A Git Glossary"
 msgstr "Глоссарий Git"
 
@@ -13459,19 +13836,34 @@ msgstr "Определение свойств подмодулей"
 msgid "Git namespaces"
 msgstr "Пространства имён Git"
 
-msgid "Helper programs to interact with remote repositories"
+msgid "Protocol v0 and v1 capabilities"
 msgstr ""
 
-msgid "Git Repository Layout"
-msgstr "Содержимое репозитория Git"
+msgid "Things common to various protocols"
+msgstr ""
 
-msgid "Specifying revisions and ranges for Git"
-msgstr "Указание редакций и диапазонов для Git"
+msgid "Git HTTP-based protocols"
+msgstr ""
 
-msgid "Mounting one repository inside another"
+msgid "How packs are transferred over-the-wire"
 msgstr ""
 
-msgid "A tutorial introduction to Git"
+msgid "Git Wire Protocol, Version 2"
+msgstr ""
+
+msgid "Helper programs to interact with remote repositories"
+msgstr ""
+
+msgid "Git Repository Layout"
+msgstr "Содержимое репозитория Git"
+
+msgid "Specifying revisions and ranges for Git"
+msgstr "Указание редакций и диапазонов для Git"
+
+msgid "Mounting one repository inside another"
+msgstr ""
+
+msgid "A tutorial introduction to Git"
 msgstr "Обучающее введение в Git"
 
 msgid "A tutorial introduction to Git: part two"
@@ -13483,6 +13875,9 @@ msgstr "Веб интерфейс Git (веб-интерфейс для Git ре
 msgid "An overview of recommended workflows with Git"
 msgstr "Обзор рекомендуемых последовательностей выполняемых действий с Git"
 
+msgid "A tool for managing large Git repositories"
+msgstr ""
+
 msgid "commit-graph file is too small"
 msgstr "файл commit-graph слишком маленький"
 
@@ -13723,6 +14118,14 @@ msgstr ""
 msgid "no libc information available\n"
 msgstr ""
 
+#, c-format
+msgid "could not determine free disk size for '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not get info for '%s'"
+msgstr ""
+
 #, c-format
 msgid "[GLE %ld] health thread could not open '%ls'"
 msgstr ""
@@ -13747,6 +14150,10 @@ msgstr ""
 msgid "health thread wait failed [GLE %ld]"
 msgstr ""
 
+#, c-format
+msgid "Invalid path: %s"
+msgstr ""
+
 msgid "Unable to create FSEventStream."
 msgstr ""
 
@@ -13777,6 +14184,30 @@ msgstr ""
 msgid "could not read directory changes [GLE %ld]"
 msgstr ""
 
+#, c-format
+msgid "opendir('%s') failed"
+msgstr ""
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr ""
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr ""
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr ""
+
+#, c-format
+msgid "[GLE %ld] unable to open for read '%ls'"
+msgstr ""
+
+#, c-format
+msgid "[GLE %ld] unable to get protocol information for '%ls'"
+msgstr ""
+
 #, c-format
 msgid "failed to copy SID (%ld)"
 msgstr ""
@@ -14047,7 +14478,7 @@ msgstr ""
 msgid "bad zlib compression level %d"
 msgstr "неправильный уровень сжатия zlib %d"
 
-msgid "core.commentChar should only be one character"
+msgid "core.commentChar should only be one ASCII character"
 msgstr ""
 
 #, c-format
@@ -14158,6 +14589,10 @@ msgstr "не удалось установить «%s» в «%s»"
 msgid "invalid section name: %s"
 msgstr ""
 
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+
 #, c-format
 msgid "missing value for '%s'"
 msgstr ""
@@ -14213,15 +14648,22 @@ msgid "unknown object format '%s' specified by server"
 msgstr ""
 
 #, c-format
-msgid "invalid ls-refs response: %s"
+msgid "error on bundle-uri response line %d: %s"
 msgstr ""
 
-msgid "expected flush after ref listing"
+msgid "expected flush after bundle-uri listing"
 msgstr ""
 
 msgid "expected response end packet after ref listing"
 msgstr ""
 
+#, c-format
+msgid "invalid ls-refs response: %s"
+msgstr ""
+
+msgid "expected flush after ref listing"
+msgstr ""
+
 #, c-format
 msgid "protocol '%s' is not supported"
 msgstr ""
@@ -14305,175 +14747,6 @@ msgstr "сбой записи в rev-list"
 msgid "failed to close rev-list's stdin"
 msgstr "сбой закрытия стандартного ввода у rev-list"
 
-#, c-format
-msgid "'%s' does not exist"
-msgstr "«%s» не существует"
-
-msgid "need a working directory"
-msgstr ""
-
-msgid "could not find enlistment root"
-msgstr ""
-
-#, c-format
-msgid "could not switch to '%s'"
-msgstr ""
-
-#, c-format
-msgid "could not configure %s=%s"
-msgstr ""
-
-msgid "could not configure log.excludeDecoration"
-msgstr ""
-
-msgid "Scalar enlistments require a worktree"
-msgstr ""
-
-#, c-format
-msgid "could not open directory '%s'"
-msgstr "не удалось открыть каталог «%s»"
-
-#, c-format
-msgid "skipping '%s', which is neither file nor directory"
-msgstr ""
-
-#, c-format
-msgid "could not determine free disk size for '%s'"
-msgstr ""
-
-#, c-format
-msgid "could not get info for '%s'"
-msgstr ""
-
-#, c-format
-msgid "remote HEAD is not a branch: '%.*s'"
-msgstr ""
-
-msgid "failed to get default branch name from remote; using local default"
-msgstr ""
-
-msgid "failed to get default branch name"
-msgstr ""
-
-msgid "failed to unregister repository"
-msgstr ""
-
-msgid "failed to delete enlistment directory"
-msgstr ""
-
-msgid "branch to checkout after clone"
-msgstr ""
-
-msgid "when cloning, create full working directory"
-msgstr ""
-
-msgid "only download metadata for the branch that will be checked out"
-msgstr ""
-
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr ""
-
-#, c-format
-msgid "cannot deduce worktree name from '%s'"
-msgstr ""
-
-#, c-format
-msgid "directory '%s' exists already"
-msgstr ""
-
-#, c-format
-msgid "failed to get default branch for '%s'"
-msgstr ""
-
-#, c-format
-msgid "could not configure remote in '%s'"
-msgstr ""
-
-#, c-format
-msgid "could not configure '%s'"
-msgstr ""
-
-msgid "partial clone failed; attempting full clone"
-msgstr ""
-
-msgid "could not configure for full clone"
-msgstr ""
-
-msgid "scalar diagnose [<enlistment>]"
-msgstr ""
-
-#, c-format
-msgid "could not create directory for '%s'"
-msgstr ""
-
-msgid "could not duplicate stdout"
-msgstr ""
-
-msgid "failed to write archive"
-msgstr ""
-
-msgid "`scalar list` does not take arguments"
-msgstr ""
-
-msgid "scalar register [<enlistment>]"
-msgstr ""
-
-msgid "reconfigure all registered enlistments"
-msgstr ""
-
-msgid "scalar reconfigure [--all | <enlistment>]"
-msgstr ""
-
-msgid "--all or <enlistment>, but not both"
-msgstr ""
-
-#, c-format
-msgid "git repository gone in '%s'"
-msgstr ""
-
-msgid ""
-"scalar run <task> [<enlistment>]\n"
-"Tasks:\n"
-msgstr ""
-
-#, c-format
-msgid "no such task: '%s'"
-msgstr ""
-
-msgid "scalar unregister [<enlistment>]"
-msgstr ""
-
-msgid "scalar delete <enlistment>"
-msgstr ""
-
-msgid "refusing to delete current working directory"
-msgstr ""
-
-msgid "include Git version"
-msgstr ""
-
-msgid "include Git's build options"
-msgstr ""
-
-msgid "scalar verbose [-v | --verbose] [--build-options]"
-msgstr ""
-
-msgid "-C requires a <directory>"
-msgstr ""
-
-#, c-format
-msgid "could not change to '%s'"
-msgstr ""
-
-msgid "-c requires a <key>=<value> argument"
-msgstr ""
-
-msgid ""
-"scalar [-C <directory>] [-c <key>=<value>] <command> [<options>]\n"
-"\n"
-"Commands:\n"
-msgstr ""
-
 #, c-format
 msgid "illegal crlf_action %d"
 msgstr ""
@@ -14689,6 +14962,32 @@ msgstr ""
 msgid "Marked %d islands, done.\n"
 msgstr ""
 
+#, c-format
+msgid "invalid --%s value '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not archive missing directory '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not open directory '%s'"
+msgstr "не удалось открыть каталог «%s»"
+
+#, c-format
+msgid "skipping '%s', which is neither file nor directory"
+msgstr ""
+
+msgid "could not duplicate stdout"
+msgstr ""
+
+#, c-format
+msgid "could not add directory '%s' to archiver"
+msgstr ""
+
+msgid "failed to write archive"
+msgstr ""
+
 msgid "--merge-base does not work with ranges"
 msgstr ""
 
@@ -14951,6 +15250,9 @@ msgstr ""
 msgid "do not show any source or destination prefix"
 msgstr ""
 
+msgid "use default prefixes a/ and b/"
+msgstr ""
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 
@@ -15238,6 +15540,14 @@ msgstr "не удалось переместить каталог git с «%s»
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "подсказка: Ожидание, пока вы закроете редактор с файлом...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "не удалось записать в «%s»"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr ""
+
 msgid "Filtering content"
 msgstr "Фильтруется содержимое"
 
@@ -15269,6 +15579,9 @@ msgstr "git fetch-pack: ожидается ACK/NAK, а получено «%s»"
 msgid "unable to write to remote"
 msgstr ""
 
+msgid "Server supports filter"
+msgstr "Сервер поддерживает фильтрацию"
+
 #, c-format
 msgid "invalid shallow line: %s"
 msgstr "неправильная строка частичного получения: %s"
@@ -15379,9 +15692,6 @@ msgstr ""
 msgid "Server does not support shallow requests"
 msgstr "Сервер не поддерживает частичные запросы"
 
-msgid "Server supports filter"
-msgstr "Сервер поддерживает фильтрацию"
-
 msgid "unable to write request to remote"
 msgstr ""
 
@@ -15403,14 +15713,12 @@ msgstr ""
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
-#.
 #, c-format
 msgid "expected packfile to be sent after '%s'"
 msgstr ""
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
-#.
 #, c-format
 msgid "expected no other sections to be sent after no '%s'"
 msgstr ""
@@ -15481,7 +15789,8 @@ msgstr ""
 
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
 
 msgid ""
@@ -15490,8 +15799,7 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 
 msgid ""
@@ -15519,15 +15827,15 @@ msgid "no namespace given for --namespace\n"
 msgstr ""
 
 #, c-format
-msgid "no prefix given for --super-prefix\n"
+msgid "-c expects a configuration string\n"
 msgstr ""
 
 #, c-format
-msgid "-c expects a configuration string\n"
+msgid "no config key given for --config-env\n"
 msgstr ""
 
 #, c-format
-msgid "no config key given for --config-env\n"
+msgid "no attribute source given for --attr-source\n"
 msgstr ""
 
 #, c-format
@@ -15626,8 +15934,11 @@ msgstr ""
 msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr ""
 
-msgid "gpg failed to sign the data"
-msgstr "gpg не удалось подписать данные"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
 
 msgid "user.signingKey needs to be set for ssh signing"
 msgstr ""
@@ -15706,6 +16017,12 @@ msgstr ""
 msgid "Low-level Commands / Internal Helpers"
 msgstr "Низкоуровневые команды / Внутренние вспомогательные"
 
+msgid "User-facing repository, command and file interfaces"
+msgstr ""
+
+msgid "Developer-facing file formats, protocols and other interfaces"
+msgstr ""
+
 #, c-format
 msgid "available git commands in '%s'"
 msgstr "доступные команды git в «%s»"
@@ -15719,6 +16036,12 @@ msgstr "Стандартные команды Git используемые в р
 msgid "The Git concept guides are:"
 msgstr ""
 
+msgid "User-facing repository, command and file interfaces:"
+msgstr ""
+
+msgid "File formats, protocols and other developer interfaces:"
+msgstr ""
+
 msgid "External commands"
 msgstr ""
 
@@ -15778,8 +16101,8 @@ msgstr[3] ""
 "\n"
 "Самые похожие команды:"
 
-msgid "git version [<options>]"
-msgstr "git version [<options>]"
+msgid "git version [--build-options]"
+msgstr ""
 
 #, c-format
 msgid "%s: %s - %s"
@@ -15814,10 +16137,6 @@ msgstr ""
 "Вы можете отключить это предупреждение с помощью команды «git config advice."
 "ignoredHook false»."
 
-#, c-format
-msgid "Couldn't start hook '%s'\n"
-msgstr ""
-
 #, c-format
 msgid "argument to --packfile must be a valid hash (got '%s')"
 msgstr ""
@@ -16001,16 +16320,14 @@ msgstr ""
 msgid "quoted CRLF detected"
 msgstr ""
 
-#, c-format
-msgid ""
-"Your local changes to the following files would be overwritten by merge:\n"
-"  %s"
-msgstr ""
-
 #, c-format
 msgid "Failed to merge submodule %s (not checked out)"
 msgstr "Не удалось слить подмодуль %s (состояние не забрано)"
 
+#, c-format
+msgid "Failed to merge submodule %s (no merge base)"
+msgstr ""
+
 #, c-format
 msgid "Failed to merge submodule %s (commits not present)"
 msgstr "Не удалось слить подмодуль %s (нет коммитов)"
@@ -16029,24 +16346,8 @@ msgstr ""
 
 #, c-format
 msgid ""
-"Failed to merge submodule %s, but a possible merge resolution exists:\n"
-"%s\n"
-msgstr ""
-
-#, c-format
-msgid ""
-"If this is correct simply add it to the index for example\n"
-"by using:\n"
-"\n"
-"  git update-index --cacheinfo 160000 %s \"%s\"\n"
-"\n"
-"which will accept this suggestion.\n"
+"Failed to merge submodule %s, but a possible merge resolution exists: %s"
 msgstr ""
-"Если оно верное, то просто добавьте его в индекс, например так:\n"
-"\n"
-"  git update-index --cacheinfo 160000 %s \"%s\"\n"
-"\n"
-"тем самым принимая это предположение.\n"
 
 #, c-format
 msgid ""
@@ -16174,15 +16475,33 @@ msgid ""
 "of %s left in tree."
 msgstr ""
 
+#. TRANSLATORS: This is a line of advice to resolve a merge
+#. conflict in a submodule. The first argument is the submodule
+#. name, and the second argument is the abbreviated id of the
+#. commit that needs to be merged.  For example:
+#.  - go to submodule (mysubmodule), and either merge commit abc1234"
 #, c-format
 msgid ""
-"Note: %s not up to date and in way of checking out conflicted version; old "
-"copy renamed to %s"
+" - go to submodule (%s), and either merge commit %s\n"
+"   or update to an existing commit which has merged those changes\n"
+msgstr ""
+
+#, c-format
+msgid ""
+"Recursive merging with submodules currently only supports trivial cases.\n"
+"Please manually handle the merging of each conflicted submodule.\n"
+"This can be accomplished with the following steps:\n"
+"%s - come back to superproject and run:\n"
+"\n"
+"      git add %s\n"
+"\n"
+"   to record the above merge or update\n"
+" - resolve any other conflicts in the superproject\n"
+" - commit the resulting index in the superproject\n"
 msgstr ""
 
 #. TRANSLATORS: The %s arguments are: 1) tree hash of a merge
 #. base, and 2-3) the trees for the two trees we're merging.
-#.
 #, c-format
 msgid "collecting merge info failed for trees %s, %s, %s"
 msgstr ""
@@ -16248,6 +16567,21 @@ msgstr "Не удалось слить подмодуль %s (нельзя сд
 msgid "Found a possible merge resolution for the submodule:\n"
 msgstr "Найдено возможное разрешение слиятия для подмодуля:\n"
 
+#, c-format
+msgid ""
+"If this is correct simply add it to the index for example\n"
+"by using:\n"
+"\n"
+"  git update-index --cacheinfo 160000 %s \"%s\"\n"
+"\n"
+"which will accept this suggestion.\n"
+msgstr ""
+"Если оно верное, то просто добавьте его в индекс, например так:\n"
+"\n"
+"  git update-index --cacheinfo 160000 %s \"%s\"\n"
+"\n"
+"тем самым принимая это предположение.\n"
+
 #, c-format
 msgid "Failed to merge submodule %s (multiple merges found)"
 msgstr "Не удалось слить подмодуль %s (найдено несколько слияний)"
@@ -16316,8 +16650,8 @@ msgstr "Отказ потери неотслеживаемого файла %s;
 
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
 "КОНФЛИКТ (переименование/переименование): Переименование «%s»→«%s» в ветке "
 "«%s» и переименование «%s»→«%s» в ветке «%s»%s"
@@ -16612,7 +16946,6 @@ msgstr "Отказ в перезаписи заметок в %s (за преде
 #. TRANSLATORS: The first %s is the name of
 #. the environment variable, the second %s is
 #. its value.
-#.
 #, c-format
 msgid "Bad %s value: '%s'"
 msgstr "Неправильное значение переменной %s: «%s»"
@@ -16629,10 +16962,6 @@ msgstr ""
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr ""
 
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr ""
-
 msgid "unable to fdopen alternates lockfile"
 msgstr ""
 
@@ -16691,6 +17020,10 @@ msgstr ""
 msgid "garbage at end of loose object '%s'"
 msgstr ""
 
+#, c-format
+msgid "unable to open loose object %s"
+msgstr ""
+
 #, c-format
 msgid "unable to parse %s header"
 msgstr ""
@@ -16707,17 +17040,13 @@ msgid "header for %s too long, exceeds %d bytes"
 msgstr ""
 
 #, c-format
-msgid "failed to read object %s"
+msgid "loose object %s (stored in %s) is corrupt"
 msgstr ""
 
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr ""
 
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr ""
-
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr ""
@@ -16730,9 +17059,6 @@ msgstr ""
 msgid "unable to set permission to '%s'"
 msgstr ""
 
-msgid "file write error"
-msgstr ""
-
 msgid "error when closing loose object file"
 msgstr ""
 
@@ -16758,14 +17084,31 @@ msgstr ""
 msgid "confused by unstable object source data for %s"
 msgstr ""
 
+#, c-format
+msgid "write stream object %ld != %<PRIuMAX>"
+msgstr ""
+
+#, c-format
+msgid "unable to stream deflate new object (%d)"
+msgstr ""
+
+#, c-format
+msgid "deflateEnd on stream object failed (%d)"
+msgstr ""
+
+#, c-format
+msgid "unable to create directory %s"
+msgstr ""
+
 #, c-format
 msgid "cannot read object for %s"
 msgstr ""
 
-msgid "corrupt commit"
+#, c-format
+msgid "object fails fsck: %s"
 msgstr ""
 
-msgid "corrupt tag"
+msgid "refusing to create malformed object"
 msgstr ""
 
 #, c-format
@@ -16815,7 +17158,6 @@ msgstr ""
 #. TRANSLATORS: This is a line of ambiguous object
 #. output shown when we cannot look up or parse the
 #. object in question. E.g. "deadbeef [bad object]".
-#.
 #, c-format
 msgid "%s [bad object]"
 msgstr ""
@@ -16824,7 +17166,6 @@ msgstr ""
 #. object output. E.g.:
 #. *
 #.    "deadbeef commit 2021-01-01 - Some Commit Message"
-#.
 #, c-format
 msgid "%s commit %s - %s"
 msgstr ""
@@ -16839,7 +17180,6 @@ msgstr ""
 #. *
 #. The third argument is the "tag" string
 #. from object.c.
-#.
 #, c-format
 msgid "%s tag %s - %s"
 msgstr ""
@@ -16849,21 +17189,18 @@ msgstr ""
 #. the tag itself. E.g.:
 #. *
 #.    "deadbeef [bad tag, could not parse it]"
-#.
 #, c-format
 msgid "%s [bad tag, could not parse it]"
 msgstr ""
 
 #. TRANSLATORS: This is a line of ambiguous <type>
 #. object output. E.g. "deadbeef tree".
-#.
 #, c-format
 msgid "%s tree"
 msgstr ""
 
 #. TRANSLATORS: This is a line of ambiguous <type>
 #. object output. E.g. "deadbeef blob".
-#.
 #, c-format
 msgid "%s blob"
 msgstr ""
@@ -16875,7 +17212,6 @@ msgstr ""
 #. TRANSLATORS: The argument is the list of ambiguous
 #. objects composed in show_ambiguous_object(). See
 #. its "TRANSLATORS" comments for details.
-#.
 #, c-format
 msgid ""
 "The candidates are:\n"
@@ -16929,57 +17265,154 @@ msgid ""
 msgstr ""
 
 #, c-format
-msgid "path '%s' exists on disk, but not in the index"
+msgid "path '%s' exists on disk, but not in the index"
+msgstr ""
+
+#, c-format
+msgid "path '%s' does not exist (neither on disk nor in the index)"
+msgstr ""
+
+msgid "relative path syntax can't be used outside working tree"
+msgstr ""
+
+#, c-format
+msgid "<object>:<path> required, only <object> '%s' given"
+msgstr ""
+
+#, c-format
+msgid "invalid object name '%.*s'."
+msgstr ""
+
+#, c-format
+msgid "invalid object type \"%s\""
+msgstr ""
+
+#, c-format
+msgid "object %s is a %s, not a %s"
+msgstr ""
+
+#, c-format
+msgid "object %s has unknown type id %d"
+msgstr ""
+
+#, c-format
+msgid "unable to parse object: %s"
+msgstr "не удалось разобрать объект: %s"
+
+#, c-format
+msgid "hash mismatch %s"
+msgstr "несоответствие хэш-кода %s"
+
+msgid "trying to write commit not in index"
+msgstr ""
+
+msgid "failed to load bitmap index (corrupted?)"
+msgstr ""
+
+msgid "corrupted bitmap index (too small)"
+msgstr ""
+
+msgid "corrupted bitmap index file (wrong header)"
+msgstr ""
+
+#, c-format
+msgid "unsupported version '%d' for bitmap index file"
+msgstr ""
+
+msgid "corrupted bitmap index file (too short to fit hash cache)"
+msgstr ""
+
+msgid "corrupted bitmap index file (too short to fit lookup table)"
+msgstr ""
+
+#, c-format
+msgid "duplicate entry in bitmap index: '%s'"
+msgstr ""
+
+#, c-format
+msgid "corrupt ewah bitmap: truncated header for entry %d"
+msgstr ""
+
+#, c-format
+msgid "corrupt ewah bitmap: commit index %u out of range"
+msgstr ""
+
+msgid "corrupted bitmap pack index"
+msgstr ""
+
+msgid "invalid XOR offset in bitmap pack index"
+msgstr ""
+
+msgid "cannot fstat bitmap file"
+msgstr ""
+
+msgid "checksum doesn't match in MIDX and bitmap"
+msgstr ""
+
+msgid "multi-pack bitmap is missing required reverse index"
+msgstr ""
+
+#, c-format
+msgid "could not open pack %s"
+msgstr ""
+
+#, c-format
+msgid "preferred pack (%s) is invalid"
 msgstr ""
 
-#, c-format
-msgid "path '%s' does not exist (neither on disk nor in the index)"
+msgid "corrupt bitmap lookup table: triplet position out of index"
 msgstr ""
 
-msgid "relative path syntax can't be used outside working tree"
+msgid "corrupt bitmap lookup table: xor chain exceeds entry count"
 msgstr ""
 
 #, c-format
-msgid "<object>:<path> required, only <object> '%s' given"
+msgid "corrupt bitmap lookup table: commit index %u out of range"
 msgstr ""
 
 #, c-format
-msgid "invalid object name '%.*s'."
+msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
 msgstr ""
 
 #, c-format
-msgid "invalid object type \"%s\""
+msgid "object '%s' not found in type bitmaps"
 msgstr ""
 
 #, c-format
-msgid "object %s is a %s, not a %s"
+msgid "object '%s' does not have a unique type"
 msgstr ""
 
 #, c-format
-msgid "object %s has unknown type id %d"
+msgid "object '%s': real type '%s', expected: '%s'"
 msgstr ""
 
 #, c-format
-msgid "unable to parse object: %s"
-msgstr "не удалось разобрать объект: %s"
+msgid "object not in bitmap: '%s'"
+msgstr ""
+
+msgid "failed to load bitmap indexes"
+msgstr ""
+
+msgid "you must specify exactly one commit to test"
+msgstr ""
 
 #, c-format
-msgid "hash mismatch %s"
-msgstr "несоответствие хэш-кода %s"
+msgid "commit '%s' doesn't have an indexed bitmap"
+msgstr ""
 
-msgid "multi-pack bitmap is missing required reverse index"
+msgid "mismatch in bitmap results"
 msgstr ""
 
 #, c-format
-msgid "could not open pack %s"
+msgid "could not find '%s' in pack '%s' at offset %<PRIuMAX>"
 msgstr ""
 
 #, c-format
-msgid "preferred pack (%s) is invalid"
+msgid "unable to get disk usage of '%s'"
 msgstr ""
 
 #, c-format
-msgid "could not find %s in pack %s at offset %<PRIuMAX>"
+msgid "bitmap file '%s' has invalid checksum"
 msgstr ""
 
 #, c-format
@@ -17022,6 +17455,13 @@ msgstr ""
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr ""
 
+msgid "invalid checksum"
+msgstr ""
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr ""
+
 msgid "cannot both write and verify reverse index"
 msgstr ""
 
@@ -17104,6 +17544,9 @@ msgstr "имелось в виду `--%s` (с двумя дефисами)?"
 msgid "alias of --%s"
 msgstr ""
 
+msgid "need a subcommand"
+msgstr ""
+
 #, c-format
 msgid "unknown option `%s'"
 msgstr ""
@@ -17125,7 +17568,6 @@ msgstr "использование: %s"
 
 #. TRANSLATORS: the colon here should align with the
 #. one in "usage: %s" translation.
-#.
 #, c-format
 msgid "   or: %s"
 msgstr "          или: %s"
@@ -17148,7 +17590,6 @@ msgstr "          или: %s"
 #. function. The "%s" is a line in the (hopefully already
 #. translated) N_() usage string, which contained embedded
 #. newlines before we split it up.
-#.
 #, c-format
 msgid "%*s%s"
 msgstr ""
@@ -17175,6 +17616,9 @@ msgstr "тихий режим"
 msgid "use <n> digits to display object names"
 msgstr ""
 
+msgid "prefixed path to initial superproject"
+msgstr ""
+
 msgid "how to strip spaces and #comments from message"
 msgstr "как удалять пробелы и #комментарии из сообщения коммита"
 
@@ -17322,6 +17766,10 @@ msgstr ""
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr ""
 
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr ""
+
 msgid "object-info: expected flush after arguments"
 msgstr ""
 
@@ -17537,9 +17985,12 @@ msgid ""
 "l, label <label> = label current HEAD with a name\n"
 "t, reset <label> = reset HEAD to a label\n"
 "m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]\n"
-".       create a merge commit using the original merge commit's\n"
-".       message (or the oneline, if no original merge commit was\n"
-".       specified); use -c <commit> to reword the commit message\n"
+"        create a merge commit using the original merge commit's\n"
+"        message (or the oneline, if no original merge commit was\n"
+"        specified); use -c <commit> to reword the commit message\n"
+"u, update-ref <ref> = track a placeholder for the <ref> to be updated\n"
+"                      to this position in the new commits. The <ref> is\n"
+"                      updated at the end of the rebase\n"
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n"
 msgstr ""
@@ -17636,6 +18087,14 @@ msgstr "позади %d"
 msgid "ahead %d, behind %d"
 msgstr "впереди %d, позади %d"
 
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr ""
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr ""
+
 #, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "ожидаемый формат: %%(color:<color>)"
@@ -17652,22 +18111,6 @@ msgstr "Ожидается целочисленное значение refname:l
 msgid "Integer value expected refname:rstrip=%s"
 msgstr "Ожидается целочисленное значение refname:rstrip=%s"
 
-#, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "неопознанный аргумент %%(%s): %s"
-
-#, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr ""
-
-#, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr ""
-
-#, c-format
-msgid "%%(body) does not take arguments"
-msgstr "параметр %%(body) не принимает аргументы"
-
 #, c-format
 msgid "expected %%(trailers:key=<value>)"
 msgstr ""
@@ -17684,10 +18127,6 @@ msgstr "положительное значение ожидает contents:line
 msgid "positive value expected '%s' in %%(%s)"
 msgstr ""
 
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr ""
-
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
 msgstr "ожидаемый формат: %%(align:<width>,<position>)"
@@ -17700,12 +18139,16 @@ msgstr "неопознанная позиция:%s"
 msgid "unrecognized width:%s"
 msgstr "неопознанная ширина:%s"
 
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "неопознанный аргумент %%(%s): %s"
+
 #, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "ожидается положительная ширина с указанием частицы %%(align)"
 
 #, c-format
-msgid "%%(rest) does not take arguments"
+msgid "expected format: %%(ahead-behind:<committish>)"
 msgstr ""
 
 #, c-format
@@ -17997,6 +18440,13 @@ msgstr ""
 msgid "http transport does not support %s"
 msgstr ""
 
+msgid "protocol error: expected '<url> <path>', missing space"
+msgstr ""
+
+#, c-format
+msgid "failed to download file at URL '%s'"
+msgstr ""
+
 msgid "git-http-push failed"
 msgstr ""
 
@@ -18063,7 +18513,6 @@ msgstr "src refspec %s соответствует более чем одному
 #. TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
 #. <remote> <src>:<dst>" push, and "being pushed ('%s')" is
 #. the <src>.
-#.
 #, c-format
 msgid ""
 "The destination you provided is not a full refname (i.e.,\n"
@@ -18305,10 +18754,6 @@ msgstr ""
 msgid "no remembered resolution for '%s'"
 msgstr ""
 
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr ""
-
 #, c-format
 msgid "Updated preimage for '%s'"
 msgstr ""
@@ -18332,6 +18777,21 @@ msgstr ""
 msgid "failed to find tree of %s"
 msgstr "не удалось найти дерево для %s"
 
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr ""
+
+msgid "--exclude-hidden= passed more than once"
+msgstr ""
+
+#, c-format
+msgid "resolve-undo records `%s` which is missing"
+msgstr ""
+
+#, c-format
+msgid "could not get commit for ancestry-path argument %s"
+msgstr ""
+
 msgid "--unpacked=<packfile> no longer supported"
 msgstr ""
 
@@ -18352,6 +18812,175 @@ msgstr ""
 msgid "cannot create async thread: %s"
 msgstr ""
 
+#, c-format
+msgid "'%s' does not exist"
+msgstr "«%s» не существует"
+
+#, c-format
+msgid "could not switch to '%s'"
+msgstr ""
+
+msgid "need a working directory"
+msgstr ""
+
+msgid "Scalar enlistments require a worktree"
+msgstr ""
+
+#, c-format
+msgid "could not configure %s=%s"
+msgstr ""
+
+msgid "could not configure log.excludeDecoration"
+msgstr ""
+
+msgid "could not add enlistment"
+msgstr ""
+
+msgid "could not set recommended config"
+msgstr ""
+
+msgid "could not turn on maintenance"
+msgstr ""
+
+msgid "could not start the FSMonitor daemon"
+msgstr ""
+
+msgid "could not turn off maintenance"
+msgstr ""
+
+msgid "could not remove enlistment"
+msgstr ""
+
+#, c-format
+msgid "remote HEAD is not a branch: '%.*s'"
+msgstr ""
+
+msgid "failed to get default branch name from remote; using local default"
+msgstr ""
+
+msgid "failed to get default branch name"
+msgstr ""
+
+msgid "failed to unregister repository"
+msgstr ""
+
+msgid "failed to stop the FSMonitor daemon"
+msgstr ""
+
+msgid "failed to delete enlistment directory"
+msgstr ""
+
+msgid "branch to checkout after clone"
+msgstr ""
+
+msgid "when cloning, create full working directory"
+msgstr ""
+
+msgid "only download metadata for the branch that will be checked out"
+msgstr ""
+
+msgid "scalar clone [<options>] [--] <repo> [<dir>]"
+msgstr ""
+
+#, c-format
+msgid "cannot deduce worktree name from '%s'"
+msgstr ""
+
+#, c-format
+msgid "directory '%s' exists already"
+msgstr ""
+
+#, c-format
+msgid "failed to get default branch for '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not configure remote in '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not configure '%s'"
+msgstr ""
+
+msgid "partial clone failed; attempting full clone"
+msgstr ""
+
+msgid "could not configure for full clone"
+msgstr ""
+
+msgid "scalar diagnose [<enlistment>]"
+msgstr ""
+
+msgid "`scalar list` does not take arguments"
+msgstr ""
+
+msgid "scalar register [<enlistment>]"
+msgstr ""
+
+msgid "reconfigure all registered enlistments"
+msgstr ""
+
+msgid "scalar reconfigure [--all | <enlistment>]"
+msgstr ""
+
+msgid "--all or <enlistment>, but not both"
+msgstr ""
+
+#, c-format
+msgid "could not remove stale scalar.repo '%s'"
+msgstr ""
+
+#, c-format
+msgid "removing stale scalar.repo '%s'"
+msgstr ""
+
+#, c-format
+msgid "git repository gone in '%s'"
+msgstr ""
+
+msgid ""
+"scalar run <task> [<enlistment>]\n"
+"Tasks:\n"
+msgstr ""
+
+#, c-format
+msgid "no such task: '%s'"
+msgstr ""
+
+msgid "scalar unregister [<enlistment>]"
+msgstr ""
+
+msgid "scalar delete <enlistment>"
+msgstr ""
+
+msgid "refusing to delete current working directory"
+msgstr ""
+
+msgid "include Git version"
+msgstr ""
+
+msgid "include Git's build options"
+msgstr ""
+
+msgid "scalar verbose [-v | --verbose] [--build-options]"
+msgstr ""
+
+msgid "-C requires a <directory>"
+msgstr ""
+
+#, c-format
+msgid "could not change to '%s'"
+msgstr ""
+
+msgid "-c requires a <key>=<value> argument"
+msgstr ""
+
+msgid ""
+"scalar [-C <directory>] [-c <key>=<value>] <command> [<options>]\n"
+"\n"
+"Commands:\n"
+msgstr ""
+
 msgid "unexpected flush packet while reading remote unpack status"
 msgstr "неожиданный пустой пакет при чтении статуса внешней распаковки"
 
@@ -18441,10 +19070,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "не удалось заблокировать «%s»"
 
-#, c-format
-msgid "could not write to '%s'"
-msgstr "не удалось записать в «%s»"
-
 #, c-format
 msgid "could not write eol to '%s'"
 msgstr "не удалось записать eol в «%s»"
@@ -18460,13 +19085,8 @@ msgstr "ваши локальные изменения будут перезап
 msgid "commit your changes or stash them to proceed."
 msgstr "для продолжения закоммитьте ваши изменения или спрячьте их."
 
-#, c-format
-msgid "%s: fast-forward"
-msgstr "%s: быстрая перемотка"
-
 #. TRANSLATORS: %s will be "revert", "cherry-pick" or
 #. "rebase".
-#.
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr "%s: Не удалось записать файл индекса"
@@ -18725,6 +19345,22 @@ msgstr "git %s: сбой чтения индекса"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: сбой обновления индекса"
 
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr ""
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr ""
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr ""
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr ""
+
 #, c-format
 msgid "%s does not accept arguments: '%s'"
 msgstr "параметр %s не принимает аргументы: «%s»"
@@ -18795,11 +19431,8 @@ msgstr ""
 msgid "could not create sequencer directory '%s'"
 msgstr "не удалось создать каталог для указателя следования коммитов «%s»"
 
-msgid "could not lock HEAD"
-msgstr "не удалось заблокировать HEAD"
-
 msgid "no cherry-pick or revert in progress"
-msgstr "копиÑ\80ование Ð¸Ð»Ð¸ Ð¾Ð±Ñ\80аÑ\89ение Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а Ñ\83же выполняются"
+msgstr "копиÑ\80ование Ð¸Ð»Ð¸ Ð¾Ð±Ñ\80аÑ\89ение Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а Ñ\81ейÑ\87аÑ\81 Ð½е выполняются"
 
 msgid "cannot resolve HEAD"
 msgstr "не удалось определить HEAD"
@@ -18885,39 +19518,33 @@ msgstr ""
 "  git rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "и были сделаны изменения в индексе и/или в рабочем каталоге\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr ""
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
-"успешное выполнение: %s\n"
-"но остались изменения в индексе и/или в рабочем каталоге\n"
-"Сделайте коммит или спрячьте ваши изменения, а затем выполните\n"
-"\n"
-"  git rebase --continue\n"
-"\n"
 
 #, c-format
 msgid "illegal label name: '%.*s'"
 msgstr ""
 
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "не удалось распознать «%s»"
+
 msgid "writing fake root commit"
 msgstr "запись поддельного корневого коммита"
 
 msgid "writing squash-onto"
 msgstr "запить уплотнить-над"
 
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "не удалось распознать «%s»"
-
 msgid "cannot merge without a current revision"
 msgstr "нельзя слить без текущей редакции"
 
@@ -18943,6 +19570,23 @@ msgstr "не удалось даже попытаться слить «%.*s»"
 msgid "merge: Unable to write new index file"
 msgstr "слияние: Не удалось записать файл индекса"
 
+#, c-format
+msgid ""
+"another 'rebase' process appears to be running; '%s.lock' already exists"
+msgstr ""
+
+#, c-format
+msgid ""
+"Updated the following refs with %s:\n"
+"%s"
+msgstr ""
+
+#, c-format
+msgid ""
+"Failed to update the following refs with %s:\n"
+"%s"
+msgstr ""
+
 msgid "Cannot autostash"
 msgstr "Не удалось автоматически спрятать изменения"
 
@@ -19099,6 +19743,10 @@ msgstr ""
 msgid "the script was already rearranged."
 msgstr "сценарий уже был перестроен."
 
+#, c-format
+msgid "update-refs file at '%s' is invalid"
+msgstr ""
+
 #, c-format
 msgid "'%s' is outside repository at '%s'"
 msgstr ""
@@ -19225,12 +19873,16 @@ msgstr ""
 
 #, c-format
 msgid ""
-"unsafe repository ('%s' is owned by someone else)\n"
-"To add an exception for this directory, call:\n"
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
 "\n"
 "\tgit config --global --add safe.directory %s"
 msgstr ""
 
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr ""
+
 #, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
@@ -19298,16 +19950,12 @@ msgstr[1] "%u байта/с"
 msgstr[2] "%u байтов/с"
 msgstr[3] "%u байта/с"
 
-#, c-format
-msgid "could not edit '%s'"
-msgstr ""
-
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "игнорирую подозрительный подмодуль с именем: %s"
 
 msgid "negative values not allowed for submodule.fetchJobs"
-msgstr "нельзя использовать отприцательные значения для submodule.fetchJobs"
+msgstr "нельзя использовать отрицательные значения для submodule.fetchJobs"
 
 #, c-format
 msgid "ignoring '%s' which may be interpreted as a command-line option: %s"
@@ -19472,6 +20120,25 @@ msgstr "ls-tree вернул неожиданный код %d"
 msgid "failed to lstat '%s'"
 msgstr ""
 
+msgid "no remote configured to get bundle URIs from"
+msgstr ""
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr ""
+
+msgid "could not get the bundle-uri list"
+msgstr ""
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr ""
+
+msgid "clear the cache tree before each iteration"
+msgstr ""
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr ""
+
 msgid "unhandled options"
 msgstr ""
 
@@ -19806,6 +20473,12 @@ msgstr "Прерываю."
 msgid "failed to push all needed submodules"
 msgstr ""
 
+msgid "bundle-uri operation not supported by protocol"
+msgstr ""
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr ""
+
 msgid "too-short tree object"
 msgstr "слишком  короткий объект дерева"
 
@@ -20077,6 +20750,18 @@ msgstr "неправильный номер порта"
 msgid "invalid '..' path segment"
 msgstr "неправильная часть пути «..»"
 
+msgid "usage: "
+msgstr ""
+
+msgid "fatal: "
+msgstr ""
+
+msgid "error: "
+msgstr ""
+
+msgid "warning: "
+msgstr ""
+
 msgid "Fetching objects"
 msgstr ""
 
@@ -20530,14 +21215,16 @@ msgstr "Игнорируемые файлы"
 
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
+msgstr ""
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr ""
+
+msgid "See 'git help status' for information on how to improve this."
 msgstr ""
-"%.2f сек занял вывод списка неотслеживаемых файлов.\n"
-"«status -uno» возможно ускорит это, но будьте внимательны\n"
-"и не забудьте добавить новые файлы вручную\n"
-"(смотрите «git help status» для подробностей)."
 
 #, c-format
 msgid "Untracked files not listed%s"
@@ -20687,296 +21374,6 @@ msgstr "Вам нужно запускать эту команду находя
 msgid "Unable to determine absolute path of git directory"
 msgstr "Не удалось определить абсолютный путь к каталогу git"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %12s %s"
-
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "тронут %d путь\n"
-msgstr[1] "тронуты %d пути\n"
-msgstr[2] "тронуты %d путей\n"
-msgstr[3] "тронуты %d пути\n"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"индексирования."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"прятанья."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"убирания из индекса."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"применения."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"отмены изменений."
-
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "не удалось открыть файл редактирования блока изменений для записи: %s"
-
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"Чтобы удалить «%s» строки, сделайте их ' ' строками (контекст).\n"
-"Чтобы удалить «%s» строки, удалите их.\n"
-"Строки, начинающиеся с %s будут удалены.\n"
-
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "не удалось открыть файл редактирования блока изменений для чтения: %s"
-
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - индексировать этот блок\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все оставшиеся блоки\n"
-"a - индексировать этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - спрятать этот блок\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - спрятать этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - убрать из индекса этот блок\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - убрать из индекса этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - принять этот блок в индекс\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - принять этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - отбросить этот блок из рабочего дерева\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - отбросить этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - отбросить этот блок из индекса и рабочего дерева\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - отбросить этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - принять этот блок в индекс и рабочее дерево\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - принять этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - принять этот блок в рабочее дерево\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - принять этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - выбрать блок изменений на который нужно перейти\n"
-"/ - поиск блока изменений с помощью регулярного выражения\n"
-"j - не принимать решение по этому блоку, перейти на следующий нерешенный\n"
-"J - не принимать решение по этому блоку, перейти на следующий\n"
-"k - не принимать решение по этому блоку, перейти на предыдущий нерешенный\n"
-"K - не принимать решение по этому блоку, перейти на предыдущий\n"
-"s - разделить текущий блок на блоки меньшего размера\n"
-"e - вручную отредактировать текущий блок\n"
-"? - вывести справку\n"
-
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "Выбранные блоки не применяются без ошибок к индексу!\n"
-
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "игнорирую не слитое: %s\n"
-
-#, perl-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr ""
-
-#, perl-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr ""
-
-#, perl-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr ""
-
-#, perl-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "Принять этот блок в рабочее дерево [y,n,q,a,d%s,?]? "
-
-msgid "No other hunks to goto\n"
-msgstr ""
-
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "Неверный номер: «%s»\n"
-
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "Простите, но только %d блок изменений доступен.\n"
-msgstr[1] "Простите, но только %d блока изменений доступно.\n"
-msgstr[2] "Простите, но только %d блоков изменений доступно.\n"
-msgstr[3] "Простите, но только %d блока изменений доступно.\n"
-
-msgid "No other hunks to search\n"
-msgstr ""
-
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "Регулярное выражение для поиска в неверном формате %s: %s\n"
-
-msgid "No hunk matches the given pattern\n"
-msgstr "Не найдены блоки, которые соответствуют указанному шаблону\n"
-
-msgid "No previous hunk\n"
-msgstr "Нет предыдущего блока\n"
-
-msgid "No next hunk\n"
-msgstr "Не следующего блока\n"
-
-msgid "Sorry, cannot split this hunk\n"
-msgstr ""
-
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "Разбито на %d блок изменений.\n"
-msgstr[1] "Разбито на %d блока изменений.\n"
-msgstr[2] "Разбито на %d блоков изменений.\n"
-msgstr[3] "Разбито на %d блока изменений.\n"
-
-msgid "Sorry, cannot edit this hunk\n"
-msgstr ""
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - показать пути с изменениями\n"
-"update        - добавить состояние рабочей копии в индекс\n"
-"revert        - вернуть проиндексированный набор изменений к HEAD-версии\n"
-"patch         - выбрать и выборочно обновить блоки\n"
-"diff          - просмотреть различия между HEAD и индексом\n"
-"add untracked - добавить содержимое неотслеживаемых файлов в индекс\n"
-
-msgid "missing --"
-msgstr "отсутствует --"
-
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "неизвестный режим для --patch: %s"
-
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "недопустимый аргумент %s, ожидается --"
-
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "локальный часовой пояс отличается от GMT на не минутный интервал\n"
 
@@ -21255,13 +21652,17 @@ msgid "(%s) Could not execute '%s'"
 msgstr "(%s) Не удалось выполнить «%s»"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Добавление %s: %s из: «%s»\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr ""
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) не удалось закрыть поток к «%s»"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Добавление %s: %s из: «%s»\n"
+
 msgid "cannot send message as 7bit"
 msgstr "не удалось отправить сообщение в 7 битной кодировке"
 
index 825142fde7d07ba1efbd7786c3ef76d10b08d593..cd2a86a4a53eed36a7c4c079b18be9c1bfe13631 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -1,22 +1,22 @@
 # Swedish translations for Git.
-# Copyright (C) 2010-2022 Peter Krefting <peter@softwolves.pp.se>
+# Copyright (C) 2010-2023 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-2022.
+# Peter Krefting <peter@softwolves.pp.se>, 2010-2023.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: git 2.38.0\n"
+"Project-Id-Version: git 2.43.0\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-09-26 06:34+0100\n"
-"PO-Revision-Date: 2022-09-26 06:35+0100\n"
+"POT-Creation-Date: 2023-11-09 14:12+0100\n"
+"PO-Revision-Date: 2023-11-09 14:28+0100\n"
 "Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
-"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
+"Language-Team: Svenska <tp-sv@listor.tp-sv.se>\n"
 "Language: sv\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Gtranslator 3.38.0\n"
+"X-Generator: Gtranslator 42.0\n"
 
 #, c-format
 msgid "Huh (%s)?"
@@ -39,24 +39,24 @@ msgstr "Uppdatera"
 
 #, c-format
 msgid "could not stage '%s'"
-msgstr "kunde inte köa \"%s\""
+msgstr "kunde inte köa ”%s”"
 
 msgid "could not write index"
 msgstr "kunde inte skriva indexet"
 
-#, c-format, perl-format
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "uppdaterade %d sökväg\n"
 msgstr[1] "uppdaterade %d sökvägar\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "observera: %s spåras inte längre.\n"
 
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
-msgstr "make_cache_entry misslyckades för sökvägen \"%s\""
+msgstr "make_cache_entry misslyckades för sökvägen ”%s”"
 
 msgid "Revert"
 msgstr "Återställ"
@@ -64,7 +64,7 @@ msgstr "Återställ"
 msgid "Could not parse HEAD^{tree}"
 msgstr "kunde inte tolka HEAD^{tree}"
 
-#, c-format, perl-format
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "återställde %d sökväg\n"
@@ -77,7 +77,7 @@ msgstr "Inga ospårade filer.\n"
 msgid "Add untracked"
 msgstr "Lägg till ospårad"
 
-#, c-format, perl-format
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "lade till %d sökväg\n"
@@ -171,19 +171,19 @@ msgstr "kunde inte uppdatera indexet"
 msgid "Bye.\n"
 msgstr "Hej då.\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "Köa ändrat läge [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Köa borttagning [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Köa tillägg [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Köa stycket [y,n,q,a,d%s,?]? "
 
@@ -207,19 +207,19 @@ msgstr ""
 "a - köa stycket och alla följande i filen\n"
 "d - köa inte stycket eller något av de följande i filen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "Stash:a ändrat läge [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Stash:a borttagning [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Stash:a tillägg [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Stash:a stycket [y,n,q,a,d%s,?]? "
 
@@ -228,7 +228,7 @@ msgid ""
 "stashing."
 msgstr ""
 "Om patchen kan appliceras rent kommer det redigerade stycket att läggas till "
-"i \"stash\" omedelbart."
+"i ”stash” omedelbart."
 
 msgid ""
 "y - stash this hunk\n"
@@ -237,25 +237,25 @@ msgid ""
 "a - stash this hunk and all later hunks in the file\n"
 "d - do not stash this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - \"stash\":a stycket\n"
-"n - \"stash\":a inte stycket\n"
-"q - avsluta; \"stash\":a inte stycket eller något av de följande\n"
-"a - \"stash\":a stycket och alla följande i filen\n"
-"d - \"stash\":a inte stycket eller något av de följande i filen\n"
+"y - ”stash”:a stycket\n"
+"n - ”stash”:a inte stycket\n"
+"q - avsluta; ”stash”:a inte stycket eller något av de följande\n"
+"a - ”stash”:a stycket och alla följande i filen\n"
+"d - ”stash”:a inte stycket eller något av de följande i filen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "Ta bort ändrat läge från kön [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Ta bort borttagning från kön [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Ta bort tillägg från kön [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Ta bort stycket från kön [y,n,q,a,d%s,?]? "
 
@@ -279,19 +279,19 @@ msgstr ""
 "a - ta bort stycket och alla följande i filen från kön\n"
 "d - ta inte bort stycket eller något av de följande i filen från kön\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "Applicera ändrat läge på indexet [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Applicera borttagning på indexet [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Applicera tillägg på indexet [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Applicera stycket på indexet [y,n,q,a,d%s,?]? "
 
@@ -315,19 +315,19 @@ msgstr ""
 "a - applicera stycket och alla följande i filen\n"
 "d - applicera inte stycket eller något av de följande i filen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "Kasta ändrat läge från arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Kasta borttagning från arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Kasta tillägg från arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "Kasta stycket från arbetskatalogen [y,n,q,a,d%s,?]? "
 
@@ -351,19 +351,19 @@ msgstr ""
 "a - förkasta stycket och alla följande i filen\n"
 "d - förkasta inte stycket eller något av de följande i filen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Kasta ändrat läge från indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Kasta borttagning från indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Kasta tillägg från indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Kasta stycket från indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
 
@@ -380,19 +380,19 @@ msgstr ""
 "a - förkasta stycket och alla följande i filen\n"
 "d - förkasta inte stycket eller något av de följande i filen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Applicera ändrat läge på indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Applicera borttagning på indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Applicera tillägg på indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Applicera stycket på indexet och arbetskatalogen [y,n,q,a,d%s,?]? "
 
@@ -409,19 +409,19 @@ msgstr ""
 "a - applicera stycket och alla följande i filen\n"
 "d - applicera inte stycket eller något av de följande i filen\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr "Applicera ändrat läge på arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "Applicera borttagning på arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "Applicera tillägg på arbetskatalogen [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "Applicera stycket på arbetskatalogen [y,n,q,a,d%s,?]? "
 
@@ -440,7 +440,7 @@ msgstr ""
 
 #, c-format
 msgid "could not parse hunk header '%.*s'"
-msgstr "kunde inte tolka styckehuvudet \"%.*s\""
+msgstr "kunde inte tolka styckehuvudet ”%.*s”"
 
 msgid "could not parse diff"
 msgstr "kunde inte tolka diff"
@@ -450,7 +450,7 @@ msgstr "kunde inte tolka färgad diff"
 
 #, c-format
 msgid "failed to run '%s'"
-msgstr "misslyckades att köra \"%s\""
+msgstr "misslyckades att köra ”%s”"
 
 msgid "mismatched output from interactive.diffFilter"
 msgstr "omaka utdata från interactive.diffFilter"
@@ -493,12 +493,10 @@ msgid ""
 "Lines starting with %c will be removed.\n"
 msgstr ""
 "---\n"
-"Ta bort \"%c\" rader genom att göra dem \" \"-rader (sammanhang).\n"
-"Ta bort \"%c\" rader genom att radera dem.\n"
+"Ta bort ”%c” rader genom att göra dem ” ”-rader (sammanhang).\n"
+"Ta bort ”%c” rader genom att radera dem.\n"
 "Rader som börjar med %c kommer att tas bort.\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -512,27 +510,19 @@ msgid "could not parse hunk header"
 msgstr "kunde inte tolka styckehuvud"
 
 msgid "'git apply --cached' failed"
-msgstr "\"git apply --cached\" misslyckades"
+msgstr "”git apply --cached” misslyckades"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
-"Ditt redigerade stycke kan inte appliceras. Redigera igen (\"nej\" kastar!) "
-"[y/n]? "
+"Ditt redigerade stycke kan inte appliceras. Redigera igen (”nej” kastar!) [y/"
+"n]? "
 
 msgid "The selected hunks do not apply to the index!"
 msgstr "Markerade stycken kan inte appliceras på indexet!"
@@ -581,7 +571,7 @@ msgstr "gå till vilket stycke? "
 
 #, c-format
 msgid "Invalid number: '%s'"
-msgstr "Ogiltigt siffervärde: \"%s\""
+msgstr "Ogiltigt siffervärde: ”%s”"
 
 #, c-format
 msgid "Sorry, only %d hunk available."
@@ -613,7 +603,7 @@ msgid "Sorry, cannot edit this hunk"
 msgstr "Beklagar, kan inte redigera stycket"
 
 msgid "'git apply' failed"
-msgstr "\"git apply\" misslyckades"
+msgstr "”git apply” misslyckades"
 
 #, c-format
 msgid ""
@@ -621,7 +611,7 @@ msgid ""
 "Disable this message with \"git config advice.%s false\""
 msgstr ""
 "\n"
-"Slå av meddelandet med \"git config advice.%s false\""
+"Slå av meddelandet med ”git config advice.%s false”"
 
 #, c-format
 msgid "%shint: %.*s%s\n"
@@ -629,13 +619,11 @@ msgstr "%stips: %.*s%s\n"
 
 msgid "Cherry-picking is not possible because you have unmerged files."
 msgstr ""
-"Du kan inte utföra en cherry-pick eftersom du har filer som inte slagits "
+"Du kan inte utföra en ”cherry-pick” eftersom du har filer som inte slagits "
 "samman."
 
 msgid "Committing is not possible because you have unmerged files."
-msgstr ""
-"Du kan inte utföra en incheckning eftersom du har filer som inte slagits "
-"samman."
+msgstr "Du kan inte checka in eftersom du har filer som inte slagits samman."
 
 msgid "Merging is not possible because you have unmerged files."
 msgstr ""
@@ -644,23 +632,19 @@ msgstr ""
 
 msgid "Pulling is not possible because you have unmerged files."
 msgstr ""
-"Du kan inte utföra en \"pull\" eftersom du har filer som inte slagits samman."
+"Du kan inte utföra en ”pull” eftersom du har filer som inte slagits samman."
 
 msgid "Reverting is not possible because you have unmerged files."
-msgstr ""
-"Du kan inte utföra en \"revert\" eftersom du har filer som inte slagits "
-"samman."
+msgstr "Du kan inte återställa eftersom du har filer som inte slagits samman."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr ""
-"Du kan inte utföra en \"%s\" eftersom du har filer som inte slagits samman."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Du kan inte ombasera eftersom du har filer som inte slagits samman."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
 "as appropriate to mark resolution and make a commit."
 msgstr ""
-"Rätta dem i din arbetskatalog och använd sedan \"git add/rm <fil>\"\n"
+"Rätta dem i din arbetskatalog och använd sedan ”git add/rm <fil>”\n"
 "som lämpligt för att ange lösning och checka in."
 
 msgid "Exiting because of an unresolved conflict."
@@ -675,6 +659,23 @@ msgstr "Checka in dina ändringar innan du utför sammanslagningen."
 msgid "Exiting because of unfinished merge."
 msgstr "Avslutar på grund av ofullbordad sammanslagning."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Divergerande grenar kan inte snabbspolas, du måste antingen använda:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"eller:\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Kan inte snabbspola, avbryter."
 
@@ -685,7 +686,7 @@ msgid ""
 "updated in the index:\n"
 msgstr ""
 "Följande sökvägar och/eller sökvägsangivelser motsvarar sökvägar\n"
-"utanför din \"sparse-checkout\"-definition, så de kommer inte\n"
+"utanför din ”sparse-checkout”-definition, så de kommer inte\n"
 "uppdateras i indexet:\n"
 
 msgid ""
@@ -718,9 +719,9 @@ msgid ""
 "false\n"
 "\n"
 msgstr ""
-"Observera: checkar ut \"%s\".\n"
+"Observera: checkar ut ”%s”.\n"
 "\n"
-"Du har nu ett \"frånkopplat HEAD\". Du kan se dig omkring, experimentera\n"
+"Du har nu ett ”frånkopplat HEAD”. Du kan se dig omkring, experimentera\n"
 "med ändringar och checka in dem, och du kan kasta incheckningar du gör\n"
 "i det här läget utan att det påverkar grenar genom att växla tillbaka\n"
 "till en gren.\n"
@@ -746,7 +747,7 @@ msgid ""
 "modifications.\n"
 msgstr ""
 "Följande sökvägar har flyttats ut från din\n"
-"\"sparse-checkout\"-definition, men är inte glesa på grund av\n"
+"”sparse-checkout”-definition, men är inte glesa på grund av\n"
 "lokala ändringar.\n"
 
 msgid ""
@@ -755,8 +756,8 @@ msgid ""
 "* Use \"git sparse-checkout reapply\" to apply the sparsity rules"
 msgstr ""
 "För att korrigera glesheten för dessa sökvägar, gör följande:\n"
-"* Använd \"git add --sparse <sökväg>\" för att uppdatera indexet\n"
-"* Använd \"git sparse-checkout reapply\" för att tillämpa gleshetsreglerna"
+"* Använd ”git add --sparse <sökväg>” för att uppdatera indexet\n"
+"* Använd ”git sparse-checkout reapply” för att tillämpa gleshetsreglerna"
 
 msgid "cmdline ends with \\"
 msgstr "kommandorad avslutas med \\"
@@ -764,21 +765,30 @@ msgstr "kommandorad avslutas med \\"
 msgid "unclosed quote"
 msgstr "citat ej stängt"
 
+msgid "too many arguments"
+msgstr "för många argument"
+
 #, c-format
 msgid "unrecognized whitespace option '%s'"
-msgstr "okänt alternativ för whitespace: \"%s\""
+msgstr "okänt alternativ för whitespace: ”%s”"
 
 #, c-format
 msgid "unrecognized whitespace ignore option '%s'"
-msgstr "okänt alternativ för ignore-whitespace: \"%s\""
+msgstr "okänt alternativ för ignore-whitespace: ”%s”"
 
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
-msgstr "flaggorna \"%s\" och \"%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%s” och ”%s” kan inte användas samtidigt"
 
 #, c-format
 msgid "'%s' outside a repository"
-msgstr "\"%s\" utanför arkiv"
+msgstr "”%s” utanför arkiv"
+
+msgid "failed to read patch"
+msgstr "misslyckades läsa patchen"
+
+msgid "patch too large"
+msgstr "patchen är för stor"
 
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
@@ -887,7 +897,7 @@ msgstr "kunde inte öppna eller läsa %s"
 
 #, c-format
 msgid "invalid start of line: '%c'"
-msgstr "felaktig inledning på rad: \"%c\""
+msgstr "felaktig inledning på rad: ”%c”"
 
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
@@ -909,41 +919,38 @@ msgstr ""
 
 #, c-format
 msgid "missing binary patch data for '%s'"
-msgstr "saknar binära patchdata för \"%s\""
+msgstr "saknar binära patchdata för ”%s”"
 
 #, c-format
 msgid "cannot reverse-apply a binary patch without the reverse hunk to '%s'"
 msgstr ""
-"kan inte applicera en binärpatch baklänges utan den omvända patchen för \"%s"
-"\""
+"kan inte applicera en binärpatch baklänges utan den omvända patchen för ”%s”"
 
 #, c-format
 msgid "cannot apply binary patch to '%s' without full index line"
-msgstr ""
-"kan inte applicera binärpatch på \"%s\" utan den fullständiga indexraden"
+msgstr "kan inte applicera binärpatch på ”%s” utan den fullständiga indexraden"
 
 #, c-format
 msgid ""
 "the patch applies to '%s' (%s), which does not match the current contents."
 msgstr ""
-"patchen appliceras på \"%s\" (%s), som inte motsvarar det nuvarande "
-"innehållet."
+"patchen appliceras på ”%s” (%s), som inte motsvarar det nuvarande innehållet."
 
 #, c-format
 msgid "the patch applies to an empty '%s' but it is not empty"
-msgstr "patchen appliceras på en tom \"%s\", men den är inte tom"
+msgstr "patchen appliceras på en tom ”%s”, men den är inte tom"
 
 #, c-format
 msgid "the necessary postimage %s for '%s' cannot be read"
-msgstr "nödvändig efterbild %s för \"%s\" kan inte läsas"
+msgstr "nödvändig efterbild %s för ”%s” kan inte läsas"
 
 #, c-format
 msgid "binary patch does not apply to '%s'"
-msgstr "binärpatchen kan inte tillämpas på \"%s\""
+msgstr "binärpatchen kan inte tillämpas på ”%s”"
 
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
-msgstr "binärpatchen på \"%s\" ger felaktigt resultat (förväntade %s, fick %s)"
+msgstr "binärpatchen på ”%s” ger felaktigt resultat (förväntade %s, fick %s)"
 
 #, c-format
 msgid "patch failed: %s:%ld"
@@ -959,7 +966,7 @@ msgstr "misslyckades läsa %s"
 
 #, c-format
 msgid "reading from '%s' beyond a symbolic link"
-msgstr "läser från \"%s\" som är på andra sidan av en symbolisk länk"
+msgstr "läser från ”%s” som är på andra sidan av en symbolisk länk"
 
 #, c-format
 msgid "path %s has been renamed/deleted"
@@ -982,7 +989,7 @@ msgstr "Utför trevägssammanslagning...\n"
 
 #, c-format
 msgid "cannot read the current contents of '%s'"
-msgstr "kunde inte läsa aktuellt innehåll i \"%s\""
+msgstr "kunde inte läsa aktuellt innehåll i ”%s”"
 
 #, c-format
 msgid "Failed to perform three-way merge...\n"
@@ -990,11 +997,11 @@ msgstr "Misslyckades utföra trevägssammanslagning...\n"
 
 #, c-format
 msgid "Applied patch to '%s' with conflicts.\n"
-msgstr "Applicerade patchen på \"%s\" med konflikter.\n"
+msgstr "Applicerade patchen på ”%s” med konflikter.\n"
 
 #, c-format
 msgid "Applied patch to '%s' cleanly.\n"
-msgstr "Tillämpade patchen på  \"%s\" rent.\n"
+msgstr "Tillämpade patchen på  ”%s” rent.\n"
 
 #, c-format
 msgid "Falling back to direct application...\n"
@@ -1013,7 +1020,7 @@ msgstr "%s har typen %o, förväntade %o"
 
 #, c-format
 msgid "invalid path '%s'"
-msgstr "ogiltig sökväg \"%s\""
+msgstr "ogiltig sökväg ”%s”"
 
 #, c-format
 msgid "%s: already exists in index"
@@ -1033,7 +1040,7 @@ msgstr "nytt läge (%o) för %s motsvarar inte gammalt läge (%o) för %s"
 
 #, c-format
 msgid "affected file '%s' is beyond a symbolic link"
-msgstr "den berörda filen \"%s\" är på andra sidan av en symbolisk länk"
+msgstr "den berörda filen ”%s” är på andra sidan av en symbolisk länk"
 
 #, c-format
 msgid "%s: patch does not apply"
@@ -1073,7 +1080,7 @@ msgstr "trasig patch för undermodulen %s"
 
 #, c-format
 msgid "unable to stat newly created file '%s'"
-msgstr "kan inte ta status på nyligen skapade filen \"%s\""
+msgstr "kan inte ta status på nyligen skapade filen ”%s”"
 
 #, c-format
 msgid "unable to create backing store for newly created file %s"
@@ -1085,15 +1092,15 @@ msgstr "kan inte lägga till cachepost för %s"
 
 #, c-format
 msgid "failed to write to '%s'"
-msgstr "misslyckades skriva till \"%s\""
+msgstr "misslyckades skriva till ”%s”"
 
 #, c-format
 msgid "closing file '%s'"
-msgstr "stänger filen \"%s\""
+msgstr "stänger filen ”%s”"
 
 #, c-format
 msgid "unable to write file '%s' mode %o"
-msgstr "kan inte skriva filen \"%s\" läge %o"
+msgstr "kan inte skriva filen ”%s” läge %o"
 
 #, c-format
 msgid "Applied patch %s cleanly."
@@ -1116,6 +1123,10 @@ msgstr "trunkerar .rej-filnamnet till %.*s.rej"
 msgid "cannot open %s"
 msgstr "kan inte öppna %s"
 
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "kan inte ta bort länken ”%s”"
+
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Stycke %d tillämpades rent."
@@ -1126,17 +1137,17 @@ msgstr "Refuserar stycke %d."
 
 #, c-format
 msgid "Skipped patch '%s'."
-msgstr "Ignorerar patch \"%s\"."
+msgstr "Ignorerar patch ”%s”."
 
 msgid "No valid patches in input (allow with \"--allow-empty\")"
-msgstr "Inga giltiga patchar i indata (tillåt med \"--allow-empty\")"
+msgstr "Inga giltiga patchar i indata (tillåt med ”--allow-empty”)"
 
 msgid "unable to read index file"
 msgstr "kan inte läsa indexfilen"
 
 #, c-format
 msgid "can't open patch '%s': %s"
-msgstr "kan inte öppna patchen \"%s\": %s"
+msgstr "kan inte öppna patchen ”%s”: %s"
 
 #, c-format
 msgid "squelched %d whitespace error"
@@ -1190,7 +1201,7 @@ msgid "make sure the patch is applicable to the current index"
 msgstr "se till att patchen kan tillämpas på aktuellt index"
 
 msgid "mark new files with `git add --intent-to-add`"
-msgstr "markera nya filer med \"git add --intent-to-add\""
+msgstr "markera nya filer med ”git add --intent-to-add”"
 
 msgid "apply a patch without touching the working tree"
 msgstr "tillämpa en patch utan att röra arbetskatalogen"
@@ -1265,14 +1276,14 @@ msgstr "fel i deflate (%d)"
 
 #, c-format
 msgid "unable to start '%s' filter"
-msgstr "kunde inte starta filtret \"%s\""
+msgstr "kunde inte starta filtret ”%s”"
 
 msgid "unable to redirect descriptor"
 msgstr "kan inte omdirigera handtag"
 
 #, c-format
 msgid "'%s' filter reported error"
-msgstr "filtret \"%s\" rapporterade fel"
+msgstr "filtret ”%s” rapporterade fel"
 
 #, c-format
 msgid "path is not valid UTF-8: %s"
@@ -1300,11 +1311,15 @@ msgstr "git archive --remote <arkiv> [--exec <kmd>] --list"
 
 #, c-format
 msgid "cannot read '%s'"
-msgstr "kunde inte läsa \"%s\""
+msgstr "kunde inte läsa ”%s”"
+
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "sökvägsangivelsen ”%s” motsvarar filer utanför aktuell katalog"
 
 #, c-format
 msgid "pathspec '%s' did not match any files"
-msgstr "sökvägsangivelsen \"%s\" motsvarade inte några filer"
+msgstr "sökvägsangivelsen ”%s” motsvarade inte några filer"
 
 #, c-format
 msgid "no such ref: %.*s"
@@ -1318,9 +1333,6 @@ msgstr "objektnamnet är inte giltigt: %s"
 msgid "not a tree object: %s"
 msgstr "inte ett trädobjekt: %s"
 
-msgid "current working directory is untracked"
-msgstr "aktuell arbetskatalog är inte spårad"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Hittar inte filen: %s"
@@ -1331,15 +1343,15 @@ msgstr "Inte en vanlig fil: %s"
 
 #, c-format
 msgid "unclosed quote: '%s'"
-msgstr "citat ej stängt: \"%s\""
+msgstr "citat ej stängt: ”%s”"
 
 #, c-format
 msgid "missing colon: '%s'"
-msgstr "kolon saknas: \"%s\""
+msgstr "kolon saknas: ”%s”"
 
 #, c-format
 msgid "empty file name: '%s'"
-msgstr "tomt filnamn: \"%s\""
+msgstr "tomt filnamn: ”%s”"
 
 msgid "fmt"
 msgstr "fmt"
@@ -1371,6 +1383,12 @@ msgstr "läs .gitattributes i arbetskatalogen"
 msgid "report archived files on stderr"
 msgstr "rapportera arkiverade filer på standard fel"
 
+msgid "time"
+msgstr "tid"
+
+msgid "set modification time of archive entries"
+msgstr "välj modifieringstid för arkivposter"
+
 msgid "set compression level"
 msgstr "välj komprimeringsgrad"
 
@@ -1394,23 +1412,30 @@ msgstr "Oväntad flagga --remote"
 
 #, c-format
 msgid "the option '%s' requires '%s'"
-msgstr "flaggan \"%s\" kräver \"%s\""
+msgstr "flaggan ”%s” kräver ”%s”"
 
 msgid "Unexpected option --output"
 msgstr "Oväntad flagga --output"
 
 #, c-format
 msgid "Unknown archive format '%s'"
-msgstr "Okänt arkivformat \"%s\""
+msgstr "Okänt arkivformat ”%s”"
 
 #, c-format
 msgid "Argument not supported for format '%s': -%d"
-msgstr "Argumentet stöd inte för formatet \"%s\": -%d"
+msgstr "Argumentet stöd inte för formatet ”%s”: -%d"
 
 #, c-format
 msgid "%.*s is not a valid attribute name"
 msgstr "%-*s är inte ett giltigt namn på attribut"
 
+msgid "unable to add additional attribute"
+msgstr "Kunde inte lägga till ytterligare attribut"
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "ignorerar överlång attributrad %d"
+
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "%s inte tillåtet: %s:%d"
@@ -1422,13 +1447,28 @@ msgstr ""
 "Negativa mönster ignoreras i git-attribut\n"
 "Använd '\\!' för att inleda med ett utropstecken."
 
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "kan inte utföra fstat på gitattributes-filen ”%s”"
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "ignorerar allt för stor gitattributes-fil ”%s”"
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "ignorerar allt för stor gitattributes-objekt ”%s”"
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "felaktig --attr-source eller GIT_ATTR_SOURCE"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
-msgstr "Felaktigt citerat innehåll i filen \"%s\": %s"
+msgstr "Felaktigt citerat innehåll i filen ”%s”: %s"
 
 #, c-format
 msgid "We cannot bisect more!\n"
-msgstr "Det finns inte mer att göra \"bisect\" på!\n"
+msgstr "Det finns inte mer att göra ”bisect” på!\n"
 
 #, c-format
 msgid "Not a valid commit name %s"
@@ -1456,7 +1496,7 @@ msgid ""
 "This means the first '%s' commit is between %s and [%s].\n"
 msgstr ""
 "Sammanslagningsbasen %s är %s.\n"
-"Det betyder att den första \"%s\" incheckningen är mellan %s och [%s].\n"
+"Det betyder att den första ”%s” incheckningen är mellan %s och [%s].\n"
 
 #, c-format
 msgid ""
@@ -1489,11 +1529,11 @@ msgstr "en %s-revision behövs"
 
 #, c-format
 msgid "could not create file '%s'"
-msgstr "kunde inte skapa filen \"%s\""
+msgstr "kunde inte skapa filen ”%s”"
 
 #, c-format
 msgid "could not read file '%s'"
-msgstr "kunde inte läsa filen \"%s\""
+msgstr "kunde inte läsa filen ”%s”"
 
 msgid "reading bisect refs failed"
 msgstr "misslyckades läsa bisect-referenser"
@@ -1528,9 +1568,6 @@ msgstr[1] "Bisect: %d revisioner kvar att testa efter denna %s\n"
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents och --reverse fungerar inte så bra tillsammans."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "kan inte använda --contents med namn på slutgiltigt incheckningsobjekt"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse och --first-parent tillsammans kräver att du anger senaste "
@@ -1566,15 +1603,15 @@ msgstr "ställer inte in grenen %s som sin egen uppströmsgren"
 
 #, c-format
 msgid "branch '%s' set up to track '%s' by rebasing."
-msgstr "grenen \"%s\" inställd på att spåra \"%s\" genom ombasering."
+msgstr "grenen ”%s” inställd på att spåra ”%s” genom ombasering."
 
 #, c-format
 msgid "branch '%s' set up to track '%s'."
-msgstr "grenen \"%s\" inställd på att spåra \"%s\"."
+msgstr "grenen ”%s” inställd på att spåra ”%s”."
 
 #, c-format
 msgid "branch '%s' set up to track:"
-msgstr "grenen \"%s\" inställd på att spåra:"
+msgstr "grenen ”%s” inställd på att spåra:"
 
 msgid "unable to write upstream branch configuration"
 msgstr "kan inte skriva inställningar för uppströmsgren"
@@ -1590,17 +1627,17 @@ msgstr ""
 
 #, c-format
 msgid "asked to inherit tracking from '%s', but no remote is set"
-msgstr "bad om att ärva spårning från \"%s\", men ingen fjärr är vald"
+msgstr "bad om att ärva spårning från ”%s”, men ingen fjärr är vald"
 
 #, c-format
 msgid "asked to inherit tracking from '%s', but no merge configuration is set"
 msgstr ""
-"bad om att ärva spårning från \"%s\", men ingen sammanslagningsinställning "
-"är vald"
+"bad om att ärva spårning från ”%s”, men ingen sammanslagningsinställning är "
+"vald"
 
 #, c-format
 msgid "not tracking: ambiguous information for ref '%s'"
-msgstr "spårar inte: tvetydig information för referensen \"%s\""
+msgstr "spårar inte: tvetydig information för referensen ”%s”"
 
 #. #-#-#-#-#  branch.c.po  #-#-#-#-#
 #. TRANSLATORS: This is a line listing a remote with duplicate
@@ -1632,7 +1669,7 @@ msgid ""
 "tracking namespaces."
 msgstr ""
 "Flera fjärrars hämtnings-referensspecifikationer motsvarar fjärr-\n"
-"spårningsreferensen \"%s\":\n"
+"spårningsreferensen ”%s”:\n"
 "%s\n"
 "Detta är vanligtvis ett fel i konfigurationen.\n"
 "\n"
@@ -1642,25 +1679,26 @@ msgstr ""
 
 #, c-format
 msgid "'%s' is not a valid branch name"
-msgstr "\"%s\" är inte ett giltigt grennamn"
+msgstr "”%s” är inte ett giltigt grennamn"
 
 #, c-format
 msgid "a branch named '%s' already exists"
-msgstr "det finns redan en gren som heter \"%s\""
+msgstr "det finns redan en gren som heter ”%s”"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
 msgstr ""
-"kan inte tvinga uppdatering av grenen \"%s\" som är utcheckad på \"%s\""
+"kan inte tvinga uppdatering av grenen ”%s” som används av arbetskatalogen på "
+"”%s”"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
 msgstr ""
-"kan inte ställa in spårningsinformation; startpunkten \"%s\" är inte en gren"
+"kan inte ställa in spårningsinformation; startpunkten ”%s” är inte en gren"
 
 #, c-format
 msgid "the requested upstream branch '%s' does not exist"
-msgstr "den efterfrågade uppströmsgrenen \"%s\" finns inte"
+msgstr "den efterfrågade uppströmsgrenen ”%s” finns inte"
 
 msgid ""
 "\n"
@@ -1674,89 +1712,78 @@ msgid ""
 msgstr ""
 "\n"
 "Om du har tänkt basera ditt arbete på en uppströmsgren\n"
-"som redan finns på fjärren kan du behöva köra \"git fetch\"\n"
+"som redan finns på fjärren kan du behöva köra ”git fetch”\n"
 "för att hämta den.\n"
 "\n"
 "Om du har tänkt sända in en ny lokal gren som ska\n"
-"spåra dess fjärrmotsvarighet kan du använda \"git push -u\"\n"
+"spåra dess fjärrmotsvarighet kan du använda ”git push -u”\n"
 "för att ställa in uppströmskonfigurationen när du sänder in."
 
 #, c-format
 msgid "not a valid object name: '%s'"
-msgstr "objektnamnet är inte giltigt: \"%s\""
+msgstr "objektnamnet är inte giltigt: ”%s”"
 
 #, c-format
 msgid "ambiguous object name: '%s'"
-msgstr "objektnamnet är tvetydigt: \"%s\""
+msgstr "objektnamnet är tvetydigt: ”%s”"
 
 #, c-format
 msgid "not a valid branch point: '%s'"
-msgstr "avgreningspunkten är inte giltig: \"%s\""
+msgstr "avgreningspunkten är inte giltig: ”%s”"
 
 #, c-format
 msgid "submodule '%s': unable to find submodule"
-msgstr "undermodulen \"%s\": kan inte hitta undermodulen"
+msgstr "undermodulen ”%s”: kan inte hitta undermodulen"
 
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
-"Du kan försöka uppdatera undermodulerna med \"git checkout %s && git "
-"submodule update --init\""
+"Du kan försöka uppdatera undermodulerna med ”git checkout --no-recurse-"
+"submodules %s && git submodule update --init”"
 
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
-msgstr "undermodulen \"%s\": kan inte skapa grenen \"%s\""
-
-#, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "\"%s\" är redan utcheckad på \"%s\""
+msgstr "undermodulen ”%s”: kan inte skapa grenen ”%s”"
 
 #, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD i arbetskatalogen %s har inte uppdaterats"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "”%s” används redan av arbetskatalogen ”%s”"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<flaggor>] [--] <sökväg>..."
 
 #, c-format
 msgid "cannot chmod %cx '%s'"
-msgstr "kan inte utföra chmod %cx \"%s\""
-
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "diff-status %c förväntades inte"
-
-msgid "updating files failed"
-msgstr "misslyckades uppdatera filer"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "ta bort \"%s\"\n"
+msgstr "kan inte utföra chmod %cx ”%s”"
 
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Oköade ändringar efter att ha uppdaterat indexet:"
 
-msgid "Could not read the index"
-msgstr "Kunde inte läsa indexet"
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"inställningen add.interactive.useBuiltin har tagits bort!\n"
+"Se dess post i ”git help config” för detaljer."
 
-msgid "Could not write patch"
-msgstr "Kunde inte skriva patch"
+msgid "could not read the index"
+msgstr "kunde inte läsa indexet"
 
 msgid "editing patch failed"
 msgstr "redigering av patch misslyckades"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Kunde inte ta status på \"%s\""
+msgid "could not stat '%s'"
+msgstr "kunde inte ta status på ”%s”"
 
-msgid "Empty patch. Aborted."
-msgstr "Tom patch. Avbryter."
+msgid "empty patch. aborted"
+msgstr "tom patch. avbryter"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Kunde inte tillämpa \"%s\""
+msgid "could not apply '%s'"
+msgstr "kunde inte tillämpa ”%s”"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr "Följande sökvägar ignoreras av en av dina .gitignore-filer:\n"
@@ -1804,7 +1831,7 @@ msgid "check if - even missing - files are ignored in dry run"
 msgstr "se om - även saknade - filer ignoreras i testkörning"
 
 msgid "allow updating entries outside of the sparse-checkout cone"
-msgstr "tillåt uppdatera poster utanför området angivet i \"sparse-checkout\""
+msgstr "tillåt uppdatera poster utanför området angivet i ”sparse-checkout”"
 
 msgid "override the executable bit of the listed files"
 msgstr "överstyr exekveringsbiten för angivna filer"
@@ -1840,7 +1867,7 @@ msgstr ""
 "\n"
 "\tgit rm --cached %s\n"
 "\n"
-"Se \"git help submodule\" för ytterligare information."
+"Se ”git help submodule” för ytterligare information."
 
 #, c-format
 msgid "adding embedded git repository: %s"
@@ -1853,18 +1880,18 @@ msgid ""
 msgstr ""
 "Använd -f om du verkligen vill lägga till dem.\n"
 "Slå av detta meddelande med\n"
-"\"git config advice.addIgnoredFile false\""
+"”git config advice.addIgnoredFile false”"
 
 msgid "adding files failed"
 msgstr "misslyckades lägga till filer"
 
 #, c-format
 msgid "--chmod param '%s' must be either -x or +x"
-msgstr "\"--chmod\"-parametern \"%s\" måste antingen vara -x eller +x"
+msgstr "”--chmod”-parametern ”%s” måste antingen vara -x eller +x"
 
 #, c-format
 msgid "'%s' and pathspec arguments cannot be used together"
-msgstr "\"%s\" kan inte användas tillsammans med sökvägsangivelser"
+msgstr "”%s” kan inte användas tillsammans med sökvägsangivelser"
 
 #, c-format
 msgid "Nothing specified, nothing added.\n"
@@ -1875,24 +1902,27 @@ msgid ""
 "Turn this message off by running\n"
 "\"git config advice.addEmptyPathspec false\""
 msgstr ""
-"Tänkte du kanske säga \"git add .\"?\n"
+"Tänkte du kanske säga ”git add .”?\n"
 "Slå av detta meddelande genom att köra\n"
-"\"git config advice.addEmptyPathspec false\""
+"”git config advice.addEmptyPathspec false”"
 
 msgid "index file corrupt"
 msgstr "indexfilen trasig"
 
+msgid "unable to write new index file"
+msgstr "kunde inte skriva ny indexfil"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
-msgstr "felaktig funktion \"%s\" för \"%s\""
+msgstr "felaktig funktion ”%s” för ”%s”"
 
 #, c-format
 msgid "invalid value for '%s': '%s'"
-msgstr "felaktigt värde för \"%s\": \"%s\""
+msgstr "felaktigt värde för ”%s”: ”%s”"
 
 #, c-format
 msgid "could not read '%s'"
-msgstr "kunde inte läsa \"%s\""
+msgstr "kunde inte läsa ”%s”"
 
 msgid "could not parse author script"
 msgstr "kunde inte tolka författarskript"
@@ -1903,30 +1933,30 @@ msgstr "kunde inte tolka %s"
 
 #, c-format
 msgid "'%s' was deleted by the applypatch-msg hook"
-msgstr "\"%s\" togs bort av kroken applypatch-msg"
+msgstr "”%s” togs bort av kroken applypatch-msg"
 
 #, c-format
 msgid "Malformed input line: '%s'."
-msgstr "Felaktig indatarad: \"%s\"."
+msgstr "Felaktig indatarad: ”%s”."
 
 #, c-format
 msgid "Failed to copy notes from '%s' to '%s'"
-msgstr "Misslyckades kopiera anteckningar från \"%s\" till \"%s\""
+msgstr "Misslyckades kopiera anteckningar från ”%s” till ”%s”"
 
 msgid "fseek failed"
-msgstr "\"fseek\" misslyckades"
+msgstr "”fseek” misslyckades"
 
 #, c-format
 msgid "could not open '%s' for reading"
-msgstr "kunde inte öppna \"%s\" för läsning"
+msgstr "kunde inte öppna ”%s” för läsning"
 
 #, c-format
 msgid "could not open '%s' for writing"
-msgstr "kunde inte öppna \"%s\" för skrivning"
+msgstr "kunde inte öppna ”%s” för skrivning"
 
 #, c-format
 msgid "could not parse patch '%s'"
-msgstr "kunde inte tolka patchen \"%s\""
+msgstr "kunde inte tolka patchen ”%s”"
 
 msgid "Only one StGIT patch series can be applied at once"
 msgstr "Endast en StGIT-patchserie kan tillämpas åt gången"
@@ -1935,7 +1965,7 @@ msgid "invalid timestamp"
 msgstr "ogiltig tidsstämpel"
 
 msgid "invalid Date line"
-msgstr "ogiltig \"Date\"-rad"
+msgstr "ogiltig ”Date”-rad"
 
 msgid "invalid timezone offset"
 msgstr "ogiltig tidszons-offset"
@@ -1945,29 +1975,29 @@ msgstr "Misslyckades detektera patchformat."
 
 #, c-format
 msgid "failed to create directory '%s'"
-msgstr "misslyckades skapa katalogen \"%s\""
+msgstr "misslyckades skapa katalogen ”%s”"
 
 msgid "Failed to split patches."
 msgstr "Misslyckades dela patchar."
 
 #, c-format
 msgid "When you have resolved this problem, run \"%s --continue\"."
-msgstr "När du har löst problemet, kör \"%s --continue\"."
+msgstr "När du har löst problemet, kör ”%s --continue”."
 
 #, c-format
 msgid "If you prefer to skip this patch, run \"%s --skip\" instead."
-msgstr "Om du hellre vill hoppa över patchen, kör \"%s --skip\" i stället."
+msgstr "Om du hellre vill hoppa över patchen, kör ”%s --skip” i stället."
 
 #, c-format
 msgid "To record the empty patch as an empty commit, run \"%s --allow-empty\"."
 msgstr ""
-"För att registrera den tomma patchen som en tom incheckning, kör \"%s --"
-"allow-empty\"."
+"För att registrera den tomma patchen som en tom incheckning, kör ”%s --allow-"
+"empty”."
 
 #, c-format
 msgid "To restore the original branch and stop patching, run \"%s --abort\"."
 msgstr ""
-"För att återgå till ursprunglig gren och sluta patcha, kör \"%s --abort\"."
+"För att återgå till ursprunglig gren och sluta patcha, kör ”%s --abort”."
 
 msgid "Patch sent with format=flowed; space at the end of lines might be lost."
 msgstr ""
@@ -1975,7 +2005,7 @@ msgstr ""
 
 #, c-format
 msgid "missing author line in commit %s"
-msgstr "saknad \"author\"-rad i incheckningen %s"
+msgstr "saknad ”author”-rad i incheckningen %s"
 
 #, c-format
 msgid "invalid ident line: %.*s"
@@ -2062,8 +2092,7 @@ msgstr "Patch misslyckades på %s %.*s"
 
 msgid "Use 'git am --show-current-patch=diff' to see the failed patch"
 msgstr ""
-"Använd \"git am --show-current-patch=diff\" för att se patchen som "
-"misslyckades"
+"Använd ”git am --show-current-patch=diff” för att se patchen som misslyckades"
 
 msgid "No changes - recorded it as an empty commit."
 msgstr "Inga ändringar - sparat som en tom incheckning."
@@ -2073,7 +2102,7 @@ msgid ""
 "If there is nothing left to stage, chances are that something else\n"
 "already introduced the same changes; you might want to skip this patch."
 msgstr ""
-"Inga ändringar - glömde du att använda \"git add\"?\n"
+"Inga ändringar - glömde du att använda ”git add”?\n"
 "Om det inte är något kvar att köa kan det hända att något annat redan\n"
 "introducerat samma ändringar; kanske du bör hoppa över patchen."
 
@@ -2084,16 +2113,13 @@ msgid ""
 "You might run `git rm` on a file to accept \"deleted by them\" for it."
 msgstr ""
 "Du har fortfarande ej sammanslagna sökvägar i indexet.\n"
-"Du bör köra \"git add\" på filer med lösta konflikter för att ange dem som "
+"Du bör köra ”git add” på filer med lösta konflikter för att ange dem som "
 "lösta.\n"
-"Du kan köra \"git rm\" för att godta \"borttagen av dem\" för den."
-
-msgid "unable to write new index file"
-msgstr "kunde inte skriva ny indexfil"
+"Du kan köra ”git rm” för att godta ”borttagen av dem” för den."
 
 #, c-format
 msgid "Could not parse object '%s'."
-msgstr "Kan inte tolka objektet \"%s\"."
+msgstr "Kan inte tolka objektet ”%s”."
 
 msgid "failed to clean index"
 msgstr "misslyckades städa upp indexet"
@@ -2102,16 +2128,12 @@ msgid ""
 "You seem to have moved HEAD since the last 'am' failure.\n"
 "Not rewinding to ORIG_HEAD"
 msgstr ""
-"Du verkar ha flyttat HEAD sedan \"am\" sist misslyckades.\n"
+"Du verkar ha flyttat HEAD sedan ”am” sist misslyckades.\n"
 "Återställer inte till ORIG_HEAD"
 
 #, c-format
 msgid "failed to read '%s'"
-msgstr "misslyckades läsa \"%s\""
-
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "flaggorna \"%s=%s\" och \"%s=%s\" kan inte användas samtidigt"
+msgstr "misslyckades läsa ”%s”"
 
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<flaggor>] [(<mbox> | <Maildir>)...]"
@@ -2122,6 +2144,9 @@ msgstr "git am [<flaggor>] (--continue | --skip | --abort)"
 msgid "run interactively"
 msgstr "kör interaktivt"
 
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "förbigå pre-applypatch- och applypatch-msg-krokar"
+
 msgid "historical option -- no-op"
 msgstr "historisk flagga -- no-op"
 
@@ -2132,7 +2157,7 @@ msgid "be quiet"
 msgstr "var tyst"
 
 msgid "add a Signed-off-by trailer to the commit message"
-msgstr "lägg till \"Signed-off-by\"-släprad i incheckningsmeddelandet"
+msgstr "lägg till ”Signed-off-by”-släprad i incheckningsmeddelandet"
 
 msgid "recode into utf8 (default)"
 msgstr "koda om till utf8 (standard)"
@@ -2149,9 +2174,6 @@ msgstr "sänd flaggan -m till git-mailinfo"
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "sänd flaggan --keep-cr till git-mailsplit för mbox-formatet"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr "sänd inte flaggan --keep-cr till git-mailsplit oberoende av am.keepcr"
-
 msgid "strip everything before a scissors line"
 msgstr "ta bort allting före en saxlinje"
 
@@ -2232,7 +2254,7 @@ msgid ""
 "Use \"git am --abort\" to remove it."
 msgstr ""
 "Kvarbliven katalog %s hittades.\n"
-"Använd \"git am --abort\" för att ta bort den."
+"Använd ”git am --abort” för att ta bort den."
 
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr "Lösningsoperation pågår inte, vi återupptar inte."
@@ -2262,56 +2284,51 @@ msgstr "git archive: protokollfel"
 msgid "git archive: expected a flush"
 msgstr "git archive: förväntade en tömning (flush)"
 
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<incheckning>]"
-
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<dålig> [<bra>...]] [--] "
-"[<sökvägar>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<dålig> [<bra>...]] [--]    [<sökvägar>...]"
 
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<incheckning>]"
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<incheckning>...]"
 
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<incheckning>...]"
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<incheckning>|<intervall>)...]"
 
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <filnamn>"
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<incheckning>]"
 
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<incheckning>|<intervall>)...]"
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <loggfil>"
 
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <kommando>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <kommando> [<argument>]..."
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
-msgstr "kan inte kopiera filen \"%s\" i läget \"%s\""
+msgstr "kan inte kopiera filen ”%s” i läget ”%s”"
 
 #, c-format
 msgid "could not write to file '%s'"
-msgstr "kunde inte skriva till filen \"%s\""
+msgstr "kunde inte skriva till filen ”%s”"
 
 #, c-format
 msgid "cannot open file '%s' for reading"
-msgstr "kan inte öppna filen \"%s\" för läsning"
+msgstr "kan inte öppna filen ”%s” för läsning"
 
 #, c-format
 msgid "'%s' is not a valid term"
-msgstr "\"%s\" är inte en giltig term"
+msgstr "”%s” är inte en giltig term"
 
 #, c-format
 msgid "can't use the builtin command '%s' as a term"
-msgstr "kan inte använda det inbyggda kommandot \"%s\" som term"
+msgstr "kan inte använda det inbyggda kommandot ”%s” som term"
 
 #, c-format
 msgid "can't change the meaning of the term '%s'"
-msgstr "kan inte ändra betydelsen av termen \"%s\""
+msgstr "kan inte ändra betydelsen av termen ”%s”"
 
 msgid "please use two different terms"
 msgstr "termerna måste vara olika"
@@ -2322,14 +2339,14 @@ msgstr "Vi utför ingen bisect för tillfället.\n"
 
 #, c-format
 msgid "'%s' is not a valid commit"
-msgstr "\"%s\" är inte en giltig incheckning"
+msgstr "”%s” är inte en giltig incheckning"
 
 #, c-format
 msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
 msgstr ""
-"Kunde inte checka ut original-HEAD \"%s\". Försök \"git bisect reset "
-"<incheckning>\"."
+"Kunde inte checka ut original-HEAD ”%s”. Försök ”git bisect reset "
+"<incheckning>."
 
 #, c-format
 msgid "Bad bisect_write argument: %s"
@@ -2337,15 +2354,15 @@ msgstr "Felaktigt argument till bisect_write: %s"
 
 #, c-format
 msgid "couldn't get the oid of the rev '%s'"
-msgstr "kan inte läsa oid för referensen \"%s\""
+msgstr "kan inte läsa oid för referensen ”%s”"
 
 #, c-format
 msgid "couldn't open the file '%s'"
-msgstr "kunde inte öppna filen \"%s\""
+msgstr "kunde inte öppna filen ”%s”"
 
 #, c-format
 msgid "Invalid command: you're currently in a %s/%s bisect"
-msgstr "Ogiltigt kommando: du utför just nu en \"bisect\" med %s/%s."
+msgstr "Ogiltigt kommando: du utför just nu en ”bisect” med %s/%s."
 
 #, c-format
 msgid ""
@@ -2353,7 +2370,7 @@ msgid ""
 "You can use \"git bisect %s\" and \"git bisect %s\" for that."
 msgstr ""
 "Du måste ange åtminstone en %s och en %s version.\n"
-"(Du kan använda \"git bisect %s\" och \"git bisect %s\" för detta.)"
+"(Du kan använda ”git bisect %s” och ”git bisect %s” för detta.)"
 
 #, c-format
 msgid ""
@@ -2361,9 +2378,9 @@ msgid ""
 "You then need to give me at least one %s and %s revision.\n"
 "You can use \"git bisect %s\" and \"git bisect %s\" for that."
 msgstr ""
-"Du måste starta med \"git bisect start\".\n"
+"Du måste starta med ”git bisect start”.\n"
 "Du måste sedan ange åtminstone en %s och en %s version.\n"
-"(Du kan använda \"git bisect %s\" och \"git bisect %s\" för detta.)"
+"(Du kan använda ”git bisect %s” och ”git bisect %s” för detta.)"
 
 #, c-format
 msgid "bisecting only with a %s commit"
@@ -2404,7 +2421,7 @@ msgid ""
 "invalid argument %s for 'git bisect terms'.\n"
 "Supported options are: --term-good|--term-old and --term-bad|--term-new."
 msgstr ""
-"ogiltigt argument %s för \"git bisect terms\".\n"
+"ogiltigt argument %s för ”git bisect terms”.\n"
 "Flaggor som stöds är: --term-good|--term-old och --term-bad|--term-new."
 
 msgid "revision walk setup failed\n"
@@ -2412,10 +2429,10 @@ msgstr "misslyckades starta revisionstraversering\n"
 
 #, c-format
 msgid "could not open '%s' for appending"
-msgstr "kunde inte öppna \"%s\" för tillägg"
+msgstr "kunde inte öppna ”%s” för tillägg"
 
 msgid "'' is not a valid term"
-msgstr "\"\" är inte en giltig term"
+msgstr "”” är inte en giltig term"
 
 #, c-format
 msgid "unrecognized option: '%s'"
@@ -2423,29 +2440,24 @@ msgstr "okänd flagga: %s"
 
 #, c-format
 msgid "'%s' does not appear to be a valid revision"
-msgstr "\"%s\" verkar inte vara en giltig revision"
+msgstr "”%s” verkar inte vara en giltig revision"
 
 msgid "bad HEAD - I need a HEAD"
 msgstr "felaktigt HEAD - Jag behöver ett HEAD"
 
 #, c-format
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
-msgstr ""
-"misslyckades checka ut \"%s\". Försök \"git bisect reset <giltig_gren>\"."
-
-# cogito-relaterat
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "kör inte \"bisect\" på träd där \"cg-seek\" använts"
+msgstr "misslyckades checka ut ”%s”. Försök ”git bisect reset <giltig_gren>”."
 
 msgid "bad HEAD - strange symbolic ref"
 msgstr "felaktigt HEAD - konstig symbolisk referens"
 
 #, c-format
 msgid "invalid ref: '%s'"
-msgstr "ogiltig referens: \"%s\""
+msgstr "ogiltig referens: ”%s”"
 
 msgid "You need to start by \"git bisect start\"\n"
-msgstr "Du måste starta med \"git bisect start\"\n"
+msgstr "Du måste starta med ”git bisect start”\n"
 
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
@@ -2455,11 +2467,11 @@ msgid "Do you want me to do it for you [Y/n]? "
 msgstr "Vill du att jag ska göra det åt dig [Y=ja/N=nej]? "
 
 msgid "Please call `--bisect-state` with at least one argument"
-msgstr "Anropa \"--bisect-state\" med minst ett argument."
+msgstr "Anropa ”--bisect-state” med minst ett argument."
 
 #, c-format
 msgid "'git bisect %s' can take only one argument."
-msgstr "\"git bisect %s\" kan bara ta ett argument."
+msgstr "”git bisect %s” kan bara ta ett argument."
 
 #, c-format
 msgid "Bad rev input: %s"
@@ -2474,11 +2486,11 @@ msgstr "Vi utför ingen bisect för tillfället."
 
 #, c-format
 msgid "'%s'?? what are you talking about?"
-msgstr "\"%s\"?? vad menar du?"
+msgstr "”%s”?? vad menar du?"
 
 #, c-format
 msgid "cannot read file '%s' for replaying"
-msgstr "kan inte läsa filen \"%s\" för återuppspelning"
+msgstr "kan inte läsa filen ”%s” för återuppspelning"
 
 #, c-format
 msgid "running %s\n"
@@ -2488,95 +2500,67 @@ msgid "bisect run failed: no command provided."
 msgstr "bisect-körning misslyckades: inget kommando gavs."
 
 #, c-format
-msgid "unable to verify '%s' on good revision"
-msgstr "kan inte bekräfta \"%s\" på bra revision"
+msgid "unable to verify %s on good revision"
+msgstr "kan inte bekräfta %s på bra revision"
 
 #, c-format
 msgid "bogus exit code %d for good revision"
 msgstr "falsk slutkod %d för bra revision"
 
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
-msgstr ""
-"\"bisect\"-körningen misslyckades: felkod %d från \"%s\" är < 0 eller >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "”bisect”-körningen misslyckades: felkod %d från %s är < 0 eller >= 128"
 
 #, c-format
 msgid "cannot open file '%s' for writing"
-msgstr "kan inte öppna \"%s\" för skrivning"
+msgstr "kan inte öppna ”%s” för skrivning"
 
 msgid "bisect run cannot continue any more"
-msgstr "\"bisect\"-körningen kan inte fortsätta längre"
+msgstr "”bisect”-körningen kan inte fortsätta längre"
 
-#, c-format
 msgid "bisect run success"
-msgstr "\"bisect\"-körningen lyckades"
+msgstr "”bisect”-körningen lyckades"
 
-#, c-format
 msgid "bisect found first bad commit"
 msgstr "bisect hittade första trasiga incheckning"
 
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
 msgstr ""
-"\"bisect\"-körningen misslyckades: \"git bisect--helper --bisect-state %s\" "
-"avslutades med felkoden %d"
-
-msgid "reset the bisection state"
-msgstr "återställ bisect-tillstånd"
-
-msgid "check whether bad or good terms exist"
-msgstr "se efter om termer för rätt och fel finns"
-
-msgid "print out the bisect terms"
-msgstr "skriv ut termer för bisect"
-
-msgid "start the bisect session"
-msgstr "påbörja bisect-körningen"
-
-msgid "find the next bisection commit"
-msgstr "hitta nästa incheckning i bisect"
-
-msgid "mark the state of ref (or refs)"
-msgstr "markera tillståndet för en eller flera referenser"
+"”bisect”-körningen misslyckades: ”git bisect %s” avslutades med felkoden %d"
 
-msgid "list the bisection steps so far"
-msgstr "lista \"bisect\"-stegen som utförts så långt"
-
-msgid "replay the bisection process from the given file"
-msgstr "spela upp \"bisect\"-processen från angiven fil"
-
-msgid "skip some commits for checkout"
-msgstr "hoppa över ett par incheckningar"
-
-msgid "visualize the bisection"
-msgstr "visualisera \"bisect\"-körningen"
-
-msgid "use <cmd>... to automatically bisect"
-msgstr "använd <kommando>... för att utföra \"bisect\" automatiskt"
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "”%s” kräver antingen inget argument eller en incheckning"
 
-msgid "no log for BISECT_WRITE"
-msgstr "ingen logg för BISECT_WRITE"
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "”%s” kräver noll eller ett argument"
 
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset kräver antingen inget argument eller en incheckning"
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "”%s” kräver noll argument"
 
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms kräver noll eller ett argument"
+msgid "no logfile given"
+msgstr "ingen loggfil angiven"
 
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next kräver 0 argument"
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "”%s” misslyckades: inget kommando gavs."
 
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log kräver 0 argument"
+msgid "need a command"
+msgstr "behöver ett kommando"
 
-msgid "no logfile given"
-msgstr "ingen loggfil angiven"
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "okänt kommando: ”%s”"
 
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<flaggor>] [<rev-flaggor>] [<rev>] [--] <fil>"
 
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<flaggor>] [<rev-flaggor>] [<rev>] [--] <fil>"
+
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<rev-flaggor> dokumenteras i git-rev-list(1)"
 
@@ -2732,51 +2716,56 @@ msgstr "git branch [<flaggor>] [-r | -a] [--format]"
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
-"tar bort grenen \"%s\" som har slagits ihop med\n"
-"         \"%s\", men ännu inte slagits ihop med HEAD."
+"tar bort grenen ”%s” som har slagits ihop med\n"
+"         ”%s”, men ännu inte slagits ihop med HEAD"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"tar inte bort grenen \"%s\" som inte har slagits ihop med\n"
-"         \"%s\", trots att den har slagits ihop med HEAD."
+"tar inte bort grenen ”%s” som inte har slagits ihop med\n"
+"         ”%s”, trots att den har slagits ihop med HEAD"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Kunde inte slå upp incheckningsobjekt för \"%s\""
+msgid "couldn't look up commit object for '%s'"
+msgstr "kunde inte slå upp incheckningsobjekt för ”%s”"
 
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
-"Grenen \"%s\" har inte slagits samman i sin helhet.\n"
-"Om du är säker på att du vill ta bort den, kör \"git branch -D %s\"."
+"grenen ”%s” har inte slagits samman i sin helhet.\n"
+"Om du är säker på att du vill ta bort den, kör ”git branch -D %s”"
 
-msgid "Update of config-file failed"
-msgstr "Misslyckades uppdatera konfigurationsfil"
+msgid "update of config-file failed"
+msgstr "misslyckades uppdatera konfigurationsfil"
 
 msgid "cannot use -a with -d"
 msgstr "kan inte ange -a med -d"
 
-msgid "Couldn't look up commit object for HEAD"
-msgstr "Kunde inte slå upp incheckningsobjekt för HEAD"
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr "kan inte ta bort grenen ”%s” som används av arbetskatalogen på ”%s”"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Kan inte ta bort grenen \"%s\" som är utcheckad på \"%s\""
+msgid "remote-tracking branch '%s' not found"
+msgstr "fjärrspårande grenen ”%s” hittades inte"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "fjärrspårande grenen \"%s\" hittades inte."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"grenen ”%s” hittades inte.\n"
+"Glömde du --remote?"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "grenen \"%s\" hittades inte."
+msgid "branch '%s' not found"
+msgstr "grenen ”%s” hittades inte"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2797,47 +2786,52 @@ msgid "HEAD (%s) points outside of refs/heads/"
 msgstr "HEAD (%s) pekar utenför refs/heads/"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "Grenen %s ombaseras på %s"
+msgid "branch %s is being rebased at %s"
+msgstr "grenen %s ombaseras på %s"
+
+#, c-format
+msgid "branch %s is being bisected at %s"
+msgstr "grenen %s är i en ”bisect” på %s"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "Grenen %s är i en \"bisect\" på %s"
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD i arbetskatalogen %s har inte uppdaterats"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "kunde inte kopiera aktuell gren när du inte befinner dig på någon."
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "gelaktigt namn på gren: ”%s”"
 
-msgid "cannot rename the current branch while not on any."
-msgstr ""
-"kunde inte byta namn på aktuell gren när du inte befinner dig på någon."
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "inga incheckningar på grenen ”%s” ännu"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Felaktigt namn på gren: \"%s\""
+msgid "no branch named '%s'"
+msgstr "ingen gren vid namnet ”%s”"
 
-msgid "Branch rename failed"
-msgstr "Misslyckades byta namn på gren"
+msgid "branch rename failed"
+msgstr "misslyckades byta namn på gren"
 
-msgid "Branch copy failed"
-msgstr "Misslyckades kopiera gren"
+msgid "branch copy failed"
+msgstr "misslyckades kopiera gren"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Skapade kopia av felaktigt namngiven gren \"%s\""
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "skapade kopia av felaktigt namngiven gren ”%s”"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Bytte bort namn på en felaktigt namngiven gren \"%s\""
+msgid "renamed a misnamed branch '%s' away"
+msgstr "bytte bort namn på en felaktigt namngiven gren ”%s”"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Grenen namnbytt till %s, men HEAD har inte uppdaterats!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "grenen namnbytt till %s, men HEAD har inte uppdaterats"
 
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "Grenen namnbytt, men misslyckades uppdatera konfigurationsfilen"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "grenen namnbytt, men misslyckades uppdatera konfigurationsfilen"
 
-msgid "Branch is copied, but update of config-file failed"
-msgstr "Grenen kopierades, men misslyckades uppdatera konfigurationsfilen"
+msgid "branch is copied, but update of config-file failed"
+msgstr "grenen kopierades, men misslyckades uppdatera konfigurationsfilen"
 
 #, c-format
 msgid ""
@@ -2847,7 +2841,7 @@ msgid ""
 msgstr ""
 "Redigera beskrivningen för grenen\n"
 "  %s\n"
-"Rader som inleds med \"%c\" ignoreras.\n"
+"Rader som inleds med ”%c” ignoreras.\n"
 
 msgid "Generic options"
 msgstr "Allmänna flaggor"
@@ -2903,6 +2897,9 @@ msgstr "flytta/ta bort en gren och dess reflogg"
 msgid "move/rename a branch, even if target exists"
 msgstr "flytta/ta bort en gren, även om målet finns"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "skriv inte ut ett nyradstecken efter tomma formaterade referenser"
+
 msgid "copy a branch and its reflog"
 msgstr "kopiera en gren och dess reflogg"
 
@@ -2948,8 +2945,8 @@ msgstr "rekursera ner i undermoduler"
 msgid "format to use for the output"
 msgstr "format att använda för utdata"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Misslyckades slå upp HEAD som giltig referens."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "misslyckades slå upp HEAD som giltig referens"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD hittades inte under refs/heads!"
@@ -2962,24 +2959,22 @@ msgstr ""
 "propagateBranches har aktiverats"
 
 msgid "--recurse-submodules can only be used to create branches"
-msgstr "--recurse-submodules jan endast användas för att skapa grenar"
+msgstr "--recurse-submodules kan endast användas för att skapa grenar"
 
 msgid "branch name required"
 msgstr "grennamn krävs"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "Kan inte beskriva frånkopplad HEAD"
+msgid "cannot give description to detached HEAD"
+msgstr "kan inte beskriva frånkopplad HEAD"
 
 msgid "cannot edit description of more than one branch"
 msgstr "kan inte redigera beskrivning för mer än en gren"
 
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Inga incheckningar på grenen \"%s\" ännu."
+msgid "cannot copy the current branch while not on any"
+msgstr "kunde inte kopiera aktuell gren när du inte befinner dig på någon"
 
-#, c-format
-msgid "No branch named '%s'."
-msgstr "Ingen gren vid namnet \"%s\"."
+msgid "cannot rename the current branch while not on any"
+msgstr "kunde inte byta namn på aktuell gren när du inte befinner dig på någon"
 
 msgid "too many branches for a copy operation"
 msgstr "för många grenar för kopiering"
@@ -2992,49 +2987,48 @@ msgstr "för många flaggor för att byta uppström"
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"kunde inte sätta uppström för HEAD till %s när det inte pekar mot någon gren."
+"kunde inte sätta uppström för HEAD till %s när det inte pekar mot någon gren"
 
 #, c-format
 msgid "no such branch '%s'"
-msgstr "okänd gren \"%s\""
+msgstr "okänd gren ”%s”"
 
 #, c-format
 msgid "branch '%s' does not exist"
-msgstr "grenen \"%s\" finns inte"
+msgstr "grenen ”%s” finns inte"
 
 msgid "too many arguments to unset upstream"
 msgstr "för många flaggor för att ta bort uppström"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr ""
-"kunde inte ta bort uppström för HEAD när det inte pekar mot någon gren."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr "kunde inte ta bort uppström för HEAD när det inte pekar mot någon gren"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "Grenen \"%s\" har ingen uppströmsinformation"
+msgid "branch '%s' has no upstream information"
+msgstr "grenen ”%s” har ingen uppströmsinformation"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"Flaggorna -a och -r på \"git branch\" tar inte ett namn på gren.\n"
+"flaggorna -a och -r på ”git branch” tar inte ett namn på gren.\n"
 "Menade du att använda: -a|-r --list <mönster>?"
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
-"Flaggan --set-upstream rekommenderas ej och kommer tas bort. Använd --track "
-"eller --set-upstream-to istället."
+"flaggan ”--set-upstream” rekommenderas ej och kommer tas bort. Använd ”--"
+"track” eller ”--set-upstream-to” istället"
 
 msgid "git version:\n"
 msgstr "git version:\n"
 
 #, c-format
 msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() misslyckades med felet \"%s\" (%d)\n"
+msgstr "uname() misslyckades med felet ”%s” (%d)\n"
 
 msgid "compiler info: "
 msgstr "kompilatorinfo:"
@@ -3046,11 +3040,11 @@ msgid "not run from a git repository - no hooks to show\n"
 msgstr "körs inte från ett git-arkiv - inga krokar att visa\n"
 
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory <fil>] [-s|--suffix <format>] [--"
-"diagnose[=<läge>]"
+"git bugreport [(-o | --output-directory) <fil>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<läge>]"
 
 msgid ""
 "Thank you for filling out a Git bug report!\n"
@@ -3093,8 +3087,7 @@ msgstr "läge"
 msgid ""
 "create an additional zip archive of detailed diagnostics (default 'stats')"
 msgstr ""
-"skapa ett ytterligare zip-arkiv med detaljerad diagnostik (förval är \"stats"
-"\")"
+"skapa ett ytterligare zip-arkiv med detaljerad diagnostik (förval är ”stats”)"
 
 msgid "specify a destination for the bugreport file(s)"
 msgstr "ange mål för buggrapporteringsfilen/-rna"
@@ -3102,9 +3095,13 @@ msgstr "ange mål för buggrapporteringsfilen/-rna"
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "ange filändelse i strftime-format"
 
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "okänt argument ”%s”"
+
 #, c-format
 msgid "could not create leading directories for '%s'"
-msgstr "kunde inte skapa inledande kataloger för \"%s\""
+msgstr "kunde inte skapa inledande kataloger för ”%s”"
 
 #, c-format
 msgid "unable to create diagnostics archive %s"
@@ -3122,19 +3119,26 @@ msgstr "kunde inte skriva till %s"
 
 #, c-format
 msgid "Created new report at '%s'.\n"
-msgstr "Skapade ny rapport på \"%s\"\n"
+msgstr "Skapade ny rapport på ”%s”\n"
 
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [<flaggor>] <fil> <git-rev-list-flaggor>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <fil> <git-rev-list-flaggor>"
 
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [<flaggor>] <fil>"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <fil>"
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads <fil> [<refnamn>...]"
 
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle <fil> [<refnamn>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <fil> [<refnamn>...]"
+
+msgid "need a <file> argument"
+msgstr "behöver ett <fil>-argument"
 
 msgid "do not show progress meter"
 msgstr "visa inte förloppsindikator"
@@ -3142,11 +3146,11 @@ msgstr "visa inte förloppsindikator"
 msgid "show progress meter"
 msgstr "visa förloppsindikator"
 
-msgid "show progress meter during object writing phase"
-msgstr "visa förloppsindikator under objektskrivningsfasen"
+msgid "historical; same as --progress"
+msgstr "historisk; samma som --progress"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "som --all-progress när förloppsindikatorn visas"
+msgid "historical; does nothing"
+msgstr "historisk flagga; gör ingenting"
 
 msgid "specify bundle format version"
 msgstr "ange formatversion för bunten."
@@ -3169,17 +3173,17 @@ msgstr "Packar upp objektbunt"
 
 #, c-format
 msgid "cannot read object %s '%s'"
-msgstr "kan inte läsa objektet %s: \"%s\""
+msgstr "kan inte läsa objektet %s: ”%s”"
 
 msgid "flush is only for --buffer mode"
-msgstr "flush är endast till för --buffer-läge"
+msgstr "flush är endast till för ”--buffer”-läge"
 
 msgid "empty command in input"
 msgstr "tomt kommando i indata"
 
 #, c-format
 msgid "whitespace before command: '%s'"
-msgstr "blanksteg före kommando: \"%s\""
+msgstr "blanksteg före kommando: ”%s”"
 
 #, c-format
 msgid "%s requires arguments"
@@ -3189,10 +3193,6 @@ msgstr "%s kräver ett argument"
 msgid "%s takes no arguments"
 msgstr "%s tar inget argument"
 
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "okänt kommando: \"%s\""
-
 msgid "only one batch option may be specified"
 msgstr "endast en buntflagga kan anges"
 
@@ -3205,24 +3205,24 @@ msgstr "git cat-file (-e | -p) <objekt>"
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objekt>"
 
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<revision>:<sökväg|träd-igt> | --path=<sökväg|träd-igt> "
+"<revision>]"
+
 msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
-msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<revision>:<sökväg|träd-igt> | --path=<sökväg|träd-igt> "
-"<revision>]"
+"             [--textconv | --filters] [-Z]"
 
 msgid "Check object existence or emit object contents"
 msgstr "Kontrollera om objektet finns eller mata ut objektets innehåll"
@@ -3237,7 +3237,7 @@ msgid "Emit [broken] object attributes"
 msgstr "Skriv ut [trasiga] objektattribut"
 
 msgid "show object type (one of 'blob', 'tree', 'commit', 'tag', ...)"
-msgstr "visa objekttyp (en av: \"blob\", \"tree\", \"commit\", \"tag\", ...)"
+msgstr "visa objekttyp (en av: ”blob”, ”tree”, ”commit”, ”tag”, ...)"
 
 msgid "show object size"
 msgstr "visa objektstorlek"
@@ -3260,6 +3260,9 @@ msgstr "som --batch, men mata inte ut <innehåll>"
 msgid "stdin is NUL-terminated"
 msgstr "standard in är NUL-terminerad"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "standard in och standard ut är NUL-terminerade"
+
 msgid "read commands from stdin"
 msgstr "läs kommandon från standard in"
 
@@ -3306,35 +3309,38 @@ msgstr "sökväg|träd-igt"
 
 #, c-format
 msgid "'%s' requires a batch mode"
-msgstr "\"%s\" behöver ett buntläge"
+msgstr "”%s” behöver ett buntläge"
 
 #, c-format
 msgid "'-%c' is incompatible with batch mode"
-msgstr "\"-%c\" är inkompatibel med buntläge"
+msgstr "”-%c” är inkompatibel med buntläge"
 
 msgid "batch modes take no arguments"
 msgstr "buntlägen inte några argument"
 
 #, c-format
 msgid "<rev> required with '%s'"
-msgstr "<rev> krävs med \"%s\""
+msgstr "<rev> krävs med ”%s”"
 
 #, c-format
 msgid "<object> required with '-%c'"
-msgstr "<objekt> krävs med \"-%c\""
-
-msgid "too many arguments"
-msgstr "för många argument"
+msgstr "<objekt> krävs med ”-%c”"
 
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr "endast två argument krävs i <typ> <objekt>-läge, inte %d"
 
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <attr>...] [--] <sökväg>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <träd:igt>] [-a | --all | <attr>...] [--] "
+"<sökväg>..."
 
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <attr>...]"
+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>...]"
 
 msgid "report all attributes set on file"
 msgstr "visa alla attribut som satts på filen"
@@ -3348,6 +3354,12 @@ msgstr "läs filnamn från standard in"
 msgid "terminate input and output records by a NUL character"
 msgstr "avsluta in- och utdataposter med NUL-tecken"
 
+msgid "<tree-ish>"
+msgstr "<träd-igt>"
+
+msgid "which tree-ish to check attributes at"
+msgstr "vilken träd-igt att kontrollera attribut på"
+
 msgid "suppress progress reporting"
 msgstr "undertryck förloppsrapportering"
 
@@ -3401,7 +3413,7 @@ msgid "git checkout-index [<options>] [--] [<file>...]"
 msgstr "git checkout-index [<flaggor>] [--] [<fil>...]"
 
 msgid "stage should be between 1 and 3 or all"
-msgstr "etapp måste vara mellan 1 och 3 eller \"all\""
+msgstr "etapp måste vara mellan 1 och 3 eller ”all”"
 
 msgid "check out all files in the index"
 msgstr "checka ut alla filer i indexet"
@@ -3444,27 +3456,27 @@ msgstr "git restore [<flaggor>] [--source=<gren>] <fil>..."
 
 #, c-format
 msgid "path '%s' does not have our version"
-msgstr "sökvägen \"%s\" har inte vår version"
+msgstr "sökvägen ”%s” har inte vår version"
 
 #, c-format
 msgid "path '%s' does not have their version"
-msgstr "sökvägen \"%s\" har inte deras version"
+msgstr "sökvägen ”%s” har inte deras version"
 
 #, c-format
 msgid "path '%s' does not have all necessary versions"
-msgstr "sökvägen \"%s\" innehåller inte alla nödvändiga versioner"
+msgstr "sökvägen ”%s” innehåller inte alla nödvändiga versioner"
 
 #, c-format
 msgid "path '%s' does not have necessary versions"
-msgstr "sökvägen \"%s\" innehåller inte nödvändiga versioner"
+msgstr "sökvägen ”%s” innehåller inte nödvändiga versioner"
 
 #, c-format
 msgid "path '%s': cannot merge"
-msgstr "sökväg \"%s\": kan inte slå ihop"
+msgstr "sökväg ”%s”: kan inte slå ihop"
 
 #, c-format
 msgid "Unable to add merge result for '%s'"
-msgstr "Kunde inte lägga till sammanslagningsresultat för \"%s\""
+msgstr "Kunde inte lägga till sammanslagningsresultat för ”%s”"
 
 #, c-format
 msgid "Recreated %d merge conflict"
@@ -3486,27 +3498,31 @@ msgstr[1] "Uppdaterade %d sökvägar från indexet"
 
 #, c-format
 msgid "'%s' cannot be used with updating paths"
-msgstr "\"%s\" kan inte användas vid uppdatering av sökvägar"
+msgstr "”%s” kan inte användas vid uppdatering av sökvägar"
 
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
-msgstr "Kan inte uppdatera sökvägar och växla till grenen \"%s\" samtidigt."
+msgstr "Kan inte uppdatera sökvägar och växla till grenen ”%s” samtidigt."
 
 #, c-format
 msgid "neither '%s' or '%s' is specified"
-msgstr "varken \"%s\" eller \"%s\" har angivits"
+msgstr "varken ”%s” eller ”%s” har angivits"
 
 #, c-format
 msgid "'%s' must be used when '%s' is not specified"
-msgstr "\"%s\" måste användas när \"%s\" inte anges"
+msgstr "”%s” måste användas när ”%s” inte anges"
 
 #, c-format
 msgid "'%s' or '%s' cannot be used with %s"
-msgstr "\"%s\" eller \"%s\" kan inte användas med %s"
+msgstr "”%s” eller ”%s” kan inte användas med %s"
+
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "”%s”, ”%s” eller ”%s” kan inte användas när en katalog checkas ut"
 
 #, c-format
 msgid "path '%s' is unmerged"
-msgstr "sökvägen \"%s\" har inte slagits ihop"
+msgstr "sökvägen ”%s” har inte slagits ihop"
 
 msgid "you need to resolve your current index first"
 msgstr "du måste lösa ditt befintliga index först"
@@ -3521,7 +3537,7 @@ msgstr ""
 
 #, c-format
 msgid "Can not do reflog for '%s': %s\n"
-msgstr "Kan inte skapa referenslogg för \"%s\": %s\n"
+msgstr "Kan inte skapa referenslogg för ”%s”: %s\n"
 
 msgid "HEAD is now at"
 msgstr "HEAD är nu på"
@@ -3531,23 +3547,23 @@ msgstr "kan inte uppdatera HEAD"
 
 #, c-format
 msgid "Reset branch '%s'\n"
-msgstr "Återställ gren \"%s\"\n"
+msgstr "Återställ gren ”%s”\n"
 
 #, c-format
 msgid "Already on '%s'\n"
-msgstr "Redan på \"%s\"\n"
+msgstr "Redan på ”%s”\n"
 
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
-msgstr "Växlade till och nollställde grenen \"%s\"\n"
+msgstr "Växlade till och nollställde grenen ”%s”\n"
 
 #, c-format
 msgid "Switched to a new branch '%s'\n"
-msgstr "Växlade till en ny gren \"%s\"\n"
+msgstr "Växlade till en ny gren ”%s”\n"
 
 #, c-format
 msgid "Switched to branch '%s'\n"
-msgstr "Växlade till grenen \"%s\"\n"
+msgstr "Växlade till grenen ”%s”\n"
 
 #, c-format
 msgid " ... and %d more.\n"
@@ -3615,7 +3631,7 @@ msgid ""
 "'%s' could be both a local file and a tracking branch.\n"
 "Please use -- (and optionally --no-guess) to disambiguate"
 msgstr ""
-"\"%s\" kan vara både en lokal fil och en spårande gren.\n"
+"”%s” kan vara både en lokal fil och en spårande gren.\n"
 "Använd -- (och möjligen --no-guess) för att göra otvetydig"
 
 msgid ""
@@ -3628,18 +3644,18 @@ msgid ""
 "one remote, e.g. the 'origin' remote, consider setting\n"
 "checkout.defaultRemote=origin in your config."
 msgstr ""
-"Om du menade checka ut en spårad fjärrgren på t.ex \"origin\", kan du\n"
+"Om du menade checka ut en spårad fjärrgren på t.ex ”origin”, kan du\n"
 "göra det genom att ange hela namnet med flaggan --track:\n"
 "\n"
 "    git checkout --track origin/<namn>\n"
 "\n"
 "Om du alltid vill att utcheckningar med tvetydiga <namn> ska\n"
-"föredra en fjärr, t.ex fjärren \"origin\" kan du ställa in\n"
+"föredra en fjärr, t.ex fjärren ”origin” kan du ställa in\n"
 "checkout.defaultRemote=origin i din konfiguration."
 
 #, c-format
 msgid "'%s' matched multiple (%d) remote tracking branches"
-msgstr "\"%s\" motsvarar flera (%d) spårade fjärrgrenar"
+msgstr "”%s” motsvarar flera (%d) spårade fjärrgrenar"
 
 msgid "only one reference expected"
 msgstr "endast en referens förväntades"
@@ -3658,19 +3674,19 @@ msgstr "referensen är inte ett träd: %s"
 
 #, c-format
 msgid "a branch is expected, got tag '%s'"
-msgstr "förväntade gren, fick taggen \"%s\""
+msgstr "förväntade gren, fick taggen ”%s”"
 
 #, c-format
 msgid "a branch is expected, got remote branch '%s'"
-msgstr "förväntade gren, fick fjärrgrenen \"%s\""
+msgstr "förväntade gren, fick fjärrgrenen ”%s”"
 
 #, c-format
 msgid "a branch is expected, got '%s'"
-msgstr "förväntade gren, fick \"%s\""
+msgstr "förväntade gren, fick ”%s”"
 
 #, c-format
 msgid "a branch is expected, got commit '%s'"
-msgstr "förväntade gren, fick incheckningen \"%s\""
+msgstr "förväntade gren, fick incheckningen ”%s”"
 
 msgid ""
 "If you want to detach HEAD at the commit, try again with the --detach option."
@@ -3683,57 +3699,57 @@ msgid ""
 "Consider \"git merge --quit\" or \"git worktree add\"."
 msgstr ""
 "kan inte växla gren vid sammanslagning\n"
-"Överväg \"git merge --quit\" eller \"git worktree add\"."
+"Överväg ”git merge --quit” eller ”git worktree add”."
 
 msgid ""
 "cannot switch branch in the middle of an am session\n"
 "Consider \"git am --quit\" or \"git worktree add\"."
 msgstr ""
-"kan inte växla gren mitt i en \"am\"-körning\n"
-"Överväg \"git am --quit\" eller \"git worktree add\"."
+"kan inte växla gren mitt i en ”am”-körning\n"
+"Överväg ”git am --quit” eller ”git worktree add”."
 
 msgid ""
 "cannot switch branch while rebasing\n"
 "Consider \"git rebase --quit\" or \"git worktree add\"."
 msgstr ""
 "kan inte växla gren vid ombasering\n"
-"Överväg \"git rebase --quit\" eller \"git worktree add\"."
+"Överväg ”git rebase --quit” eller ”git worktree add”."
 
 msgid ""
 "cannot switch branch while cherry-picking\n"
 "Consider \"git cherry-pick --quit\" or \"git worktree add\"."
 msgstr ""
-"kan inte växla gren i en \"cherry-pick\"\n"
-"Överväg \"git cherry-pick --quit\" eller \"git worktree add\"."
+"kan inte växla gren i en ”cherry-pick”\n"
+"Överväg ”git cherry-pick --quit” eller ”git worktree add”."
 
 msgid ""
 "cannot switch branch while reverting\n"
 "Consider \"git revert --quit\" or \"git worktree add\"."
 msgstr ""
-"kan inte växla gren i en \"revert\"\n"
-"Överväg \"git revert --quit\" eller \"git worktree add\"."
+"kan inte växla gren i en ”revert”\n"
+"Överväg ”git revert --quit” eller ”git worktree add”."
 
 msgid "you are switching branch while bisecting"
-msgstr "då växlar grenar medan du gör en \"bisect\""
+msgstr "då växlar grenar medan du gör en ”bisect”"
 
 msgid "paths cannot be used with switching branches"
 msgstr "sökvägar kan inte användas vid byte av gren"
 
 #, c-format
 msgid "'%s' cannot be used with switching branches"
-msgstr "\"%s\" kan inte användas vid byte av gren"
+msgstr "”%s” kan inte användas vid byte av gren"
 
 #, c-format
 msgid "'%s' cannot be used with '%s'"
-msgstr "\"%s\" kan inte användas med \"%s\""
+msgstr "”%s” kan inte användas med ”%s”"
 
 #, c-format
 msgid "'%s' cannot take <start-point>"
-msgstr "\"%s\" kan inte ta <startpunkt>"
+msgstr "”%s” kan inte ta <startpunkt>"
 
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
-msgstr "Kan inte växla gren till icke-incheckningen \"%s\""
+msgstr "Kan inte växla gren till icke-incheckningen ”%s”"
 
 msgid "missing branch or commit argument"
 msgstr "saknar gren- eller incheckingsargument"
@@ -3777,7 +3793,7 @@ msgstr "begränsa inte sökvägar till endast glesa poster"
 
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
-msgstr "flaggorna \"%-c\", \"-%c\" och \"%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%-c”, ”-%c” och ”%s” kan inte användas samtidigt"
 
 msgid "--track needs a branch name"
 msgstr "--track behöver ett namn på en gren"
@@ -3795,12 +3811,11 @@ msgstr "felaktig sökvägsangivelse"
 
 #, c-format
 msgid "'%s' is not a commit and a branch '%s' cannot be created from it"
-msgstr ""
-"\"%s\" är inte en incheckning och grenen \"%s\" kan inte skapas från den"
+msgstr "”%s” är inte en incheckning och grenen ”%s” kan inte skapas från den"
 
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
-msgstr "git checkout: --detach tar inte en sökväg som argument \"%s\""
+msgstr "git checkout: --detach tar inte en sökväg som argument ”%s”"
 
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
@@ -3825,7 +3840,7 @@ msgid "create reflog for new branch"
 msgstr "skapa reflogg för ny gren"
 
 msgid "second guess 'git checkout <no-such-branch>' (default)"
-msgstr "förutspå \"git checkout <gren-saknas>\" (förval)"
+msgstr "förutspå ”git checkout <gren-saknas>” (förval)"
 
 msgid "use overlay mode (default)"
 msgstr "använd överläggsläge (standard)"
@@ -3837,7 +3852,7 @@ msgid "create/reset and switch to a branch"
 msgstr "skapa/nollställ och växla till en gren"
 
 msgid "second guess 'git switch <no-such-branch>'"
-msgstr "förutspå \"git checkout <gren-saknas>\""
+msgstr "förutspå ”git checkout <gren-saknas>”"
 
 msgid "throw away local modifications"
 msgstr "kasta bort lokala ändringar"
@@ -3858,10 +3873,10 @@ msgid "use overlay mode"
 msgstr "använd överläggsläge"
 
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <mönster>] [-x | -X] [--] "
-"<sökvägar>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <mönster>] [-x | -X] [--] <sökväg>..."
 
 #, c-format
 msgid "Removing %s\n"
@@ -3885,7 +3900,7 @@ msgstr "misslyckades ta bort %s"
 
 #, c-format
 msgid "could not lstat %s\n"
-msgstr "kunde inte ta status (\"lstat\") på %s\n"
+msgstr "kunde inte ta status (”lstat”) på %s\n"
 
 msgid "Refusing to remove current working directory\n"
 msgstr "Vägrar ta bort aktuell arbetskatalog\n"
@@ -3925,7 +3940,7 @@ msgstr ""
 "*          - välj alla poster\n"
 "           - (tomt) avsluta markering\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Vadå (%s)?\n"
 
@@ -3957,7 +3972,7 @@ msgstr ""
 "clean               - börja städa\n"
 "filter by pattern   - uteslut poster från borttagning\n"
 "select by numbers   - markera poster som ska tas bort med siffror\n"
-"ask each            - bekräfta varje borttagning (som \"rm -i\")\n"
+"ask each            - bekräfta varje borttagning (som ”rm -i”)\n"
 "quit                - sluta städa\n"
 "help                - denna skärm\n"
 "?                   - hjälp för kommandoval"
@@ -4021,10 +4036,10 @@ msgid "don't create a checkout"
 msgstr "skapa inte någon utcheckning"
 
 msgid "create a bare repository"
-msgstr "skapa ett naket (\"bare\") arkiv"
+msgstr "skapa ett naket (”bare”) arkiv"
 
 msgid "create a mirror repository (implies bare)"
-msgstr "skapa ett spegelarkiv (implicerar \"bare\")"
+msgstr "skapa ett spegelarkiv (implicerar ”bare”)"
 
 msgid "to clone from a local repository"
 msgstr "för att klona från ett lokalt arkiv"
@@ -4060,7 +4075,7 @@ msgid "name"
 msgstr "namn"
 
 msgid "use <name> instead of 'origin' to track upstream"
-msgstr "använd <namn> istället för \"origin\" för att spåra uppströms"
+msgstr "använd <namn> istället för ”origin” för att spåra uppströms"
 
 msgid "checkout <branch> instead of the remote's HEAD"
 msgstr "checka ut <gren> istället för fjärrens HEAD"
@@ -4074,9 +4089,6 @@ msgstr "djup"
 msgid "create a shallow clone of that depth"
 msgstr "skapa en grund klon på detta djup"
 
-msgid "time"
-msgstr "tid"
-
 msgid "create a shallow clone since a specific time"
 msgstr "skapa en grund klon från en angiven tidpunkt"
 
@@ -4113,12 +4125,6 @@ msgstr "serverspecifik"
 msgid "option to transmit"
 msgstr "flagga att sända"
 
-msgid "use IPv4 addresses only"
-msgstr "använd endast IPv4-adresser"
-
-msgid "use IPv6 addresses only"
-msgstr "använd endast IPv6-adresser"
-
 msgid "apply partial clone filters to submodules"
 msgstr "tillämpa delvisa klonfilter på undermoduler"
 
@@ -4136,35 +4142,43 @@ 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"
+msgstr "info: Kan inte skapa suppleant för ”%s”: %s\n"
 
 #, c-format
 msgid "failed to stat '%s'"
-msgstr "misslyckades ta status på \"%s\""
+msgstr "misslyckades ta status på ”%s”"
 
 #, c-format
 msgid "%s exists and is not a directory"
 msgstr "%s finns och är ingen katalog"
 
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "”%s” är en symbolisk länk, vägrar klona med --local"
+
 #, c-format
 msgid "failed to start iterator over '%s'"
-msgstr "misslyckades starta iterator över \"%s\""
+msgstr "misslyckades starta iterator över ”%s”"
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "symbolisk länk ”%s” finns redan, vägrar klona med --local"
 
 #, c-format
 msgid "failed to unlink '%s'"
-msgstr "misslyckades ta bort länken \"%s\""
+msgstr "misslyckades ta bort länken ”%s”"
 
 #, c-format
 msgid "failed to create link '%s'"
-msgstr "misslyckades skapa länken \"%s\""
+msgstr "misslyckades skapa länken ”%s”"
 
 #, c-format
 msgid "failed to copy file to '%s'"
-msgstr "misslyckades kopiera filen till \"%s\""
+msgstr "misslyckades kopiera filen till ”%s”"
 
 #, c-format
 msgid "failed to iterate over '%s'"
-msgstr "misslyckades iterera över \"%s\""
+msgstr "misslyckades iterera över ”%s”"
 
 #, c-format
 msgid "done.\n"
@@ -4176,8 +4190,8 @@ msgid ""
 "and retry with 'git restore --source=HEAD :/'\n"
 msgstr ""
 "Klonen lyckades, men utcheckningen misslyckades.\n"
-"Du kan inspektera det som checkades ut med \"git status\"\n"
-"och försöka med \"git restore --source=HEAD :/\"\n"
+"Du kan inspektera det som checkades ut med ”git status”\n"
+"och försöka med ”git restore --source=HEAD :/”\n"
 
 #, c-format
 msgid "Could not find remote branch %s to clone."
@@ -4206,7 +4220,7 @@ 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"
+msgstr "kunde inte ta bort temporär ”alternates”-fil"
 
 msgid "Too many arguments."
 msgstr "För många argument."
@@ -4214,10 +4228,6 @@ msgstr "För många argument."
 msgid "You must specify a repository to clone."
 msgstr "Du måste ange ett arkiv att klona."
 
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "flaggorna \"%s\" och \"%s %s\" kan inte användas samtidigt"
-
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
 "exclude"
@@ -4227,7 +4237,7 @@ msgstr ""
 
 #, c-format
 msgid "repository '%s' does not exist"
-msgstr "arkivet \"%s\" finns inte"
+msgstr "arkivet ”%s” finns inte"
 
 #, c-format
 msgid "depth %s is not a positive number"
@@ -4235,31 +4245,31 @@ msgstr "djupet %s är inte ett positivt tal"
 
 #, c-format
 msgid "destination path '%s' already exists and is not an empty directory."
-msgstr "destinationssökvägen \"%s\" finns redan och är inte en tom katalog."
+msgstr "destinationssökvägen ”%s” finns redan och är inte en tom katalog."
 
 #, c-format
 msgid "repository path '%s' already exists and is not an empty directory."
-msgstr "arkivsökvägen \"%s\" finns redan och är inte en tom katalog."
+msgstr "arkivsökvägen ”%s” finns redan och är inte en tom katalog."
 
 #, c-format
 msgid "working tree '%s' already exists."
-msgstr "arbetsträdet \"%s\" finns redan."
+msgstr "arbetsträdet ”%s” finns redan."
 
 #, c-format
 msgid "could not create leading directories of '%s'"
-msgstr "kunde inte skapa inledande kataloger för \"%s\""
+msgstr "kunde inte skapa inledande kataloger för ”%s”"
 
 #, c-format
 msgid "could not create work tree dir '%s'"
-msgstr "kunde inte skapa arbetskatalogen \"%s\""
+msgstr "kunde inte skapa arbetskatalogen ”%s”"
 
 #, c-format
 msgid "Cloning into bare repository '%s'...\n"
-msgstr "Klonar till ett naket arkiv \"%s\"...\n"
+msgstr "Klonar till ett naket arkiv ”%s”...\n"
 
 #, c-format
 msgid "Cloning into '%s'...\n"
-msgstr "Klonar till \"%s\"...\n"
+msgstr "Klonar till ”%s”...\n"
 
 msgid ""
 "clone --recursive is not compatible with both --reference and --reference-if-"
@@ -4269,7 +4279,7 @@ msgstr ""
 
 #, c-format
 msgid "'%s' is not a valid remote name"
-msgstr "\"%s\" är inte ett giltigt namn på fjärrarkiv"
+msgstr "”%s” är inte ett giltigt namn på fjärrarkiv"
 
 msgid "--depth is ignored in local clones; use file:// instead."
 msgstr "--depth ignoreras i lokala kloningar; använd file:// istället."
@@ -4301,7 +4311,10 @@ msgstr "misslyckades initiera arkivet, hoppar över bunt-URI"
 
 #, c-format
 msgid "failed to fetch objects from bundle URI '%s'"
-msgstr "misslyckades hämta objekt från bunt-URI \"%s\""
+msgstr "misslyckades hämta objekt från bunt-URI ”%s”"
+
+msgid "failed to fetch advertised bundles"
+msgstr "misslyckades hämta annonserade buntar"
 
 msgid "remote transport reported error"
 msgstr "fjärrtransport rapporterade fel"
@@ -4338,18 +4351,24 @@ msgid "--command must be the first argument"
 msgstr "--command måste vara första argument"
 
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir <objkat>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <kat>] [--shallow] [--[no-]progress]"
 
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 msgstr ""
-"git commit-graph write [--object-dir <objkat>] [--append] [--"
-"split[=<strategi>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <delnings-flaggor>"
+"git commit-graph write [--object-dir <kat>] [--append]\n"
+"                       [--split[=<strategi>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <delnings-flaggor>"
 
 msgid "dir"
 msgstr "kat"
@@ -4362,7 +4381,11 @@ msgstr "om inchecknignsgrafen är delad, kontrollera bara spetsfilen"
 
 #, c-format
 msgid "Could not open commit-graph '%s'"
-msgstr "Kunde inte öppna incheckningsgrafen \"%s\""
+msgstr "Kunde inte öppna incheckningsgrafen ”%s”"
+
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "kunde inte öppna incheckningsgrafen ”%s”"
 
 #, c-format
 msgid "unrecognized --split argument, %s"
@@ -4378,7 +4401,7 @@ msgstr "ogiltigt objekt: %s"
 
 #, c-format
 msgid "option `%s' expects a numerical value"
-msgstr "flaggan \"%s\" antar ett numeriskt värde"
+msgstr "flaggan ”%s” antar ett numeriskt värde"
 
 msgid "start walk at all refs"
 msgstr "starta traversering vid alla referenser"
@@ -4417,12 +4440,16 @@ msgstr "använd som mest en av --reachable, --stdin-commits och --stdin-packs"
 msgid "Collecting commits from input"
 msgstr "Hämtar incheckningar från indata"
 
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <träd> [(-p <förälder>)...]"
+
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p <föräldrer>)...] [-S[<nyckelid>]] [(-m "
-"<meddelande>)...] [(-F <fil>)...] <träd>"
+"git commit-tree [(-p <förälder>)...] [-S[<nyckelid>]] [(-m "
+"<meddelande>)...]\n"
+"                [(-F <fil>)...] <träd>"
 
 #, c-format
 msgid "duplicate parent %s ignored"
@@ -4434,11 +4461,11 @@ msgstr "objektnamnet är inte giltigt: %s"
 
 #, c-format
 msgid "git commit-tree: failed to read '%s'"
-msgstr "git commit-tree: misslyckades läsa \"%s\""
+msgstr "git commit-tree: misslyckades läsa ”%s”"
 
 #, c-format
 msgid "git commit-tree: failed to close '%s'"
-msgstr "git commit-tree: misslyckades stänga \"%s\""
+msgstr "git commit-tree: misslyckades stänga ”%s”"
 
 msgid "parent"
 msgstr "förälder"
@@ -4464,10 +4491,29 @@ msgstr "måste ange exakt ett träd"
 msgid "git commit-tree: failed to read"
 msgstr "git commit-tree: misslyckades läsa"
 
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<flaggor>] [--] <sökväg>..."
-
-msgid "git status [<options>] [--] <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"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<läge>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <incheckning> | --fixup [(amend|"
+"reword):]<incheckning>)]\n"
+"           [-F <fil> | -m <medd>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--"
+"author=<författare>]\n"
+"           [--date=<datum>] [--cleanup=<läge>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<fil> [--pathspec-file-nul]]\n"
+"           [(--trailer <symbol>[(=|:)<värde>])...] [-S[<nyckel-id>]]\n"
+"           [--] [<sökväg>...]"
+
+msgid "git status [<options>] [--] [<pathspec>...]"
 msgstr "git status [<flaggor>] [--] <sökväg>..."
 
 msgid ""
@@ -4477,7 +4523,7 @@ msgid ""
 msgstr ""
 "Du bad om att utöka den senaste incheckningen, men om du gör det\n"
 "blir den tom. Du kan köra kommandot på nytt med --allow-empty, eller\n"
-"så kan du ta bort incheckningen helt med \"git reset HEAD^\".\n"
+"så kan du ta bort incheckningen helt med ”git reset HEAD^”.\n"
 
 msgid ""
 "The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
@@ -4486,17 +4532,17 @@ msgid ""
 "    git commit --allow-empty\n"
 "\n"
 msgstr ""
-"Den tidigare \"cherry-pick\":en är nu tom, kanske på grund av en löst\n"
+"Den tidigare ”cherry-pick”:en är nu tom, kanske på grund av en löst\n"
 "konflikt. Om du vill checka in den ändå använder du:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
 
 msgid "Otherwise, please use 'git rebase --skip'\n"
-msgstr "Använd annars \"git rebase --skip\"\n"
+msgstr "Använd annars ”git rebase --skip”\n"
 
 msgid "Otherwise, please use 'git cherry-pick --skip'\n"
-msgstr "Använd annars \"git cherry-pick --skip\"\n"
+msgstr "Använd annars ”git cherry-pick --skip”\n"
 
 msgid ""
 "and then use:\n"
@@ -4513,12 +4559,15 @@ msgstr ""
 "\n"
 "    git cherry-pick --continue\n"
 "\n"
-"för att fortsätta \"cherry-pick\" med resterande incheckningar.\n"
+"för att fortsätta ”cherry-pick” med resterande incheckningar.\n"
 "Om du vill hoppa över den här incheckningen, använd:\n"
 "\n"
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "misslyckades uppdatera filer"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "misslyckades packa upp HEAD:s trädobjekt"
 
@@ -4537,9 +4586,6 @@ msgstr "kan inte uppdatera temporärt index"
 msgid "Failed to update main cache tree"
 msgstr "Misslyckades uppdatera huvud-cacheträdet"
 
-msgid "unable to write new_index file"
-msgstr "kunde inte skriva filen new_index"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "kan inte utföra en delvis incheckning under en sammanslagning."
 
@@ -4557,14 +4603,14 @@ msgstr "kunde inte skriva temporär indexfil"
 
 #, c-format
 msgid "commit '%s' lacks author header"
-msgstr "incheckningen \"%s\" saknar författarhuvud"
+msgstr "incheckningen ”%s” saknar författarhuvud"
 
 #, c-format
 msgid "commit '%s' has malformed author line"
-msgstr "incheckningen \"%s\" har felformaterat författarhuvud"
+msgstr "incheckningen ”%s” har felformaterat författarhuvud"
 
 msgid "malformed --author parameter"
-msgstr "felformad \"--author\"-flagga"
+msgstr "felformad ”--author”-flagga"
 
 #, c-format
 msgid "invalid date format: %s"
@@ -4578,8 +4624,8 @@ msgstr ""
 "i det befintliga incheckningsmeddelandet"
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "kunde inte slå upp incheckningen %s"
+msgid "could not lookup commit '%s'"
+msgstr "kunde inte slå upp incheckningen ”%s”"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -4590,11 +4636,11 @@ msgstr "kunde inte läsa logg från standard in"
 
 #, c-format
 msgid "could not read log file '%s'"
-msgstr "kunde inte läsa loggfilen \"%s\""
+msgstr "kunde inte läsa loggfilen ”%s”"
 
 #, c-format
 msgid "options '%s' and '%s:%s' cannot be used together"
-msgstr "flaggorna \"%s\" och \"%s:%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%s” och ”%s:%s” kan inte användas samtidigt"
 
 msgid "could not read SQUASH_MSG"
 msgstr "kunde inte läsa SQUASH_MSG"
@@ -4604,7 +4650,7 @@ msgstr "kunde inte läsa MERGE_MSG"
 
 #, c-format
 msgid "could not open '%s'"
-msgstr "kunde inte öppna \"%s\""
+msgstr "kunde inte öppna ”%s”"
 
 msgid "could not write commit template"
 msgstr "kunde inte skriva incheckningsmall"
@@ -4615,7 +4661,7 @@ msgid ""
 "with '%c' will be ignored.\n"
 msgstr ""
 "Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
-"med \"%c\" kommer ignoreras.\n"
+"med ”%c” kommer ignoreras.\n"
 
 #, c-format
 msgid ""
@@ -4623,8 +4669,7 @@ msgid ""
 "with '%c' will be ignored, and an empty message aborts the commit.\n"
 msgstr ""
 "Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
-"med \"%c\" kommer ignoreras, och ett tomt meddelande avbryter "
-"incheckningen.\n"
+"med ”%c” kommer ignoreras, och ett tomt meddelande avbryter incheckningen.\n"
 
 #, c-format
 msgid ""
@@ -4632,7 +4677,7 @@ msgid ""
 "with '%c' will be kept; you may remove them yourself if you want to.\n"
 msgstr ""
 "Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
-"med \"%c\" kommer behållas; du kan själv ta bort dem om du vill.\n"
+"med ”%c” kommer behållas; du kan själv ta bort dem om du vill.\n"
 
 #, c-format
 msgid ""
@@ -4641,7 +4686,7 @@ msgid ""
 "An empty message aborts the commit.\n"
 msgstr ""
 "Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
-"med \"%c\" kommer behållas; du kan själv ta bort dem om du vill.\n"
+"med ”%c” kommer behållas; du kan själv ta bort dem om du vill.\n"
 "Ett tomt meddelande avbryter incheckningen.\n"
 
 msgid ""
@@ -4702,11 +4747,11 @@ msgstr ""
 
 #, c-format
 msgid "Invalid ignored mode '%s'"
-msgstr "Ogiltigt ignorerat läge \"%s\""
+msgstr "Ogiltigt ignorerat läge ”%s”"
 
 #, c-format
 msgid "Invalid untracked files mode '%s'"
-msgstr "Ogiltigt läge för ospårade filer: \"%s\""
+msgstr "Ogiltigt läge för ospårade filer: ”%s”"
 
 msgid "You are in the middle of a merge -- cannot reword."
 msgstr "Du är i mitten av en sammanslagning -- kan inte omformulera."
@@ -4717,11 +4762,11 @@ msgstr "Du är i mitten av en cherry-pick -- kan inte omformulera."
 #, c-format
 msgid "reword option of '%s' and path '%s' cannot be used together"
 msgstr ""
-"reword-flaggan till \"%s\" och sökvägen \"%s\" kan inte användas tillsammans"
+"reword-flaggan till ”%s” och sökvägen ”%s” kan inte användas tillsammans"
 
 #, c-format
 msgid "reword option of '%s' and '%s' cannot be used together"
-msgstr "reword-flaggan till \"%s\" och \"%s\" kan inte användas tillsammans"
+msgstr "reword-flaggan till ”%s” och ”%s” kan inte användas tillsammans"
 
 msgid "You have nothing to amend."
 msgstr "Du har inget att utöka."
@@ -4744,7 +4789,7 @@ msgstr "okänd flagga: --fixup=%s:%s"
 
 #, c-format
 msgid "paths '%s ...' with -a does not make sense"
-msgstr "sökvägarna \"%s ...\" med -a ger ingen mening"
+msgstr "sökvägarna ”%s ...” med -a ger ingen mening"
 
 msgid "show status concisely"
 msgstr "visa koncis status"
@@ -4937,13 +4982,13 @@ msgstr "Avbryter på grund av tom incheckningsmeddelandekropp.\n"
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"arkivet har uppdaterats, men kunde inte skriva filen\n"
-"new_index. Kontrollera att disken inte är full och\n"
+"arkivet har uppdaterats, men kunde inte skriva ny\n"
+"indexfil. Kontrollera att disken inte är full och\n"
 "att kvoten inte har överskridits, och kör sedan\n"
-"\"git restore --staged :/\" för att återställa."
+"”git restore --staged :/” för att återställa."
 
 msgid "git config [<options>]"
 msgstr "git config [<flaggor>]"
@@ -5016,7 +5061,7 @@ msgid "list all"
 msgstr "visa alla"
 
 msgid "use string equality when comparing values to 'value-pattern'"
-msgstr "använd stränglikhet vid när värden jämförs med \"värde-mönster\""
+msgstr "använd stränglikhet vid när värden jämförs med ”värde-mönster”"
 
 msgid "open an editor"
 msgstr "öppna textredigeringsprogram"
@@ -5037,7 +5082,7 @@ msgid "value is given this type"
 msgstr "värdet har givits denna typ"
 
 msgid "value is \"true\" or \"false\""
-msgstr "värdet är \"true\" eller \"false\""
+msgstr "värdet är ”true” eller ”false”"
 
 msgid "value is decimal number"
 msgstr "värdet är ett decimalt tal"
@@ -5102,7 +5147,7 @@ msgstr "misslyckades formatera standardkonfigurationsvärde: %s"
 
 #, c-format
 msgid "cannot parse color '%s'"
-msgstr "kan inte tolka färgen \"%s\""
+msgstr "kan inte tolka färgen ”%s”"
 
 msgid "unable to parse default color value"
 msgstr "kan inte tolka standardfärgvärde"
@@ -5152,7 +5197,7 @@ msgid ""
 msgstr ""
 "--worktree kan inte användas med flera arbetskataloger om inte\n"
 "konfigurationsutöknignen worktreeConfig har aktiverats. Läsa stycket\n"
-"\"KONFIGURATIONSFIL\" i \"git help worktree\" för detaljer"
+"”KONFIGURATIONSFIL” i ”git help worktree” för detaljer"
 
 msgid "--get-color and variable type are incoherent"
 msgstr "--get-color och variabeltyp stämmer inte överens"
@@ -5173,11 +5218,11 @@ msgid "--default is only applicable to --get"
 msgstr "--default gäller bara för --get"
 
 msgid "--fixed-value only applies with 'value-pattern'"
-msgstr "--fixed-value gäller endast med \"värde-mönster\""
+msgstr "--fixed-value gäller endast med ”värde-mönster”"
 
 #, c-format
 msgid "unable to read config file '%s'"
-msgstr "kan inte konfigurationsfil \"%s\""
+msgstr "kan inte läsa konfigurationsfilen ”%s”"
 
 msgid "error processing config file(s)"
 msgstr "fel vid hantering av konfigurationsfil(er)"
@@ -5190,7 +5235,7 @@ msgstr "redigering av blobbar stöds ej"
 
 #, c-format
 msgid "cannot create configuration file %s"
-msgstr "kan inte skapa konfigurationsfilen \"%s\""
+msgstr "kan inte skapa konfigurationsfilen ”%s”"
 
 #, c-format
 msgid ""
@@ -5223,21 +5268,28 @@ msgid "print debugging messages to stderr"
 msgstr "skriv felsökningsmeddelanden på standard fel"
 
 msgid "credential-cache--daemon unavailable; no unix socket support"
-msgstr ""
-"\"credential-cache--daemon\" ej tillgänglig; stöd för unix-uttag saknas"
+msgstr "”credential-cache--daemon” ej tillgänglig; stöd för unix-uttag saknas"
 
 msgid "credential-cache unavailable; no unix socket support"
-msgstr "\"credential-cache\" ej tillgänglig; stöd för unix-uttag saknas"
+msgstr "”credential-cache” ej tillgänglig; stöd för unix-uttag saknas"
 
 #, c-format
 msgid "unable to get credential storage lock in %d ms"
 msgstr "kunde inte erhålla låset för lagring av inlogginsuppgifter på %d ms"
 
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<flaggor>] [<incheckning-igt>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tag] [--contains] [--abbrev=<n>] [<incheckning-"
+"igt>...]"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<märke>]"
 
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<flaggor>] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe <objekt>"
 
 msgid "head"
 msgstr "huvud"
@@ -5254,11 +5306,11 @@ msgstr "den annoterade taggen %s inte tillgänglig"
 
 #, c-format
 msgid "tag '%s' is externally known as '%s'"
-msgstr "taggen \"%s\" är utanför känd som \"%s\""
+msgstr "taggen ”%s” är utanför känd som ”%s”"
 
 #, c-format
 msgid "no tag exactly matches '%s'"
-msgstr "ingen tagg motsvarar \"%s\" exakt"
+msgstr "ingen tagg motsvarar ”%s” exakt"
 
 #, c-format
 msgid "No exact match on refs or tags, searching to describe\n"
@@ -5274,7 +5326,7 @@ msgid ""
 "No annotated tags can describe '%s'.\n"
 "However, there were unannotated tags: try --tags."
 msgstr ""
-"Inga annoterade taggar kan beskriva \"%s\".\n"
+"Inga annoterade taggar kan beskriva ”%s”.\n"
 "Det finns dock oannoterade taggar: testa --tags."
 
 #, c-format
@@ -5282,7 +5334,7 @@ msgid ""
 "No tags can describe '%s'.\n"
 "Try --always, or create some tags."
 msgstr ""
-"Inga taggar kan beskriva \"%s\".\n"
+"Inga taggar kan beskriva ”%s”.\n"
 "Testa --always, eller skapa några taggar."
 
 #, c-format
@@ -5346,24 +5398,25 @@ msgid "mark"
 msgstr "märke"
 
 msgid "append <mark> on dirty working tree (default: \"-dirty\")"
-msgstr "lägg till <märke> på lortigt arbetsträd (standard: \"-dirty\")"
+msgstr "lägg till <märke> på lortigt arbetsträd (standard: ”-dirty”)"
 
 msgid "append <mark> on broken working tree (default: \"-broken\")"
-msgstr "lägg till <märke> på trasigt arbetsträd (standard: \"-broken\")"
+msgstr "lägg till <märke> på trasigt arbetsträd (standard: ”-broken”)"
 
 msgid "No names found, cannot describe anything."
 msgstr "Inga namn hittades, kan inte beskriva något."
 
 #, c-format
 msgid "option '%s' and commit-ishes cannot be used together"
-msgstr "flaggorna \"%s\" och incheckning-igter kan inte användas samtidigt"
+msgstr "flaggorna ”%s” och incheckning-igter kan inte användas samtidigt"
 
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory <sökväg>] [-s|--suffix <format>] [--"
-"mode=<läge>]"
+"git diagnose [(-o | --output-directory) <sökväg>] [(-s | --suffix) "
+"<format>]\n"
+"             [--mode=<läge>]"
 
 msgid "specify a destination for the diagnostics archive"
 msgstr "ange mål för diagnostikarkivet"
@@ -5379,7 +5432,10 @@ msgstr "--merge-base fungerar endast med två incheckningar"
 
 #, c-format
 msgid "'%s': not a regular file or symlink"
-msgstr "\"%s\": inte en normal fil eller symbolisk länk"
+msgstr "”%s”: inte en normal fil eller symbolisk länk"
+
+msgid "no merge given, only parents."
+msgstr "ingen sammanslagning angiven, endast föräldrar."
 
 #, c-format
 msgid "invalid option: %s"
@@ -5394,15 +5450,15 @@ msgstr "Inte ett git-arkiv"
 
 #, c-format
 msgid "invalid object '%s' given."
-msgstr "objektet \"%s\" som angavs är felaktigt."
+msgstr "objektet ”%s” som angavs är felaktigt."
 
 #, c-format
 msgid "more than two blobs given: '%s'"
-msgstr "mer än två blobbar angavs: \"%s\""
+msgstr "mer än två blobbar angavs: ”%s”"
 
 #, c-format
 msgid "unhandled object '%s' given."
-msgstr "ej hanterat objekt \"%s\" angavs."
+msgstr "ej hanterat objekt ”%s” angavs."
 
 #, c-format
 msgid "%s...%s: multiple merge bases, using %s"
@@ -5428,23 +5484,23 @@ msgid ""
 "combined diff formats ('-c' and '--cc') are not supported in\n"
 "directory diff mode ('-d' and '--dir-diff')."
 msgstr ""
-"kombinerade diff-format (\"-c\" och \"--cc\") stöds inte i\n"
-"katalogdiffläge (\"-d\" och \"--dir-diff\")."
+"kombinerade diff-format (”-c” och ”--cc”) stöds inte i\n"
+"katalogdiffläge (”-d” och ”--dir-diff”)."
 
 #, c-format
 msgid "both files modified: '%s' and '%s'."
-msgstr "bägge filerna ändrade: \"%s\" och \"%s\"."
+msgstr "bägge filerna ändrade: ”%s” och ”%s”."
 
 msgid "working tree file has been left."
 msgstr "filen i arbetskatalogen lämnades kvar."
 
 #, c-format
 msgid "could not copy '%s' to '%s'"
-msgstr "kunde inte kopiera in \"%s\" till \"%s\""
+msgstr "kunde inte kopiera in ”%s” till ”%s”"
 
 #, c-format
 msgid "temporary files exist in '%s'."
-msgstr "temporära filer finns i \"%s\"."
+msgstr "temporära filer finns i ”%s”."
 
 msgid "you may want to cleanup or recover these."
 msgstr "du kanske vill städa eller rädda dem."
@@ -5454,7 +5510,7 @@ msgid "failed: %d"
 msgstr "misslyckades: %d"
 
 msgid "use `diff.guitool` instead of `diff.tool`"
-msgstr "använd \"diff.guitool\" istället för \"diff.tool\""
+msgstr "använd ”diff.guitool” istället för ”diff.tool”"
 
 msgid "perform a full-directory diff"
 msgstr "utför diff för hela katalogen"
@@ -5472,20 +5528,20 @@ msgid "use the specified diff tool"
 msgstr "använd angivet diff-verktyg"
 
 msgid "print a list of diff tools that may be used with `--tool`"
-msgstr "visa en lista över diff-verktyg som kan användas med \"--tool\""
+msgstr "visa en lista över diff-verktyg som kan användas med ”--tool”"
 
 msgid ""
 "make 'git-difftool' exit when an invoked diff tool returns a non-zero exit "
 "code"
 msgstr ""
-"låt \"git-difftool\" avsluta när ett anropat diff-verktyg ger returvärde "
-"skilt från noll"
+"låt ”git-difftool” avsluta när ett anropat diff-verktyg ger returvärde skilt "
+"från noll"
 
 msgid "specify a custom command for viewing diffs"
 msgstr "ange eget kommando för att visa diffar"
 
 msgid "passed to `diff`"
-msgstr "sändes till \"diff\""
+msgstr "sändes till ”diff”"
 
 msgid "difftool requires worktree or --no-index"
 msgstr "difftool kräver en arbetskatalog eller --no-index"
@@ -5496,29 +5552,6 @@ msgstr "inget <verktyg> angavs för --tool=<verktyg>"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "inget <kommando> angavs för --extcmd=<kommando>"
 
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <flaggor> <miljövariabel>"
-
-msgid "default for git_env_*(...) to fall back on"
-msgstr "standard för git_env_*(...) att falla tillbaka på"
-
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr "var tyst, använd bara git_env_*() som resultatvärde"
-
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr ""
-"flaggan \"--default\" förväntar ett sanningsvärde med \"--type=bool\", inte "
-"\"%s\""
-
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not `"
-"%s`"
-msgstr ""
-"flaggan \"--default\" förväntar ett teckenlöst långt värde med \"--type=ulong"
-"\", inte \"%s\""
-
 msgid "git fast-export [<rev-list-opts>]"
 msgstr "git fast-export [<rev-list-flaggor>]"
 
@@ -5587,26 +5620,26 @@ msgstr "märk taggar med märke-id"
 
 #, c-format
 msgid "Missing from marks for submodule '%s'"
-msgstr "Saknar från-märken för undermodulen \"%s\""
+msgstr "Saknar från-märken för undermodulen ”%s”"
 
 #, c-format
 msgid "Missing to marks for submodule '%s'"
-msgstr "Saknar till-märken för undermodulen \"%s\""
+msgstr "Saknar till-märken för undermodulen ”%s”"
 
 #, c-format
 msgid "Expected 'mark' command, got %s"
-msgstr "Förväntade \"mark\"-kommando, fick %s"
+msgstr "Förväntade ”mark”-kommando, fick %s"
 
 #, c-format
 msgid "Expected 'to' command, got %s"
-msgstr "Förväntade \"to\"-kommando, fick %s"
+msgstr "Förväntade ”to”-kommando, fick %s"
 
 msgid "Expected format name:filename for submodule rewrite option"
 msgstr "Förvändae formatet namn:filnamn för undermodul-omskrivningsflaggan"
 
 #, c-format
 msgid "feature '%s' forbidden in input without --allow-unsafe-features"
-msgstr "funktionen \"%s\" förbjuden i indata utan --allow-unsafe-features"
+msgstr "funktionen ”%s” förbjuden i indata utan --allow-unsafe-features"
 
 #, c-format
 msgid "Lockfile created but not reported: %s"
@@ -5627,114 +5660,12 @@ msgstr "git fetch --all [<flaggor>]"
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel kan inte vara negativt"
 
-msgid "fetch from all remotes"
-msgstr "hämta från alla fjärrar"
-
-msgid "set upstream for git pull/fetch"
-msgstr "ställ in uppström för git pull/fetch"
+msgid "couldn't find remote ref HEAD"
+msgstr "kunde inte hitta fjärr-referensen HEAD"
 
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "lägg till i .git/FETCH_HEAD istället för att skriva över"
-
-msgid "use atomic transaction to update references"
-msgstr "använd atomiska transaktioner för att uppdatera referenser"
-
-msgid "path to upload pack on remote end"
-msgstr "sökväg till upload pack på fjärren"
-
-msgid "force overwrite of local reference"
-msgstr "tvinga överskrivning av lokal referens"
-
-msgid "fetch from multiple remotes"
-msgstr "hämta från flera fjärrar"
-
-msgid "fetch all tags and associated objects"
-msgstr "hämta alla taggar och associerade objekt"
-
-msgid "do not fetch all tags (--no-tags)"
-msgstr "hämta inte alla taggar (--no-tags)"
-
-msgid "number of submodules fetched in parallel"
-msgstr "antal undermoduler som hämtas parallellt"
-
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr ""
-"modifiera referensspecifikationen så att alla referenser hamnar i refs/"
-"prefetch/"
-
-msgid "prune remote-tracking branches no longer on remote"
-msgstr "rensa fjärrspårande grenar ej längre på fjärren"
-
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr ""
-"rensa lokala taggar inte längre på fjärren och skriv över ändrade taggar"
-
-msgid "on-demand"
-msgstr "on-demand"
-
-msgid "control recursive fetching of submodules"
-msgstr "styr rekursiv hämtning av undermoduler"
-
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "skriv hämtade referenser till FETCH_HEAD-filen"
-
-msgid "keep downloaded pack"
-msgstr "behåll hämtade paket"
-
-msgid "allow updating of HEAD ref"
-msgstr "tillåt uppdatering av HEAD-referens"
-
-msgid "deepen history of shallow clone"
-msgstr "fördjupa historik för grund klon"
-
-msgid "deepen history of shallow repository based on time"
-msgstr "fördjupa historik för grund klon baserad på tid"
-
-msgid "convert to a complete repository"
-msgstr "konvertera till komplett arkiv"
-
-msgid "re-fetch without negotiating common commits"
-msgstr "hämta om utan att förhandla om gemensamma incheckningar"
-
-msgid "prepend this to submodule path output"
-msgstr "lägg till i början av undermodulens sökvägsutdata"
-
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr ""
-"standard för rekursiv hämtning av undermoduler (lägre prioritet än "
-"konfigurationsfiler)"
-
-msgid "accept refs that update .git/shallow"
-msgstr "tar emot referenser som uppdaterar .git/shallow"
-
-msgid "refmap"
-msgstr "referenskarta"
-
-msgid "specify fetch refmap"
-msgstr "ange referenskarta för \"fetch\""
-
-msgid "report that we have only objects reachable from this object"
-msgstr "rapportera att vi bara har objekt nåbara från detta objektet"
-
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr "hämta inte paketfil; skriv istället förfäder till förhandlingstips"
-
-msgid "run 'maintenance --auto' after fetching"
-msgstr "kör \"maintenance --auto\" efter hämtning"
-
-msgid "check for forced-updates on all updated branches"
-msgstr "se efter tvingade uppdateringar i alla uppdaterade grenar"
-
-msgid "write the commit-graph after fetching"
-msgstr "skriv incheckingsgrafen efter hämtning"
-
-msgid "accept refspecs from stdin"
-msgstr "ta emot referenser från standard in"
-
-msgid "couldn't find remote ref HEAD"
-msgstr "kunde inte hitta fjärr-referensen HEAD"
+#, c-format
+msgid "From %.*s\n"
+msgstr "Från %.*s\n"
 
 #, c-format
 msgid "object %s not found"
@@ -5775,7 +5706,7 @@ msgstr "ej snabbspolad"
 
 #, c-format
 msgid "cannot open '%s'"
-msgstr "kan inte öppna \"%s\""
+msgstr "kan inte öppna ”%s”"
 
 msgid ""
 "fetch normally indicates which branches had a forced update,\n"
@@ -5784,8 +5715,8 @@ msgid ""
 msgstr ""
 "fetch visar normalt vilka grenar som tvångsuppdaterats, men testet har "
 "slagits\n"
-"av; för att slå på igen, använd flaggan \"--show-forced-updates\" eller kör\n"
-"\"git config fetch.showForcedUpdates true\""
+"av; för att slå på igen, använd flaggan ”--show-forced-updates” eller kör\n"
+"”git config fetch.showForcedUpdates true”"
 
 #, c-format
 msgid ""
@@ -5795,9 +5726,8 @@ msgid ""
 "to avoid this check\n"
 msgstr ""
 "det tog %.2f sekunder att se efter tvångsuppdateringar; Du kan använda\n"
-"\"--no-show-forced-updates\" eller köra \"git config fetch."
-"showForcedUpdates\n"
-"false\" för att undvika testet\n"
+"”--no-show-forced-updates” eller köra ”git config fetch.showForcedUpdates\n"
+"false” för att undvika testet\n"
 
 #, c-format
 msgid "%s did not send all necessary objects\n"
@@ -5807,17 +5737,13 @@ msgstr "%s sände inte alla nödvändiga objekt\n"
 msgid "rejected %s because shallow roots are not allowed to be updated"
 msgstr "avvisade %s då grunda rötter inte tillåts uppdateras"
 
-#, c-format
-msgid "From %.*s\n"
-msgstr "Från %.*s\n"
-
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
 " 'git remote prune %s' to remove any old, conflicting branches"
 msgstr ""
 "vissa lokala referenser kunde inte uppdateras; testa att köra\n"
-" \"git remote prune %s\" för att ta bort gamla grenar som står i konflikt"
+" ”git remote prune %s” för att ta bort gamla grenar som står i konflikt"
 
 #, c-format
 msgid "   (%s will become dangling)"
@@ -5835,15 +5761,15 @@ msgstr "(ingen)"
 
 #, c-format
 msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "vägrar hämta till grenen \"%s\" som är utcheckad på \"%s\""
+msgstr "vägrar hämta till grenen ”%s” som är utcheckad på ”%s”"
 
 #, c-format
 msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "flaggan \"%s\" med värdet \"%s\" är inte giltigt för %s"
+msgstr "flaggan ”%s” med värdet ”%s” är inte giltigt för %s"
 
 #, c-format
 msgid "option \"%s\" is ignored for %s\n"
-msgstr "flaggan \"%s\" ignoreras för %s\n"
+msgstr "flaggan ”%s” ignoreras för %s\n"
 
 #, c-format
 msgid "%s is not a valid object"
@@ -5861,8 +5787,8 @@ msgid ""
 "could not set upstream of HEAD to '%s' from '%s' when it does not point to "
 "any branch."
 msgstr ""
-"kunde inte sätta uppström för HEAD till \"%s\" från \"%s\" när det inte "
-"pekar mot någon gren."
+"kunde inte sätta uppström för HEAD till ”%s” från ”%s” när det inte pekar "
+"mot någon gren."
 
 msgid "not setting upstream for a remote remote-tracking branch"
 msgstr "ställer inte in uppströmsgren för en fjärrspårande gren på fjärren"
@@ -5890,7 +5816,7 @@ msgstr "kunde inte hämta %s"
 
 #, c-format
 msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "kunde inte hämta \"%s\" (felkod: %d)\n"
+msgstr "kunde inte hämta ”%s” (felkod: %d)\n"
 
 msgid ""
 "no remote repository specified; please specify either a URL or a\n"
@@ -5902,6 +5828,112 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr "du måste ange namnet på en tagg"
 
+msgid "fetch from all remotes"
+msgstr "hämta från alla fjärrar"
+
+msgid "set upstream for git pull/fetch"
+msgstr "ställ in uppström för git pull/fetch"
+
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "lägg till i .git/FETCH_HEAD istället för att skriva över"
+
+msgid "use atomic transaction to update references"
+msgstr "använd atomiska transaktioner för att uppdatera referenser"
+
+msgid "path to upload pack on remote end"
+msgstr "sökväg till upload pack på fjärren"
+
+msgid "force overwrite of local reference"
+msgstr "tvinga överskrivning av lokal referens"
+
+msgid "fetch from multiple remotes"
+msgstr "hämta från flera fjärrar"
+
+msgid "fetch all tags and associated objects"
+msgstr "hämta alla taggar och associerade objekt"
+
+msgid "do not fetch all tags (--no-tags)"
+msgstr "hämta inte alla taggar (--no-tags)"
+
+msgid "number of submodules fetched in parallel"
+msgstr "antal undermoduler som hämtas parallellt"
+
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"modifiera referensspecifikationen så att alla referenser hamnar i refs/"
+"prefetch/"
+
+msgid "prune remote-tracking branches no longer on remote"
+msgstr "rensa fjärrspårande grenar ej längre på fjärren"
+
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr ""
+"rensa lokala taggar inte längre på fjärren och skriv över ändrade taggar"
+
+msgid "on-demand"
+msgstr "on-demand"
+
+msgid "control recursive fetching of submodules"
+msgstr "styr rekursiv hämtning av undermoduler"
+
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "skriv hämtade referenser till FETCH_HEAD-filen"
+
+msgid "keep downloaded pack"
+msgstr "behåll hämtade paket"
+
+msgid "allow updating of HEAD ref"
+msgstr "tillåt uppdatering av HEAD-referens"
+
+msgid "deepen history of shallow clone"
+msgstr "fördjupa historik för grund klon"
+
+msgid "deepen history of shallow repository based on time"
+msgstr "fördjupa historik för grund klon baserad på tid"
+
+msgid "convert to a complete repository"
+msgstr "konvertera till komplett arkiv"
+
+msgid "re-fetch without negotiating common commits"
+msgstr "hämta om utan att förhandla om gemensamma incheckningar"
+
+msgid "prepend this to submodule path output"
+msgstr "lägg till i början av undermodulens sökvägsutdata"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"standard för rekursiv hämtning av undermoduler (lägre prioritet än "
+"konfigurationsfiler)"
+
+msgid "accept refs that update .git/shallow"
+msgstr "tar emot referenser som uppdaterar .git/shallow"
+
+msgid "refmap"
+msgstr "referenskarta"
+
+msgid "specify fetch refmap"
+msgstr "ange referenskarta för ”fetch”"
+
+msgid "report that we have only objects reachable from this object"
+msgstr "rapportera att vi bara har objekt nåbara från detta objektet"
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr "hämta inte paketfil; skriv istället förfäder till förhandlingstips"
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr "kör ”maintenance --auto” efter hämtning"
+
+msgid "check for forced-updates on all updated branches"
+msgstr "se efter tvingade uppdateringar i alla uppdaterade grenar"
+
+msgid "write the commit-graph after fetching"
+msgstr "skriv incheckingsgrafen efter hämtning"
+
+msgid "accept refspecs from stdin"
+msgstr "ta emot referenser från standard in"
+
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only behöver en eller flera --negotiation-tip=*"
 
@@ -5911,6 +5943,10 @@ msgstr "negativa djup stöds inte i --deepen"
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "--unshallow kan inte användas på ett komplett arkiv"
 
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "misslyckades hämta buntar från ”%s”"
+
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all tar inte namnet på ett arkiv som argument"
 
@@ -6013,8 +6049,14 @@ msgstr "visa endast referenser som innehåller incheckningen"
 msgid "print only refs which don't contain the commit"
 msgstr "visa endast referenser som inte innehåller incheckningen"
 
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=<konfig> <kommandoargument>"
+msgid "read reference patterns from stdin"
+msgstr "läs referensmönster från standard in"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "okända argument angavs tillsammans med --stdin"
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<konfig> [--] <argument>"
 
 msgid "config"
 msgstr "konfig"
@@ -6025,6 +6067,10 @@ msgstr "konfigurationsnyckel som innehåller en lista över arkivsökvägar"
 msgid "missing --config=<config>"
 msgstr "saknar --config=<konfig>"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "fick felaktig konfiguration --config=%s"
+
 msgid "unknown"
 msgstr "okänd"
 
@@ -6075,11 +6121,11 @@ msgstr "kunde inte skapa lost-found"
 
 #, c-format
 msgid "could not write '%s'"
-msgstr "kunde inte skriva \"%s\""
+msgstr "kunde inte skriva ”%s”"
 
 #, c-format
 msgid "could not finish '%s'"
-msgstr "kunde inte avsluta \"%s\""
+msgstr "kunde inte avsluta ”%s”"
 
 #, c-format
 msgid "Checking %s"
@@ -6116,7 +6162,7 @@ msgstr "%s: ogiltig reflog-post %s"
 
 #, c-format
 msgid "Checking reflog %s->%s"
-msgstr "Kontrollerar reflog %s->%s"
+msgstr "Kontrollerar reflog %s%s"
 
 #, c-format
 msgid "%s: invalid sha1 pointer %s"
@@ -6139,7 +6185,7 @@ msgstr "%s: objektet trasigt eller saknas: %s"
 
 #, c-format
 msgid "%s: object is of unknown type '%s': %s"
-msgstr "%s: objektet har okänd typ \"%s\": %s"
+msgstr "%s: objektet har okänd typ ”%s”: %s"
 
 #, c-format
 msgid "%s: object could not be parsed: %s"
@@ -6175,22 +6221,39 @@ msgstr "%s: frånkopplat HEAD pekar på ingenting"
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "obs: %s pekar på en ofödd gren (%s)"
 
-msgid "Checking cache tree"
-msgstr "Kontrollerar cacheträd"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Kontrollerar cacheträd för %s"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: ogiltig sha1-pekare i cacheträd"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: ogiltig sha1-pekare i cacheträd för %s"
 
 msgid "non-tree in cache-tree"
 msgstr "icke-träd i cacheträd"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: ogiltig sha1-pekare i resolve-undo"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: ogiltig sha1-pekare i resolve-undo för %s"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "kunde inte läsa rev-index för paketfil ”%s”"
 
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<flaggor>] [<objekt>...]"
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "ogiltigt rev-index för paketet ”%s”"
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<objekt>...]"
 
 msgid "show unreachable objects"
 msgstr "visa onåbara objekt"
@@ -6239,7 +6302,7 @@ msgstr "%s: objekt saknas"
 
 #, c-format
 msgid "invalid parameter: expected sha1, got '%s'"
-msgstr "ogiltig parameter: förväntade sha1, fick \"%s\""
+msgstr "ogiltig parameter: förväntade sha1, fick ”%s”"
 
 msgid "git fsmonitor--daemon start [<options>]"
 msgstr "git fsmonitor--daemon start [<flaggor>]"
@@ -6247,31 +6310,25 @@ msgstr "git fsmonitor--daemon start [<flaggor>]"
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [<flaggor>]"
 
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
-
 #, c-format
 msgid "value of '%s' out of range: %d"
-msgstr "värdet för \"%s\" utanför intervallet: %d"
+msgstr "värdet för ”%s” utanför intervallet: %d"
 
 #, c-format
 msgid "value of '%s' not bool or int: %d"
-msgstr "värdet för \"%s\" är inte bool eller int: %d"
+msgstr "värdet för ”%s” är inte bool eller int: %d"
 
 #, c-format
 msgid "fsmonitor-daemon is watching '%s'\n"
-msgstr "fsmonitor-daemon bevakar \"%s\"\n"
+msgstr "fsmonitor-daemon bevakar ”%s”\n"
 
 #, c-format
 msgid "fsmonitor-daemon is not watching '%s'\n"
-msgstr "fsmonitor-daemon bevakar inte \"%s\"\n"
+msgstr "fsmonitor-daemon bevakar inte ”%s”\n"
 
 #, c-format
 msgid "could not create fsmonitor cookie '%s'"
-msgstr "kunde inte skapa fsmonitor-kaka \"%s\""
+msgstr "kunde inte skapa fsmonitor-kaka ”%s”"
 
 #, c-format
 msgid "fsmonitor: cookie_result '%d' != SEEN"
@@ -6279,7 +6336,7 @@ 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ådpol på ”%s”"
 
 msgid "could not start fsmonitor listener thread"
 msgstr "kunde inte starta fsmonitor-lyssnartråd"
@@ -6295,19 +6352,19 @@ msgstr "kunde inte initiera hälsotråd"
 
 #, c-format
 msgid "could not cd home '%s'"
-msgstr "kunde inte byta katalog hem \"%s\""
+msgstr "kunde inte byta katalog hem ”%s”"
 
 #, c-format
 msgid "fsmonitor--daemon is already running '%s'"
-msgstr "fsmonitor--daemon körs redan på \"%s\""
+msgstr "fsmonitor--daemon körs redan på ”%s”"
 
 #, c-format
 msgid "running fsmonitor-daemon in '%s'\n"
-msgstr "kör fsmonitor-daemon i \"%s\"\n"
+msgstr "kör fsmonitor-daemon i ”%s”\n"
 
 #, c-format
 msgid "starting fsmonitor-daemon in '%s'\n"
-msgstr "startar fsmonitor-daemon i \"%s\"\n"
+msgstr "startar fsmonitor-daemon i ”%s”\n"
 
 msgid "daemon failed to start"
 msgstr "serverprocessen kunde inte startas"
@@ -6329,11 +6386,11 @@ msgstr "max sekunder att vänta på att serverprocessen startar"
 
 #, c-format
 msgid "invalid 'ipc-threads' value (%d)"
-msgstr "ogiltigt värde för \"ipc-threads\" (%d)"
+msgstr "ogiltigt värde för ”ipc-threads” (%d)"
 
 #, c-format
 msgid "Unhandled subcommand '%s'"
-msgstr "Ej hanterat underkommando \"%s\""
+msgstr "Ej hanterat underkommando ”%s”"
 
 msgid "fsmonitor--daemon not supported on this platform"
 msgstr "fsmonitor--daemon stöds inte på denna plattform"
@@ -6347,11 +6404,11 @@ msgstr "Misslyckades ta status (fstat) på %s: %s"
 
 #, c-format
 msgid "failed to parse '%s' value '%s'"
-msgstr "misslyckades tolka \"%s\" värde \"%s\""
+msgstr "misslyckades tolka ”%s” värde ”%s”"
 
 #, c-format
 msgid "cannot stat '%s'"
-msgstr "kan inte ta status på \"%s\""
+msgstr "kan inte ta status på ”%s”"
 
 #, c-format
 msgid ""
@@ -6373,6 +6430,9 @@ msgstr "rensa ej refererade objekt"
 msgid "pack unreferenced objects separately"
 msgstr "packa ej refererade objekt separat"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "med --cruft, begränsa storleken på nya onödiga paket"
+
 msgid "be more thorough (increased runtime)"
 msgstr "var mer grundlig (ökar körtiden)"
 
@@ -6403,20 +6463,19 @@ msgstr "Packar arkivet automatiskt för optimal prestanda.\n"
 
 #, c-format
 msgid "See \"git help gc\" for manual housekeeping.\n"
-msgstr "Se \"git help gc\" för manuell hushållning.\n"
+msgstr "Se ”git help gc” för manuell hushållning.\n"
 
 #, c-format
 msgid ""
 "gc is already running on machine '%s' pid %<PRIuMAX> (use --force if not)"
 msgstr ""
-"gc körs redan på maskinen \"%s\" pid %<PRIuMAX> (använd --force om så inte "
-"är fallet)"
+"gc körs redan på maskinen ”%s” pid %<PRIuMAX> (använd --force om så inte är "
+"fallet)"
 
 msgid ""
 "There are too many unreachable loose objects; run 'git prune' to remove them."
 msgstr ""
-"Det finns för många onåbara lösa objekt; kör \"git prune\" för att ta bort "
-"dem."
+"Det finns för många onåbara lösa objekt; kör ”git prune” för att ta bort dem."
 
 msgid ""
 "git maintenance run [--auto] [--[no-]quiet] [--task=<task>] [--schedule]"
@@ -6437,41 +6496,41 @@ msgid "failed to prefetch remotes"
 msgstr "kunde inte förhämta fjärrar"
 
 msgid "failed to start 'git pack-objects' process"
-msgstr "kunde inte starta \"git pack-objects\"-process"
+msgstr "kunde inte starta ”git pack-objects”-process"
 
 msgid "failed to finish 'git pack-objects' process"
-msgstr "kunde inte avsluta \"git pack-objects\"-process"
+msgstr "kunde inte avsluta ”git pack-objects”-process"
 
 msgid "failed to write multi-pack-index"
 msgstr "kunde inte skriva multi-pack-index"
 
 msgid "'git multi-pack-index expire' failed"
-msgstr "\"git multi-pack-index expire\" misslyckades"
+msgstr "”git multi-pack-index expire” misslyckades"
 
 msgid "'git multi-pack-index repack' failed"
-msgstr "\"git multi-pack-index repack\" misslyckades"
+msgstr "”git multi-pack-index repack” misslyckades"
 
 msgid ""
 "skipping incremental-repack task because core.multiPackIndex is disabled"
 msgstr ""
-"hoppar över \"incremental-repack\"-uppgift eftersom core.multiPackIndex är "
+"hoppar över ”incremental-repack”-uppgift eftersom core.multiPackIndex är "
 "inaktiverat"
 
 #, c-format
 msgid "lock file '%s' exists, skipping maintenance"
-msgstr "låsfilen \"%s\" finns, hoppar över underhåll"
+msgstr "låsfilen ”%s” finns, hoppar över underhåll"
 
 #, c-format
 msgid "task '%s' failed"
-msgstr "uppgiften \"%s\" misslyckades"
+msgstr "uppgiften ”%s” misslyckades"
 
 #, c-format
 msgid "'%s' is not a valid task"
-msgstr "\"%s\" är inte en giltig uppgift"
+msgstr "”%s” är inte en giltig uppgift"
 
 #, c-format
 msgid "task '%s' cannot be selected multiple times"
-msgstr "uppgiften \"%s\" kan inte väljas flera gånger"
+msgstr "uppgiften ”%s” kan inte väljas flera gånger"
 
 msgid "run tasks based on the state of the repository"
 msgstr "kör uppgifter baserad på arkivets tillstånd"
@@ -6494,19 +6553,31 @@ msgstr "utför en specifik uppgift"
 msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr "använd som mest en av --auto och --schedule=<frekvens>"
 
-msgid "failed to run 'git config'"
-msgstr "misslyckades köra \"git config\""
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "kan inte lägga till ”%s”-värdet för ”%s”"
+
+msgid "return success even if repository was not registered"
+msgstr "returnera framgång även om arkivet inte var registrerat"
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "kan inte ta bort ”%s”-värdet för ”%s”"
+
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "arkivet ”%s” har inte registrerats"
 
 #, c-format
 msgid "failed to expand path '%s'"
-msgstr "misslyckades expandera sökvägen \"%s\""
+msgstr "misslyckades expandera sökvägen ”%s”"
 
 msgid "failed to start launchctl"
 msgstr "misslyckades starta launchctl"
 
 #, c-format
 msgid "failed to create directories for '%s'"
-msgstr "misslyckades skapa kataloger för \"%s\""
+msgstr "misslyckades skapa kataloger för ”%s”"
 
 #, c-format
 msgid "failed to bootstrap service %s"
@@ -6519,8 +6590,7 @@ msgid "failed to start schtasks"
 msgstr "misslyckades starta schtasks"
 
 msgid "failed to run 'crontab -l'; your system might not support 'cron'"
-msgstr ""
-"misslyckades köra \"crontab -l\"; ditt system kanske inte stöder \"cron\""
+msgstr "misslyckades köra ”crontab -l”; ditt system kanske inte stöder ”cron”"
 
 msgid "failed to create crontab temporary file"
 msgstr "misslyckades skapa temporär crontab-fil"
@@ -6529,28 +6599,28 @@ msgid "failed to open temporary file"
 msgstr "misslyckades öppna temporär fil"
 
 msgid "failed to run 'crontab'; your system might not support 'cron'"
-msgstr "misslyckades köra \"crontab\"; ditt system kanske inte stöder \"cron\""
+msgstr "misslyckades köra ”crontab”; ditt system kanske inte stöder ”cron”"
 
 msgid "'crontab' died"
-msgstr "\"crontab\" dog"
-
-msgid "failed to start systemctl"
-msgstr "misslyckades starta systemctl"
-
-msgid "failed to run systemctl"
-msgstr "misslyckades att köra systemctl"
+msgstr "”crontab” dog"
 
 #, c-format
 msgid "failed to delete '%s'"
-msgstr "misslyckades ta bort \"%s\""
+msgstr "misslyckades ta bort ”%s”"
 
 #, c-format
 msgid "failed to flush '%s'"
-msgstr "misslyckades spola \"%s\""
+msgstr "misslyckades spola ”%s”"
+
+msgid "failed to start systemctl"
+msgstr "misslyckades starta systemctl"
+
+msgid "failed to run systemctl"
+msgstr "misslyckades att köra systemctl"
 
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
-msgstr "okänt argument för --scheduler, \"%s\""
+msgstr "okänt argument för --scheduler, ”%s”"
 
 msgid "neither systemd timers nor crontab are available"
 msgstr "varken systemd-timer eller crontab är tillgänglig"
@@ -6569,7 +6639,10 @@ msgid "scheduler"
 msgstr "schemaläggare"
 
 msgid "scheduler to trigger git maintenance run"
-msgstr "schemaläggare som utlöser \"git maintenance\"-körning"
+msgstr "schemaläggare som utlöser ”git maintenance”-körning"
+
+msgid "failed to set up maintenance schedule"
+msgstr "misslyckades uppdatera underhållsschema"
 
 msgid "failed to add repo to global config"
 msgstr "misslyckades lägga till arkiv till global konfiguration"
@@ -6603,11 +6676,11 @@ msgstr "kunde inte läsa träd (%s)"
 
 #, c-format
 msgid "unable to grep from object of type %s"
-msgstr "kunde inte \"grep\" från objekt av typen %s"
+msgstr "kunde inte ”grep” från objekt av typen %s"
 
 #, c-format
 msgid "switch `%c' expects a numerical value"
-msgstr "flaggan \"%c\" antar ett numeriskt värde"
+msgstr "flaggan ”%c” antar ett numeriskt värde"
 
 msgid "search in index instead of in the work tree"
 msgstr "sök i indexet istället för i arbetskatalogen"
@@ -6619,7 +6692,7 @@ msgid "search in both tracked and untracked files"
 msgstr "sök i både spårade och ospårade filer"
 
 msgid "ignore files specified via '.gitignore'"
-msgstr "ignorera filer angivna i \".gitignore\""
+msgstr "ignorera filer angivna i ”.gitignore”"
 
 msgid "recursively search in each submodule"
 msgstr "sök varje undermodul rekursivt"
@@ -6645,8 +6718,8 @@ msgstr "hantera binärfiler med textconv-filter"
 msgid "search in subdirectories (default)"
 msgstr "sök i underkataloger (standard)"
 
-msgid "descend at most <depth> levels"
-msgstr "gå som mest ned <djup> nivåer"
+msgid "descend at most <n> levels"
+msgstr "gå som mest ned <n> nivåer"
 
 msgid "use extended POSIX regular expressions"
 msgstr "använd utökade POSIX-reguljära uttryck"
@@ -6783,11 +6856,14 @@ msgid "both --cached and trees are given"
 msgstr "både --cached och träd angavs"
 
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <typ>] [-w] [--path=<fil> | --no-filters] [--stdin] [--] "
-"<fil>..."
+"git hash-object [-t <typ>] [-w] [--path=<fil> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <fil>..."
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <typ>] [-w] --stdin-paths [--no-filters]"
 
 msgid "object type"
 msgstr "objekttyp"
@@ -6850,7 +6926,7 @@ msgstr "git help [[-i|--info] [-m|--man] [-w|--web]] [<kommando>|<doc>]"
 
 #, c-format
 msgid "unrecognized help format '%s'"
-msgstr "okänt hjälpformat: \"%s\""
+msgstr "okänt hjälpformat: ”%s”"
 
 msgid "Failed to start emacsclient."
 msgstr "Misslyckades starta emacsclient."
@@ -6860,31 +6936,31 @@ msgstr "Kunde inte tolka emacsclient-version."
 
 #, c-format
 msgid "emacsclient version '%d' too old (< 22)."
-msgstr "emacsclient version \"%d\" för gammal (< 22)."
+msgstr "emacsclient version ”%d” för gammal (< 22)."
 
 #, c-format
 msgid "failed to exec '%s'"
-msgstr "exec misslyckades för \"%s\""
+msgstr "exec misslyckades för ”%s”"
 
 #, c-format
 msgid ""
 "'%s': path for unsupported man viewer.\n"
 "Please consider using 'man.<tool>.cmd' instead."
 msgstr ""
-"\"%s\": sökväg för man-visare som ej stöds.\n"
-"Använd \"man.<verktyg>.cmd\" istället."
+"”%s”: sökväg för man-visare som ej stöds.\n"
+"Använd ”man.<verktyg>.cmd” istället."
 
 #, c-format
 msgid ""
 "'%s': cmd for supported man viewer.\n"
 "Please consider using 'man.<tool>.path' instead."
 msgstr ""
-"\"%s\": kommando för man-visare som stöds.\n"
-"Använd \"man.<verktyg>.path\" istället."
+"”%s”: kommando för man-visare som stöds.\n"
+"Använd ”man.<verktyg>.path” istället."
 
 #, c-format
 msgid "'%s': unknown man viewer."
-msgstr "\"%s\": okänd man-visare."
+msgstr "”%s”: okänd man-visare."
 
 msgid "no man viewer handled the request"
 msgstr "ingen man-visare hanterade förfrågan"
@@ -6894,7 +6970,7 @@ msgstr "ingen info-visare hanterade förfrågan"
 
 #, c-format
 msgid "'%s' is aliased to '%s'"
-msgstr "\"%s\" är ett alias för \"%s\""
+msgstr "”%s” är ett alias för ”%s”"
 
 #, c-format
 msgid "bad alias.%s string: %s"
@@ -6902,27 +6978,33 @@ msgstr "felaktig alias.%s-sträng: %s"
 
 #, c-format
 msgid "the '%s' option doesn't take any non-option arguments"
-msgstr "flaggan \"%s\" tar inte några argument som inte är flaggor"
+msgstr "flaggan ”%s” tar inte några argument som inte är flaggor"
 
 msgid ""
 "the '--no-[external-commands|aliases]' options can only be used with '--all'"
 msgstr ""
-"flaggorna '--no-[external-commands|aliases]' kan endast användas med \"--all"
-"\""
+"flaggorna '--no-[external-commands|aliases]' kan endast användas med ”--all”"
 
 #, c-format
 msgid "usage: %s%s"
 msgstr "användning: %s%s"
 
 msgid "'git help config' for more information"
-msgstr "\"git help config\" för mer information"
+msgstr "”git help config” för mer information"
 
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] <krok-namn> [-- <krok-argument>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<sökväg>] <krok-namn> [-- <krok-"
+"argument>]"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "ignorera tyst om önskat <krok-namn> saknas"
 
+msgid "file to read into hooks' stdin"
+msgstr "misslyckades läsa till krokens standard in"
+
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "objekttyp stämmer inte överens vid %s"
@@ -6983,7 +7065,7 @@ msgid "unknown object type %d"
 msgstr "okänd objekttyp %d"
 
 msgid "cannot pread pack file"
-msgstr "kan inte utföra \"pread\" på paketfil"
+msgstr "kan inte utföra ”pread” på paketfil"
 
 #, c-format
 msgid "premature end of pack file, %<PRIuMAX> byte missing"
@@ -7034,7 +7116,7 @@ msgid "pack is corrupted (SHA1 mismatch)"
 msgstr "paketet är trasigt (SHA1 stämmer inte)"
 
 msgid "cannot fstat packfile"
-msgstr "kan inte utföra \"fstat\" på paketfil"
+msgstr "kan inte utföra ”fstat” på paketfil"
 
 msgid "pack has junk at the end"
 msgstr "paket har skräp i slutet"
@@ -7070,7 +7152,7 @@ msgstr[1] "paketet har %d oanalyserade delta"
 
 #, c-format
 msgid "unable to deflate appended object (%d)"
-msgstr "kunde inte utföra \"deflate\" på tillagt objekt (%d)"
+msgstr "kunde inte utföra ”deflate” på tillagt objekt (%d)"
 
 #, c-format
 msgid "local object %s is corrupt"
@@ -7078,19 +7160,19 @@ msgstr "lokalt objekt %s är trasigt"
 
 #, c-format
 msgid "packfile name '%s' does not end with '.%s'"
-msgstr "paketfilnamnet \"%s\" slutar inte med \".%s\""
+msgstr "paketfilnamnet ”%s” slutar inte med ”.%s”"
 
 #, c-format
 msgid "cannot write %s file '%s'"
-msgstr "kan inte ta skriva %s-fil \"%s\""
+msgstr "kan inte ta skriva %s-fil ”%s”"
 
 #, c-format
 msgid "cannot close written %s file '%s'"
-msgstr "kan inte stänga skriven %s-fil \"%s\""
+msgstr "kan inte stänga skriven %s-fil ”%s”"
 
 #, c-format
 msgid "unable to rename temporary '*.%s' file to '%s'"
-msgstr "kunde inte byta namn på temporär \"*.%s\"-fil till \"%s\""
+msgstr "kunde inte byta namn på temporär ”*.%s”-fil till ”%s”"
 
 msgid "error while closing pack file"
 msgstr "fel vid stängning av paketfil"
@@ -7101,11 +7183,11 @@ msgstr "felaktig pack.indexVersion=%<PRIu32>"
 
 #, c-format
 msgid "Cannot open existing pack file '%s'"
-msgstr "Kan inte öppna befintlig paketfil \"%s\""
+msgstr "Kan inte öppna befintlig paketfil ”%s”"
 
 #, c-format
 msgid "Cannot open existing pack idx file for '%s'"
-msgstr "Kan inte öppna befintlig paket-idx-fil för \"%s\""
+msgstr "Kan inte öppna befintlig paket-idx-fil för ”%s”"
 
 #, c-format
 msgid "non delta: %d object"
@@ -7124,98 +7206,31 @@ msgstr "Kan inte gå tillbaka till arbetskatalogen (cwd)"
 
 #, c-format
 msgid "bad %s"
-msgstr "felaktig %s"
-
-#, c-format
-msgid "unknown hash algorithm '%s'"
-msgstr "okänd hashningsalgoritm \"%s\""
-
-msgid "--stdin requires a git repository"
-msgstr "--stdin kräver ett git-arkiv"
-
-msgid "--verify with no packfile name given"
-msgstr "--verify angavs utan paketfilnamn"
-
-msgid "fsck error in pack objects"
-msgstr "fsck-fel i packat objekt"
-
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "kan inte ta status på mallen \"%s\""
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "kan inte öppna katalogen (opendir) \"%s\""
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "kan inte läsa länk (readlink) \"%s\""
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "kan inte skapa symbolisk länk \"%s\" \"%s\""
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "kan inte kopiera \"%s\" till \"%s\""
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "ignorerar mallen %s"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "mallarna hittades inte i %s"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "kopierade inte mallar från \"%s\": %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "ogiltigt namn på första gren: \"%s\""
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "kan inte hantera filtyp %d"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "kan inte flytta %s till %s"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "försöker initiera arkivet på nytt med annan hash"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s finns redan"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: ignorerade --initial-branch=%s"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "Ominitierade befintligt delat Git-arkiv i %s%s\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Ominitierade befintligt Git-arkiv i %s%s\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Initierade tomt delat Git-arkiv i %s%s\n"
+msgstr "felaktig %s"
 
 #, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Initierade tomt Git-arkiv i %s%s\n"
+msgid "unknown hash algorithm '%s'"
+msgstr "okänd hashningsalgoritm ”%s”"
+
+msgid "--stdin requires a git repository"
+msgstr "--stdin kräver ett git-arkiv"
+
+msgid "--verify with no packfile name given"
+msgstr "--verify angavs utan paketfilnamn"
+
+msgid "fsck error in pack objects"
+msgstr "fsck-fel i packat objekt"
 
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<mallkatalog>] [--"
-"shared[=<behörigheter>]] [<katalog>]"
+"git init [-q | --quiet] [--bare] [--template=<mallkatalog>]\n"
+"         [--separate-git-dir <git-kat>] [--object-format=<format>]\n"
+"         [-b <grennamn> | --initial-branch=<grennamn>]\n"
+"         [--shared[=<behörigheter>]] [<katalog>]"
 
 msgid "permissions"
 msgstr "behörigheter"
@@ -7250,17 +7265,20 @@ msgstr ""
 
 #, c-format
 msgid "Cannot access work tree '%s'"
-msgstr "Kan inte komma åt arbetskatalogen \"%s\""
+msgstr "Kan inte komma åt arbetskatalogen ”%s”"
 
 msgid "--separate-git-dir incompatible with bare repository"
 msgstr "--separate-git-dir är inkompatibelt med naket arkiv"
 
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<symbol>[(=|:)<värde>])...] [<fil>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<nyckel>|<nyckelAlias>)"
+"[(=|:)<värde>])...]\n"
+"                       [--parse] [<fil>...]"
 
 msgid "edit files in place"
 msgstr "redigera filer på plats"
@@ -7268,6 +7286,9 @@ msgstr "redigera filer på plats"
 msgid "trim empty trailers"
 msgstr "ta bort tomma släprader"
 
+msgid "placement"
+msgstr "placering"
+
 msgid "where to place the new trailer"
 msgstr "var nya släprader ska placeras"
 
@@ -7280,17 +7301,17 @@ msgstr "att göra om släprader saknas"
 msgid "output only the trailers"
 msgstr "visa endast släprader"
 
-msgid "do not apply config rules"
-msgstr "använd inte regler från konfigurationen"
+msgid "do not apply trailer.* configuration variables"
+msgstr "tillämpa inte konfigurationsvariablerna trailer.*"
 
-msgid "join whitespace-continued values"
-msgstr "slå ihop värden avdelade med blanksteg"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "omformatera flerradiga släpradsvärden som enradsvärden"
 
-msgid "set parsing options"
-msgstr "välj tolkningsalternativ"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "alias för --only-trailers --only-input --unfold"
 
-msgid "do not treat --- specially"
-msgstr "tolka inte --- speciellt"
+msgid "do not treat \"---\" as the end of input"
+msgstr "tolka inte ”---” som slut på indata"
 
 msgid "trailer(s) to add"
 msgstr "släprad(er) att lägga till"
@@ -7379,6 +7400,10 @@ msgstr "behöver precis ett intervall"
 msgid "not a range"
 msgstr "inte ett intervall"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "kan inte läsa grenbeskrivningsfilen ”%s”"
+
 msgid "cover letter needs email format"
 msgstr "omslagsbrevet behöver e-postformat"
 
@@ -7401,7 +7426,7 @@ msgstr "okänd incheckning %s"
 
 #, c-format
 msgid "failed to resolve '%s' as a valid ref"
-msgstr "misslyckades slå upp \"%s\" som en giltig referens"
+msgstr "misslyckades slå upp ”%s” som en giltig referens"
 
 msgid "could not find exact merge base"
 msgstr "kunde inte hitta exakt sammanslagningsbas"
@@ -7432,7 +7457,7 @@ msgstr "misslyckades räkna ut intervalldiff-ursprung för aktuell serie"
 
 #, c-format
 msgid "using '%s' as range-diff origin of current series"
-msgstr "använd \"%s\" som intervalldiff-ursprung för aktuell serie"
+msgstr "använd ”%s” som intervalldiff-ursprung för aktuell serie"
 
 msgid "use [PATCH n/m] even with a single patch"
 msgstr "använd [PATCH n/m] även för en ensam patch"
@@ -7453,7 +7478,7 @@ msgid "sfx"
 msgstr "sfx"
 
 msgid "use <sfx> instead of '.patch'"
-msgstr "använd <sfx> istället för \".patch\""
+msgstr "använd <sfx> istället för ”.patch”"
 
 msgid "start numbering patches at <n> instead of 1"
 msgstr "börja numrera patchar på <n> istället för 1"
@@ -7476,6 +7501,9 @@ msgstr "cover-from-description-läge"
 msgid "generate parts of a cover letter based on a branch's description"
 msgstr "skapa delar av omslagsbrevet baserat på grenbeskrivelsen"
 
+msgid "use branch description from file"
+msgstr "använd grenbeskrivningar från fil"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "använd [<prefix>] istället för [PATCH]"
 
@@ -7510,10 +7538,10 @@ msgid "email"
 msgstr "epost"
 
 msgid "add To: header"
-msgstr "lägg till mottagarhuvud (\"To:\")"
+msgstr "lägg till mottagarhuvud (”To:”)"
 
 msgid "add Cc: header"
-msgstr "lägg till kopiehuvud (\"Cc:\")"
+msgstr "lägg till kopiehuvud (”Cc:”)"
 
 msgid "ident"
 msgstr "ident"
@@ -7590,7 +7618,7 @@ msgstr "kan inte använda --remerge-diff"
 
 #, c-format
 msgid "could not create directory '%s'"
-msgstr "kunde inte skapa katalogen \"%s\""
+msgstr "kunde inte skapa katalogen ”%s”"
 
 msgid "--interdiff requires --cover-letter or single patch"
 msgstr "--interdiff kräver --cover-letter eller ensam patch"
@@ -7614,7 +7642,7 @@ msgstr "Intervall-diff mot v%d:"
 
 #, c-format
 msgid "unable to read signature file '%s'"
-msgstr "kunde inte läsa signaturfil \"%s\""
+msgstr "kunde inte läsa signaturfil ”%s”"
 
 msgid "Generating patches"
 msgstr "Skapar patchar"
@@ -7630,13 +7658,17 @@ msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "Kunde inte hitta en spårad fjärrgren, ange <uppström> manuellt.\n"
 
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "kunde inte hämta objektinfo om ”%s”"
+
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
-msgstr "felaktigt ls-files-format: elementet \"%s\" börjar inte med \"(\""
+msgstr "felaktigt ls-files-format: elementet ”%s” börjar inte med ”(”"
 
 #, c-format
 msgid "bad ls-files format: element '%s' does not end in ')'"
-msgstr "felaktigt ls-files-format: elementet \"%s\" slutar inte med \")\""
+msgstr "felaktigt ls-files-format: elementet ”%s” slutar inte med ”)”"
 
 #, c-format
 msgid "bad ls-files format: %%%.*s"
@@ -7652,10 +7684,10 @@ msgid "identify the file status with tags"
 msgstr "identifiera filstatus med taggar"
 
 msgid "use lowercase letters for 'assume unchanged' files"
-msgstr "använd små bokstäver för \"anta oförändrade\"-filer"
+msgstr "använd små bokstäver för ”anta oförändrade”-filer"
 
 msgid "use lowercase letters for 'fsmonitor clean' files"
-msgstr "använd små bokstäver för \"fsmonitor clean\"-filer"
+msgstr "använd små bokstäver för ”fsmonitor clean”-filer"
 
 msgid "show cached files in the output (default)"
 msgstr "visa cachade filer i utdata (standard)"
@@ -7679,7 +7711,7 @@ msgid "show files on the filesystem that need to be removed"
 msgstr "visa filer i filsystemet som behöver tas bort"
 
 msgid "show 'other' directories' names only"
-msgstr "visa endast namn för \"andra\" kataloger"
+msgstr "visa endast namn för ”andra” kataloger"
 
 msgid "show line endings of files"
 msgstr "visa radslut i filer"
@@ -7691,7 +7723,7 @@ msgid "show unmerged files in the output"
 msgstr "visa ej sammanslagna filer i utdata"
 
 msgid "show resolve-undo information"
-msgstr "visa \"resolve-undo\"-information"
+msgstr "visa ”resolve-undo”-information"
 
 msgid "skip files matching pattern"
 msgstr "hoppa över filer som motsvarar mönster"
@@ -7735,12 +7767,12 @@ msgstr ""
 
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<arkiv> [<referenser>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<nyckel>]\n"
+"              [--symref] [<arkiv> [<mönster>...]]"
 
 msgid "do not print remote URL"
 msgstr "visa inte fjärr-URL"
@@ -7772,17 +7804,13 @@ msgstr "visa underliggande referens och objektet det pekar på"
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
 msgstr "git ls-tree [<flaggor>] <träd-igt> [<sökväg>...]"
 
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "kunde inte hämta objektinfo om \"%s\""
-
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
-msgstr "felaktigt ls-tree-format: elementet \"%s\" börjar inte med \"(\""
+msgstr "felaktigt ls-tree-format: elementet ”%s” börjar inte med ”(”"
 
 #, c-format
 msgid "bad ls-tree format: element '%s' does not end in ')'"
-msgstr "felaktigt ls-tree-format: elementet \"%s\" slutar inte med \")\""
+msgstr "felaktigt ls-tree-format: elementet ”%s” slutar inte med ”)”"
 
 #, c-format
 msgid "bad ls-tree format: %%%.*s"
@@ -7826,7 +7854,7 @@ msgid "keep subject"
 msgstr "behåll ärenderad"
 
 msgid "keep non patch brackets in subject"
-msgstr "behåll hakparanterser som inte är \"patch\" i ärenderaden"
+msgstr "behåll hakparanterser som inte är ”patch” i ärenderaden"
 
 msgid "copy Message-ID to the end of commit message"
 msgstr "kopiera Message-ID till slutet av incheckningsmeddelandet"
@@ -7860,7 +7888,7 @@ msgstr "läser patchar från standard in/tty..."
 
 #, c-format
 msgid "empty mbox: '%s'"
-msgstr "tom mbox: \"%s\""
+msgstr "tom mbox: ”%s”"
 
 msgid "git merge-base [-a | --all] <commit> <commit>..."
 msgstr "git merge-base [-a | --all] <incheckning> <incheckning>..."
@@ -7868,12 +7896,12 @@ msgstr "git merge-base [-a | --all] <incheckning> <incheckning>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <incheckning>..."
 
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <incheckning>..."
-
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <incheckning> <incheckning>"
 
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <incheckning>..."
+
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <ref> [<incheckning>]"
 
@@ -7902,6 +7930,9 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "sänd resultat till standard ut"
 
+msgid "use object IDs instead of filenames"
+msgstr "använd objekt-ID istället för filnamn"
+
 msgid "use a diff3 based merge"
 msgstr "använd diff3-baserad sammanslagning"
 
@@ -7926,13 +7957,20 @@ msgstr "varna inte om konflikter"
 msgid "set labels for file1/orig-file/file2"
 msgstr "sätt etiketter för fil1/origfil/fil2"
 
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "objektet ”%s” finns inte"
+
+msgid "Could not write object file"
+msgstr "Kunde inte skriva objektfilen"
+
 #, c-format
 msgid "unknown option %s"
 msgstr "okänd flagga %s"
 
 #, c-format
 msgid "could not parse object '%s'"
-msgstr "kunde inte tolka objektet \"%s\""
+msgstr "kunde inte tolka objektet ”%s”"
 
 #, c-format
 msgid "cannot handle more than %d base. Ignoring %s."
@@ -7945,7 +7983,7 @@ msgstr "hanterar inte något annat än en sammanslagning av två huvuden."
 
 #, c-format
 msgid "could not resolve ref '%s'"
-msgstr "kunde inte bestämma referensen \"%s\""
+msgstr "kunde inte bestämma referensen ”%s”"
 
 #, c-format
 msgid "Merging %s with %s\n"
@@ -7981,22 +8019,49 @@ msgstr "lista filnamn utan lägen/oid/köer"
 msgid "allow merging unrelated histories"
 msgstr "tillåt sammanslagning av orelaterade historier"
 
+msgid "perform multiple merges, one per line of input"
+msgstr "utför flera sammanslagningar, en per indatarad"
+
+msgid "specify a merge-base for the merge"
+msgstr "ange en sammanslagningsbas för sammanslagningen"
+
+msgid "option=value"
+msgstr "alternativ=värde"
+
+msgid "option for selected merge strategy"
+msgstr "alternativ för vald sammanslagningsstrategi"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge är inkompatibelt med andra flaggor"
 
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "okänd strategiflagga: -X%s"
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base är inkompatibel med --stdin"
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "felaktig indatarad: ”%s”."
+
+#, 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>...]"
 
 msgid "switch `m' requires a value"
-msgstr "flaggan \"m\" behöver ett värde"
+msgstr "flaggan ”m” behöver ett värde"
 
 #, c-format
 msgid "option `%s' requires a value"
-msgstr "flaggan \"%s\" behöver ett värde"
+msgstr "flaggan ”%s” behöver ett värde"
 
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
-msgstr "Kunde inte hitta sammanslagningsstrategin \"%s\".\n"
+msgstr "Kunde inte hitta sammanslagningsstrategin ”%s”.\n"
 
 #, c-format
 msgid "Available strategies are:"
@@ -8043,12 +8108,6 @@ msgstr "strategi"
 msgid "merge strategy to use"
 msgstr "sammanslagningsstrategi att använda"
 
-msgid "option=value"
-msgstr "alternativ=värde"
-
-msgid "option for selected merge strategy"
-msgstr "alternativ för vald sammanslagningsstrategi"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "incheckningsmeddelande för (icke snabbspolande) sammanslagning"
 
@@ -8096,7 +8155,7 @@ msgstr "Inget sammanslagningsmeddelande -- uppdaterar inte HEAD\n"
 
 #, c-format
 msgid "'%s' does not point to a commit"
-msgstr "\"%s\" verkar inte peka på en incheckning"
+msgstr "”%s” verkar inte peka på en incheckning"
 
 #, c-format
 msgid "Bad branch.%s.mergeoptions string: %s"
@@ -8108,23 +8167,18 @@ msgstr "Kunde inte skriva indexet."
 msgid "Not handling anything other than two heads merge."
 msgstr "Hanterar inte något annat än en sammanslagning av två huvuden."
 
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "okänd strategiflagga: -X%s"
-
 #, c-format
 msgid "unable to write %s"
 msgstr "kunde inte skriva %s"
 
 #, c-format
 msgid "Could not read from '%s'"
-msgstr "Kunde inte läsa från \"%s\""
+msgstr "Kunde inte läsa från ”%s”"
 
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr ""
-"Checkar inte in sammanslagningen; använd \"git commit\" för att slutföra "
-"den.\n"
+"Checkar inte in sammanslagningen; använd ”git commit” för att slutföra den.\n"
 
 msgid ""
 "Please enter a commit message to explain why this merge is necessary,\n"
@@ -8144,7 +8198,7 @@ msgid ""
 "Lines starting with '%c' will be ignored, and an empty message aborts\n"
 "the commit.\n"
 msgstr ""
-"Rader som inleds med \"%c\" kommer ignoreras, och ett tomt meddelande\n"
+"Rader som inleds med ”%c” kommer ignoreras, och ett tomt meddelande\n"
 "avbryter incheckningen.\n"
 
 msgid "Empty commit message."
@@ -8174,11 +8228,11 @@ msgstr "Ingen fjärrspårande gren för %s från %s"
 
 #, c-format
 msgid "Bad value '%s' in environment '%s'"
-msgstr "Felaktigt värde \"%s\" i miljövariabeln \"%s\""
+msgstr "Felaktigt värde ”%s” i miljövariabeln ”%s”"
 
 #, c-format
 msgid "could not close '%s'"
-msgstr "kunde inte stänga \"%s\""
+msgstr "kunde inte stänga ”%s”"
 
 #, c-format
 msgid "not something we can merge in %s: %s"
@@ -8210,11 +8264,11 @@ msgid ""
 "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 "Please, commit your changes before you merge."
 msgstr ""
-"Du har inte avslutat din \"cherry-pick\" (CHERRY_PICK_HEAD finns).\n"
+"Du har inte avslutat din ”cherry-pick” (CHERRY_PICK_HEAD finns).\n"
 "Checka in dina ändringar innan du slår ihop."
 
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
-msgstr "Du har inte avslutat din \"cherry-pick\" (CHERRY_PICK_HEAD finns)."
+msgstr "Du har inte avslutat din ”cherry-pick” (CHERRY_PICK_HEAD finns)."
 
 msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr "Ingen incheckning angiven och merge.defaultToUpstream är ej satt."
@@ -8280,7 +8334,7 @@ msgstr ""
 
 #, c-format
 msgid "When finished, apply stashed changes with `git stash pop`\n"
-msgstr "När färdig, applicerade sparade ändringar med \"git stash pop\"\n"
+msgstr "När färdig, applicerade sparade ändringar med ”git stash pop”\n"
 
 #, c-format
 msgid "warning: tag input does not pass fsck: %s"
@@ -8296,11 +8350,11 @@ msgstr "%d (FSCK_IGNORE?) skulle aldrig utlösa detta återanrop"
 
 #, c-format
 msgid "could not read tagged object '%s'"
-msgstr "kunde inte läsa det taggade objektet \"%s\""
+msgstr "kunde inte läsa det taggade objektet ”%s”"
 
 #, c-format
 msgid "object '%s' tagged as '%s', but is a '%s' type"
-msgstr "objektet \"%s\" taggat som \"%s\", men är av typen \"%s\""
+msgstr "objektet ”%s” taggat som ”%s”, men är av typen ”%s”"
 
 msgid "could not read from stdin"
 msgstr "kunde inte läsa från standard in"
@@ -8376,7 +8430,7 @@ msgstr "Katalogen %s är i indexet och inte en undermodul?"
 
 msgid "Please stage your changes to .gitmodules or stash them to proceed"
 msgstr ""
-"Köa dina ändringar i .gitmodules eller använd \"stash\" för att fortsätta"
+"Köa dina ändringar i .gitmodules eller använd ”stash” för att fortsätta"
 
 #, c-format
 msgid "%.*s is in index"
@@ -8390,11 +8444,11 @@ msgstr "hoppa över fel vid flytt/namnändring"
 
 #, c-format
 msgid "destination '%s' is not a directory"
-msgstr "destinationen \"%s\" är ingen katalog"
+msgstr "destinationen ”%s” är ingen katalog"
 
 #, c-format
 msgid "Checking rename of '%s' to '%s'\n"
-msgstr "Kontrollerar namnbyte av \"%s\" till \"%s\"\n"
+msgstr "Kontrollerar namnbyte av ”%s” till ”%s”\n"
 
 msgid "bad source"
 msgstr "felaktig källa"
@@ -8405,8 +8459,8 @@ msgstr "destinationen finns"
 msgid "can not move directory into itself"
 msgstr "kan inte flytta katalog till sig själv"
 
-msgid "cannot move directory over file"
-msgstr "kan inte flytta katalog över fil"
+msgid "destination already exists"
+msgstr "destinationen finns redan"
 
 msgid "source directory is empty"
 msgstr "källkatalogen är tom"
@@ -8419,7 +8473,7 @@ msgstr "i konflikt"
 
 #, c-format
 msgid "overwriting '%s'"
-msgstr "skriver över \"%s\""
+msgstr "skriver över ”%s”"
 
 msgid "Cannot overwrite"
 msgstr "Kan inte skriva över"
@@ -8443,7 +8497,7 @@ msgstr "Byter namn på %s till %s\n"
 
 #, c-format
 msgid "renaming '%s' failed"
-msgstr "misslyckades byta namn på \"%s\""
+msgstr "misslyckades byta namn på ”%s”"
 
 msgid "git name-rev [<options>] <commit>..."
 msgstr "git name-rev [<flaggor>] <incheckning>..."
@@ -8476,7 +8530,7 @@ msgid "annotate text from stdin"
 msgstr "annotera text från standard in"
 
 msgid "allow to print `undefined` names (default)"
-msgstr "tillåt att skriva \"odefinierade\" namn (standard)"
+msgstr "tillåt att skriva ”odefinierade” namn (standard)"
 
 msgid "dereference tags in the input (internal use)"
 msgstr "avreferera taggar i indata (används internt)"
@@ -8485,22 +8539,26 @@ msgid "git notes [--ref <notes-ref>] [list [<object>]]"
 msgstr "git notes [--ref <anteckningsref>] [list [<objekt>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <anteckningsref>] add [-f] [--allow-empty] [-m <medd> | -F "
-"<fil> | (-c | -C) <objekt>] [<objekt>]"
+"git notes [--ref <anteckningsref>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<styckebrytning>] [--[no-]stripspace] [-m <medd> "
+"| -F <fil> | (-c | -C) <objekt>] [<objekt>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <anteckningsref>] copy [-f] <från-objekt> <till-objekt>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <anteckningsref>] append [--allow-empty] [-m <medd> | -F "
-"<fil> | (-c | -C) <objekt>] [<objekt>]"
+"git notes [--ref <anteckningsref>] append [--allow-empty] [--"
+"[no-]separator|--separator=<styckebrytning>] [--[no-]stripspace] [-m <medd> "
+"| -F <fil> | (-c | -C) <objekt>] [<objekt>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref <anteckningsref>] edit [--allow-empty] [<objekt>]"
@@ -8564,14 +8622,14 @@ msgstr "Skriv/redigera anteckningar för följande objekt:"
 
 #, c-format
 msgid "unable to start 'show' for object '%s'"
-msgstr "kunde inte starta \"show\" för objektet \"%s\""
+msgstr "kunde inte starta ”show” för objektet ”%s”"
 
 msgid "could not read 'show' output"
-msgstr "kunde inte läsa utdata från \"show\""
+msgstr "kunde inte läsa utdata från ”show”"
 
 #, c-format
 msgid "failed to finish 'show' for object '%s'"
-msgstr "kunde inte avsluta \"show\" för objektet \"%s\""
+msgstr "kunde inte avsluta ”show” för objektet ”%s”"
 
 msgid "please supply the note contents using either -m or -F option"
 msgstr "ange innehåll för anteckningen med antingen -m eller -F"
@@ -8585,34 +8643,30 @@ msgstr "anteckningens innehåll har lämnats kvar i %s"
 
 #, c-format
 msgid "could not open or read '%s'"
-msgstr "kunde inte öppna eller läsa \"%s\""
+msgstr "kunde inte öppna eller läsa ”%s”"
 
 #, c-format
 msgid "failed to resolve '%s' as a valid ref."
-msgstr "kunde inte slå upp \"%s\" som en giltig referens."
+msgstr "kunde inte slå upp ”%s” som en giltig referens."
 
 #, c-format
 msgid "failed to read object '%s'."
-msgstr "kunde inte läsa objektet \"%s\"."
+msgstr "kunde inte läsa objektet ”%s”."
 
 #, c-format
 msgid "cannot read note data from non-blob object '%s'."
-msgstr "kan inte läsa anteckningsdata från icke-blob-objektet \"%s\"."
-
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "felaktig indatarad: \"%s\"."
+msgstr "kan inte läsa anteckningsdata från icke-blob-objektet ”%s”."
 
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
-msgstr "misslyckades kopiera anteckningar från \"%s\" till \"%s\""
+msgstr "misslyckades kopiera anteckningar från ”%s” till ”%s”"
 
 #. TRANSLATORS: the first %s will be replaced by a git
 #. notes command: 'add', 'merge', 'remove', etc.
 #.
 #, c-format
 msgid "refusing to %s notes in %s (outside of refs/notes/)"
-msgstr "vägrar utföra \"%s\" på anteckningar i %s (utanför refs/notes/)"
+msgstr "vägrar utföra ”%s” på anteckningar i %s (utanför refs/notes/)"
 
 #, c-format
 msgid "no note found for object %s."
@@ -8636,13 +8690,22 @@ msgstr "tillåt lagra tom anteckning"
 msgid "replace existing notes"
 msgstr "ersätt befintliga anteckningar"
 
+msgid "<paragraph-break>"
+msgstr "<styckebrytning>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "sätt in <styckebrytning> mellan stycken"
+
+msgid "remove unnecessary whitespace"
+msgstr "ta bort onödiga blanksteg"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
 "existing notes"
 msgstr ""
 "Kan inte lägga till anteckningar. Hittade befintliga anteckningar för "
-"objektet %s. Använd \"-f\" för att skriva över befintliga anteckningar"
+"objektet %s. Använd ”-f” för att skriva över befintliga anteckningar"
 
 #, c-format
 msgid "Overwriting existing notes for object %s\n"
@@ -8667,7 +8730,7 @@ msgid ""
 "existing notes"
 msgstr ""
 "Kan inte kopiera anteckningar. Hittade befintliga anteckningar för objektet "
-"%s. Använd \"-f\" för att skriva över befintliga anteckningar"
+"%s. Använd ”-f” för att skriva över befintliga anteckningar"
 
 #, c-format
 msgid "missing notes on source object %s. Cannot copy."
@@ -8678,8 +8741,8 @@ msgid ""
 "The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
 "Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
 msgstr ""
-"Flaggorna -m/-F/-c/-C rekommenderas inte för underkommandot \"edit\".\n"
-"Använd \"git notes add -f -m/-F/-c/-C\" istället.\n"
+"Flaggorna -m/-F/-c/-C rekommenderas inte för underkommandot ”edit”.\n"
+"Använd ”git notes add -f -m/-F/-c/-C” istället.\n"
 
 msgid "failed to delete ref NOTES_MERGE_PARTIAL"
 msgstr "misslyckades ta bort referensen NOTES_MERGE_PARTIAL"
@@ -8688,7 +8751,7 @@ msgid "failed to delete ref NOTES_MERGE_REF"
 msgstr "misslyckades ta bort referensen NOTES_MERGE_REF"
 
 msgid "failed to remove 'git notes merge' worktree"
-msgstr "misslyckades ta bort arbetskatalogen för \"git notes merge\""
+msgstr "misslyckades ta bort arbetskatalogen för ”git notes merge”"
 
 msgid "failed to read ref NOTES_MERGE_PARTIAL"
 msgstr "misslyckades läsa references NOTES_MERGE_PARTIAL"
@@ -8761,12 +8824,12 @@ msgid ""
 "abort'.\n"
 msgstr ""
 "Automatisk sammanslagning av anteckningar misslyckades. Rätta konflikter i "
-"%s och checka in resultatet med \"git notes merge --commit\", eller avbryt "
-"sammanslagningen med \"git notes merge --abort\".\n"
+"%s och checka in resultatet med ”git notes merge --commit”, eller avbryt "
+"sammanslagningen med ”git notes merge --abort”.\n"
 
 #, c-format
 msgid "Failed to resolve '%s' as a valid ref."
-msgstr "Kunde inte slå upp \"%s\" som en giltig referens."
+msgstr "Kunde inte slå upp ”%s” som en giltig referens."
 
 #, c-format
 msgid "Object %s has no note\n"
@@ -8792,17 +8855,15 @@ msgstr "använd anteckningar från <anteckningsref>"
 
 #, c-format
 msgid "unknown subcommand: `%s'"
-msgstr "okänt underkommando: \"%s\""
+msgstr "okänt underkommando: ”%s”"
 
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
-msgstr ""
-"git pack-objects --stdout [<flaggor>...] [< <reflista> | < <objektlista>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
+msgstr "git pack-objects --stdout [<flaggor>] [< <reflista> | < <objektlista>]"
 
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects [<flaggor>...] <basnamn> [< <reflista> | < <objektlista>]"
+"git pack-objects [<flaggor>] <basnamn> [< <reflista> | < <objektlista>]"
 
 #, c-format
 msgid ""
@@ -8845,7 +8906,7 @@ msgstr "misslyckades ta status på %s"
 
 #, c-format
 msgid "failed utime() on %s"
-msgstr "\"utime()\" misslyckades på %s"
+msgstr "”utime()” misslyckades på %s"
 
 msgid "failed to write bitmap index"
 msgstr "misslyckade skriva bitkarteindex"
@@ -8925,7 +8986,7 @@ msgstr "kunde inte hämta typ för objektet %s i paketet %s"
 
 #, c-format
 msgid "could not find pack '%s'"
-msgstr "kunde inte hitta paketet \"%s\""
+msgstr "kunde inte hitta paketet ”%s”"
 
 #, c-format
 msgid "packfile %s cannot be accessed"
@@ -8971,11 +9032,11 @@ msgstr "kan inte tvinga lösa objekt"
 
 #, c-format
 msgid "not a rev '%s'"
-msgstr "inte en referens \"%s\""
+msgstr "inte en referens ”%s”"
 
 #, c-format
 msgid "bad revision '%s'"
-msgstr "felaktig revision \"%s\""
+msgstr "felaktig revision ”%s”"
 
 msgid "unable to add recent objects"
 msgstr "kan inte lägga till nya objekt"
@@ -8986,7 +9047,13 @@ msgstr "indexversionen %s stöds ej"
 
 #, c-format
 msgid "bad index version '%s'"
-msgstr "felaktig indexversion \"%s\""
+msgstr "felaktig indexversion ”%s”"
+
+msgid "show progress meter during object writing phase"
+msgstr "visa förloppsindikator under objektskrivningsfasen"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "som --all-progress när förloppsindikatorn visas"
 
 msgid "<version>[,<offset>]"
 msgstr "<version>[,<offset>]"
@@ -9085,7 +9152,7 @@ msgid "pack compression level"
 msgstr "komprimeringsgrad för paket"
 
 msgid "do not hide commits by grafts"
-msgstr "göm inte incheckningar med ympningar (\"grafts\")"
+msgstr "göm inte incheckningar med ympningar (”grafts”)"
 
 msgid "use a bitmap index if available to speed up counting objects"
 msgstr "använd bitkartindex om tillgängligt för att räkna objekt snabbare"
@@ -9134,9 +9201,6 @@ msgstr "minsta packstorlek är 1 MiB"
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin kan inte användas för att bygga ett indexerbart paket"
 
-msgid "cannot use --filter without --stdout"
-msgstr "kan inte använda --filter utan --stdout"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "kan inte använda --filter med --stdin-packs"
 
@@ -9149,9 +9213,6 @@ msgstr "kan inte använda intern revisionslista med --cruft"
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "kan inte använda --stdin-packs med --cruft"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "kan inte använda --max-pack-size med --cruft"
-
 msgid "Enumerating objects"
 msgstr "Räknar upp objekt"
 
@@ -9170,14 +9231,21 @@ msgid ""
 "and let us know you still use it by sending an e-mail\n"
 "to <git@vger.kernel.org>.  Thanks.\n"
 msgstr ""
-"\"git pack-redundant\" har nominerats för borttagning.\n"
+"”git pack-redundant” har nominerats för borttagning.\n"
 "Om du fortfarande använder kommandot, lägg till flaggan\n"
-"\"--i-still-use-this\" på kommandoraden och berätta för\n"
+"”--i-still-use-this” på kommandoraden och berätta för\n"
 "oss att du fortfarande använder det på e-post till\n"
 "<git@vger.kernel.org>. Tack.\n"
 
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<flaggor>]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "vägrar köra utan --i-still-use-this"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <mönster>] [--exclude "
+"<mönster>]"
 
 msgid "pack everything"
 msgstr "packa allt"
@@ -9185,6 +9253,24 @@ msgstr "packa allt"
 msgid "prune loose refs (default)"
 msgstr "ta bort lösa referenser (standard)"
 
+msgid "references to include"
+msgstr "referenser att ta med"
+
+msgid "references to exclude"
+msgstr "referenser att utesluta"
+
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+msgid "use the unstable patch-id algorithm"
+msgstr "använd den instabila patch-id-algoritmen"
+
+msgid "use the stable patch-id algorithm"
+msgstr "använd den stabila patch-id-algoritmen"
+
+msgid "don't strip whitespace from the patch"
+msgstr "ta inte bort blanksteg från patchen"
+
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr "git prune [-n] [-v] [--progress] [--expire <tid>] [--] [<huvud>...]"
 
@@ -9198,7 +9284,7 @@ msgid "limit traversal to objects outside promisor packfiles"
 msgstr "begränsa vandring av objekt utanför kontraktspackfiler."
 
 msgid "cannot prune in a precious-objects repo"
-msgstr "kan inte rensa i ett \"precious-objekt\"-arkiv"
+msgstr "kan inte rensa i ett ”precious-objekt”-arkiv"
 
 msgid "git pull [<options>] [<repository> [<refspec>...]]"
 msgstr "git pull [<flaggor>] [<arkiv> [<refspec>...]]"
@@ -9230,6 +9316,12 @@ msgstr "tvinga överskrivning av lokal gren"
 msgid "number of submodules pulled in parallel"
 msgstr "antal undermoduler som hämtas parallellt"
 
+msgid "use IPv4 addresses only"
+msgstr "använd endast IPv4-adresser"
+
+msgid "use IPv6 addresses only"
+msgstr "använd endast IPv6-adresser"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9256,7 +9348,7 @@ msgid ""
 "a branch. Because this is not the default configured remote\n"
 "for your current branch, you must specify a branch on the command line."
 msgstr ""
-"Du bad om att hämta från fjärren \"%s\", men angav inte någon\n"
+"Du bad om att hämta från fjärren ”%s”, men angav inte någon\n"
 "gren. Eftersom det inte är den fjärr som är konfigurerad som\n"
 "standard för aktuell gren måste du ange en gren på kommandoraden."
 
@@ -9290,7 +9382,7 @@ msgid ""
 "Your configuration specifies to merge with the ref '%s'\n"
 "from the remote, but no such ref was fetched."
 msgstr ""
-"Dina inställningar anger sammanslagning med referensen \"%s\"\n"
+"Dina inställningar anger sammanslagning med referensen ”%s”\n"
 "från fjärren, men någon sådan referens togs inte emot."
 
 #, c-format
@@ -9317,13 +9409,13 @@ msgid ""
 msgstr ""
 "Du har avvikande grenar och måste ange hur de skall förlikas.\n"
 "Du kan göra detta genom att köra ett av följande kommando innan du\n"
-"gör \"pull\" nästa gång: \n"
+"gör ”pull” nästa gång: \n"
 "\n"
 "  git config pull.rebase false  # sammanslagning\n"
 "  git config pull.rebase true   # ombasering\n"
 "  git config pull.ff only       # endast snabbspolning\n"
 "\n"
-"Du kan ersätta \"git config\" med \"git config --global\" för att välja en\n"
+"Du kan ersätta ”git config” med ”git config --global” för att välja en\n"
 "förvald inställning för alla arkiv. Du kan också ange --rebase, --no-rebase\n"
 "eller --ff-only på kommandoraden för att överstyra det konfigurerade\n"
 "förvalet vid körning.\n"
@@ -9334,8 +9426,8 @@ msgstr "Uppdaterar en ofödd gren med ändringar som lagts till i indexet."
 msgid "pull with rebase"
 msgstr "pull med ombasering"
 
-msgid "please commit or stash them."
-msgstr "checka in eller använd \"stash\" på dem."
+msgid "Please commit or stash them."
+msgstr "Checka in eller använd ”stash” på dem."
 
 #, c-format
 msgid ""
@@ -9392,21 +9484,20 @@ msgid ""
 "To choose either option permanently, see push.default in 'git help config'.\n"
 msgstr ""
 "\n"
-"För att välja ett av alternativen permanent, se push.default i \"git help "
-"config\".\n"
+"För att välja ett av alternativen permanent, se push.default i git help "
+"config.\n"
 
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
-"För att undvika att uppströmsgrenar automatiskt konfigureras när deras\n"
-"namn inte motsvarar en lokal gren, se värdet \"simple\" i branch."
+"För att undvika att en uppströmsgren automatiskt konfigureras när dess namn\n"
+"inte motsvarar den lokala grenen, se värdet ”simple” i branch."
 "autoSetupMerge\n"
-"i \"git help config\".\n"
+"i ”git help config”.\n"
 
 #, c-format
 msgid ""
@@ -9453,7 +9544,7 @@ msgid ""
 msgstr ""
 "\n"
 "För att detta ska ske automatiskt för grenar som saknar en spårande\n"
-"uppströmsgren, se \"push.autoSetupRemote\" i \"git help config\".\n"
+"uppströmsgren, se ”push.autoSetupRemote” i ”git help config”.\n"
 
 #, c-format
 msgid ""
@@ -9477,7 +9568,7 @@ msgid ""
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
 "Du angav inga referensspecifikationer att sända, och push.default är "
-"\"nothing\"."
+"”nothing”."
 
 #, c-format
 msgid ""
@@ -9485,44 +9576,44 @@ msgid ""
 "your current branch '%s', without telling me what to push\n"
 "to update which remote branch."
 msgstr ""
-"Du sänder till fjärren \"%s\", som inte är uppströms för den\n"
-"aktuella grenen \"%s\", utan att tala om för mig vad som\n"
+"Du sänder till fjärren ”%s”, som inte är uppströms för den\n"
+"aktuella grenen ”%s”, utan att tala om för mig vad som\n"
 "ska sändas för att uppdatera fjärrgrenen."
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då änden på din befintliga gren är bakom\n"
-"dess fjärrmotsvarighet. Integrera fjärrändringarna (t.ex\n"
-"\"git pull ....\") innan du sänder igen.\n"
-"Se avsnittet \"Note about fast-forward\" i \"git push --help\" för detaljer."
+"dess fjärrmotsvarighet. Om du vill integrera fjärrändringarna,\n"
+"använd ”git pull” innan du sänder igen.\t\n"
+"Se avsnittet ”Note about fast-forward” i ”git push --help” för detaljer."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då änden på en gren som sänds in är bakom dess\n"
-"fjärrmotsvarighet. Checka ut grenen och integrera fjärrändringarna (t.ex.\n"
-"\"git pull ...\") innan du sänder igen.\n"
-"Se avsnittet \"Note about fast-forward\" i \"git push --help\" för detaljer."
+"fjärrmotsvarighet. Om du vill integrera fjärrändringarna, använd ”git pull”\n"
+"innan du sänder igen.\n"
+"Se avsnittet ”Note about fast-forward” i ”git push --help” för detaljer."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då fjärren innehåller ändringar som du inte\n"
 "har lokalt. Det beror oftast på att ett annat arkiv har sänt in samma\n"
-"referenser. Det kan vara en idé att först integrera fjärrändringarna\n"
-"(t.ex. \"git pull ...\") innan du sänder igen.\n"
-"Se avsnittet \"Note about fast-forwards\" i \"git push --help\" för detaljer."
+"referenser. Om du vill integrera fjärrändringarna, använd ”git pull”\n"
+"innan du sänder igen.\n"
+"Se avsnittet ”Note about fast-forwards” i ”git push --help” för detaljer."
 
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr "Uppdateringarna avvisades eftersom taggen redan finns på fjärren."
@@ -9535,18 +9626,18 @@ msgstr ""
 "Du kan inte uppdatera en fjärr-referens som pekar på ett objekt som\n"
 "inte är en incheckning, eller uppdatera en fjärr-referens så att den\n"
 "pekar på något som inte är en incheckning, utan att använda flaggan\n"
-"\"--force\".\n"
+"”--force”.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Uppdateringar avvisades då änden på den fjärrspårande grenen\n"
-"har uppdaterats sedan senaste utcheckning. Integrera\n"
-"fjärrändringarna lokalt (t.ex \"git pull ....\") innan du\n"
-"tvingar en uppdatering.\n"
+"Uppdateringar avvisades då änden på din befintliga gren är bakom\n"
+"dess fjärrmotsvarighet. Om du vill integrera fjärrändringarna,\n"
+"använd ”git pull” innan du sänder igen.\n"
+"Se avsnittet ”Note about fast-forward” i ”git push --help” för detaljer."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -9554,17 +9645,24 @@ msgstr "Sänder till %s\n"
 
 #, c-format
 msgid "failed to push some refs to '%s'"
-msgstr "misslyckades sända vissa referenser till \"%s\""
+msgstr "misslyckades sända vissa referenser till ”%s”"
+
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"rekurserar in i undermoduler med push.recurseSubmodules=only; använder "
+"istället vid behov"
 
 #, c-format
 msgid "invalid value for '%s'"
-msgstr "ogiltigt värde för \"%s\""
+msgstr "ogiltigt värde för ”%s”"
 
 msgid "repository"
 msgstr "arkiv"
 
-msgid "push all refs"
-msgstr "sänd alla referenser"
+msgid "push all branches"
+msgstr "sänd alla grenar"
 
 msgid "mirror all refs"
 msgstr "spegla alla referenser"
@@ -9572,8 +9670,9 @@ msgstr "spegla alla referenser"
 msgid "delete refs"
 msgstr "ta bort referenser"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "sänd taggar (kan inte användas med --all eller --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"sänd taggar (kan inte användas med --all eller --branches eller --mirror)"
 
 msgid "force updates"
 msgstr "tvinga uppdateringar"
@@ -9619,7 +9718,7 @@ msgstr "--delete kan inte användas utan referenser"
 
 #, c-format
 msgid "bad repository '%s'"
-msgstr "felaktigt arkiv \"%s\""
+msgstr "felaktigt arkiv ”%s”"
 
 msgid ""
 "No configured push destination.\n"
@@ -9667,7 +9766,7 @@ msgid "notes"
 msgstr "anteckningar"
 
 msgid "passed to 'git log'"
-msgstr "sänds till \"git log\""
+msgstr "sänds till ”git log”"
 
 msgid "only emit output related to the first range"
 msgstr "visa endast utdata för det första intervallet"
@@ -9677,27 +9776,29 @@ msgstr "visa endast utdata för det andra intervallet"
 
 #, c-format
 msgid "not a revision: '%s'"
-msgstr "inte en revision: \"%s\""
+msgstr "inte en revision: ”%s”"
 
 #, c-format
 msgid "not a commit range: '%s'"
-msgstr "inte ett incheckningsintervall: \"%s\""
+msgstr "inte ett incheckningsintervall: ”%s”"
 
 #, c-format
 msgid "not a symmetric range: '%s'"
-msgstr "inte ett symmetriskt intervall: \"%s\""
+msgstr "inte ett symmetriskt intervall: ”%s”"
 
 msgid "need two commit ranges"
 msgstr "behöver två incheckningsintervall"
 
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<fil>] (--empty | <träd-"
-"igt1> [<träd-igt2> [<träd-igt3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<fil>] [--no-sparse-checkout]\n"
+"              (--empty | <träd-igt1> [<träd-igt2> [<träd-igt3>]])"
 
 msgid "write resulting index to <file>"
 msgstr "skriv resulterande index till <fil>"
@@ -9767,7 +9868,7 @@ msgstr ""
 
 #, c-format
 msgid "could not read '%s'."
-msgstr "kunde inte läsa \"%s\"."
+msgstr "kunde inte läsa ”%s”."
 
 #, c-format
 msgid "could not create temporary %s"
@@ -9784,23 +9885,23 @@ msgstr "en basincheckning måste anges med --upstream eller --onto"
 
 #, c-format
 msgid "%s requires the merge backend"
-msgstr "%s kräver \"merge\"-bakändan"
+msgstr "%s kräver ”merge”-bakändan"
 
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "kunde inte hämta \"onto\": \"%s\""
+msgid "invalid onto: '%s'"
+msgstr "ogiltig ”onto”: ”%s”"
 
 #, c-format
 msgid "invalid orig-head: '%s'"
-msgstr "ogiltigt orig-head: \"%s\""
+msgstr "ogiltig ”orig-head”: ”%s”"
 
 #, c-format
 msgid "ignoring invalid allow_rerere_autoupdate: '%s'"
-msgstr "ignorera ogiltigt allow_rerere_autoupdate: \"%s\""
+msgstr "ignorera ogiltigt allow_rerere_autoupdate: ”%s”"
 
 #, c-format
 msgid "could not remove '%s'"
-msgstr "kunde inte ta bort \"%s\""
+msgstr "kunde inte ta bort ”%s”"
 
 msgid ""
 "Resolve all conflicts manually, mark them as resolved with\n"
@@ -9810,10 +9911,10 @@ msgid ""
 "abort\"."
 msgstr ""
 "Lös alla konflikter manuellt, märk dem som lösta med\n"
-"\"git add/rm <filer_i_konflikt>\", kör sedan \"git rebase --continue\".\n"
-"Du kan hoppa över incheckningen istället: kör \"git rebase --skip\".\n"
-"För att avbryta och återgå till där du var före ombaseringen, kör \"git "
-"rebase --abort\"."
+"”git add/rm <filer_i_konflikt>”, kör sedan ”git rebase --continue”.\n"
+"Du kan hoppa över incheckningen istället: kör ”git rebase --skip”.\n"
+"För att avbryta och återgå till där du var före ombaseringen, kör git "
+"rebase --abort."
 
 #, c-format
 msgid ""
@@ -9833,16 +9934,32 @@ msgstr ""
 "\n"
 "Därför kan inte git ombasera dessa."
 
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Okänd rebase-merges-läge: %s"
+
 #, c-format
 msgid "could not switch to %s"
 msgstr "kunde inte växla till %s"
 
+msgid "apply options and merge options cannot be used together"
+msgstr ""
+"appliceringsflaggor och sammanslagningsflaggor kan inte användas tillsammans"
+
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
+msgstr "okänd tom-typ ”%s”; giltiga värden är ”drop”, ”keep” och ”ask”."
+
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
 msgstr ""
-"okänd tom-typ \"%s\"; giltiga värden är \"drop\", \"keep\" och \"ask\"."
+"--rebase-merges med en tom sträng som argument avråds från och kommer sluta "
+"fungera i en framtida version av Git. Använd istället --rebase-merges utan "
+"argument, vilket har samma effekt."
 
 #, c-format
 msgid ""
@@ -9897,7 +10014,7 @@ msgid "do not show diffstat of what changed upstream"
 msgstr "visa inte en diffstat för vad som ändrats uppströms"
 
 msgid "add a Signed-off-by trailer to each commit"
-msgstr "lägg \"Signed-off-by:\"-släprad till varje incheckning"
+msgstr "lägg ”Signed-off-by:”-släprad till varje incheckning"
 
 msgid "make committer date match author date"
 msgstr "sätt incheckningsdatum till författardatum"
@@ -9909,7 +10026,7 @@ msgid "synonym of --reset-author-date"
 msgstr "synonym för --reset-author-date"
 
 msgid "passed to 'git apply'"
-msgstr "sänds till \"git apply\""
+msgstr "sänds till ”git apply”"
 
 msgid "ignore changes in whitespace"
 msgstr "ignorera ändringar i blanksteg"
@@ -9971,7 +10088,7 @@ msgid "try to rebase merges instead of skipping them"
 msgstr "försök ombasera sammanslagningar istället för att ignorera dem"
 
 msgid "use 'merge-base --fork-point' to refine upstream"
-msgstr "använd \"merge-base --fork-point\" för att förfina uppström"
+msgstr "använd ”merge-base --fork-point” för att förfina uppström"
 
 msgid "use the given merge strategy"
 msgstr "använd angiven sammanslagningsstrategi"
@@ -9986,21 +10103,21 @@ msgid "rebase all reachable commits up to the root(s)"
 msgstr "ombasera alla nåbara incheckningar upp till roten/rötterna"
 
 msgid "automatically re-schedule any `exec` that fails"
-msgstr "kör automatiskt alla \"exec\" som misslyckas på nytt"
+msgstr "kör automatiskt alla ”exec” som misslyckas på nytt"
 
 msgid "apply all changes, even those already present upstream"
 msgstr "applicera alla ändringar, även de som redan finns uppströms"
 
 msgid "It looks like 'git am' is in progress. Cannot rebase."
-msgstr "Det verkar som en \"git am\" körs. Kan inte ombasera."
+msgstr "Det verkar som en ”git am” körs. Kan inte ombasera."
 
 msgid ""
 "`rebase --preserve-merges` (-p) is no longer supported.\n"
 "Use `git rebase --abort` to terminate current rebase.\n"
 "Or downgrade to v2.33, or earlier, to complete the rebase."
 msgstr ""
-"\"rebase --preserve-merges\" (-p) stöds ej längre.\n"
-"Använd \"git rebase --abort\" för att avsluta aktuell ombasering.\n"
+"”rebase --preserve-merges” (-p) stöds ej längre.\n"
+"Använd ”git rebase --abort” för att avsluta aktuell ombasering.\n"
 "Eller nedgradera till v2.33 eller tidigare för att slutföra ombaseringen."
 
 msgid ""
@@ -10009,8 +10126,8 @@ msgid ""
 "which is no longer supported; use 'merges' instead"
 msgstr ""
 "--preserve-merges ersattes av --rebase-merges\n"
-"Observera: Din inställning för \"pull.rebase\" kan också vara satt till\n"
-"\"preserve\", som inte längre stöds; använd \"merges\" istället"
+"Observera: Din inställning för ”pull.rebase” kan också vara satt till\n"
+"”preserve”, som inte längre stöds; använd ”merges” istället"
 
 msgid "No rebase in progress?"
 msgstr "Ingen ombasering pågår?"
@@ -10056,18 +10173,28 @@ msgstr ""
 "något av värde där.\n"
 
 msgid "switch `C' expects a numerical value"
-msgstr "flaggan \"C\" förväntar ett numeriskt värde"
+msgstr "flaggan ”C” förväntar ett numeriskt värde"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Okänt läge: %s"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+"argument för ”apply” är inkompatibla med rebase.autoSquash. Överväg att "
+"lägga till --no-autosquash"
 
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy kräver --merge eller --interactive"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+"argument för ”apply” är inkompatibla med rebase.rebaseMerges. Överväg att "
+"lägga till --no-rebase-merges"
 
-msgid "apply options and merge options cannot be used together"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
 msgstr ""
-"appliceringsflaggor och sammanslagningsflaggor kan inte användas tillsammans"
+"argument för ”apply” är inkompatibla med rebase.updateRefs. Överväg att "
+"lägga till --no-update-refs"
 
 #, c-format
 msgid "Unknown rebase backend: %s"
@@ -10078,36 +10205,33 @@ msgstr "--reschedule-failed-exec kräver --exec eller --interactive"
 
 #, c-format
 msgid "invalid upstream '%s'"
-msgstr "felaktig uppström \"%s\""
+msgstr "felaktig uppström ”%s”"
 
 msgid "Could not create new root commit"
 msgstr "kunde inte skapa ny rotincheckning"
 
 #, c-format
 msgid "no such branch/commit '%s'"
-msgstr "ingen sådan gren/incheckning: \"%s\""
+msgstr "ingen sådan gren/incheckning: ”%s”"
 
 #, c-format
 msgid "No such ref: %s"
 msgstr "Ingen sådan referens: %s"
 
-msgid "Could not resolve HEAD to a revision"
-msgstr "Kunde inte bestämma HEAD:s incheckning"
+msgid "Could not resolve HEAD to a commit"
+msgstr "Kunde inte bestämma en incheckning för HEAD"
 
 #, c-format
 msgid "'%s': need exactly one merge base with branch"
-msgstr "\"%s\": behöver precis en sammanslagningsbas med gren"
+msgstr "”%s”: behöver precis en sammanslagningsbas med gren"
 
 #, c-format
 msgid "'%s': need exactly one merge base"
-msgstr "\"%s\": behöver precis en sammanslagningsbas"
+msgstr "”%s”: behöver precis en sammanslagningsbas"
 
 #, c-format
 msgid "Does not point to a valid commit '%s'"
-msgstr "Pekar inte på en giltig incheckning: \"%s\""
-
-msgid "Please commit or stash them."
-msgstr "Checka in eller använd \"stash\" på dem."
+msgstr "Pekar inte på en giltig incheckning: ”%s”"
 
 msgid "HEAD is up to date."
 msgstr "HEAD är à jour."
@@ -10166,17 +10290,17 @@ msgid ""
 msgstr ""
 "Normalt tillåts inte uppdatering av aktuell gren i ett icke-naket\n"
 "arkiv, då index och arbetskatalog inte kommer stämma med det du\n"
-"sände och \"git reset --hard\" krävs för att få arbetskatalogen och\n"
+"sände och ”git reset --hard” krävs för att få arbetskatalogen och\n"
 "HEAD att stämma överens.\n"
 "\n"
-"Du kan ställa in variabeln \"receive.denyCurrentBranch\" till\n"
-"\"ignore\" eller \"warn\" i fjärrarkivet för att tillåta sändning till\n"
+"Du kan ställa in variabeln ”receive.denyCurrentBranch” till\n"
+"”ignore” eller ”warn” i fjärrarkivet för att tillåta sändning till\n"
 "dess aktuella gren; detta rekommenderas dock inte såvida du inte\n"
 "sett till att dess arbetskatalog uppdateras till det tu sände in\n"
 "på annat sätt.\n"
 "\n"
 "För att undvika detta meddelande och fortfarande behålla det\n"
-"normala beteendet, sätt \"receive.denyCurrentBranch\" till \"refuse\"."
+"normala beteendet, sätt ”receive.denyCurrentBranch” till ”refuse”."
 
 msgid ""
 "By default, deleting the current branch is denied, because the next\n"
@@ -10189,14 +10313,14 @@ msgid ""
 "To squelch this message, you can set it to 'refuse'."
 msgstr ""
 "Normalt tillåts inte radering av aktuell gren, eftersom nästa\n"
-"\"git clone\" inte kommer innebära att några filer checkas ut,\n"
+"”git clone” inte kommer innebära att några filer checkas ut,\n"
 "vilket är förvirrande.\n"
 "\n"
-"Du kan ställa in variabeln \"receive.denyDeleteCurrent\" till\n"
-"\"warn\" eller \"ignore\" i fjärrarkivet för att tillåta borttagning\n"
+"Du kan ställa in variabeln ”receive.denyDeleteCurrent” till\n"
+"”warn” eller ”ignore” i fjärrarkivet för att tillåta borttagning\n"
 "av aktuell gren, med eller utan varningsmeddelande.\n"
 "\n"
-"För att undvika detta meddelande kan du sätta det till \"refuse\"."
+"För att undvika detta meddelande kan du sätta det till ”refuse”."
 
 msgid "quiet"
 msgstr "tyst"
@@ -10230,7 +10354,7 @@ msgstr "git reflog exists <referens>"
 
 #, c-format
 msgid "invalid timestamp '%s' given to '--%s'"
-msgstr "ogiltig tidsstämpel \"%s\" given i \"--%s\""
+msgstr "ogiltig tidsstämpel ”%s” given i ”--%s”"
 
 msgid "do not actually prune any entries"
 msgstr "rensa faktiskt inte några poster"
@@ -10363,11 +10487,12 @@ msgstr "okänt argument till mirror: %s"
 msgid "fetch the remote branches"
 msgstr "hämta fjärrgrenarna"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "importera alla taggar och associerade objekt vid hämtning"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "eller hämta inte några taggar alls (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"importera alla taggar och associerade objekt vid hämtning\n"
+"eller hämta inte några taggar alls (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "gren(ar) att spåra"
@@ -10390,7 +10515,7 @@ msgstr "fjärrarkivet %s finns redan."
 
 #, c-format
 msgid "Could not setup master '%s'"
-msgstr "Kunde inte skapa master \"%s\""
+msgstr "Kunde inte skapa master ”%s”"
 
 #, c-format
 msgid "more than one %s"
@@ -10398,7 +10523,7 @@ msgstr "mer än en %s"
 
 #, c-format
 msgid "unhandled branch.%s.rebase=%s; assuming 'true'"
-msgstr "ohanterad branch.%s.rebase=%s; antar \"true\""
+msgstr "ohanterad branch.%s.rebase=%s; antar ”true”"
 
 #, c-format
 msgid "Could not get fetch map for refspec %s"
@@ -10412,11 +10537,11 @@ msgstr "(ta bort)"
 
 #, c-format
 msgid "could not set '%s'"
-msgstr "kunde inte ställa in \"%s\""
+msgstr "kunde inte ställa in ”%s”"
 
 #, c-format
 msgid "could not unset '%s'"
-msgstr "kunde inte ta bort inställning för \"%s\""
+msgstr "kunde inte ta bort inställning för ”%s”"
 
 #, c-format
 msgid ""
@@ -10426,15 +10551,15 @@ msgid ""
 msgstr ""
 "Konfigurationen för %s för remote.pushDefault i:\n"
 "\t%s:%d\n"
-"anger nu den icke-existerande fjärren \"%s\""
+"anger nu den icke-existerande fjärren ”%s”"
 
 #, c-format
 msgid "No such remote: '%s'"
-msgstr "Ingen sådan fjärr: \"%s\""
+msgstr "Ingen sådan fjärr: ”%s”"
 
 #, c-format
 msgid "Could not rename config section '%s' to '%s'"
-msgstr "Kunde inte byta namn på konfigurationssektionen \"%s\" till \"%s\""
+msgstr "Kunde inte byta namn på konfigurationssektionen ”%s” till ”%s”"
 
 #, c-format
 msgid ""
@@ -10451,11 +10576,11 @@ msgstr "Byter namn på fjärreferenser"
 
 #, c-format
 msgid "deleting '%s' failed"
-msgstr "misslyckades ta bort \"%s\""
+msgstr "misslyckades ta bort ”%s”"
 
 #, c-format
 msgid "creating '%s' failed"
-msgstr "misslyckades skapa \"%s\""
+msgstr "misslyckades skapa ”%s”"
 
 msgid ""
 "Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
@@ -10472,7 +10597,7 @@ msgstr[1] ""
 
 #, c-format
 msgid "Could not remove config section '%s'"
-msgstr "Kunde inte ta bort konfigurationssektionen \"%s\""
+msgstr "Kunde inte ta bort konfigurationssektionen ”%s”"
 
 #, c-format
 msgid " new (next fetch will store in remotes/%s)"
@@ -10485,7 +10610,7 @@ msgid " skipped"
 msgstr " överhoppad"
 
 msgid " stale (use 'git remote prune' to remove)"
-msgstr " förlegad (använd \"git remote prune\" för att ta bort)"
+msgstr " förlegad (använd ”git remote prune” för att ta bort)"
 
 msgid " ???"
 msgstr " ???"
@@ -10597,17 +10722,17 @@ msgstr " (status inte förfrågad)"
 
 msgid "  Local branch configured for 'git pull':"
 msgid_plural "  Local branches configured for 'git pull':"
-msgstr[0] "  Lokal gren konfigurerad för \"git pull\":"
-msgstr[1] "  Lokala grenar konfigurerade för \"git pull\":"
+msgstr[0] "  Lokal gren konfigurerad för ”git pull”:"
+msgstr[1] "  Lokala grenar konfigurerade för ”git pull”:"
 
 msgid "  Local refs will be mirrored by 'git push'"
-msgstr "  Lokala referenser speglas av \"git push\""
+msgstr "  Lokala referenser speglas av ”git push”"
 
 #, c-format
 msgid "  Local ref configured for 'git push'%s:"
 msgid_plural "  Local refs configured for 'git push'%s:"
-msgstr[0] "  Lokal referens konfigurerad för \"git push\"%s:"
-msgstr[1] "  Lokala referenser konfigurerade för \"git push\"%s:"
+msgstr[0] "  Lokal referens konfigurerad för ”git push”%s:"
+msgstr[1] "  Lokala referenser konfigurerade för ”git push”%s:"
 
 msgid "set refs/remotes/<name>/HEAD according to remote"
 msgstr "sätt refs/remotes/<namn>/HEAD enligt fjärren"
@@ -10662,7 +10787,7 @@ msgstr "rensa fjärrar efter hämtning"
 
 #, c-format
 msgid "No such remote '%s'"
-msgstr "Ingen sådan fjärr \"%s\""
+msgstr "Ingen sådan fjärr ”%s”"
 
 msgid "add branch"
 msgstr "lägg till gren"
@@ -10678,7 +10803,7 @@ msgstr "returnera alla URL:er"
 
 #, c-format
 msgid "no URLs configured for remote '%s'"
-msgstr "ingen URL:er angivna för fjärren \"%s\""
+msgstr "ingen URL:er angivna för fjärren ”%s”"
 
 msgid "manipulate push URLs"
 msgstr "manipulera URL:ar för sändning"
@@ -10746,6 +10871,14 @@ msgstr "kunde inte öppna temporär fil %s för skrivning"
 msgid "could not close refs snapshot tempfile"
 msgstr "kunde inte stänga temporär fil för refs-ögonblicksbild"
 
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "kunde inte ta bort gammal bitkarta: %s"
+
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "paketprefixet %s börjar inte med objkat %s"
+
 msgid "pack everything in a single pack"
 msgstr "packa allt i ett enda paket"
 
@@ -10758,8 +10891,8 @@ msgstr "samma som -a, packa onåbara onödiga objekt separat"
 msgid "approxidate"
 msgstr "cirkadatum"
 
-msgid "with -C, expire objects older than this"
-msgstr "med -C, låt tid gå ut för objekt äldre än detta"
+msgid "with --cruft, expire objects older than this"
+msgstr "med --cruft, låt tid gå ut för objekt äldre än detta"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "ta bort överflödiga paket, och kör git-prune-packed"
@@ -10818,23 +10951,33 @@ 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"
+
 msgid "cannot delete packs in a precious-objects repo"
-msgstr "kan inte ta bort paket i ett \"precious-objects\"-arkiv"
+msgstr "kan inte ta bort paket i ett ”precious-objects”-arkiv"
+
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "flaggan ”%s” kan inte användas med ”%s”"
 
 msgid "Nothing new to pack."
 msgstr "Inget nytt att packa."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "paketprefixet %s börjar inte med objkat %s"
+msgid "renaming pack to '%s' failed"
+msgstr "misslyckades byta namn på paket till ”%s”"
 
 #, c-format
-msgid "missing required file: %s"
-msgstr "nödvändig fil saknas: %s"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects skrev inte en ”%s”-fil för paketet %s-%s"
 
 #, c-format
 msgid "could not unlink: %s"
-msgstr "kunde inte ta bort: \"%s\""
+msgstr "kunde inte ta bort: ”%s”"
 
 msgid "git replace [-f] <object> <replacement>"
 msgstr "git replace [-f] <objekt> <ersättning>"
@@ -10856,24 +10999,24 @@ msgid ""
 "invalid replace format '%s'\n"
 "valid formats are 'short', 'medium' and 'long'"
 msgstr ""
-"ogiltigt ersättningsformat \"%s\"\n"
-"giltiga format är \"short\", \"medium\" och \"long\""
+"ogiltigt ersättningsformat ”%s”\n"
+"giltiga format är ”short”, ”medium” och ”long”"
 
 #, c-format
 msgid "replace ref '%s' not found"
-msgstr "ersättningsreferensen \"%s\" hittades inte"
+msgstr "ersättningsreferensen ”%s” hittades inte"
 
 #, c-format
 msgid "Deleted replace ref '%s'"
-msgstr "Tog bort ersättningsreferensen \"%s\""
+msgstr "Tog bort ersättningsreferensen ”%s”"
 
 #, c-format
 msgid "'%s' is not a valid ref name"
-msgstr "\"%s\" är inte ett giltigt referensnamn"
+msgstr "”%s” är inte ett giltigt referensnamn"
 
 #, c-format
 msgid "replace ref '%s' already exists"
-msgstr "ersättningsreferensen \"%s\" finns redan"
+msgstr "ersättningsreferensen ”%s” finns redan"
 
 #, c-format
 msgid ""
@@ -10882,8 +11025,8 @@ msgid ""
 "while '%s' points to a replacement object of type '%s'."
 msgstr ""
 "Objekt måste vara av samma typ.\n"
-"\"%s\" pekar på ett ersatt objekt med typen \"%s\"\n"
-"medan \"%s\" pekar på ett ersättningsobjekt av typen \"%s\"."
+"”%s” pekar på ett ersatt objekt med typen ”%s”\n"
+"medan ”%s” pekar på ett ersättningsobjekt av typen ”%s”."
 
 #, c-format
 msgid "unable to open %s for writing"
@@ -10910,7 +11053,7 @@ msgstr "mktree returnerade inte ett objektnamn"
 
 #, c-format
 msgid "unable to fstat %s"
-msgstr "kan inte utföra \"fstat\" på %s"
+msgstr "kan inte utföra ”fstat” på %s"
 
 msgid "unable to write object to database"
 msgstr "kan inte skriva objektet till databasen"
@@ -10924,7 +11067,7 @@ msgstr "misslyckades redigera objektfilen"
 
 #, c-format
 msgid "new object is the same as the old one: '%s'"
-msgstr "nytt objekt är samma som det gamla: \"%s\""
+msgstr "nytt objekt är samma som det gamla: ”%s”"
 
 #, c-format
 msgid "could not parse %s as a commit"
@@ -10932,38 +11075,38 @@ msgstr "kunde inte tolka %s som incheckning"
 
 #, c-format
 msgid "bad mergetag in commit '%s'"
-msgstr "felaktig sammanslagningstagg i incheckningen \"%s\""
+msgstr "felaktig sammanslagningstagg i incheckningen ”%s”"
 
 #, c-format
 msgid "malformed mergetag in commit '%s'"
-msgstr "felformad sammanslagningstagg i incheckningen \"%s\""
+msgstr "felformad sammanslagningstagg i incheckningen ”%s”"
 
 #, c-format
 msgid ""
 "original commit '%s' contains mergetag '%s' that is discarded; use --edit "
 "instead of --graft"
 msgstr ""
-"den ursprungliga incheckningen \"%s\" innehåller sammanslagningstaggen \"%s"
-"\" som har förkastats; använd --edit istället för --graft"
+"den ursprungliga incheckningen ”%s” innehåller sammanslagningstaggen ”%s” "
+"som har förkastats; använd --edit istället för --graft"
 
 #, c-format
 msgid "the original commit '%s' has a gpg signature"
-msgstr "den ursprungliga incheckningen \"%s\" har en gpg-signatur"
+msgstr "den ursprungliga incheckningen ”%s” har en gpg-signatur"
 
 msgid "the signature will be removed in the replacement commit!"
 msgstr "signaturen kommer att tas bort i ersättningsincheckningen!"
 
 #, c-format
 msgid "could not write replacement commit for: '%s'"
-msgstr "kunde inte skriva ersättningsincheckning för: \"%s\""
+msgstr "kunde inte skriva ersättningsincheckning för: ”%s”"
 
 #, c-format
 msgid "graft for '%s' unnecessary"
-msgstr "ympning för \"%s\" behövs inte"
+msgstr "ympning för ”%s” behövs inte"
 
 #, c-format
 msgid "new commit is the same as the old one: '%s'"
-msgstr "ny incheckning är samma som den gamla: \"%s\""
+msgstr "ny incheckning är samma som den gamla: ”%s”"
 
 #, c-format
 msgid ""
@@ -11024,18 +11167,20 @@ msgstr "--convert-graft-file tar inga argument"
 msgid "only one pattern can be given with -l"
 msgstr "endast ett mönster kan anges med -l"
 
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <sökväg>... | diff | status | remaining | gc]"
 
 msgid "register clean resolutions in index"
 msgstr "registrera rena lösningar i indexet"
 
 msgid "'git rerere forget' without paths is deprecated"
-msgstr "\"git rerere forget\" utan sökvägar är föråldrat"
+msgstr "”git rerere forget” utan sökvägar är föråldrat"
 
 #, c-format
 msgid "unable to generate diff for '%s'"
-msgstr "misslyckades skapa diff för \"%s\""
+msgstr "misslyckades skapa diff för ”%s”"
 
 msgid ""
 "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
@@ -11109,15 +11254,15 @@ msgstr "registrera endast att borttagna sökvägar kommer läggas till senare"
 
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
-msgstr "Kunde inte slå upp \"%s\" som en giltig revision."
+msgstr "Kunde inte slå upp ”%s” som en giltig revision."
 
 #, c-format
 msgid "Failed to resolve '%s' as a valid tree."
-msgstr "Kunde inte slå upp \"%s\" som ett giltigt träd."
+msgstr "Kunde inte slå upp ”%s” som ett giltigt träd."
 
 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
 msgstr ""
-"--mixed rekommenderas inte med sökvägar; använd \"git reset -- <sökvägar>\"."
+"--mixed rekommenderas inte med sökvägar; använd ”git reset -- <sökvägar>”."
 
 #, c-format
 msgid "Cannot do %s reset with paths."
@@ -11136,11 +11281,11 @@ msgid ""
 "'--no-refresh' to avoid this."
 msgstr ""
 "Det tog %.2f sekunder att uppdatera indexet efter återställning.\n"
-"Du kan använda \"--no-refresh\" för undvika detta."
+"Du kan använda ”--no-refresh” för undvika detta."
 
 #, c-format
 msgid "Could not reset index file to revision '%s'."
-msgstr "Kunde inte återställa indexfilen till versionen \"%s\"."
+msgstr "Kunde inte återställa indexfilen till versionen ”%s”."
 
 msgid "Could not write new index file."
 msgstr "Kunde inte skriva ny indexfil."
@@ -11151,21 +11296,20 @@ msgstr "kan inte hämta diskanvändning för %s"
 
 #, c-format
 msgid "invalid value for '%s': '%s', the only allowed format is '%s'"
-msgstr ""
-"felaktigt värde för \"%s\": \"%s\", det enda tillåtna formatet är \"%s\""
+msgstr "felaktigt värde för ”%s”: ”%s”, det enda tillåtna formatet är ”%s”"
 
 msgid "rev-list does not support display of notes"
 msgstr "rev-list stöder inte visning av anteckningar"
 
 #, c-format
 msgid "marked counting and '%s' cannot be used together"
-msgstr "markerad räkning och \"%s\" kan inte användas samtidigt."
+msgstr "markerad räkning och ”%s” kan inte användas samtidigt."
 
 msgid "git rev-parse --parseopt [<options>] -- [<args>...]"
 msgstr "git rev-parse --parseopt [<options>] -- [<argument>...]"
 
 msgid "keep the `--` passed as an arg"
-msgstr "behåll \"--\" sänt som argument"
+msgstr "behåll ”--” sänt som argument"
 
 msgid "stop parsing after the first non-option argument"
 msgstr "sluta tolka efter första argument som inte är flagga"
@@ -11177,7 +11321,7 @@ msgid "premature end of input"
 msgstr "för tidigt slut på indata"
 
 msgid "no usage string given before the `--' separator"
-msgstr "ingen användningssträng angavs före \"--\"-avdelaren"
+msgstr "ingen användningssträng angavs före ”--”-avdelaren"
 
 msgid "missing opt-spec before option flags"
 msgstr "saknar flagg-spec före alternativflaggor"
@@ -11196,7 +11340,7 @@ msgstr ""
 "     eller: git rev-parse --sq-quote [<argument>...]\n"
 "     eller: git rev-parse [<flaggor>] [<argument>...]\n"
 "\n"
-"Kör \"git rev-parse --parseopt -h\" för mer information om den första "
+"Kör ”git rev-parse --parseopt -h” för mer information om den första "
 "varianten."
 
 msgid "--resolve-git-dir requires an argument"
@@ -11204,7 +11348,7 @@ msgstr "--resolve-git-dir kräver ett argument"
 
 #, c-format
 msgid "not a gitdir '%s'"
-msgstr "inte en gitkatalog \"%s\""
+msgstr "inte en gitkatalog ”%s”"
 
 msgid "--git-path requires an argument"
 msgstr "--git-path kräver ett argument"
@@ -11229,28 +11373,48 @@ msgstr "--prefix kräver ett argument"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "okänt läge för --abbrev-ref: %s"
 
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden kan endast användas tillsammans med --branches"
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden kan  kan inte användas tillsammans med --tags"
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden kan  kan inte användas tillsammans med --remotes"
+
 msgid "this operation must be run in a work tree"
 msgstr "funktionen måste köras i en arbetskatalog"
 
+msgid "Could not read the index"
+msgstr "Kunde inte läsa indexet"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "okänt läge för --show-object-format: %s"
 
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<flaggor>] <incheckning-igt>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <förälder-nummer>] [-s] [-S[<nyckelid>]] "
+"<incheckning>..."
 
-msgid "git revert <subcommand>"
-msgstr "git revert <underkommando>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
 
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<flaggor>] <incheckning-igt>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <förälder-nummer>] [-s] [-x] [--ff]\n"
+"                [-S[<nyckelid>]] <incheckning>..."
 
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <underkommando>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
-msgstr "flaggan \"%s\" antar ett numeriskt värde större än noll"
+msgstr "flaggan ”%s” antar ett numeriskt värde större än noll"
 
 #, c-format
 msgid "%s: %s cannot be used with %s"
@@ -11299,16 +11463,22 @@ msgid "keep redundant, empty commits"
 msgstr "behåll redundanta, tomma incheckningar"
 
 msgid "use the 'reference' format to refer to commits"
-msgstr "använd \"referens\"-format för att referera till incheckningar"
+msgstr "använd ”referens”-format för att referera till incheckningar"
 
 msgid "revert failed"
-msgstr "\"revert\" misslyckades"
+msgstr "”revert” misslyckades"
 
 msgid "cherry-pick failed"
-msgstr "\"cherry-pick\" misslyckades"
+msgstr "”cherry-pick” misslyckades"
 
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<flaggor>] [--] <fil>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<fil> [--pathspec-file-nul]]\n"
+"       [--] [<sökväg>...]"
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -11365,11 +11535,11 @@ msgstr "Ingen sökvägsangivelse gavs. Vilka filer ska jag ta bort?"
 
 msgid "please stage your changes to .gitmodules or stash them to proceed"
 msgstr ""
-"löa dina ändringar i .gitmodules eller använd \"stash\" för att fortsätta"
+"löa dina ändringar i .gitmodules eller använd ”stash” för att fortsätta"
 
 #, c-format
 msgid "not removing '%s' recursively without -r"
-msgstr "tar inte bort \"%s\" rekursivt utan -r"
+msgstr "tar inte bort ”%s” rekursivt utan -r"
 
 #, c-format
 msgid "git rm: unable to remove %s"
@@ -11379,16 +11549,21 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<värd>:]<katalog> (--all | <ref>...)"
 
 msgid "remote name"
 msgstr "fjärrnamn"
 
+msgid "push all refs"
+msgstr "sänd alla referenser"
+
 msgid "use stateless RPC protocol"
 msgstr "använd tillståndslöst RPC-protokoll"
 
@@ -11405,10 +11580,11 @@ msgid "git log --pretty=short | git shortlog [<options>]"
 msgstr "git log --pretty=short | git shortlog [<flaggor>]"
 
 msgid "using multiple --group options with stdin is not supported"
-msgstr "mer än en \"--group\"-flagga stöds inte med standard in"
+msgstr "mer än en ”--group”-flagga stöds inte med standard in"
 
-msgid "using --group=trailer with stdin is not supported"
-msgstr "att använda --group=trailer stöds inte med standard in"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "använda %s med standard in stöds inte"
 
 #, c-format
 msgid "unknown group type: %s"
@@ -11445,12 +11621,14 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<när>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <mönster>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <mönster>)...]"
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g | --reflog)[=<n>[,<bas>]] [--list] [<ref>]"
@@ -11472,7 +11650,7 @@ msgid "show remote-tracking branches"
 msgstr "visa fjärrspårande grenar"
 
 msgid "color '*!+-' corresponding to the branch"
-msgstr "färga \"*!+-\" enligt grenen"
+msgstr "färga ”*!+-” enligt grenen"
 
 msgid "show <n> more commits after the common ancestor"
 msgstr "visa <n> ytterligare incheckningar efter gemensam anfader"
@@ -11537,7 +11715,7 @@ msgstr[1] "kan inte hantera mer än %d revisioner."
 
 #, c-format
 msgid "'%s' is not a valid ref."
-msgstr "\"%s\" är inte en giltig referens."
+msgstr "”%s” är inte en giltig referens."
 
 #, c-format
 msgid "cannot find commit %s (%s)"
@@ -11550,21 +11728,44 @@ msgid "Unknown hash algorithm"
 msgstr "okänd hashningsalgoritm"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<mönster>...]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<mönster>...]"
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<mönster>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <ref>"
+
+msgid "reference does not exist"
+msgstr "referensen existerar inte"
+
+msgid "failed to look up reference"
+msgstr "misslyckades slå upp referensen"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "visa endast taggar (kan kombineras med huvuden)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "visa endast huvuden (kan kombineras med taggar)"
 
+msgid "check for reference existence without resolving"
+msgstr "kontrollerar att referensen existerar utan att slå upp dem"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr "striktare referenskontroll, kräver exakt referenssökväg"
 
@@ -11583,8 +11784,16 @@ msgstr "visa inte resultat på standard ut (användbart med --verify)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "visa referenser från standard in som inte finns i lokalt arkiv"
 
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) <flaggor>"
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "endast en av ”%s”, ”%s” och ”%s” kan anges"
+
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) <flaggor>"
 
 msgid "this worktree is not sparse"
 msgstr "arbetskatalogen är inte gren"
@@ -11598,15 +11807,15 @@ 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 "
-"\"sparse-checkout\""
+"katalogen ”%s” innehåller ospårade filer, men är inte i området som ages i "
+"”sparse-checkout”"
 
 #, c-format
 msgid "failed to remove directory '%s'"
-msgstr "misslyckades ta bort katalogen \"%s\""
+msgstr "misslyckades ta bort katalogen ”%s”"
 
 msgid "failed to create directory for sparse-checkout file"
-msgstr "misslyckades skapa katalog för \"sparse-checkout\"-filen"
+msgstr "misslyckades skapa katalog för ”sparse-checkout”-filen"
 
 msgid "failed to initialize worktree config"
 msgstr "misslyckades initiera arbetskataloginställning"
@@ -11626,15 +11835,15 @@ msgstr "kunde inte skapa inledande kataloger för %s"
 
 #, c-format
 msgid "failed to open '%s'"
-msgstr "misslyckades öppna \"%s\""
+msgstr "misslyckades öppna ”%s”"
 
 #, c-format
 msgid "could not normalize path %s"
-msgstr "kunde inte normalisera sökvägen \"%s\""
+msgstr "kunde inte normalisera sökvägen ”%s”"
 
 #, c-format
 msgid "unable to unquote C-style string '%s'"
-msgstr "kan inte ta bort citering av C-sträng \"%s\""
+msgstr "kan inte ta bort citering av C-sträng ”%s”"
 
 msgid "unable to load existing sparse-checkout patterns"
 msgstr "kunde inte läsa in existerande mönster för gles utcheckning"
@@ -11652,8 +11861,8 @@ msgid ""
 "specify directories rather than patterns.  If your directory starts with a "
 "'!', pass --skip-checks"
 msgstr ""
-"ange kataloger istället för mönster. Om din katalog börjar med ett \"!\", "
-"sänd med --skip-checks"
+"ange kataloger istället för mönster. Om din katalog börjar med ett ”!”, sänd "
+"med --skip-checks"
 
 msgid ""
 "specify directories rather than patterns.  If your directory really has any "
@@ -11667,7 +11876,7 @@ msgid ""
 "'%s' is not a directory; to treat it as a directory anyway, rerun with --"
 "skip-checks"
 msgstr ""
-"\"%s\" är inte en katalog: för att ändå behandla det som en katalog, kör på "
+"”%s” är inte en katalog: för att ändå behandla det som en katalog, kör på "
 "nytt med --skip-checks"
 
 #, c-format
@@ -11675,7 +11884,7 @@ msgid ""
 "pass a leading slash before paths such as '%s' if you want a single file "
 "(see NON-CONE PROBLEMS in the git-sparse-checkout manual)."
 msgstr ""
-"sänd med ett inledande snedstreck före sökvägar som \"%s\" om du vill ha en "
+"sänd med ett inledande snedstreck före sökvägar som ”%s” om du vill ha en "
 "enstaka file (se NON-CONE PROBLEMS i manualen git-sparse-checkout)."
 
 msgid "git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"
@@ -11707,85 +11916,92 @@ msgstr ""
 msgid "error while refreshing working directory"
 msgstr "fel vid uppdatering av arbetskatalog"
 
-msgid "git stash list [<options>]"
-msgstr "git stash list [<flaggor>]"
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks] [--[no-]cone] [--rules-"
+"file <fil>]"
 
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<flaggor>] [<stash>]"
+msgid "terminate input and output files by a NUL character"
+msgstr "avsluta in- och utdatafiler med NUL-tecken"
 
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<stash>]"
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr "om används med --rules-file tolka mönster som kon-lägemönster"
 
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
+msgid "use patterns in <file> instead of the current ones."
+msgstr "använd mönster i <fil> istället för de nuvarande."
 
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <grennamn> [<stash>]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<”log”-flaggor>]"
 
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <meddelande>]\n"
-"          [--pathspec-from-file=<fil> [--pathspec-file-nul]]\n"
-"          [--] [<sökväg>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<”diff”-"
+"flaggor>] [<stash>]"
 
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<meddelande>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<stash>]"
+
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<stash>]"
 
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<stash>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<stash>]"
 
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<stash>]"
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <grennamn> [<stash>]"
 
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <meddelande>] [-q|--quiet] <incheckning>"
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr ""
+"git stash store [(-m | --message) <meddelande>] [-q | --quiet] <incheckning>"
 
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <meddelande>]\n"
+"git stash [push [-p | --patch] [S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message "
+"<meddelande>]\n"
+"          [--pathspec-from-file=<fil> [--pathspec-file-nul]]\n"
 "          [--] [<sökväg>...]]"
 
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<meddelande>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<meddelande>]"
+
+msgid "git stash create [<message>]"
+msgstr "git stash create [<meddelande>]"
 
 #, c-format
 msgid "'%s' is not a stash-like commit"
-msgstr "\"%s\" är inte en \"stash\"-liknande incheckning"
+msgstr "”%s” är inte en ”stash”-liknande incheckning"
 
 #, c-format
 msgid "Too many revisions specified:%s"
 msgstr "För många revisioner angivna:%s"
 
 msgid "No stash entries found."
-msgstr "Inga \"stash\"-poster hittades."
+msgstr "Inga ”stash”-poster hittades."
 
 #, c-format
 msgid "%s is not a valid reference"
 msgstr "%s är inte en giltigt referens"
 
 msgid "git stash clear with arguments is unimplemented"
-msgstr "\"git stash clear\" med argument har inte implementerats"
+msgstr "”git stash clear” med argument har inte implementerats"
 
 #, c-format
 msgid ""
@@ -11794,11 +12010,11 @@ msgid ""
 "         to make room.\n"
 msgstr ""
 "VARNING: En ospårad fil är i vägen för en spårad fil! Byter namn\n"
-"            %s -> %s\n"
+"            %s  %s\n"
 "         för att lämna plats.\n"
 
 msgid "cannot apply a stash in the middle of a merge"
-msgstr "kan inte tillämpa en \"stash\" mitt i en sammanslagning"
+msgstr "kan inte tillämpa en ”stash” mitt i en sammanslagning"
 
 #, c-format
 msgid "could not generate diff %s^!."
@@ -11815,7 +12031,7 @@ msgid "Merging %s with %s"
 msgstr "Slår ihop %s med %s"
 
 msgid "Index was not unstashed."
-msgstr "Indexet har inte tagits upp ur \"stash\":en"
+msgstr "Indexet har inte tagits upp ur ”stash”:en"
 
 msgid "could not restore untracked files from stash"
 msgstr "kunde inte återställa ospårade filer från stash-post"
@@ -11829,11 +12045,11 @@ msgstr "Kastade %s (%s)"
 
 #, c-format
 msgid "%s: Could not drop stash entry"
-msgstr "%s: Kunde inte kasta \"stash\"-post"
+msgstr "%s: Kunde inte kasta ”stash”-post"
 
 #, c-format
 msgid "'%s' is not a stash reference"
-msgstr "\"%s\" är inte en \"stash\"-referens"
+msgstr "”%s” är inte en ”stash”-referens"
 
 msgid "The stash entry is kept in case you need it again."
 msgstr "Stash-posten behålls ifall du behöver den igen."
@@ -11848,20 +12064,20 @@ msgid "failed to unpack trees"
 msgstr "misslyckades packa upp träd"
 
 msgid "include untracked files in the stash"
-msgstr "ta med ospårade filer i \"stash\""
+msgstr "ta med ospårade filer i ”stash”"
 
 msgid "only show untracked files in the stash"
-msgstr "visa bara ospårade filer i \"stash\""
+msgstr "visa bara ospårade filer i ”stash”"
 
 #, c-format
 msgid "Cannot update %s with %s"
 msgstr "Kan inte uppdatera %s med %s"
 
 msgid "stash message"
-msgstr "\"stash\"-meddelande"
+msgstr "”stash”-meddelande"
 
 msgid "\"git stash store\" requires one <commit> argument"
-msgstr "\"git stash store\" kräver ett <incheckning>-argument"
+msgstr "”git stash store” kräver ett <incheckning>-argument"
 
 msgid "No staged changes"
 msgstr "Inga köade ändringar"
@@ -11895,13 +12111,13 @@ msgstr ""
 "Kan inte använda --staged och --include-untracked eller --all samtidigt"
 
 msgid "Did you forget to 'git add'?"
-msgstr "Glömde du använda \"git add\"?"
+msgstr "Glömde du använda ”git add”?"
 
 msgid "No local changes to save"
 msgstr "Inga lokala ändringar att spara"
 
 msgid "Cannot initialize stash"
-msgstr "Kan inte initiera \"stash\""
+msgstr "Kan inte initiera ”stash”"
 
 msgid "Cannot save the current status"
 msgstr "Kan inte spara aktuell status"
@@ -11920,13 +12136,13 @@ msgid "stash staged changes only"
 msgstr "stash:a endast köade ändringar"
 
 msgid "stash in patch mode"
-msgstr "\"stash\" i \"patch\"-läge"
+msgstr "”stash” i ”patch”-läge"
 
 msgid "quiet mode"
 msgstr "tyst läge"
 
 msgid "include untracked files in stash"
-msgstr "ta med ospårade filer i \"stash\""
+msgstr "ta med ospårade filer i ”stash”"
 
 msgid "include ignore files"
 msgstr "ta med ignorerade filer"
@@ -11943,23 +12159,23 @@ msgstr "Förväntade fullt referensnamn, fick %s"
 
 #, c-format
 msgid "could not get a repository handle for submodule '%s'"
-msgstr "kunde inte få tag i arkivhandtag för undermodulen \"%s\""
+msgstr "kunde inte få tag i arkivhandtag för undermodulen ”%s”"
 
 #, c-format
 msgid ""
 "could not look up configuration '%s'. Assuming this repository is its own "
 "authoritative upstream."
 msgstr ""
-"kunde inte slå upp konfigurationen \"%s\". Antar att arkivet är sin eget "
+"kunde inte slå upp konfigurationen ”%s”. Antar att arkivet är sin eget "
 "officiella uppström."
 
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
-msgstr "Hittade ingen url för undermodulsökvägen \"%s\" i .gitmodules"
+msgstr "Hittade ingen url för undermodulsökvägen ”%s” i .gitmodules"
 
 #, c-format
 msgid "Entering '%s'\n"
-msgstr "Går in i \"%s\"\n"
+msgstr "Går in i ”%s”\n"
 
 #, c-format
 msgid ""
@@ -11990,19 +12206,19 @@ msgstr "git submodule foreach [--quiet] [--recursive] [--] <kommando>"
 
 #, c-format
 msgid "Failed to register url for submodule path '%s'"
-msgstr "Misslyckades registrera url för undermodulsökväg \"%s\""
+msgstr "Misslyckades registrera url för undermodulsökväg ”%s”"
 
 #, c-format
 msgid "Submodule '%s' (%s) registered for path '%s'\n"
-msgstr "Undermodulen \"%s\" (%s) registrerad för sökvägen \"%s\"\n"
+msgstr "Undermodulen ”%s” (%s) registrerad för sökvägen ”%s”\n"
 
 #, c-format
 msgid "warning: command update mode suggested for submodule '%s'\n"
-msgstr "varning: kommandouppdateringsläge föreslogs för undermodulen \"%s\"\n"
+msgstr "varning: kommandouppdateringsläge föreslogs för undermodulen ”%s”\n"
 
 #, c-format
 msgid "Failed to register update mode for submodule path '%s'"
-msgstr "Misslyckades registrera uppdateringsläge för undermodulsökväg \"%s\""
+msgstr "Misslyckades registrera uppdateringsläge för undermodulsökväg ”%s”"
 
 msgid "suppress output for initializing a submodule"
 msgstr "dölj utdata från initiering av undermodul"
@@ -12012,15 +12228,15 @@ msgstr "git submodule init [<flaggor>] [<sökväg>]"
 
 #, c-format
 msgid "no submodule mapping found in .gitmodules for path '%s'"
-msgstr "hittade ingen undermodulmappning i .gitmodules för sökvägen \"%s\""
+msgstr "hittade ingen undermodulmappning i .gitmodules för sökvägen ”%s”"
 
 #, c-format
 msgid "could not resolve HEAD ref inside the submodule '%s'"
-msgstr "kunde inte bestämma HEAD:s incheckning i undermodulen \"%s\""
+msgstr "kunde inte bestämma HEAD:s incheckning i undermodulen ”%s”"
 
 #, c-format
 msgid "failed to recurse into submodule '%s'"
-msgstr "misslyckades rekursera in i undermodulen \"%s\""
+msgstr "misslyckades rekursera in i undermodulen ”%s”"
 
 msgid "suppress submodule status output"
 msgstr "hindra statusutskrift för undermodul"
@@ -12037,11 +12253,11 @@ msgstr "git submodule status [--quitet] [--cached] [--recursive] [<sökväg>...]
 
 #, c-format
 msgid "* %s %s(blob)->%s(submodule)"
-msgstr "* %s %s(blob)->%s(submodule)"
+msgstr "* %s %s(blob)%s(submodule)"
 
 #, c-format
 msgid "* %s %s(submodule)->%s(blob)"
-msgstr "* %s %s(submodule)->%s(blob)"
+msgstr "* %s %s(submodule)%s(blob)"
 
 #, c-format
 msgid "%s"
@@ -12049,7 +12265,7 @@ msgstr "%s"
 
 #, c-format
 msgid "couldn't hash object from '%s'"
-msgstr "kunde inte hasha objekt från \"%s\""
+msgstr "kunde inte hasha objekt från ”%s”"
 
 #, c-format
 msgid "unexpected mode %o\n"
@@ -12063,7 +12279,7 @@ msgstr "jämför incheckningen i indexet med den i undermodulens HEAD"
 
 msgid "skip submodules with 'ignore_config' value set to 'all'"
 msgstr ""
-"hoppa över undermoduler där värdet för \"ignore_config\" är satt till \"all\""
+"hoppa över undermoduler där värdet för ”ignore_config” är satt till ”all”"
 
 msgid "limit the summary size"
 msgstr "begränsa översiktsstorleken"
@@ -12076,15 +12292,15 @@ msgstr "kunde inte hämta en version för HEAD"
 
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
-msgstr "Synkroniserar undermodul-url för \"%s\"\n"
+msgstr "Synkroniserar undermodul-url för ”%s”\n"
 
 #, c-format
 msgid "failed to register url for submodule path '%s'"
-msgstr "misslyckades registrera url för undermodulsökväg \"%s\""
+msgstr "misslyckades registrera url för undermodulsökväg ”%s”"
 
 #, c-format
 msgid "failed to update remote for submodule '%s'"
-msgstr "misslyckades uppdatera fjärr för undermodulsökväg \"%s\""
+msgstr "misslyckades uppdatera fjärr för undermodulsökväg ”%s”"
 
 msgid "suppress output of synchronizing submodule url"
 msgstr "dölj utdata från synkronisering av undermodul-url"
@@ -12097,7 +12313,7 @@ msgid ""
 "Submodule work tree '%s' contains a .git directory. This will be replaced "
 "with a .git file by using absorbgitdirs."
 msgstr ""
-"Undermodulsarbetskatalogen \"%s\" innehåller en .git-katalog. Denna kommer "
+"Undermodulsarbetskatalogen ”%s” innehåller en .git-katalog. Denna kommer "
 "ersättas med en .git-fil med absorbgitdirs."
 
 #, c-format
@@ -12105,16 +12321,15 @@ msgid ""
 "Submodule work tree '%s' contains local modifications; use '-f' to discard "
 "them"
 msgstr ""
-"Undermodulens arbetskatalog \"%s\" har lokala ändringar; \"-f\" kastar bort "
-"dem"
+"Undermodulens arbetskatalog ”%s” har lokala ändringar; ”-f” kastar bort dem"
 
 #, c-format
 msgid "Cleared directory '%s'\n"
-msgstr "Rensade katalogen \"%s\"\n"
+msgstr "Rensade katalogen ”%s”\n"
 
 #, c-format
 msgid "Could not remove submodule work tree '%s'\n"
-msgstr "Kunde inte ta bort undermodulens arbetskatalog \"%s\"\n"
+msgstr "Kunde inte ta bort undermodulens arbetskatalog ”%s”\n"
 
 #, c-format
 msgid "could not create empty submodule directory %s"
@@ -12122,7 +12337,7 @@ msgstr "kunde inte skapa tom undermodulskatalog %s"
 
 #, c-format
 msgid "Submodule '%s' (%s) unregistered for path '%s'\n"
-msgstr "Undermodulen \"%s\" (%s) registrerad för sökvägen \"%s\"\n"
+msgstr "Undermodulen ”%s” (%s) registrerad för sökvägen ”%s”\n"
 
 msgid "remove submodule working trees even if they contain local changes"
 msgstr ""
@@ -12137,7 +12352,7 @@ msgstr ""
 "git submodule deinit [--quiet] [-f | --force] [--all | [--] [<sökväg>...]]"
 
 msgid "Use '--all' if you really want to deinitialize all submodules"
-msgstr "Använd \"--all\" om du verkligen vill avinitiera alla undermoduler"
+msgstr "Använd ”--all” om du verkligen vill avinitiera alla undermoduler"
 
 msgid ""
 "An alternate computed from a superproject's alternate is invalid.\n"
@@ -12147,40 +12362,40 @@ msgid ""
 msgstr ""
 "En suppleant beräknad från huvudprojektets suppleant är ogiltig.\n"
 "För att i så fall låta Git klona utan ett suppleant, sätt\n"
-"submodule.alternateErrorStrategy till \"info\" eller, likvärdigt, klona\n"
-"med \"--reference-if-able\" istället för \"--reference\"."
+"submodule.alternateErrorStrategy till ”info” eller, likvärdigt, klona\n"
+"med ”--reference-if-able” istället för ”--reference”."
 
 #, c-format
 msgid "could not get a repository handle for gitdir '%s'"
-msgstr "kunde inte få tag i arkivhandtag för gitkatalogen \"%s\""
+msgstr "kunde inte få tag i arkivhandtag för gitkatalogen ”%s”"
 
 #, c-format
 msgid "submodule '%s' cannot add alternate: %s"
-msgstr "undermodulen \"%s\" kan inte lägga till suppleant: %s"
+msgstr "undermodulen ”%s” kan inte lägga till suppleant: %s"
 
 #, c-format
 msgid "Value '%s' for submodule.alternateErrorStrategy is not recognized"
-msgstr "Värdet \"%s\" i submodule.alternateErrorStrategy förstås inte"
+msgstr "Värdet ”%s” i submodule.alternateErrorStrategy förstås inte"
 
 #, c-format
 msgid "Value '%s' for submodule.alternateLocation is not recognized"
-msgstr "Värdet \"%s\" i submodule.alternateLocation förstås inte"
+msgstr "Värdet ”%s” i submodule.alternateLocation förstås inte"
 
 #, c-format
 msgid "refusing to create/use '%s' in another submodule's git dir"
-msgstr "vägrar skapa/använda \"%s\" i en annan undermoduls gitkatalog"
+msgstr "vägrar skapa/använda ”%s” i en annan undermoduls gitkatalog"
 
 #, c-format
 msgid "clone of '%s' into submodule path '%s' failed"
-msgstr "misslyckades klona \"%s\" till undermodulsökvägen \"%s\""
+msgstr "misslyckades klona ”%s” till undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "directory not empty: '%s'"
-msgstr "katalogen inte tom: \"%s\""
+msgstr "katalogen inte tom: ”%s”"
 
 #, c-format
 msgid "could not get submodule directory for '%s'"
-msgstr "kunde inte få tag i undermodulkatalog för \"%s\""
+msgstr "kunde inte få tag i undermodulkatalog för ”%s”"
 
 msgid "alternative anchor for relative paths"
 msgstr "alternativa ankare för relativa sökvägar"
@@ -12214,15 +12429,14 @@ msgstr ""
 
 #, c-format
 msgid "Invalid update mode '%s' configured for submodule path '%s'"
-msgstr ""
-"Ogiltigt uppdateringsläge \"%s\" konfigurerat för undermodulsökväg \"%s\""
+msgstr "Ogiltigt uppdateringsläge ”%s” konfigurerat för undermodulsökväg ”%s”"
 
 #, c-format
 msgid "Submodule path '%s' not initialized"
-msgstr "Undermodulsökvägen \"%s\" har inte initierats"
+msgstr "Undermodulsökvägen ”%s” har inte initierats"
 
 msgid "Maybe you want to use 'update --init'?"
-msgstr "Kanske menade du att använda \"update --init\"?"
+msgstr "Kanske menade du att använda ”update --init”?"
 
 #, c-format
 msgid "Skipping unmerged submodule %s"
@@ -12230,63 +12444,67 @@ msgstr "Hoppar över ej sammanslagen undermodul %s"
 
 #, c-format
 msgid "Skipping submodule '%s'"
-msgstr "Hoppar över undermodulen \"%s\""
+msgstr "Hoppar över undermodulen ”%s”"
+
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "kan inte klona undermodulen ”%s” utan en URL"
 
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
-msgstr "Misslyckades klona \"%s\". Nytt försök planlagt"
+msgstr "Misslyckades klona ”%s”. Nytt försök planlagt"
 
 #, c-format
 msgid "Failed to clone '%s' a second time, aborting"
-msgstr "Misslyckades klona \"%s\" för andra gången, avbryter"
+msgstr "Misslyckades klona ”%s” för andra gången, avbryter"
 
 #, c-format
 msgid "Unable to checkout '%s' in submodule path '%s'"
-msgstr "Kan inte checka ut \"%s\" i undermodulsökvägen \"%s\""
+msgstr "Kan inte checka ut ”%s” i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Unable to rebase '%s' in submodule path '%s'"
-msgstr "Kan inte ombasera \"%s\" i undermodulsökvägen \"%s\""
+msgstr "Kan inte ombasera ”%s” i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Unable to merge '%s' in submodule path '%s'"
-msgstr "Kan inte slå ihop \"%s\" i undermodulsökvägen \"%s\""
+msgstr "Kan inte slå ihop ”%s” i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Execution of '%s %s' failed in submodule path '%s'"
-msgstr "Misslyckades köra \"%s %s\" i undermodulsökvägen \"%s\""
+msgstr "Misslyckades köra ”%s %s” i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Submodule path '%s': checked out '%s'\n"
-msgstr "Undermodulsökvägen \"%s\": checkade ut \"%s\"\n"
+msgstr "Undermodulsökvägen ”%s”: checkade ut ”%s”\n"
 
 #, c-format
 msgid "Submodule path '%s': rebased into '%s'\n"
-msgstr "Undermodulsökvägen \"%s\": ombaserade in i \"%s\"\n"
+msgstr "Undermodulsökvägen ”%s”: ombaserade in i ”%s”\n"
 
 #, c-format
 msgid "Submodule path '%s': merged in '%s'\n"
-msgstr "Undermodulsökvägen \"%s\": sammanslagen i \"%s\"\n"
+msgstr "Undermodulsökvägen ”%s”: sammanslagen i ”%s”\n"
 
 #, c-format
 msgid "Submodule path '%s': '%s %s'\n"
-msgstr "Undermodulsökvägen \"%s\": \"%s %s\"\n"
+msgstr "Undermodulsökvägen ”%s”: ”%s %s”\n"
 
 #, c-format
 msgid "Unable to fetch in submodule path '%s'; trying to directly fetch %s:"
-msgstr "Kan inte hämta i undermodulsökväg \"%s\"; försökte hämta %s direkt:"
+msgstr "Kan inte hämta i undermodulsökväg ”%s”; försökte hämta %s direkt:"
 
 #, c-format
 msgid ""
 "Fetched in submodule path '%s', but it did not contain %s. Direct fetching "
 "of that commit failed."
 msgstr ""
-"Hämtade i undermodulssökvägen \"%s\", men den innehöll inte %s. Direkt "
+"Hämtade i undermodulssökvägen ”%s”, men den innehöll inte %s. Direkt "
 "hämtning av incheckningen misslyckades."
 
 #, c-format
 msgid "could not initialize submodule at path '%s'"
-msgstr "kunde inte initiera undermodul i sökvägen \"%s\""
+msgstr "kunde inte initiera undermodul i sökvägen ”%s”"
 
 #, c-format
 msgid ""
@@ -12298,19 +12516,19 @@ msgstr ""
 
 #, c-format
 msgid "Unable to find current revision in submodule path '%s'"
-msgstr "Kan inte hitta aktuell revision i undermodulsökvägen \"%s\""
+msgstr "Kan inte hitta aktuell revision i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Unable to fetch in submodule path '%s'"
-msgstr "Kan inte hämta i undermodulsökväg \"%s\""
+msgstr "Kan inte hämta i undermodulsökväg ”%s”"
 
 #, c-format
 msgid "Unable to find %s revision in submodule path '%s'"
-msgstr "Kan inte hitta %s revision i undermodulsökvägen \"%s\""
+msgstr "Kan inte hitta %s revision i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Failed to recurse into submodule path '%s'"
-msgstr "Misslyckades rekursera in i undermodulsökväg \"%s\""
+msgstr "Misslyckades rekursera in i undermodulsökväg ”%s”"
 
 msgid "force checkout updates"
 msgstr "tvinga utcheckningsuppdateringar"
@@ -12327,17 +12545,14 @@ msgstr "traversera undermoduler rekursivt"
 msgid "don't fetch new objects from the remote site"
 msgstr "hämta inte nya objekt från fjärrplatsen"
 
-msgid "path into the working tree"
-msgstr "sökväg inuti arbetskatalogen"
-
 msgid "use the 'checkout' update strategy (default)"
-msgstr "använd uppdateringsstrategin \"checkout\" (utcheckning; förval)"
+msgstr "använd uppdateringsstrategin ”checkout” (utcheckning; förval)"
 
 msgid "use the 'merge' update strategy"
-msgstr "använd uppdateringsstrategin \"merge\" (sammanslagning)"
+msgstr "använd uppdateringsstrategin ”merge” (sammanslagning)"
 
 msgid "use the 'rebase' update strategy"
-msgstr "använd uppdateringsstrategin \"rebase\" (ombasering)"
+msgstr "använd uppdateringsstrategin ”rebase” (ombasering)"
 
 msgid "create a shallow clone truncated to the specified number of revisions"
 msgstr "skapa en grund klon trunkerad till angivet antal revisioner"
@@ -12365,27 +12580,12 @@ msgstr ""
 "[no-]recommend-shallow] [--reference <arkiv>] [--recursive] [--[no-]single-"
 "branch] [--] [<sökväg>...]"
 
-msgid "recurse into submodules"
-msgstr "rekursera ner i undermoduler"
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Misslyckades slå upp HEAD som giltig referens."
 
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<flaggor>] [<sökväg>...]"
 
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr "se om det är säkert att skriva till .gitmodules-filen"
-
-msgid "unset the config in the .gitmodules file"
-msgstr "ta bort konfigurationen från .gitmodules-filen"
-
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <namn> [<värde>]"
-
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <namn>"
-
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr "se till att .gitmodules finns i arbetskatalogen"
-
 msgid "suppress output for setting url of a submodule"
 msgstr "dölj utdata från inställning av url för undermodul"
 
@@ -12425,19 +12625,19 @@ msgstr ""
 
 #, c-format
 msgid "creating branch '%s'"
-msgstr "skapar grenen \"%s\""
+msgstr "skapar grenen ”%s”"
 
 #, c-format
 msgid "Adding existing repo at '%s' to the index\n"
-msgstr "Lägger till befintligt arkiv i \"%s\" i indexet\n"
+msgstr "Lägger till befintligt arkiv i ”%s” i indexet\n"
 
 #, c-format
 msgid "'%s' already exists and is not a valid git repo"
-msgstr "\"%s\" finns redan och är inte ett giltigt git-arkiv"
+msgstr "”%s” finns redan och är inte ett giltigt git-arkiv"
 
 #, c-format
 msgid "A git directory for '%s' is found locally with remote(s):\n"
-msgstr "En git-katalog för \"%s\" hittades lokalt med fjärr(ar):\n"
+msgstr "En git-katalog för ”%s” hittades lokalt med fjärr(ar):\n"
 
 #, c-format
 msgid ""
@@ -12451,38 +12651,41 @@ msgstr ""
 "Om du vill återanvända den lokala git-katalogen istället för att klona på "
 "nytt från\n"
 "  %s\n"
-"kan du använda flaggan \"--force\". Om den lokala git-katalogen inte är "
+"kan du använda flaggan ”--force”. Om den lokala git-katalogen inte är "
 "korrekt\n"
 "arkiv eller om du är osäker på vad det här betyder, välj ett annat namn med\n"
-"flaggan \"--name\"."
+"flaggan ”--name”."
 
 #, c-format
 msgid "Reactivating local git directory for submodule '%s'\n"
-msgstr "Aktiverar lokal git-katalog för undermodulen \"%s\" på nytt.\n"
+msgstr "Aktiverar lokal git-katalog för undermodulen ”%s” på nytt.\n"
 
 #, c-format
 msgid "unable to checkout submodule '%s'"
-msgstr "Kan inte checka ut undermodulen \"%s\""
+msgstr "Kan inte checka ut undermodulen ”%s”"
+
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr "se till att .gitmodules finns i arbetskatalogen"
 
 #, c-format
 msgid "Failed to add submodule '%s'"
-msgstr "Misslyckades lägga till undermodulen \"%s\""
+msgstr "Misslyckades lägga till undermodulen ”%s”"
 
 #, c-format
 msgid "Failed to register submodule '%s'"
-msgstr "Misslyckades registrera undermodulen \"%s\""
+msgstr "Misslyckades registrera undermodulen ”%s”"
 
 #, c-format
 msgid "'%s' already exists in the index"
-msgstr "\"%s\" finns redan i indexet"
+msgstr "”%s” finns redan i indexet"
 
 #, c-format
 msgid "'%s' already exists in the index and is not a submodule"
-msgstr "\"%s\" finns redan i indexet och är inte en undermodul"
+msgstr "”%s” finns redan i indexet och är inte en undermodul"
 
 #, c-format
 msgid "'%s' does not have a commit checked out"
-msgstr "\"%s\" har inte någon utcheckad incheckning"
+msgstr "”%s” har inte någon utcheckad incheckning"
 
 msgid "branch of repository to add as submodule"
 msgstr "gren från arkivet att lägga till som undermodul"
@@ -12508,25 +12711,23 @@ msgstr "Relativ sökväg kan endast användas från arbetskatalogens toppnivå"
 
 #, c-format
 msgid "repo URL: '%s' must be absolute or begin with ./|../"
-msgstr "arkiv-URL: \"%s\" måste vara absolut eller börja med ./|../"
+msgstr "arkiv-URL: ”%s” måste vara absolut eller börja med ./|../"
 
 #, c-format
 msgid "'%s' is not a valid submodule name"
-msgstr "\"%s\" är inte ett giltigt namn på undermodul"
+msgstr "”%s” är inte ett giltigt namn på undermodul"
 
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s stöder inte --super-prefix"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <kommando>"
 
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "\"%s\" är inte ett giltigt underkommando till submodule--helper"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <orsak>] <namn> <ref>"
 
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<flaggor>] <namn> [<ref>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <namn>"
 
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <namn>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <namn>"
 
 msgid "suppress error message for non-symbolic (detached) refs"
 msgstr ""
@@ -12538,6 +12739,9 @@ msgstr "ta bort symbolisk referens"
 msgid "shorten ref output"
 msgstr "förkorta ref-utdata"
 
+msgid "recursively dereference (default)"
+msgstr "avreferera rekursivt (standard)"
+
 msgid "reason"
 msgstr "skäl"
 
@@ -12545,36 +12749,37 @@ msgid "reason of the update"
 msgstr "skäl till uppdateringen"
 
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u <nyckel-id>] [-f] [-m <medd> | -F <fil>]\n"
-"        <taggnamn> [<huvud>]"
+"git tag [-a | -s | -u <nyckel-id>] [-f] [-m <medd> | -F <fil>] [-e]\n"
+"        <taggnamn> [<incheckning> | <objekt>]"
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d <taggnamn>..."
 
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[<antal>]] [--contains <incheckning>] [--no-contains "
-"<incheckning>] [--points-at <objekt>]\n"
-"        [--format=<format>] [--merged <incheckning>] [--no-merged "
-"<incheckning>] [<mönster>...]"
+"git tag [-n[<antal>]] -l [--contains <incheckning>] [--no-contains "
+"<incheckning>\n"
+"        [--points-at <objekt>] [--column[=<flaggor>] | --no-column]\n"
+"        [--create-reflog] [--sort=<nyckel>] [--format=<format>]\n"
+"        [--merged <incheckning>] [--no-merged <incheckning>] [<mönster>...]"
 
 msgid "git tag -v [--format=<format>] <tagname>..."
 msgstr "git tag -v [--format=<format>] <taggnamn>..."
 
 #, c-format
 msgid "tag '%s' not found."
-msgstr "taggen \"%s\" hittades inte."
+msgstr "taggen ”%s” hittades inte."
 
 #, c-format
 msgid "Deleted tag '%s' (was %s)\n"
-msgstr "Tog bort tagg \"%s\" (var %s)\n"
+msgstr "Tog bort tagg ”%s” (var %s)\n"
 
 #, c-format
 msgid ""
@@ -12586,7 +12791,7 @@ msgstr ""
 "\n"
 "Skriv ett meddelande för taggen:\n"
 "  %s\n"
-"Rader som inleds med \"%c\" ignoreras.\n"
+"Rader som inleds med ”%c” ignoreras.\n"
 
 #, c-format
 msgid ""
@@ -12599,7 +12804,7 @@ msgstr ""
 "\n"
 "Skriv ett meddelande för taggen:\n"
 "  %s\n"
-"Rader som inleds med \"%c\" kommer behållas; du kan själv ta bort dem om\n"
+"Rader som inleds med ”%c” kommer behållas; du kan själv ta bort dem om\n"
 "du vill.\n"
 
 msgid "unable to sign the tag"
@@ -12686,15 +12891,15 @@ msgstr "visa endast taggar för objektet"
 
 #, c-format
 msgid "the '%s' option is only allowed in list mode"
-msgstr "flaggan \"%s\" är endast tillåten i listläge"
+msgstr "flaggan ”%s” är endast tillåten i listläge"
 
 #, c-format
 msgid "'%s' is not a valid tag name."
-msgstr "\"%s\" är inte ett giltigt taggnamn."
+msgstr "”%s” är inte ett giltigt taggnamn."
 
 #, c-format
 msgid "tag '%s' already exists"
-msgstr "taggen \"%s\" finns redan"
+msgstr "taggen ”%s” finns redan"
 
 #, c-format
 msgid "Invalid cleanup mode %s"
@@ -12702,7 +12907,7 @@ msgstr "Felaktigt städningsläge %s"
 
 #, c-format
 msgid "Updated tag '%s' (was %s)\n"
-msgstr "Uppdaterad tagg \"%s\" (var %s)\n"
+msgstr "Uppdaterad tagg ”%s” (var %s)\n"
 
 msgid "pack exceeds maximum allowed size"
 msgstr "paket är större än tillåten maximal storlek"
@@ -12734,7 +12939,7 @@ msgstr "misslyckades ta bort katalogen %s"
 
 #, c-format
 msgid "Testing mtime in '%s' "
-msgstr "Testar mtime i \"%s\" "
+msgstr "Testar mtime i ”%s” "
 
 msgid "directory stat info does not change after adding a new file"
 msgstr "stat-informationen för en katalog ändras inte när nya filer läggs till"
@@ -12794,19 +12999,19 @@ msgid "add the specified entry to the index"
 msgstr "lägg till angiven post i indexet"
 
 msgid "mark files as \"not changing\""
-msgstr "markera filer som \"ändras inte\""
+msgstr "markera filer som ”ändras inte”"
 
 msgid "clear assumed-unchanged bit"
-msgstr "rensa \"assume-unchanged\"-biten"
+msgstr "rensa ”assume-unchanged”-biten"
 
 msgid "mark files as \"index-only\""
-msgstr "markera filer som \"endast index\""
+msgstr "markera filer som ”endast index”"
 
 msgid "clear skip-worktree bit"
-msgstr "töm \"skip-worktree\"-biten"
+msgstr "töm ”skip-worktree”-biten"
 
 msgid "do not touch index-only entries"
-msgstr "rör inte \"endast index\"-poster"
+msgstr "rör inte ”endast index”-poster"
 
 msgid "add to index only; do not add content to object database"
 msgstr "lägg endast till indexet; lägg inte till innehållet i objektdatabasen"
@@ -12841,6 +13046,9 @@ msgstr "(för porslin) glöm sparade olösta konflikter"
 msgid "write index in this format"
 msgstr "skriv index i detta format"
 
+msgid "report on-disk index format version"
+msgstr "rapportera formatversion för indexfilen på disk"
+
 msgid "enable or disable split index"
 msgstr "aktivera eller inaktivera delat index"
 
@@ -12860,10 +13068,18 @@ msgid "enable or disable file system monitor"
 msgstr "aktivera eller inaktivera filsystemsövervakning"
 
 msgid "mark files as fsmonitor valid"
-msgstr "markera filer som \"fsmonitor valid\""
+msgstr "markera filer som ”fsmonitor valid”"
 
 msgid "clear fsmonitor valid bit"
-msgstr "töm \"fsmonitor valid\"-bit"
+msgstr "töm ”fsmonitor valid”-bit"
+
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: vad %d, sattes till %d"
 
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
@@ -12898,7 +13114,7 @@ msgstr ""
 
 #, c-format
 msgid "Untracked cache enabled for '%s'"
-msgstr "Ospårad cache är aktiverad för \"%s\""
+msgstr "Ospårad cache är aktiverad för ”%s”"
 
 msgid "core.fsmonitor is unset; set it if you really want to enable fsmonitor"
 msgstr "core.fsmonitor inte satt; sätt om du verkligen vill aktivera fsmonitor"
@@ -12938,8 +13154,12 @@ msgstr "läs uppdateringar från standard in"
 msgid "update the info files from scratch"
 msgstr "uppdatera informationsfilerna från grunden"
 
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<flaggor>] <katalog>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <katalog>"
 
 msgid "quit after a single request/response exchange"
 msgstr "avsluta omedelbart efter första anrop/svar-utväxling"
@@ -12953,8 +13173,8 @@ msgstr "testa inte <katalog>/.git/ om <katalog> inte är en Git-katalog"
 msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "avbryt överföringen efter <n> sekunders inaktivitet"
 
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <incheckning>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | --verbose] [--raw] <incheckning>..."
 
 msgid "print commit contents"
 msgstr "visa innehåll för incheckning"
@@ -12962,8 +13182,9 @@ msgstr "visa innehåll för incheckning"
 msgid "print raw gpg status output"
 msgstr "visa råa gpg-statusdata"
 
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <paket>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr ""
+"git verify-pack [-v | --verbose] [-s | --stat-only] [--] <paket>.idx..."
 
 msgid "verbose"
 msgstr "pratsam"
@@ -12971,35 +13192,71 @@ msgstr "pratsam"
 msgid "show statistics only"
 msgstr "visa endast statistik"
 
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<format>] <tagg>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tagg>..."
 
 msgid "print tag contents"
 msgstr "visa innehåll för tag"
 
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<flaggor>] <sökväg> [<incheckning-igt>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <sträng>]]\n"
+"                 [--orphan] [(-b | -B) <ny-gren>] <sökväg> [<incheckning-"
+"igt>]"
 
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<flaggor>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
 
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<flaggor>] <sökväg>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <sträng>] <arbetskatalog>"
 
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <arbetskatalog> <ny-sökväg>"
 
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<flaggor>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <utgår>]"
 
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<flaggor>] <arbetskatalog>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <arbetskatalog>"
 
 msgid "git worktree repair [<path>...]"
 msgstr "git worktree repair [<sökväg>...]"
 
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <sökväg>"
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <arbetskatalog>"
+
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Ingen möjlig källgren, använder ”--orphan”"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Om meningen var att skapa en arbetskatalog från en ny föräldrals\n"
+"gren (gren utan incheckningar) för det här arkivet kan du göra\n"
+"det med flaggan --orphan:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Om meningen var att skapa en arbetskatalog från en ny föräldrals\n"
+"gren (gren utan incheckningar) för det här arkivet kan du göra\n"
+"det med flaggan --orphan:\n"
+"\n"
+"    git worktree add --orphan %s\n"
 
 #, c-format
 msgid "Removing %s/%s: %s"
@@ -13013,68 +13270,92 @@ msgstr "låt tid gå ut för arbetskataloger äldre än <tid>"
 
 #, c-format
 msgid "'%s' already exists"
-msgstr "\"%s\" finns redan"
+msgstr "”%s” finns redan"
 
 #, c-format
 msgid "unusable worktree destination '%s'"
-msgstr "oanvändbar mål för arbetskatalog \"%s\""
+msgstr "oanvändbar mål för arbetskatalog ”%s”"
 
 #, c-format
 msgid ""
 "'%s' is a missing but locked worktree;\n"
 "use '%s -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"
 msgstr ""
-"\"%s\" är en saknad men låst arbetskatalog;\n"
-"använd \"%s -f -f\" för att överstyra, eller \"unlock\" och \"prune\" eller "
-"\"remove\" för att rensa"
+"”%s” är en saknad men låst arbetskatalog;\n"
+"använd ”%s -f -f” för att överstyra, eller ”unlock” och ”prune” eller "
+"”remove” för att rensa"
 
 #, c-format
 msgid ""
 "'%s' is a missing but already registered worktree;\n"
 "use '%s -f' to override, or 'prune' or 'remove' to clear"
 msgstr ""
-"\"%s\" är en saknad men redan registrerad arbetskatalog;\n"
-"använd \"%s -f\" för att överstyra, eller \"prune\" eller \"remove\" för att "
-"rensa"
+"”%s” är en saknad men redan registrerad arbetskatalog;\n"
+"använd ”%s -f” för att överstyra, eller ”prune” eller ”remove” för att rensa"
 
 #, c-format
 msgid "failed to copy '%s' to '%s'; sparse-checkout may not work correctly"
 msgstr ""
-"misslyckades kopiera \"%s\" till \"%s\"; sparse-checkout kanske inte kommer "
-"att fungera korrekt"
+"misslyckades kopiera ”%s” till ”%s”; sparse-checkout kanske inte kommer att "
+"fungera korrekt"
 
 #, c-format
 msgid "failed to copy worktree config from '%s' to '%s'"
-msgstr ""
-"misslyckades kopiera arbetskatalogkonfiguration från \"%s\" till \"%s\""
+msgstr "misslyckades kopiera arbetskatalogkonfiguration från ”%s” till ”%s”"
 
 #, c-format
 msgid "failed to unset '%s' in '%s'"
-msgstr "misslyckades slå av \"%s\" i \"%s\""
+msgstr "misslyckades slå av ”%s” i ”%s”"
 
 #, c-format
 msgid "could not create directory of '%s'"
-msgstr "kunde inte skapa katalogen \"%s\""
+msgstr "kunde inte skapa katalogen ”%s”"
 
 msgid "initializing"
 msgstr "initierar"
 
 #, c-format
 msgid "Preparing worktree (new branch '%s')"
-msgstr "Förbereder arbetskatalog (ny gren \"%s\")"
+msgstr "Förbereder arbetskatalog (ny gren ”%s”)"
 
 #, c-format
 msgid "Preparing worktree (resetting branch '%s'; was at %s)"
-msgstr "Förbereder arbetskatalog (återställer gren \"%s\"; var på %s)"
+msgstr "Förbereder arbetskatalog (återställer gren ”%s”; var på %s)"
 
 #, c-format
 msgid "Preparing worktree (checking out '%s')"
-msgstr "Förbereder arbetskatalog (checkar ut \"%s\")"
+msgstr "Förbereder arbetskatalog (checkar ut ”%s”)"
+
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "onåbar: felaktig referens: %s"
 
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Förbereder arbetskatalog (frånkopplat HEAD %s)"
 
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD pekar på en ogiltig (eller övergiven) referens.\n"
+"HEAD-sökväg: ”%s”\n"
+"HEAD-innehåll: ”%s”"
+
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"Ingen lokal eller fjärr-referens finns trots att åtminstone en fjärr\n"
+"finns, avslutar; använd ”add -f” för att överstyra eller hämta från en fjärr "
+"först"
+
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "”%s” och ”%s” kan inte användas samtidigt"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
 "checka ut <gren> även om den redan är utcheckad i en annan arbetskatalog"
@@ -13085,6 +13366,9 @@ msgstr "skapa en ny gren"
 msgid "create or reset a branch"
 msgstr "skapa eller återställ en gren"
 
+msgid "create unborn/orphaned branch"
+msgstr "skapa en ofödd/övergiven gren"
+
 msgid "populate the new working tree"
 msgstr "befolka den nya arbetskatalogen"
 
@@ -13102,7 +13386,14 @@ msgstr "försök träffa namn på ny gren mot en fjärrspårande gren"
 
 #, c-format
 msgid "options '%s', '%s', and '%s' cannot be used together"
-msgstr "flaggorna \"%s\", \"%s\" och \"%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%s”, ”%s” och ”%s” kan inte användas samtidigt"
+
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "flaggorna ”%s” och ”%s” kan inte användas samtidigt"
+
+msgid "<commit-ish>"
+msgstr "<incheckning-igt>"
 
 msgid "added with --lock"
 msgstr "lagt till med --lock"
@@ -13114,30 +13405,29 @@ msgid "show extended annotations and reasons, if available"
 msgstr "visa utökade annoteringar och grunder, om tillgängliga"
 
 msgid "add 'prunable' annotation to worktrees older than <time>"
-msgstr ""
-"lägg till \"prunable\"-annoteringar till arbetskataloger äldre än <tid>"
+msgstr "lägg till ”prunable”-annoteringar till arbetskataloger äldre än <tid>"
 
 msgid "terminate records with a NUL character"
 msgstr "avsluta poster med NUL-tecken"
 
 #, c-format
 msgid "'%s' is not a working tree"
-msgstr "\"%s\" är inte en arbetskatalog"
+msgstr "”%s” är inte en arbetskatalog"
 
 msgid "The main working tree cannot be locked or unlocked"
 msgstr "Huvudarbetskatalogen kan inte låsas eller låsas upp"
 
 #, c-format
 msgid "'%s' is already locked, reason: %s"
-msgstr "\"%s\" är redan låst, orsak: %s"
+msgstr "”%s” är redan låst, orsak: %s"
 
 #, c-format
 msgid "'%s' is already locked"
-msgstr "\"%s\" är redan låst"
+msgstr "”%s” är redan låst"
 
 #, c-format
 msgid "'%s' is not locked"
-msgstr "\"%s\" är inte låst"
+msgstr "”%s” är inte låst"
 
 msgid "working trees containing submodules cannot be moved or removed"
 msgstr "arbetskataloger med undermoduler kan inte flyttas eller tas bort"
@@ -13147,11 +13437,11 @@ msgstr "tvinga flyttning även om arbetskatalogen är smutsig eller låst"
 
 #, c-format
 msgid "'%s' is a main working tree"
-msgstr "\"%s\" är inte en huvudarbetskatalog"
+msgstr "”%s” är inte en huvudarbetskatalog"
 
 #, c-format
 msgid "could not figure out destination name from '%s'"
-msgstr "kunde inte lista ut målnamn från \"%s\""
+msgstr "kunde inte lista ut målnamn från ”%s”"
 
 #, c-format
 msgid ""
@@ -13159,14 +13449,14 @@ msgid ""
 "use 'move -f -f' to override or unlock first"
 msgstr ""
 "kan inte flytta en låst arbetskatalog, orsak till lås: %s\n"
-"använd \"move -f -f\" för att överstyra, eller lås upp först"
+"använd ”move -f -f” för att överstyra, eller lås upp först"
 
 msgid ""
 "cannot move a locked working tree;\n"
 "use 'move -f -f' to override or unlock first"
 msgstr ""
 "kan inte flytta en låst arbetskatalog;\n"
-"använd \"move -f -f\" för att överstyra, eller lås upp först"
+"använd ”move -f -f” för att överstyra, eller lås upp först"
 
 #, c-format
 msgid "validation failed, cannot move working tree: %s"
@@ -13174,21 +13464,21 @@ msgstr "kontroll misslyckades, kan inte flytta arbetskatalog: %s"
 
 #, c-format
 msgid "failed to move '%s' to '%s'"
-msgstr "misslyckades flytta \"%s\" till \"%s\""
+msgstr "misslyckades flytta ”%s” till ”%s”"
 
 #, c-format
 msgid "failed to run 'git status' on '%s'"
-msgstr "misslyckades köra \"git status\" på \"%s\""
+msgstr "misslyckades köra ”git status” på ”%s”"
 
 #, c-format
 msgid "'%s' contains modified or untracked files, use --force to delete it"
 msgstr ""
-"\"%s\" innehåller ändrade eller ospårade filer, använd --force för att ta "
-"bort det"
+"”%s” innehåller ändrade eller ospårade filer, använd --force för att ta bort "
+"det"
 
 #, c-format
 msgid "failed to run 'git status' on '%s', code %d"
-msgstr "misslyckades köra \"git status\" på \"%s\", kod %d"
+msgstr "misslyckades köra ”git status” på ”%s”, kod %d"
 
 msgid "force removal even if worktree is dirty or locked"
 msgstr "tvinga ta bort även om arbetskatalogen är smutsig eller låst"
@@ -13199,14 +13489,14 @@ msgid ""
 "use 'remove -f -f' to override or unlock first"
 msgstr ""
 "kan inte ta bort en låst arbetskatalog, orsak till låset: %s\n"
-"använd \"remove -f -f\" för att överstyra, eller lås upp först"
+"använd ”remove -f -f” för att överstyra, eller lås upp först"
 
 msgid ""
 "cannot remove a locked working tree;\n"
 "use 'remove -f -f' to override or unlock first"
 msgstr ""
 "kan inte ta bort en låst arbetskatalog;\n"
-"använd \"remove -f -f\" för att överstyra, eller lås upp först"
+"använd ”remove -f -f” för att överstyra, eller lås upp först"
 
 #, c-format
 msgid "validation failed, cannot remove working tree: %s"
@@ -13235,31 +13525,66 @@ msgstr "endast användbart vid felsökning"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "core.fsyncMethod = batch stöds inte på denna plattform"
 
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "kunde inte tolka listnyckeln %s med värdet ”%s”"
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "buntlistan på ”%s” har inget läge"
+
 msgid "failed to create temporary file"
 msgstr "misslyckades skapa temporär fil"
 
-msgid "insufficient capabilities"
-msgstr "otillräckliga kapabiliteter"
+msgid "insufficient capabilities"
+msgstr "otillräckliga kapabiliteter"
+
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "filen hämtad från ”%s” är inte en bunt"
+
+msgid "failed to store maximum creation token"
+msgstr "misslyckades lagra maximal skaparsymbol"
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "okänt buntlägre från URI:en ”%s”"
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "överskred buntens URI-rekursionsgräns (%d)"
 
 #, c-format
 msgid "failed to download bundle from URI '%s'"
-msgstr "kunde inte hämta bunt från URI:en \"%s\""
+msgstr "kunde inte hämta bunt från URI:en ”%s”"
 
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "filen på URI \"%s\" är inte en bunt"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "filen på URI:en ”%s” är inte en bunt eller buntlista"
 
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "misslyckades packa upp bunten från URI:en \"%s\""
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: okänt argument: ”%s”"
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: förväntade ”flush” efter argument"
+
+msgid "bundle-uri: got an empty line"
+msgstr "bunt-uri: fick en tom rad"
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bunt-uri: raden är inte på formen ”nyckel=värde”"
+
+msgid "bundle-uri: line has empty key or value"
+msgstr "bunt-uri: raden har tom nyckel eller värde"
 
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
-msgstr "okänd hashningsalgoritm för bunt: \"%s\""
+msgstr "okänd hashningsalgoritm för bunt: ”%s”"
 
 #, c-format
 msgid "unknown capability '%s'"
-msgstr "okänd kapabilitet \"%s\""
+msgstr "okänd kapabilitet ”%s”"
 
 #, c-format
 msgid "'%s' does not look like a v2 or v3 bundle file"
@@ -13275,6 +13600,13 @@ msgstr "Arkivet saknar dessa nödvändiga incheckningar:"
 msgid "need a repository to verify a bundle"
 msgstr "behöver ett arkiv för att bekräfta en bunt."
 
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"några förutsättande incheckningar finns i objektkatalogen, men är inte "
+"anslutna i arkivets historik"
+
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -13290,6 +13622,14 @@ msgid_plural "The bundle requires these %<PRIuMAX> refs:"
 msgstr[0] "Bunten kräver denna referens:"
 msgstr[1] "Bunten kräver dessa %<PRIuMAX> referenser:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Bunten använder denna hashningsalgoritm: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Bunten använder detta filter. %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "kan inte duplicera bunthandtag"
 
@@ -13301,7 +13641,7 @@ msgstr "pack-objects misslyckades"
 
 #, c-format
 msgid "ref '%s' is excluded by the rev-list options"
-msgstr "referensen \"%s\" exkluderas av argumenten till rev-list"
+msgstr "referensen ”%s” exkluderas av argumenten till rev-list"
 
 #, c-format
 msgid "unsupported bundle version %d"
@@ -13316,7 +13656,7 @@ msgstr "Vägrar skapa en tom bunt."
 
 #, c-format
 msgid "cannot create '%s'"
-msgstr "kan inte skapa \"%s\""
+msgstr "kan inte skapa ”%s”"
 
 msgid "index-pack died"
 msgstr "index-pack dog"
@@ -13324,6 +13664,10 @@ msgstr "index-pack dog"
 msgid "terminating chunk id appears earlier than expected"
 msgstr "avslutande stycke-id förekommer tidigare än förväntat"
 
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "stycke-id %<PRIx32> är inte %d-byte-justerad"
+
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "felaktigt stycke-offset %<PRIx64> och %<PRIx64>"
@@ -13376,9 +13720,8 @@ msgstr "Samla information från användaren för att sända en felrapport"
 msgid "Move objects and refs by archive"
 msgstr "Flytta objekt och referenser efter arkiv"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Visa innehåller eller typ- och storleksinformation för objekt i arkivet"
+msgid "Provide contents or details of repository objects"
+msgstr "Visa innehåll eller detaljer för objekt i arkivet"
 
 msgid "Display gitattributes information"
 msgstr "Visa information från gitattributes"
@@ -13515,8 +13858,8 @@ msgstr "Visa rader som motsvarar mönster"
 msgid "A portable graphical interface to Git"
 msgstr "Ett portabelt grafiskt gränssnitt för Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "Beräkna objekt-id och möjligen skapa en blob från en fil"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "Beräkna objekt-ID och möjligen skapa ett objekt från en fil"
 
 msgid "Display help information about Git"
 msgstr "Visa hjälpinformation om Git"
@@ -13703,7 +14046,7 @@ msgid "Restricted login shell for Git-only SSH access"
 msgstr "Begränsat inloggningsskal för SSH-åtkomst till bara Git"
 
 msgid "Summarize 'git log' output"
-msgstr "Summera \"git log\"-utdata"
+msgstr "Summera ”git log”-utdata"
 
 msgid "Show various types of objects"
 msgstr "Visa olika sorters objekt"
@@ -13783,8 +14126,8 @@ msgstr "Kontrollera GPG-signaturer i taggar"
 msgid "Display version information about Git"
 msgstr "Visa versionsinformation om Git"
 
-msgid "Show logs with difference each commit introduces"
-msgstr "Visa loggar med differenser varje incheckning introducerar"
+msgid "Show logs with differences each commit introduces"
+msgstr "Visa loggar med ändringarna varje incheckning introducerar"
 
 msgid "Manage multiple working trees"
 msgstr "Hantera ytterligare arbetskataloger"
@@ -13822,7 +14165,7 @@ msgstr "Filformat för bunt"
 msgid "Chunk-based file formats"
 msgstr "Styckebaserade filformat"
 
-msgid "Git commit graph format"
+msgid "Git commit-graph format"
 msgstr "Format för Git-incheckningsgraf"
 
 msgid "Git index format"
@@ -13919,12 +14262,19 @@ msgstr "incheckningsgraffilen är för liten för att innehålla %u stycken"
 msgid "commit-graph has no base graphs chunk"
 msgstr "incheckningsgrafen har inga bas-graf-stycken"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "incheckningsgrafens bas-graf-stycken är för små"
+
 msgid "commit-graph chain does not match"
 msgstr "incheckningsgrafens kedja stämmer inte"
 
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "antalet incheckningar i basgrafen för högt: %<PRIuMAX>"
+
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
-msgstr "ogiltig incheckingsgrafkedja: rad \"%s\" är inte ett hash-värde"
+msgstr "ogiltig incheckingsgrafkedja: rad ”%s” är inte ett hash-värde"
 
 msgid "unable to find all commit-graph files"
 msgstr "kan inte hitta alla incheckingsgraffiler"
@@ -13939,6 +14289,9 @@ msgstr "kunde inte hitta incheckningen %s"
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "incheckningsgraf kräver spillgenerationsdata, men har ingen"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr "incheckningsgrafens spillgenerationsdata är för liten"
+
 msgid "Loading known commits in commit graph"
 msgstr "Läser in kända incheckningar i incheckningsgraf"
 
@@ -13988,7 +14341,7 @@ msgstr "kan inte skapa temporärt graflager"
 
 #, c-format
 msgid "unable to adjust shared permissions for '%s'"
-msgstr "kan inte justera delade behörigheter för \"%s\""
+msgstr "kan inte justera delade behörigheter för ”%s”"
 
 #, c-format
 msgid "Writing out commit graph in %d pass"
@@ -14005,6 +14358,14 @@ msgstr "kunde inte byta namn på bas-incheckingsgraffilen"
 msgid "failed to rename temporary commit-graph file"
 msgstr "kunde inte byta namn på temporär incheckningsgraffil"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "kan inte slå ihop grafer med %<PRIuMAX>, %<PRIuMAX> incheckningar"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "kan inte slå ihop grafen %s, med för många incheckningar: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Söker sammanslagna incheckningar"
 
@@ -14013,7 +14374,7 @@ msgstr "Slår ihop incheckningsgraf"
 
 msgid "attempting to write a commit-graph, but 'core.commitGraph' is disabled"
 msgstr ""
-"försöker skriva en incheckningsgraf, men \"core.commitGraph\" är inaktiverad"
+"försöker skriva en incheckningsgraf, men ”core.commitGraph” är inaktiverad"
 
 msgid "too many commits to write graph"
 msgstr "för många incheckningar för att skriva graf"
@@ -14036,9 +14397,6 @@ msgstr ""
 msgid "failed to parse commit %s from commit-graph"
 msgstr "kunde inte tolka incheckning %s från incheckningsgraf"
 
-msgid "Verifying commits in commit graph"
-msgstr "Bekräftar incheckningar i incheckningsgrafen"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14061,20 +14419,6 @@ msgid "commit-graph parent list for commit %s terminates early"
 msgstr ""
 "incheckningsgrafens föräldralista för incheckningen %s avslutas för tidigt"
 
-#, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"incheckningsgrafen har generationsnummer noll för incheckningen %s, men icke-"
-"noll på annan plats"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"incheckningsgrafen har generationsnummer skilt från noll för incheckningen "
-"%s, men noll på annan plats"
-
 #, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr ""
@@ -14087,6 +14431,17 @@ msgstr ""
 "incheckningsdatumet för incheckningen %s i incheckningsgrafen är %<PRIuMAX> !"
 "= %<PRIuMAX>"
 
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"incheckningsgrafen har generationsnummer som både är noll och icke-noll "
+"(dvs, incheckningarna ”%s” och ”%s”)"
+
+msgid "Verifying commits in commit graph"
+msgstr "Bekräftar incheckningar i incheckningsgrafen"
+
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s är inte en incheckning!"
@@ -14104,11 +14459,16 @@ msgstr ""
 "Stöd för <GIT_DIR>/info/grafts avråds från och\n"
 "kommer tas bort i en framtida version av Git.\n"
 "\n"
-"Använd \"git replace --convert-graft-file\"\n"
+"Använd ”git replace --convert-graft-file”\n"
 "för att omvandla grafts till ersättningsreferenser.\n"
 "\n"
 "Slå av detta meddelande genom att skriva\n"
-"\"git config advice.graftFileDeprecated false\""
+"”git config advice.graftFileDeprecated false”"
+
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"incheckningen %s finns i incheckningsgrafen, men inte i objektdatabasen"
 
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
@@ -14145,36 +14505,40 @@ msgstr "ingen libc-information tillgänglig\n"
 
 #, c-format
 msgid "could not determine free disk size for '%s'"
-msgstr "kunde inte ta reda på ledigt diskutrymme för \"%s\""
+msgstr "kunde inte ta reda på ledigt diskutrymme för ”%s”"
 
 #, c-format
 msgid "could not get info for '%s'"
-msgstr "kunde inte hämta info för \"%s\""
+msgstr "kunde inte hämta info för ”%s”"
 
 #, c-format
 msgid "[GLE %ld] health thread could not open '%ls'"
-msgstr "[GLE %ld] hälsotråden kunde inte öppna \"%ls\""
+msgstr "[GLE %ld] hälsotråden kunde inte öppna ”%ls”"
 
 #, c-format
 msgid "[GLE %ld] health thread getting BHFI for '%ls'"
-msgstr "[GLE %ld] hälsotråden hämtar BHFI för \"%ls\""
+msgstr "[GLE %ld] hälsotråden hämtar BHFI för ”%ls”"
 
 #, c-format
 msgid "could not convert to wide characters: '%s'"
-msgstr "kunde inte konvertera till breda tecken: \"%s\""
+msgstr "kunde inte konvertera till breda tecken: ”%s”"
 
 #, c-format
 msgid "BHFI changed '%ls'"
-msgstr "BHFI ändrade \"%ls\""
+msgstr "BHFI ändrade ”%ls”"
 
 #, c-format
 msgid "unhandled case in 'has_worktree_moved': %d"
-msgstr "ohanterat fall i \"has_worktree_moved\": %d"
+msgstr "ohanterat fall i ”has_worktree_moved”: %d"
 
 #, c-format
 msgid "health thread wait failed [GLE %ld]"
 msgstr "misslyckades vänta på hälsotråden [GLE %ld]"
 
+#, c-format
+msgid "Invalid path: %s"
+msgstr "ogiltig sökväg: %s"
+
 msgid "Unable to create FSEventStream."
 msgstr "kunde inte skapa FSEventStream."
 
@@ -14183,35 +14547,51 @@ msgstr "Misslyckades starta FSEventStream:en"
 
 #, c-format
 msgid "[GLE %ld] could not convert path to UTF-8: '%.*ls'"
-msgstr "[GLE %ld] kunde inte konvertera sökväg till UTF-8: \"%.*ls\""
+msgstr "[GLE %ld] kunde inte konvertera sökväg till UTF-8: ”%.*ls”"
 
 #, c-format
 msgid "[GLE %ld] could not watch '%s'"
-msgstr "[GLE %ld] kunde inte övervaka \"%s\""
+msgstr "[GLE %ld] kunde inte övervaka ”%s”"
 
 #, c-format
 msgid "[GLE %ld] could not get longname of '%s'"
-msgstr "[GLE %ld] kunde inte hämta långt namn för \"%s\""
+msgstr "[GLE %ld] kunde inte hämta långt namn för ”%s”"
 
 #, c-format
 msgid "ReadDirectoryChangedW failed on '%s' [GLE %ld]"
-msgstr "ReadDirectoryChangedW misslyckades på \"%s\" [GLE %ld]"
+msgstr "ReadDirectoryChangedW misslyckades på ”%s” [GLE %ld]"
 
 #, c-format
 msgid "GetOverlappedResult failed on '%s' [GLE %ld]"
-msgstr "GetOverlappedResult misslyckades på \"%s\" [GLE %ld]"
+msgstr "GetOverlappedResult misslyckades på ”%s” [GLE %ld]"
 
 #, c-format
 msgid "could not read directory changes [GLE %ld]"
 msgstr "kunde inte läsa katalogändringar [GLE %ld]"
 
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "opendir('%s') misslyckades"
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "lstat('%s') misslyckades"
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "strbuf_readlink('%s') misslyckades"
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "closedir('%s') misslyckades"
+
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
-msgstr "[GLE %ld] kunde inte öppna \"%ls\" för läsning"
+msgstr "[GLE %ld] kunde inte öppna ”%ls” för läsning"
 
 #, c-format
 msgid "[GLE %ld] unable to get protocol information for '%ls'"
-msgstr "[GLE %ld] kunde inte hämta protokollinformation för \"%ls\""
+msgstr "[GLE %ld] kunde inte hämta protokollinformation för ”%ls”"
 
 #, c-format
 msgid "failed to copy SID (%ld)"
@@ -14219,7 +14599,7 @@ msgstr "misslyckades kopiera SID (%ld)"
 
 #, c-format
 msgid "failed to get owner for '%s' (%ld)"
-msgstr "misslyckades hämta ägaren för \"%s\" (%ld)"
+msgstr "misslyckades hämta ägaren för ”%s” (%ld)"
 
 msgid "memory exhausted"
 msgstr "minnet slut"
@@ -14286,15 +14666,15 @@ msgstr "kunde inte läsa IPC-svar"
 
 #, c-format
 msgid "could not start accept_thread '%s'"
-msgstr "kunde inte ta status \"accept_thread\" \"%s\""
+msgstr "kunde inte ta status ”accept_thread” ”%s”"
 
 #, c-format
 msgid "could not start worker[0] for '%s'"
-msgstr "kunde inte starta \"worker[0]\" för \"%s\""
+msgstr "kunde inte starta ”worker[0]” för ”%s”"
 
 #, c-format
 msgid "ConnectNamedPipe failed for '%s' (%lu)"
-msgstr "ConnectNamedPipe misslyckades för \"%s\" (%lu)"
+msgstr "ConnectNamedPipe misslyckades för ”%s” (%lu)"
 
 #, c-format
 msgid "could not create fd from pipe for '%s'"
@@ -14302,14 +14682,14 @@ msgstr "kunde inte skapa filhandtag från rör för %s"
 
 #, c-format
 msgid "could not start thread[0] for '%s'"
-msgstr "kunde inte starta thread[0] för \"%s\""
+msgstr "kunde inte starta thread[0] för ”%s”"
 
 #, c-format
 msgid "wait for hEvent failed for '%s'"
-msgstr "misslyckades vänta på hEvent för \"%s\""
+msgstr "misslyckades vänta på hEvent för ”%s”"
 
 msgid "cannot resume in the background, please use 'fg' to resume"
-msgstr "kan inte fortsätta i bakgrunden, använd \"fg\" för att återuppta"
+msgstr "kan inte fortsätta i bakgrunden, använd ”fg” för att återuppta"
 
 msgid "cannot restore terminal settings"
 msgstr "kan inte återställa terminalinställningar"
@@ -14330,7 +14710,7 @@ msgstr ""
 
 #, c-format
 msgid "could not expand include path '%s'"
-msgstr "kunde inte expandera inkluderingssökväg \"%s\""
+msgstr "kunde inte expandera inkluderingssökväg ”%s”"
 
 msgid "relative config includes must come from files"
 msgstr "relativa konfigureringsinkluderingar måste komma från filer"
@@ -14351,11 +14731,11 @@ msgstr "felaktigt konfigurationsformat: %s"
 
 #, c-format
 msgid "missing environment variable name for configuration '%.*s'"
-msgstr "miljövariabelnamn saknas för konfigurationen \"%.*s\""
+msgstr "miljövariabelnamn saknas för konfigurationen ”%.*s”"
 
 #, c-format
 msgid "missing environment variable '%s' for configuration '%.*s'"
-msgstr "miljövariabeln \"%s\" saknas för konfigurationen \"%.*s\""
+msgstr "miljövariabeln ”%s” saknas för konfigurationen ”%.*s”"
 
 #, c-format
 msgid "key does not contain a section: %s"
@@ -14432,38 +14812,35 @@ msgstr "ogiltig enhet"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s': %s"
-msgstr "felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\": %s"
+msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s”: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in blob %s: %s"
-msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i blob:en %s: %s"
+msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i blob:en %s: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in file %s: %s"
-msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i filen %s: %s"
+msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i filen %s: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in standard input: %s"
 msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i standard in: %s"
+"felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i standard in: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in submodule-blob %s: %s"
 msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i undermodul-blob:"
-"en %s: %s"
+"felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i undermodul-blob:en "
+"%s: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in command line %s: %s"
 msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i kommandoraden "
-"%s: %s"
+"felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i kommandoraden %s: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in %s: %s"
-msgstr "felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i %s: %s"
+msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i %s: %s"
 
 #, c-format
 msgid "invalid value for variable %s"
@@ -14471,19 +14848,19 @@ msgstr "ogiltigt värde för variabeln %s"
 
 #, c-format
 msgid "ignoring unknown core.fsync component '%s'"
-msgstr "ignorerar okänd core.fsync-komponent \"%s\""
+msgstr "ignorerar okänd core.fsync-komponent ”%s”"
 
 #, c-format
 msgid "bad boolean config value '%s' for '%s'"
-msgstr "felaktigt booleskt konfigurationsvärde \"%s\" för \"%s\""
+msgstr "felaktigt booleskt konfigurationsvärde ”%s” för ”%s”"
 
 #, c-format
 msgid "failed to expand user dir in: '%s'"
-msgstr "misslyckades expandera användarkatalog i: \"%s\""
+msgstr "misslyckades expandera användarkatalog i: ”%s”"
 
 #, c-format
 msgid "'%s' for '%s' is not a valid timestamp"
-msgstr "\"%s\" för \"%s\" är inte en giltig tidsstämpel"
+msgstr "”%s” för ”%s” är inte en giltig tidsstämpel"
 
 #, c-format
 msgid "abbrev length out of range: %d"
@@ -14493,12 +14870,12 @@ msgstr "förkortningslängd utanför intervallet: %d"
 msgid "bad zlib compression level %d"
 msgstr "felaktigt zlib-komprimeringsgrad %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar kan bara vara ett tecken"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar kan bara vara ett ASCII-tecken"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
-msgstr "ignorerar okänt core.fsyncMethod-värde \"%s\""
+msgstr "ignorerar okänt core.fsyncMethod-värde ”%s”"
 
 msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead"
 msgstr "core.fsyncObjectFiles avråds från; använd core.fsync istället"
@@ -14520,19 +14897,15 @@ msgstr "måste vara en av nothing, matching, simple, upstream eller current"
 
 #, c-format
 msgid "unable to load config blob object '%s'"
-msgstr "kunde inte läsa konfigurerings-blobobjektet \"%s\""
+msgstr "kunde inte läsa konfigurerings-blobobjektet ”%s”"
 
 #, c-format
 msgid "reference '%s' does not point to a blob"
-msgstr "referensen \"%s\" pekar inte på en blob"
+msgstr "referensen ”%s” pekar inte på en blob"
 
 #, c-format
 msgid "unable to resolve config blob '%s'"
-msgstr "kan inte slå upp konfigurerings-bloben \"%s\""
-
-#, c-format
-msgid "failed to parse %s"
-msgstr "kunde inte tolka %s"
+msgstr "kan inte slå upp konfigurerings-bloben ”%s”"
 
 msgid "unable to parse command-line config"
 msgstr "kan inte tolka kommandoradskonfiguration"
@@ -14542,24 +14915,24 @@ msgstr "okänt fel uppstod vid läsning av konfigurationsfilerna"
 
 #, c-format
 msgid "Invalid %s: '%s'"
-msgstr "Felaktigt %s: \"%s\""
+msgstr "Felaktigt %s: ”%s”"
 
 #, c-format
 msgid "splitIndex.maxPercentChange value '%d' should be between 0 and 100"
 msgstr ""
-"värdet \"%d\" för splitIndex.maxPercentChange borde vara mellan 0 och 100"
+"värdet ”%d” för splitIndex.maxPercentChange borde vara mellan 0 och 100"
 
 #, c-format
 msgid "unable to parse '%s' from command-line config"
-msgstr "kunde inte tolka värdet \"%s\" från kommandoradskonfiguration"
+msgstr "kunde inte tolka värdet ”%s” från kommandoradskonfiguration"
 
 #, c-format
 msgid "bad config variable '%s' in file '%s' at line %d"
-msgstr "felaktig konfigurationsvariabel \"%s\" i filen \"%s\" på rad %d"
+msgstr "felaktig konfigurationsvariabel ”%s” i filen ”%s” på rad %d"
 
 #, c-format
 msgid "invalid section name '%s'"
-msgstr "felaktigt sektionsnamn \"%s\""
+msgstr "felaktigt sektionsnamn ”%s”"
 
 #, c-format
 msgid "%s has multiple values"
@@ -14567,7 +14940,7 @@ msgstr "%s har flera värden"
 
 #, c-format
 msgid "failed to write new configuration file %s"
-msgstr "kan inte skriva nya konfigurationsfilen \"%s\""
+msgstr "kan inte skriva nya konfigurationsfilen ”%s”"
 
 #, c-format
 msgid "could not lock config file %s"
@@ -14579,7 +14952,7 @@ msgstr "öppnar %s"
 
 #, c-format
 msgid "invalid config file %s"
-msgstr "ogiltig konfigurationsfil: \"%s\""
+msgstr "ogiltig konfigurationsfil: ”%s”"
 
 #, c-format
 msgid "fstat on %s failed"
@@ -14587,7 +14960,7 @@ msgstr "fstat misslyckades på %s"
 
 #, c-format
 msgid "unable to mmap '%s'%s"
-msgstr "kunde inte utföra mmap på \"%s\"%s"
+msgstr "kunde inte utföra mmap på ”%s”%s"
 
 #, c-format
 msgid "chmod on %s failed"
@@ -14599,15 +14972,19 @@ msgstr "kunde inte skriva konfigurationsfilen %s"
 
 #, c-format
 msgid "could not set '%s' to '%s'"
-msgstr "kunde inte ställa in \"%s\" till \"%s\""
+msgstr "kunde inte ställa in ”%s” till ”%s”"
 
 #, c-format
 msgid "invalid section name: %s"
 msgstr "felaktigt namn på stycke: %s"
 
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "vägrar arbeta med för långa rader i ”%s” på rad %<PRIuMAX>"
+
 #, c-format
 msgid "missing value for '%s'"
-msgstr "värde saknas för \"%s\""
+msgstr "värde saknas för ”%s”"
 
 msgid "the remote end hung up upon initial contact"
 msgstr "fjärren lade på vid inledande kontakt"
@@ -14625,25 +15002,25 @@ msgstr ""
 
 #, c-format
 msgid "server doesn't support '%s'"
-msgstr "Servern stöder inte \"%s\""
+msgstr "Servern stöder inte ”%s”"
 
 #, c-format
 msgid "server doesn't support feature '%s'"
-msgstr "servern stöder inte funktionen \"%s\""
+msgstr "servern stöder inte funktionen ”%s”"
 
 msgid "expected flush after capabilities"
-msgstr "förväntade \"flush\" efter förmågor"
+msgstr "förväntade ”flush” efter förmågor"
 
 #, c-format
 msgid "ignoring capabilities after first line '%s'"
-msgstr "ignorerar förmågor efter första raden \"%s\""
+msgstr "ignorerar förmågor efter första raden ”%s”"
 
 msgid "protocol error: unexpected capabilities^{}"
 msgstr "protokollfel: förväntade inte capabilities^{}"
 
 #, c-format
 msgid "protocol error: expected shallow sha-1, got '%s'"
-msgstr "protokollfel: förväntade \"shallow sha-1\" fick \"%s\""
+msgstr "protokollfel: förväntade ”shallow sha-1” fick ”%s”"
 
 msgid "repository on the other end cannot be shallow"
 msgstr "arkivet på andra sidan kan inte vara grunt"
@@ -14653,25 +15030,32 @@ msgstr "ogiltigt paket"
 
 #, c-format
 msgid "protocol error: unexpected '%s'"
-msgstr "protokollfel: förväntade inte \"%s\""
+msgstr "protokollfel: förväntade inte ”%s”"
 
 #, c-format
 msgid "unknown object format '%s' specified by server"
-msgstr "okänt objektformat \"%s\" angavs av servern"
+msgstr "okänt objektformat ”%s” angavs av servern"
 
 #, c-format
-msgid "invalid ls-refs response: %s"
-msgstr "ogiltigt svar på ls-refs: %s"
+msgid "error on bundle-uri response line %d: %s"
+msgstr "fel på bundle-uri-svar rad %d: %s"
 
-msgid "expected flush after ref listing"
-msgstr "förväntade \"flush\" efter ref-listan"
+msgid "expected flush after bundle-uri listing"
+msgstr "förväntade ”flush” efter bundle-uri-listan"
 
 msgid "expected response end packet after ref listing"
 msgstr "förväntade svarsavslutningspaket efter ref-listan"
 
+#, c-format
+msgid "invalid ls-refs response: %s"
+msgstr "ogiltigt svar på ls-refs: %s"
+
+msgid "expected flush after ref listing"
+msgstr "förväntade ”flush” efter ref-listan"
+
 #, c-format
 msgid "protocol '%s' is not supported"
-msgstr "protokollet \"%s\" stöds inte"
+msgstr "protokollet ”%s” stöds inte"
 
 msgid "unable to set SO_KEEPALIVE on socket"
 msgstr "kunde inte sätta SO_KEEPALIVE på uttaget"
@@ -14715,40 +15099,40 @@ msgstr "okänd port %s"
 
 #, c-format
 msgid "strange hostname '%s' blocked"
-msgstr "konstigt värdnamn \"%s\" blockerat"
+msgstr "konstigt värdnamn ”%s” blockerat"
 
 #, c-format
 msgid "strange port '%s' blocked"
-msgstr "konstig port \"%s\" blockerad"
+msgstr "konstig port ”%s” blockerad"
 
 #, c-format
 msgid "cannot start proxy %s"
 msgstr "kan inte starta mellanserver (proxy) %s"
 
 msgid "no path specified; see 'git help pull' for valid url syntax"
-msgstr "ingen sökväg angavs; se \"git help pull\" för giltig URL-syntax"
+msgstr "ingen sökväg angavs; se ”git help pull” för giltig URL-syntax"
 
 msgid "newline is forbidden in git:// hosts and repo paths"
 msgstr "radbrytningar är förbjudna i git://-värdnamn och arkivsökvägar"
 
 msgid "ssh variant 'simple' does not support -4"
-msgstr "ssh-varianten \"simple\" stöder inte -4"
+msgstr "ssh-varianten ”simple” stöder inte -4"
 
 msgid "ssh variant 'simple' does not support -6"
-msgstr "ssh-varianten \"simple\" stöder inte -6"
+msgstr "ssh-varianten ”simple” stöder inte -6"
 
 msgid "ssh variant 'simple' does not support setting port"
-msgstr "ssh-varianten \"simple\" stöder inte val av port"
+msgstr "ssh-varianten ”simple” stöder inte val av port"
 
 #, c-format
 msgid "strange pathname '%s' blocked"
-msgstr "konstigt sökvägsnamn \"%s\" blockerat"
+msgstr "konstigt sökvägsnamn ”%s” blockerat"
 
 msgid "unable to fork"
 msgstr "kunde inte grena (fork)"
 
 msgid "Could not run 'git rev-list'"
-msgstr "Kunde inte köra \"git rev-list\""
+msgstr "Kunde inte köra ”git rev-list”"
 
 msgid "failed write to rev-list"
 msgstr "kunde inte skriva till rev-list"
@@ -14769,7 +15153,7 @@ msgid ""
 "in the working copy of '%s', CRLF will be replaced by LF the next time Git "
 "touches it"
 msgstr ""
-"CRLF i arbetskopian av \"%s\" kommer ersättas med LF nästa gång Git rör den"
+"CRLF i arbetskopian av ”%s” kommer ersättas med LF nästa gång Git rör den"
 
 #, c-format
 msgid "LF would be replaced by CRLF in %s"
@@ -14780,60 +15164,59 @@ msgid ""
 "in the working copy of '%s', LF will be replaced by CRLF the next time Git "
 "touches it"
 msgstr ""
-"LF i arbetskopian av \"%s\" kommer ersättas med CRLF nästa gång Git rör den"
+"LF i arbetskopian av ”%s” kommer ersättas med CRLF nästa gång Git rör den"
 
 #, c-format
 msgid "BOM is prohibited in '%s' if encoded as %s"
-msgstr "BOM är förbjudet i \"%s\" om kodat som %s"
+msgstr "BOM är förbjudet i ”%s” om kodat som %s"
 
 #, c-format
 msgid ""
 "The file '%s' contains a byte order mark (BOM). Please use UTF-%.*s as "
 "working-tree-encoding."
 msgstr ""
-"Filen \"%s\" innehåller byte order mark (BOM). Använd UTF-%.*s som "
+"Filen ”%s” innehåller byte order mark (BOM). Använd UTF-%.*s som "
 "teckenkodning i arbetskatalogen."
 
 #, c-format
 msgid "BOM is required in '%s' if encoded as %s"
-msgstr "BOM krävs om \"%s\" kodas som %s"
+msgstr "BOM krävs om ”%s” kodas som %s"
 
 #, c-format
 msgid ""
 "The file '%s' is missing a byte order mark (BOM). Please use UTF-%sBE or UTF-"
 "%sLE (depending on the byte order) as working-tree-encoding."
 msgstr ""
-"Filen \"%s\" saknar byte order mark (BOM). Använd UTF-%sBE eller UTF-%sLE "
+"Filen ”%s” saknar byte order mark (BOM). Använd UTF-%sBE eller UTF-%sLE "
 "(beroende på byteordning) som teckenkodning i arbetskatalogen."
 
 #, c-format
 msgid "failed to encode '%s' from %s to %s"
-msgstr "misslyckades omkoda \"%s\" från %s till %s"
+msgstr "misslyckades omkoda ”%s” från %s till %s"
 
 #, c-format
 msgid "encoding '%s' from %s to %s and back is not the same"
-msgstr ""
-"omkodning av \"%s\" från %s till %s och tillbaka ger inte samma resultat"
+msgstr "omkodning av ”%s” från %s till %s och tillbaka ger inte samma resultat"
 
 #, c-format
 msgid "cannot fork to run external filter '%s'"
-msgstr "kan inte grena (fork) för att köra externt filter \"%s\""
+msgstr "kan inte grena (fork) för att köra externt filter ”%s”"
 
 #, c-format
 msgid "cannot feed the input to external filter '%s'"
-msgstr "kunde inte skicka indata till externt filter \"%s\""
+msgstr "kunde inte skicka indata till externt filter ”%s”"
 
 #, c-format
 msgid "external filter '%s' failed %d"
-msgstr "externt filter \"%s\" misslyckades %d"
+msgstr "externt filter ”%s” misslyckades %d"
 
 #, c-format
 msgid "read from external filter '%s' failed"
-msgstr "läsning från externt filter \"%s\" misslyckades"
+msgstr "läsning från externt filter ”%s” misslyckades"
 
 #, c-format
 msgid "external filter '%s' failed"
-msgstr "externt filter \"%s\" misslyckades"
+msgstr "externt filter ”%s” misslyckades"
 
 msgid "unexpected filter type"
 msgstr "oväntad filtertyp"
@@ -14846,19 +15229,19 @@ msgid ""
 "external filter '%s' is not available anymore although not all paths have "
 "been filtered"
 msgstr ""
-"externt filter \"%s\" är inte längre tillgängligt trots att alla sökvägar "
-"inte har filtrerats"
+"externt filter ”%s” är inte längre tillgängligt trots att alla sökvägar inte "
+"har filtrerats"
 
 msgid "true/false are no valid working-tree-encodings"
 msgstr "true/false är inte giltig teckenkodning för arbetskatalogen"
 
 #, c-format
 msgid "%s: clean filter '%s' failed"
-msgstr "%s: \"clean\"-filtret \"%s\" misslyckades"
+msgstr "%s: ”clean”-filtret ”%s” misslyckades"
 
 #, c-format
 msgid "%s: smudge filter %s failed"
-msgstr "%s: \"smudge\"-filtret \"%s\" misslyckades"
+msgstr "%s: ”smudge”-filtret ”%s” misslyckades"
 
 #, c-format
 msgid "skipping credential lookup for key: credential.%s"
@@ -14949,7 +15332,7 @@ msgstr "felaktigt trädobjektet %s"
 
 #, c-format
 msgid "failed to load island regex for '%s': %s"
-msgstr "kunde inte hämta ö-regex för \"%s\": %s"
+msgstr "kunde inte hämta ö-regex för ”%s”: %s"
 
 #, c-format
 msgid "island regex from config has too many capture groups (max=%d)"
@@ -14961,26 +15344,26 @@ msgstr "Markerade %d öar, klar.\n"
 
 #, c-format
 msgid "invalid --%s value '%s'"
-msgstr "ogiltigt värde för --%s: \"%s\""
+msgstr "ogiltigt värde för --%s: ”%s”"
 
 #, c-format
 msgid "could not archive missing directory '%s'"
-msgstr "kunde inte arkivera saknad katalog \"%s\""
+msgstr "kunde inte arkivera saknad katalog ”%s”"
 
 #, c-format
 msgid "could not open directory '%s'"
-msgstr "kunde inte öppna katalogen \"%s\""
+msgstr "kunde inte öppna katalogen ”%s”"
 
 #, c-format
 msgid "skipping '%s', which is neither file nor directory"
-msgstr "hoppar över \"%s\", som varken är en fil eller en katalog"
+msgstr "hoppar över ”%s”, som varken är en fil eller en katalog"
 
 msgid "could not duplicate stdout"
 msgstr "kunde inte duplicera standard ut"
 
 #, c-format
 msgid "could not add directory '%s' to archiver"
-msgstr "kunde inte lägga till katalogen \"%s\" till arkiveraren"
+msgstr "kunde inte lägga till katalogen ”%s” till arkiveraren"
 
 msgid "failed to write archive"
 msgstr "misslyckades skriva arkiv"
@@ -14988,9 +15371,6 @@ msgstr "misslyckades skriva arkiv"
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base fungerar inte med intervall"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base fungerar bara med incheckningar"
-
 msgid "unable to get HEAD"
 msgstr "kan inte hämta HEAD"
 
@@ -15000,6 +15380,12 @@ msgstr "ingen sammanslagningsbas hittades"
 msgid "multiple merge bases found"
 msgstr "flera sammanslagningsbaser hittades"
 
+msgid "cannot compare stdin to a directory"
+msgstr "kan inte jämföra standard in med en katalog"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "kan inte jämföra ett namngivet rör med en katalog"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<flaggor>] <sökväg> <sökväg>"
 
@@ -15012,27 +15398,26 @@ msgstr ""
 
 #, c-format
 msgid "  Failed to parse dirstat cut-off percentage '%s'\n"
-msgstr "  Misslyckades tolka dirstat-avskärningsprocentandel \"%s\"\n"
+msgstr "  Misslyckades tolka dirstat-avskärningsprocentandel ”%s”\n"
 
 #, c-format
 msgid "  Unknown dirstat parameter '%s'\n"
-msgstr "  Okänd dirstat-parameter \"%s\"\n"
+msgstr "  Okänd dirstat-parameter ”%s”\n"
 
 msgid ""
 "color moved setting must be one of 'no', 'default', 'blocks', 'zebra', "
 "'dimmed-zebra', 'plain'"
 msgstr ""
-"färginställningen för flyttade block måste vara en av \"no\", \"default\", "
-"\"blocks\", \"zebra\", \"dimmed-zebra\", \"plain\""
+"färginställningen för flyttade block måste vara en av ”no”, ”default”, "
+"”blocks”, ”zebra”, ”dimmed-zebra”, ”plain”"
 
 #, c-format
 msgid ""
 "unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', "
 "'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"
 msgstr ""
-"okänt läge \"%s\" för color-moved-ws, möjliga värden är \"ignore-space-change"
-"\", \"ignore-space-at-eol\", \"ignore-all-space\", \"allow-indentation-change"
-"\""
+"okänt läge ”%s” för color-moved-ws, möjliga värden är ”ignore-space-change”, "
+"”ignore-space-at-eol”, ”ignore-all-space”, ”allow-indentation-change”"
 
 msgid ""
 "color-moved-ws: allow-indentation-change cannot be combined with other "
@@ -15043,40 +15428,42 @@ msgstr ""
 
 #, c-format
 msgid "Unknown value for 'diff.submodule' config variable: '%s'"
-msgstr "Okänt värde för konfigurationsvariabeln \"diff.submodule\": \"%s\""
+msgstr "Okänt värde för konfigurationsvariabeln ”diff.submodule”: ”%s”"
 
 #, c-format
 msgid ""
 "Found errors in 'diff.dirstat' config variable:\n"
 "%s"
 msgstr ""
-"Hittade fel i konfigurationsvariabeln \"diff.dirstat\":\n"
+"Hittade fel i konfigurationsvariabeln ”diff.dirstat”:\n"
 "%s"
 
 #, c-format
 msgid "external diff died, stopping at %s"
 msgstr "extern diff dog, stannar vid %s"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow kräver exakt en sökvägsangivelse"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "sökvägs-magi stöds inte av --follow: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
-msgstr ""
-"flaggorna \"%s\", \"%s\", \"%s\" och \"%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%s”, ”%s”, ”%s” och ”%s” kan inte användas samtidigt"
 
 #, c-format
 msgid "options '%s' and '%s' cannot be used together, use '%s' with '%s'"
 msgstr ""
-"flaggorna \"%s\" och \"%s\" kan inte användas samtidigt, använd \"%s\" med "
-"\"%s\""
+"flaggorna ”%s” och ”%s” kan inte användas samtidigt, använd ”%s” med ”%s”"
 
 #, c-format
 msgid ""
 "options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"
 msgstr ""
-"flaggorna \"%s\" och  \"%s\" kan inte användas samtidigt, använd \"%s\" med "
-"\"%s\" och \"%s\""
-
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow kräver exakt en sökvägsangivelse"
+"flaggorna ”%s” och  ”%s” kan inte användas samtidigt, använd ”%s” med ”%s” "
+"och ”%s”"
 
 #, c-format
 msgid "invalid --stat value: %s"
@@ -15096,7 +15483,7 @@ msgstr ""
 
 #, c-format
 msgid "unknown change class '%c' in --diff-filter=%s"
-msgstr "okänd ändringsklass \"%c\" i --diff-filter=%s"
+msgstr "okänd ändringsklass ”%c” i --diff-filter=%s"
 
 #, c-format
 msgid "unknown value after ws-error-highlight=%.*s"
@@ -15104,7 +15491,7 @@ msgstr "okänt värde efter ws-error-highlight=%.*s"
 
 #, c-format
 msgid "unable to resolve '%s'"
-msgstr "kunde inte slå upp \"%s\""
+msgstr "kunde inte slå upp ”%s”"
 
 #, c-format
 msgid "%s expects <n>/<m> form"
@@ -15112,7 +15499,7 @@ msgstr "%s förväntar formen <n>/<m>"
 
 #, c-format
 msgid "%s expects a character, got '%s'"
-msgstr "%s förväntar ett tecken, fick \"%s\""
+msgstr "%s förväntar ett tecken, fick ”%s”"
 
 #, c-format
 msgid "bad --color-moved argument: %s"
@@ -15120,14 +15507,13 @@ msgstr "felaktigt argument till --color-moved: %s"
 
 #, c-format
 msgid "invalid mode '%s' in --color-moved-ws"
-msgstr "ogiltigt läge %s\" i --color-moved-ws"
+msgstr "ogiltigt läge %s i --color-moved-ws"
 
 msgid ""
 "option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
 "\"histogram\""
 msgstr ""
-"flaggan diff-algorithm godtar\"myers\", \"minimal\", \"patience\" och "
-"\"histogram\""
+"flaggan diff-algorithm godtar ”myers”, ”minimal”, ”patience” och ”histogram”"
 
 #, c-format
 msgid "invalid argument to %s"
@@ -15135,11 +15521,11 @@ msgstr "ogiltigt argument för %s"
 
 #, c-format
 msgid "invalid regex given to -I: '%s'"
-msgstr "ogiltigt reguljärt uttryck angavs för -I: \"%s\""
+msgstr "ogiltigt reguljärt uttryck angavs för -I: ”%s”"
 
 #, c-format
 msgid "failed to parse --submodule option parameter: '%s'"
-msgstr "misslyckades tolka argument till flaggan --submodule: \"%s\""
+msgstr "misslyckades tolka argument till flaggan --submodule: ”%s”"
 
 #, c-format
 msgid "bad --word-diff argument: %s"
@@ -15161,10 +15547,10 @@ msgid "generate the diff in raw format"
 msgstr "generera diff i råformat"
 
 msgid "synonym for '-p --raw'"
-msgstr "synonym till \"-p --raw\""
+msgstr "synonym till ”-p --raw”"
 
 msgid "synonym for '-p --stat'"
-msgstr "synonym till \"-p --stat\""
+msgstr "synonym till ”-p --stat”"
 
 msgid "machine friendly --stat"
 msgstr "maskinläsbar --stat"
@@ -15230,7 +15616,7 @@ msgstr "skapa en binärdiff som kan appliceras"
 
 msgid "show full pre- and post-image object names on the \"index\" lines"
 msgstr ""
-"visa fullständiga objektnamn i \"index\"-rader för läget både före och efter"
+"visa fullständiga objektnamn i ”index”-rader för läget både före och efter"
 
 msgid "show colored diff"
 msgstr "visa färgad diff"
@@ -15242,8 +15628,8 @@ msgid ""
 "highlight whitespace errors in the 'context', 'old' or 'new' lines in the "
 "diff"
 msgstr ""
-"ljusmarkera blankstegsfel i \"context\" (sammanhang), \"old\" (gamla) eller "
-"\"new\" (nya) rader i diffen"
+"ljusmarkera blankstegsfel i ”context” (sammanhang), ”old” (gamla) eller "
+"”new” (nya) rader i diffen"
 
 msgid ""
 "do not munge pathnames and use NULs as output field terminators in --raw or "
@@ -15256,10 +15642,10 @@ msgid "<prefix>"
 msgstr "<prefix>"
 
 msgid "show the given source prefix instead of \"a/\""
-msgstr "visa givet källprefix istället för \"a/\""
+msgstr "visa givet källprefix istället för ”a/”"
 
 msgid "show the given destination prefix instead of \"b/\""
-msgstr "visa givet målprefix istället för \"b/\""
+msgstr "visa givet målprefix istället för ”b/”"
 
 msgid "prepend an additional prefix to every line of output"
 msgstr "lägg till ytterligare prefix på alla rader i utdata"
@@ -15267,6 +15653,9 @@ msgstr "lägg till ytterligare prefix på alla rader i utdata"
 msgid "do not show any source or destination prefix"
 msgstr "visa inte käll- eller målprefix"
 
+msgid "use default prefixes a/ and b/"
+msgstr "använd standardprefixen a/ och b/"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "visa sammnhang mellan diff-stycken upp till angivet antal rader"
 
@@ -15274,13 +15663,13 @@ msgid "<char>"
 msgstr "<tecken>"
 
 msgid "specify the character to indicate a new line instead of '+'"
-msgstr "ange tecken för att ange ny rad istället för \"+\""
+msgstr "ange tecken för att ange ny rad istället för ”+”"
 
 msgid "specify the character to indicate an old line instead of '-'"
-msgstr "ange tecken för att ange gammal rad istället för \"-\""
+msgstr "ange tecken för att ange gammal rad istället för ”-”"
 
 msgid "specify the character to indicate a context instead of ' '"
-msgstr "ange tecken för att ange sammanhang istället för \" \""
+msgstr "ange tecken för att ange sammanhang istället för ” ”"
 
 msgid "Diff rename options"
 msgstr "Diff-namnbytesflaggor"
@@ -15350,10 +15739,10 @@ msgid "heuristic to shift diff hunk boundaries for easy reading"
 msgstr "heuristik för att flytta diff-gränser för lättare läsning"
 
 msgid "generate diff using the \"patience diff\" algorithm"
-msgstr "skapa diffar med algoritmen \"patience diff\""
+msgstr "skapa diffar med algoritmen ”patience diff”"
 
 msgid "generate diff using the \"histogram diff\" algorithm"
-msgstr "skapa diffar med algoritmen \"histogram diff\""
+msgstr "skapa diffar med algoritmen ”histogram diff”"
 
 msgid "<algorithm>"
 msgstr "<algoritm>"
@@ -15365,7 +15754,7 @@ msgid "<text>"
 msgstr "<text>"
 
 msgid "generate diff using the \"anchored diff\" algorithm"
-msgstr "skapa diffar med algoritmen \"anchored diff\""
+msgstr "skapa diffar med algoritmen ”anchored diff”"
 
 msgid "<mode>"
 msgstr "<läge>"
@@ -15424,10 +15813,10 @@ msgid "specify how differences in submodules are shown"
 msgstr "ange hur ändringar i undermoduler visas"
 
 msgid "hide 'git add -N' entries from the index"
-msgstr "dölj \"git add -N\"-poster från indexet"
+msgstr "dölj ”git add -N”-poster från indexet"
 
 msgid "treat 'git add -N' entries as real in the index"
-msgstr "tolka \"git add -N\"-poster som äkta i indexet"
+msgstr "tolka ”git add -N”-poster som äkta i indexet"
 
 msgid "<string>"
 msgstr "<sträng>"
@@ -15497,18 +15886,18 @@ msgstr ""
 
 #, c-format
 msgid "failed to read orderfile '%s'"
-msgstr "kunde inte läsa orderfilen \"%s\""
+msgstr "kunde inte läsa orderfilen ”%s”"
 
 msgid "Performing inexact rename detection"
 msgstr "Utför onöjaktig namnbytesdetektering"
 
 #, c-format
 msgid "No such path '%s' in the diff"
-msgstr "Sökvägen \"%s\" finns inte i diffen"
+msgstr "Sökvägen ”%s” finns inte i diffen"
 
 #, c-format
 msgid "pathspec '%s' did not match any file(s) known to git"
-msgstr "sökvägsangivelsen \"%s\" motsvarade inte några av git kända filer"
+msgstr "sökvägsangivelsen ”%s” motsvarade inte några av git kända filer"
 
 #, c-format
 msgid "unrecognized pattern: '%s'"
@@ -15521,7 +15910,7 @@ msgstr "okänt negativt mönster: %s"
 #, c-format
 msgid "your sparse-checkout file may have issues: pattern '%s' is repeated"
 msgstr ""
-"din \"sparse-checkout\"-fil kan ha problem: mönstret \"%s\" förekommer flera "
+"din ”sparse-checkout”-fil kan ha problem: mönstret ”%s” förekommer flera "
 "gånger"
 
 msgid "disabling cone pattern matching"
@@ -15554,22 +15943,30 @@ msgstr "kunde inte skapa kataloger för %s"
 
 #, c-format
 msgid "could not migrate git directory from '%s' to '%s'"
-msgstr "kunde inte migrera git-katalog från \"%s\" till \"%s\""
+msgstr "kunde inte migrera git-katalog från ”%s” till ”%s”"
 
 #, c-format
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "tips: Väntar på att textredigeringsprogrammet ska stänga filen...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "kunde inte skriva till ”%s”"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "kunde inte redigera ”%s”"
+
 msgid "Filtering content"
 msgstr "Filtrerar innehåll"
 
 #, c-format
 msgid "could not stat file '%s'"
-msgstr "kunde inte ta status på filen \"%s\""
+msgstr "kunde inte ta status på filen ”%s”"
 
 #, c-format
 msgid "bad git namespace path \"%s\""
-msgstr "felaktig git-namnrymdssökväg \"%s\""
+msgstr "felaktig git-namnrymdssökväg ”%s”"
 
 #, c-format
 msgid "too many args to run %s"
@@ -15586,7 +15983,7 @@ msgstr "git fetch-pack: förväntade ACK/NAK, fick flush-paket"
 
 #, c-format
 msgid "git fetch-pack: expected ACK/NAK, got '%s'"
-msgstr "git fetch-pack: förväntade ACK/NAK, fick \"%s\""
+msgstr "git fetch-pack: förväntade ACK/NAK, fick ”%s”"
 
 msgid "unable to write to remote"
 msgstr "kunde inte skriva till fjärren"
@@ -15596,11 +15993,11 @@ msgstr "Servern stöder filter"
 
 #, c-format
 msgid "invalid shallow line: %s"
-msgstr "ogiltig \"shallow\"-rad: %s"
+msgstr "ogiltig ”shallow”-rad: %s"
 
 #, c-format
 msgid "invalid unshallow line: %s"
-msgstr "ogiltig \"unshallow\"-rad: %s"
+msgstr "ogiltig ”unshallow”-rad: %s"
 
 #, c-format
 msgid "object not found: %s"
@@ -15612,7 +16009,7 @@ msgstr "fel i objekt: %s"
 
 #, c-format
 msgid "no shallow found: %s"
-msgstr "ingen \"shallow\" hittades: %s"
+msgstr "ingen ”shallow” hittades: %s"
 
 #, c-format
 msgid "expected shallow/unshallow, got %s"
@@ -15699,7 +16096,7 @@ msgstr "omaka algoritmer: klient %s; server %s"
 
 #, c-format
 msgid "the server does not support algorithm '%s'"
-msgstr "servern stöder inte algoritmen \"%s\""
+msgstr "servern stöder inte algoritmen ”%s”"
 
 msgid "Server does not support shallow requests"
 msgstr "Servern stöder inte grunda förfrågningar"
@@ -15709,15 +16106,15 @@ msgstr "kunde inte skriva anrop till fjärren"
 
 #, c-format
 msgid "expected '%s', received '%s'"
-msgstr "förväntade \"%s\", tog emot \"%s\""
+msgstr "förväntade ”%s”, tog emot ”%s”"
 
 #, c-format
 msgid "expected '%s'"
-msgstr "förväntade \"%s\""
+msgstr "förväntade ”%s”"
 
 #, c-format
 msgid "unexpected acknowledgment line: '%s'"
-msgstr "oväntad bekräftelserad: \"%s\""
+msgstr "oväntad bekräftelserad: ”%s”"
 
 #, c-format
 msgid "error processing acks: %d"
@@ -15728,19 +16125,18 @@ msgstr "fel vid hantering av bekräftelser: %d"
 #.
 #, c-format
 msgid "expected packfile to be sent after '%s'"
-msgstr "väntade att paketfil skulle sändas efter \"%s\""
+msgstr "väntade att paketfil skulle sändas efter ”%s”"
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
 #.
 #, c-format
 msgid "expected no other sections to be sent after no '%s'"
-msgstr ""
-"väntade inte att några ytterligare sektioner skulle sändas efter \"%s\""
+msgstr "väntade inte att några ytterligare sektioner skulle sändas efter ”%s”"
 
 #, c-format
 msgid "error processing shallow info: %d"
-msgstr "fel vid hantering av grund (\"shallow\") info: %d"
+msgstr "fel vid hantering av grund (”shallow”) info: %d"
 
 #, c-format
 msgid "expected wanted-ref, got '%s'"
@@ -15748,7 +16144,7 @@ msgstr "förväntade wanted-ref, fick %s"
 
 #, c-format
 msgid "unexpected wanted-ref: '%s'"
-msgstr "oväntad wanted-ref: \"%s\""
+msgstr "oväntad wanted-ref: ”%s”"
 
 #, c-format
 msgid "error processing wanted refs: %d"
@@ -15761,7 +16157,7 @@ msgid "no matching remote head"
 msgstr "inget motsvarande fjärrhuvud"
 
 msgid "unexpected 'ready' from remote"
-msgstr "oväntat \"ready\" från fjärr"
+msgstr "oväntat ”ready” från fjärr"
 
 #, c-format
 msgid "no such remote ref %s"
@@ -15773,41 +16169,42 @@ msgstr "Servern tillåter inte förfrågan om ej tillkännagivet objekt %s"
 
 #, c-format
 msgid "fsmonitor_ipc__send_query: invalid path '%s'"
-msgstr "fsmonitor_ipc__send_query: ogilitg sökväg \"%s\""
+msgstr "fsmonitor_ipc__send_query: ogilitg sökväg ”%s”"
 
 #, c-format
 msgid "fsmonitor_ipc__send_query: unspecified error on '%s'"
-msgstr "fsmonitor_ipc__send_query: ospecificerat fel på \"%s\""
+msgstr "fsmonitor_ipc__send_query: ospecificerat fel på ”%s”"
 
 msgid "fsmonitor--daemon is not running"
 msgstr "fsmonitor--daemon kör inte"
 
 #, c-format
 msgid "could not send '%s' command to fsmonitor--daemon"
-msgstr "kunde inte sända kommandot \"%s\" till fsmonitor--daemon"
+msgstr "kunde inte sända kommandot ”%s” till fsmonitor--daemon"
 
 #, c-format
 msgid "bare repository '%s' is incompatible with fsmonitor"
-msgstr "naket arkiv \"%s\" är inkompatibelt med fsmonitor"
+msgstr "naket arkiv ”%s” är inkompatibelt med fsmonitor"
 
 #, c-format
 msgid "repository '%s' is incompatible with fsmonitor due to errors"
-msgstr "arkivet \"%s\" är inkompatibelt med fsmonitor på grund av fel"
+msgstr "arkivet ”%s” är inkompatibelt med fsmonitor på grund av fel"
 
 #, c-format
 msgid "remote repository '%s' is incompatible with fsmonitor"
-msgstr "fjärrarkivet \"%s\" är inkompatibelt med fsmonitor"
+msgstr "fjärrarkivet ”%s” är inkompatibelt med fsmonitor"
 
 #, c-format
 msgid "virtual repository '%s' is incompatible with fsmonitor"
-msgstr "det virtuella arkivet \"%s\" är inkompatibelt med fsmonitor"
+msgstr "det virtuella arkivet ”%s” är inkompatibelt med fsmonitor"
 
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
-"arkivet \"%s\" är inkompatibelt med fsmonitor på grund av avsaknad av Unix-"
-"uttag"
+"uttagskatalogen ”%s” är inkompatibelt med fsmonitor på grund av avsaknad av "
+"Unix-uttag"
 
 msgid ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
@@ -15815,8 +16212,7 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 "git [-v | --version] [-h |--help] [-C <sökväg>] [-c <namn>=<värde>]\n"
 "           [--exec-path[=<sökväg>]] [--html-path] [--man-path] [--info-"
@@ -15824,8 +16220,7 @@ msgstr ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<sökväg>] [--work-tree=<sökväg>] [--namespace=<namn>]\n"
-"           [--super-prefix=<sökväg>] [--config-env=<namn>=<miljövar>]\n"
-"           <kommando> [<flaggor>]"
+"           [--config-env=<namn>=<miljövar>] <kommando> [<flaggor>]"
 
 msgid ""
 "'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -15833,27 +16228,23 @@ msgid ""
 "to read about a specific subcommand or concept.\n"
 "See 'git help git' for an overview of the system."
 msgstr ""
-"\"git help -a\" och \"git help -g\" visar tillgängliga underkommandon och\n"
-"några konceptvägledningar. Se \"git help <kommando>\" eller \"git help\n"
-"<koncept>\" för att läsa mer om specifika underkommandon och koncept.\n"
-"See \"git help git\" för en översikt över systemet."
+"”git help -a” och ”git help -g” visar tillgängliga underkommandon och\n"
+"några konceptvägledningar. Se ”git help <kommando>” eller ”git help\n"
+"<koncept> för att läsa mer om specifika underkommandon och koncept.\n"
+"See ”git help git” för en översikt över systemet."
 
 #, c-format
 msgid "unsupported command listing type '%s'"
-msgstr "okänd kommandolisttyp \"%s\""
+msgstr "okänd kommandolisttyp ”%s”"
 
 #, c-format
 msgid "no directory given for '%s' option\n"
-msgstr "ingen katalog angavs för flaggan \"%s\"\n"
+msgstr "ingen katalog angavs för flaggan ”%s”\n"
 
 #, c-format
 msgid "no namespace given for --namespace\n"
 msgstr "ingen namnrymd angavs för --namespace\n"
 
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "inget prefix angavs för --super-prefix\n"
-
 #, c-format
 msgid "-c expects a configuration string\n"
 msgstr "-c förväntar en konfigurationssträng\n"
@@ -15862,21 +16253,25 @@ msgstr "-c förväntar en konfigurationssträng\n"
 msgid "no config key given for --config-env\n"
 msgstr "ingen konfigurationsnyckel angavs för --config-env\n"
 
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "ingen attributkälla angavs för --attr-source\n"
+
 #, c-format
 msgid "unknown option: %s\n"
 msgstr "okänd flagga: %s\n"
 
 #, c-format
 msgid "while expanding alias '%s': '%s'"
-msgstr "vid expandering av aliaset \"%s\": \"%s\""
+msgstr "vid expandering av aliaset ”%s”: ”%s”"
 
 #, c-format
 msgid ""
 "alias '%s' changes environment variables.\n"
 "You can use '!git' in the alias to do this"
 msgstr ""
-"aliaset \"%s\" ändrar miljövariabler.\n"
-"Du kan använda \"!git\" i aliaset för att göra det"
+"aliaset ”%s” ändrar miljövariabler.\n"
+"Du kan använda ”!git” i aliaset för att göra det"
 
 #, c-format
 msgid "empty alias for %s"
@@ -15897,7 +16292,7 @@ msgstr "stäng misslyckades på standard ut"
 
 #, c-format
 msgid "alias loop detected: expansion of '%s' does not terminate:%s"
-msgstr "alias-slinga detekterades: expansionen av \"%s\" avslutas aldrig:%s"
+msgstr "alias-slinga detekterades: expansionen av ”%s” avslutas aldrig:%s"
 
 #, c-format
 msgid "cannot handle %s as a builtin"
@@ -15914,18 +16309,18 @@ msgstr ""
 #, c-format
 msgid "expansion of alias '%s' failed; '%s' is not a git command\n"
 msgstr ""
-"expandering av alias \"%s\" misslyckades; \"%s\" är inte ett git-kommando\n"
+"expandering av alias ”%s” misslyckades; ”%s” är inte ett git-kommando\n"
 
 #, c-format
 msgid "failed to run command '%s': %s\n"
-msgstr "misslyckades köra kommandot \"%s\": %s\n"
+msgstr "misslyckades köra kommandot ”%s”: %s\n"
 
 msgid "could not create temporary file"
 msgstr "kunde inte skapa temporära fil"
 
 #, c-format
 msgid "failed writing detached signature to '%s'"
-msgstr "misslyckades skriva fristående signatur till \"%s\""
+msgstr "misslyckades skriva fristående signatur till ”%s”"
 
 msgid ""
 "gpg.ssh.allowedSignersFile needs to be configured and exist for ssh "
@@ -15938,7 +16333,7 @@ msgid ""
 "ssh-keygen -Y find-principals/verify is needed for ssh signature "
 "verification (available in openssh version 8.2p1+)"
 msgstr ""
-"\"ssh-keygen -Y find-principals/verify\" behövs för att bekräfta ssh-"
+"”ssh-keygen -Y find-principals/verify” behövs för att bekräfta ssh-"
 "signaturer (tillgängligt i openssh version 8.2p1+)"
 
 #, c-format
@@ -15947,11 +16342,11 @@ msgstr "återkallningsfilen för ssh-signering inställd men saknas: %s"
 
 #, c-format
 msgid "bad/incompatible signature '%s'"
-msgstr "felaktig/inkompatibel signatur \"%s\""
+msgstr "felaktig/inkompatibel signatur ”%s”"
 
 #, c-format
 msgid "failed to get the ssh fingerprint for key '%s'"
-msgstr "misslyckades hämta ssh-fingeravtrycket för nyckeln \"%s\""
+msgstr "misslyckades hämta ssh-fingeravtrycket för nyckeln ”%s”"
 
 msgid ""
 "either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"
@@ -15966,34 +16361,39 @@ msgstr "gpg.ssh.defaultKeyCommand lyckades men gav inga nycklar: %s %s"
 msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "gpg.ssh.defaultKeyCommand misslyckades: %s %s"
 
-msgid "gpg failed to sign the data"
-msgstr "gpg misslyckades signera data"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg misslyckades signera data:\n"
+"%s"
 
 msgid "user.signingKey needs to be set for ssh signing"
 msgstr "user.signingKey måste anges för ssh-signering"
 
 #, c-format
 msgid "failed writing ssh signing key to '%s'"
-msgstr "misslyckades skriva ssh-signeringsnyckel till \"%s\""
+msgstr "misslyckades skriva ssh-signeringsnyckel till ”%s”"
 
 #, c-format
 msgid "failed writing ssh signing key buffer to '%s'"
-msgstr "misslyckades skriva ssh-signeringsnyckelbuffert till \"%s\""
+msgstr "misslyckades skriva ssh-signeringsnyckelbuffert till ”%s”"
 
 msgid ""
 "ssh-keygen -Y sign is needed for ssh signing (available in openssh version "
 "8.2p1+)"
 msgstr ""
-"\"ssh-keygen -Y sign\" behövs för ssh-signering (tillgängligt i openssh "
+"”ssh-keygen -Y sign” behövs för ssh-signering (tillgängligt i openssh "
 "version 8.2p1+)"
 
 #, c-format
 msgid "failed reading ssh signing data buffer from '%s'"
-msgstr "misslyckades läsa ssh-signeringsdatabuffert från \"%s\""
+msgstr "misslyckades läsa ssh-signeringsdatabuffert från ”%s”"
 
 #, c-format
 msgid "ignored invalid color '%.*s' in log.graphColors"
-msgstr "ignorerade felaktig färg \"%.*s\" i log.graphColors"
+msgstr "ignorerade felaktig färg ”%.*s” i log.graphColors"
 
 msgid ""
 "given pattern contains NULL byte (via -f <file>). This is only supported "
@@ -16004,11 +16404,11 @@ msgstr ""
 
 #, c-format
 msgid "'%s': unable to read %s"
-msgstr "\"%s\" kunde inte läsa %s"
+msgstr "”%s” kunde inte läsa %s"
 
 #, c-format
 msgid "'%s': short read"
-msgstr "\"%s\": kort läsning"
+msgstr "”%s”: kort läsning"
 
 msgid "start a working area (see also: git help tutorial)"
 msgstr "starta arbetskatalog (se också: git help tutorial)"
@@ -16057,7 +16457,7 @@ msgstr "Filformat, protokoll och andra gränssnitt tänkta för utvecklare"
 
 #, c-format
 msgid "available git commands in '%s'"
-msgstr "git-kommandon tillgängliga i \"%s\""
+msgstr "git-kommandon tillgängliga i ”%s”"
 
 msgid "git commands available from elsewhere on your $PATH"
 msgstr "git-kommandon från andra platser i din $PATH"
@@ -16081,39 +16481,39 @@ msgid "Command aliases"
 msgstr "Kommadoalias"
 
 msgid "See 'git help <command>' to read about a specific subcommand"
-msgstr "Se \"git help <kommando>\" för att läsa om ett specifikt underkommando"
+msgstr "Se ”git help <kommando>” för att läsa om ett specifikt underkommando"
 
 #, c-format
 msgid ""
 "'%s' appears to be a git command, but we were not\n"
 "able to execute it. Maybe git-%s is broken?"
 msgstr ""
-"\"%s\" verkar vara ett git-kommando, men vi kan inte\n"
+"”%s” verkar vara ett git-kommando, men vi kan inte\n"
 "köra det. Kanske git-%s är trasigt?"
 
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
-msgstr "git: \"%s\" är inte ett git-kommando. Se \"git --help\"."
+msgstr "git: ”%s” är inte ett git-kommando. Se ”git --help”."
 
 msgid "Uh oh. Your system reports no Git commands at all."
 msgstr "Oj då. Ditt system rapporterar inga Git-kommandon alls."
 
 #, c-format
 msgid "WARNING: You called a Git command named '%s', which does not exist."
-msgstr "VARNING: Du anropade ett Git-kommando vid namn \"%s\", som inte finns."
+msgstr "VARNING: Du anropade ett Git-kommando vid namn ”%s”, som inte finns."
 
 #, c-format
 msgid "Continuing under the assumption that you meant '%s'."
-msgstr "Fortsätter under förutsättningen att du menade \"%s\"."
+msgstr "Fortsätter under förutsättningen att du menade ”%s”."
 
 #, c-format
 msgid "Run '%s' instead [y/N]? "
-msgstr "Köra \"%s\" istället (j/N)?"
+msgstr "Köra ”%s” istället (j/N)?"
 
 #, c-format
 msgid "Continuing in %0.1f seconds, assuming that you meant '%s'."
 msgstr ""
-"Fortsätter om %0.1f sekunder, under förutsättningen att du menade \"%s\"."
+"Fortsätter om %0.1f sekunder, under förutsättningen att du menade ”%s”."
 
 msgid ""
 "\n"
@@ -16128,8 +16528,8 @@ msgstr[1] ""
 "\n"
 "Mest lika kommandon är"
 
-msgid "git version [<options>]"
-msgstr "git version [<flaggor>]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #, c-format
 msgid "%s: %s - %s"
@@ -16153,8 +16553,8 @@ msgid ""
 "The '%s' hook was ignored because it's not set as executable.\n"
 "You can disable this warning with `git config advice.ignoredHook false`."
 msgstr ""
-"Kroken \"%s\" ignorerades eftersom den inte är markerad som körbar.\n"
-"Du kan inaktivera varningen med \"git config advice.ignoredHook false\"."
+"Kroken ”%s” ignorerades eftersom den inte är markerad som körbar.\n"
+"Du kan inaktivera varningen med ”git config advice.ignoredHook false”."
 
 #, c-format
 msgid "argument to --packfile must be a valid hash (got '%s')"
@@ -16179,15 +16579,15 @@ msgstr "CURLSSLOPT_NO_REVOKE stöds inte av cURL < 7.44.0"
 
 #, c-format
 msgid "Unsupported SSL backend '%s'. Supported SSL backends:"
-msgstr "SSL-bakändan \"%s\" stöds inte. Dessa SSL-bakändor stöds:"
+msgstr "SSL-bakändan ”%s” stöds inte. Dessa SSL-bakändor stöds:"
 
 #, c-format
 msgid "Could not set SSL backend to '%s': cURL was built without SSL backends"
-msgstr "Kan inte sätta SSL-bakända till \"%s\": cURL byggdes utan SSL-bakändor"
+msgstr "Kan inte sätta SSL-bakända till ”%s”: cURL byggdes utan SSL-bakändor"
 
 #, c-format
 msgid "Could not set SSL backend to '%s': already set"
-msgstr "Kunde inte sätta SSL-bakända till \"%s\": redan valt"
+msgstr "Kunde inte sätta SSL-bakända till ”%s”: redan valt"
 
 #, c-format
 msgid ""
@@ -16235,14 +16635,14 @@ msgstr "ingen e-post angavs och autodetektering är inaktiverad"
 
 #, c-format
 msgid "unable to auto-detect email address (got '%s')"
-msgstr "kunde inte autodetektera e-postadress (fick \"%s\")"
+msgstr "kunde inte autodetektera e-postadress (fick ”%s”)"
 
 msgid "no name was given and auto-detection is disabled"
 msgstr "inget namn angavs och autodetektering är inaktiverad"
 
 #, c-format
 msgid "unable to auto-detect name (got '%s')"
-msgstr "kunde inte autodetektera namn (fick \"%s\")"
+msgstr "kunde inte autodetektera namn (fick ”%s”)"
 
 #, c-format
 msgid "empty ident name (for <%s>) not allowed"
@@ -16253,22 +16653,22 @@ msgid "name consists only of disallowed characters: %s"
 msgstr "namnet består enbart av ej tillåtna tecken: %s"
 
 msgid "expected 'tree:<depth>'"
-msgstr "förväntade \"tree:<djup>\""
+msgstr "förväntade ”tree:<djup>”"
 
 msgid "sparse:path filters support has been dropped"
 msgstr "sparse:sökväg-filter stöds inte längre"
 
 #, c-format
 msgid "'%s' for 'object:type=<type>' is not a valid object type"
-msgstr "\"%s\" för \"object:type=<typ>\" är inte en giltig objekttyp"
+msgstr "”%s” för ”object:type=<typ>” är inte en giltig objekttyp"
 
 #, c-format
 msgid "invalid filter-spec '%s'"
-msgstr "felaktig filterspecifikation: \"%s\""
+msgstr "felaktig filterspecifikation: ”%s”"
 
 #, c-format
 msgid "must escape char in sub-filter-spec: '%c'"
-msgstr "måste använda specialsekvens i delfilter-spec: \"%c\""
+msgstr "måste använda specialsekvens i delfilter-spec: ”%c”"
 
 msgid "expected something after combine:"
 msgstr "förväntade någonting efter combine:"
@@ -16287,7 +16687,7 @@ msgstr "objektfiltrering"
 
 #, c-format
 msgid "unable to access sparse blob in '%s'"
-msgstr "kunde inte nå gles blob på \"%s\""
+msgstr "kunde inte nå gles blob på ”%s”"
 
 #, c-format
 msgid "unable to parse sparse filter data in %s"
@@ -16295,11 +16695,11 @@ msgstr "kunde inte tolka gles filterdata i %s"
 
 #, c-format
 msgid "entry '%s' in tree %s has tree mode, but is not a tree"
-msgstr "posten \"%s\" i trädet %s har träd-läge, men är inte ett träd"
+msgstr "posten ”%s” i trädet %s har träd-läge, men är inte ett träd"
 
 #, c-format
 msgid "entry '%s' in tree %s has blob mode, but is not a blob"
-msgstr "posten \"%s\" i trädet %s har blob-läge, men är inte en blob"
+msgstr "posten ”%s” i trädet %s har blob-läge, men är inte en blob"
 
 #, c-format
 msgid "unable to load root tree for commit %s"
@@ -16315,10 +16715,10 @@ msgid ""
 "may have crashed in this repository earlier:\n"
 "remove the file manually to continue."
 msgstr ""
-"Kunde inte skapa \"%s.lock\": %s.\n"
+"Kunde inte skapa ”%s.lock”: %s.\n"
 "\n"
 "Det verkar som en annan git-process kör i det här arkivet, t.ex.\n"
-"ett textredigeringsprogram startat av \"git commit\". Se till att\n"
+"ett textredigeringsprogram startat av ”git commit”. Se till att\n"
 "alla processer avslutats och försök sedan igen. Om det fortfarande\n"
 "misslyckas kanske en git-process har kraschat i det här arkivet\n"
 "tidigare:\n"
@@ -16326,14 +16726,14 @@ msgstr ""
 
 #, c-format
 msgid "Unable to create '%s.lock': %s"
-msgstr "Kunde inte skapa \"%s.lock\": %s"
+msgstr "Kunde inte skapa ”%s.lock”: %s"
 
 #, c-format
 msgid "unexpected line: '%s'"
-msgstr "oväntad rad: \"%s\""
+msgstr "oväntad rad: ”%s”"
 
 msgid "expected flush after ls-refs arguments"
-msgstr "förväntade \"flush\" efter ls-refs-argument"
+msgstr "förväntade ”flush” efter ls-refs-argument"
 
 msgid "quoted CRLF detected"
 msgstr "citerad CRLF upptäcktes"
@@ -16378,12 +16778,12 @@ msgstr ""
 "finns:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "Misslyckades exekvera intern sammanslagning"
+msgid "failed to execute internal merge"
+msgstr "misslyckades exekvera intern sammanslagning"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "Kunde inte lägga till %s till databasen"
+msgid "unable to add %s to database"
+msgstr "kunde inte lägga till %s till databasen"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -16420,7 +16820,7 @@ msgid ""
 "WARNING: Avoiding applying %s -> %s rename to %s, because %s itself was "
 "renamed."
 msgstr ""
-"VARNING: Undviker att applicera namnändring %s -> %s på %s, då %s själv har "
+"VARNING: Undviker att applicera namnändring %s  %s på %s, då %s själv har "
 "bytt namn."
 
 #, c-format
@@ -16466,7 +16866,7 @@ msgid ""
 "conflicts AND collides with another path; this may result in nested conflict "
 "markers."
 msgstr ""
-"KONFLIKT (namnbyte involverad i krock): namnbyte av %s -> %s har "
+"KONFLIKT (namnbyte involverad i krock): namnbyte av %s  %s har "
 "innehållskonflikter OCH krockar med en annan sökväg; detta kan leda till "
 "nästlade konfliktmarkörer."
 
@@ -16581,17 +16981,17 @@ msgstr "(felaktig incheckning)\n"
 #, c-format
 msgid "add_cacheinfo failed for path '%s'; merge aborting."
 msgstr ""
-"add_cacheinfo misslyckades för sökvägen \"%s\"; avslutar sammanslagningen."
+"add_cacheinfo misslyckades för sökvägen ”%s”; avslutar sammanslagningen."
 
 #, c-format
 msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting."
 msgstr ""
-"add_cacheinfo misslyckades uppdatera för sökvägen \"%s\"; avslutar "
+"add_cacheinfo misslyckades uppdatera för sökvägen ”%s”; avslutar "
 "sammanslagningen."
 
 #, c-format
 msgid "failed to create path '%s'%s"
-msgstr "misslyckades skapa sökvägen \"%s\"%s"
+msgstr "misslyckades skapa sökvägen ”%s”%s"
 
 #, c-format
 msgid "Removing %s to make room for subdirectory\n"
@@ -16602,23 +17002,23 @@ msgstr ": kanske en K/F-konflikt?"
 
 #, c-format
 msgid "refusing to lose untracked file at '%s'"
-msgstr "vägrar förlora ospårad fil vid \"%s\""
+msgstr "vägrar förlora ospårad fil vid ”%s”"
 
 #, c-format
 msgid "blob expected for %s '%s'"
-msgstr "blob förväntades för %s \"%s\""
+msgstr "blob förväntades för %s ”%s”"
 
 #, c-format
 msgid "failed to open '%s': %s"
-msgstr "misslyckades öppna \"%s\": %s"
+msgstr "misslyckades öppna ”%s”: %s"
 
 #, c-format
 msgid "failed to symlink '%s': %s"
-msgstr "misslyckades skapa symboliska länken \"%s\": %s"
+msgstr "misslyckades skapa symboliska länken ”%s”: %s"
 
 #, c-format
 msgid "do not know what to do with %06o %s '%s'"
-msgstr "vet inte hur %06o %s \"%s\" ska hanteras"
+msgstr "vet inte hur %06o %s ”%s” ska hanteras"
 
 #, c-format
 msgid "Fast-forwarding submodule %s to the following commit:"
@@ -16706,7 +17106,7 @@ msgstr "namnbytt"
 
 #, c-format
 msgid "Refusing to lose dirty file at %s"
-msgstr "Vägrar förlora lortig fil vid \"%s\""
+msgstr "Vägrar förlora lortig fil vid ”%s”"
 
 #, c-format
 msgid "Refusing to lose untracked file at %s, even though it's in the way."
@@ -16714,7 +17114,7 @@ msgstr "Vägrar förlora ospårad fil vid %s, trots att den är i vägen."
 
 #, c-format
 msgid "CONFLICT (rename/add): Rename %s->%s in %s.  Added %s in %s"
-msgstr "KONFLIKT (namnbyte/tillägg): Namnbyte %s->%s i %s. Lade till %s i %s"
+msgstr "KONFLIKT (namnbyte/tillägg): Namnbyte %s%s i %s. Lade till %s i %s"
 
 #, c-format
 msgid "%s is a directory in %s adding as %s instead"
@@ -16726,19 +17126,18 @@ msgstr "Vägrar förlora ospårad fil vid %s; lägger till som %s istället"
 
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
-"KONFLIKT (namnbyte/namnbyte): Namnbyte \"%s\"->\"%s\" på grenen \"%s\" "
-"namnbyte \"%s\"->\"%s\" i \"%s\"%s"
+"KONFLIKT (namnbyte/namnbyte): Namnbyte ”%s”→”%s” på grenen ”%s” namnbyte "
+"”%s”→”%s” i ”%s”%s"
 
 msgid " (left unresolved)"
 msgstr " (lämnad olöst)"
 
 #, c-format
 msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
-msgstr ""
-"KONFLIKT (namnbyte/namnbyte): Namnbyte %s->%s i %s. Namnbyte %s->%s i %s"
+msgstr "KONFLIKT (namnbyte/namnbyte): Namnbyte %s→%s i %s. Namnbyte %s→%s i %s"
 
 #, c-format
 msgid ""
@@ -16755,8 +17154,8 @@ msgid ""
 "CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory %s-"
 ">%s in %s"
 msgstr ""
-"KONFLIKT (namnbyte/namnbyte): Namnbytt katalog %s->%s i %s. Namnbytt katalog "
-"%s->%s i %s"
+"KONFLIKT (namnbyte/namnbyte): Namnbytt katalog %s%s i %s. Namnbytt katalog "
+"%s%s i %s"
 
 msgid "modify"
 msgstr "ändra"
@@ -16813,13 +17212,19 @@ msgstr "sammanslagningen returnerade ingen incheckning"
 
 #, c-format
 msgid "Could not parse object '%s'"
-msgstr "Kunde inte tolka objektet \"%s\""
+msgstr "Kunde inte tolka objektet ”%s”"
 
 msgid "failed to read the cache"
 msgstr "misslyckades läsa cachen"
 
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "multi-pack-indexets OID-utbredning har fel storlek"
+msgstr "OID-utbredning för multi-pack-index har fel storlek"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "OID-uppslagningsstycket för multi-pack-index har fel storlek"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "objekt-offset-stycket för multi-pack-index har fel storlek"
 
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -16837,36 +17242,47 @@ msgstr "multi-pack-indexversionen %d stöds inte"
 msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "multi-pack-index-hashversionen %u stämmer inte med versionen %u"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "multi-pack-index saknar krävd paketnamn-stycke"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"nödvändigt paketnamn-stycke för multi-pack-index saknas eller är trasigt"
+
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"nödvändigt OID-utbredningsstycke för multi-pack-index saknas eller är trasigt"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "multi-pack-index saknar krävt OID-utbredningsstycke"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"nödvändigt OID-uppslagningsstycke för multi-pack-index saknas eller är "
+"trasigt"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "multi-pack-index saknar krävt OID-uppslagnignsstycke"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"nödvändigt objekt-offsetstycke för multi-pack-index saknas eller är trasigt"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "multi-pack-index saknar krävt objekt-offsetstycke"
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "paketnamnstycke för multi-pack-index är för kort"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
-msgstr "multi-pack-index-paketnamn i fel ordning: \"%s\" före \"%s\""
+msgstr "paketnamn för multi-pack-index i fel ordning: ”%s” före ”%s”"
 
 #, c-format
 msgid "bad pack-int-id: %u (%u total packs)"
 msgstr "bad pack-int-id: %u (%u paket totalt)"
 
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
-msgstr "multi-pack-index skriver 64-bitars offset, men off_t är för liten"
+msgstr "multi-pack-index innehåller 64-bitars offset, men off_t är för liten"
+
+msgid "multi-pack-index large offset out of bounds"
+msgstr "stort offset för mult-pack-index utanför gränsen"
 
 #, c-format
 msgid "failed to add packfile '%s'"
-msgstr "misslyckades läsa paketfilen \"%s\""
+msgstr "misslyckades läsa paketfilen ”%s”"
 
 #, c-format
 msgid "failed to open pack-index '%s'"
-msgstr "misslyckades öppna paketindexet \"%s\""
+msgstr "misslyckades öppna paketindexet ”%s”"
 
 #, c-format
 msgid "failed to locate object %d in packfile"
@@ -16910,7 +17326,7 @@ msgstr "såg inte paketfilen %s som skulle kastas"
 
 #, c-format
 msgid "preferred pack '%s' is expired"
-msgstr "föredraget paket \"%s\" har löpt ut"
+msgstr "föredraget paket ”%s” har löpt ut"
 
 msgid "no pack files to index."
 msgstr "inga paketfiler att indexera."
@@ -17002,9 +17418,9 @@ msgid ""
 "commit/abort the previous merge before you start a new notes merge."
 msgstr ""
 "Du har inte avslutat föregående antecknings-sammanslagning (%s finns).\n"
-"Använd \"git notes merge --commit\" eller \"git notes merge --abort\" för "
-"att checka in eller avbryta föregående sammanslagning innan du påbörjar en "
-"ny antecknings-sammanslagning."
+"Använd ”git notes merge --commit” eller ”git notes merge --abort” för att "
+"checka in eller avbryta föregående sammanslagning innan du påbörjar en ny "
+"antecknings-sammanslagning."
 
 #, c-format
 msgid "You have not concluded your notes merge (%s exists)."
@@ -17027,7 +17443,7 @@ msgstr "Vägrar skriva över anteckningar i %s (utanför refs/notes/)"
 #.
 #, c-format
 msgid "Bad %s value: '%s'"
-msgstr "Felaktigt värde på %s: \"%s\""
+msgstr "Felaktigt värde på %s: ”%s”"
 
 #, c-format
 msgid "object directory %s does not exist; check .git/objects/info/alternates"
@@ -17041,38 +17457,34 @@ msgstr "kunde inte normalisera supplerande objektsökväg: %s"
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s: ignorerar supplerande objektlager, för djup nästling"
 
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "kan inte normalisera objektkatalogen: %s"
-
 msgid "unable to fdopen alternates lockfile"
-msgstr "kan inte utföra \"fdopen\" på suppleantlåsfil"
+msgstr "kan inte utföra ”fdopen” på suppleantlåsfil"
 
 msgid "unable to read alternates file"
-msgstr "kan inte läsa \"alternates\"-filen"
+msgstr "kan inte läsa ”alternates”-filen"
 
 msgid "unable to move new alternates file into place"
-msgstr "kan inte flytta ny \"alternates\"-fil på plats"
+msgstr "kan inte flytta ny ”alternates”-fil på plats"
 
 #, c-format
 msgid "path '%s' does not exist"
-msgstr "sökvägen \"%s\" finns inte"
+msgstr "sökvägen ”%s” finns inte"
 
 #, c-format
 msgid "reference repository '%s' as a linked checkout is not supported yet."
-msgstr "referensarkivet \"%s\" som en länkad utcheckning stöds inte ännu."
+msgstr "referensarkivet ”%s” som en länkad utcheckning stöds inte ännu."
 
 #, c-format
 msgid "reference repository '%s' is not a local repository."
-msgstr "referensarkivet \"%s\" är inte ett lokalt arkiv."
+msgstr "referensarkivet ”%s” är inte ett lokalt arkiv."
 
 #, c-format
 msgid "reference repository '%s' is shallow"
-msgstr "referensarkivet \"%s\" är grunt"
+msgstr "referensarkivet ”%s” är grunt"
 
 #, c-format
 msgid "reference repository '%s' is grafted"
-msgstr "referensarkivet \"%s\" är ympat"
+msgstr "referensarkivet ”%s” är ympat"
 
 #, c-format
 msgid "could not find object directory matching %s"
@@ -17084,7 +17496,7 @@ msgstr "felaktig rad vid tolkning av supplerande referenser: %s"
 
 #, c-format
 msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>"
-msgstr "försök att utföra \"mmap\" på %<PRIuMAX> över gränsen %<PRIuMAX>"
+msgstr "försök att utföra ”mmap” på %<PRIuMAX> över gränsen %<PRIuMAX>"
 
 #, c-format
 msgid "mmap failed%s"
@@ -17096,11 +17508,15 @@ msgstr "objektfilen %s är tom"
 
 #, c-format
 msgid "corrupt loose object '%s'"
-msgstr "trasigt löst objekt \"%s\""
+msgstr "trasigt löst objekt ”%s”"
 
 #, c-format
 msgid "garbage at end of loose object '%s'"
-msgstr "skräp i slutet av löst objekt \"%s\""
+msgstr "skräp i slutet av löst objekt ”%s”"
+
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "kan inte öppna lösa objekt %s"
 
 #, c-format
 msgid "unable to parse %s header"
@@ -17118,17 +17534,13 @@ msgid "header for %s too long, exceeds %d bytes"
 msgstr "huvudet för %s är för långt, mer än %d byte"
 
 #, c-format
-msgid "failed to read object %s"
-msgstr "misslyckades läsa objektet %s"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "löst objekt %s (lagrat i %s) är trasigt"
 
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "ersättningen %s hittades inte för %s"
 
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "löst objekt %s (lagrat i %s) är trasigt"
-
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr "packat objekt %s (lagrat i %s) är trasigt"
@@ -17139,10 +17551,7 @@ msgstr "kunde inte skriva filen %s"
 
 #, c-format
 msgid "unable to set permission to '%s'"
-msgstr "kan inte sätta behörigheten till \"%s\""
-
-msgid "file write error"
-msgstr "fel vid skrivning av fil"
+msgstr "kan inte sätta behörigheten till ”%s”"
 
 msgid "error when closing loose object file"
 msgstr "fel vid stängning av fil för löst objekt"
@@ -17160,11 +17569,11 @@ msgstr "kunde inte skriva fil för löst objekt"
 
 #, c-format
 msgid "unable to deflate new object %s (%d)"
-msgstr "kan inte utföra \"deflate\" på nytt objekt %s (%d)"
+msgstr "kan inte utföra ”deflate” på nytt objekt %s (%d)"
 
 #, c-format
 msgid "deflateEnd on object %s failed (%d)"
-msgstr "\"deflateend\" på objektet %s misslyckades (%d)"
+msgstr "”deflateEnd” på objektet %s misslyckades (%d)"
 
 #, c-format
 msgid "confused by unstable object source data for %s"
@@ -17176,11 +17585,11 @@ msgstr "skriv strömobjektet %ld != %<PRIuMAX>"
 
 #, c-format
 msgid "unable to stream deflate new object (%d)"
-msgstr "kan inte utföra \"deflate\" på nytt strömobjekt (%d)"
+msgstr "kan inte utföra ”deflate” på nytt strömobjekt (%d)"
 
 #, c-format
 msgid "deflateEnd on stream object failed (%d)"
-msgstr "\"deflateend\" på strömobjektet misslyckades (%d)"
+msgstr "”deflatEend” på strömobjektet misslyckades (%d)"
 
 #, c-format
 msgid "unable to create directory %s"
@@ -17190,11 +17599,12 @@ msgstr "kunde inte skapa katalogen %s"
 msgid "cannot read object for %s"
 msgstr "kan inte läsa objekt för %s"
 
-msgid "corrupt commit"
-msgstr "trasik incheckning"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "objekt klarar inte fsck: %s"
 
-msgid "corrupt tag"
-msgstr "trasig tagg"
+msgid "refusing to create malformed object"
+msgstr "vägrar skapa ett felaktigt format objekt"
 
 #, c-format
 msgid "read error while indexing %s"
@@ -17214,7 +17624,7 @@ msgstr "%s: filtypen stöds ej"
 
 #, c-format
 msgid "%s is not a valid '%s' object"
-msgstr "%s är inte ett giltigt \"%s\"-objekt"
+msgstr "%s är inte ett giltigt ”%s”-objekt"
 
 #, c-format
 msgid "unable to open %s"
@@ -17226,7 +17636,7 @@ msgstr "hash stämmer inte för %s (förväntade %s)"
 
 #, c-format
 msgid "unable to mmap %s"
-msgstr "kan inte utföra \"mmap\" för %s"
+msgstr "kan inte utföra ”mmap” för %s"
 
 #, c-format
 msgid "unable to unpack header of %s"
@@ -17329,72 +17739,72 @@ msgstr ""
 "\n"
 "  git switch -c $br $(git rev-parse ...)\n"
 "\n"
-"där \"$br\" på något sätt blivit tomt och en 40-hex-referens skapats.\n"
+"där ”$br” på något sätt blivit tomt och en 40-hex-referens skapats.\n"
 "Undersök referenserna och ta kanske bort dem. Stäng av meddelandet\n"
-"genom att köra \"git config advice.objectNameWarning false\""
+"genom att köra ”git config advice.objectNameWarning false”"
 
 #, c-format
 msgid "log for '%.*s' only goes back to %s"
-msgstr "loggen för \"%.*s\" räcker bara tillbaka till %s"
+msgstr "loggen för ”%.*s” räcker bara tillbaka till %s"
 
 #, c-format
 msgid "log for '%.*s' only has %d entries"
-msgstr "loggen för \"%.*s\" har bara %d poster"
+msgstr "loggen för ”%.*s” har bara %d poster"
 
 #, c-format
 msgid "path '%s' exists on disk, but not in '%.*s'"
-msgstr "Sökvägen \"%s\" finns på disken, men inte i \"%.*s\""
+msgstr "Sökvägen ”%s” finns på disken, men inte i ”%.*s”"
 
 #, c-format
 msgid ""
 "path '%s' exists, but not '%s'\n"
 "hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?"
 msgstr ""
-"sökvägen \"%s\" finns, men inte i \"%s\"\n"
-"tips: Menade du \"%.*s:%s\", även känd som \"%.*s:./%s\"?"
+"sökvägen ”%s” finns, men inte i ”%s”\n"
+"tips: Menade du ”%.*s:%s”, även känd som ”%.*s:./%s”?"
 
 #, c-format
 msgid "path '%s' does not exist in '%.*s'"
-msgstr "sökvägen \"%s\" finns inte i \"%.*s\""
+msgstr "sökvägen ”%s” finns inte i ”%.*s”"
 
 #, c-format
 msgid ""
 "path '%s' is in the index, but not at stage %d\n"
 "hint: Did you mean ':%d:%s'?"
 msgstr ""
-"sökvägen \"%s\" finns i indexet men inte i etapp %d\n"
-"tips: Menade du \":%d:%s\"?"
+"sökvägen ”%s” finns i indexet men inte i etapp %d\n"
+"tips: Menade du ”:%d:%s”?"
 
 #, c-format
 msgid ""
 "path '%s' is in the index, but not '%s'\n"
 "hint: Did you mean ':%d:%s' aka ':%d:./%s'?"
 msgstr ""
-"sökvägen \"%s\" finns i indexet, men inte i \"%s\"\n"
-"tips: Menade du \":%d:%s\", även känd som \":%d:./%s\"?"
+"sökvägen ”%s” finns i indexet, men inte i ”%s”\n"
+"tips: Menade du ”:%d:%s”, även känd som ”:%d:./%s”?"
 
 #, c-format
 msgid "path '%s' exists on disk, but not in the index"
-msgstr "sökvägen \"%s\" finns på disk, men inte i indexet"
+msgstr "sökvägen ”%s” finns på disk, men inte i indexet"
 
 #, c-format
 msgid "path '%s' does not exist (neither on disk nor in the index)"
-msgstr "sökvägen \"%s\" finns inte (varken i disken eller i indexet)"
+msgstr "sökvägen ”%s” finns inte (varken i disken eller i indexet)"
 
 msgid "relative path syntax can't be used outside working tree"
 msgstr "relativ sökväg kan inte användas utanför arbetskatalogen"
 
 #, c-format
 msgid "<object>:<path> required, only <object> '%s' given"
-msgstr "<objekt>:<sökväg> krävs, endast <objekt> \"%s\" har angivits"
+msgstr "<objekt>:<sökväg> krävs, endast <objekt> ”%s” har angivits"
 
 #, c-format
 msgid "invalid object name '%.*s'."
-msgstr "felaktigt objektnamn \"%.*s\"."
+msgstr "felaktigt objektnamn ”%.*s”."
 
 #, c-format
 msgid "invalid object type \"%s\""
-msgstr "ogiltig objekttyp \"%s\""
+msgstr "ogiltig objekttyp ”%s”"
 
 #, c-format
 msgid "object %s is a %s, not a %s"
@@ -17426,7 +17836,7 @@ msgstr "trasigt bitkarteindex (felaktigt huvud)"
 
 #, c-format
 msgid "unsupported version '%d' for bitmap index file"
-msgstr "versionen \"%d\" i bitkarteindexfilen stöds inte"
+msgstr "versionen ”%d” i bitkarteindexfilen stöds inte"
 
 msgid "corrupted bitmap index file (too short to fit hash cache)"
 msgstr "trasigt bitkarteindex (för kort för att få plats för hash-cache)"
@@ -17436,7 +17846,7 @@ msgstr "trasigt bitkarteindex (för kort för att få plats för uppslagstabell)
 
 #, c-format
 msgid "duplicate entry in bitmap index: '%s'"
-msgstr "duplicerad post i bitkarteindex: \"%s\""
+msgstr "duplicerad post i bitkarteindex: ”%s”"
 
 #, c-format
 msgid "corrupt ewah bitmap: truncated header for entry %d"
@@ -17453,11 +17863,7 @@ msgid "invalid XOR offset in bitmap pack index"
 msgstr "ogiltigt XOR-offset i bitkarte-packindex"
 
 msgid "cannot fstat bitmap file"
-msgstr "kan inte utföra \"fstat\" på bitkartefil"
-
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "ignorerar extra bitkartefil: %s"
+msgstr "kan inte utföra ”fstat” på bitkartefil"
 
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "checksumman stämmer inte i MIDX och bitkarta"
@@ -17486,20 +17892,19 @@ msgstr ""
 
 #, c-format
 msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
-msgstr ""
-"trasig ewah-bitkarta: avhugget huvud för bitkarta för incheckning \"%s\""
+msgstr "trasig ewah-bitkarta: avhugget huvud för bitkarta för incheckning ”%s”"
 
 #, c-format
 msgid "object '%s' not found in type bitmaps"
-msgstr "objektet \"%s\" hittades inte i typbitkartor"
+msgstr "objektet ”%s” hittades inte i typbitkartor"
 
 #, c-format
 msgid "object '%s' does not have a unique type"
-msgstr "objektet \"%s\" har inte en unik typ"
+msgstr "objektet ”%s” har inte en unik typ"
 
 #, c-format
 msgid "object '%s': real type '%s', expected: '%s'"
-msgstr "objektet \"%s\": riktig typ \"%s\", förväntade \"%s\""
+msgstr "objektet ”%s”: riktig typ ”%s”, förväntade ”%s”"
 
 #, c-format
 msgid "object not in bitmap: '%s'"
@@ -17513,18 +17918,22 @@ msgstr "du måste ange exakt en incheckning att testa"
 
 #, c-format
 msgid "commit '%s' doesn't have an indexed bitmap"
-msgstr "incheckningen \"%s\" har inte en indexerad bitkarta"
+msgstr "incheckningen ”%s” har inte en indexerad bitkarta"
 
 msgid "mismatch in bitmap results"
 msgstr "bitkarteresultat stämmer inte överens"
 
 #, c-format
 msgid "could not find '%s' in pack '%s' at offset %<PRIuMAX>"
-msgstr "kunde inte hitta \"%s\" i paketet \"%s\" på offset %<PRIuMAX>"
+msgstr "kunde inte hitta ”%s” i paketet ”%s” på offset %<PRIuMAX>"
 
 #, c-format
 msgid "unable to get disk usage of '%s'"
-msgstr "kan inte hämta diskanvändning för \"%s\""
+msgstr "kan inte hämta diskanvändning för ”%s”"
+
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "bitkartefilen ”%s” har ogiltig kontrollsumma"
 
 #, c-format
 msgid "mtimes file %s is too small"
@@ -17566,6 +17975,16 @@ msgstr "reverse-index-filen %s har versionen %<PRIu32> som inte stöds"
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "reverse-index-filen %s har hash-ID %<PRIu32> som inte stöds"
 
+msgid "invalid checksum"
+msgstr "ogiltig kontrollsumma"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "ogiltig rev-indexposition vid %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "baklängesindex-stycke för multi-pack-index har fel storlek"
+
 msgid "cannot both write and verify reverse index"
 msgstr "kan inte både skriva och bekräfta reverse-index"
 
@@ -17579,7 +17998,7 @@ msgstr "kunde inte göra %s läsbar"
 
 #, c-format
 msgid "could not write '%s' promisor file"
-msgstr "kunde inte skriva kontraktsfilen \"%s\""
+msgstr "kunde inte skriva kontraktsfilen ”%s”"
 
 msgid "offset before end of packfile (broken .idx?)"
 msgstr "offset före slutet av packfilen (trasig .idx?)"
@@ -17598,33 +18017,24 @@ msgstr "offset borton slutet av packindex för %s (trunkerat index?)"
 
 #, c-format
 msgid "malformed expiration date '%s'"
-msgstr "trasigt utlöpsdatum: \"%s\""
+msgstr "trasigt utlöpsdatum: ”%s”"
 
 #, c-format
 msgid "option `%s' expects \"always\", \"auto\", or \"never\""
-msgstr ""
-"flaggan \"%s\" antar \"always\" (alltid), \"auto\" eller \"never\" (aldrig)"
+msgstr "flaggan ”%s” antar ”always” (alltid), ”auto” eller ”never” (aldrig)"
 
 #, c-format
 msgid "malformed object name '%s'"
-msgstr "felformat objektnamn \"%s\""
+msgstr "felformat objektnamn ”%s”"
 
 #, c-format
 msgid "option `%s' expects \"%s\" or \"%s\""
-msgstr "flaggan \"%s\" kräver \"%s\" eller \"%s\""
+msgstr "flaggan ”%s” kräver ”%s” eller ”%s”"
 
 #, c-format
 msgid "%s requires a value"
 msgstr "%s behöver ett värde"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s är inkompatibel med %s"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s: inkompatibelt med något annat"
-
 #, c-format
 msgid "%s takes no value"
 msgstr "%s tar inget värde"
@@ -17637,13 +18047,17 @@ msgstr "%s är inte tillgängligt"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s förväntar ett icke-negativt heltalsvärde, med valfritt k/m/g-suffix"
 
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s är inkompatibel med %s"
+
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "tvetydig flagga: %s (kan vara --%s%s eller --%s%s)"
 
 #, c-format
 msgid "did you mean `--%s` (with two dashes)?"
-msgstr "menade du \"--%s\" (med två bindestreck)?"
+msgstr "menade du ”--%s” (med två bindestreck)?"
 
 #, c-format
 msgid "alias of --%s"
@@ -17654,15 +18068,15 @@ msgstr "behöver ett underkommando"
 
 #, c-format
 msgid "unknown option `%s'"
-msgstr "okänd flagga \"%s\""
+msgstr "okänd flagga ”%s”"
 
 #, c-format
 msgid "unknown switch `%c'"
-msgstr "okänd flagga \"%c\""
+msgstr "okänd flagga ”%c”"
 
 #, c-format
 msgid "unknown non-ascii option in string: `%s'"
-msgstr "okänd icke-ascii-flagga i strängen: \"%s\""
+msgstr "okänd icke-ascii-flagga i strängen: ”%s”"
 
 msgid "..."
 msgstr "..."
@@ -17708,6 +18122,10 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-TAL"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "motsatsen mot --no-%s"
+
 msgid "expiry-date"
 msgstr "giltig-till"
 
@@ -17723,6 +18141,9 @@ msgstr "var mer tyst"
 msgid "use <n> digits to display object names"
 msgstr "använd <n> siffror för att visa objektnamn"
 
+msgid "prefixed path to initial superproject"
+msgstr "inledande sökväg till start-överprojekt"
+
 msgid "how to strip spaces and #comments from message"
 msgstr "hur blanksteg och #kommentarer ska tas bort från meddelande"
 
@@ -17733,15 +18154,23 @@ msgid ""
 "with --pathspec-from-file, pathspec elements are separated with NUL character"
 msgstr "med --pathspec-from-file, sökvägsangivelser avdelas med NUL-tecken"
 
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "felaktigt booleskt miljövariabelvärde ”%s” för ”%s”"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "kunde inte tolka %s"
+
 #, c-format
 msgid "Could not make %s writable by group"
 msgstr "Kunde inte göra %s skrivbar för gruppen"
 
 msgid "Escape character '\\' not allowed as last character in attr value"
-msgstr "Specialtecknet \"\\\" tillåts inte som sista tecken i attributvärde"
+msgstr "Specialtecknet ”\\” tillåts inte som sista tecken i attributvärde"
 
 msgid "Only one 'attr:' specification is allowed."
-msgstr "Endast en \"attr:\"-angivelse tillåten."
+msgstr "Endast en ”attr:”-angivelse tillåten."
 
 msgid "attr spec must not be empty"
 msgstr "attr-angivelse kan inte vara tom"
@@ -17751,42 +18180,45 @@ msgid "invalid attribute name %s"
 msgstr "ogiltigt attributnamn %s"
 
 msgid "global 'glob' and 'noglob' pathspec settings are incompatible"
-msgstr ""
-"de globala sökvägsinställningarna \"glob\" och \"noglob\" är inkompatibla"
+msgstr "de globala sökvägsinställningarna ”glob” och ”noglob” är inkompatibla"
 
 msgid ""
 "global 'literal' pathspec setting is incompatible with all other global "
 "pathspec settings"
 msgstr ""
-"den globala sökvägsinställningen \"literal\" är inkompatibel med alla andra "
+"den globala sökvägsinställningen ”literal” är inkompatibel med alla andra "
 "globala sökvägsinställningar"
 
 msgid "invalid parameter for pathspec magic 'prefix'"
-msgstr "ogiltig parameter för sökvägsuttrycket för \"prefix\""
+msgstr "ogiltig parameter för sökvägsuttrycket för ”prefix”"
 
 #, c-format
 msgid "Invalid pathspec magic '%.*s' in '%s'"
-msgstr "Felaktigt sökvägsuttryck \"%.*s\" i \"%s\""
+msgstr "Felaktigt sökvägsuttryck ”%.*s” i ”%s”"
 
 #, c-format
 msgid "Missing ')' at the end of pathspec magic in '%s'"
-msgstr "\")\" saknas i slutet av sökvägsuttrycket för \"%s\""
+msgstr "”)” saknas i slutet av sökvägsuttrycket för ”%s”"
 
 #, c-format
 msgid "Unimplemented pathspec magic '%c' in '%s'"
-msgstr "Ej implementerat sökvägsuttryckmagi \"%c\" i \"%s\""
+msgstr "Ej implementerat sökvägsuttryckmagi ”%c” i ”%s”"
 
 #, c-format
 msgid "%s: 'literal' and 'glob' are incompatible"
-msgstr "%s: \"literal\" och \"glob\" är inkompatibla"
+msgstr "%s: ”literal” och ”glob” är inkompatibla"
+
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "”%s” är utanför katalogträdet"
 
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
-msgstr "%s: \"%s\" är utanför arkivet på \"%s\""
+msgstr "%s: ”%s” är utanför arkivet på ”%s”"
 
 #, c-format
 msgid "'%s' (mnemonic: '%c')"
-msgstr "\"%s\" (minnesstöd: \"%c\")"
+msgstr "”%s” (minnesstöd: ”%c”)"
 
 #, c-format
 msgid "%s: pathspec magic not supported by this command: %s"
@@ -17794,7 +18226,7 @@ msgstr "%s: sökvägsuttrycket hanteras inte av det här kommandot: %s"
 
 #, c-format
 msgid "pathspec '%s' is beyond a symbolic link"
-msgstr "sökvägsangivelsen \"%s\" är på andra sidan av en symbolisk länk"
+msgstr "sökvägsangivelsen ”%s” är på andra sidan av en symbolisk länk"
 
 #, c-format
 msgid "line is badly quoted: %s"
@@ -17810,7 +18242,7 @@ msgid "unable to write response end packet"
 msgstr "kunde inte skriva svarsavslutningspaket"
 
 msgid "flush packet write failed"
-msgstr "fel vid skrivning av \"flush\"-paket"
+msgstr "fel vid skrivning av ”flush”-paket"
 
 msgid "protocol error: impossibly long line"
 msgstr "protokollfel: omöjligt lång rad"
@@ -17865,46 +18297,56 @@ msgstr ""
 
 #, c-format
 msgid "promisor remote name cannot begin with '/': %s"
-msgstr "kontraktsfjärr kan inte börja med \"/\": %s"
+msgstr "kontraktsfjärr kan inte börja med ”/”: %s"
+
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "kunde inte hämta %s från kontraktsfjärr"
 
 msgid "object-info: expected flush after arguments"
-msgstr "object-info: förväntade \"flush\" efter argument"
+msgstr "object-info: förväntade ”flush” efter argument"
 
 msgid "Removing duplicate objects"
 msgstr "Tar bort duplicerade objekt"
 
 msgid "could not start `log`"
-msgstr "kunde inte starta \"log\""
+msgstr "kunde inte starta ”log”"
 
 msgid "could not read `log` output"
-msgstr "kunde inte läsa utdata från \"log\""
+msgstr "kunde inte läsa utdata från ”log”"
 
 #, c-format
 msgid "could not parse commit '%s'"
-msgstr "kunde inte tolka incheckningen \"%s\""
+msgstr "kunde inte tolka incheckningen ”%s”"
 
 #, c-format
 msgid ""
 "could not parse first line of `log` output: did not start with 'commit ': "
 "'%s'"
 msgstr ""
-"kunde inte tolka första raden i \"log\"-updata: börjar inte med \"commit \": "
-"\"%s\""
+"kunde inte tolka första raden i ”log”-updata: börjar inte med ”commit ”: ”%s”"
 
 #, c-format
 msgid "could not parse git header '%.*s'"
-msgstr "kunde inte tolka git-huvudet \"%.*s\""
+msgstr "kunde inte tolka git-huvudet ”%.*s”"
 
 msgid "failed to generate diff"
 msgstr "misslyckades skapa diff"
 
 #, c-format
 msgid "could not parse log for '%s'"
-msgstr "kunde inte tolka loggen för \"%s\""
+msgstr "kunde inte tolka loggen för ”%s”"
+
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "ogiltig extra överbliven ände: ”%s”"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "kan inte räkna ytterligare nyliga objekt"
 
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
-msgstr "lägger inte till filalias \"%s\" (\"%s\" finns redan i indexet)"
+msgstr "lägger inte till filalias ”%s” (”%s” finns redan i indexet)"
 
 msgid "cannot create an empty blob in the object database"
 msgstr "kan inte skapa tom blob i objektdatabasen"
@@ -17916,19 +18358,19 @@ msgstr ""
 
 #, c-format
 msgid "unable to index file '%s'"
-msgstr "kan inte indexera filen \"%s\""
+msgstr "kan inte indexera filen ”%s”"
 
 #, c-format
 msgid "unable to add '%s' to index"
-msgstr "kan inte lägga till \"%s\" till indexet"
+msgstr "kan inte lägga till ”%s” till indexet"
 
 #, c-format
 msgid "unable to stat '%s'"
-msgstr "kan inte ta status på \"%s\""
+msgstr "kan inte ta status på ”%s”"
 
 #, c-format
 msgid "'%s' appears as both a file and as a directory"
-msgstr "\"%s\" finns både som en fil och en katalog"
+msgstr "”%s” finns både som en fil och en katalog"
 
 msgid "Refresh index"
 msgstr "Uppdatera indexet"
@@ -17974,18 +18416,18 @@ msgstr "okänt format 0x%08x på indexpost"
 
 #, c-format
 msgid "malformed name field in the index, near path '%s'"
-msgstr "felformat namnfält i indexet, nära sökvägen \"%s\""
+msgstr "felformat namnfält i indexet, nära sökvägen ”%s”"
 
 msgid "unordered stage entries in index"
 msgstr "osorterade köposter i index"
 
 #, c-format
 msgid "multiple stage entries for merged file '%s'"
-msgstr "flera köposter för den sammanslagna filen \"%s\""
+msgstr "flera köposter för den sammanslagna filen ”%s”"
 
 #, c-format
 msgid "unordered stage entries for '%s'"
-msgstr "osorterade köposter för \"%s\""
+msgstr "osorterade köposter för ”%s”"
 
 #, c-format
 msgid "unable to create load_cache_entries thread: %s"
@@ -18021,7 +18463,7 @@ msgstr "kunde inte utföra join på load_index_extensions-tråden: %s"
 
 #, c-format
 msgid "could not freshen shared index '%s'"
-msgstr "kunde inte uppdatera delat index \"%s\""
+msgstr "kunde inte uppdatera delat index ”%s”"
 
 #, c-format
 msgid "broken index, expect %s in %s, got %s"
@@ -18033,10 +18475,6 @@ msgstr "kan inte skriva delat index för ett glest index"
 msgid "failed to convert to a sparse-index"
 msgstr "misslyckades omvandla till glest index"
 
-#, c-format
-msgid "could not stat '%s'"
-msgstr "kunde inte ta status på \"%s\""
-
 #, c-format
 msgid "unable to open git dir: %s"
 msgstr "kunde inte öppna git-katalog: %s"
@@ -18047,20 +18485,28 @@ msgstr "misslyckades ta bort länken: %s"
 
 #, c-format
 msgid "cannot fix permission bits on '%s'"
-msgstr "kan inte rätta behörighetsbitar på \"%s\""
+msgstr "kan inte rätta behörighetsbitar på ”%s”"
 
 #, c-format
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: kan inte återgå till kö 0"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "diff-status %c förväntades inte"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "ta bort ”%s”\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
 "Or you can abort the rebase with 'git rebase --abort'.\n"
 msgstr ""
-"Du kan rätta detta med \"git rebase --edit-todo\" följt av \"git rebase --"
-"continue\".\n"
-"Avbryt ombaseringen med \"git rebase --abort\".\n"
+"Du kan rätta detta med ”git rebase --edit-todo” följt av ”git rebase --"
+"continue.\n"
+"Avbryt ombaseringen med ”git rebase --abort”.\n"
 
 #, c-format
 msgid ""
@@ -18102,18 +18548,17 @@ msgstr ""
 "e, edit <incheckning> = använd incheckning, men stanna för tillägg\n"
 "s, squash <incheckning> = använd incheckning, men infoga i föregående "
 "incheckning\n"
-"f, fixup [-C | -c] <incheckning> = som \"squash\" men behåll bara "
+"f, fixup [-C | -c] <incheckning> = som ”squash” men behåll bara "
 "loggmeddelandet\n"
 "                   från föregående incheckning, såvida inte -C används, då "
 "används\n"
 "                   istället bara den här incheckningens meddelande; -c är "
 "samma\n"
 "                   som -C, men öppnar redigeringsprogrammet\n"
-"f, fixup <incheckning> = som \"squash\", men förkasta "
-"incheckningsmeddelandet\n"
+"f, fixup <incheckning> = som ”squash”, men förkasta incheckningsmeddelandet\n"
 "x, exec <kommando> = kör kommando (resten av raden) i skalet\n"
-"b, break = stoppa här (fortsätt ombaseringen senare med \"git rebase --"
-"continue\")\n"
+"b, break = stoppa här (fortsätt ombaseringen senare med git rebase --"
+"continue)\n"
 "d, drop <incheckning> = ta bort incheckning\n"
 "l, label <etikett> = ge aktuellt HEAD ett namn\n"
 "t, reset <etikett> = återställ HEAD till en etikett\n"
@@ -18138,7 +18583,7 @@ msgid ""
 "Do not remove any line. Use 'drop' explicitly to remove a commit.\n"
 msgstr ""
 "\n"
-"Ta inte bort rader. Använd \"drop\" för att specifikt förkasta en "
+"Ta inte bort rader. Använd ”drop” för att specifikt förkasta en "
 "incheckning.\n"
 
 msgid ""
@@ -18156,7 +18601,7 @@ msgid ""
 "\n"
 msgstr ""
 "\n"
-"Du redigerar \"todo\"-filen för en pågående interaktiv ombasering.\n"
+"Du redigerar ”todo”-filen för en pågående interaktiv ombasering.\n"
 "För att forsätta ombasera efter redigeringen, kör:\n"
 "    git rebase --continue\n"
 "\n"
@@ -18172,7 +18617,7 @@ msgstr ""
 
 #, c-format
 msgid "could not write '%s'."
-msgstr "kunde inte skriva \"%s\"."
+msgstr "kunde inte skriva ”%s”."
 
 #, c-format
 msgid ""
@@ -18191,19 +18636,16 @@ msgid ""
 "The possible behaviours are: ignore, warn, error.\n"
 "\n"
 msgstr ""
-"För att undvika det här meddelandet kan du använda \"drop\" för att "
-"explicit\n"
+"För att undvika det här meddelandet kan du använda ”drop” för att explicit\n"
 "kasta en incheckning.\n"
 "\n"
-"Använd \"git config rebase.missingCommitsCheck\" för att ändra "
-"varningsnivån.\n"
-"Möjliga bettenden är: \"ignore\" (ignorera), \"warn\" (varna), \"error"
-"\" (fel).\n"
+"Använd ”git config rebase.missingCommitsCheck” för att ändra varningsnivån.\n"
+"Möjliga bettenden är: ”ignore” (ignorera), ”warn” (varna), ”error” (fel).\n"
 "\n"
 
 #, c-format
 msgid "%s: 'preserve' superseded by 'merges'"
-msgstr "%s: \"preserve\" har ersatts av \"merges\""
+msgstr "%s: ”preserve” har ersatts av ”merges”"
 
 msgid "gone"
 msgstr "försvunnen"
@@ -18220,6 +18662,14 @@ msgstr "bakom %d"
 msgid "ahead %d, behind %d"
 msgstr "före %d, bakom %d"
 
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) tar inte argument"
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "okänt %%(%.*s)-argument: %s"
+
 #, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "förväntat format: %%(color:<color>)"
@@ -18237,40 +18687,36 @@ msgid "Integer value expected refname:rstrip=%s"
 msgstr "Heltalsvärde förväntades refname:rstrip=%s"
 
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "okänt %%(%s)-argument: %s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "förväntade %%(trailers:key=<värde>)"
 
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) tar inte argument"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "okänt %%(trailers)-argument: %s"
 
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) tar inte argument"
+msgid "positive value expected contents:lines=%s"
+msgstr "positivt värde förväntat contents:lines=%s"
 
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) tar inte argument"
+msgid "argument expected for %s"
+msgstr "argument förväntades för %s"
 
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "förväntade %%(trailers:key=<värde>)"
+msgid "positive value expected %s=%s"
+msgstr "positivt värde förväntat %s=%s"
 
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "okänt %%(trailers)-argument: %s"
+msgid "cannot fully parse %s=%s"
+msgstr "kan inte helt tolka %s=%s"
 
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "positivt värde förväntat contents:lines=%s"
+msgid "value expected %s="
+msgstr "vädre förväntades %s="
 
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
-msgstr "positivt värde förväntat \"%s\" i %%(%s)"
-
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "okänd e-postalternativ: %s"
+msgstr "positivt värde förväntat ”%s” i %%(%s)"
 
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
@@ -18284,13 +18730,17 @@ msgstr "okänd position:%s"
 msgid "unrecognized width:%s"
 msgstr "okänd bredd:%s"
 
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "okänt %%(%s)-argument: %s"
+
 #, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "positiv bredd förväntad med atomen %%(align)"
 
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) tar inte argument"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "förväntat format: %%(ahead-behind:<incheckning-igt>)"
 
 #, c-format
 msgid "malformed field name: %.*s"
@@ -18303,8 +18753,7 @@ msgstr "okänt fältnamn: %.*s"
 #, c-format
 msgid ""
 "not a git repository, but the field '%.*s' requires access to object data"
-msgstr ""
-"inte ett git-arkiv, men fältet \"%.*s\" kräver tillgång till objektdata"
+msgstr "inte ett git-arkiv, men fältet ”%.*s” kräver tillgång till objektdata"
 
 #, c-format
 msgid "format: %%(%s) atom used without a %%(%s) atom"
@@ -18338,6 +18787,9 @@ msgstr "kommandot förkastar atom %%(%.*s)"
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s kan inte användas med --python, --shell, --tcl"
 
+msgid "failed to run 'describe'"
+msgstr "misslyckades att köra ”describe”"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(ingen gren, ombaserar %s)"
@@ -18348,7 +18800,7 @@ msgstr "(ingen gren, ombaserar frånkopplat HEAD %s)"
 
 #, c-format
 msgid "(no branch, bisect started on %s)"
-msgstr "(ingen gren, \"bisect\" startad på %s)"
+msgstr "(ingen gren, ”bisect” startad på %s)"
 
 #, c-format
 msgid "(HEAD detached at %s)"
@@ -18371,7 +18823,7 @@ msgstr "parse_object_buffer misslyckades på %s för %s"
 
 #, c-format
 msgid "malformed object at '%s'"
-msgstr "felformat objekt vid \"%s\""
+msgstr "felformat objekt vid ”%s”"
 
 #, c-format
 msgid "ignoring ref with broken name %s"
@@ -18391,7 +18843,7 @@ msgstr "felformat objektnamn %s"
 
 #, c-format
 msgid "option `%s' must point to a commit"
-msgstr "flaggan \"%s\" måste peka på en incheckning"
+msgstr "flaggan ”%s” måste peka på en incheckning"
 
 msgid "key"
 msgstr "nyckel"
@@ -18399,17 +18851,20 @@ msgstr "nyckel"
 msgid "field name to sort on"
 msgstr "fältnamn att sortera på"
 
+msgid "exclude refs which match pattern"
+msgstr "uteslut referenser som motsvarar mönster"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "inte en referenslogg: %s"
 
 #, c-format
 msgid "no reflog for '%s'"
-msgstr "ingen referenslogg för \"%s\""
+msgstr "ingen referenslogg för ”%s”"
 
 #, c-format
 msgid "%s does not point to a valid object!"
-msgstr "\"%s\" pekar inte på ett giltigt objekt!"
+msgstr "”%s” pekar inte på ett giltigt objekt!"
 
 #, c-format
 msgid ""
@@ -18424,20 +18879,20 @@ msgid ""
 "\n"
 "\tgit branch -m <name>\n"
 msgstr ""
-"Använder \"%s\" som namn för den inledande grenen. Detta förvalda grennamn\n"
+"Använder ”%s” som namn för den inledande grenen. Detta förvalda grennamn\n"
 "kan ändras i framtiden. För att välja vilket namn som ska användas på\n"
 "den inledande grenen i alla nya arkiv, och dölja denna varning, kör du:\n"
 "\n"
 "\tgit config --global init.defaultBranch <namn>\n"
 "\n"
-"Namn som ofta används istället för \"master\" är \"main\", \"trunk\" och\n"
-"\"development\". Den nyskapade grenen kan ges nytt namn med kommandot:\n"
+"Namn som ofta används istället för ”master” är ”main”, ”trunk” och\n"
+"”development”. Den nyskapade grenen kan ges nytt namn med kommandot:\n"
 "\n"
 "\tgit branch -m <namn>\n"
 
 #, c-format
 msgid "could not retrieve `%s`"
-msgstr "kunde inte hämta \"%s\""
+msgstr "kunde inte hämta ”%s”"
 
 #, c-format
 msgid "invalid branch name: %s = %s"
@@ -18461,15 +18916,15 @@ msgstr "loggen för %s är tom"
 
 #, c-format
 msgid "refusing to update ref with bad name '%s'"
-msgstr "vägrar uppdatera referens med trasigt namn \"%s\""
+msgstr "vägrar uppdatera referens med trasigt namn ”%s”"
 
 #, c-format
 msgid "update_ref failed for ref '%s': %s"
-msgstr "update_ref misslyckades för referensen \"%s\": %s"
+msgstr "update_ref misslyckades för referensen ”%s”: %s"
 
 #, c-format
 msgid "multiple updates for ref '%s' not allowed"
-msgstr "flera uppdateringar för referensen \"%s\" tillåts inte"
+msgstr "flera uppdateringar för referensen ”%s” tillåts inte"
 
 msgid "ref updates forbidden inside quarantine environment"
 msgstr "referensuppdateringar förbjudna i karantänmiljö"
@@ -18479,11 +18934,11 @@ msgstr "referensuppdateringar avbrutna av krok"
 
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
-msgstr "\"%s\" finns; kan inte skapa \"%s\""
+msgstr "”%s” finns; kan inte skapa ”%s”"
 
 #, c-format
 msgid "cannot process '%s' and '%s' at the same time"
-msgstr "kan inte hantera \"%s\" och \"%s\" samtidigt"
+msgstr "kan inte hantera ”%s” och ”%s” samtidigt"
 
 #, c-format
 msgid "could not remove reference %s"
@@ -18499,11 +18954,11 @@ msgstr "kunde inte ta bort referenser: %s"
 
 #, c-format
 msgid "invalid refspec '%s'"
-msgstr "felaktig referensspecifikation: \"%s\""
+msgstr "felaktig referensspecifikation: ”%s”"
 
 #, c-format
 msgid "invalid quoting in push-option value: '%s'"
-msgstr "felaktig citering på värde för push-option: \"%s\""
+msgstr "felaktig citering på värde för push-option: ”%s”"
 
 #, c-format
 msgid "%sinfo/refs not valid: is this a git repository?"
@@ -18514,23 +18969,23 @@ msgstr "ogiltigt svar från servern; förväntade tjänst, fick flush-paket"
 
 #, c-format
 msgid "invalid server response; got '%s'"
-msgstr "ogiltigt svar från servern; fick \"%s\""
+msgstr "ogiltigt svar från servern; fick ”%s”"
 
 #, c-format
 msgid "repository '%s' not found"
-msgstr "arkivet \"%s\" hittades inte"
+msgstr "arkivet ”%s” hittades inte"
 
 #, c-format
 msgid "Authentication failed for '%s'"
-msgstr "Autentisering misslyckades \"%s\""
+msgstr "Autentisering misslyckades ”%s”"
 
 #, c-format
 msgid "unable to access '%s' with http.pinnedPubkey configuration: %s"
-msgstr "kan inte nå \"%s\" med http.pinnedPubkey inställt till: %s"
+msgstr "kan inte nå ”%s” med http.pinnedPubkey inställt till: %s"
 
 #, c-format
 msgid "unable to access '%s': %s"
-msgstr "kan inte komma åt \"%s\": %s"
+msgstr "kan inte komma åt ”%s”: %s"
 
 #, c-format
 msgid "redirecting to %s"
@@ -18586,18 +19041,18 @@ msgstr "kan inte hämta med sha1 över smart http"
 
 #, c-format
 msgid "protocol error: expected sha/ref, got '%s'"
-msgstr "protokollfel: förväntade sha/ref, fick \"%s\""
+msgstr "protokollfel: förväntade sha/ref, fick ”%s”"
 
 #, c-format
 msgid "http transport does not support %s"
 msgstr "http-transporten stöder inte %s"
 
 msgid "protocol error: expected '<url> <path>', missing space"
-msgstr "protokollfel: förväntade \"<url> <sökväg>\", saknar blanksteg"
+msgstr "protokollfel: förväntade ”<url> <sökväg>”, saknar blanksteg"
 
 #, c-format
 msgid "failed to download file at URL '%s'"
-msgstr "misslyckades hämta filen på URL \"%s\""
+msgstr "misslyckades hämta filen på URL ”%s”"
 
 msgid "git-http-push failed"
 msgstr "git-http-push misslyckades"
@@ -18613,11 +19068,11 @@ msgstr "remote-curl: försökte ta emot utan lokalt arkiv"
 
 #, c-format
 msgid "remote-curl: unknown command '%s' from git"
-msgstr "remote-curl: okänt kommando \"%s\" från git"
+msgstr "remote-curl: okänt kommando ”%s” från git"
 
 #, c-format
 msgid "config remote shorthand cannot begin with '/': %s"
-msgstr "konfigurerad kortform för fjärr kan inte börja med \"/\": %s"
+msgstr "konfigurerad kortform för fjärr kan inte börja med ”/”: %s"
 
 msgid "more than one receivepack given, using the first"
 msgstr "mer än en receivepack angavs, använder den första"
@@ -18627,11 +19082,11 @@ msgstr "mer än en uploadpack angavs, använder den första"
 
 #, c-format
 msgid "unrecognized value transfer.credentialsInUrl: '%s'"
-msgstr "okänt värde transfer.credentialsInUrl: \"%s\""
+msgstr "okänt värde transfer.credentialsInUrl: ”%s”"
 
 #, c-format
 msgid "URL '%s' uses plaintext credentials"
-msgstr "URL \"%s\" använder inloggningsuppgifter i klartext"
+msgstr "URL ”%s” använder inloggningsuppgifter i klartext"
 
 #, c-format
 msgid "Cannot fetch both %s and %s to %s"
@@ -18647,11 +19102,11 @@ 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 \"*\""
+msgstr "nyckeln ”%s” i mönstret innehåller ingen ”*”"
 
 #, c-format
 msgid "value '%s' of pattern has no '*'"
-msgstr "värdet \"%s\" i mönstret innehåller ingen \"*\""
+msgstr "värdet ”%s” i mönstret innehåller ingen ”*”"
 
 #, c-format
 msgid "src refspec %s does not match any"
@@ -18678,11 +19133,11 @@ msgid ""
 "Neither worked, so we gave up. You must fully qualify the ref."
 msgstr ""
 "Målet du angav är inte ett komplett referensamn (dvs.,\n"
-"startar med \"refs/\"). Vi försökte gissa vad du menade genom att:\n"
+"startar med ”refs/”). Vi försökte gissa vad du menade genom att:\n"
 "\n"
-"- Se efter en referens som motsvarar \"%s\" på fjärrsidan.\n"
-"- Se om <källan> som sänds (\"%s\")\n"
-"  är en referens i \"refs/{heads,tags}/\". Om så lägger vi till\n"
+"- Se efter en referens som motsvarar ”%s” på fjärrsidan.\n"
+"- Se om <källan> som sänds (”%s”)\n"
+"  är en referens i ”refs/{heads,tags}/”. Om så lägger vi till\n"
 "  motsvarande refs/{heads,tags}/-prefix på fjärrsidan.\n"
 "\n"
 "Inget av dem fungerade, så vi gav upp. Ange fullständig referens."
@@ -18695,7 +19150,7 @@ msgid ""
 msgstr ""
 "<Källa>-delen av ref.spec-en är ett incheckningsobjekt.\n"
 "Var det meningen att skapa en ny gren genom att sända\n"
-"till \"%s:refs/heads/%s\"?"
+"till ”%s:refs/heads/%s”?"
 
 #, c-format
 msgid ""
@@ -18705,7 +19160,7 @@ msgid ""
 msgstr ""
 "<Källa>-delen av ref.spec-en är ett taggobjekt.\n"
 "Var det meningen att skapa en ny tagg genom att sända\n"
-"till \"%s:refs/tags/%s\"?"
+"till ”%s:refs/tags/%s”?"
 
 #, c-format
 msgid ""
@@ -18715,7 +19170,7 @@ msgid ""
 msgstr ""
 "<Källa>-delen av ref.spec-en är ett trädobjekt.\n"
 "Var det meningen att tagga ett nytt träd genom att sända\n"
-"till \"%s:refs/tags/%s\"?"
+"till ”%s:refs/tags/%s”?"
 
 #, c-format
 msgid ""
@@ -18725,7 +19180,7 @@ msgid ""
 msgstr ""
 "<Källa>-delen av ref.spec-en är ett blobobjekt.\n"
 "Var det meningen att tagga en ny blob genom att sända\n"
-"till \"%s:refs/tags/%s\"?"
+"till ”%s:refs/tags/%s”?"
 
 #, c-format
 msgid "%s cannot be resolved to branch"
@@ -18733,48 +19188,48 @@ msgstr "%s kan inte slås upp till en gren"
 
 #, c-format
 msgid "unable to delete '%s': remote ref does not exist"
-msgstr "kan inte ta bort \"%s\": fjärreferensen finns inte"
+msgstr "kan inte ta bort ”%s”: fjärreferensen finns inte"
 
 #, c-format
 msgid "dst refspec %s matches more than one"
-msgstr "fjärr-referensspecifikationen \"%s\" motsvarar mer än en"
+msgstr "fjärr-referensspecifikationen ”%s” motsvarar mer än en"
 
 #, c-format
 msgid "dst ref %s receives from more than one src"
-msgstr "fjärr-referensen \"%s\" hämtar från mer än en källa"
+msgstr "fjärr-referensen ”%s” hämtar från mer än en källa"
 
 msgid "HEAD does not point to a branch"
 msgstr "HEAD pekar inte på en gren"
 
 #, c-format
 msgid "no such branch: '%s'"
-msgstr "okänd gren: \"%s\""
+msgstr "okänd gren: ”%s”"
 
 #, c-format
 msgid "no upstream configured for branch '%s'"
-msgstr "ingen standarduppström angiven för grenen \"%s\""
+msgstr "ingen standarduppström angiven för grenen ”%s”"
 
 #, c-format
 msgid "upstream branch '%s' not stored as a remote-tracking branch"
-msgstr "uppströmsgrenen \"%s\" är inte lagrad som en fjärrspårande gren"
+msgstr "uppströmsgrenen ”%s” är inte lagrad som en fjärrspårande gren"
 
 #, c-format
 msgid "push destination '%s' on remote '%s' has no local tracking branch"
-msgstr "push-målet \"%s\" på fjärren \"%s\" har ingen lokalt spårande gren"
+msgstr "push-målet ”%s” på fjärren ”%s” har ingen lokalt spårande gren"
 
 #, c-format
 msgid "branch '%s' has no remote for pushing"
-msgstr "grenen \"%s\" har ingen fjärr för \"push\""
+msgstr "grenen ”%s” har ingen fjärr för ”push”"
 
 #, c-format
 msgid "push refspecs for '%s' do not include '%s'"
-msgstr "\"push\"-referensspecifikation för \"%s\" innehåller inte \"%s\""
+msgstr "”push”-referensspecifikation för ”%s” innehåller inte ”%s”"
 
 msgid "push has no destination (push.default is 'nothing')"
-msgstr "\"push\" har inget mål (push.default är \"ingenting\")"
+msgstr "”push” har inget mål (push.default är ”ingenting”)"
 
 msgid "cannot resolve 'simple' push to a single destination"
-msgstr "\"enkel push\" motsvarar flera olika mål"
+msgstr "”enkel push” motsvarar flera olika mål"
 
 #, c-format
 msgid "couldn't find remote ref %s"
@@ -18782,47 +19237,47 @@ msgstr "Kunde inte hitta fjärr-referensen %s"
 
 #, c-format
 msgid "* Ignoring funny ref '%s' locally"
-msgstr "* Ignorerar märklig referens \"%s\" lokalt"
+msgstr "* Ignorerar märklig referens ”%s” lokalt"
 
 #, c-format
 msgid "Your branch is based on '%s', but the upstream is gone.\n"
-msgstr "Din gren är baserad på \"%s\", men den har försvunnit uppströms.\n"
+msgstr "Din gren är baserad på ”%s”, men den har försvunnit uppströms.\n"
 
 msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
-msgstr "  (använd \"git branch --unset-upstream\" för att rätta)\n"
+msgstr "  (använd ”git branch --unset-upstream” för att rätta)\n"
 
 #, c-format
 msgid "Your branch is up to date with '%s'.\n"
-msgstr "Din gren är à jour med \"%s\".\n"
+msgstr "Din gren är à jour med ”%s”.\n"
 
 #, c-format
 msgid "Your branch and '%s' refer to different commits.\n"
-msgstr "Din gren och \"%s\" pekar på olika incheckningar.\n"
+msgstr "Din gren och ”%s” pekar på olika incheckningar.\n"
 
 #, c-format
 msgid "  (use \"%s\" for details)\n"
-msgstr "  (använd \"%s\" för detaljer)\n"
+msgstr "  (använd ”%s” för detaljer)\n"
 
 #, c-format
 msgid "Your branch is ahead of '%s' by %d commit.\n"
 msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
-msgstr[0] "Din gren ligger före \"%s\" med %d incheckning.\n"
-msgstr[1] "Din gren ligger före \"%s\" med %d incheckningar.\n"
+msgstr[0] "Din gren ligger före ”%s” med %d incheckning.\n"
+msgstr[1] "Din gren ligger före ”%s” med %d incheckningar.\n"
 
 msgid "  (use \"git push\" to publish your local commits)\n"
-msgstr "  (använd \"git push\" för att publicera dina lokala incheckningar)\n"
+msgstr "  (använd ”git push” för att publicera dina lokala incheckningar)\n"
 
 #, c-format
 msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
 msgid_plural ""
 "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
 msgstr[0] ""
-"Din gren ligger efter \"%s\" med %d incheckning, och kan snabbspolas.\n"
+"Din gren ligger efter ”%s” med %d incheckning, och kan snabbspolas.\n"
 msgstr[1] ""
-"Din gren ligger efter \"%s\" med %d incheckningar, och kan snabbspolas.\n"
+"Din gren ligger efter ”%s” med %d incheckningar, och kan snabbspolas.\n"
 
 msgid "  (use \"git pull\" to update your local branch)\n"
-msgstr "  (använd \"git pull\" för att uppdatera din lokala gren)\n"
+msgstr "  (använd ”git pull” för att uppdatera din lokala gren)\n"
 
 #, c-format
 msgid ""
@@ -18832,22 +19287,23 @@ msgid_plural ""
 "Your branch and '%s' have diverged,\n"
 "and have %d and %d different commits each, respectively.\n"
 msgstr[0] ""
-"Din gren och \"%s\" har divergerat,\n"
+"Din gren och ”%s” har divergerat,\n"
 "och har %d respektive %d olika incheckning.\n"
 msgstr[1] ""
-"Din gren och \"%s\" har divergerat,\n"
+"Din gren och ”%s” har divergerat,\n"
 "och har %d respektive %d olika incheckningar.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (använd \"git pull\" för att slå ihop fjärrgrenen med din egen)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr "  (använd ”git pull” om du vill integrera fjärrgrenen med din egen)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
-msgstr "kan inte tolka förväntat objektnamn \"%s\""
+msgstr "kan inte tolka förväntat objektnamn ”%s”"
 
 #, c-format
 msgid "cannot strip one component off url '%s'"
-msgstr "kan inte ta bort en komponent från url:en \"%s\""
+msgstr "kan inte ta bort en komponent från url:en ”%s”"
 
 #, c-format
 msgid "bad replace ref name: %s"
@@ -18869,59 +19325,55 @@ msgstr "kunde inte skriva rerere-post"
 
 #, c-format
 msgid "there were errors while writing '%s' (%s)"
-msgstr "fel vid skrivning av \"%s\" (%s)"
+msgstr "fel vid skrivning av ”%s” (%s)"
 
 #, c-format
 msgid "could not parse conflict hunks in '%s'"
-msgstr "kunde inte tolka konflikt-stycket i \"%s\""
+msgstr "kunde inte tolka konflikt-stycket i ”%s”"
 
 #, c-format
 msgid "failed utime() on '%s'"
-msgstr "\"utime()\" misslyckades på \"%s\""
+msgstr "”utime()” misslyckades på ”%s”"
 
 #, c-format
 msgid "writing '%s' failed"
-msgstr "misslyckades skriva \"%s\""
+msgstr "misslyckades skriva ”%s”"
 
 #, c-format
 msgid "Staged '%s' using previous resolution."
-msgstr "Köade \"%s\" med sparad lösning."
+msgstr "Köade ”%s” med sparad lösning."
 
 #, c-format
 msgid "Recorded resolution for '%s'."
-msgstr "Sparade lösning för \"%s\"."
+msgstr "Sparade lösning för ”%s”."
 
 #, c-format
 msgid "Resolved '%s' using previous resolution."
-msgstr "Löste \"%s\" med tidigare lösning."
+msgstr "Löste ”%s” med tidigare lösning."
 
 #, c-format
 msgid "cannot unlink stray '%s'"
-msgstr "kan inte ta bort lös länk \"%s\""
+msgstr "kan inte ta bort lös länk ”%s”"
 
 #, c-format
 msgid "Recorded preimage for '%s'"
-msgstr "Sparade förhandsbild för \"%s\""
+msgstr "Sparade förhandsbild för ”%s”"
 
 #, c-format
 msgid "failed to update conflicted state in '%s'"
-msgstr "misslyckades uppdatera tillstånd för sammanslagningsproblem i \"%s\""
+msgstr "misslyckades uppdatera tillstånd för sammanslagningsproblem i ”%s”"
 
 #, c-format
 msgid "no remembered resolution for '%s'"
-msgstr "inget sparat sammanslagningsresultat för \"%s\""
-
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "kan inte ta bort länken \"%s\""
+msgstr "inget sparat sammanslagningsresultat för ”%s”"
 
 #, c-format
 msgid "Updated preimage for '%s'"
-msgstr "Uppdaterade förhandsbild för \"%s\""
+msgstr "Uppdaterade förhandsbild för ”%s”"
 
 #, c-format
 msgid "Forgot resolution for '%s'\n"
-msgstr "Glömde lösning för \"%s\"\n"
+msgstr "Glömde lösning för ”%s”\n"
 
 msgid "unable to open rr-cache directory"
 msgstr "kan inte uppdatera katalogen rr-cache"
@@ -18936,23 +19388,34 @@ msgstr "kunde inte bestämma HEAD-revision"
 msgid "failed to find tree of %s"
 msgstr "kunde inte hitta trädet för %s."
 
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "sktionen för dolda referenser stöds ej: %s"
+
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= angavs mer än en gång"
+
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
-msgstr "resolve-undo registrerar \"%s\" som saknas"
+msgstr "resolve-undo registrerar ”%s” som saknas"
 
 #, c-format
 msgid "could not get commit for ancestry-path argument %s"
-msgstr "kunde inte hämta incheckning för \"ancestry-path\"-argumentet %s"
+msgstr "kunde inte hämta incheckning för ”ancestry-path”-argumentet %s"
 
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<paketfil> stöds inte längre"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "ogiltig flagga ”%s” i --stdin-läge"
+
 msgid "your current branch appears to be broken"
 msgstr "din nuvarande gren verkar vara trasig"
 
 #, c-format
 msgid "your current branch '%s' does not have any commits yet"
-msgstr "din nuvarande gren \"%s\" innehåller ännu inte några incheckningar"
+msgstr "din nuvarande gren ”%s” innehåller ännu inte några incheckningar"
 
 msgid "object filtering requires --objects"
 msgstr "objektfiltrering kräver --objects"
@@ -18966,11 +19429,11 @@ msgstr "kan inte skapa asynkron tråd: %s"
 
 #, c-format
 msgid "'%s' does not exist"
-msgstr "\"%s\" finns inte"
+msgstr "”%s” finns inte"
 
 #, c-format
 msgid "could not switch to '%s'"
-msgstr "kunde inte växla till \"%s\""
+msgstr "kunde inte växla till ”%s”"
 
 msgid "need a working directory"
 msgstr "behöver en arbetskatalog"
@@ -19005,7 +19468,7 @@ msgstr "kunde inte ta bort enrollering"
 
 #, c-format
 msgid "remote HEAD is not a branch: '%.*s'"
-msgstr "HEAD hos fjärren är inte en gren: \"%.*s\""
+msgstr "HEAD hos fjärren är inte en gren: ”%.*s”"
 
 msgid "failed to get default branch name from remote; using local default"
 msgstr ""
@@ -19032,28 +19495,35 @@ msgstr "skapa komplett arbetskatalog vid kloning"
 msgid "only download metadata for the branch that will be checked out"
 msgstr "hämta endast metadata för grenen som skall checkas ut"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<flaggor>] [--] <arkiv> [<kat>]"
+msgid "create repository within 'src' directory"
+msgstr "skapa arkiv inuti katalogen ”src”"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <huvudgren>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enrollering>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
-msgstr "Kan inte härleda arbetsträdsnamn från \"%s\""
+msgstr "Kan inte härleda arbetsträdsnamn från ”%s”"
 
 #, c-format
 msgid "directory '%s' exists already"
-msgstr "katalogen \"%s\" finns redan"
+msgstr "katalogen ”%s” finns redan"
 
 #, c-format
 msgid "failed to get default branch for '%s'"
-msgstr "misslyckades hämta standardgren för \"%s\""
+msgstr "misslyckades hämta standardgren för ”%s”"
 
 #, c-format
 msgid "could not configure remote in '%s'"
-msgstr "kunde inte ställa in fjärr i \"%s\""
+msgstr "kunde inte ställa in fjärr i ”%s”"
 
 #, c-format
 msgid "could not configure '%s'"
-msgstr "kunde inte ställa in \"%s\""
+msgstr "kunde inte ställa in ”%s”"
 
 msgid "partial clone failed; attempting full clone"
 msgstr "delvis klon misslyckades; försöker med fullständig klon"
@@ -19065,7 +19535,7 @@ msgid "scalar diagnose [<enlistment>]"
 msgstr "scalar diagnose [<enrollering>]"
 
 msgid "`scalar list` does not take arguments"
-msgstr "\"scalar list\" tar inte argument"
+msgstr "”scalar list” tar inte argument"
 
 msgid "scalar register [<enlistment>]"
 msgstr "scalar register [<enrollering>]"
@@ -19080,8 +19550,32 @@ msgid "--all or <enlistment>, but not both"
 msgstr "--all eller <enrollering>, men inte bägge"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "git-arkiv försvunnet i \"%s\""
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "kunde inte ta bort gammal scalar.repo ”%s”"
+
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "tog bort gammal scalar.repo ”%s”"
+
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "arkivet ”%s” har en annan ägare"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "arkivet ”%s” har ett formatproblem"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "arkivet hittades inte i ”%s”"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"för att avregistrera arkivet från Scalar, kör\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -19092,7 +19586,7 @@ msgstr ""
 
 #, c-format
 msgid "no such task: '%s'"
-msgstr "okänd uppgift: \"%s\""
+msgstr "okänd uppgift: ”%s”"
 
 msgid "scalar unregister [<enlistment>]"
 msgstr "scalar unregister [<enrollering>]"
@@ -19117,7 +19611,7 @@ msgstr "-C kräver en <katalog>"
 
 #, c-format
 msgid "could not change to '%s'"
-msgstr "kunde inte byta till \"%s\""
+msgstr "kunde inte byta till ”%s”"
 
 msgid "-c requires a <key>=<value> argument"
 msgstr "-c kräver ett argument på formen <nyckel>=<värde>"
@@ -19172,11 +19666,11 @@ msgstr "mottagarsidan stöder inte push-flaggor"
 
 #, c-format
 msgid "invalid commit message cleanup mode '%s'"
-msgstr "felaktigt incheckningsmeddelandestädningsläge \"%s\""
+msgstr "felaktigt incheckningsmeddelandestädningsläge ”%s”"
 
 #, c-format
 msgid "could not delete '%s'"
-msgstr "kunde inte ta bort \"%s\""
+msgstr "kunde inte ta bort ”%s”"
 
 msgid "revert"
 msgstr "revert"
@@ -19196,7 +19690,7 @@ msgid ""
 "with 'git add <paths>' or 'git rm <paths>'"
 msgstr ""
 "efter att ha löst konflikterna, markera de rättade sökvägarna\n"
-"med \"git add <sökvägar>\" eller \"git rm <sökvägar>\""
+"med ”git add <sökvägar>” eller ”git rm <sökvägar>”"
 
 msgid ""
 "After resolving the conflicts, mark them with\n"
@@ -19207,11 +19701,11 @@ msgid ""
 "run \"git cherry-pick --abort\"."
 msgstr ""
 "Efter att ha löst konflikterna, märk dem med\n"
-"\"git add/rm <sökvägsangivelse>\" och kör sedan\n"
-"\"git cherry-pick --continue\".\n"
-"Du kan hoppa över incheckningen istället med \"git cherry-pick --skip\"\n"
-"För att avbryta och återgå till där du var före \"git cherry-pick\",\n"
-"kör \"git cherry-pick --abort\"."
+"”git add/rm <sökvägsangivelse>” och kör sedan\n"
+"”git cherry-pick --continue”.\n"
+"Du kan hoppa över incheckningen istället med ”git cherry-pick --skip”\n"
+"För att avbryta och återgå till där du var före ”git cherry-pick”,\n"
+"kör ”git cherry-pick --abort”."
 
 msgid ""
 "After resolving the conflicts, mark them with\n"
@@ -19222,34 +19716,30 @@ msgid ""
 "run \"git revert --abort\"."
 msgstr ""
 "Efter att ha löst konflikterna, märk dem med\n"
-"\"git add/rm <sökvägsangivelse>\" och kör sedan\n"
-"\"git revert --continue\".\n"
-"Du kan hoppa över incheckningen istället med \"git revert --skip\"\n"
-"För att avbryta och återgå till där du var före \"git revert\",\n"
-"kör \"git revert --abort\"."
+"”git add/rm <sökvägsangivelse>” och kör sedan\n"
+"”git revert --continue”.\n"
+"Du kan hoppa över incheckningen istället med ”git revert --skip”\n"
+"För att avbryta och återgå till där du var före ”git revert”,\n"
+"kör ”git revert --abort”."
 
 #, c-format
 msgid "could not lock '%s'"
-msgstr "kunde inte låsa \"%s\""
-
-#, c-format
-msgid "could not write to '%s'"
-msgstr "kunde inte skriva till \"%s\""
+msgstr "kunde inte låsa ”%s”"
 
 #, c-format
 msgid "could not write eol to '%s'"
-msgstr "kunde inte skriva radslut till \"%s\""
+msgstr "kunde inte skriva radslut till ”%s”"
 
 #, c-format
 msgid "failed to finalize '%s'"
-msgstr "misslyckades färdigställa \"%s\""
+msgstr "misslyckades färdigställa ”%s”"
 
 #, c-format
 msgid "your local changes would be overwritten by %s."
 msgstr "dina lokala ändringar skulle skrivas över av %s."
 
 msgid "commit your changes or stash them to proceed."
-msgstr "checka in dina ändringar eller använd \"stash\" för att fortsätta."
+msgstr "checka in dina ändringar eller använd ”stash” för att fortsätta."
 
 #. TRANSLATORS: %s will be "revert", "cherry-pick" or
 #. "rebase".
@@ -19266,33 +19756,33 @@ msgstr "kunde inte bestämma HEAD:s incheckning"
 
 #, c-format
 msgid "no key present in '%.*s'"
-msgstr "ingen nyckel i  \"%.*s\""
+msgstr "ingen nyckel i  ”%.*s”"
 
 #, c-format
 msgid "unable to dequote value of '%s'"
-msgstr "kan inte ta bort citering av värdet \"%s\""
+msgstr "kan inte ta bort citering av värdet ”%s”"
 
 msgid "'GIT_AUTHOR_NAME' already given"
-msgstr "\"GIT_AUTHOR_NAME\" har redan angivits"
+msgstr "”GIT_AUTHOR_NAME” har redan angivits"
 
 msgid "'GIT_AUTHOR_EMAIL' already given"
-msgstr "\"GIT_AUTHOR_EMAIL\" har redan angivits"
+msgstr "”GIT_AUTHOR_EMAIL” har redan angivits"
 
 msgid "'GIT_AUTHOR_DATE' already given"
-msgstr "\"GIT_AUTHOR_DATE\" har redan angivits"
+msgstr "”GIT_AUTHOR_DATE” har redan angivits"
 
 #, c-format
 msgid "unknown variable '%s'"
-msgstr "okänd variabel \"%s\""
+msgstr "okänd variabel ”%s”"
 
 msgid "missing 'GIT_AUTHOR_NAME'"
-msgstr "\"GIT_AUTHOR_NAME\" saknas"
+msgstr "”GIT_AUTHOR_NAME” saknas"
 
 msgid "missing 'GIT_AUTHOR_EMAIL'"
-msgstr "\"GIT_AUTHOR_EMAIL\" saknas"
+msgstr "”GIT_AUTHOR_EMAIL” saknas"
 
 msgid "missing 'GIT_AUTHOR_DATE'"
-msgstr "\"GIT_AUTHOR_DATE\" saknas"
+msgstr "”GIT_AUTHOR_DATE” saknas"
 
 #, c-format
 msgid ""
@@ -19323,7 +19813,7 @@ msgstr ""
 "  git rebase --continue\n"
 
 msgid "'prepare-commit-msg' hook failed"
-msgstr "kroken \"prepare-commit-msg\" misslyckades"
+msgstr "kroken ”prepare-commit-msg” misslyckades"
 
 msgid ""
 "Your name and email address were configured automatically based\n"
@@ -19402,11 +19892,11 @@ msgstr "kunde inte tolka incheckningens författare"
 
 #, c-format
 msgid "unable to read commit message from '%s'"
-msgstr "kunde inte läsa incheckningsmeddelande från \"%s\""
+msgstr "kunde inte läsa incheckningsmeddelande från ”%s”"
 
 #, c-format
 msgid "invalid author identity '%s'"
-msgstr "ogiltig författar-identitet \"%s\""
+msgstr "ogiltig författar-identitet ”%s”"
 
 msgid "corrupt author: missing date information"
 msgstr "trasig författare: saknar datuminformation"
@@ -19447,7 +19937,7 @@ msgstr "Det här är en kombination av %d incheckningar."
 
 #, c-format
 msgid "cannot write '%s'"
-msgstr "kan inte skriva \"%s\""
+msgstr "kan inte skriva ”%s”"
 
 msgid "need a HEAD to fixup"
 msgstr "behöver en HEAD-incheckning att rätta"
@@ -19466,7 +19956,7 @@ msgid "your index file is unmerged."
 msgstr "din indexfil har inte slagits ihop."
 
 msgid "cannot fixup root commit"
-msgstr "kan inte göra \"fixup\" på rotincheckning"
+msgstr "kan inte göra ”fixup” på rotincheckning"
 
 #, c-format
 msgid "commit %s is a merge but no -m option was given."
@@ -19486,10 +19976,6 @@ msgstr "kan inte hämta incheckningsmeddelande för %s"
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s: kan inte tolka föräldraincheckningen %s"
 
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "kunde inte byta namn på \"%s\" till \"%s\""
-
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "kunde inte ångra %s... %s"
@@ -19510,9 +19996,25 @@ msgstr "git %s: misslyckades läsa indexet"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: misslyckades uppdatera indexet"
 
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "”%s” är inte en giltig etikett"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "”%s” är inte ett giltigt referensnamn"
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr "update-ref kräver ett fullständigt referensnamn, t.ex refs/heads/%s"
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "ogiltigt kommando ”%.*s”"
+
 #, c-format
 msgid "%s does not accept arguments: '%s'"
-msgstr "%s tar inte argument: \"%s\""
+msgstr "%s tar inte argument: ”%s”"
 
 #, c-format
 msgid "missing arguments for %s"
@@ -19520,7 +20022,7 @@ msgstr "argument saknas för %s"
 
 #, c-format
 msgid "could not parse '%s'"
-msgstr "kunde inte tolka \"%s\""
+msgstr "kunde inte tolka ”%s”"
 
 #, c-format
 msgid "invalid line %d: %.*s"
@@ -19528,63 +20030,60 @@ msgstr "ogiltig rad %d: %.*s"
 
 #, c-format
 msgid "cannot '%s' without a previous commit"
-msgstr "kan inte utföra \"%s\" utan en föregående incheckning"
+msgstr "kan inte utföra ”%s” utan en föregående incheckning"
 
 msgid "cancelling a cherry picking in progress"
-msgstr "avbryter pågående \"cherry-pick\""
+msgstr "avbryter pågående ”cherry-pick”"
 
 msgid "cancelling a revert in progress"
-msgstr "avbryter pågående \"revert\""
+msgstr "avbryter pågående ”revert”"
 
 msgid "please fix this using 'git rebase --edit-todo'."
-msgstr "rätta det med \"git rebase --edit-todo\"."
+msgstr "rätta det med ”git rebase --edit-todo”."
 
 #, c-format
 msgid "unusable instruction sheet: '%s'"
-msgstr "oanvändbart manus: \"%s\""
+msgstr "oanvändbart manus: ”%s”"
 
 msgid "no commits parsed."
 msgstr "inga incheckningar lästes."
 
 msgid "cannot cherry-pick during a revert."
-msgstr "kan inte utföra \"cherry-pick\" under en \"revert\"."
+msgstr "kan inte utföra ”cherry-pick” under en ”revert”."
 
 msgid "cannot revert during a cherry-pick."
-msgstr "kan inte utföra \"revert\" under en \"cherry-pick\"."
+msgstr "kan inte utföra ”revert” under en ”cherry-pick”."
 
 msgid "unusable squash-onto"
 msgstr "oanvändbar squash-onto"
 
 #, c-format
 msgid "malformed options sheet: '%s'"
-msgstr "trasigt manus: \"%s\""
+msgstr "trasigt manus: ”%s”"
 
 msgid "empty commit set passed"
 msgstr "den angivna uppsättningen incheckningar är tom"
 
 msgid "revert is already in progress"
-msgstr "en \"revert\" pågår redan"
+msgstr "en ”revert” pågår redan"
 
 #, c-format
 msgid "try \"git revert (--continue | %s--abort | --quit)\""
-msgstr "testa \"git revert (--continue | %s--abort | --quit)\""
+msgstr "testa ”git revert (--continue | %s--abort | --quit)”"
 
 msgid "cherry-pick is already in progress"
-msgstr "en \"cherry-pick\" pågår redan"
+msgstr "en ”cherry-pick” pågår redan"
 
 #, c-format
 msgid "try \"git cherry-pick (--continue | %s--abort | --quit)\""
-msgstr "testa \"git cherry-pick (--continue | %s--abort | --quit)\""
+msgstr "testa ”git cherry-pick (--continue | %s--abort | --quit)”"
 
 #, c-format
 msgid "could not create sequencer directory '%s'"
-msgstr "kunde inte skapa \"sequencer\"-katalogen \"%s\""
-
-msgid "could not lock HEAD"
-msgstr "kunde inte låsa HEAD"
+msgstr "kunde inte skapa ”sequencer”-katalogen ”%s”"
 
 msgid "no cherry-pick or revert in progress"
-msgstr "ingen \"cherry-pick\" eller \"revert\" pågår"
+msgstr "ingen ”cherry-pick” eller ”revert” pågår"
 
 msgid "cannot resolve HEAD"
 msgstr "kan inte bestämma HEAD"
@@ -19594,14 +20093,14 @@ msgstr "kan inte avbryta från en gren som ännu inte är född"
 
 #, c-format
 msgid "cannot read '%s': %s"
-msgstr "kan inte läsa \"%s\": %s"
+msgstr "kan inte läsa ”%s”: %s"
 
 msgid "unexpected end of file"
 msgstr "oväntat filslut"
 
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
-msgstr "sparad HEAD-fil från före \"cherry-pick\", \"%s\", är trasig"
+msgstr "sparad HEAD-fil från före ”cherry-pick”, ”%s”, är trasig"
 
 msgid "You seem to have moved HEAD. Not rewinding, check your HEAD!"
 msgstr ""
@@ -19609,10 +20108,10 @@ msgstr ""
 "Spolar inte tillbaka, kontrollera HEAD!"
 
 msgid "no revert in progress"
-msgstr "ingen \"revers\" pågår"
+msgstr "ingen ”revert” pågår"
 
 msgid "no cherry-pick in progress"
-msgstr "ingen \"cherry-pick\" pågår"
+msgstr "ingen ”cherry-pick” pågår"
 
 msgid "failed to skip the commit"
 msgstr "kunde inte hoppa över incheckningen"
@@ -19626,14 +20125,14 @@ msgid ""
 "try \"git %s --continue\""
 msgstr ""
 "har du redan checkat in?\n"
-"testa \"git %s --continue\""
+"testa ”git %s --continue”"
 
 msgid "cannot read HEAD"
 msgstr "kan inte läsa HEAD"
 
 #, c-format
 msgid "unable to copy '%s' to '%s'"
-msgstr "kan inte kopiera in \"%s\" till \"%s\""
+msgstr "kan inte kopiera in ”%s” till ”%s”"
 
 #, c-format
 msgid ""
@@ -19679,28 +20178,32 @@ msgstr ""
 "\tgit rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "och gjorde ändringar till indexet och/eller arbetskatalogen\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "och gjorde ändringar till indexet och/eller arbetskatalogen.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "körningen lyckades: %s\n"
-"men lämnade kvar ändringar i indexet och/eller arbetskatalogen\n"
-"Checka in eller utför \"stash\" på ändringarna och kör sedan\n"
+"men lämnade kvar ändringar i indexet och/eller arbetskatalogen.\n"
+"Checka in eller utför ”stash” på ändringarna och kör sedan\n"
 "\n"
 "\tgit rebase --continue\n"
 "\n"
 
 #, c-format
 msgid "illegal label name: '%.*s'"
-msgstr "ogiltigt etikettnamn: \"%.*s\""
+msgstr "ogiltigt etikettnamn: ”%.*s”"
+
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "kunde inte upplösa ”%s”"
 
 msgid "writing fake root commit"
 msgstr "skriver fejkad rotincheckning"
@@ -19708,31 +20211,27 @@ msgstr "skriver fejkad rotincheckning"
 msgid "writing squash-onto"
 msgstr "skriver squash-onto"
 
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "kunde inte upplösa \"%s\""
-
 msgid "cannot merge without a current revision"
 msgstr "kan inte slå ihop utan en aktuell incheckning"
 
 #, c-format
 msgid "unable to parse '%.*s'"
-msgstr "kan inte tolka \"%.*s\""
+msgstr "kan inte tolka ”%.*s”"
 
 #, c-format
 msgid "nothing to merge: '%.*s'"
-msgstr "inget att slå samman: \"%.*s\""
+msgstr "inget att slå samman: ”%.*s”"
 
 msgid "octopus merge cannot be executed on top of a [new root]"
-msgstr "\"octopus\"-sammanslagning kan inte köras ovanpå en [ny rot]"
+msgstr "”octopus”-sammanslagning kan inte köras ovanpå en [ny rot]"
 
 #, c-format
 msgid "could not get commit message of '%s'"
-msgstr "kunde inte läsa incheckningsmeddelande för \"%s\""
+msgstr "kunde inte läsa incheckningsmeddelande för ”%s”"
 
 #, c-format
 msgid "could not even attempt to merge '%.*s'"
-msgstr "kunde inte ens försöka slå ihop \"%.*s\""
+msgstr "kunde inte ens försöka slå ihop ”%.*s”"
 
 msgid "merge: Unable to write new index file"
 msgstr "sammanslagning: Kunde inte skriva ny indexfil"
@@ -19740,7 +20239,7 @@ msgstr "sammanslagning: Kunde inte skriva ny indexfil"
 #, c-format
 msgid ""
 "another 'rebase' process appears to be running; '%s.lock' already exists"
-msgstr "en annan \"rebase\"-process verkar vara aktiv; \"%s.lock\" finns redan"
+msgstr "en annan ”rebase”-process verkar vara aktiv; ”%s.lock” finns redan"
 
 #, c-format
 msgid ""
@@ -19759,22 +20258,22 @@ msgstr ""
 "%s"
 
 msgid "Cannot autostash"
-msgstr "Kan inte utföra \"autostash\""
+msgstr "Kan inte utföra ”autostash”"
 
 #, c-format
 msgid "Unexpected stash response: '%s'"
-msgstr "Oväntat svar från stash: \"%s\""
+msgstr "Oväntat svar från stash: ”%s”"
 
 #, c-format
 msgid "Could not create directory for '%s'"
-msgstr "Kunde inte skapa katalog för \"%s\""
+msgstr "Kunde inte skapa katalog för ”%s”"
 
 #, c-format
 msgid "Created autostash: %s\n"
 msgstr "Skapade autostash: %s\n"
 
 msgid "could not reset --hard"
-msgstr "kunde inte utföra \"reset --hard\""
+msgstr "kunde inte utföra ”reset --hard”"
 
 #, c-format
 msgid "Applied autostash.\n"
@@ -19792,7 +20291,7 @@ msgid ""
 msgstr ""
 "%s\n"
 "Dina ändringar är säkra i stashen.\n"
-"Du kan när som helst använda \"git stash pop\" eller \"git stash drop\".\n"
+"Du kan när som helst använda ”git stash pop” eller ”git stash drop”.\n"
 
 msgid "Applying autostash resulted in conflicts."
 msgstr "Tillämpning av autostash gav konflikter."
@@ -19827,18 +20326,18 @@ msgstr ""
 "    %.*s\n"
 "Det har lagts tillbaka i kön; För att redigera kommandot innan du\n"
 "fortsätter, redigera först attgöra-listan:\n"
-"\n"
-"    git rebase --edit-todo\n"
-"    git rebase --continue\n"
-
-#, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Ombaserar (%d/%d)%s"
+"\n"
+"    git rebase --edit-todo\n"
+"    git rebase --continue\n"
 
 #, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Stoppade på %s... %.*s\n"
 
+#, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Ombaserar (%d/%d)%s"
+
 #, c-format
 msgid "unknown command %d"
 msgstr "okänt kommando %d"
@@ -19847,7 +20346,7 @@ msgid "could not read orig-head"
 msgstr "kunde inte läsa orig-head"
 
 msgid "could not read 'onto'"
-msgstr "kunde inte läsa \"onto\""
+msgstr "kunde inte läsa ”onto”"
 
 #, c-format
 msgid "could not update HEAD to %s"
@@ -19865,11 +20364,11 @@ msgstr "kan inte lägga till incheckning som inte finns"
 
 #, c-format
 msgid "invalid file: '%s'"
-msgstr "ogiltig fil: \"%s\""
+msgstr "ogiltig fil: ”%s”"
 
 #, c-format
 msgid "invalid contents: '%s'"
-msgstr "ogiltigt innehåll: \"%s\""
+msgstr "ogiltigt innehåll: ”%s”"
 
 msgid ""
 "\n"
@@ -19878,11 +20377,11 @@ msgid ""
 msgstr ""
 "\n"
 "Du har ändringar i arbetskatalogen som inte checkats in. Checka in dem\n"
-"först och kör sedan \"git rebase --continue\" igen."
+"först och kör sedan ”git rebase --continue” igen."
 
 #, c-format
 msgid "could not write file: '%s'"
-msgstr "kunde inte skriva fil: \"%s\""
+msgstr "kunde inte skriva fil: ”%s”"
 
 msgid "could not remove CHERRY_PICK_HEAD"
 msgstr "kunde inte ta bort CHERRY_PICK_HEAD"
@@ -19892,7 +20391,7 @@ msgstr "kunde inte checka in köade ändringar."
 
 #, c-format
 msgid "%s: can't cherry-pick a %s"
-msgstr "%s: kan inte göra \"cherry-pick\" på typen \"%s\""
+msgstr "%s: kan inte göra ”cherry-pick” på typen ”%s”"
 
 #, c-format
 msgid "%s: bad revision"
@@ -19918,18 +20417,18 @@ msgid "nothing to do"
 msgstr "inget att göra"
 
 msgid "could not skip unnecessary pick commands"
-msgstr "kunde inte hoppa över onödiga \"pick\"-kommandon"
+msgstr "kunde inte hoppa över onödiga ”pick”-kommandon"
 
 msgid "the script was already rearranged."
 msgstr "skriptet har redan omordnats."
 
 #, c-format
 msgid "update-refs file at '%s' is invalid"
-msgstr "update-refs-filen vid \"%s\" är ogiltig"
+msgstr "update-refs-filen vid ”%s” är ogiltig"
 
 #, c-format
 msgid "'%s' is outside repository at '%s'"
-msgstr "\"%s\" är utanför arkivet på \"%s\""
+msgstr "”%s” är utanför arkivet på ”%s”"
 
 #, c-format
 msgid ""
@@ -19937,7 +20436,7 @@ msgid ""
 "Use 'git <command> -- <path>...' to specify paths that do not exist locally."
 msgstr ""
 "%s: sökvägen finns inte i arbetskatalogen.\n"
-"Använd \"git <kommando> -- <sökväg>..\" för att ange sökvägar som inte finns "
+"Använd ”git <kommando> -- <sökväg>..” för att ange sökvägar som inte finns "
 "lokalt."
 
 #, c-format
@@ -19946,14 +20445,14 @@ msgid ""
 "Use '--' to separate paths from revisions, like this:\n"
 "'git <command> [<revision>...] -- [<file>...]'"
 msgstr ""
-"tvetydigt argument \"%s\": okänd revision eller sökväg inte i "
+"tvetydigt argument ”%s”: okänd revision eller sökväg inte i "
 "arbetskatalogen.\n"
-"Använd \"--\" för att skilja sökvägar från revisioner, så här:\n"
-"\"git <kommando> [<revision>...] -- [<fil>...]\""
+"Använd ”--” för att skilja sökvägar från revisioner, så här:\n"
+"”git <kommando> [<revision>...] -- [<fil>...]”"
 
 #, c-format
 msgid "option '%s' must come before non-option arguments"
-msgstr "flaggan \"%s\" måste anges före argument som inte är flaggor"
+msgstr "flaggan ”%s” måste anges före argument som inte är flaggor"
 
 #, c-format
 msgid ""
@@ -19961,9 +20460,9 @@ msgid ""
 "Use '--' to separate paths from revisions, like this:\n"
 "'git <command> [<revision>...] -- [<file>...]'"
 msgstr ""
-"tvetydigt argument \"%s\": både revision och filnamn\n"
-"Använd \"--\" för att skilja sökvägar från revisioner, så här:\n"
-"\"git <kommando> [<revision>...] -- [<fil>...]\""
+"tvetydigt argument ”%s”: både revision och filnamn\n"
+"Använd ”--” för att skilja sökvägar från revisioner, så här:\n"
+"”git <kommando> [<revision>...] -- [<fil>...]”"
 
 msgid "unable to set up work tree using invalid config"
 msgstr "kan inte skapa arbetskatalog med felaktig konfiguration"
@@ -19984,11 +20483,11 @@ msgstr[1] "arkivversionen är 0, men utökningar som bara finns i v1 upptäcktes
 
 #, c-format
 msgid "error opening '%s'"
-msgstr "fel vid öppning av \"%s\""
+msgstr "fel vid öppning av ”%s”"
 
 #, c-format
 msgid "too large to be a .git file: '%s'"
-msgstr "för stor för att vara en .git-fil: \"%s\""
+msgstr "för stor för att vara en .git-fil: ”%s”"
 
 #, c-format
 msgid "error reading %s"
@@ -20008,29 +20507,29 @@ msgstr "inte ett git-arkiv: %s"
 
 #, c-format
 msgid "'$%s' too big"
-msgstr "\"$%s\" för stor"
+msgstr "”$%s” för stor"
 
 #, c-format
 msgid "not a git repository: '%s'"
-msgstr "inte ett git-arkiv: \"%s\""
+msgstr "inte ett git-arkiv: ”%s”"
 
 #, c-format
 msgid "cannot chdir to '%s'"
-msgstr "kan inte byta katalog (chdir) till \"%s\""
+msgstr "kan inte byta katalog (chdir) till ”%s”"
 
 msgid "cannot come back to cwd"
 msgstr "kan inte gå tillbaka till arbetskatalogen (cwd)"
 
 #, c-format
 msgid "failed to stat '%*s%s%s'"
-msgstr "misslyckades ta status på \"%*ss%s%s\""
+msgstr "misslyckades ta status på ”%*ss%s%s”"
 
 msgid "Unable to read current working directory"
 msgstr "Kan inte läsa aktuell arbetskatalog"
 
 #, c-format
 msgid "cannot change to '%s'"
-msgstr "kan inte byta till \"%s\""
+msgstr "kan inte byta till ”%s”"
 
 #, c-format
 msgid "not a git repository (or any of the parent directories): %s"
@@ -20052,14 +20551,14 @@ msgid ""
 "\n"
 "\tgit config --global --add safe.directory %s"
 msgstr ""
-"upptäckte tveksamt ägarskap i arkivet i \"%s\"\n"
+"upptäckte tveksamt ägarskap i arkivet i ”%s”\n"
 "%sFör att lägga till ett undantag för denna katalog, kör:\n"
 "\n"
 "\tgit config --global --add safe.directory %s"
 
 #, c-format
 msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr "kan inte använda naket arkiv \"%s\" (safe.bareRepository är \"%s\")"
+msgstr "kan inte använda naket arkiv ”%s” (safe.bareRepository är ”%s”)"
 
 #, c-format
 msgid ""
@@ -20070,10 +20569,81 @@ msgstr ""
 "Ägaren av filerna måste alltid ha läs- och skrivbehörighet."
 
 msgid "fork failed"
-msgstr "\"fork\" misslyckades"
+msgstr "”fork” misslyckades"
 
 msgid "setsid failed"
-msgstr "\"setsid\" misslyckades"
+msgstr "”setsid” misslyckades"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "kan inte ta status på mallen ”%s”"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "kan inte öppna katalogen (opendir) ”%s”"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "kan inte läsa länk (readlink) ”%s”"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "kan inte skapa symbolisk länk ”%s” ”%s”"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "kan inte kopiera ”%s” till ”%s”"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "ignorerar mallen %s"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "mallarna hittades inte i %s"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "kopierade inte mallar från ”%s”: %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "ogiltigt namn på första gren: ”%s”"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "kan inte hantera filtyp %d"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "kan inte flytta %s till %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "försöker initiera arkivet på nytt med annan hash"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s finns redan"
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: ignorerade --initial-branch=%s"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Ominitierade befintligt delat Git-arkiv i %s%s\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Ominitierade befintligt Git-arkiv i %s%s\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Initierade tomt delat Git-arkiv i %s%s\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Initierade tomt Git-arkiv i %s%s\n"
 
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -20126,10 +20696,6 @@ msgid_plural "%u bytes/s"
 msgstr[0] "%u byte/s"
 msgstr[1] "%u bytes/s"
 
-#, c-format
-msgid "could not edit '%s'"
-msgstr "kunde inte redigera \"%s\""
-
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "ignorerar misstänkt undermodulnamn: %s"
@@ -20139,7 +20705,7 @@ msgstr "negativa värden är inte tillåtna för submodule.fetchJobs"
 
 #, c-format
 msgid "ignoring '%s' which may be interpreted as a command-line option: %s"
-msgstr "ignorerar \"%s\" som kan tolkas som en kommandoradsflagga: %s"
+msgstr "ignorerar ”%s” som kan tolkas som en kommandoradsflagga: %s"
 
 #, c-format
 msgid "Could not update .gitmodules entry %s"
@@ -20163,11 +20729,11 @@ msgstr "misslyckades köa uppdaterad .gitmodules"
 
 #, c-format
 msgid "in unpopulated submodule '%s'"
-msgstr "i ej utcheckad undermodul \"%s\""
+msgstr "i ej utcheckad undermodul ”%s”"
 
 #, c-format
 msgid "Pathspec '%s' is in submodule '%.*s'"
-msgstr "Sökvägsangivelsen \"%s\" är i undermodulen \"%.*s\""
+msgstr "Sökvägsangivelsen ”%s” är i undermodulen ”%.*s”"
 
 #, c-format
 msgid "bad --ignore-submodules argument: %s"
@@ -20178,32 +20744,32 @@ msgid ""
 "Submodule in commit %s at path: '%s' collides with a submodule named the "
 "same. Skipping it."
 msgstr ""
-"Undermodulen i incheckning %s på sökvägen: \"%s\" krockar med en undermodul "
+"Undermodulen i incheckning %s på sökvägen: ”%s” krockar med en undermodul "
 "med samma namn. Hoppar över den."
 
 #, c-format
 msgid "submodule entry '%s' (%s) is a %s, not a commit"
-msgstr "undermodulposten \"%s\" (%s) är en %s, inte en incheckning"
+msgstr "undermodulposten ”%s” (%s) är en %s, inte en incheckning"
 
 #, c-format
 msgid ""
 "Could not run 'git rev-list <commits> --not --remotes -n 1' command in "
 "submodule %s"
 msgstr ""
-"kunde inte köra \"git rev-list <incheckningar> --not --remotes -n 1\" i "
-"undermodulen \"%s\""
+"kunde inte köra ”git rev-list <incheckningar> --not --remotes -n 1” i "
+"undermodulen ”%s”"
 
 #, c-format
 msgid "process for submodule '%s' failed"
-msgstr "process för undermodulen \"%s\" misslyckades"
+msgstr "process för undermodulen ”%s” misslyckades"
 
 #, c-format
 msgid "Pushing submodule '%s'\n"
-msgstr "Sänder undermodulen \"%s\"\n"
+msgstr "Sänder undermodulen ”%s”\n"
 
 #, c-format
 msgid "Unable to push submodule '%s'\n"
-msgstr "Kunde inte sända undermodulen \"%s\"\n"
+msgstr "Kunde inte sända undermodulen ”%s”\n"
 
 #, c-format
 msgid "Fetching submodule %s%s\n"
@@ -20211,11 +20777,11 @@ msgstr "Hämtar undermodulen %s%s\n"
 
 #, c-format
 msgid "Could not access submodule '%s'\n"
-msgstr "Kunde inte komma åt undermodulen \"%s\"\n"
+msgstr "Kunde inte komma åt undermodulen ”%s”\n"
 
 #, c-format
 msgid "Could not access submodule '%s' at commit %s\n"
-msgstr "Kunde inte komma åt undermodulen \"%s\" vid incheckningen %s\n"
+msgstr "Kunde inte komma åt undermodulen ”%s” vid incheckningen %s\n"
 
 #, c-format
 msgid "Fetching submodule %s%s at commit %s\n"
@@ -20231,61 +20797,61 @@ msgstr ""
 
 #, c-format
 msgid "'%s' not recognized as a git repository"
-msgstr "\"%s\" känns inte igen som ett git-arkiv"
+msgstr "”%s” känns inte igen som ett git-arkiv"
 
 #, c-format
 msgid "Could not run 'git status --porcelain=2' in submodule %s"
-msgstr "Kunde inte köra \"git status --porcelain=2\" i undermodulen \"%s\""
+msgstr "Kunde inte köra ”git status --porcelain=2” i undermodulen ”%s”"
 
 #, c-format
 msgid "'git status --porcelain=2' failed in submodule %s"
-msgstr "\"git status --porcelain=2\" misslyckades i undermodulen \"%s\""
+msgstr "”git status --porcelain=2” misslyckades i undermodulen ”%s”"
 
 #, c-format
 msgid "could not start 'git status' in submodule '%s'"
-msgstr "kunde inte starta \"git status\" i undermodulen \"%s\""
+msgstr "kunde inte starta ”git status” i undermodulen ”%s”"
 
 #, c-format
 msgid "could not run 'git status' in submodule '%s'"
-msgstr "kunde inte köra \"git status\" i undermodulen \"%s\""
+msgstr "kunde inte köra ”git status” i undermodulen ”%s”"
 
 #, c-format
 msgid "Could not unset core.worktree setting in submodule '%s'"
-msgstr "Kunde inte ta bort inställningen core.worktree i undermodulen \"%s\""
+msgstr "Kunde inte ta bort inställningen core.worktree i undermodulen ”%s”"
 
 #, c-format
 msgid "could not recurse into submodule '%s'"
-msgstr "kunde inte rekursera in i undermodulen \"%s\""
+msgstr "kunde inte rekursera in i undermodulen ”%s”"
 
 msgid "could not reset submodule index"
 msgstr "kunde inte återställa indexet i undermodul"
 
 #, c-format
 msgid "submodule '%s' has dirty index"
-msgstr "undermodulen \"%s\" har ett smutsigt index"
+msgstr "undermodulen ”%s” har ett smutsigt index"
 
 #, c-format
 msgid "Submodule '%s' could not be updated."
-msgstr "Undermoduler \"%s\" kunde inte uppdateras."
+msgstr "Undermoduler ”%s” kunde inte uppdateras."
 
 #, c-format
 msgid "submodule git dir '%s' is inside git dir '%.*s'"
-msgstr "undermodul-gitkatalogen \"%s\" är inuti gitkatalogen \"%.*s\""
+msgstr "undermodul-gitkatalogen ”%s” är inuti gitkatalogen ”%.*s”"
 
 #, c-format
 msgid ""
 "relocate_gitdir for submodule '%s' with more than one worktree not supported"
 msgstr ""
-"relocate_gitdir för undermodulen \"%s\", som har mer än en arbetskatalog, "
+"relocate_gitdir för undermodulen ”%s”, som har mer än en arbetskatalog, "
 "stöds ej"
 
 #, c-format
 msgid "could not lookup name for submodule '%s'"
-msgstr "kunde inte slå upp namnet för undermodulen \"%s\""
+msgstr "kunde inte slå upp namnet för undermodulen ”%s”"
 
 #, c-format
 msgid "refusing to move '%s' into an existing git dir"
-msgstr "vägrar flytta \"%s\" till en befintlig gitkatalog"
+msgstr "vägrar flytta ”%s” till en befintlig gitkatalog"
 
 #, c-format
 msgid ""
@@ -20293,9 +20859,9 @@ msgid ""
 "'%s' to\n"
 "'%s'\n"
 msgstr ""
-"Migrerar git-katalogen för \"%s%s\" från\n"
-"\"%s\" till\n"
-"\"%s\"\n"
+"Migrerar git-katalogen för ”%s%s” från\n"
+"”%s” till\n"
+"”%s”\n"
 
 msgid "could not start ls-files in .."
 msgstr "kunde inte starta ls-files i .."
@@ -20306,7 +20872,26 @@ msgstr "ls-tree returnerade en oväntad returkod %d"
 
 #, c-format
 msgid "failed to lstat '%s'"
-msgstr "misslyckades ta status (lstat) på \"%s\""
+msgstr "misslyckades ta status (lstat) på ”%s”"
+
+msgid "no remote configured to get bundle URIs from"
+msgstr "ingen fjärr att hämta bunt-URI:er från inställd"
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "fjärren ”%s” har ingen URL konfigurerad"
+
+msgid "could not get the bundle-uri list"
+msgstr "kunde inte hämta bundle-uri-listan"
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <flaggor> (control|prime|update)"
+
+msgid "clear the cache tree before each iteration"
+msgstr "töm cacheträdet före varje iteration"
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr "antal poster i cacheträdet att ogiltigförklara (förval är 0)"
 
 msgid "unhandled options"
 msgstr "flaggor som inte hanterats"
@@ -20385,19 +20970,19 @@ msgstr "igenkänningstecken för kommando att sända till servern"
 
 #, c-format
 msgid "running trailer command '%s' failed"
-msgstr "misslyckades utföra släpradskommandot \"%s\""
+msgstr "misslyckades utföra släpradskommandot ”%s”"
 
 #, c-format
 msgid "unknown value '%s' for key '%s'"
-msgstr "okänt värde \"%s\" för nyckeln \"%s\""
+msgstr "okänt värde ”%s” för nyckeln ”%s”"
 
 #, c-format
 msgid "empty trailer token in trailer '%.*s'"
-msgstr "tom släpradssymbol i släpraden \"%.*s\""
+msgstr "tom släpradssymbol i släpraden ”%.*s”"
 
 #, c-format
 msgid "could not read input file '%s'"
-msgstr "kunde inte läsa indatafilen \"%s\""
+msgstr "kunde inte läsa indatafilen ”%s”"
 
 #, c-format
 msgid "could not stat %s"
@@ -20423,7 +21008,7 @@ msgstr "komplett skrivning till fjärrhjälpare misslyckades"
 
 #, c-format
 msgid "unable to find remote helper for '%s'"
-msgstr "kan inte hitta fjärrhjälpare för \"%s\""
+msgstr "kan inte hitta fjärrhjälpare för ”%s”"
 
 msgid "can't dup helper output fd"
 msgstr "kunde inte duplicera utdata-filhandtag"
@@ -20443,7 +21028,7 @@ msgstr ""
 
 #, c-format
 msgid "%s unexpectedly said: '%s'"
-msgstr "%s sade oväntat: \"%s\""
+msgstr "%s sade oväntat: ”%s”"
 
 #, c-format
 msgid "%s also locked %s"
@@ -20480,11 +21065,11 @@ msgid "--negotiate-only requires protocol v2"
 msgstr "--negotiate-only kräver protokoll v2"
 
 msgid "'option' without a matching 'ok/error' directive"
-msgstr "\"option\" utan mostsvarande \"ok/error\"-direktiv"
+msgstr "”option” utan mostsvarande ”ok/error”-direktiv"
 
 #, c-format
 msgid "expected ok/error, helper said '%s'"
-msgstr "förväntade ok/error, hjälpprogrammet svarade \"%s\""
+msgstr "förväntade ok/error, hjälpprogrammet svarade ”%s”"
 
 #, c-format
 msgid "helper reported unexpected status of %s"
@@ -20512,14 +21097,14 @@ msgstr "hjälparen %s stöder inte --%s"
 
 #, c-format
 msgid "helper %s does not support 'push-option'"
-msgstr "hjälparen %s stöder inte \"push-option\""
+msgstr "hjälparen %s stöder inte ”push-option”"
 
 msgid "remote-helper doesn't support push; refspec needed"
 msgstr "fjärrhjälparen stöder inte push; referensspecifikation krävs"
 
 #, c-format
 msgid "helper %s does not support 'force'"
-msgstr "hjälparen %s stöder inte \"force\""
+msgstr "hjälparen %s stöder inte ”force”"
 
 msgid "couldn't run fast-export"
 msgstr "kunde inte köra fast-export"
@@ -20537,7 +21122,7 @@ msgstr ""
 
 #, c-format
 msgid "unsupported object format '%s'"
-msgstr "objektformatet \"%s\" stöds ej"
+msgstr "objektformatet ”%s” stöds ej"
 
 #, c-format
 msgid "malformed response in ref list: %s"
@@ -20576,18 +21161,18 @@ msgstr "kan inte skapa tråd för kopiering av data"
 
 #, c-format
 msgid "Would set upstream of '%s' to '%s' of '%s'\n"
-msgstr "Skulle sätta uppströms för \"%s\" till \"%s\" från \"%s\"\n"
+msgstr "Skulle sätta uppströms för ”%s” till ”%s” från ”%s”\n"
 
 #, c-format
 msgid "could not read bundle '%s'"
-msgstr "kunde inte läsa bunten \"%s\""
+msgstr "kunde inte läsa bunten ”%s”"
 
 #, c-format
 msgid "transport: invalid depth option '%s'"
-msgstr "transport: ogiltig flagga för depth: \"%s\""
+msgstr "transport: ogiltig flagga för depth: ”%s”"
 
 msgid "see protocol.version in 'git help config' for more details"
-msgstr "se protocol.version i \"git help config\" för mer information"
+msgstr "se protocol.version i ”git help config” för mer information"
 
 msgid "server options require protocol version 2 or later"
 msgstr "serverflaggor kräver protokollversion 2 eller senare"
@@ -20603,11 +21188,11 @@ msgstr "stöd för protokoll v2 ännu ej implementerat"
 
 #, c-format
 msgid "unknown value for config '%s': %s"
-msgstr "okänt värde för inställningen \"%s\": %s"
+msgstr "okänt värde för inställningen ”%s”: %s"
 
 #, c-format
 msgid "transport '%s' not allowed"
-msgstr "transporten \"%s\" tillåts inte"
+msgstr "transporten ”%s” tillåts inte"
 
 msgid "git-over-rsync is no longer supported"
 msgstr "git-over-rsync stöds inte längre"
@@ -20652,6 +21237,12 @@ msgstr "Avbryter."
 msgid "failed to push all needed submodules"
 msgstr "kunde inte sända alla nödvändiga undermoduler"
 
+msgid "bundle-uri operation not supported by protocol"
+msgstr "bundle-uri-funktionen stöds inte av protokollet"
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "kunde inte hämta bundle-uri-listan som servern annonserade"
+
 msgid "too-short tree object"
 msgstr "trädobjekt för kort"
 
@@ -20670,7 +21261,7 @@ msgid ""
 "%%sPlease commit your changes or stash them before you switch branches."
 msgstr ""
 "Dina lokala ändringar av följande filer skulle skrivas över av utcheckning:\n"
-"%%sChecka in dina ändringar eller använd \"stash\" innan du byter gren."
+"%%sChecka in dina ändringar eller använd ”stash” innan du byter gren."
 
 #, c-format
 msgid ""
@@ -20687,7 +21278,7 @@ msgid ""
 msgstr ""
 "Dina lokala ändringar av följande filer skulle skrivas över av "
 "sammanslagning:\n"
-"%%sChecka in dina ändringar eller använd \"stash\" innan du byter gren."
+"%%sChecka in dina ändringar eller använd ”stash” innan du byter gren."
 
 #, c-format
 msgid ""
@@ -20703,15 +21294,15 @@ msgid ""
 "Your local changes to the following files would be overwritten by %s:\n"
 "%%sPlease commit your changes or stash them before you %s."
 msgstr ""
-"Dina lokala ändringar av följande filer skulle skrivas över av \"%s\":\n"
-"%%sChecka in dina ändringar eller använd \"stash\" innan du \"%s\"."
+"Dina lokala ändringar av följande filer skulle skrivas över av ”%s”:\n"
+"%%sChecka in dina ändringar eller använd ”stash” innan du ”%s”."
 
 #, c-format
 msgid ""
 "Your local changes to the following files would be overwritten by %s:\n"
 "%%s"
 msgstr ""
-"Dina lokala ändringar av följande filer skulle skrivas över av \"%s\":\n"
+"Dina lokala ändringar av följande filer skulle skrivas över av ”%s”:\n"
 "%%s"
 
 #, c-format
@@ -20770,15 +21361,15 @@ msgid ""
 "The following untracked working tree files would be removed by %s:\n"
 "%%sPlease move or remove them before you %s."
 msgstr ""
-"Följande ospårade filer i arbetskatalogen skulle tas bort av \"%s\":\n"
-"%%sFlytta eller ta bort dem innan du \"%s\"."
+"Följande ospårade filer i arbetskatalogen skulle tas bort av ”%s”:\n"
+"%%sFlytta eller ta bort dem innan du ”%s”."
 
 #, c-format
 msgid ""
 "The following untracked working tree files would be removed by %s:\n"
 "%%s"
 msgstr ""
-"Följande ospårade filer i arbetskatalogen skulle tas bort av \"%s\":\n"
+"Följande ospårade filer i arbetskatalogen skulle tas bort av ”%s”:\n"
 "%%s"
 
 #, c-format
@@ -20824,20 +21415,20 @@ msgid ""
 "The following untracked working tree files would be overwritten by %s:\n"
 "%%sPlease move or remove them before you %s."
 msgstr ""
-"Följande ospårade filer i arbetskatalogen skulle skrivas över av \"%s\":\n"
-"%%sFlytta eller ta bort dem innan du \"%s\"."
+"Följande ospårade filer i arbetskatalogen skulle skrivas över av ”%s”:\n"
+"%%sFlytta eller ta bort dem innan du ”%s”."
 
 #, c-format
 msgid ""
 "The following untracked working tree files would be overwritten by %s:\n"
 "%%s"
 msgstr ""
-"Följande ospårade filer i arbetskatalogen skulle skrivas över av \"%s\":\n"
+"Följande ospårade filer i arbetskatalogen skulle skrivas över av ”%s”:\n"
 "%%s"
 
 #, c-format
 msgid "Entry '%s' overlaps with '%s'.  Cannot bind."
-msgstr "Posten \"%s\" överlappar \"%s\". Kan inte binda."
+msgstr "Posten ”%s” överlappar ”%s”. Kan inte binda."
 
 #, c-format
 msgid ""
@@ -20884,7 +21475,7 @@ msgid ""
 "After fixing the above paths, you may want to run `git sparse-checkout "
 "reapply`.\n"
 msgstr ""
-"Du bör köra \"git sparse-checkout reapply\" efter att ha fixat sökvägarna "
+"Du bör köra ”git sparse-checkout reapply” efter att ha fixat sökvägarna "
 "ovan.\n"
 
 msgid "Updating files"
@@ -20907,20 +21498,20 @@ msgid "worktree and untracked commit have duplicate entries: %s"
 msgstr "arbetskatalog och ospårad incheckning har dublettposter: %s"
 
 msgid "expected flush after fetch arguments"
-msgstr "förväntade \"flush\" efter \"fetch\"-argument"
+msgstr "förväntade ”flush” efter ”fetch”-argument"
 
 msgid "invalid URL scheme name or missing '://' suffix"
-msgstr "ogiltig URL-schemanamn eller saknat \"://\"-suffix"
+msgstr "ogiltig URL-schemanamn eller saknat ”://”-suffix"
 
 #, c-format
 msgid "invalid %XX escape sequence"
 msgstr "ogiltig %XX-teckensekvens"
 
 msgid "missing host and scheme is not 'file:'"
-msgstr "värd saknas och schemat är inte \"file:\""
+msgstr "värd saknas och schemat är inte ”file:”"
 
 msgid "a 'file:' URL may not have a port number"
-msgstr "en \"file:\"-URL kan inte innehålla portnummer"
+msgstr "en ”file:”-URL kan inte innehålla portnummer"
 
 msgid "invalid characters in host name"
 msgstr "ogiltiga tecken i värdnamnet"
@@ -20929,7 +21520,7 @@ msgid "invalid port number"
 msgstr "felaktigt portnummer"
 
 msgid "invalid '..' path segment"
-msgstr "felaktigt \"..\"-sökvägssegment"
+msgstr "felaktigt ”..”-sökvägssegment"
 
 msgid "usage: "
 msgstr "användning: "
@@ -20948,19 +21539,19 @@ msgstr "Hämtar objekt"
 
 #, c-format
 msgid "'%s' at main working tree is not the repository directory"
-msgstr "\"%s\" i huvudarbetskatalogen är inte arkivkatalogen"
+msgstr "”%s” i huvudarbetskatalogen är inte arkivkatalogen"
 
 #, c-format
 msgid "'%s' file does not contain absolute path to the working tree location"
-msgstr "filen \"%s\" innehåller inte absolut sökväg till arbetskatalogen"
+msgstr "filen ”%s” innehåller inte absolut sökväg till arbetskatalogen"
 
 #, c-format
 msgid "'%s' is not a .git file, error code %d"
-msgstr "\"%s\" är inte en .git-fil, felkod %d"
+msgstr "”%s” är inte en .git-fil, felkod %d"
 
 #, c-format
 msgid "'%s' does not point back to '%s'"
-msgstr "\"%s\" pekar inte tillbaka till \"%s\""
+msgstr "”%s” pekar inte tillbaka till ”%s”"
 
 msgid "not a directory"
 msgstr "inte en katalog"
@@ -21014,57 +21605,60 @@ msgstr "gitdir-filen pekar på en ickeexisterande plats"
 
 #, c-format
 msgid "unable to set %s in '%s'"
-msgstr "kan inte sätta %s i \"%s\""
+msgstr "kan inte sätta %s i ”%s”"
 
 #, c-format
 msgid "unable to unset %s in '%s'"
-msgstr "kan inte slå av %s i \"%s\""
+msgstr "kan inte slå av %s i ”%s”"
 
 msgid "failed to set extensions.worktreeConfig setting"
 msgstr "misslyckades ändra inställningen extensions.worktreeConfig"
 
 #, c-format
 msgid "could not setenv '%s'"
-msgstr "kunde inte lagra miljövariabeln \"%s\""
+msgstr "kunde inte lagra miljövariabeln ”%s”"
 
 #, c-format
 msgid "unable to create '%s'"
-msgstr "kunde inte skapa \"%s\""
+msgstr "kunde inte skapa ”%s”"
 
 #, c-format
 msgid "could not open '%s' for reading and writing"
-msgstr "kunde inte öppna \"%s\" för läsning och skrivning"
+msgstr "kunde inte öppna ”%s” för läsning och skrivning"
 
 #, c-format
 msgid "unable to access '%s'"
-msgstr "kan inte komma åt \"%s\""
+msgstr "kan inte komma åt ”%s”"
 
 msgid "unable to get current working directory"
 msgstr "kan inte hämta aktuell arbetskatalog"
 
+msgid "unable to get random bytes"
+msgstr "kunde inte hämta slumpdata"
+
 msgid "Unmerged paths:"
 msgstr "Ej sammanslagna sökvägar:"
 
 msgid "  (use \"git restore --staged <file>...\" to unstage)"
-msgstr "  (använd \"git restore --staged <fil>...\" för att ta bort från kö)"
+msgstr "  (använd ”git restore --staged <fil>...” för att ta bort från kö)"
 
 #, c-format
 msgid "  (use \"git restore --source=%s --staged <file>...\" to unstage)"
 msgstr ""
-"  (använd \"git restore --source=%s --staged <fil>...\" för att ta bort från "
+"  (använd ”git restore --source=%s --staged <fil>...” för att ta bort från "
 "kö)"
 
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
-msgstr "  (använd \"git rm --cached <fil>...\" för att ta bort från kö)"
+msgstr "  (använd ”git rm --cached <fil>...” för att ta bort från kö)"
 
 msgid "  (use \"git add <file>...\" to mark resolution)"
-msgstr "  (använd \"git add <fil>...\" för att ange lösning)"
+msgstr "  (använd ”git add <fil>...” för att ange lösning)"
 
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
-msgstr "  (använd \"git add/rm <fil>...\" som lämpligt för att ange lösning)"
+msgstr "  (använd ”git add/rm <fil>...” som lämpligt för att ange lösning)"
 
 msgid "  (use \"git rm <file>...\" to mark resolution)"
-msgstr "  (använd \"git rm <fil>...\" för att ange lösning)"
+msgstr "  (använd ”git rm <fil>...” för att ange lösning)"
 
 msgid "Changes to be committed:"
 msgstr "Ändringar att checka in:"
@@ -21073,17 +21667,16 @@ msgid "Changes not staged for commit:"
 msgstr "Ändringar ej i incheckningskön:"
 
 msgid "  (use \"git add <file>...\" to update what will be committed)"
-msgstr ""
-"  (använd \"git add <fil>...\" för att uppdatera vad som ska checkas in)"
+msgstr "  (använd ”git add <fil>...” för att uppdatera vad som ska checkas in)"
 
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
 msgstr ""
-"  (använd \"git add/rm <fil>...\" för att uppdatera vad som ska checkas in)"
+"  (använd ”git add/rm <fil>...” för att uppdatera vad som ska checkas in)"
 
 msgid ""
 "  (use \"git restore <file>...\" to discard changes in working directory)"
 msgstr ""
-"  (använd \"git restore <fil>...\" för att förkasta ändringar i "
+"  (använd ”git restore <fil>...” för att förkasta ändringar i "
 "arbetskatalogen)"
 
 msgid "  (commit or discard the untracked or modified content in submodules)"
@@ -21092,7 +21685,7 @@ msgstr ""
 
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
-msgstr "  (använd \"git %s <fil>...\" för att ta med i det som ska checkas in)"
+msgstr "  (använd ”git %s <fil>...” för att ta med i det som ska checkas in)"
 
 msgid "both deleted:"
 msgstr "borttaget av bägge:"
@@ -21175,43 +21768,43 @@ msgid ""
 msgstr ""
 "\n"
 "Det tog %.2f sekunder att räkna före/bakom-värden.\n"
-"Du kan använda \"--no-ahead-behind\" för undvika detta.\n"
+"Du kan använda ”--no-ahead-behind” för undvika detta.\n"
 
 msgid "You have unmerged paths."
 msgstr "Du har ej sammanslagna sökvägar."
 
 msgid "  (fix conflicts and run \"git commit\")"
-msgstr "  (rätta konflikter och kör \"git commit\")"
+msgstr "  (rätta konflikter och kör ”git commit”)"
 
 msgid "  (use \"git merge --abort\" to abort the merge)"
-msgstr "  (använd \"git merge --abort\" för att avbryta sammanslagningen)"
+msgstr "  (använd ”git merge --abort” för att avbryta sammanslagningen)"
 
 msgid "All conflicts fixed but you are still merging."
 msgstr "Alla konflikter har rättats men du är fortfarande i en sammanslagning."
 
 msgid "  (use \"git commit\" to conclude merge)"
-msgstr "  (använd \"git commit\" för att slutföra sammanslagningen)"
+msgstr "  (använd ”git commit” för att slutföra sammanslagningen)"
 
 msgid "You are in the middle of an am session."
-msgstr "Du är i mitten av en körning av \"git am\"."
+msgstr "Du är i mitten av en körning av ”git am”."
 
 msgid "The current patch is empty."
 msgstr "Aktuell patch är tom."
 
 msgid "  (fix conflicts and then run \"git am --continue\")"
-msgstr "  (rätta konflikter och kör sedan \"git am --continue\")"
+msgstr "  (rätta konflikter och kör sedan ”git am --continue”)"
 
 msgid "  (use \"git am --skip\" to skip this patch)"
-msgstr "  (använd \"git am --skip\" för att hoppa över patchen)"
+msgstr "  (använd ”git am --skip” för att hoppa över patchen)"
 
 msgid ""
 "  (use \"git am --allow-empty\" to record this patch as an empty commit)"
 msgstr ""
-"  (använd \"git am --allow-empty\" för att registrera patchen som en tom "
+"  (använd ”git am --allow-empty” för att registrera patchen som en tom "
 "incheckning)"
 
 msgid "  (use \"git am --abort\" to restore the original branch)"
-msgstr "  (använd \"git am --abort\" för att återställa ursprungsgrenen)"
+msgstr "  (använd ”git am --abort” för att återställa ursprungsgrenen)"
 
 msgid "git-rebase-todo is missing."
 msgstr "git-rebase-todo saknas."
@@ -21239,79 +21832,79 @@ msgstr[0] "Nästa kommando att utföra (%<PRIuMAX> kommando återstår):"
 msgstr[1] "Följande kommandon att utföra (%<PRIuMAX> kommandon återstår):"
 
 msgid "  (use \"git rebase --edit-todo\" to view and edit)"
-msgstr "  (använd \"git rebase --edit-todo\" för att visa och redigera)"
+msgstr "  (använd ”git rebase --edit-todo” för att visa och redigera)"
 
 #, c-format
 msgid "You are currently rebasing branch '%s' on '%s'."
-msgstr "Du håller på att ombasera grenen \"%s\" ovanpå \"%s\"."
+msgstr "Du håller på att ombasera grenen ”%s” ovanpå ”%s”."
 
 msgid "You are currently rebasing."
 msgstr "Du håller på med en ombasering."
 
 msgid "  (fix conflicts and then run \"git rebase --continue\")"
-msgstr "  (rätta konflikter och kör sedan \"git rebase --continue\")"
+msgstr "  (rätta konflikter och kör sedan ”git rebase --continue”)"
 
 msgid "  (use \"git rebase --skip\" to skip this patch)"
-msgstr "  (använd \"git rebase --skip\" för att hoppa över patchen)"
+msgstr "  (använd ”git rebase --skip” för att hoppa över patchen)"
 
 msgid "  (use \"git rebase --abort\" to check out the original branch)"
-msgstr "  (använd \"git rebase --abort\" för att checka ut ursprungsgrenen)"
+msgstr "  (använd ”git rebase --abort” för att checka ut ursprungsgrenen)"
 
 msgid "  (all conflicts fixed: run \"git rebase --continue\")"
-msgstr "  (alla konflikter rättade: kör \"git rebase --continue\")"
+msgstr "  (alla konflikter rättade: kör ”git rebase --continue”)"
 
 #, c-format
 msgid ""
 "You are currently splitting a commit while rebasing branch '%s' on '%s'."
 msgstr ""
-"Du håller på att dela upp en incheckning medan du ombaserar grenen \"%s\" "
-"ovanpå \"%s\"."
+"Du håller på att dela upp en incheckning medan du ombaserar grenen ”%s” "
+"ovanpå ”%s”."
 
 msgid "You are currently splitting a commit during a rebase."
 msgstr "Du håller på att dela upp en incheckning i en ombasering."
 
 msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
-msgstr "  (Så fort din arbetskatalog är ren, kör \"git rebase --continue\")"
+msgstr "  (Så fort din arbetskatalog är ren, kör ”git rebase --continue”)"
 
 #, c-format
 msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
 msgstr ""
-"Du håller på att redigera en incheckning medan du ombaserar grenen \"%s\" "
-"ovanpå \"%s\"."
+"Du håller på att redigera en incheckning medan du ombaserar grenen ”%s” "
+"ovanpå ”%s”."
 
 msgid "You are currently editing a commit during a rebase."
 msgstr "Du håller på att redigera en incheckning under en ombasering."
 
 msgid "  (use \"git commit --amend\" to amend the current commit)"
 msgstr ""
-"  (använd \"git commit --amend\" för att lägga till på aktuell incheckning)"
+"  (använd ”git commit --amend” för att lägga till på aktuell incheckning)"
 
 msgid ""
 "  (use \"git rebase --continue\" once you are satisfied with your changes)"
-msgstr "  (använd \"git rebase --continue\" när du är nöjd med dina ändringar)"
+msgstr "  (använd ”git rebase --continue” när du är nöjd med dina ändringar)"
 
 msgid "Cherry-pick currently in progress."
 msgstr "Cherry-pick pågår."
 
 #, c-format
 msgid "You are currently cherry-picking commit %s."
-msgstr "Du håller på med en \"cherry-pick\" av incheckningen %s."
+msgstr "Du håller på med en ”cherry-pick” av incheckningen %s."
 
 msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
-msgstr "  (rätta konflikter och kör sedan \"git cherry-pick --continue\")"
+msgstr "  (rätta konflikter och kör sedan ”git cherry-pick --continue”)"
 
 msgid "  (run \"git cherry-pick --continue\" to continue)"
-msgstr "  (kör \"git cherry-pick --continue\" för att fortsätta)"
+msgstr "  (kör ”git cherry-pick --continue” för att fortsätta)"
 
 msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
-msgstr "  (alla konflikter rättade: kör \"git cherry-pick --continue\")"
+msgstr "  (alla konflikter rättade: kör ”git cherry-pick --continue”)"
 
 msgid "  (use \"git cherry-pick --skip\" to skip this patch)"
-msgstr "  (använd \"git cherry-pick --skip\" för att hoppa över patchen)"
+msgstr "  (använd ”git cherry-pick --skip” för att hoppa över patchen)"
 
 msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
 msgstr ""
-"  (använd \"git cherry-pick --abort\" för att avbryta \"cherry-pick\"-"
+"  (använd ”git cherry-pick --abort” för att avbryta ”cherry-pick”-"
 "operationen)"
 
 msgid "Revert currently in progress."
@@ -21322,30 +21915,30 @@ msgid "You are currently reverting commit %s."
 msgstr "Du håller på med att ångra incheckningen %s."
 
 msgid "  (fix conflicts and run \"git revert --continue\")"
-msgstr "  (rätta konflikter och kör sedan \"git revert --continue\")"
+msgstr "  (rätta konflikter och kör sedan ”git revert --continue”)"
 
 msgid "  (run \"git revert --continue\" to continue)"
-msgstr "  (kör \"git revert --continue\" för att fortsätta)"
+msgstr "  (kör ”git revert --continue” för att fortsätta)"
 
 msgid "  (all conflicts fixed: run \"git revert --continue\")"
-msgstr "  (alla konflikter rättade: kör \"git revert --continue\")"
+msgstr "  (alla konflikter rättade: kör ”git revert --continue”)"
 
 msgid "  (use \"git revert --skip\" to skip this patch)"
-msgstr "  (använd \"git revert --skip\" för att hoppa över patchen)"
+msgstr "  (använd ”git revert --skip” för att hoppa över patchen)"
 
 msgid "  (use \"git revert --abort\" to cancel the revert operation)"
-msgstr "  (använd \"git revert --abort\" för att avbryta ångrandet)"
+msgstr "  (använd ”git revert --abort” för att avbryta ångrandet)"
 
 #, c-format
 msgid "You are currently bisecting, started from branch '%s'."
-msgstr "Du håller på med en \"bisect\", startad från grenen \"%s\"."
+msgstr "Du håller på med en ”bisect”, startad från grenen ”%s”."
 
 msgid "You are currently bisecting."
-msgstr "Du håller på med en \"bisect\"."
+msgstr "Du håller på med en ”bisect”."
 
 msgid "  (use \"git bisect reset\" to get back to the original branch)"
 msgstr ""
-"  (använd \"git bisect reset\" för att komma tillbaka till ursprungsgrenen)"
+"  (använd ”git bisect reset” för att komma tillbaka till ursprungsgrenen)"
 
 msgid "You are in a sparse checkout."
 msgstr "Du är i en gles utcheckning."
@@ -21386,13 +21979,18 @@ msgstr "Ignorerade filer"
 
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
 msgstr ""
-"Det tog %.2f sekunder att räkna upp ospårade filer. \"status -uno\"\n"
-"kan gå snabbare, men du måste vara försiktig så du inte glömmer\n"
-"lägga till nya filer själv (se \"git help status\")."
+"Det tog %.2f sekunder att räkna upp ospårade filer,\n"
+"men resultaten cachelagrades och senare körningar kan bli snabbare."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "Det tog %.2f sekunder att räkna upp ospårade filer."
+
+msgid "See 'git help status' for information on how to improve this."
+msgstr "Se ”git help status” för information om hur du kan förbättra detta."
 
 # %s är nästa sträng eller tom.
 #, c-format
@@ -21408,8 +22006,7 @@ msgstr "Inga ändringar"
 #, c-format
 msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
 msgstr ""
-"inga ändringar att checka in (använd \"git add\" och/eller \"git commit -a"
-"\")\n"
+"inga ändringar att checka in (använd ”git add” och/eller ”git commit -a”)\n"
 
 #, c-format
 msgid "no changes added to commit\n"
@@ -21420,8 +22017,7 @@ msgid ""
 "nothing added to commit but untracked files present (use \"git add\" to "
 "track)\n"
 msgstr ""
-"inget köat för incheckning, men ospårade filer finns (spåra med \"git add"
-"\")\n"
+"inget köat för incheckning, men ospårade filer finns (spåra med ”git add”)\n"
 
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
@@ -21429,7 +22025,7 @@ msgstr "inget köat för incheckning, men ospårade filer finns\n"
 
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
-msgstr "inget att checka in (skapa/kopiera filer och spåra med \"git add\")\n"
+msgstr "inget att checka in (skapa/kopiera filer och spåra med ”git add”)\n"
 
 #, c-format
 msgid "nothing to commit\n"
@@ -21535,274 +22131,6 @@ msgstr "Du måste köra kommandot från arbetskatalogens toppnivå."
 msgid "Unable to determine absolute path of git directory"
 msgstr "Kunde inte bestämma absolut sökväg till git-katalogen"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %12s %s"
-
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "rörde %d sökväg\n"
-msgstr[1] "rörde %d sökvägar\n"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-"Om patchen kan appliceras rent kommer det redigerade stycket att\n"
-"köas omedelbart."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-"Om patchen kan appliceras rent kommer det redigerade stycket att\n"
-"läggas till i \"stash\" omedelbart."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-"Om patchen kan appliceras rent kommer det redigerade stycket att\n"
-"tas bort från kön omedelbart."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-"Om patchen kan appliceras rent kommer det redigerade stycket att\n"
-"markeras för applicering omedelbart."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-"Om patchen kan appliceras rent kommer det redigerade stycket att\n"
-"markeras för kasta omedelbart."
-
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "misslyckades öppna styckeredigeringsfil för skrivning: %s"
-
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"Ta bort \"%s\" rader genom att göra dem \" \"-rader (sammanhang).\n"
-"Ta bort \"%s\" rader genom att radera dem.\n"
-"Rader som börjar med %s kommer att tas bort.\n"
-
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "misslyckades öppna styckesredigeringsfil för läsning: %s"
-
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - köa stycket\n"
-"n - köa inte stycket\n"
-"q - avsluta; köa inte stycket eller något av de följande\n"
-"a - köa stycket och alla följande i filen\n"
-"d - köa inte stycket eller något av de följande i filen"
-
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - \"stash\":a stycket\n"
-"n - \"stash\":a inte stycket\n"
-"q - avsluta; \"stash\":a inte stycket eller något av de följande\n"
-"a - \"stash\":a stycket och alla följande i filen\n"
-"d - \"stash\":a inte stycket eller något av de följande i filen"
-
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - ta bort stycket från kön\n"
-"n - ta inte bort stycket från kön\n"
-"q - avsluta; ta inte bort stycket eller något av de följande från kön\n"
-"a - ta bort stycket och alla följande i filen från kön\n"
-"d - ta inte bort stycket eller något av de följande i filen från kön"
-
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - applicera stycket på indexet\n"
-"n - applicera inte stycket på indexet\n"
-"q - avsluta; applicera inte stycket eller något av de följande\n"
-"a - applicera stycket och alla följande i filen\n"
-"d - applicera inte stycket eller något av de följande i filen"
-
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - förkasta stycket från arbetskatalogen\n"
-"n - förkasta inte stycket från arbetskatalogen\n"
-"q - avsluta; förkasta inte stycket eller något av de följande\n"
-"a - förkasta stycket och alla följande i filen\n"
-"d - förkasta inte stycket eller något av de följande i filen"
-
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - förkasta stycket från indexet och arbetskatalogen\n"
-"n - förkasta inte stycket från indexet och arbetskatalogen\n"
-"q - avsluta; förkasta inte stycket eller något av de följande\n"
-"a - förkasta stycket och alla följande i filen\n"
-"d - förkasta inte stycket eller något av de följande i filen"
-
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - applicera stycket på indexet och arbetskatalogen\n"
-"n - applicera inte stycket på indexet och arbetskatalogen\n"
-"q - avsluta; applicera inte stycket eller något av de följande\n"
-"a - applicera stycket och alla följande i filen\n"
-"d - applicera inte stycket eller något av de följande i filen"
-
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - applicera stycket på arbetskatalogen\n"
-"n - applicera inte stycket på arbetskatalogen\n"
-"q - avsluta; applicera inte stycket eller något av de följande\n"
-"a - applicera stycket och alla följande i filen\n"
-"d - applicera inte stycket eller något av de följande i filen"
-
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - välj ett stycke att gå till\n"
-"/ - sök efter stycke som motsvarar angivet reguljärt uttryck\n"
-"j - lämna stycket obestämt, se nästa obestämda stycke\n"
-"J - lämna stycket obestämt, se nästa stycke\n"
-"k - lämna stycket obestämt, se föregående obestämda stycke\n"
-"K - lämna stycket obestämt, se föregående stycke\n"
-"s - dela aktuellt stycke i mindre styckens\n"
-"e - redigera aktuellt stycke manuellt\n"
-"? - visa hjälp\n"
-
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "Markerade stycken kan inte appliceras på indexet!\n"
-
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "ignorerar ej sammanslagen: %s\n"
-
-msgid "No other hunks to goto\n"
-msgstr "Inga andra stycken att gå till\n"
-
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "Ogiltigt siffervärde: \"%s\"\n"
-
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "Beklagar, det finns bara %d stycke.\n"
-msgstr[1] "Beklagar, det finns bara %d stycken.\n"
-
-msgid "No other hunks to search\n"
-msgstr "Inga andra stycken att söka efter\n"
-
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "Felaktigt format på reguljärt sökuttryck %s: %s\n"
-
-msgid "No hunk matches the given pattern\n"
-msgstr "Inga stycken motsvarar givet mönster\n"
-
-msgid "No previous hunk\n"
-msgstr "Inget föregående stycke\n"
-
-msgid "No next hunk\n"
-msgstr "Inget följande stycke\n"
-
-msgid "Sorry, cannot split this hunk\n"
-msgstr "Beklagar, kan inte dela stycket\n"
-
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "Dela i %d stycke.\n"
-msgstr[1] "Dela i %d stycken.\n"
-
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "Beklagar, kan inte redigera stycket\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - visa sökvägar med ändringar\n"
-"update        - lägg arbetskatalogens tillstånd till köade ändringar\n"
-"revert        - återställ köade ändringar till HEAD-versionen\n"
-"patch         - välj och uppdatera valda stycken\n"
-"diff          - visa diff mellan HEAD och index\n"
-"add untracked - lägg till innehåll i ospårade filer till köade ändringar\n"
-
-msgid "missing --"
-msgstr "saknad --"
-
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "okänt läge för --patch: %s"
-
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "felaktigt argument %s, förväntar --"
-
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "lokal zon skiljer sig från GMT med delar av minuter\n"
 
@@ -21811,7 +22139,7 @@ msgstr "lokal tidszonförskjutning större än eller lika med 24 timmar\n"
 
 #, perl-format
 msgid "fatal: command '%s' died with exit code %d"
-msgstr "ödesdigert: kommandot \"%s\" dog med slutkoden %d"
+msgstr "ödesdigert: kommandot ”%s” dog med slutkoden %d"
 
 msgid "the editor exited uncleanly, aborting everything"
 msgstr "textredigeringsprogrammet avslutades med fel, avbryter allting"
@@ -21819,12 +22147,11 @@ msgstr "textredigeringsprogrammet avslutades med fel, avbryter allting"
 #, perl-format
 msgid ""
 "'%s' contains an intermediate version of the email you were composing.\n"
-msgstr ""
-"\"%s\" innehåller en mellanliggande version av e-postbrevet du skrev.\n"
+msgstr "”%s” innehåller en mellanliggande version av e-postbrevet du skrev.\n"
 
 #, perl-format
 msgid "'%s.final' contains the composed email.\n"
-msgstr "\"%s.final\" innehåller det skrivna brevet.\n"
+msgstr "”%s.final” innehåller det skrivna brevet.\n"
 
 msgid "--dump-aliases incompatible with other options\n"
 msgstr "--dump-aliases är inkompatibelt med andra flaggor\n"
@@ -21834,9 +22161,9 @@ msgid ""
 "git-send-email is configured with the sendemail.* options - note the 'e'.\n"
 "Set sendemail.forbidSendmailVariables to false to disable this check.\n"
 msgstr ""
-"ödesdigert: hittade konfigurationsflaggor för \"sendmail\"\n"
-"git-send-email konfigureras med \"sendemail.*\"-flaggor - lägg märke till \"e"
-"\".\n"
+"ödesdigert: hittade konfigurationsflaggor för ”sendmail”\n"
+"git-send-email konfigureras med ”sendemail.*”-flaggor - lägg märke till "
+"”e”.\n"
 "Sätt sendemail.forbidSendmailVariables till false för att inaktivera denna "
 "kontroll.\n"
 
@@ -21847,16 +22174,16 @@ msgid ""
 "`batch-size` and `relogin` must be specified together (via command-line or "
 "configuration option)\n"
 msgstr ""
-"\"batch-size\" och \"relogin\" måste anges tillsammans (via kommandorad "
-"eller konfigurationsflagga)\n"
+"”batch-size” och ”relogin” måste anges tillsammans (via kommandorad eller "
+"konfigurationsflagga)\n"
 
 #, perl-format
 msgid "Unknown --suppress-cc field: '%s'\n"
-msgstr "Okänt fält i --suppress-cc: \"%s\"\n"
+msgstr "Okänt fält i --suppress-cc: ”%s”\n"
 
 #, perl-format
 msgid "Unknown --confirm setting: '%s'\n"
-msgstr "Okänd inställning i --confirm: \"%s\"\n"
+msgstr "Okänd inställning i --confirm: ”%s”\n"
 
 #, perl-format
 msgid "warning: sendmail alias with quotes is not supported: %s\n"
@@ -21864,11 +22191,11 @@ msgstr "varning: sendmail-alias med citationstecken stöds inte. %s\n"
 
 #, perl-format
 msgid "warning: `:include:` not supported: %s\n"
-msgstr "varning: \":include:\" stöds inte: %s\n"
+msgstr "varning: ”:include:” stöds inte: %s\n"
 
 #, perl-format
 msgid "warning: `/file` or `|pipe` redirection not supported: %s\n"
-msgstr "varning: omdirigering til \"/fil\" eller \"|rör\" stöds inte: %s\n"
+msgstr "varning: omdirigering til ”/fil” eller ”|rör” stöds inte: %s\n"
 
 #, perl-format
 msgid "warning: sendmail line is not recognized: %s\n"
@@ -21882,10 +22209,10 @@ msgid ""
 "    * Saying \"./%s\" if you mean a file; or\n"
 "    * Giving --format-patch option if you mean a range.\n"
 msgstr ""
-"Filen \"%s\" finns men kan också vara ett incheckningsintervall\n"
+"Filen ”%s” finns men kan också vara ett incheckningsintervall\n"
 "att skapa patchar för. Gör otvetydigt genom att...\n"
 "\n"
-"    * Säga \"./%s\" om du menar en fil; eller\n"
+"    * Säga ”./%s” om du menar en fil; eller\n"
 "    * Ange flaggan --format-patch om du menar ett intervall.\n"
 
 #, perl-format
@@ -21916,20 +22243,20 @@ msgid ""
 "\n"
 "Clear the body content if you don't wish to send a summary.\n"
 msgstr ""
-"Rader som börjar med \"GIT:\" kommer tas bort.\n"
+"Rader som börjar med ”GIT:” kommer tas bort.\n"
 "Överväg att ta med en övergripande diffstatus eller\n"
 "innehållsförteckning för patchen du skriver.\n"
 "\n"
 "Rensa brevkroppen om du inte vill sända någon sammanfattning.\n"
 
-#, perl-format
-msgid "Failed to open %s: %s"
-msgstr "Misslyckades öppna %s: %s"
-
 #, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "Misslyckades öppna %s.final: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "Misslyckades öppna %s: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "Sammanfattande brev tomt, hoppar över\n"
 
@@ -21956,15 +22283,15 @@ msgid ""
 msgstr ""
 "Vägrar sända eftersom patchen\n"
 "\t%s\n"
-"har mallärendet \"*** SUBJECT HERE ***\". Använd --force om du verkligen "
-"vill sända.\n"
+"har mallärendet ”*** SUBJECT HERE ***”. Använd --force om du verkligen vill "
+"sända.\n"
 
 msgid "To whom should the emails be sent (if anyone)?"
 msgstr "Till vem ska breven sändas (om någon)?"
 
 #, perl-format
 msgid "fatal: alias '%s' expands to itself\n"
-msgstr "ödesdigert: aliaset \"%s\" expanderar till sig själv\n"
+msgstr "ödesdigert: aliaset ”%s” expanderar till sig själv\n"
 
 msgid "Message-ID to be used as In-Reply-To for the first email (if any)? "
 msgstr ""
@@ -21982,7 +22309,7 @@ msgstr "Vad vill du göra med adressen? (q=avsluta, d=kasta, e=redigera): "
 
 #, perl-format
 msgid "CA path \"%s\" does not exist"
-msgstr "CA-sökvägen \"%s\" finns inte"
+msgstr "CA-sökvägen ”%s” finns inte"
 
 msgid ""
 "    The Cc list above has been expanded by additional\n"
@@ -22002,9 +22329,9 @@ msgstr ""
 "    Beteendet styrs av konfigurationsinställningen\n"
 "    sendemail.confirm\n"
 "\n"
-"    För ytterligare information, kör \"git send-email --help\".\n"
+"    För ytterligare information, kör ”git send-email --help”.\n"
 "    För att behålla nuvarande beteende, men dölja detta\n"
-"    meddelande, kör \"git config --global sendemail.confirm auto\".\n"
+"    meddelande, kör ”git config --global sendemail.confirm auto”.\n"
 "\n"
 
 #. TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your
@@ -22014,7 +22341,7 @@ msgid "Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "
 msgstr "Sända brevet? (y=ja, n=nej, e=redigera, q=avsluta, a=alla): "
 
 msgid "Send this email reply required"
-msgstr "Svar krävs på frågan \"Sända brevet?\""
+msgstr "Svar krävs på frågan ”Sända brevet?”"
 
 msgid "The required SMTP server is not properly defined."
 msgstr "Nödvändig SMTP-server har inte angivits korrekt."
@@ -22062,31 +22389,35 @@ msgstr "kan inte öppna filen %s"
 
 #, perl-format
 msgid "(mbox) Adding cc: %s from line '%s'\n"
-msgstr "(mbox) Lägger till cc: %s från raden \"%s\"\n"
+msgstr "(mbox) Lägger till cc: %s från raden ”%s”\n"
 
 #, perl-format
 msgid "(mbox) Adding to: %s from line '%s'\n"
-msgstr "(mbox) Lägger till to: %s från raden \"%s\"\n"
+msgstr "(mbox) Lägger till to: %s från raden ”%s”\n"
 
 #, perl-format
 msgid "(non-mbox) Adding cc: %s from line '%s'\n"
-msgstr "(icke-mbox) Lägger till cc: %s från raden \"%s\"\n"
+msgstr "(icke-mbox) Lägger till cc: %s från raden ”%s”\n"
 
 #, perl-format
 msgid "(body) Adding cc: %s from line '%s'\n"
-msgstr "(kropp) Lägger till cc: %s från raden \"%s\"\n"
+msgstr "(kropp) Lägger till cc: %s från raden ”%s”\n"
 
 #, perl-format
 msgid "(%s) Could not execute '%s'"
-msgstr "(%s) Kunde inte köra \"%s\""
+msgstr "(%s) Kunde inte köra ”%s”"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Lägger till %s: %s från: \"%s\"\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Felformaterad utdata från ”%s”"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
-msgstr "(%s) misslyckades stänga röret till \"%s\""
+msgstr "(%s) misslyckades stänga röret till ”%s”"
+
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Lägger till %s: %s från: ”%s”\n"
 
 msgid "cannot send message as 7bit"
 msgstr "kan inte sända brev som sjubitars"
@@ -22119,8 +22450,7 @@ msgstr ""
 #, perl-format
 msgid "Skipping %s with backup suffix '%s'.\n"
 msgstr ""
-"Hoppar över %s med filnamnstillägget \"%s\" som används för "
-"säkerhetskopior.\n"
+"Hoppar över %s med filnamnstillägget ”%s” som används för säkerhetskopior.\n"
 
 #. TRANSLATORS: please keep "[y|N]" as is.
 #, perl-format
index 484e0acf2d27435deb14cf29aa0fca78b43a7d6e..f01962d1dbdb40028e6c949e6d86e6f9cd322d55 100644 (file)
--- a/po/tr.po
+++ b/po/tr.po
@@ -1,8 +1,8 @@
 # Turkish translations for Git
 # Git Türkçe çevirileri
-# Copyright (C) 2020-2022 Emir SARI <emir_sari@icloud.com>
+# Copyright (C) 2020-2023 Emir SARI <emir_sari@icloud.com>
 # This file is distributed under the same license as the Git package.
-# Emir SARI <emir_sari@icloud.com>, 2020-2022
+# Emir SARI <emir_sari@icloud.com>, 2020-2023
 #
 # ######################################################### #
 #     Git Türkçe kavramlar dizini / Git Turkish Glossary    #
@@ -39,6 +39,8 @@
 # mark                        | im(lemek)                   #
 # merge                       | birleştirme(k)              #
 # octopus                     | ahtapot                     #
+# orphan                      | yetim                       #
+# orphaned                    | yetim bırakılmış            #
 # overlay                     | yerpaylaşım                 #
 # pack                        | paket                       #
 # parent                      | üst öge                     #
@@ -92,8 +94,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: 2022-09-28 10:54+0300\n"
-"PO-Revision-Date: 2022-09-28 14:00+0300\n"
+"POT-Creation-Date: 2023-11-09 11:15+0300\n"
+"PO-Revision-Date: 2023-11 13: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"
@@ -128,13 +130,13 @@ msgstr "'%s' hazırlanamadı"
 msgid "could not write index"
 msgstr "indeks yazılamadı"
 
-#, c-format, perl-format
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "%d yol güncellendi\n"
 msgstr[1] "%d yol güncellendi\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "not: %s artık izlenmiyor.\n"
 
@@ -148,7 +150,7 @@ msgstr "Geri al"
 msgid "Could not parse HEAD^{tree}"
 msgstr "HEAD^{tree} ayrıştırılamadı"
 
-#, c-format, perl-format
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "%d yol geri alındı\n"
@@ -161,7 +163,7 @@ msgstr "İzlenmeyen dosya yok.\n"
 msgid "Add untracked"
 msgstr "İzlenmeyenleri ekle"
 
-#, c-format, perl-format
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "%d yol eklendi\n"
@@ -255,19 +257,19 @@ msgstr "indeks yenilenemedi"
 msgid "Bye.\n"
 msgstr "Güle güle.\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "Kip değişimi hazırlansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Silme hazırlansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Ekleme hazırlansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Bu parça hazırlansın mı [y,n,q,a,d%s,?]? "
 
@@ -291,19 +293,19 @@ msgstr ""
 "a - bu parçayı ve sonraki tüm parçaları hazırla\n"
 "d - bu parçayı veya sonraki parçalardan herhangi birini hazırlama\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "Kip değişimi zulalansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Silme zulalansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Ekleme zulalansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Bu parça zulalansın mı [y,n,q,a,d%s,?]? "
 
@@ -327,19 +329,19 @@ msgstr ""
 "a - bu parçayı ve sonraki tüm parçaları zulala\n"
 "d - bu parçayı veya sonraki parçalardan herhangi birini zulalama\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "Kip değişimi hazırlıktan çıkarılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Silme hazırlıktan çıkarılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Ekleme hazırlıktan çıkarılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Bu parça hazırlıktan çıkarılsın mı [y,n,q,a,d%s,?]? "
 
@@ -363,19 +365,19 @@ msgstr ""
 "a - bu parçayı ve sonraki tüm parçaları hazırlıktan çıkar\n"
 "d - bu parçayı veya sonraki parçalardan herhangi birini hazırlıktan çıkarma\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "Kip değişimi indekse uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Silme indekse uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Ekleme indekse uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Bu parça indekse uygulansın mı [y,n,q,a,d%s,?]? "
 
@@ -399,19 +401,19 @@ msgstr ""
 "a - bu parçayı ve sonraki tüm parçaları uygula\n"
 "d - bu parçayı veya sonraki parçalardan herhangi birini uygulama\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "Kip değişimi çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Silme çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Ekleme çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "Bu parça çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
 
@@ -435,20 +437,20 @@ msgstr ""
 "a - bu parçayı ve sonraki tüm parçaları at\n"
 "d - bu parçayı veya sonraki parçalardan herhangi birini atma\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Kip değişimi indeksten ve çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Silme indeksten ve çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Ekleme indeksten ve çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Bu parça indeksten ve çalışma ağacından atılsın mı [y,n,q,a,d%s,?]? "
 
@@ -465,20 +467,20 @@ msgstr ""
 "a - bu parçayı ve sonraki tüm parçaları at\n"
 "d - bu parçayı veya sonraki parçalardan herhangi birini atma\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr ""
 "Kip değişimi indekse ve çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Silme indekse ve çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Ekleme indekse ve çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Bu parça indekse ve çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
 
@@ -495,19 +497,19 @@ msgstr ""
 "a - bu parçayı ve sonraki tüm parçaları uygula\n"
 "d - bu parçayı veya sonraki parçalardan herhangi birini uygulama\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr "Kip değişimi çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "Silme çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "Ekleme çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "Bu parça çalışma ağacına uygulansın mı [y,n,q,a,d%s,?]? "
 
@@ -583,8 +585,6 @@ msgstr ""
 "'%c' satır kaldırmak için onları silin.\n"
 "%c kaldırılacak.\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -600,20 +600,12 @@ msgstr "parça üstbilgisi ayrıştırılamadı"
 msgid "'git apply --cached' failed"
 msgstr "'git apply --cached' başarısız oldu"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
@@ -728,9 +720,10 @@ msgstr "Çekme yapılamaz; birleştirmesi tamamlanmamış dosyalarınız var."
 msgid "Reverting is not possible because you have unmerged files."
 msgstr "Geriye al yapılamaz; birleştirmesi tamamlanmamış dosyalarınız var."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "%s yapılamıyor; birleştirmesi tamamlanmamış dosyalarınız var."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr ""
+"Yeniden temellendirme yapılamaz; birleştirmesi tamamlanmamış dosyalarınız "
+"var."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -751,6 +744,23 @@ msgstr "Birleştirme öncesinde değişikliklerinizi işleyin."
 msgid "Exiting because of unfinished merge."
 msgstr "Tamamlanmamış birleştirmeden dolayı çıkılıyor."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Iraksak dallar ileri sarılamaz; şunlardan birini yapmanız gerekiyor:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"veya\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "İleri sarma olanaklı değil, iptal ediliyor."
 
@@ -840,6 +850,9 @@ msgstr "komut satırı \\ ile bitiyor"
 msgid "unclosed quote"
 msgstr "kapatılmamış tırnak"
 
+msgid "too many arguments"
+msgstr "pek fazla argüman"
+
 #, c-format
 msgid "unrecognized whitespace option '%s'"
 msgstr "tanımlanamayan boşluk seçeneği '%s'"
@@ -856,6 +869,12 @@ msgstr "'%s' ve '%s' seçenekleri birlikte kullanılamaz"
 msgid "'%s' outside a repository"
 msgstr "'%s' bir depo dışında"
 
+msgid "failed to read patch"
+msgstr "yama okunamadı"
+
+msgid "patch too large"
+msgstr "yama pek büyük"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "Zaman damgası düzenli ifadesi %s hazırlanamıyor"
@@ -1189,6 +1208,10 @@ msgstr ".rej dosya adı %.*s.rej olarak kısaltılıyor"
 msgid "cannot open %s"
 msgstr "%s açılamıyor"
 
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "'%s' bağlantısı kesilemiyor"
+
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Parça #%d sorunsuzca uygulandı."
@@ -1372,9 +1395,13 @@ msgstr "git archive --remote <depo> [--exec <komut>] --list"
 msgid "cannot read '%s'"
 msgstr "'%s' okunamıyor"
 
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "yol belirteci '%s', geçerli dizinin dışındaki dosyalarla eşleşiyor"
+
 #, c-format
 msgid "pathspec '%s' did not match any files"
-msgstr "yol belirteci '%s' hiçbir dosya ile eşleşmedi"
+msgstr "yol belirteci '%s', hiçbir dosya ile eşleşmedi"
 
 #, c-format
 msgid "no such ref: %.*s"
@@ -1388,9 +1415,6 @@ msgstr "geçerli bir nesne adı değil: %s"
 msgid "not a tree object: %s"
 msgstr "bir ağaç nesnesi değil: %s"
 
-msgid "current working directory is untracked"
-msgstr "geçerli çalışma dizini izlenmiyor"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Dosya bulunamadı: %s"
@@ -1441,6 +1465,12 @@ msgstr "çalışma dizinindeki .gitattributes'u oku"
 msgid "report archived files on stderr"
 msgstr "arşivlenmiş dosyaları stderr'de raporla"
 
+msgid "time"
+msgstr "zaman"
+
+msgid "set modification time of archive entries"
+msgstr "arşiv girdilerinin değiştirilme zamanını ayarla"
+
 msgid "set compression level"
 msgstr "sıkıştırma düzeyini ayarla"
 
@@ -1481,6 +1511,13 @@ msgstr "'%s' biçimi için desteklenmeyen argüman: -%d"
 msgid "%.*s is not a valid attribute name"
 msgstr "%.*s geçerli bir öznitelik adı değil"
 
+msgid "unable to add additional attribute"
+msgstr "ek öznitelik eklenemiyor"
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "pek uzun öznitelik satırı %d yok sayılıyor"
+
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "%s izin verilmiyor: %s:%d"
@@ -1492,6 +1529,21 @@ msgstr ""
 "Negatif dizgiler git özniteliklerinde yok sayılır.\n"
 "Gerçek öncü ünlem için '\\!' kullanın."
 
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "fstat gitattributes dosyası '%s' bulunamıyor"
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "pek büyük gitattributes dosyası '%s' dosyası yok sayılıyor"
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "pek büyük gitattributes ikili nesnesi '%s' yok sayılıyor"
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "hatalı --attr-source veya GIT_ATTR_SOURCE"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "'%s' dosyasında hatalı tırnağa alınmış içerik: %s"
@@ -1597,9 +1649,6 @@ msgstr[1] "İkili arama: Şundan sonra sınanacak %d revizyon kaldı: %s\n"
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents ve --reverse birlikte pek iyi gitmiyor."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "--contents son işleme nesnesi adı ile kullanılamıyor"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse ve --first-parent birlikte en son işlemenin belirtilmesini "
@@ -1720,8 +1769,10 @@ msgid "a branch named '%s' already exists"
 msgstr "'%s' adında bir dal halihazırda var"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "'%s' dalı zorla güncellenemiyor, '%s' konumunda çıkış yapılmış"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr ""
+"şuradaki çalışma ağacı tarafından kullanılan '%s' dalı zorla "
+"güncellenemiyor: '%s'"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1768,23 +1819,19 @@ msgstr "'%s' altmodülü: altmodül bulunamıyor"
 
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
-"Altmodülleri güncellemeyi 'git checkout %s && git submodule update --init' "
-"kullanarak deneyebilirsiniz"
+"'git checkout --no-recurse-submodules %s && git submodule update --init' "
+"kullanarak altmodülleri güncellemeyi deneyebilirsiniz"
 
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
 msgstr "'%s' altmodülü: '%s' dalı oluşturulamıyor"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' çıkışı '%s' konumunda halihazırda yapılmış"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "%s çalışma ağacının HEAD'i güncellenmemiş"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s', '%s' konumunda halihazırda çalışma ağacı tarafından kullanılıyor"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<seçenekler>] [--] <yol-blrtç>..."
@@ -1793,38 +1840,31 @@ msgstr "git add [<seçenekler>] [--] <yol-blrtç>..."
 msgid "cannot chmod %cx '%s'"
 msgstr "%cx '%s' chmod yapılamıyor"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "beklenmedik diff durumu %c"
-
-msgid "updating files failed"
-msgstr "dosyaları güncelleme başarısız"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "kaldır: '%s'\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr "İndeksi yeniledikten sonra hazırlanmamış değişiklikler:"
 
-msgid "Could not read the index"
-msgstr "İndeks okunamadı"
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"add.interactive.useBuiltin ayarı kaldırıldı!\n"
+"Ayrıntılar için onun 'git help config' içindeki girdisine bakın."
 
-msgid "Could not write patch"
-msgstr "Yama yazılamadı"
+msgid "could not read the index"
+msgstr "indeks okunamadı"
 
 msgid "editing patch failed"
 msgstr "yamayı düzenleme başarısız"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "'%s' dosya bilgileri alınamadı"
+msgid "could not stat '%s'"
+msgstr "'%s' bilgileri alınamadı"
 
-msgid "Empty patch. Aborted."
-msgstr "Boş yama. İptal edildi."
+msgid "empty patch. aborted"
+msgstr "boş yama. iptal edildi."
 
 #, c-format
-msgid "Could not apply '%s'"
+msgid "could not apply '%s'"
 msgstr "'%s' uygulanamadı"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
@@ -1880,7 +1920,7 @@ msgid "allow updating entries outside of the sparse-checkout cone"
 msgstr "aralıklı çıkış konisi dışındaki girdileri güncellemeye izin ver"
 
 msgid "override the executable bit of the listed files"
-msgstr "listelenen dosyaların çalıştırılabilir kısımlarını geçersiz kıl"
+msgstr "listelenen dosyaların yürütülebilir kısımlarını geçersiz kıl"
 
 msgid "warn when adding an embedded repository"
 msgstr "gömülü bir depo eklenirken uyar"
@@ -1955,6 +1995,9 @@ msgstr ""
 msgid "index file corrupt"
 msgstr "indeks dosyası hasarlı"
 
+msgid "unable to write new index file"
+msgstr "yeni indeks dosyası yazılamıyor"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "hatalı eylem '%s', '%s' için"
@@ -2163,9 +2206,6 @@ msgstr ""
 "Bir dosyanın \"onlar sildi\" olduğunu kabul etmek için dosya ile 'git rm' "
 "yapabilirsiniz."
 
-msgid "unable to write new index file"
-msgstr "yeni indeks dosyası yazılamıyor"
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "'%s' nesnesi ayrıştırılamadı."
@@ -2184,10 +2224,6 @@ msgstr ""
 msgid "failed to read '%s'"
 msgstr "'%s' okunamadı"
 
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "'%s=%s' ve '%s=%s' seçenekleri birlikte kullanılamaz"
-
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<seçenekler>] [(<mbox> | <posta-dizin>)...]"
 
@@ -2197,6 +2233,9 @@ msgstr "git am [<seçenekler>] (--continue | --skip | --abort)"
 msgid "run interactively"
 msgstr "etkileşimli olarak çalıştır"
 
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "pre-applypatch ve applypatch-msg kancalarını atla"
+
 msgid "historical option -- no-op"
 msgstr "tarihi seçenek -- no-op"
 
@@ -2224,10 +2263,6 @@ msgstr "'git-mailinfo'ya -m bayrağını geçir"
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "'git-mailsplit'e mbox biçimi için --keep-cr bayrağını geçir"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"'git-mailsplit'e 'am.keepcr'dan bağımsız olarak --keep-cr bayrağını geçirme"
-
 msgid "strip everything before a scissors line"
 msgstr "bir kesim çizgisinden önceki her şeyi çıkar"
 
@@ -2339,32 +2374,27 @@ msgstr "git archive: Protokol hatası"
 msgid "git archive: expected a flush"
 msgstr "git archive: Floş bekleniyordu"
 
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<işleme>]"
-
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<terim> --term-{old,good}"
-"=<terim>] [--no-checkout] [--first-parent] [<kötü> [<iyi>...]] [--] "
-"[<yollar>...]"
+"git bisect start [--term-(new|bad)=<uçbirim> --term-(old|good)=<uçbirim>]    "
+"[--no-checkout] [--first-parent] [<kötü> [<iyi>...]] [--]   [<yol-blrtç>...]"
 
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<rev>]"
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<rev>...]"
 
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<rev>...]"
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<rev>|<erim>)...]"
 
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <dosyaadı>"
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<işleme>]"
 
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<rev>|<erim>)...]"
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <günlük-dosyası>"
 
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <komut>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <komut> [<argüman>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2508,9 +2538,6 @@ msgstr "hatalı HEAD - Bana bir HEAD gerek"
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
 msgstr "'%s' çıkış yapımı başarısız. 'git bisect start <geçerli-dal>' deneyin."
 
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "cg-seek yapılmış bir ağaçta ikili arama yapılmayacak"
-
 msgid "bad HEAD - strange symbolic ref"
 msgstr "hatalı HEAD - tuhaf sembolik başvuru"
 
@@ -2562,17 +2589,16 @@ msgid "bisect run failed: no command provided."
 msgstr "ikili arama başarısız: Komut verilmedi."
 
 #, c-format
-msgid "unable to verify '%s' on good revision"
-msgstr "'%s', iyi revizyonda doğrulanamadı"
+msgid "unable to verify %s on good revision"
+msgstr "%s, iyi revizyonda doğrulanamıyor"
 
 #, c-format
 msgid "bogus exit code %d for good revision"
 msgstr "iyi revizyon için anlamsız %d çıkış kodu"
 
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
-msgstr ""
-"bisect çalıştırılamadı: çıkış kodu %d, '%s' konumundan, < 0 veya >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "bisect çalıştırılamadı: çıkış kodu %d, %s konumundan, < 0 veya >= 128"
 
 #, c-format
 msgid "cannot open file '%s' for writing"
@@ -2581,76 +2607,48 @@ msgstr "'%s' dosyası yazma için açılamadı"
 msgid "bisect run cannot continue any more"
 msgstr "ikili arama artık çalışmayı sürdüremiyor"
 
-#, c-format
 msgid "bisect run success"
 msgstr "ikili arama başarılı"
 
-#, c-format
 msgid "bisect found first bad commit"
 msgstr "ikili arama ilk hatalı işlemeyi buldu"
 
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
-msgstr ""
-"ikili arama çalıştırılamadı: 'git bisect--helper --bisect-state %s', %d hata "
-"koduyla çıktı"
-
-msgid "reset the bisection state"
-msgstr "ikili arama durumunu sıfırla"
-
-msgid "check whether bad or good terms exist"
-msgstr "iyi veya kötü terimlerin olup olmadığını denetle"
-
-msgid "print out the bisect terms"
-msgstr "ikili arama terimlerini yazdır"
-
-msgid "start the bisect session"
-msgstr "ikili arama oturumunu başlat"
-
-msgid "find the next bisection commit"
-msgstr "bir sonraki ikili arama işlemesini bul"
-
-msgid "mark the state of ref (or refs)"
-msgstr "başvurunun (veya başvuruların) durumunu imle"
-
-msgid "list the bisection steps so far"
-msgstr "şu ana kadarki ikili arama durumunu listele"
-
-msgid "replay the bisection process from the given file"
-msgstr "verilen dosyadan ikili arama işlemini yeniden oynat"
-
-msgid "skip some commits for checkout"
-msgstr "çıkış için birkaç işlemeyi atla"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr "ikili arama çalıştırılamadı: 'git bisect %s', %d hata koduyla çıktı"
 
-msgid "visualize the bisection"
-msgstr "ikili aramayı görselleştir"
-
-msgid "use <cmd>... to automatically bisect"
-msgstr "kendiliğinden ikili aramak için <komut>... kullan"
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "'%s', bir argüman veya işleme gerektirmiyor"
 
-msgid "no log for BISECT_WRITE"
-msgstr "BISECT_WRITE için günlük yok"
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "'%s', 0 veya 1 argümanı gerektiriyor"
 
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset bir argüman veya işleme gerektirmiyor"
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "'%s', 0 argüman gerektiriyor"
 
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms 0 veya 1 argüman gerektiriyor"
+msgid "no logfile given"
+msgstr "hiçbir günlük dosyası verilmedi"
 
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next 0 argüman gerektiriyor"
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "'%s' başarısız: Komut verilmedi."
 
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log 0 argüman gerektiriyor"
+msgid "need a command"
+msgstr "bir komut gerekli"
 
-msgid "no logfile given"
-msgstr "hiçbir günlük dosyası verilmedi"
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "bilinmeyen komut: '%s'"
 
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<seçenekler>] [<rev-sçnk>] [<rev>] [--] <dosya>"
 
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<seçenekler>] [<rev-sçnk>] [<rev>] [--] <dosya>"
+
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<rev-sçnk>, git-rev-list(1) içinde belgelendirilmiştir"
 
@@ -2774,7 +2772,7 @@ msgstr[0] "%s dosyasında yalnızca %lu satır var"
 msgstr[1] "%s dosyasında yalnızca %lu satır var"
 
 msgid "Blaming lines"
-msgstr "Genel bakış satırları"
+msgstr "Satırlara bakılıyor"
 
 msgid "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
 msgstr "git branch [<seçenekler>] [-r | -a] [--merged] [--no-merged]"
@@ -2807,7 +2805,7 @@ msgstr "git branch [<seçenekler>] [-r | -a] [--format]"
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "'%s' dalı siliniyor: Bu dal '%s'\n"
 "         dalına birleştirilmiş; ancak HEAD'e henüz birleştirilmemiş."
@@ -2815,43 +2813,49 @@ msgstr ""
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
 "'%s' dalı silinmiyor: Bu dal HEAD'e birleştirilmiş olmasına rağmen\n"
 "         '%s' dalına birleştirilmemiş."
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
+msgid "couldn't look up commit object for '%s'"
 msgstr "'%s' için işleme nesnesi aranamadı"
 
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
 "'%s' dalı tümüyle birleştirilmemiş.\n"
 "Eğer silmek istediğinizden eminseniz 'git branch -D %s' çalıştırın."
 
-msgid "Update of config-file failed"
-msgstr "config-file güncellemesi başarısız"
+msgid "update of config-file failed"
+msgstr "config-file güncellenemedi"
 
 msgid "cannot use -a with -d"
 msgstr "-a, -d ile kullanılamıyor"
 
-msgid "Couldn't look up commit object for HEAD"
-msgstr "HEAD için işleme nesnesi aranamadı"
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"'%s' konumundaki çalışma ağacı tarafından kullanılan '%s' dalı silinemiyor"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "'%s' dalı silinemiyor, şurada çıkış yapılmış: '%s'"
+msgid "remote-tracking branch '%s' not found"
+msgstr "uzak izleme dalı '%s' bulunamadı"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "Uzak izleme dalı '%s' bulunamadı."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"'%s' dalı bulunamadı.\n"
+"--remote yazmayı mı unuttunuz?"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "'%s' dalı bulunamadı."
+msgid "branch '%s' not found"
+msgstr "'%s' dalı bulunamadı"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2872,46 +2876,52 @@ msgid "HEAD (%s) points outside of refs/heads/"
 msgstr "HEAD (%s), refs/heads/ dışına işaret ediyor"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
+msgid "branch %s is being rebased at %s"
 msgstr "%s dalı %s konumunda yeniden temellendiriliyor"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
+msgid "branch %s is being bisected at %s"
 msgstr "%s dalı %s konumunda ikili aranıyor"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "Bir dalın üzerinde değilken geçerli dal kopyalanamaz."
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "%s çalışma ağacının HEAD'i güncellenmemiş"
+
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "geçersiz dal adı: '%s'"
 
-msgid "cannot rename the current branch while not on any."
-msgstr "Bir dalın üzerinde değilken geçerli dal yeniden adlandırılamaz."
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "'%s' dalında henüz bir işleme yok"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Geçersiz dal adı: '%s'"
+msgid "no branch named '%s'"
+msgstr "'%s' adında bir dal yok"
 
-msgid "Branch rename failed"
-msgstr "Dal yeniden adlandırması başarısız"
+msgid "branch rename failed"
+msgstr "dal yeniden adlandırılamadı"
 
-msgid "Branch copy failed"
-msgstr "Dal kopyalaması başarısız"
+msgid "branch copy failed"
+msgstr "dal kopyalanamadı"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Yanlış adlandırılan '%s' dalının bir kopyası oluşturuldu"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "yanlış adlandırılan '%s' dalının bir kopyası oluşturuldu"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Yanlış adlandırılan '%s' dalı yeniden adlandırıldı"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "yanlış adlandırılan '%s' dalı yeniden adlandırıldı"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Dal %s olarak yeniden adlandırıldı; ancak HEAD güncellenmedi!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "dal %s olarak yeniden adlandırıldı; ancak HEAD güncellenmedi!"
 
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "Dal yeniden adlandırıldı; ancak config-file güncellemesi başarısız"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "dal yeniden adlandırıldı; ancak config-file güncellenemedi"
 
-msgid "Branch is copied, but update of config-file failed"
-msgstr "Dal kopyalandı; ancak config-file güncellemesi başarısız"
+msgid "branch is copied, but update of config-file failed"
+msgstr "dal kopyalandı; ancak config-file güncellenemedi"
 
 #, c-format
 msgid ""
@@ -2977,6 +2987,9 @@ msgstr "bir dalı ve onun başvuru günlüğünü taşı/yeniden adlandır"
 msgid "move/rename a branch, even if target exists"
 msgstr "bir dalı taşı/yeniden adlandır, hedef var olsa bile"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "boş biçimli başvurulardan sonra bir yenisatır çıktılama"
+
 msgid "copy a branch and its reflog"
 msgstr "bir dalı ve onun başvuru günlüğünü kopyala"
 
@@ -3022,8 +3035,8 @@ msgstr "altmodüller içinden özyinele"
 msgid "format to use for the output"
 msgstr "çıktı için kullanılacak biçim"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "HEAD geçerli bir başvuru olarak çözülemedi."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "HEAD geçerli bir başvuru olarak çözülemedi"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD, refs/heads altında bulunamadı!"
@@ -3041,35 +3054,33 @@ msgstr "--recurse-submodules, yalnızca dal oluşturmada kullanılabilir"
 msgid "branch name required"
 msgstr "dal adı gerekli"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "Ayrılmış HEAD'e açıklama verilemiyor"
+msgid "cannot give description to detached HEAD"
+msgstr "ayrık HEAD'e açıklama verilemiyor"
 
 msgid "cannot edit description of more than one branch"
 msgstr "birden çok dalın açıklaması düzenlenemiyor"
 
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "'%s' dalında henüz bir işleme yok."
+msgid "cannot copy the current branch while not on any"
+msgstr "bir dalın üzerinde değilken geçerli dal kopyalanamaz"
 
-#, c-format
-msgid "No branch named '%s'."
-msgstr "'%s' adında bir dal yok."
+msgid "cannot rename the current branch while not on any"
+msgstr "bir dalın üzerinde değilken geçerli dal yeniden adlandırılamaz"
 
 msgid "too many branches for a copy operation"
-msgstr "bir kopyalama işlemi için çok fazla dal"
+msgstr "bir kopyalama işlemi için pek fazla dal"
 
 msgid "too many arguments for a rename operation"
-msgstr "bir yeniden adlandırma işlemi için çok fazla argüman"
+msgstr "bir yeniden adlandırma işlemi için pek fazla argüman"
 
 msgid "too many arguments to set new upstream"
-msgstr "yeni üstkaynak ayarlamak için çok fazla argüman"
+msgstr "yeni üstkaynak ayarlamak için pek fazla argüman"
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
 "HEAD'in üst kaynağı %s olarak ayarlanamadı; çünkü herhangi bir dala işaret "
-"etmiyor."
+"etmiyor"
 
 #, c-format
 msgid "no such branch '%s'"
@@ -3080,18 +3091,18 @@ msgid "branch '%s' does not exist"
 msgstr "'%s' diye bir dal yok"
 
 msgid "too many arguments to unset upstream"
-msgstr "üst kaynağı kaldırmak için çok fazla argüman"
+msgstr "üst kaynağı kaldırmak için pek fazla argüman"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr ""
-"HEAD'in üst kaynağı kaldırılamadı; çünkü herhangi bir dala işaret etmiyor."
+"HEAD'in üst kaynağı kaldırılamadı; çünkü herhangi bir dala işaret etmiyor"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
+msgid "branch '%s' has no upstream information"
 msgstr "'%s' dalının üstkaynak bilgisi yok"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
 "'git branch'in -a ve -r seçenekleri bir dal adı almaz.\n"
@@ -3099,10 +3110,10 @@ msgstr ""
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "--set-upstream seçeneği artık desteklenmiyor. Lütfen --track veya --set-"
-"upstream-to kullanın."
+"upstream-to kullanın"
 
 msgid "git version:\n"
 msgstr "git sürümü:\n"
@@ -3121,11 +3132,11 @@ msgid "not run from a git repository - no hooks to show\n"
 msgstr "bir git deposundan çalıştırılmadı - gösterilecek kanca yok\n"
 
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory <dosya>] [-s|--suffix <biçim>] [--"
-"diagnose[=<kip>]"
+"git bugreport [(-o | --output-directory) <yol>] [(-s | --suffix) <biçim>]\n"
+"              [--diagnose[=<kip>]]"
 
 msgid ""
 "Thank you for filling out a Git bug report!\n"
@@ -3174,6 +3185,10 @@ msgstr "hata raporu dosyaları için bir hedef konum belirtin"
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "dosya adları için bir strftime biçim soneki belirtin"
 
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "bilinmeyen argüman '%s'"
+
 #, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "'%s' için öncü dizinler oluşturulamadı"
@@ -3196,17 +3211,24 @@ msgstr "%s dosyasına yazılamıyor"
 msgid "Created new report at '%s'.\n"
 msgstr "Hata raporu '%s' dosyasına yazıldı.\n"
 
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [<seçenekler>] <dosya> <git-rev-liste argümanlar>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<sürüm>] <dosya> <git-rev-listesi-argümanları>"
 
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [<seçenekler>] <dosya>"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <dosya>"
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads <dosya> [<başvuru-adı>...]"
 
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle <dosya> [<başvuru-adı>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <dosya> [<başvuru-adı>...]"
+
+msgid "need a <file> argument"
+msgstr "bir <dosya> argümanı gerekiyor"
 
 msgid "do not show progress meter"
 msgstr "ilerleme çubuğunu gösterme"
@@ -3214,11 +3236,11 @@ msgstr "ilerleme çubuğunu gösterme"
 msgid "show progress meter"
 msgstr "ilerleme çubuğunu göster"
 
-msgid "show progress meter during object writing phase"
-msgstr "ilerleme çubuğunu nesne yazımı aşaması sırasında göster"
+msgid "historical; same as --progress"
+msgstr "eski seçenek; --progress ile aynı"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "ilerleme çubuğu gösterildiğinde --all-progress'e benzer"
+msgid "historical; does nothing"
+msgstr "eski seçenek; bir şey yapmaz"
 
 msgid "specify bundle format version"
 msgstr "demet biçim sürümünü belirt"
@@ -3261,10 +3283,6 @@ msgstr "%s, argümanlar gerektiriyor"
 msgid "%s takes no arguments"
 msgstr "%s, bir argüman almıyor"
 
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "bilinmeyen komut: '%s'"
-
 msgid "only one batch option may be specified"
 msgstr "yalnızca bir toplu iş seçeneği belirtilebilir"
 
@@ -3277,23 +3295,23 @@ msgstr "git cat-file (-e | -p) <nesne>"
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <nesne>"
 
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<başvuru>:<yol|ağacımsı> | --path=<yol|ağacımsı> <revizyon>]"
+
 msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
-msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<başvuru>:<yol|ağacımsı> | --path=<yol|ağacımsı> <revizyon>]"
+"             [--textconv | --filters] [-Z]"
 
 msgid "Check object existence or emit object contents"
 msgstr "Nesne varlığını denetle veya nesne içeriğini yay"
@@ -3331,6 +3349,9 @@ msgstr "--batch gibi; ancak <içerik> yayma"
 msgid "stdin is NUL-terminated"
 msgstr "stdin, NUL ile sonlandırılmış"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin ve stdout NUL ile sonlandırılmış"
+
 msgid "read commands from stdin"
 msgstr "komutları stdin'den oku"
 
@@ -3396,18 +3417,22 @@ msgstr "<revizyon>, '%s' ile gerekiyor"
 msgid "<object> required with '-%c'"
 msgstr "<nesne>, '-%c' ile gerekiyor"
 
-msgid "too many arguments"
-msgstr "çok fazla argüman"
-
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr "<tür> <nesne> kipinde yalnızca iki argümana izin veriliyor, %d değil"
 
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <öznitelik>...] [--] <yol-adı>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <ağacımsı>] [-a | --all | <öznitelik>...] [--] <yol-"
+"adı>..."
 
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <öznitelik>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <ağacımsı>] [-a | --all | "
+"<öznitelik>...]"
 
 msgid "report all attributes set on file"
 msgstr "tüm dosya özniteliklerini bildir"
@@ -3421,6 +3446,12 @@ msgstr "dosya adlarını stdin'den oku"
 msgid "terminate input and output records by a NUL character"
 msgstr "girdi ve çıktı kayıtlarını bir NUL karakteri ile sonlandır"
 
+msgid "<tree-ish>"
+msgstr "<ağacımsı>"
+
+msgid "which tree-ish to check attributes at"
+msgstr "özniteliklerin hangi ağacımsıda denetleneceği"
+
 msgid "suppress progress reporting"
 msgstr "ilerleme bildirimini gizle"
 
@@ -3577,6 +3608,10 @@ msgstr "'%s', '%s' ögesinin belirtilmediği durumlarda kullanılmalıdır"
 msgid "'%s' or '%s' cannot be used with %s"
 msgstr "'%s' veya '%s', %s ile birlikte kullanılamaz"
 
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "'%s', '%s' veya '%s' bir ağaçtan çıkış yaparken kullanılamaz"
+
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "'%s' yolu birleştirilmemiş"
@@ -3929,9 +3964,11 @@ msgid "use overlay mode"
 msgstr "yerpaylaşım kipini kullan"
 
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <dizgi>] [-x | -X] [--] <yollar>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <dizgi>] [-x | -X] [--] [<yol-"
+"blrtç>...]"
 
 #, c-format
 msgid "Removing %s\n"
@@ -3995,7 +4032,7 @@ msgstr ""
 "*          - tüm ögeleri seç\n"
 "           - (boş) seçimi bitir\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Pardon (%s)?\n"
 
@@ -4144,9 +4181,6 @@ msgstr "derinlik"
 msgid "create a shallow clone of that depth"
 msgstr "verilen derinlikte sığ bir depo oluştur"
 
-msgid "time"
-msgstr "zaman"
-
 msgid "create a shallow clone since a specific time"
 msgstr "verilen zamandan sonrasını içeren bir sığ depo oluştur"
 
@@ -4183,12 +4217,6 @@ msgstr "sunucuya özel"
 msgid "option to transmit"
 msgstr "iletme seçeneği"
 
-msgid "use IPv4 addresses only"
-msgstr "yalnızca IPv4 adresleri kullan"
-
-msgid "use IPv6 addresses only"
-msgstr "yalnızca IPv6 adresleri kullan"
-
 msgid "apply partial clone filters to submodules"
 msgstr "altmodüllere kısımsal klon süzgeçlerini uygula"
 
@@ -4218,10 +4246,18 @@ msgstr "'%s' dosyasının bilgileri alınamadı"
 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ğlantı; --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ğlantısı var, --local ile klonlama reddediliyor"
+
 #, c-format
 msgid "failed to unlink '%s'"
 msgstr "'%s' bağlantısı kesilemedi"
@@ -4286,10 +4322,6 @@ msgstr "Çok fazla argüman."
 msgid "You must specify a repository to clone."
 msgstr "Klonlamak için bir depo belirtmelisiniz."
 
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "'%s' ve '%s %s' seçenekleri birlikte kullanılamaz"
-
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
 "exclude"
@@ -4374,6 +4406,9 @@ msgstr "depo ilklendirilemedi, demet URI'si atlanıyor"
 msgid "failed to fetch objects from bundle URI '%s'"
 msgstr "'%s' demet URI'sinden nesneler getirilemedi"
 
+msgid "failed to fetch advertised bundles"
+msgstr "tanıtılan demetler alınamadı"
+
 msgid "remote transport reported error"
 msgstr "uzak konum taşıması hata bildirdi"
 
@@ -4409,18 +4444,24 @@ msgid "--command must be the first argument"
 msgstr "--command ilk argüman olmalı"
 
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir <nsndzn>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dizin>] [--shallow] [--[no-]progress]"
 
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 msgstr ""
-"git commit-graph write [--object-dir <nsndzn>] [--append] [--"
-"split[=<strateji>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <bölme-seçenekleri>"
+"git commit-graph write [--object-dir <dizin>] [--append]\n"
+"                       [--split[=<<strateji>]] [--reachable | --stdin-packs "
+"| --stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <bölme-seçenekleri>"
 
 msgid "dir"
 msgstr "dizin"
@@ -4435,6 +4476,10 @@ msgstr "commit-graph parçalara bölünmüşse yalnızca uç dosyayı doğrula"
 msgid "Could not open commit-graph '%s'"
 msgstr "commit-graph '%s' açılamadı"
 
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "commit-graph zinciri '%s' açılamadı"
+
 #, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "tanımlanamayan --split argümanı, %s"
@@ -4491,12 +4536,16 @@ msgstr ""
 msgid "Collecting commits from input"
 msgstr "Girdiden işlemeler toplanıyor"
 
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <ağaç> [(-p <üst-öge>)...]"
+
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p <üst-öge>)...] [-S[<değer-no>]] [(-m <ileti>)...] [(-F "
-"<dosya>)...] <ağaç>"
+"git commit-tree [(-p <üst-öge>)...] [-S[<anahtar-kimliği>]] [(-m "
+"<ileti>)...]\n"
+"                [(-F <dosya>)...] <ağaç>"
 
 #, c-format
 msgid "duplicate parent %s ignored"
@@ -4538,11 +4587,29 @@ msgstr "bir tam ağaç vermeli"
 msgid "git commit-tree: failed to read"
 msgstr "git commit-tree: okunamadı"
 
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<seçenekler>] [--] <yol-blrtç>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<kip>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <işleme> | --fixup [(amend|"
+"reword):]<işleme>)]\n"
+"           [-F <dosya> | -m <ileti>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\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"
+"           [--] [<yol-blrtç>...]"
 
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<seçenekler>] [--] <yol-blrtç>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<seçenekler>] [--] [<yol-blrtç>...]"
 
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -4593,6 +4660,9 @@ msgstr ""
 "\tgit cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "dosyaları güncelleme başarısız"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "HEAD ağaç nesnesi açılamadı"
 
@@ -4611,9 +4681,6 @@ msgstr "geçici indeks güncellenemiyor"
 msgid "Failed to update main cache tree"
 msgstr "Ana önbellek ağacı güncellenemedi"
 
-msgid "unable to write new_index file"
-msgstr "new_index dosyası yazılamıyor"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "Bir birleştirme sırasında kısmi işleme yapılamaz."
 
@@ -4652,8 +4719,8 @@ msgstr ""
 "karakteri seçilemiyor"
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "%s işlemesi aranamadı"
+msgid "could not lookup commit '%s'"
+msgstr "'%s' işlemesi aranamadı"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -5019,10 +5086,10 @@ msgstr "İşleme iletisi gövdesinin boş bırakılmasından ötürü iptal edil
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"Depo güncellendi; ancak new_index dosyası yazılamıyor.\n"
+"Depo güncellendi; ancak yeni indeks dosyası yazılamıyor.\n"
 "Diskin dolu olup olmadığını ve kotanızı aşıp aşmadığınızı denetleyin,\n"
 "sonra kurtarmak için \"git restore --staged :/\" kullanın."
 
@@ -5298,7 +5365,7 @@ msgid ""
 "\n"
 "\tchmod 0700 %s"
 msgstr ""
-"Soket dizininizdeki izinler çok gevşek; diğer kullanıcılar sizin\n"
+"Yuva dizininizdeki izinler çok gevşek; diğer kullanıcılar sizin\n"
 "önbelleğe alınmış yetkilerinizi okuyabilirler. Şunu çalıştırmayı düşünün:\n"
 "\n"
 "\tchmod 0700 %s"
@@ -5307,20 +5374,27 @@ msgid "print debugging messages to stderr"
 msgstr "hata ayıklama iletilerini stderr'e yazdır"
 
 msgid "credential-cache--daemon unavailable; no unix socket support"
-msgstr "credential-cache--daemon kullanılamıyor; unix soket desteği yok"
+msgstr "credential-cache--daemon kullanılamıyor; unix yuva desteği yok"
 
 msgid "credential-cache unavailable; no unix socket support"
-msgstr "credential-cache kullanılamıyor; unix soket desteği yok"
+msgstr "credential-cache kullanılamıyor; unix yuva desteği yok"
 
 #, c-format
 msgid "unable to get credential storage lock in %d ms"
 msgstr "kimlik depo kilidi %d ms içinde alınamadı"
 
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<seçenekler>] [<işlememsi>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<işlememsi>...]"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<im>]"
 
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<seçenekler>] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe <ikili>"
 
 msgid "head"
 msgstr "dal ucu"
@@ -5442,11 +5516,11 @@ msgid "option '%s' and commit-ishes cannot be used together"
 msgstr "'%s' seçeneği ve işlememsiler birlikte kullanılamaz"
 
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory <yol>] [-s|--suffix <biçim>] [--"
-"mode=<kip>]"
+"git diagnose [(-o | --output-directory) <yol>] [(-s | --suffix) <biçim>]\n"
+"             [--mode=<kip>]"
 
 msgid "specify a destination for the diagnostics archive"
 msgstr "tanı arşivi için bir hedef konum belirtin"
@@ -5464,6 +5538,9 @@ msgstr "--merge-base yalnızca iki işleme ile kullanılabilir"
 msgid "'%s': not a regular file or symlink"
 msgstr "'%s': Sıradan bir dosya veya sembolik bağ değil"
 
+msgid "no merge given, only parents."
+msgstr "birleştirme verilmedi, yalnızca üst ögeler."
+
 #, c-format
 msgid "invalid option: %s"
 msgstr "geçersiz seçenek: %s"
@@ -5579,29 +5656,6 @@ msgstr "--tool=<araç> için bir <araç> verilmedi"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "--extcmd=<komut> için bir <komut> verilmedi"
 
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <seçenekler> <ortam-dğşkn>"
-
-msgid "default for git_env_*(...) to fall back on"
-msgstr "git_env_*(...)'ın geri çekileceği öntanımlı"
-
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr "sessiz ol, yalnızca git_env_*() değerini çıkış kodu olarak kullan"
-
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr ""
-"--default seçeneği, --type=bool ile birlikte bir Boole değeri bekliyor, '%s' "
-"değil"
-
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not `"
-"%s`"
-msgstr ""
-"--default seçeneği, --type=ulong ile birlikte bir imzalanmamış uzun değer "
-"bekliyor, '%s' değil"
-
 msgid "git fast-export [<rev-list-opts>]"
 msgstr "git fast-export [<revizyon-listesi-seçenekleri>]"
 
@@ -5713,124 +5767,19 @@ msgstr "git fetch --all [<seçenekler>]"
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel negatif olamaz"
 
-msgid "fetch from all remotes"
-msgstr "tüm uzak konumlardan getir"
+msgid "couldn't find remote ref HEAD"
+msgstr "uzak HEAD başvurusu bulunamadı"
 
-msgid "set upstream for git pull/fetch"
-msgstr "git pull/fetch için üstkaynak ayarla"
+#, c-format
+msgid "From %.*s\n"
+msgstr "Şu konumdan: %.*s\n"
 
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr ".git/FETCH_HEAD'in üzerine yazmak yerine ona iliştir"
+#, c-format
+msgid "object %s not found"
+msgstr "%s nesnesi bulunamadı"
 
-msgid "use atomic transaction to update references"
-msgstr "başvuruları güncellemek için atomsal işlem kullan"
-
-msgid "path to upload pack on remote end"
-msgstr "uzak uçtaki yükleme paketine olan yol"
-
-msgid "force overwrite of local reference"
-msgstr "yerel başvurunun üzerine zorla yaz"
-
-msgid "fetch from multiple remotes"
-msgstr "birden çok uzak konumdan getir"
-
-msgid "fetch all tags and associated objects"
-msgstr "tüm etiketleri ve ilişkilendirilen nesneleri getir"
-
-msgid "do not fetch all tags (--no-tags)"
-msgstr "tüm etiketleri getirme (--no-tags)"
-
-msgid "number of submodules fetched in parallel"
-msgstr "paralelde getirilen altmodüllerin sayısı"
-
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr ""
-"başvuru belirtecini tüm başvuruları refs/prefetch/'e yerleştirecek biçimde "
-"değiştir"
-
-msgid "prune remote-tracking branches no longer on remote"
-msgstr "artık uzak konumda olmayan uzak izleme dallarını buda"
-
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr ""
-"artık uzak konumda olmayan yerel etiketleri buda ve değiştirilen etiketleri "
-"güncelle"
-
-msgid "on-demand"
-msgstr "istek üzerine"
-
-msgid "control recursive fetching of submodules"
-msgstr "altmodüllerin özyineli getirilmesini denetle"
-
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "getirilen başvuruları FETCH_HEAD dosyasına yaz"
-
-msgid "keep downloaded pack"
-msgstr "indirilen paketi tut"
-
-msgid "allow updating of HEAD ref"
-msgstr "HEAD başvurusunun güncellenmesine izin ver"
-
-msgid "deepen history of shallow clone"
-msgstr "sığ klonun geçmişini derinleştir"
-
-msgid "deepen history of shallow repository based on time"
-msgstr "zamana bağlı olarak sığ deponun geçmişini derinleştir"
-
-msgid "convert to a complete repository"
-msgstr "tam bir depoya dönüştür"
-
-msgid "re-fetch without negotiating common commits"
-msgstr "ortak işlemeleri pazarlık etmeden yeniden getir"
-
-msgid "prepend this to submodule path output"
-msgstr "bunu altmodül yol çıktısının başına ekle"
-
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr ""
-"altmodüllerin özyineli getirilmesi için öntanımlı (yapılandırma "
-"dosyalarından daha az önceliğe iye)"
-
-msgid "accept refs that update .git/shallow"
-msgstr ".git/shallow'u güncelleyen başvuruları kabul et"
-
-msgid "refmap"
-msgstr "ilgili başvuru"
-
-msgid "specify fetch refmap"
-msgstr "getirme ile ilgili başvuruları belirt"
-
-msgid "report that we have only objects reachable from this object"
-msgstr "yalnızca bu nesneden ulaşılabilir nesnelerimiz olduğunu bildir"
-
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr ""
-"bir paket dosyasını getirme; bunun yerine pazarlık ipuçlarının atalarını "
-"yazdır"
-
-msgid "run 'maintenance --auto' after fetching"
-msgstr "getirme sonrasında 'maintenance --auto' çalıştır"
-
-msgid "check for forced-updates on all updated branches"
-msgstr "tüm güncellenmiş dalları zorlanmış güncellemeler için denetle"
-
-msgid "write the commit-graph after fetching"
-msgstr "getirdikten sonra işleme grafiğini yaz"
-
-msgid "accept refspecs from stdin"
-msgstr "başvuru belirteçlerini stdin'den oku"
-
-msgid "couldn't find remote ref HEAD"
-msgstr "uzak HEAD başvurusu bulunamadı"
-
-#, c-format
-msgid "object %s not found"
-msgstr "%s nesnesi bulunamadı"
-
-msgid "[up to date]"
-msgstr "[güncel]"
+msgid "[up to date]"
+msgstr "[güncel]"
 
 msgid "[rejected]"
 msgstr "[reddedildi]"
@@ -5895,10 +5844,6 @@ msgstr "%s tüm gerekli nesneleri göndermedi\n"
 msgid "rejected %s because shallow roots are not allowed to be updated"
 msgstr "%s reddedildi; çünkü sığ köklerin güncellenmesine izin verilmiyor"
 
-#, c-format
-msgid "From %.*s\n"
-msgstr "Şu konumdan: %.*s\n"
-
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -5990,6 +5935,115 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr "bir etiket adı belirtmeniz gerekiyor"
 
+msgid "fetch from all remotes"
+msgstr "tüm uzak konumlardan getir"
+
+msgid "set upstream for git pull/fetch"
+msgstr "git pull/fetch için üstkaynak ayarla"
+
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr ".git/FETCH_HEAD'in üzerine yazmak yerine ona iliştir"
+
+msgid "use atomic transaction to update references"
+msgstr "başvuruları güncellemek için atomsal işlem kullan"
+
+msgid "path to upload pack on remote end"
+msgstr "uzak uçtaki yükleme paketine olan yol"
+
+msgid "force overwrite of local reference"
+msgstr "yerel başvurunun üzerine zorla yaz"
+
+msgid "fetch from multiple remotes"
+msgstr "birden çok uzak konumdan getir"
+
+msgid "fetch all tags and associated objects"
+msgstr "tüm etiketleri ve ilişkilendirilen nesneleri getir"
+
+msgid "do not fetch all tags (--no-tags)"
+msgstr "tüm etiketleri getirme (--no-tags)"
+
+msgid "number of submodules fetched in parallel"
+msgstr "paralelde getirilen altmodüllerin sayısı"
+
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"başvuru belirtecini tüm başvuruları refs/prefetch/'e yerleştirecek biçimde "
+"değiştir"
+
+msgid "prune remote-tracking branches no longer on remote"
+msgstr "artık uzak konumda olmayan uzak izleme dallarını buda"
+
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr ""
+"artık uzak konumda olmayan yerel etiketleri buda ve değiştirilen etiketleri "
+"güncelle"
+
+msgid "on-demand"
+msgstr "istek üzerine"
+
+msgid "control recursive fetching of submodules"
+msgstr "altmodüllerin özyineli getirilmesini denetle"
+
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "getirilen başvuruları FETCH_HEAD dosyasına yaz"
+
+msgid "keep downloaded pack"
+msgstr "indirilen paketi tut"
+
+msgid "allow updating of HEAD ref"
+msgstr "HEAD başvurusunun güncellenmesine izin ver"
+
+msgid "deepen history of shallow clone"
+msgstr "sığ klonun geçmişini derinleştir"
+
+msgid "deepen history of shallow repository based on time"
+msgstr "zamana bağlı olarak sığ deponun geçmişini derinleştir"
+
+msgid "convert to a complete repository"
+msgstr "tam bir depoya dönüştür"
+
+msgid "re-fetch without negotiating common commits"
+msgstr "ortak işlemeleri pazarlık etmeden yeniden getir"
+
+msgid "prepend this to submodule path output"
+msgstr "bunu altmodül yol çıktısının başına ekle"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"altmodüllerin özyineli getirilmesi için öntanımlı (yapılandırma "
+"dosyalarından daha az önceliğe iye)"
+
+msgid "accept refs that update .git/shallow"
+msgstr ".git/shallow'u güncelleyen başvuruları kabul et"
+
+msgid "refmap"
+msgstr "ilgili başvuru"
+
+msgid "specify fetch refmap"
+msgstr "getirme ile ilgili başvuruları belirt"
+
+msgid "report that we have only objects reachable from this object"
+msgstr "yalnızca bu nesneden ulaşılabilir nesnelerimiz olduğunu bildir"
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+"bir paket dosyasını getirme; bunun yerine pazarlık ipuçlarının atalarını "
+"yazdır"
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr "getirme sonrasında 'maintenance --auto' çalıştır"
+
+msgid "check for forced-updates on all updated branches"
+msgstr "tüm güncellenmiş dalları zorlanmış güncellemeler için denetle"
+
+msgid "write the commit-graph after fetching"
+msgstr "getirdikten sonra işleme grafiğini yaz"
+
+msgid "accept refspecs from stdin"
+msgstr "başvuru belirteçlerini stdin'den oku"
+
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only'e bir veya daha çok --negotiation-tip=* gerekiyor"
 
@@ -5999,6 +6053,10 @@ msgstr "--deepen içinde negatif derinlik desteklenmiyor"
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "tam bir depo üzerinde --unshallow bir anlam ifade etmiyor"
 
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "'%s' konumundan demetler getirilemedi"
+
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all bir depo argümanı almıyor"
 
@@ -6101,8 +6159,14 @@ msgstr "yalnızca işlemeyi içeren başvuruları yazdır"
 msgid "print only refs which don't contain the commit"
 msgstr "yalnızca işlemeyi içermeyen başvuruları yazdır"
 
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=<yapılandırma> <komut-argümanları>"
+msgid "read reference patterns from stdin"
+msgstr "başvuru dizgilerini stdin'den oku"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "--stdin ile bilinmeyen argümanlar verilmiş"
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<yapılandırma> [--] <argümanlar>"
 
 msgid "config"
 msgstr "yapılandırma"
@@ -6113,6 +6177,10 @@ msgstr "bir depo yolları listesi tutan yapılandırma anahtarı"
 msgid "missing --config=<config>"
 msgstr "--config=<yapılandırma> eksik"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "hatayı yapılandırma alındı, --config=%s"
+
 msgid "unknown"
 msgstr "bilinmeyen"
 
@@ -6259,22 +6327,39 @@ msgstr "%s: ayrık HEAD bir şeye işaret etmiyor"
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "Uyarı: %s henüz doğmamış bir dala işaret ediyor (%s)"
 
-msgid "Checking cache tree"
-msgstr "Önbellek ağacı denetleniyor"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "%s ögesinin önbellek ağacı denetleniyor"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: cache-tree içinde geçersiz sha1 işaretçisi"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: %s ögesinin cache-tree'si içinde geçersiz sha1 işaretçisi"
 
 msgid "non-tree in cache-tree"
 msgstr "cache-tree içinde ağaç olmayan öge"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: resolve-undo içinde geçersiz sha1 işaretçisi"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: %s ögesinin resolve-undo'su içinde geçersiz sha1 işaretçisi"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "'%s' paketi için rev-index yüklenemiyor"
 
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<seçenekler>] [<nesne>...]"
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "'%s' paketi için geçersiz rev-index"
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<nesne>...]"
 
 msgid "show unreachable objects"
 msgstr "ulaşılamayan nesneleri göster"
@@ -6329,12 +6414,6 @@ msgstr "git fsmonitor--daemon start [<seçenekler>]"
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [<seçenekler>]"
 
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
-
 #, c-format
 msgid "value of '%s' out of range: %d"
 msgstr "'%s' değeri erim dışında: %d"
@@ -6455,6 +6534,9 @@ msgstr "başvurulmayan nesneleri buda"
 msgid "pack unreferenced objects separately"
 msgstr "başvurulmamış nesneleri ayrı olarak paketle"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "--cruft ile yeni süprüntü paketlerinin boyutunu sınırla"
+
 msgid "be more thorough (increased runtime)"
 msgstr "biraz daha titiz ol (artırılmış işleyiş süresi)"
 
@@ -6574,8 +6656,20 @@ msgstr "belirli bir görevi çalıştır"
 msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr "tek kezde --auto ve --schedule=<sıklık>'tan birini kullan"
 
-msgid "failed to run 'git config'"
-msgstr "'git config' çalıştırılamadı"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "şunun için '%s' değeri eklenemiyor: '%s'"
+
+msgid "return success even if repository was not registered"
+msgstr "depo kaydı yapılmamış olsa bile başarılı durum döndür"
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "şunun '%s' değeri ayarı kaldırılamıyor: '%s'"
+
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "'%s' deposu kaydı yapılmamış"
 
 #, c-format
 msgid "failed to expand path '%s'"
@@ -6613,12 +6707,6 @@ msgstr "'crontab' çalıştırılamadı; sisteminiz 'cron' desteklemiyor olabili
 msgid "'crontab' died"
 msgstr "'crontab' beklenmedik bir biçimde sonlandı"
 
-msgid "failed to start systemctl"
-msgstr "systemctl başlatılamadı"
-
-msgid "failed to run systemctl"
-msgstr "systemctl çalıştırılamadı"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "'%s' silinemedi"
@@ -6627,6 +6715,12 @@ msgstr "'%s' silinemedi"
 msgid "failed to flush '%s'"
 msgstr "'%s' floş yapılamadı"
 
+msgid "failed to start systemctl"
+msgstr "systemctl başlatılamadı"
+
+msgid "failed to run systemctl"
+msgstr "systemctl çalıştırılamadı"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "tanımlanamayan --scheduler argümanı, '%s'"
@@ -6650,6 +6744,9 @@ msgstr "görev planlayıcı"
 msgid "scheduler to trigger git maintenance run"
 msgstr "git bakımını tetikleyecek görev planlayıcı"
 
+msgid "failed to set up maintenance schedule"
+msgstr "bakım programı ayarlanamadı"
+
 msgid "failed to add repo to global config"
 msgstr "depo, global yapılandırmaya eklenemedi"
 
@@ -6724,8 +6821,8 @@ msgstr "ikili dosyaları textconv süzgeçleri ile işle"
 msgid "search in subdirectories (default)"
 msgstr "altdizinlerde ara (öntanımlı)"
 
-msgid "descend at most <depth> levels"
-msgstr "en çok <derinlik> düzey in"
+msgid "descend at most <n> levels"
+msgstr "en çok <n> düzey aşağı in"
 
 msgid "use extended POSIX regular expressions"
 msgstr "genişletilmiş POSIX düzenli ifadelerini kullan"
@@ -6863,11 +6960,14 @@ msgid "both --cached and trees are given"
 msgstr "hem --cached hem ağaçlar verilmiş"
 
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <tür>] [-w] [--path=<dosya> | --no-filters] [--stdin] "
-"[--] <dosya>..."
+"git hash-object [-t <tür>] [-w] [--path=<dosya> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <dosya>..."
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <tür>] [-w] --stdin-paths [--no-filters]"
 
 msgid "object type"
 msgstr "nesne türü"
@@ -7001,12 +7101,19 @@ msgstr "kullanım: %s%s"
 msgid "'git help config' for more information"
 msgstr "ek bilgi için: 'git help config'"
 
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] <kanca-adı> [-- <kanca-argümanları>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<yol>] <kanca-adı> [-- <kanca-"
+"argümanları>]"
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr "istenen eksik <kanca-adı> sessizce yok sayılıyor"
 
+msgid "file to read into hooks' stdin"
+msgstr "kancaların stdin'ine okunacak dosya"
+
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "%s konumunda nesne türü uyuşmazlığı"
@@ -7223,83 +7330,16 @@ msgstr "--verify ile bir paket dosyası adı verilmedi"
 msgid "fsck error in pack objects"
 msgstr "paket nesnelerinde fsck hatası"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "'%s' şablonunun bilgileri alınamıyor"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "'%s' opendir yapılamıyor"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "'%s' readlink yapılamıyor"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "'%s', '%s' ögesine sembolik bağla bağlanamıyor"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "'%s' şuraya kopyalanamıyor: '%s'"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "%s şablonu yok sayılıyor"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "şablonlar %s içinde bulunamadı"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "şablonlar '%s' konumundan kopyalanmıyor: %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "geçersiz başlangıç dalı adı: '%s'"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "%d dosya türü ele alınamıyor"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "%s şuraya taşınamıyor: %s"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "depoyu başka bir sağlama ile yeniden ilklendirme deneniyor"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s halihazırda var"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: --initial-branch=%s yok sayıldı"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "%s%s içindeki var olan paylaşılan Git deposu yeniden ilklendirildi\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "%s%s içindeki var olan Git deposu yeniden ilklendirildi\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "%s%s içinde paylaşılan boş Git deposu ilklendirildi\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "%s%s içinde boş Git deposu ilklendirildi\n"
-
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<şablon-dizini>] [--"
-"shared[=<izinler>]] [<dizin>]"
+"git init [-q | --quiet] [--bare] [--template=<şablon-dizini>]\n"
+"         [--separate-git-dir <git-dizini>] [--object-format=<biçim>]\n"
+"         [-b <dal-adı> | --initial-branch=<dal-adı>]\n"
+"         [--shared[=<izinler>]] [<dizin>]"
 
 msgid "permissions"
 msgstr "izinler"
@@ -7340,11 +7380,14 @@ msgid "--separate-git-dir incompatible with bare repository"
 msgstr "--separate-git-dir, çıplak depo ile uyumsuz"
 
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<jeton>[(=|:)<değer>])...] [<dosya>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<anahtar>|<anArması>)"
+"[(=|:)<değer>])...]\n"
+"                       [--parse] [<dosya>...]"
 
 msgid "edit files in place"
 msgstr "dosyaları yerinde düzenle"
@@ -7352,6 +7395,9 @@ msgstr "dosyaları yerinde düzenle"
 msgid "trim empty trailers"
 msgstr "boş artbilgileri kırp"
 
+msgid "placement"
+msgstr "yerleştirme"
+
 msgid "where to place the new trailer"
 msgstr "yeni artbilgiler nereye yerleştirilecek"
 
@@ -7364,17 +7410,17 @@ msgstr "artbilgi eksikse yapılacak eylem"
 msgid "output only the trailers"
 msgstr "yalnızca artbilgileri çıktı ver"
 
-msgid "do not apply config rules"
-msgstr "yapılandırma kurallarını uygulama"
+msgid "do not apply trailer.* configuration variables"
+msgstr "trailer.* yapılandırma değişkenlerini uygulama"
 
-msgid "join whitespace-continued values"
-msgstr "boşluk ile sürdürülen değerleri uç uca ekle"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "çok satırlı artbilgileri tek satırlı değerler olarak biçimlendir"
 
-msgid "set parsing options"
-msgstr "ayrıştırma seçeneklerini ayarla"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "--only-trailers --only-input --unfold için arma"
 
-msgid "do not treat --- specially"
-msgstr "ayırma çizgilerine (---) özel davranma"
+msgid "do not treat \"---\" as the end of input"
+msgstr "\"---\" dizgisine satır sonu olarak davranma"
 
 msgid "trailer(s) to add"
 msgstr "eklenecek artbilgi(ler)"
@@ -7463,6 +7509,10 @@ msgstr "bir tam erim gerekiyor"
 msgid "not a range"
 msgstr "bir erim değil"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "'%s' dal açıklama dosyası okunamıyor"
+
 msgid "cover letter needs email format"
 msgstr "ön yazı için e-posta biçimi gerekli"
 
@@ -7561,6 +7611,9 @@ msgstr "açıklama kipinden kapak sayfası kipi"
 msgid "generate parts of a cover letter based on a branch's description"
 msgstr "ön yazının bazı kısımlarını dalın açıklamasından oluştur"
 
+msgid "use branch description from file"
+msgstr "dal açıklamasını dosyadan oku"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "[PATCH] yerine [<önek>] kullan"
 
@@ -7717,6 +7770,10 @@ msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "İzlenen bir uzak dal bulunamadı, lütfen el ile <üstkaynak> belirtin.\n"
 
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "'%s' hakkında nesne bilgisi alınamadı"
+
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "hatalı ls-files biçimi: '%s' ögesi, '(' ile başlamıyor"
@@ -7822,12 +7879,12 @@ msgstr ""
 
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
-"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<çlştr>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<depo> [<başvurular>...]]"
+"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<yürütülebilir>]\n"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<anahtar>]\n"
+"              [--symref] [<depo> [<dizgiler>...]]"
 
 msgid "do not print remote URL"
 msgstr "uzak konum URL'sini yazdırma"
@@ -7859,10 +7916,6 @@ msgstr "işaret ettiği nesneye ek olarak altında yatan başvuruyu göster"
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
 msgstr "git ls-tree [<seçenekler>] <ağacımsı> [<yol>...]"
 
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "'%s' hakkında nesne bilgisi alınamadı"
-
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "hatalı ls-tree biçimi: '%s' ögesi '(' ile başlamıyor"
@@ -7955,12 +8008,12 @@ msgstr "git merge-base [-a | --all] <işleme> <işleme>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <işleme>..."
 
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <işleme>..."
-
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <işleme> <işleme>"
 
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <işleme>..."
+
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <başvuru> [<işleme>]"
 
@@ -7989,6 +8042,9 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "sonuçları standart çıktıya gönder"
 
+msgid "use object IDs instead of filenames"
+msgstr "dosya adları yerine nesne kimlikleri kullan"
+
 msgid "use a diff3 based merge"
 msgstr "diff3 tabanlı birleştirme kullan"
 
@@ -8013,6 +8069,13 @@ msgstr "çakışmalar hakkında uyarma"
 msgid "set labels for file1/orig-file/file2"
 msgstr "file1/orig-file/file2 için etiketler yapıştır"
 
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "'%s' diye bir nesne yok"
+
+msgid "Could not write object file"
+msgstr "nesne dosyası yazılamadı"
+
 #, c-format
 msgid "unknown option %s"
 msgstr "bilinmeyen seçenek %s"
@@ -8068,9 +8131,36 @@ msgstr "kipi/oid'si/hazırlığı olmayan dosya adlarını listele"
 msgid "allow merging unrelated histories"
 msgstr "birbiriyle ilişkisi olmayan geçmişlerin birleştirilmesine izin ver"
 
+msgid "perform multiple merges, one per line of input"
+msgstr "girdi satırı başına bir adet çoklu birleştirmeler gerçekleştir"
+
+msgid "specify a merge-base for the merge"
+msgstr "birleştirme için bir birleştirme temeli belirtilmeli"
+
+msgid "option=value"
+msgstr "seçenek=değer"
+
+msgid "option for selected merge strategy"
+msgstr "seçili birleştirme stratejisi için seçenekler"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge, tüm diğer seçeneklerle uyumsuz"
 
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "bilinmeyen strateji seçeneği: -X%s"
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base, --stdin ile uyumsuz"
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "hatalı oluşturulmuş girdi satırı: '%s'."
+
+#, 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>...]"
 
@@ -8130,12 +8220,6 @@ msgstr "strateji"
 msgid "merge strategy to use"
 msgstr "kullanılacak birleştirme stratejisi"
 
-msgid "option=value"
-msgstr "seçenek=değer"
-
-msgid "option for selected merge strategy"
-msgstr "seçili birleştirme stratejisi için seçenekler"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
 "birleştirme işlemesi iletisi (ileri sarım olmayan bir birleştirme için)"
@@ -8196,10 +8280,6 @@ msgstr "İndeks yazılamıyor."
 msgid "Not handling anything other than two heads merge."
 msgstr "İki uç işlemenin birleştirilmesi dışında bir şey yapılmıyor."
 
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "bilinmeyen strateji seçeneği: -X%s"
-
 #, c-format
 msgid "unable to write %s"
 msgstr "%s yazılamıyor"
@@ -8490,8 +8570,8 @@ msgstr "hedef konum var"
 msgid "can not move directory into itself"
 msgstr "dizin kendi içine taşınamıyor"
 
-msgid "cannot move directory over file"
-msgstr "dizin dosya üzerinden taşınamıyor"
+msgid "destination already exists"
+msgstr "hedef halihazırda var"
 
 msgid "source directory is empty"
 msgstr "kaynak dizin boş"
@@ -8570,21 +8650,25 @@ msgid "git notes [--ref <notes-ref>] [list [<object>]]"
 msgstr "git notes [--ref <not-bşvr>] [list [<nesne>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <not-bşvr>] add [-f] [--allow-empty] [-m <ileti> | -F "
-"<dosya> | (-c | -C) <nesne>] [<nesne>]"
+"git notes [--ref <not-bşv>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraf-sonu>] [--[no-]stripspace] [-m <ilet> | -F <dosya> | (-c "
+"| -C) <nesne>] [<nesne>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr "git notes [--ref <not-bşvr>] copy [-f] <nesneden> <nesneye>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <not-bşvr>] append [--allow-empty] [-m <ileti> | -F <dosya> "
-"| (-c | -C) <nesne>] [<nesne>]"
+"git notes [--ref <not-bşv>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraf-sonu>] [--[no-]stripspace] [-m <ileti> | -F <dosya> | (-"
+"c | -C) <nesne>] [<nesne>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref <not-bşvr>] edit [--allow-empty] [<nesne>]"
@@ -8682,10 +8766,6 @@ msgstr "'%s' nesnesi okunamadı."
 msgid "cannot read note data from non-blob object '%s'."
 msgstr "İkili nesne olmayan '%s' nesnesinden not verisi okunamıyor."
 
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "hatalı oluşturulmuş girdi satırı: '%s'."
-
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
 msgstr "notlar '%s' konumundan '%s' konumuna kopyalanamadı"
@@ -8719,6 +8799,15 @@ msgstr "boş not depolamasına izin ver"
 msgid "replace existing notes"
 msgstr "var olan notları başkalarıyla değiştir"
 
+msgid "<paragraph-break>"
+msgstr "<paragraf-sonu>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "paragraflar arasında <paragraf-sonu> ekle"
+
+msgid "remove unnecessary whitespace"
+msgstr "gereksiz boşlukları kaldır"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -8875,16 +8964,16 @@ msgstr "notları <not-bşvr>'ndan kullan"
 msgid "unknown subcommand: `%s'"
 msgstr "bilinmeyen altkomut: '%s'"
 
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects --stdout [<seçenekler>...] [< <bşvr-liste> | < <nesne-"
-"liste>]"
+"git pack-objects --stdout [<sçnklr>] [< <başvuru-listesi> | < <nesne-"
+"listesi>]"
 
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects [<sçnklr>...] <base-name> [< <bşvr-liste> | < <nesne-liste>]"
+"git pack-objects [<sçnklr>] <temel-ad> [< <bşvru-listesi> | < <nesne-"
+"listesi>]"
 
 #, c-format
 msgid ""
@@ -9072,6 +9161,12 @@ msgstr "desteklenmeyen indeks sürümü %s"
 msgid "bad index version '%s'"
 msgstr "hatalı indeks sürümü '%s'"
 
+msgid "show progress meter during object writing phase"
+msgstr "ilerleme çubuğunu nesne yazımı aşaması sırasında göster"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "ilerleme çubuğu gösterildiğinde --all-progress'e benzer"
+
 msgid "<version>[,<offset>]"
 msgstr "<sürüm>[,<ofset>]"
 
@@ -9219,9 +9314,6 @@ msgstr "olabilecek en küçük paket boyutu limiti 1 MiB'dır"
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin bir indekslenebilir paket yapımında kullanılamaz"
 
-msgid "cannot use --filter without --stdout"
-msgstr "--filter, --stdout olmadan kullanılamaz"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "--filter, --stdin-packs ile birlikte kullanılamıyor"
 
@@ -9234,9 +9326,6 @@ msgstr "iç revizyon listeleri, --cruft ile birlikte kullanılamıyor"
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "--stdin-packs, --cruft ile birlikte kullanılamıyor"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "--max-pack-size, --cruft ile birlikte kullanılamıyor"
-
 msgid "Enumerating objects"
 msgstr "Nesneler ortaya dökülüyor"
 
@@ -9262,8 +9351,15 @@ msgstr ""
 "<git@vger.kernel.org> adresine bir e-posta atarak\n"
 "bize haber verin. Sağ olun.\n"
 
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<seçenekler>]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "--i-still-use-this olmadan çalıştırma reddediliyor"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune]git pack-refs [--all] [--no-prune] [--"
+"include <dizgi>] [--exclude <dizgi>]"
 
 msgid "pack everything"
 msgstr "her şeyi paketle"
@@ -9271,6 +9367,24 @@ msgstr "her şeyi paketle"
 msgid "prune loose refs (default)"
 msgstr "gevşek başvuruları buda (öntanımlı)"
 
+msgid "references to include"
+msgstr "içerilecek başvurular"
+
+msgid "references to exclude"
+msgstr "dışarıda tutulacak başvurular"
+
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+msgid "use the unstable patch-id algorithm"
+msgstr "kararlı olmayan yama kimliği algoritmasını kullan"
+
+msgid "use the stable patch-id algorithm"
+msgstr "kararlı yama kimliği algoritmasını kullan"
+
+msgid "don't strip whitespace from the patch"
+msgstr "yamadan boşlukları çıkarma"
+
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr ""
 "git prune [-n] [-v] [--progress] [--expire <zaman>] [--] [<dal-ucu>...]"
@@ -9317,6 +9431,12 @@ msgstr "zorla yerel dalın üzerine yaz"
 msgid "number of submodules pulled in parallel"
 msgstr "paralelde çekilen altmodüllerin sayısı"
 
+msgid "use IPv4 addresses only"
+msgstr "yalnızca IPv4 adresleri kullan"
+
+msgid "use IPv6 addresses only"
+msgstr "yalnızca IPv6 adresleri kullan"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9420,7 +9540,7 @@ msgstr "İndekse eklenen değişikliklerle henüz doğmamış bir dal güncellen
 msgid "pull with rebase"
 msgstr "yeniden temellendirme ile çekim"
 
-msgid "please commit or stash them."
+msgid "Please commit or stash them."
 msgstr "Lütfen onları işleyin veya zulalayın."
 
 #, c-format
@@ -9484,9 +9604,8 @@ msgstr ""
 
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
@@ -9576,39 +9695,39 @@ msgstr ""
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Güncellemeler reddedildi; çünkü geçerli dalınızın ucu kendisinin\n"
-"uzak konum karşıtından geride. Yeniden itmeden önce uzak konumdaki\n"
-"değişiklikleri tümleştirin (örn. 'git pull ...').\n"
+"uzak konum karşıtından geride. Uzaktaki değişiklikleri tümleştirmek\n"
+"istiyorsanız yeniden itmeden önce 'git pull' yapın.\n"
 "Ayrıntılar için 'git push --help' içinde 'Notes about fast-forwards'a\n"
 "bakın."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Güncellemeler reddedildi; çünkü itilmiş bir dal ucu kendisinin\n"
-"uzak konum karşıtından geride. Yeniden itmeden önce bu dalı çıkış\n"
-"yapın ve uzak konumdaki değişiklikleri tümleştirin (örn. 'git pull\n"
-"...'). Ayrıntılar için 'git push --help' içinde 'Notes about\n"
-"fast-forwards'a bakın."
+"uzak konum karşıtından geride. Uzaktaki değişiklikleri tümleştirmek\n"
+"istiyorsanız yeniden itmeden önce 'git pull' yapın.\n"
+"Ayrıntılar için 'git push --help' içinde 'Notes about fast-forwards'a\n"
+"bakın."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Güncellemeler reddedildi; çünkü uzak konumda henüz yerelde sizde olmayan\n"
 "değişiklikler var. Bu genelde başka bir deponun aynı başvuruya itmesinden\n"
-"dolayı olur. Yeniden itmeden önce uzak konumdaki değişiklikleri tümleş-\n"
-"tirmek isteyebilirsiniz (örn. 'git pull ...').\n"
+"dolayı olur. Uzaktaki değişiklikleri tümleştirmek istiyorsanız yeniden\n"
+"itmeden önce 'git pull' yapın..\n"
 "Ayrıntılar için 'git push --help' içinde 'Notes about fast-forwards'a\n"
 "bakın."
 
@@ -9625,24 +9744,32 @@ msgstr ""
 "olmayan bir nesneye işaret etmesini sağlaması için güncelleyemezsiniz.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Güncellemeler reddedildi; çünkü uzak izleme dalının ucu son\n"
-"çıkıştan bu yana güncellenmiş. Bir güncellemeyi zorlamadan\n"
-"önce bu değişiklikleri yerel olarak tümleştirmek isteye-\n"
-"bilirsiniz (örn. 'git pull ...'\n"
+"Güncellemeler reddedildi; çünkü geçerli dalınızın ucu kendisinin\n"
+"uzak konum karşıtından geride. Uzaktaki değişiklikleri tümleştirmek\n"
+"istiyorsanız yeniden itmeden önce 'git pull' yapın.\n"
+"Ayrıntılar için 'git push --help' içinde 'Notes about fast-forwards'a\n"
+"bakın."
 
 #, c-format
 msgid "Pushing to %s\n"
-msgstr "İtme konumu: %s\n"
+msgstr "Şuraya itiliyor: %s\n"
 
 #, c-format
 msgid "failed to push some refs to '%s'"
 msgstr "bazı başvurular '%s' konumuna itilemedi"
 
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"altmodül içine push.recurseSubmodules=only ile özyineleniyor; yerine talep "
+"başına kullanılıyor"
+
 #, c-format
 msgid "invalid value for '%s'"
 msgstr "'%s' için geçersiz değer"
@@ -9650,8 +9777,8 @@ msgstr "'%s' için geçersiz değer"
 msgid "repository"
 msgstr "depo"
 
-msgid "push all refs"
-msgstr "tüm başvuruları it"
+msgid "push all branches"
+msgstr "tüm dalları it"
 
 msgid "mirror all refs"
 msgstr "tüm başvuruları yansıla"
@@ -9659,14 +9786,14 @@ msgstr "tüm başvuruları yansıla"
 msgid "delete refs"
 msgstr "başvuruları sil"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "etiketleri it (--all veya --mirror ile kullanılamaz)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr "etiketleri it (--all, --branches  veya --mirror ile kullanılamaz)"
 
 msgid "force updates"
-msgstr "zorla güncelle"
+msgstr "güncellemeleri zorla"
 
 msgid "<refname>:<expect>"
-msgstr "<başvuruadı>:<bekle>"
+msgstr "<bşvr-adı>:<bekle>"
 
 msgid "require old value of ref to be at this value"
 msgstr "başvurunun eski değerinin bu değerde olmasını gerektir"
@@ -9779,13 +9906,14 @@ msgid "need two commit ranges"
 msgstr "iki işleme erimi gerekli"
 
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<önek>) [-"
-"u | -i]] [--no-sparse-checkout] [--index-output=<dosya>] (--empty | "
-"<ağacımsı1> [<ağacımsı2> [<ağacımsı3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<önek>)\n"
+"              [-u | -i]] [--index-output=<dosya>] [--no-sparse-checkout]\n"
+"              (--empty | <ağacımsı1> [<ağacımsı2> [<ağacımsı3>]])"
 
 msgid "write resulting index to <file>"
 msgstr "ortaya çıkan indeksi <dosya>'ya yaz"
@@ -9876,8 +10004,8 @@ msgid "%s requires the merge backend"
 msgstr "%s birleştirme arka ucunu gerektiriyor"
 
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "'onto' alınamadı: '%s'"
+msgid "invalid onto: '%s'"
+msgstr "üzerine geçersiz: '%s'"
 
 #, c-format
 msgid "invalid orig-head: '%s'"
@@ -9922,17 +10050,33 @@ msgstr ""
 "\n"
 "Bunun sonucu olarak git onları yeniden temellendiremiyor."
 
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Bilinmeyen rebase-merges kipi: %s"
+
 #, c-format
 msgid "could not switch to %s"
 msgstr "şuraya geçilemedi: %s"
 
+msgid "apply options and merge options cannot be used together"
+msgstr "uygulama seçenekleri ve birleştirme seçenekleri birlikte kullanılamaz"
+
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
 msgstr ""
 "Tanımlanamayan boş tür '%s'; geçerli türler: \"drop\", \"keep\" ve \"ask\"."
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"bir boş dizi argümanıyla --rebase-merges yapmak artık kullanılmıyor ve "
+"Git'in ileriki bir sürümünde tümüyle kaldırılacak. Bunun yerine, aynı şeyi "
+"yapan argümansız bir --rebase-merges kullanın."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10154,15 +10298,26 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "'C' anahtarı sayısal bir değer bekliyor"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Bilinmeyen kip: %s"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+"seçenekleri uygula, rebase.autoSquash ile uyumlu değil. --no-autosquash "
+"eklemeyi düşünün"
 
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy, --merge veya --interactive gerektiriyor"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+"seçenekleri uygula, rebase.rebaseMerges ile uyumlu değil. --no-rebase-merges "
+"eklemeyi düşünün"
 
-msgid "apply options and merge options cannot be used together"
-msgstr "uygulama seçenekleri ve birleştirme seçenekleri birlikte kullanılamaz"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr ""
+"uygulama seçenekleri, rebase.updateRefs ile uyumlu değil. --no-update-refs "
+"eklemeyi düşünün"
 
 #, c-format
 msgid "Unknown rebase backend: %s"
@@ -10186,8 +10341,8 @@ msgstr "böyle bir dal/işleme yok: '%s'"
 msgid "No such ref: %s"
 msgstr "Böyle bir başvuru yok: %s"
 
-msgid "Could not resolve HEAD to a revision"
-msgstr "HEAD bir revizyona çözülemedi"
+msgid "Could not resolve HEAD to a commit"
+msgstr "HEAD, bir işlemeye çözülemedi"
 
 #, c-format
 msgid "'%s': need exactly one merge base with branch"
@@ -10201,9 +10356,6 @@ msgstr "'%s': tam olarak bir birleştirme temeli gerekiyor"
 msgid "Does not point to a valid commit '%s'"
 msgstr "'%s' geçerli bir işlemeye işaret etmiyor"
 
-msgid "Please commit or stash them."
-msgstr "Lütfen onları işleyin veya zulalayın."
-
 msgid "HEAD is up to date."
 msgstr "HEAD güncel."
 
@@ -10460,11 +10612,12 @@ msgstr "bilinmeyen yansı argümanı: %s"
 msgid "fetch the remote branches"
 msgstr "uzak konum dallarını getir"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "getirirken tüm etiketleri ve ilişkili nesneleri içe aktar"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "veya hiçbir etiketi getirme (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"getirirken tüm etiketleri ve ilişkili nesneleri\n"
+"içe aktar veya etiketleri hiç içe aktarma (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "izlenecek dal(lar)"
@@ -10849,6 +11002,14 @@ msgstr "geçici dosya '%s', yazma için açılamadı"
 msgid "could not close refs snapshot tempfile"
 msgstr "başvurular anlık görüntü geçici dosyası kapatılamadı"
 
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "eskimiş biteşlem kaldırılamadı: %s"
+
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "paket öneki %s, nesne dizini %s ile başlamıyor"
+
 msgid "pack everything in a single pack"
 msgstr "her şeyi tek bir pakete sığdır"
 
@@ -10861,8 +11022,8 @@ msgstr "-a ile aynı, ulaşılamayan süprüntü nesneleri ayrı paketle"
 msgid "approxidate"
 msgstr "yaklaşık tarih"
 
-msgid "with -C, expire objects older than this"
-msgstr "-C ile, bundan daha eski nesneleri yürürlükten kaldır"
+msgid "with --cruft, expire objects older than this"
+msgstr "--cruft ile bundan daha eski nesneleri yürürlükten kaldır"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "gereksiz paketleri kaldır ve 'git-prune-packed' çalıştır"
@@ -10921,19 +11082,29 @@ 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"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "bir precious-objects deposundaki paketler silinemiyor"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "'%s' seçeneği, yalnızca '%s' ile birlikte kullanılabilir"
+
 msgid "Nothing new to pack."
 msgstr "Paketlenecek yeni bir şey yok."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "paket öneki %s, nesne dizini %s ile başlamıyor"
+msgid "renaming pack to '%s' failed"
+msgstr "paketi '%s' olarak yeniden adlandırma başarısız"
 
 #, c-format
-msgid "missing required file: %s"
-msgstr "gereken dosya eksik: %s"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects, şu paket için bir '%s' dosyası yazmadı: %s-%s"
 
 #, c-format
 msgid "could not unlink: %s"
@@ -11127,8 +11298,10 @@ msgstr "--convert-graft-file argüman almaz"
 msgid "only one pattern can be given with -l"
 msgstr "-l ile yalnızca bir dizgi verilebilir"
 
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forget <yol>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <yol-blrtç>... | diff | status | remaining | gc]"
 
 msgid "register clean resolutions in index"
 msgstr "indeksteki temiz çözümlerin kaydını yap"
@@ -11333,24 +11506,44 @@ msgstr "--prefix bir argüman gerektiriyor"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "--abbrev-ref için bilinmeyen kip: %s"
 
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden, --branches ile birlikte kullanılamıyor"
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden, --tags ile birlikte kullanılamıyor"
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden, --remotes ile birlikte kullanılamıyor"
+
 msgid "this operation must be run in a work tree"
 msgstr "bu işlem bir çalışma ağacı içinde çalıştırılmalı"
 
+msgid "Could not read the index"
+msgstr "İndeks okunamadı"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "--show-object-format için bilinmeyen kip: %s"
 
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<seçenekler>] <işlememsi>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <üst-öge-numarası>] [-s] [-S[<anahtar-"
+"kimliği>]] <işleme>..."
 
-msgid "git revert <subcommand>"
-msgstr "git revert <altkomut>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
 
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<seçenekler>] <işlememsi>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <üst-öge-numarası>] [-s] [-x] [--ff]\n"
+"                [-S[<anahtar-kimliği>]] <işleme>..."
 
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <altkomut>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
@@ -11411,8 +11604,14 @@ msgstr "geri al başarısız"
 msgid "cherry-pick failed"
 msgstr "seç-al başarısız"
 
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<seçenekler>] [--] <dosya>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<dosya> [--pathspec-file-nul]]\n"
+"       [--] [<yol-blrtç>...]"
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -11486,16 +11685,21 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
-"              [--receive-pack=<git-receive-pack>]\n"
+"              [--receive-pack=<git-paket-al>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
-"              [<makine>:]<dizin> (--all | <başvurular>...)"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
+"              [<makine>:]<dizin> (--all | <başvuru>...)"
 
 msgid "remote name"
 msgstr "uzak konum adı"
 
+msgid "push all refs"
+msgstr "tüm başvuruları it"
+
 msgid "use stateless RPC protocol"
 msgstr "durumsuz RPC protokolünü kullan"
 
@@ -11514,8 +11718,9 @@ msgstr "git log --pretty=short | git shortlog [<seçenekler>]"
 msgid "using multiple --group options with stdin is not supported"
 msgstr "stdin ile çoklu --group seçenekleri kullanımı desteklenmiyor"
 
-msgid "using --group=trailer with stdin is not supported"
-msgstr "stdin ile --group=trailer kullanımı desteklenmiyor"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "stdin ile %s kullanma desteklenmiyor"
 
 #, c-format
 msgid "unknown group type: %s"
@@ -11546,18 +11751,20 @@ msgid "group by field"
 msgstr "alan ile grupla"
 
 msgid "too many arguments given outside repository"
-msgstr "depo dışında çok fazla argüman verildi"
+msgstr "depo dışında pek fazla argüman verildi"
 
 msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
-"                [--current] [--color[=<nezaman>] | --no-color] [--sparse]\n"
+"                [--current] [--color[=<ne-zaman>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<bşvr> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g | --reflog)[=<n>[,<temel>]] [--list] [<başvuru>]"
@@ -11657,21 +11864,44 @@ msgid "Unknown hash algorithm"
 msgstr "bilinmeyen sağlama algoritması '%s'"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<dizgi>...]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<dizgi>...]"
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<başvuru>...]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<dizgi>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <başvuru>"
+
+msgid "reference does not exist"
+msgstr "başvuru yok"
+
+msgid "failed to look up reference"
+msgstr "başvuru bakılamadı"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "yalnızca etiketleri göster (dal uçlarıyla birlikte kullanılabilir)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "yalnızca dal uçlarını göster (etiketlerle birlikte kullanılabilir)"
 
+msgid "check for reference existence without resolving"
+msgstr "çözmeden başvuru varlığını denetle"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr "daha sıkı başvuru denetlemesi; kesin başvuru yolu gerektirir"
 
@@ -11690,8 +11920,15 @@ msgstr "sonuçları stdout'a yazdırma (--verify ile birlikte kullanışlı)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "stdin'den yerel bir depoda olmayan başvuruları göster"
 
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) <seçenekler>"
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "yalnızca '%s', '%s' veya '%s' arasından biri verilebilir"
+
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<seçenekler>]"
 
 msgid "this worktree is not sparse"
 msgstr "bu çalışma ağacı aralıklı değil"
@@ -11813,67 +12050,73 @@ msgstr ""
 msgid "error while refreshing working directory"
 msgstr "çalışma dizini yenilenirken hata"
 
-msgid "git stash list [<options>]"
-msgstr "git stash list [<seçenekler>]"
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <dosya>]"
 
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<seçenekler>] [<zula>]"
+msgid "terminate input and output files by a NUL character"
+msgstr "girdi ve çıktı dosyalarını bir NUL karakteri ile sonlandır"
 
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<zula>]"
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"--rules-file ile kullanıldığında dizgileri koni kipi dizgileri olarak yorumla"
 
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<zula>]"
+msgid "use patterns in <file> instead of the current ones."
+msgstr "geçerli dizgiler yerine <dosya> içindekileri kullan."
 
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <dal-adı> [<zula>]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<günlük-seçenekleri>]"
 
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <ileti>]\n"
-"          [--pathspec-from-file=<dosya> [--pathspec-file-nul]]\n"
-"          [--] [<yol-blrtç>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"seçenekleri>] [<zula>]"
 
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<ileti>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<zula>]"
+
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<zula>]"
 
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<zula>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<zula>]"
 
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<zula>]"
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <dal-adı> [<zula>]"
 
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <ileti>] [-q|--quiet] <işleme>"
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr "git stash store [(-m | --message) <ileti>] [-q | --quiet] <işleme>"
 
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <ileti>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q\n"
+"          | --quiet] [-u | --include-untracked] [-a | --all] [(-m | --"
+"message)\n"
+"          <ileti>] [--pathspec-from-file=<dosya> [--pathspec-file-nul]]\n"
 "          [--] [<yol-blrtç>...]]"
 
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<ileti>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"|\n"
+"          --quiet] [-u | --include-untracked] [-a | --all] [<ileti>]"
+
+msgid "git stash create [<message>]"
+msgstr "git stash create [<ileti>]"
 
 #, c-format
 msgid "'%s' is not a stash-like commit"
@@ -12337,6 +12580,10 @@ msgstr "Birleştirilmemiş altmodül %s atlanıyor"
 msgid "Skipping submodule '%s'"
 msgstr "'%s' altmodülü atlanıyor"
 
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "bir URL olmadan '%s' altmodülü içe aktarılamıyor"
+
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "'%s' klonlanamadı. Yeniden deneme zamanlandı"
@@ -12359,7 +12606,7 @@ msgstr "Şu altmodül yolunda '%s' birleştirilemedi: '%s'"
 
 #, c-format
 msgid "Execution of '%s %s' failed in submodule path '%s'"
-msgstr "Şu altmodül yolunda '%s %s' çalıştırılamadı: '%s'"
+msgstr "Şu altmodül yolunda '%s %s' yürütülemedi: '%s'"
 
 #, c-format
 msgid "Submodule path '%s': checked out '%s'\n"
@@ -12434,9 +12681,6 @@ msgstr "altmodülleri özyineli basamaklandır"
 msgid "don't fetch new objects from the remote site"
 msgstr "yeni nesneleri uzak konumdan getirme"
 
-msgid "path into the working tree"
-msgstr "çalışma ağacına giden yol"
-
 msgid "use the 'checkout' update strategy (default)"
 msgstr "'checkout' güncelleme stratejisini kullan (öntanımlı)"
 
@@ -12472,27 +12716,12 @@ msgstr ""
 "shallow] [--reference <depo>] [--recursive] [--[no-]single-branch] [--] "
 "[<yol>...]"
 
-msgid "recurse into submodules"
-msgstr "altmodüllere özyinele"
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "HEAD geçerli bir başvuru olarak çözülemedi."
 
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<seçenekler>] [<yol>...]"
 
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr ".gitmodules dosyasına yazım güvenli mi değil mi denetle"
-
-msgid "unset the config in the .gitmodules file"
-msgstr ".gitmodules dosyasındaki yapılandırmayı kaldır"
-
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <ad> [<değer>]"
-
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <ad>"
-
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr ".gitmodules dosyasının çalışma ağacında olduğundan lütfen emin ol"
-
 msgid "suppress output for setting url of a submodule"
 msgstr "bir altmodül url ayarlanması çıktısını gizle"
 
@@ -12570,6 +12799,9 @@ msgstr "'%s' altmodülü için yerel git dizini yeniden etkinleştiriliyor\n"
 msgid "unable to checkout submodule '%s'"
 msgstr "'%s' altmodülü çıkış yapılamıyor"
 
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr ".gitmodules dosyasının çalışma ağacında olduğundan lütfen emin ol"
+
 #, c-format
 msgid "Failed to add submodule '%s'"
 msgstr "'%s' altmodülü eklenemedi"
@@ -12620,19 +12852,17 @@ msgstr "depo URL'si: '%s' mutlak olmalı veya ./|../ ile başlamalıdır"
 msgid "'%s' is not a valid submodule name"
 msgstr "'%s' geçerli bir altmodül adı değil"
 
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s, --super-prefix desteklemiyor"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <komut>"
 
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "'%s' geçerli bir submodule--helper altkomutu değil"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <neden>] <ad> <başvuru>"
 
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<seçenekler>] <ad> [<başvuru>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <ad>"
 
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <ad>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <ad>"
 
 msgid "suppress error message for non-symbolic (detached) refs"
 msgstr "sembolik olmayan (ayrık) başvurular için hata iletisini gizle"
@@ -12643,6 +12873,9 @@ msgstr "sembolik başvuruyu sil"
 msgid "shorten ref output"
 msgstr "başvuru çıktısını kısalt"
 
+msgid "recursively dereference (default)"
+msgstr "başvuruyu özyineli olarak kaldır (öntanımlı)"
+
 msgid "reason"
 msgstr "neden"
 
@@ -12650,24 +12883,26 @@ msgid "reason of the update"
 msgstr "güncelleme nedeni"
 
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u <anahtar-kimliği>] [-f] [-m <ileti> | -F <dosya>]\n"
-"        <etiket-adı> [<head>]"
+"git tag [-a | -s | -u <anahtar-kimliği>] [-f] [-m <ileti> | -F <dosya>] [-"
+"e]\n"
+"        <etiket-adı> [<işleme> | <nesne>]"
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d <etiket-adı>..."
 
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[<sayı>]] [--contains <işleme>] [--no-contains <işleme>]\n"
-" [-- points-at <nesne>] [--format=<biçim>] [--merged <işleme>]\n"
-" [--no-merged <işleme>] [<dizgi>...]"
+"git tag [-n[<sayı>]] -l [--contains <işleme>] [--no-contains <işleme>]\n"
+"        [--points-at <nesne>] [--column[=<seçenekler>] | --no-column]\n"
+"        [--create-reflog] [--sort=<anahtar>] [--format=<biçim>]\n"
+"        [--merged <işleme>] [--no-merged <işleme>] [<dizgi>...]"
 
 msgid "git tag -v [--format=<format>] <tagname>..."
 msgstr "git tag -v [--format=<biçim>] <etiket-adı>..."
@@ -12942,6 +13177,9 @@ msgstr "(okunabilir veri için) kaydedilmiş çözülmeyen çakışmaları unut"
 msgid "write index in this format"
 msgstr "indeksi bu biçimle yaz"
 
+msgid "report on-disk index format version"
+msgstr "diskteki indeks biçimi sürümünü raporla"
+
 msgid "enable or disable split index"
 msgstr "bölünmüş indeksi etkinleştir veya devre dışı bırak"
 
@@ -12966,6 +13204,14 @@ msgstr "dosyaları dosya sistemi monitöründe geçerli olarak imle"
 msgid "clear fsmonitor valid bit"
 msgstr "dosya sistemi monitöründe geçerli kısmını temizle"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: %d idi, %d olarak ayarlandı"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13043,8 +13289,12 @@ msgstr "güncellemeleri stdin'den oku"
 msgid "update the info files from scratch"
 msgstr "bilgi dosyalarını en baştan güncelle"
 
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<seçenekler>] <dizin>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <dizin>"
 
 msgid "quit after a single request/response exchange"
 msgstr "tek bir istek/yanıt değiş tokuşundan sonra çık"
@@ -13058,8 +13308,8 @@ msgstr "eğer <dizin> bir Git dizini değilse <dizin>/.git/ deneme"
 msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "aktarımı <n> saniye hareketsizlikten sonra kes"
 
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <işleme>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | --verbose] [--raw] <işleme>..."
 
 msgid "print commit contents"
 msgstr "işleme içeriğini yazdır"
@@ -13067,8 +13317,9 @@ msgstr "işleme içeriğini yazdır"
 msgid "print raw gpg status output"
 msgstr "ham gpg durum çıktısını yazdır"
 
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <paket>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr ""
+"git verify-pack [-v | --verbose] [-s | --stat-only] [--] <paket>.idx..."
 
 msgid "verbose"
 msgstr "ayrıntılı anlatım"
@@ -13076,35 +13327,70 @@ msgstr "ayrıntılı anlatım"
 msgid "show statistics only"
 msgstr "yalnızca istatistikleri göster"
 
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<biçim>] <etiket>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<biçim>] [--raw] <etiket>..."
 
 msgid "print tag contents"
 msgstr "etiket içeriğini yazdır"
 
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<seçenekler>] <yol> [<işlememsi>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <dizi>]]\n"
+"                 [--orphan] [(-b | -B) <yeni-dal>] <yol> [<işlememsi>]"
 
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<seçenekler>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
 
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<seçenekler>] <yol>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <dizi>] <çalışma-ağacı>"
 
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <ç-ağacı> <yeni-yol>"
 
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<seçenekler>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <süre-dolum>]"
 
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<seçenekler>] <ç-ağacı>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <çalışma-ağacı>"
 
 msgid "git worktree repair [<path>...]"
 msgstr "git worktree repair [<yol>...]"
 
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <yol>"
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <çalışma-ağacı>"
+
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Olası kaynak dal yok, '--orphan' anlamı çıkarılıyor"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Bu depo için yeni bir yetim dal içeren (işlemesiz dal) bir\n"
+"çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağı\n"
+"ile yapabilirsiniz:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Bu depo için yeni bir yetim dal içeren (işlemesiz dal) bir\n"
+"çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağı\n"
+"ile yapabilirsiniz:\n"
+"\n"
+"    git worktree add --orphan %s\n"
 
 #, c-format
 msgid "Removing %s/%s: %s"
@@ -13172,10 +13458,35 @@ msgstr "Çalışma ağacı hazırlanıyor ('%s' dalı sıfırlanıyor; %s konumu
 msgid "Preparing worktree (checking out '%s')"
 msgstr "Çalışma ağacı hazırlanıyor ('%s' çıkış yapılıyor)"
 
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "erişilemiyor: geçersiz başvuru: %s"
+
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Çalışma ağacı hazırlanıyor (ayrık HEAD %s)"
 
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD, geçersiz (veya yetim bırakılmış bir başvuruya işaret ediyor.\n"
+"HEAD yolu: '%s'\n"
+"HEAD içeriği: '%s'"
+
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"Bir uzak konum olmasına rağmen hiçbir yerel/uzak başvuru yok, durduruluyor;\n"
+"geçersiz kılmak veya önce bir uzak konum getirmek için 'add -f' kullanın"
+
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "'%s' ve '%s' birlikte kullanılamaz"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "diğer çalışma ağacında çıkış yapılmış olsa bile <dal> çıkışını yap"
 
@@ -13185,6 +13496,9 @@ msgstr "yeni bir dal oluştur"
 msgid "create or reset a branch"
 msgstr "yeni bir dal oluştur veya sıfırla"
 
+msgid "create unborn/orphaned branch"
+msgstr "doğmamış/yetim bırakılmış dal oluştur"
+
 msgid "populate the new working tree"
 msgstr "yeni çalışma ağacını doldur"
 
@@ -13204,6 +13518,13 @@ msgstr "yeni dalın adını bir uzak izleme dalıyla eşleştirmeyi dene"
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "'%s', '%s' ve '%s' seçenekleri birlikte kullanılamaz"
 
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "'%s' ve '%s' seçenekleri birlikte kullanılamaz"
+
+msgid "<commit-ish>"
+msgstr "<işlememsi>"
+
 msgid "added with --lock"
 msgstr "--lock ile eklendi"
 
@@ -13334,23 +13655,58 @@ msgstr "yalnızca hata ayıklama için yararlı"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "core.fsyncMethod = batch, bu platformda desteklenmiyor"
 
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "demet liste anahtarı %s ile '%s' değeri ayrıştırılamıyor"
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "'%s' konumundaki demet listesinin kipi yok"
+
 msgid "failed to create temporary file"
 msgstr "geçici dosya oluşturulamadı"
 
 msgid "insufficient capabilities"
 msgstr "yetersiz yetenekler"
 
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "'%s' konumundan indirilen dosya bir demet değil"
+
+msgid "failed to store maximum creation token"
+msgstr "en büyük oluşturma jetonu depolanamadı"
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "'%s' URI'sinden tanımlanamayan demet kipi"
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "demet URI özyineleme sınırı aşıldı (%d)"
+
 #, c-format
 msgid "failed to download bundle from URI '%s'"
 msgstr "'%s' URI'sinden demet indirilemedi"
 
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "'%s' URI'sindeki dosya bir demet değil"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "'%s' URI'sindeki dosya bir demet veya demet listesi değil"
 
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "'%s' URI'sindeki demet çözülemedi"
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: beklenmedik argüman: '%s'"
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: argümanlardan sonra floş bekleniyordu"
+
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: boş bir satır alındı"
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: satır, 'anahtar=değer' olarak biçimlenmemiş"
+
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: satırda boş anahtar veya değer var"
 
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
@@ -13374,6 +13730,13 @@ msgstr "Depo aşağıdaki önkoşul işlemelere iye değil:"
 msgid "need a repository to verify a bundle"
 msgstr "bir demeti doğrulamak için bir depo gerekiyor"
 
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"nesne deposunda bazı önkoşul işlemeleri var; ancak deponun geçmişine bağlı "
+"değiller"
+
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -13389,6 +13752,14 @@ msgid_plural "The bundle requires these %<PRIuMAX> refs:"
 msgstr[0] "Demet bu başvuruyu gerektiriyor:"
 msgstr[1] "Demet bu %<PRIuMAX> başvuruyu gerektiriyor:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Demet, bu sağlama algoritmasını kullanıyor: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Demet, bu süzgeci kullanıyor: %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "demet açıklayıcısı çoğaltılamıyor"
 
@@ -13423,6 +13794,10 @@ msgstr "index-pack sonlandı"
 msgid "terminating chunk id appears earlier than expected"
 msgstr "iri parça numarası sonlandırması beklenenden önce ortaya çıkıyor"
 
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "iri parça kimliği %<PRIx32>, %d bayt hizalı değil"
+
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "düzgün olmayan iri parça ofseti %<PRIx64> ve %<PRIx64>"
@@ -13475,8 +13850,8 @@ msgstr "Hata raporu bildirimi için veri topla"
 msgid "Move objects and refs by archive"
 msgstr "Nesneleri ve başvuruları arşive göre taşı"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr "Depo nesneleri için içerik veya tür/boyut bilgisi sağla"
+msgid "Provide contents or details of repository objects"
+msgstr "Depo nesnelerinin içeriğini veya ayrıntılarını sağla"
 
 msgid "Display gitattributes information"
 msgstr "gitattributes bilgisini görüntüle"
@@ -13613,8 +13988,8 @@ msgstr "Bir dizgi ile eşleşen satırları yazdır"
 msgid "A portable graphical interface to Git"
 msgstr "Git için taşınabilir bir grafik arabirim"
 
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "Sağlamayı hesapla ve isteğe göre dosyadan ikili oluştur"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "Nesne kimliğini hesapla/dosyadan isteğe bağlı nesne oluştur"
 
 msgid "Display help information about Git"
 msgstr "Git yardım bilgisini görüntüle"
@@ -13881,8 +14256,8 @@ msgstr "Etiketlerin GPG imzasını doğrula"
 msgid "Display version information about Git"
 msgstr "Git sürüm bilgisini görüntüle"
 
-msgid "Show logs with difference each commit introduces"
-msgstr "Günlükleri her işlemenin sunduğu değişikliklerle göster"
+msgid "Show logs with differences each commit introduces"
+msgstr "Günlükleri her işlemenin değişiklikleriyle göster"
 
 msgid "Manage multiple working trees"
 msgstr "Birden çok çalışma ağacını yönet"
@@ -13920,8 +14295,8 @@ msgstr "Demet dosya biçimi"
 msgid "Chunk-based file formats"
 msgstr "geçersiz gitfile biçimi: %s"
 
-msgid "Git commit graph format"
-msgstr "Git işleme grafiği biçimi"
+msgid "Git commit-graph format"
+msgstr "Git commit-graph biçimi"
 
 msgid "Git index format"
 msgstr "Git indeks biçimi"
@@ -14017,9 +14392,16 @@ msgstr "commit-graph dosyası %u iri parça tutmak için pek küçük"
 msgid "commit-graph has no base graphs chunk"
 msgstr "commit-graph temel grafiği iri parçasına iye değil"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "commit-graph temel grafiği iri parçası pek küçük"
+
 msgid "commit-graph chain does not match"
 msgstr "commit-graph zinciri eşleşmiyor"
 
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "temel grafikteki işleme sayısı pek yüksek: %<PRIuMAX>"
+
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr "geçersiz commit-graph zinciri: '%s'. satır bir sağlama değil"
@@ -14037,6 +14419,9 @@ msgstr "%s işlemesi bulunamadı"
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "commit-graph, taşım oluşturma verisi gerektiriyor; ancak hiç yok"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr "commit-graph, taşım üretim verisi pek küçük"
+
 msgid "Loading known commits in commit graph"
 msgstr "İşleme grafiğindeki bilinen işlemeler yükleniyor"
 
@@ -14104,6 +14489,14 @@ msgstr "temel commit-graph dosyası yeniden adlandırılamadı"
 msgid "failed to rename temporary commit-graph file"
 msgstr "geçici commit-graph dosyası yeniden adlandırılamadı"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "%<PRIuMAX>, %<PRIuMAX> işlemeli grafikler birleştirilemiyor"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "%s grafiği birleştirilemiyor, pek çok işleme: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Birleştirilen işlemeler taranıyor"
 
@@ -14115,7 +14508,7 @@ msgstr ""
 "bir commit-graph yazılmaya çalışılıyor; ancak 'core.commitGraph' devre dışı"
 
 msgid "too many commits to write graph"
-msgstr "grafik yazımı için çok fazla işleme"
+msgstr "grafik yazımı için pek fazla işleme"
 
 msgid "the commit-graph file has incorrect checksum and is likely corrupt"
 msgstr ""
@@ -14133,9 +14526,6 @@ msgstr "commit-graph hatalı fanout değerine iye: fanout[%d] = %u != %u"
 msgid "failed to parse commit %s from commit-graph"
 msgstr "%s işlemesi commit-graph'tan ayrıştırılamadı"
 
-msgid "Verifying commits in commit graph"
-msgstr "İşleme grafiğindeki işlemeler doğrulanıyor"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14158,20 +14548,6 @@ msgstr "%s için olan commit-graph üst ögesi %s != %s"
 msgid "commit-graph parent list for commit %s terminates early"
 msgstr "%s işlemesi için olan commit-graph üst öge listesi erkenden sonlanıyor"
 
-#, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"%s işlemesi için commit-graph kuşak sayısı sıfır; ancak başka yerlerde "
-"sıfırdan farklı"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"%s işlemesi için commit-graph kuşak sayısı sıfırdan farklı; ancak başka "
-"yerlerde sıfır"
-
 #, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr "%s işlemesi için commit-graph kuşağı %<PRIuMAX> < %<PRIuMAX>"
@@ -14181,6 +14557,17 @@ msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
 msgstr ""
 "%s işlemesi için commit-graph içindeki işleme tarihi %<PRIuMAX> != %<PRIuMAX>"
 
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"commit-graph'te hem sıfır hem de sıfır olmayan üretimler var (örneğin, "
+"'%s've '%s' işlemeleri)"
+
+msgid "Verifying commits in commit graph"
+msgstr "İşleme grafiğindeki işlemeler doğrulanıyor"
+
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s bir işleme değil!"
@@ -14204,6 +14591,10 @@ msgstr ""
 "\"git config advice.graftFileDeprecated false\"\n"
 "kullanarak bu iletiyi kapatabilirsiniz"
 
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr "%s işlemesi işleme grafiğinde var; ancak nesne veritabanında yok"
+
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr ""
@@ -14269,6 +14660,10 @@ msgstr "'has_worktree_moved' içinde işlenmemiş senaryo: %d"
 msgid "health thread wait failed [GLE %ld]"
 msgstr "sağlık iş parçacığı bekleme başarısız oldu [GLE %ld]"
 
+#, c-format
+msgid "Invalid path: %s"
+msgstr "Geçersiz yol %s"
+
 msgid "Unable to create FSEventStream."
 msgstr "FSEventStream oluşturulamadı."
 
@@ -14299,6 +14694,22 @@ msgstr "GetOverlappedResult, '%s' üzerinde başarısız oldu [GLE %ld]"
 msgid "could not read directory changes [GLE %ld]"
 msgstr "dizin değişiklikleri okunamadı [GLE %ld]"
 
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "opendir('%s') başarısız oldu"
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "lstat('%s') başarısız oldu"
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "strbuf_readlink('%s') başarısız oldu"
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "closedir('%s') başarısız oldu"
+
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
 msgstr "[GLE %ld] '%ls', okuma için açılamıyor"
@@ -14484,7 +14895,7 @@ msgstr "%s içinde düzmece sayım"
 
 #, c-format
 msgid "too many entries in %s"
-msgstr "%s içinde çok fazla girdi"
+msgstr "%s içinde pek fazla girdi"
 
 #, c-format
 msgid "missing config key %s"
@@ -14588,8 +14999,8 @@ msgstr "kısaltma uzunluğu erim dışında: %d"
 msgid "bad zlib compression level %d"
 msgstr "hatalı zlib sıkıştırma düzeyi %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar yalnızca bir karakter olmalı"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar yalnızca bir ASCII karakter olmalı"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -14625,10 +15036,6 @@ msgstr "'%s' başvurusu ikili bir nesneye işaret etmiyor"
 msgid "unable to resolve config blob '%s'"
 msgstr "'%s' yapılandırma ikili nesnesi çözülemiyor"
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "%s ayrıştırılamadı"
-
 msgid "unable to parse command-line config"
 msgstr "komut satırı yapılandırması ayrıştırılamıyor"
 
@@ -14699,6 +15106,10 @@ msgstr "'%s', '%s' olarak ayarlanamadı"
 msgid "invalid section name: %s"
 msgstr "geçersiz bölüm adı: %s"
 
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "'%s' içindeki haddinden uzun %<PRIuMAX>. satırla çalışma reddediliyor"
+
 #, c-format
 msgid "missing value for '%s'"
 msgstr "'%s' için değer eksik"
@@ -14753,6 +15164,16 @@ msgstr "protokol hatası: beklenmedik '%s'"
 msgid "unknown object format '%s' specified by server"
 msgstr "sunucu tarafından bilinmeyen nesne biçimi '%s' belirtildi"
 
+#, c-format
+msgid "error on bundle-uri response line %d: %s"
+msgstr "bundle-uri %d. yanıt satırında hata: %s"
+
+msgid "expected flush after bundle-uri listing"
+msgstr "bundle-uri listelemesinden sonra floş bekleniyordu"
+
+msgid "expected response end packet after ref listing"
+msgstr "başvuru listelemesinden sonra yanıt sonu paketi bekleniyordu"
+
 #, c-format
 msgid "invalid ls-refs response: %s"
 msgstr "geçersiz ls-refs yanıtı: %s"
@@ -14760,15 +15181,12 @@ msgstr "geçersiz ls-refs yanıtı: %s"
 msgid "expected flush after ref listing"
 msgstr "başvuru listelemesinden sonra floş bekleniyordu"
 
-msgid "expected response end packet after ref listing"
-msgstr "başvuru listelemesinden sonra yanıt sonu paketi bekleniyordu"
-
 #, c-format
 msgid "protocol '%s' is not supported"
 msgstr "'%s' protokolü desteklenmiyor"
 
 msgid "unable to set SO_KEEPALIVE on socket"
-msgstr "soket üzerinde SO_KEEPALIVE ayarlanamıyor"
+msgstr "yuva üzerinde SO_KEEPALIVE ayarlanamıyor"
 
 #, c-format
 msgid "Looking up %s ... "
@@ -15049,7 +15467,7 @@ msgstr "'%s' için delta adası düzenli ifadesi yüklenemedi: %s"
 #, c-format
 msgid "island regex from config has too many capture groups (max=%d)"
 msgstr ""
-"yapılandırmanın delta adası düzenli ifadesinde çok fazla yakalama grubu var "
+"yapılandırmanın delta adası düzenli ifadesinde pek fazla yakalama grubu var "
 "(en çok %d)"
 
 #, c-format
@@ -15085,9 +15503,6 @@ msgstr "arşiv yazılamadı"
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base erimlerle çalışmaz"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base yalnızca işlemelerle çalışır"
-
 msgid "unable to get HEAD"
 msgstr "HEAD alınamıyor"
 
@@ -15097,6 +15512,12 @@ msgstr "bir birleştirme temeli bulunamadı"
 msgid "multiple merge bases found"
 msgstr "birden çok birleştirme temeli bulundu"
 
+msgid "cannot compare stdin to a directory"
+msgstr "stdin, bir dizinle karşılaştırılamıyor"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "adlandırılmış bir veriyolu bir dizinle karşılaştırılamıyor"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<seçenekler>] <yol> <yol>"
 
@@ -15153,6 +15574,13 @@ msgstr ""
 msgid "external diff died, stopping at %s"
 msgstr "dış diff sonlandı, %s konumunda durdu"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow tam olarak yalnızca bir yol belirteci gerektiriyor"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "yol belirteci sihri --follow tarafından desteklenmiyor: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr "'%s', '%s', '%s' ve '%s' seçenekleri birlikte kullanılamaz"
@@ -15170,9 +15598,6 @@ msgstr ""
 "'%s' ve '%s' seçenekleri birlikte kullanılamaz, '%s' seçeneğini '%s' ve '%s' "
 "ile kullanın"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow tam olarak yalnızca bir yol belirteci gerektiriyor"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "geçersiz --stat değeri: %s"
@@ -15362,6 +15787,9 @@ msgstr "çıktının her satırının başına ek bir önek ekle"
 msgid "do not show any source or destination prefix"
 msgstr "hiçbir kaynak ve hedef önekini gösterme"
 
+msgid "use default prefixes a/ and b/"
+msgstr "a/ ve b/ ögelerinin öntanımlı öneklerini kullan"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "diff parçaları arasındaki bağlamı belirtilen satır sayısı kadar göster"
 
@@ -15504,7 +15932,7 @@ msgid "disable all output of the program"
 msgstr "tüm program çıktısını devre dışı bırak"
 
 msgid "allow an external diff helper to be executed"
-msgstr "bir dış diff yardımcısının çalıştırılmasına izin ver"
+msgstr "bir dış diff yardımcısının yürütülmesine izin ver"
 
 msgid "run external text conversion filters when comparing binary files"
 msgstr ""
@@ -15581,7 +16009,7 @@ msgstr "belirli bir dosyaya çıktıla"
 
 msgid "exhaustive rename detection was skipped due to too many files."
 msgstr ""
-"Geniş kapsamlı yeniden adlandırma algılaması çok fazla dosya olmasından "
+"Geniş kapsamlı yeniden adlandırma algılaması pek fazla dosya olmasından "
 "dolayı atlandı."
 
 msgid "only found copies from modified paths due to too many files."
@@ -15659,6 +16087,14 @@ msgstr "git dizini '%s' konumundan '%s' konumuna göç ettirilemedi"
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "İpucu: Düzenleyicinizin dosyayı kapatması bekleniyor...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "şuraya yazılamadı: '%s'"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "'%s' düzenlenemedi"
+
 msgid "Filtering content"
 msgstr "İçerik süzülüyor"
 
@@ -15672,7 +16108,7 @@ msgstr "hatalı git ad alanı yolu \"%s\""
 
 #, c-format
 msgid "too many args to run %s"
-msgstr "%s çalıştırmak için çok fazla argüman"
+msgstr "%s çalıştırmak için pek fazla argüman"
 
 msgid "git fetch-pack: expected shallow list"
 msgstr "git fetch-pack: sığ bir liste bekleniyordu"
@@ -15902,9 +16338,10 @@ msgstr "sanal depo '%s', fsmonitor ile uyumsuz"
 
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
-"Unix soketleri eksik olduğundan dolayı; '%s' deposu, fsmonitor ile uyumsuz"
+"yuva dizini '%s', Unix yuva dasteği olmadığından dolayı fsmonitor ile uyumsuz"
 
 msgid ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
@@ -15912,16 +16349,14 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 "git [-v | --version] [-h | --help] [-C <yol>] [-c <ad>=<değer>]\n"
 "           [--exec-path[=<yol>]] [--html-path] [--man-path] [--info-path]\n"
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<yol>] [--work-tree=<yol>] [--namespace=<ad>]\n"
-"           [--super-prefix=<yol>] [--config-env=<ad>=<çevredeğişkeni>]\n"
-"           <komut> [<argümanlar>]"
+"           [--config-env=<ad>=<çevre-değişkeni>] <komut> [<argümanlar>]"
 
 msgid ""
 "'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -15946,10 +16381,6 @@ msgstr "'%s' seçeneği için bir dizin verilmedi\n"
 msgid "no namespace given for --namespace\n"
 msgstr "--namespace için ad alanı verilmedi\n"
 
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "--super-prefix için önek verilmedi\n"
-
 #, c-format
 msgid "-c expects a configuration string\n"
 msgstr "-c bir yapılandırma dizisi bekliyor\n"
@@ -15958,6 +16389,10 @@ msgstr "-c bir yapılandırma dizisi bekliyor\n"
 msgid "no config key given for --config-env\n"
 msgstr "--config-env için bir yapılandırma anahtarı verilmedi\n"
 
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "--attr-source için öznitelik kaynağı verilmedi\n"
+
 #, c-format
 msgid "unknown option: %s\n"
 msgstr "bilinmeyen seçenek: %s\n"
@@ -16065,8 +16500,13 @@ msgstr ""
 msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "gpg.ssh.defaultKeyCommand başarısız oldu: %s %s"
 
-msgid "gpg failed to sign the data"
-msgstr "gpg veriyi imzalayamadı"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg veriyi imzalayamadı:\n"
+"%s"
 
 msgid "user.signingKey needs to be set for ssh signing"
 msgstr "user.signingKey'in ssh imzalaması için ayarlanması gerekiyor"
@@ -16189,7 +16629,7 @@ msgid ""
 "able to execute it. Maybe git-%s is broken?"
 msgstr ""
 "'%s' bir git komutu gibi görünüyor; ancak biz onu\n"
-"çalıştıramadık. git-%s bozuk olabilir mi?"
+"yürütemedik. git-%s bozuk olabilir mi?"
 
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
@@ -16228,8 +16668,8 @@ msgstr[1] ""
 "\n"
 "Buna en yakın komutlar:"
 
-msgid "git version [<options>]"
-msgstr "git version [<seçenekler>]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #, c-format
 msgid "%s: %s - %s"
@@ -16253,7 +16693,7 @@ msgid ""
 "The '%s' hook was ignored because it's not set as executable.\n"
 "You can disable this warning with `git config advice.ignoredHook false`."
 msgstr ""
-"'%s' kancası yok sayıldı; çünkü bir çalıştırılabilir olarak ayarlanmamış.\n"
+"'%s' kancası yok sayıldı; çünkü bir yürütülebilir olarak ayarlanmamış.\n"
 "Bu uyarıyı 'git config advice.ignoredHook false' ile kapatabilirsiniz."
 
 #, c-format
@@ -16478,12 +16918,12 @@ msgstr ""
 "%s altmodülü birleştirilemedi; ancak birden çok olası birleştirmeler var:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "İç birleştirme çalıştırılamadı"
+msgid "failed to execute internal merge"
+msgstr "iç birleştirme yürütülemedi"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "%s veritabanına eklenemedi"
+msgid "unable to add %s to database"
+msgstr "%s veritabanına eklenemiyor"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -16833,8 +17273,8 @@ msgstr ""
 
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
 "ÇAKIŞMA (y. adlandır/y. adlandır): \"%s\"->\"%s\" olarak adlandır (\"%s\" "
 "dalında), \"%s\"->\"%s\" olarak adlandır (\"%s\"%s içinde)"
@@ -16845,8 +17285,8 @@ msgstr " (çözülmeden bırakıldı)"
 #, c-format
 msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
 msgstr ""
-"ÇAKIŞMA (y. adlandır/y. adlandır): %s->%s olarak adlandır (%s içinde). %s->"
-"%s olarak adlandır (%s içinde)"
+"ÇAKIŞMA (y. adlandır/y. adlandır): %s->%s olarak adlandır (%s içinde). %s-"
+">%s olarak adlandır (%s içinde)"
 
 #, c-format
 msgid ""
@@ -16929,6 +17369,12 @@ msgstr "önbellek okunamadı"
 msgid "multi-pack-index OID fanout is of the wrong size"
 msgstr "multi-pack-index OID ikiye bölümünün boyutu hatalı"
 
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "multi-pack-index OID arama iri parçası yanlış boyutlu"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "multi-pack-index OID nesne ofseti iri parçası yanlış boyutlu"
+
 #, c-format
 msgid "multi-pack-index file %s is too small"
 msgstr "multi-pack-index dosyası %s pek küçük"
@@ -16945,17 +17391,21 @@ msgstr "multi-pack-index sürümü %d tanımlanamıyor"
 msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "multi-pack-index sağlama sürümü %u, %u sürümü ile eşleşmiyor"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "multi-pack-index'ten gerekli pack-name iri parçası eksik"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "multi-pack-index'ten gerekli pack-name iri parçası eksik veya hasarlı"
+
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr "multi-pack-index'ten gerekli OID fanout iri parçası eksik veya hasarlı"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "multi-pack-index'ten gerekli OID fanout iri parçası eksik"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr "multi-pack-index'ten gerekli OID arama iri parçası eksik veya hasarlı"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "multi-pack-index'ten gerekli OID arama iri parçası eksik"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"multi-pack-index'ten gerekli nesne ofsetleri iri parçası eksik veya hasarlı"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "multi-pack-index'ten gerekli nesne ofsetleri iri parçası eksik"
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "multi-pack-index pack-name iri parçası pek kısa"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -16968,6 +17418,9 @@ msgstr "hatalı pack-int-id: %u (%u toplam paket)"
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr "multi-pack-index bir 64 bit ofset depoluyor; ancak off_t pek küçük"
 
+msgid "multi-pack-index large offset out of bounds"
+msgstr "multi-pack-index geniş ofseti sınırlar dışında"
+
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "paket dosyası '%s' eklenemedi"
@@ -17148,10 +17601,6 @@ msgstr "alternatif nesne yolu olağanlaştırılamıyor: %s"
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s: alternatif nesne depoları yok sayılıyor, iç içe geçme pek derin"
 
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "nesne dizini olağanlaştırılamıyor: %s"
-
 msgid "unable to fdopen alternates lockfile"
 msgstr "alternatifler kilit dosyası fdopen yapılamıyor"
 
@@ -17210,6 +17659,10 @@ msgstr "hasarlı gevşek nesne '%s'"
 msgid "garbage at end of loose object '%s'"
 msgstr "gevşek nesne '%s' sonunda anlamsız veri"
 
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "gevşek nesne %s açılamıyor"
+
 #, c-format
 msgid "unable to parse %s header"
 msgstr "%s üstbilgisi ayrıştırılamıyor"
@@ -17226,17 +17679,13 @@ msgid "header for %s too long, exceeds %d bytes"
 msgstr "%s üstbilgisi pek uzun, %d bayt'ı aşıyor"
 
 #, c-format
-msgid "failed to read object %s"
-msgstr "%s nesnesi okunamadı"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "%s gevşek nesnesi (%s içinde depolanıyor) hasarlı"
 
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "%s yedeği %s için bulunamadı"
 
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "%s gevşek nesnesi (%s içinde depolanıyor) hasarlı"
-
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr "paketlenmiş nesne %s (%s içinde depolanıyor) hasarlı"
@@ -17249,9 +17698,6 @@ msgstr "%s dosyası yazılamıyor"
 msgid "unable to set permission to '%s'"
 msgstr "'%s' ögesine izin ayarlanamıyor"
 
-msgid "file write error"
-msgstr "dosya yazım hatası"
-
 msgid "error when closing loose object file"
 msgstr "gevşek nesne dosyası kapatılırken hata"
 
@@ -17297,11 +17743,12 @@ msgstr "%s dizini oluşturulamıyor"
 msgid "cannot read object for %s"
 msgstr "%s için nesne okunamıyor"
 
-msgid "corrupt commit"
-msgstr "hasarlı işleme"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "nesne fsck'yi başarısız ediyor: %s"
 
-msgid "corrupt tag"
-msgstr "hasarlı etiket"
+msgid "refusing to create malformed object"
+msgstr "hatalı oluşturulmuş nesne oluşturma reddediliyor"
 
 #, c-format
 msgid "read error while indexing %s"
@@ -17565,10 +18012,6 @@ msgstr "biteşlem paket indeksinde geçersi XOR ofseti"
 msgid "cannot fstat bitmap file"
 msgstr "biteşlem dosyası fstat yapılamıyor"
 
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "ek biteşlem dosyası yok sayılıyor: '%s'"
-
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "sağlama toplamı, MIDX ve biteşlem içinde uymuyor"
 
@@ -17635,6 +18078,10 @@ msgstr "öge bulunamadı: '%s'; '%s' paketinde, %<PRIuMAX> ofsetinde"
 msgid "unable to get disk usage of '%s'"
 msgstr "'%s' ögesinin disk kullanımı alınamadı"
 
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "biteşlem dosyası '%s', geçersiz sağlama toplamına iye"
+
 #, c-format
 msgid "mtimes file %s is too small"
 msgstr "mtimes dosyası %s pek küçük"
@@ -17675,6 +18122,16 @@ msgstr "%s reverse-index dosyasının sürümü %<PRIu32> desteklenmiyor"
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "%s reverse-index dosyasının sağlama numarası %<PRIu32> desteklenmiyor"
 
+msgid "invalid checksum"
+msgstr "geçersiz sağlama toplamı"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "%<PRIu64> konumunda geçersiz rev-index konumu: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "multi-pack-index reverse-index iri parçası yanlış boyutlu"
+
 msgid "cannot both write and verify reverse index"
 msgstr "ters indeks dosyası hem yazılıp hem doğrulanamıyor"
 
@@ -17725,14 +18182,6 @@ msgstr "'%s' seçeneği \"%s\" veya \"%s\" bekliyor"
 msgid "%s requires a value"
 msgstr "%s bir değer gerektiriyor"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s, %s ile uyumsuz"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s: başka bir şeyle uyumsuz"
-
 #, c-format
 msgid "%s takes no value"
 msgstr "%s bir değer almıyor"
@@ -17745,6 +18194,10 @@ msgstr "%s kullanılabilir değil"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s negatif olmayan bir tamsayı bekliyor, isteğe bağlı k/m/g eki ile"
 
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s, %s ile uyumsuz"
+
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "belirsiz seçenek: %s (--%s%s veya --%s%s olabilir)"
@@ -17816,6 +18269,10 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-SAYI"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "--no-%s karşıtı"
+
 msgid "expiry-date"
 msgstr "son kullanım tarihi"
 
@@ -17831,6 +18288,9 @@ msgstr "daha sessiz ol"
 msgid "use <n> digits to display object names"
 msgstr "nesne adlarını görüntülemek için <n> basamak kullan"
 
+msgid "prefixed path to initial superproject"
+msgstr "yol, ilk üst projeye öneklendi"
+
 msgid "how to strip spaces and #comments from message"
 msgstr "iletiden boşlukları ve #yorumları çıkart"
 
@@ -17840,7 +18300,15 @@ msgstr "yol belirtecini dosyadan oku"
 msgid ""
 "with --pathspec-from-file, pathspec elements are separated with NUL character"
 msgstr ""
-"--pathspec-from-file ile, yol belirteci ögeleri NUL karakteri ile ayrılır"
+"--pathspec-from-file ile yol belirteci ögeleri NUL karakteri ile ayrılır"
+
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "hatalı Boole çevre değeri '%s', '%s' için"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "%s ayrıştırılamadı"
 
 #, c-format
 msgid "Could not make %s writable by group"
@@ -17888,6 +18356,10 @@ msgstr "Yerine getirilmemiş yol belirteci sihri '%c' ('%s' içinde)"
 msgid "%s: 'literal' and 'glob' are incompatible"
 msgstr "%s: 'literal' ve 'glob' birbiriyle uyumsuz"
 
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s', dizin ağacının dışında"
+
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: '%s', '%s' konumunda depo dışında"
@@ -17974,6 +18446,10 @@ msgstr "promisor-remote: alt sürecine getirmek için stdin kapatılamıyor"
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr "vaatçi uzak konum adı '/' ile başlayamaz: %s"
 
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "vaatçi uzak konumundan %s getirilemedi"
+
 msgid "object-info: expected flush after arguments"
 msgstr "object-info: argümanlardan sonra floş bekleniyordu"
 
@@ -18008,6 +18484,13 @@ msgstr "diff oluşturulamadı"
 msgid "could not parse log for '%s'"
 msgstr "'%s' günlüğü ayrıştırılamadı"
 
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "geçersiz ek süprüntü ucu: '%s'"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "ek son kullanılan nesneler numaralandırılamıyor"
+
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr "dosya arması '%s' eklenmeyecek ('%s' indekste halihazırda var)"
@@ -18139,10 +18622,6 @@ msgstr "bir aralıklı indeks için bölünmüş indeks yazılamıyor"
 msgid "failed to convert to a sparse-index"
 msgstr "bir sparse-index'e dönüştürülemedi"
 
-#, c-format
-msgid "could not stat '%s'"
-msgstr "'%s' bilgileri alınamadı"
-
 #, c-format
 msgid "unable to open git dir: %s"
 msgstr "git dizini açılamıyor: %s"
@@ -18159,6 +18638,14 @@ msgstr "'%s' üzerindeki izin bitleri onarılamıyor"
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: #0 numaralı hazırlama alanına bırakılamıyor"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "beklenmedik diff durumu %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "kaldır: '%s'\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -18225,7 +18712,7 @@ msgstr ""
 "                      bir yer tutucu izle. <baş>, yeniden temellendirmenin\n"
 "                      sonunda güncellenir\n"
 "\n"
-"Bu satırlar yeniden sıralanabilirler, yukarıdan aşağıya çalıştırılırlar.\n"
+"Bu satırlar yeniden sıralanabilirler, yukarıdan aşağıya yürütülürler.\n"
 
 #, c-format
 msgid "Rebase %s onto %s (%d command)"
@@ -18318,6 +18805,14 @@ msgstr "%d arkasında"
 msgid "ahead %d, behind %d"
 msgstr "%d önünde, %d arkasında"
 
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) argüman almıyor"
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "tanımlanamayan %%(%.*s) argümanı: %s"
+
 #, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "beklenen biçim: %%(color:<renk>)"
@@ -18335,41 +18830,37 @@ msgid "Integer value expected refname:rstrip=%s"
 msgstr "Tamsayı değeri şunu bekliyordu: refname:rstrip=%s"
 
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "tanımlanamayan %%(%s) argümanı: %s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "%%(trailers:key=<değer>) bekleniyordu"
 
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) argüman almıyor"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "bilinmeyen %%(trailers) argümanı: %s"
 
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) argüman almıyor"
+msgid "positive value expected contents:lines=%s"
+msgstr "pozitif değer şunu bekliyordu: contents:lines=%s"
 
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) argüman almıyor"
+msgid "argument expected for %s"
+msgstr "%s için argüman bekleniyordu"
 
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "%%(trailers:key=<değer>) bekleniyordu"
+msgid "positive value expected %s=%s"
+msgstr "pozitif değer şunu bekliyordu: %s=%s"
 
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "bilinmeyen %%(trailers) argümanı: %s"
+msgid "cannot fully parse %s=%s"
+msgstr "tümüyle ayrıştırılamıyor: %s=%s"
 
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "pozitif değer şunu bekliyordu: contents:lines=%s"
+msgid "value expected %s="
+msgstr "değer şunu bekliyordu: %s="
 
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "pozitif değer şurada '%s' bekliyordu: %%(%s)"
 
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "tanımlanamayan e-posta seçeneği: %s"
-
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
 msgstr "beklenen biçim: %%(align:<genişlik>,<konum>)"
@@ -18382,13 +18873,17 @@ msgstr "tanımlanamayan konum:%s"
 msgid "unrecognized width:%s"
 msgstr "tanımlanamayan genişlik:%s"
 
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "tanımlanamayan %%(%s) argümanı: %s"
+
 #, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "pozitif genişlik %%(align) ögeciği ile birlikte bekleniyordu"
 
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) argüman almıyor"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "beklenen biçim: %%(ahead-behind:<işlememsi>)"
 
 #, c-format
 msgid "malformed field name: %.*s"
@@ -18436,6 +18931,9 @@ msgstr "bu komut atom %%(%.*s) reddediyor"
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s, --python, --shell ve --tcl ile kullanılamaz"
 
+msgid "failed to run 'describe'"
+msgstr "'describe' çalıştırılamadı"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(dal yok, %s yeniden temellendiriliyor)"
@@ -18497,6 +18995,9 @@ msgstr "anahtar"
 msgid "field name to sort on"
 msgstr "üzerine sıralanacak alan adı"
 
+msgid "exclude refs which match pattern"
+msgstr "dizgiyle eşleşen başvuruları dışarıda bırak"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "bir başvuru günlüğü değil: %s"
@@ -18938,8 +19439,11 @@ msgstr[1] ""
 "Sizin dalınız ve '%s' birbirinden uzaklaşmış ve sırasıyla\n"
 "her birinde %d ve %d işleme var.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (uzak dalı kendi dalınıza birleştirmek için \"git pull\" kullanın)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr ""
+"  (uzak dalı kendi dalınızla birleştirmek istiyorsanız \"git pull\" "
+"kullanın)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
@@ -19011,10 +19515,6 @@ msgstr "'%s' içindeki çakışan durum güncellenemedi"
 msgid "no remembered resolution for '%s'"
 msgstr "'%s' için hatırlanan çözüm yok"
 
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "'%s' bağlantısı kesilemiyor"
-
 #, c-format
 msgid "Updated preimage for '%s'"
 msgstr "'%s' için öngörüntü güncellendi"
@@ -19037,6 +19537,13 @@ msgstr "HEAD revizyonu saptanamadı"
 msgid "failed to find tree of %s"
 msgstr "%s ögesinin ağacı bulunamadı"
 
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "gizli başvurular için desteklenmeyen bölüm: %s"
+
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden=, birden çok kez geçirildi"
+
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
 msgstr "resolve-undo, kayıp olan '%s' ögesini kaydetmiş"
@@ -19048,6 +19555,10 @@ msgstr "soy yolu argümanı %s için işleme alınamadı"
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<paketdosyası> artık desteklenmiyor"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "--stdin kipinde geçersiz seçenek '%s'"
+
 msgid "your current branch appears to be broken"
 msgstr "geçerli dalınız bozuk gibi görünüyor"
 
@@ -19133,8 +19644,15 @@ msgstr "klonlama sırasında tam çalışma dizini oluştur"
 msgid "only download metadata for the branch that will be checked out"
 msgstr "yalnızca çıkış yapılacak dalın üstverisini indir"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<seçenekler>] [--] <depo> [<dizin>]"
+msgid "create repository within 'src' directory"
+msgstr "'src' dizininde depo oluştur"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <ana-dal>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<yazılma>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19181,8 +19699,32 @@ msgid "--all or <enlistment>, but not both"
 msgstr "--all veya <gönüllükayıt>; ancak ikisi değil"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "git deposu '%s' içinde gitti"
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "eskimiş scalar.repo '%s' kaldırılamadı"
+
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "eskimiş scalar.repo '%s' kaldırıldı"
+
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "'%s' konumundaki deponun sahibi başkası"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "'%s' konumundaki depoda bir biçim sorunu var"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "'%s' konumunda depo bulunamadı"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"bu deponun kaydını Scalar'dan kaldırmak için şu komutu çalıştırın:\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -19333,10 +19875,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "'%s' kilitlenemedi"
 
-#, c-format
-msgid "could not write to '%s'"
-msgstr "şuraya yazılamadı: '%s'"
-
 #, c-format
 msgid "could not write eol to '%s'"
 msgstr "satır sonu şuraya yazılamadı: '%s'"
@@ -19586,10 +20124,6 @@ msgstr "%s işlemesinin iletisi alınamıyor"
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s: üst işleme %s ayrıştırılamıyor"
 
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "'%s', '%s' olarak yeniden adlandırılamadı"
-
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "%s geri alınamadı... %s"
@@ -19610,6 +20144,23 @@ msgstr "git %s: indeks okunamadı"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: indeks yenilenemedi"
 
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "'%s', geçerli bir etiket değil"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "'%s', geçerli bir başvuru adı değil"
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr ""
+"update-ref, tümüyle kalifiye bir başvuru adı gerektiriyor; örn. refs/heads/%s"
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "geçersiz komut %.*s"
+
 #, c-format
 msgid "%s does not accept arguments: '%s'"
 msgstr "%s argüman kabul etmiyor: '%s'"
@@ -19680,9 +20231,6 @@ msgstr "\"git cherry-pick (--continue | %s--abort | --quit)\" deneyin"
 msgid "could not create sequencer directory '%s'"
 msgstr "ardıştırıcı dizini '%s' oluşturulamadı"
 
-msgid "could not lock HEAD"
-msgstr "HEAD kilitlenemedi"
-
 msgid "no cherry-pick or revert in progress"
 msgstr "süren bir seç-al veya geri al yok"
 
@@ -19761,7 +20309,7 @@ msgstr "%.*s birleştirilemedi"
 
 #, c-format
 msgid "Executing: %s\n"
-msgstr "Çalıştırılıyor: %s\n"
+msgstr "Yürütülüyor: %s\n"
 
 #, c-format
 msgid ""
@@ -19771,26 +20319,26 @@ msgid ""
 "  git rebase --continue\n"
 "\n"
 msgstr ""
-"Çalıştırma başarısız: %s\n"
+"Yürütme başarısız: %s\n"
 "%sSorunu çözüp sürdürmek için şunu çalıştırın:\n"
 "\n"
 "\tgit rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "ve indekse ve/veya çalışma ağacına değişiklikler yapıldı\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "ve indekse ve/veya çalışma ağacına değişiklikler yapıldı.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
-"Çalıştırma başarılı oldu: %s,\n"
-"ancak indeksinize ve/veya çalışma ağacınıza değişiklikler bıraktı\n"
+"Yürütme başarılı oldu: %s;\n"
+"ancak indeksinize ve/veya çalışma ağacınıza değişiklikler bıraktı.\n"
 "Değişikliklerinizi işleyin veya zulalayın, ardından şunu çalıştırın:\n"
 "\n"
 "\tgit rebase --continue\n"
@@ -19800,16 +20348,16 @@ msgstr ""
 msgid "illegal label name: '%.*s'"
 msgstr "izin verilmeyen etiket adı: '%.*s'"
 
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "'%s' çözülemedi"
+
 msgid "writing fake root commit"
 msgstr "sahte kök işlemesi yazılıyor"
 
 msgid "writing squash-onto"
 msgstr "squash-onto yazılıyor"
 
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "'%s' çözülemedi"
-
 msgid "cannot merge without a current revision"
 msgstr "güncel bir revizyon olmadan birleştirilemiyor"
 
@@ -19822,7 +20370,7 @@ msgid "nothing to merge: '%.*s'"
 msgstr "birleştirilecek bir şey yok: '%.*s'"
 
 msgid "octopus merge cannot be executed on top of a [new root]"
-msgstr "ahtapot birleştirmesi bir [yeni kök]ün üzerinde çalıştırılamaz"
+msgstr "ahtapot birleştirmesi bir [yeni kök]ün üzerinde yürütülemez"
 
 #, c-format
 msgid "could not get commit message of '%s'"
@@ -19920,7 +20468,7 @@ msgid ""
 "    git rebase --edit-todo\n"
 "    git rebase --continue\n"
 msgstr ""
-"todo komutu çalıştırılamadı.\n"
+"todo komutu yürütülemedi.\n"
 "\n"
 "\t%.*s\n"
 "Yeniden zamanlandı; sürdürmeden önce komutu düzenlemek için lütfen\n"
@@ -19929,14 +20477,14 @@ msgstr ""
 "\tgit rebase --edit-todo\n"
 "\tgit rebase --continue\n"
 
-#, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Yeniden temellendiriliyor: (%d/%d)%s"
-
 #, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "%s konumunda durdu... %.*s\n"
 
+#, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Yeniden temellendiriliyor: (%d/%d)%s"
+
 #, c-format
 msgid "unknown command %d"
 msgstr "bilinmeyen komut %d"
@@ -20119,58 +20667,129 @@ msgid "cannot come back to cwd"
 msgstr "cwd'ye geri dönülemiyor"
 
 #, c-format
-msgid "failed to stat '%*s%s%s'"
-msgstr "'%*s%s%s' bilgileri alınamadı"
-
-msgid "Unable to read current working directory"
-msgstr "Şu anki çalışma dizini okunamıyor"
+msgid "failed to stat '%*s%s%s'"
+msgstr "'%*s%s%s' bilgileri alınamadı"
+
+msgid "Unable to read current working directory"
+msgstr "Şu anki çalışma dizini okunamıyor"
+
+#, c-format
+msgid "cannot change to '%s'"
+msgstr "şuraya değiştirilemiyor: '%s'"
+
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "bir git deposu (veya üst dizinlerinden birisi) değil: %s"
+
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"Bir git deposu veya (%s bağlama noktasına kadar olan üstü) değil\n"
+"Dosya sistemi sınırında duruluyor (GIT_DISCOVERY_ACROSS_FILESYSTEM "
+"ayarlanmamış)."
+
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"'%s' konumundaki depoda belirsiz iyelik algılandı\n"
+"%sBu dizin için istisna eklemek için şunu çağırın:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr "çıplak depo '%s', kullanılamaz (safe.bareRepository '%s')"
+
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"core.sharedRepository dosya kipi değeri ile sorun (0%.3o).\n"
+"Dosyaların sahibinin her zaman okuma ve yazma izni olması gerekir."
+
+msgid "fork failed"
+msgstr "çatallama başarısız"
+
+msgid "setsid failed"
+msgstr "setsid başarısız"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "'%s' şablonunun bilgileri alınamıyor"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "'%s' opendir yapılamıyor"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "'%s' readlink yapılamıyor"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "'%s', '%s' ögesine sembolik bağla bağlanamıyor"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "'%s' şuraya kopyalanamıyor: '%s'"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "%s şablonu yok sayılıyor"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "şablonlar %s içinde bulunamadı"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "şablonlar '%s' konumundan kopyalanmıyor: %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "geçersiz başlangıç dalı adı: '%s'"
 
 #, c-format
-msgid "cannot change to '%s'"
-msgstr "şuraya değiştirilemiyor: '%s'"
+msgid "unable to handle file type %d"
+msgstr "%d dosya türü ele alınamıyor"
 
 #, c-format
-msgid "not a git repository (or any of the parent directories): %s"
-msgstr "bir git deposu (veya üst dizinlerinden birisi) değil: %s"
+msgid "unable to move %s to %s"
+msgstr "%s şuraya taşınamıyor: %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "depoyu başka bir sağlama ile yeniden ilklendirme deneniyor"
 
 #, c-format
-msgid ""
-"not a git repository (or any parent up to mount point %s)\n"
-"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
-msgstr ""
-"Bir git deposu veya (%s bağlama noktasına kadar olan üstü) değil\n"
-"Dosya sistemi sınırında duruluyor (GIT_DISCOVERY_ACROSS_FILESYSTEM "
-"ayarlanmamış)."
+msgid "%s already exists"
+msgstr "%s halihazırda var"
 
 #, c-format
-msgid ""
-"detected dubious ownership in repository at '%s'\n"
-"%sTo add an exception for this directory, call:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
-msgstr ""
-"'%s' konumundaki depoda belirsiz iyelik algılandı\n"
-"%sBu dizin için istisna eklemek için şunu çağırın:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: --initial-branch=%s yok sayıldı"
 
 #, c-format
-msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr "çıplak depo '%s', kullanılamaz (safe.bareRepository '%s')"
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "%s%s içindeki var olan paylaşılan Git deposu yeniden ilklendirildi\n"
 
 #, c-format
-msgid ""
-"problem with core.sharedRepository filemode value (0%.3o).\n"
-"The owner of files must always have read and write permissions."
-msgstr ""
-"core.sharedRepository dosya kipi değeri ile sorun (0%.3o).\n"
-"Dosyaların sahibinin her zaman okuma ve yazma izni olması gerekir."
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "%s%s içindeki var olan Git deposu yeniden ilklendirildi\n"
 
-msgid "fork failed"
-msgstr "çatallama başarısız"
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "%s%s içinde paylaşılan boş Git deposu ilklendirildi\n"
 
-msgid "setsid failed"
-msgstr "setsid başarısız"
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "%s%s içinde boş Git deposu ilklendirildi\n"
 
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -20223,10 +20842,6 @@ msgid_plural "%u bytes/s"
 msgstr[0] "%u bayt/sn"
 msgstr[1] "%u bayt/sn"
 
-#, c-format
-msgid "could not edit '%s'"
-msgstr "'%s' düzenlenemedi"
-
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "kuşku doğuran altmodül yok sayılıyor: %s"
@@ -20405,6 +21020,25 @@ msgstr "ls-tree beklenmedik bir biçimde %d kodu ile çıktı"
 msgid "failed to lstat '%s'"
 msgstr "'%s', lstat yapılamadı"
 
+msgid "no remote configured to get bundle URIs from"
+msgstr "demet URI'lerini almak için bir uzak konum yapılandırılmamış"
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "'%s' uzak konumunun yapılandırılmış bir URL'si yok"
+
+msgid "could not get the bundle-uri list"
+msgstr "bundle-uri listesi alınamadı"
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <seçenekler> (control|prime|update)"
+
+msgid "clear the cache tree before each iteration"
+msgstr "her bir yinelemeden önce önbellek ağacını temizle"
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr "önbellek ağacındaki geçersizleştirilecek girdi sayısı (öntanımlı 0)"
+
 msgid "unhandled options"
 msgstr "beklenmeyen seçenekler"
 
@@ -20416,7 +21050,7 @@ msgid "commit %s is not marked reachable"
 msgstr "%s işlemesi ulaşılabilir olarak imlenmedi"
 
 msgid "too many commits marked reachable"
-msgstr "çok fazla işleme ulaşılabilir olarak imlenmiş"
+msgstr "pek fazla işleme ulaşılabilir olarak imlenmiş"
 
 msgid "test-tool serve-v2 [<options>]"
 msgstr "test-tool serve-v2 [<seçenekler>]"
@@ -20747,6 +21381,12 @@ msgstr "İptal ediliyor."
 msgid "failed to push all needed submodules"
 msgstr "gereken tüm altmodüller itilemedi"
 
+msgid "bundle-uri operation not supported by protocol"
+msgstr "bundle-uri işlemi protokol tarafından desteklenmiyor"
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "sunucu tarafından tanıtılan bundle-uri listesi alınamadı"
+
 msgid "too-short tree object"
 msgstr "ağaç nesnesi çok kısa"
 
@@ -21133,6 +21773,9 @@ msgstr "'%s' erişilemiyor"
 msgid "unable to get current working directory"
 msgstr "geçerli çalışma dizini alınamıyor"
 
+msgid "unable to get random bytes"
+msgstr "rastgele baytlar alınamıyor"
+
 msgid "Unmerged paths:"
 msgstr "Birleştirilmemiş yollar:"
 
@@ -21175,8 +21818,8 @@ msgstr "  (işlenecekleri güncellemek için \"git add/rm <dosya>...\" kullanın
 msgid ""
 "  (use \"git restore <file>...\" to discard changes in working directory)"
 msgstr ""
-"  (çalışma dizinindeki değişiklikleri atmak için \"git restore <dosya>...\" "
-"kullanın)"
+"  (çalışma dizinindeki değişiklikleri atmak için\n"
+"    \"git restore <dosya>...\" kullanın)"
 
 msgid "  (commit or discard the untracked or modified content in submodules)"
 msgstr "  (altmodüllerdeki izlenmeyen/değiştirilen içeriği gönder veya at)"
@@ -21476,13 +22119,18 @@ msgstr "Yok sayılan dosyalar"
 
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
 msgstr ""
-"İzlenmeyen dosyaların ortaya dökülmesi %.2f saniye sürdü. 'status -uno'\n"
-"bunu hızlandırabilir; ancak yeni dosyaları eklemeyi unutmamanız\n"
-"konusunda dikkatli olmalısınız (ek bilgi için 'git help status')."
+"İzlenmeyen dosyaları ortaya dökme %.2f saniye sürdü;\n"
+"ancak sonuçlar önbelleğe alındı ve sonrakiler daha hızlı olabilir."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "İzlenmeyen dosyaları ortaya dökme %.2f saniye sürdü."
+
+msgid "See 'git help status' for information on how to improve this."
+msgstr "Bunu iyileştirme üzerine bilgi için 'git help status'a bakın."
 
 #, c-format
 msgid "Untracked files not listed%s"
@@ -21509,8 +22157,8 @@ msgid ""
 "nothing added to commit but untracked files present (use \"git add\" to "
 "track)\n"
 msgstr ""
-"işlemeye bir şey eklenmedi; ancak izlenmeyen dosyalar var (izlemek için "
-"\"git add\" kullanın)\n"
+"işlemeye bir şey eklenmedi; ancak izlenmeyen dosyalar var\n"
+"  (izlemek için \"git add\" kullanın)\n"
 
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
@@ -21519,8 +22167,8 @@ msgstr "işlemeye bir şey eklenmedi; ancak izlenmeyen dosyalar var\n"
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
 msgstr ""
-"İşlenecek bir şey yok (dosyalar oluşturun/kopyalayın ve izlemek için \"git "
-"add\" kullanın)\n"
+"İşlenecek bir şey yok\n"
+"  (dosyalar oluşturun/kopyalayın ve izlemek için \"git add\" kullanın)\n"
 
 #, c-format
 msgid "nothing to commit\n"
@@ -21626,275 +22274,6 @@ msgstr "Bu komutu çalışma ağacının en üst düzeyinden çalıştırmanız
 msgid "Unable to determine absolute path of git directory"
 msgstr "Git dizininin kesin yolu algılanamıyor"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %12s %s"
-
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "%d yola dokunuldu\n"
-msgstr[1] "%d yola dokunuldu\n"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-"Eğer yama sorunsuzca uygulanırsa düzenlenen parça derhal hazırlama\n"
-"için imlenecektir."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-"Eğer yama sorunsuzca uygulanırsa düzenlenen parça derhal zulalama\n"
-"için imlenecektir."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-"Eğer yama sorunsuzca uygulanırsa, düzenlenen parça derhal hazırlıktan\n"
-"çıkarılma için imlenecektir."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-"Eğer yama sorunsuzca uygulanırsa düzenlenen parça derhal uygulama\n"
-"için imlenecektir."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-"Eğer yama sorunsuzca uygulanırsa düzenlenen parça derhal ıskartaya\n"
-"çıkarım için imlenecektir."
-
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "parça düzenleme dosyası yazım için açılamadı: %s"
-
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"'%s' satır kaldırmak için onları ' ' satır yapın (bağlam).\n"
-"'%s' satır kaldırmak için onları silin.\n"
-"%s ile başlayan satırlar kaldırılacaktır.\n"
-
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "parça düzenleme dosyası okuma için açılamadı: %s"
-
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - bu parçayı hazırla\n"
-"n - bu parçayı hazırlama\n"
-"q - çık; bu parçayı veya kalanlardan herhangi birini hazırlama\n"
-"a - bu parçayı ve sonraki tüm parçaları hazırla\n"
-"d - bu parçayı veya sonraki parçalardan herhangi birini hazırlama"
-
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - bu parçayı zulala\n"
-"n - bu parçayı zulalama\n"
-"q - çık; bu parçayı veya kalanlardan herhangi birini zulalama\n"
-"a - bu parçayı ve sonraki tüm parçaları zulala\n"
-"d - bu parçayı veya sonraki parçalardan herhangi birini zulalama"
-
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - bu parçayı hazırlıktan çıkar\n"
-"n - bu parçayı hazırlıktan çıkarma\n"
-"q - çık; bu parçayı veya kalanlardan herhangi birini hazırlıktan çıkarma\n"
-"a - bu parçayı ve sonraki tüm parçaları hazırlıktan çıkar\n"
-"d - bu parçayı veya sonraki parçalardan herhangi birini hazırlıktan çıkarma"
-
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - bu parçayı indekse uygula\n"
-"n - bu parçayı indekse uygulama\n"
-"q - çık; bu parçayı veya kalanlardan herhangi birini uygulama\n"
-"a - bu parçayı ve sonraki tüm parçaları uygula\n"
-"d - bu parçayı veya sonraki parçalardan herhangi birini uygulama"
-
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - bu parçayı çalışma ağacından at\n"
-"n - bu parçayı çalışma ağacından atma\n"
-"q - çık; bu parçayı veya kalanlardan herhangi birini atma\n"
-"a - bu parçayı ve sonraki tüm parçaları at\n"
-"d - bu parçayı veya sonraki parçalardan herhangi birini atma"
-
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - bu parçayı çalışma ağacından ve indeksten at\n"
-"n - bu parçayı çalışma ağacından ve indeksten atma\n"
-"q - çık; bu parçayı veya kalanlardan herhangi birini atma\n"
-"a - bu parçayı ve sonraki tüm parçaları at\n"
-"d - bu parçayı veya sonraki parçalardan herhangi birini atma"
-
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - bu parçayı indekse ve çalışma ağacına uygula\n"
-"n - bu parçayı indekse ve çalışma ağacına uygulama\n"
-"q - çık; bu parçayı veya kalanlardan herhangi birini uygulama\n"
-"a - bu parçayı ve sonraki tüm parçaları uygula\n"
-"d - bu parçayı veya sonraki parçalardan herhangi birini uygulama"
-
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - bu parçayı çalışma ağacına uygula\n"
-"n - bu parçayı çalışma ağacına uygulama\n"
-"q - çık; bu parçayı veya kalanlardan herhangi birini uygulama\n"
-"a - bu parçayı ve sonraki tüm parçaları uygula\n"
-"d - bu parçayı veya sonraki parçalardan herhangi birini uygulama"
-
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - gidilecek bir parça seç\n"
-"/ - verilen düzenli ifade ile eşleşen bir parça ara\n"
-"j - bu parça için sonra karar ver, bir sonraki karar verilmemiş parçayı gör\n"
-"J - bu parça için sonra karar ver, bir sonraki parçayı gör\n"
-"k - bu parça için sonra karar ver, bir önceki karar verilmemiş parçayı gör\n"
-"K - bu parça için sonra karar ver, bir önceki parçayı gör\n"
-"s - geçerli parçayı daha ufak parçalara böl\n"
-"e - geçerli parçayı el ile düzenle\n"
-"? - yardımı yazdır\n"
-
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "Seçili parçalar indekse uygulanamıyor!\n"
-
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "birleştirilmeyenler yok sayılıyor: %s\n"
-
-msgid "No other hunks to goto\n"
-msgstr "Gidilecek başka parça yok\n"
-
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "Geçersiz sayı: '%s'\n"
-
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "Üzgünüm, yalnızca %d parça kullanılabilir.\n"
-msgstr[1] "Üzgünüm, yalnızca %d parça kullanılabilir.\n"
-
-msgid "No other hunks to search\n"
-msgstr "Aranacak başka parça yok\n"
-
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "Hatalı oluşturulmuş arama düzenli ifadesi %s: %s\n"
-
-msgid "No hunk matches the given pattern\n"
-msgstr "Verilen dizgi ile hiçbir parça eşleşmiyor\n"
-
-msgid "No previous hunk\n"
-msgstr "Öncesinde parça yok\n"
-
-msgid "No next hunk\n"
-msgstr "Sonrasında parça yok\n"
-
-msgid "Sorry, cannot split this hunk\n"
-msgstr "Üzgünüm, bu parça bölünemiyor\n"
-
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "%d parçaya bölündü.\n"
-msgstr[1] "%d parçaya bölündü.\n"
-
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "Üzgünüm, bu parça düzenlenemiyor\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - değişiklik içeren yolları göster\n"
-"update        - çalışma ağacı durumunu hazırlanan değişiklik setine ekle\n"
-"revert        - hazırlanan değişiklik setini HEAD sürümüne geri al\n"
-"patch         - parçaları seç ve seçici olarak güncelle\n"
-"diff          - HEAD ve indeks arasındaki diff'i (ayrımları) görüntüle\n"
-"add untracked - izlenmeyen dosyaların içeriğini hazırlanan değişiklik setine "
-"ekle\n"
-
-msgid "missing --"
-msgstr "-- eksik"
-
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "bilinmeyen --patch kipi: %s"
-
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "geçersiz argüman %s, -- bekleniyor"
-
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "yerel dilim GMT'den bir dakikadan az bir aralıkla ayrımlı\n"
 
@@ -22012,14 +22391,14 @@ msgstr ""
 "\n"
 "Bir özet göndermek istemiyorsanız gövde kısmını temizleyin.\n"
 
-#, perl-format
-msgid "Failed to open %s: %s"
-msgstr "%s açılamadı: %s"
-
 #, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "%s.final açılamadı: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "%s açılamadı: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "Özet e-postası boş, atlanıyor\n"
 
@@ -22169,15 +22548,19 @@ msgstr "(body) Cc: %s, '%s' satırından ekleniyor\n"
 
 #, perl-format
 msgid "(%s) Could not execute '%s'"
-msgstr "(%s) '%s' çalıştırılamadı"
+msgstr "(%s) '%s' yürütülemedi"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) %s: %s, '%s' konumundan ekleniyor\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) '%s' ögesinden hatalı oluşturulmuş çıktı"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
-msgstr "(%s) şuraya olan veri yolu kapatılamadı: '%s'"
+msgstr "(%s) şuraya olan veriyolu kapatılamadı: '%s'"
+
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) %s: %s, '%s' konumundan ekleniyor\n"
 
 msgid "cannot send message as 7bit"
 msgstr "ileti 7 bit olarak gönderilemiyor"
diff --git a/po/uk.po b/po/uk.po
new file mode 100644 (file)
index 0000000..0868905
--- /dev/null
+++ b/po/uk.po
@@ -0,0 +1,22859 @@
+# Ukrainian translation for Git package.
+# Український переклад пакету Git.
+# Copyright (c) 2023 Arkadii Yakovets.
+# Copyright (c) 2023 Kateryna Golovanova.
+# This file is distributed under the same license as the Git package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Git v2.43\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2023-11-09 14:26-0800\n"
+"PO-Revision-Date: 2023-11-09 14:34-0800\n"
+"Last-Translator: Arkadii Yakovets <ark@cho.red>\n"
+"Language-Team: Ukrainian <https://github.com/arkid15r/git-uk-l10n/>\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
+"X-Generator: Poedit 3.4.1\n"
+
+#, c-format
+msgid "Huh (%s)?"
+msgstr "Га (%s)?"
+
+msgid "could not read index"
+msgstr "не вдалося прочитати індекс"
+
+msgid "binary"
+msgstr "бінарний"
+
+msgid "nothing"
+msgstr "нічого"
+
+msgid "unchanged"
+msgstr "без змін"
+
+msgid "Update"
+msgstr "Оновити"
+
+#, c-format
+msgid "could not stage '%s'"
+msgstr "не вдалося додати до індексу %s"
+
+msgid "could not write index"
+msgstr "не вдалося записати індекс"
+
+#, c-format
+msgid "updated %d path\n"
+msgid_plural "updated %d paths\n"
+msgstr[0] "оновлено %d шлях\n"
+msgstr[1] "оновлено %d шляхи\n"
+msgstr[2] "оновлено %d шляхів\n"
+
+#, c-format
+msgid "note: %s is untracked now.\n"
+msgstr "примітка: %s зараз не відстежується.\n"
+
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "невдала спроба make_cache_entry для шляху \"%s\""
+
+msgid "Revert"
+msgstr "Вивернути"
+
+msgid "Could not parse HEAD^{tree}"
+msgstr "Не вдалося розібрати HEAD^{tree}"
+
+#, c-format
+msgid "reverted %d path\n"
+msgid_plural "reverted %d paths\n"
+msgstr[0] "вивернуто %d шлях\n"
+msgstr[1] "вивернуто %d шляхи\n"
+msgstr[2] "вивернуто %d шляхів\n"
+
+#, c-format
+msgid "No untracked files.\n"
+msgstr "Невідстежуваних файлів немає\n"
+
+msgid "Add untracked"
+msgstr "Додати невідстежувані"
+
+#, c-format
+msgid "added %d path\n"
+msgid_plural "added %d paths\n"
+msgstr[0] "додано %d шлях\n"
+msgstr[1] "додано %d шляхи\n"
+msgstr[2] "додано %d шляхів\n"
+
+#, c-format
+msgid "ignoring unmerged: %s"
+msgstr "ігноруються не злиті записи: %s"
+
+#, c-format
+msgid "Only binary files changed.\n"
+msgstr "Змінилися лише бінарні файли.\n"
+
+#, c-format
+msgid "No changes.\n"
+msgstr "Нічого не змінено.\n"
+
+msgid "Patch update"
+msgstr "Оновлення латки"
+
+msgid "Review diff"
+msgstr "Переглянути різницю"
+
+msgid "show paths with changes"
+msgstr "показати шляхи зі змінами"
+
+msgid "add working tree state to the staged set of changes"
+msgstr "додати стан робочого дерева до індексу"
+
+msgid "revert staged set of changes back to the HEAD version"
+msgstr "вивернути зміни індексу до версії HEAD"
+
+msgid "pick hunks and update selectively"
+msgstr "вибирати шматки і оновлювати вибірково"
+
+msgid "view diff between HEAD and index"
+msgstr "переглянути різницю між HEAD та індексом"
+
+msgid "add contents of untracked files to the staged set of changes"
+msgstr "додати вміст невідстежуваних файлів до індексу"
+
+msgid "Prompt help:"
+msgstr "Підказка по опціям:"
+
+msgid "select a single item"
+msgstr "вибрати один елемент"
+
+msgid "select a range of items"
+msgstr "вибрати діапазон елементів"
+
+msgid "select multiple ranges"
+msgstr "вибрати кілька діапазонів"
+
+msgid "select item based on unique prefix"
+msgstr "вибрати елемент за унікальним префіксом"
+
+msgid "unselect specified items"
+msgstr "зняти позначку із зазначених елементів"
+
+msgid "choose all items"
+msgstr "вибрати всі елементи"
+
+msgid "(empty) finish selecting"
+msgstr "(пусто) закінчити вибір"
+
+msgid "select a numbered item"
+msgstr "вибрати пронумерований елемент"
+
+msgid "(empty) select nothing"
+msgstr "(пусто) нічого не вибирати"
+
+msgid "*** Commands ***"
+msgstr "*** Команди ***"
+
+msgid "What now"
+msgstr "Що тепер"
+
+msgid "staged"
+msgstr "в індексі"
+
+msgid "unstaged"
+msgstr "поза індексом"
+
+msgid "path"
+msgstr "шлях"
+
+msgid "could not refresh index"
+msgstr "не вдалося оновити індекс"
+
+#, c-format
+msgid "Bye.\n"
+msgstr "До побачення.\n"
+
+#, c-format
+msgid "Stage mode change [y,n,q,a,d%s,?]? "
+msgstr "Індексувати зміну режиму [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage deletion [y,n,q,a,d%s,?]? "
+msgstr "Індексувати видалення [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage addition [y,n,q,a,d%s,?]? "
+msgstr "Індексувати додавання [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stage this hunk [y,n,q,a,d%s,?]? "
+msgstr "Індексувати цей шматок [y,n,q,a,d%s,?]? "
+
+msgid ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"staging."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для індексації."
+
+msgid ""
+"y - stage this hunk\n"
+"n - do not stage this hunk\n"
+"q - quit; do not stage this hunk or any of the remaining ones\n"
+"a - stage this hunk and all later hunks in the file\n"
+"d - do not stage this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - індексувати цей шматок\n"
+"n - не індексувати цей шматок\n"
+"q - вийти; не індексувати ні цей шматок, ні решту\n"
+"a - індексувати цей шматок і всі наступні шматки у файлі\n"
+"d - не індексувати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Stash mode change [y,n,q,a,d%s,?]? "
+msgstr "Сховати зміну режиму [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stash deletion [y,n,q,a,d%s,?]? "
+msgstr "Сховати видалення [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stash addition [y,n,q,a,d%s,?]? "
+msgstr "Сховати додавання [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stash this hunk [y,n,q,a,d%s,?]? "
+msgstr "Сховати цей шматок [y,n,q,a,d%s,?]? "
+
+msgid ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"stashing."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для схову."
+
+msgid ""
+"y - stash this hunk\n"
+"n - do not stash this hunk\n"
+"q - quit; do not stash this hunk or any of the remaining ones\n"
+"a - stash this hunk and all later hunks in the file\n"
+"d - do not stash this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - сховати цей шматок\n"
+"n - не ховати цей шматок\n"
+"q - вийти; не ховати ні цей шматок, ні решту\n"
+"a - сховати цей шматок і всі наступні шматки у файлі\n"
+"d - не ховати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Unstage mode change [y,n,q,a,d%s,?]? "
+msgstr "Прибрати з індексу зміну режиму [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Unstage deletion [y,n,q,a,d%s,?]? "
+msgstr "Прибрати з індексу видалення [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Unstage addition [y,n,q,a,d%s,?]? "
+msgstr "Прибрати з індексу додавання [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
+msgstr "Прибрати з індексу цей шматок [y,n,q,a,d%s,?]? "
+
+msgid ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"unstaging."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для розіндексації."
+
+msgid ""
+"y - unstage this hunk\n"
+"n - do not unstage this hunk\n"
+"q - quit; do not unstage this hunk or any of the remaining ones\n"
+"a - unstage this hunk and all later hunks in the file\n"
+"d - do not unstage this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - прибрати з індексу цей шматок\n"
+"n - не прибирати з індексу цей шматок\n"
+"q - вийти; не прибирати з індексу ні цей шматок, ні решту\n"
+"a - прибрати з індексу цей шматок і всі наступні шматки у файлі\n"
+"d - не прибирати з індексу цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
+msgstr "Застосувати зміну режиму до індексу [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
+msgstr "Застосувати видалення до індексу [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Apply addition to index [y,n,q,a,d%s,?]? "
+msgstr "Застосувати додавання до індексу [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
+msgstr "Застосувати цей шматок до індексу [y,n,q,a,d%s,?]? "
+
+msgid ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"applying."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для застосування."
+
+msgid ""
+"y - apply this hunk to index\n"
+"n - do not apply this hunk to index\n"
+"q - quit; do not apply this hunk or any of the remaining ones\n"
+"a - apply this hunk and all later hunks in the file\n"
+"d - do not apply this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - застосувати цей шматок до індексу\n"
+"n - не застосовувати цей шматок до індексу\n"
+"q - вийти; не застосовувати ні цей шматок, ні решту\n"
+"a - застосувати цей шматок і всі наступні шматки у файлі\n"
+"d - не застосовувати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути зміну режиму з робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути видалення з робочого дерева [y,n,q,a,d%s,?] "
+
+#, c-format
+msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути додавання з робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути цей шматок з робочого дерева [y,n,q,a,d%s,?]? "
+
+msgid ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"discarding."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для відкидання."
+
+msgid ""
+"y - discard this hunk from worktree\n"
+"n - do not discard this hunk from worktree\n"
+"q - quit; do not discard this hunk or any of the remaining ones\n"
+"a - discard this hunk and all later hunks in the file\n"
+"d - do not discard this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - відкинути цей шматок з робочого дерева\n"
+"n - не відкидати цей шматок з робочого дерева\n"
+"q - вийти; не відкидати ні цей шматок, ні решту\n"
+"a - відкинути цей шматок і всі наступні шматки у файлі\n"
+"d - не відкидати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути зміну режиму з індексу та робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути видалення з індексу та робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути додавання з індексу та робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути цей шматок з індексу та робочого дерева [y,n,q,a,d%s,?]? "
+
+msgid ""
+"y - discard this hunk from index and worktree\n"
+"n - do not discard this hunk from index and worktree\n"
+"q - quit; do not discard this hunk or any of the remaining ones\n"
+"a - discard this hunk and all later hunks in the file\n"
+"d - do not discard this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - відкинути цей шматок з індексу та робочого дерева\n"
+"n - не відкидати цей шматок з індексу та робочого дерева\n"
+"q - вийти; не відкидати ні цей шматок, ні решту\n"
+"a - відкинути цей шматок і всі наступні шматки у файлі\n"
+"d - не відкидати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
+msgstr ""
+"Застосувати зміну режиму до індексу та робочого дерева [y,n,q,a,d%s,?] "
+
+#, c-format
+msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати видалення до індексу та робочого дерева [y,n,q,a,d%s,?] "
+
+#, c-format
+msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати додавання до індексу та робочого дерева [y,n,q,a,d%s,?] "
+
+#, c-format
+msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати цей шматок до індексу та робочого дерева [y,n,q,a,d%s,?] "
+
+msgid ""
+"y - apply this hunk to index and worktree\n"
+"n - do not apply this hunk to index and worktree\n"
+"q - quit; do not apply this hunk or any of the remaining ones\n"
+"a - apply this hunk and all later hunks in the file\n"
+"d - do not apply this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - застосувати цей шматок до індексу та робочого дерева\n"
+"n - не застосовувати цей шматок до індексу та робочого дерева\n"
+"q - вийти; не застосовувати ні цей шматок, ні решту\n"
+"a - застосувати цей шматок і всі наступні шматки у файлі\n"
+"d - не застосовувати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати зміну режиму до робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати видалення до робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати додавання до робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати цей шматок до робочого дерева [y,n,q,a,d%s,?]? "
+
+msgid ""
+"y - apply this hunk to worktree\n"
+"n - do not apply this hunk to worktree\n"
+"q - quit; do not apply this hunk or any of the remaining ones\n"
+"a - apply this hunk and all later hunks in the file\n"
+"d - do not apply this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - застосувати цей шматок до робочого дерева\n"
+"n - не застосовувати цей шматок до робочого дерева\n"
+"q - вийти; не застосовувати ні цей шматок, ні решту\n"
+"a - застосувати цей шматок і всі наступні шматки у файлі\n"
+"d - не застосовувати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "could not parse hunk header '%.*s'"
+msgstr "не вдалося розібрати заголовок шматка \"%.*s\""
+
+msgid "could not parse diff"
+msgstr "не вдалося розібрати різницю"
+
+msgid "could not parse colored diff"
+msgstr "не вдалося розібрати кольоровану різницю"
+
+#, c-format
+msgid "failed to run '%s'"
+msgstr "не вдалося запустити \"%s\""
+
+msgid "mismatched output from interactive.diffFilter"
+msgstr "неспівпадіння виводу з interactive.diffFilter"
+
+msgid ""
+"Your filter must maintain a one-to-one correspondence\n"
+"between its input and output lines."
+msgstr ""
+"Ваш фільтр повинен підтримувати один до одного відповідність\n"
+"між його вхідними та вихідними рядками."
+
+#, c-format
+msgid ""
+"expected context line #%d in\n"
+"%.*s"
+msgstr ""
+"очікувався рядок контексту #%d в\n"
+"%.*s"
+
+#, c-format
+msgid ""
+"hunks do not overlap:\n"
+"%.*s\n"
+"\tdoes not end with:\n"
+"%.*s"
+msgstr ""
+"шматки не перетинаються:\n"
+"%.*s\n"
+"\tне закінчується на:\n"
+"%.*s"
+
+msgid "Manual hunk edit mode -- see bottom for a quick guide.\n"
+msgstr "Режим ручного редагування шматка -- див. короткий посібник внизу.\n"
+
+#, c-format
+msgid ""
+"---\n"
+"To remove '%c' lines, make them ' ' lines (context).\n"
+"To remove '%c' lines, delete them.\n"
+"Lines starting with %c will be removed.\n"
+msgstr ""
+"---\n"
+"Щоб видалити рядки \"%c\", зробіть їх рядками \" \" (контекст).\n"
+"Щоб видалити рядки \"%c\", вилучіть їх.\n"
+"Буде видалено рядки, що починаються з %c.\n"
+
+msgid ""
+"If it does not apply cleanly, you will be given an opportunity to\n"
+"edit again.  If all lines of the hunk are removed, then the edit is\n"
+"aborted and the hunk is left unchanged.\n"
+msgstr ""
+"Якщо шматок буде застосований з помилками, вам буде надана можливість\n"
+"відредагувати ще раз.  Якщо всі рядки шматка буде вилучено, то редагування "
+"буде\n"
+"перервано, і шматок залишиться без змін.\n"
+
+msgid "could not parse hunk header"
+msgstr "не вдалося розібрати заголовок шматка"
+
+msgid "'git apply --cached' failed"
+msgstr "\"git apply --cached\" завершився невдало"
+
+#. TRANSLATORS: do not translate [y/n]
+#. The program will only accept that input at this point.
+#. Consider translating (saying "no" discards!) as
+#. (saying "n" for "no" discards!) if the translation
+#. of the word "no" does not start with n.
+#.
+msgid ""
+"Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
+msgstr ""
+"Ваш відредагований шматок неможливо застосувати. Відредагувати ще раз "
+"(\"no\" - відкинути!) [y/n]? "
+
+msgid "The selected hunks do not apply to the index!"
+msgstr "Вибрані шматки неможливо застосувати до індексу!"
+
+msgid "Apply them to the worktree anyway? "
+msgstr "Все одно застосувати їх до робочого дерева? "
+
+msgid "Nothing was applied.\n"
+msgstr "Нічого не застосовано.\n"
+
+msgid ""
+"j - leave this hunk undecided, see next undecided hunk\n"
+"J - leave this hunk undecided, see next hunk\n"
+"k - leave this hunk undecided, see previous undecided hunk\n"
+"K - leave this hunk undecided, see previous hunk\n"
+"g - select a hunk to go to\n"
+"/ - search for a hunk matching the given regex\n"
+"s - split the current hunk into smaller hunks\n"
+"e - manually edit the current hunk\n"
+"? - print help\n"
+msgstr ""
+"j - залишити цей шматок невизначеним, перейти до наступного невизначеного "
+"шматка\n"
+"J - залишити цей шматок невизначеним, перейти до наступного шматка\n"
+"k - залишити цей шматок невизначеним, перейти до попереднього невизначеного "
+"шматка\n"
+"K - залишити цей шматок невизначеним, перейти до попереднього шматка\n"
+"g - вибрати шматок, до якого ви хочете перейти\n"
+"/ - шукати шматок, що відповідає заданому регвиру\n"
+"s - розбити поточний шматок на менші шматки\n"
+"e - редагувати поточний шматок вручну\n"
+"? - показати довідку\n"
+
+msgid "No previous hunk"
+msgstr "Немає попереднього шматка"
+
+msgid "No next hunk"
+msgstr "Немає наступного шматка"
+
+msgid "No other hunks to goto"
+msgstr "Немає інших шматків для переходу"
+
+msgid "go to which hunk (<ret> to see more)? "
+msgstr "до якого шматка перейти (<ret>, щоб побачити більше)? "
+
+msgid "go to which hunk? "
+msgstr "до якого шматка перейти? "
+
+#, c-format
+msgid "Invalid number: '%s'"
+msgstr "Неприпустиме число: \"%s\""
+
+#, c-format
+msgid "Sorry, only %d hunk available."
+msgid_plural "Sorry, only %d hunks available."
+msgstr[0] "Вибачайте, доступний лише %d шматок."
+msgstr[1] "Вибачайте, доступно лише %d шматки."
+msgstr[2] "Вибачайте, доступно лише %d шматків."
+
+msgid "No other hunks to search"
+msgstr "Немає інших шматків для пошуку"
+
+msgid "search for regex? "
+msgstr "шукати регвир? "
+
+#, c-format
+msgid "Malformed search regexp %s: %s"
+msgstr "Невірно сформований регвир пошуку %s: %s"
+
+msgid "No hunk matches the given pattern"
+msgstr "Жоден шматок не відповідає заданому шаблону"
+
+msgid "Sorry, cannot split this hunk"
+msgstr "Вибачайте, не можу розщепити цей шматок"
+
+#, c-format
+msgid "Split into %d hunks."
+msgstr "Розщепити на %d шматків."
+
+msgid "Sorry, cannot edit this hunk"
+msgstr "Вибачайте, не можу редагувати цей шматок"
+
+msgid "'git apply' failed"
+msgstr "\"git apply\" завершився невдало"
+
+#, c-format
+msgid ""
+"\n"
+"Disable this message with \"git config advice.%s false\""
+msgstr ""
+"\n"
+"Вимкнути це повідомлення можна за допомогою \"git config advice.%s false\""
+
+#, c-format
+msgid "%shint: %.*s%s\n"
+msgstr "%sпідказка: %.*s%s\n"
+
+msgid "Cherry-picking is not possible because you have unmerged files."
+msgstr "Висмикування неможливе, оскільки у вас є не злиті файли."
+
+msgid "Committing is not possible because you have unmerged files."
+msgstr "Створення коміту неможливе, оскільки у вас є не злиті файли."
+
+msgid "Merging is not possible because you have unmerged files."
+msgstr "Злиття неможливе, оскільки у вас є не злиті файли."
+
+msgid "Pulling is not possible because you have unmerged files."
+msgstr "Отримання неможливе, оскільки у вас є не злиті файли."
+
+msgid "Reverting is not possible because you have unmerged files."
+msgstr "Вивертання неможливе, оскільки у вас є не злиті файли."
+
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Перебазування неможливе, оскільки у вас є не злиті файли."
+
+msgid ""
+"Fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution and make a commit."
+msgstr ""
+"Виправте їх у робочому дереві, а потім скористайтесь командою \"git add/rm "
+"<файл>\"\n"
+"щоб позначити вирішення і зробити коміт."
+
+msgid "Exiting because of an unresolved conflict."
+msgstr "Вихід через невирішений конфлікт."
+
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr "Ви не завершили злиття (існує MERGE_HEAD)."
+
+msgid "Please, commit your changes before merging."
+msgstr "Будь ласка, зробіть коміт ваших змін перед злиттям."
+
+msgid "Exiting because of unfinished merge."
+msgstr "Вихід через незавершене злиття."
+
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Гілки, що розходяться, не можуть бути перемотані вперед, вам потрібно "
+"зробити:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"або\n"
+"\n"
+"\tgit rebase\n"
+
+msgid "Not possible to fast-forward, aborting."
+msgstr "Неможливо перемотати вперед, переривання."
+
+#, c-format
+msgid ""
+"The following paths and/or pathspecs matched paths that exist\n"
+"outside of your sparse-checkout definition, so will not be\n"
+"updated in the index:\n"
+msgstr ""
+"Наступні шляхи та/або визначники шляхів відповідають шляхам, які існують\n"
+"за межами визначення вашого розрідженого переходу, тому не будуть\n"
+"оновлені в індексі:\n"
+
+msgid ""
+"If you intend to update such entries, try one of the following:\n"
+"* Use the --sparse option.\n"
+"* Disable or modify the sparsity rules."
+msgstr ""
+"Якщо ви маєте намір оновити такі записи, спробуйте один із наведених нижче "
+"способів:\n"
+"* Використайте параметр --sparse.\n"
+"* Вимкніть або змініть правила розрідженості."
+
+#, c-format
+msgid ""
+"Note: switching to '%s'.\n"
+"\n"
+"You are in 'detached HEAD' state. You can look around, make experimental\n"
+"changes and commit them, and you can discard any commits you make in this\n"
+"state without impacting any branches by switching back to a branch.\n"
+"\n"
+"If you want to create a new branch to retain commits you create, you may\n"
+"do so (now or later) by using -c with the switch command. Example:\n"
+"\n"
+"  git switch -c <new-branch-name>\n"
+"\n"
+"Or undo this operation with:\n"
+"\n"
+"  git switch -\n"
+"\n"
+"Turn off this advice by setting config variable advice.detachedHead to "
+"false\n"
+"\n"
+msgstr ""
+"Примітка: перемикання на \"%s\".\n"
+"\n"
+"Ви перебуваєте в стані \"відʼєднаного HEAD\". Ви можете озирнутися довкола, "
+"внести експериментальні\n"
+"зміни і зробити коміт, також ви можете відкинути будь-які коміти, зроблені у "
+"цьому\n"
+"стані, не впливаючи на інші гілки, просто перейшовши до іншої гілки.\n"
+"\n"
+"Якщо ви хочете створити нову гілку для збереження зроблених вами комітів, ви "
+"можете\n"
+"використати (зараз або пізніше) -c з командою switch. Наприклад:\n"
+"\n"
+"  git switch -c <назва-нової-гілки\n"
+"\n"
+"Або скасувати цю операцію за допомогою:\n"
+"\n"
+"  git switch -\n"
+"\n"
+"Щоб вимкнути цю пораду, встановіть конфігураційний параметр advice."
+"detachedHead у false\n"
+
+#, c-format
+msgid ""
+"The following paths have been moved outside the\n"
+"sparse-checkout definition but are not sparse due to local\n"
+"modifications.\n"
+msgstr ""
+"Наступні шляхи, що було винесено за межі визначення розрідженого\n"
+"переходу, не є розрідженими через локальні\n"
+"зміни.\n"
+
+msgid ""
+"To correct the sparsity of these paths, do the following:\n"
+"* Use \"git add --sparse <paths>\" to update the index\n"
+"* Use \"git sparse-checkout reapply\" to apply the sparsity rules"
+msgstr ""
+"Щоб виправити розрідженість цих шляхів, виконайте наступне:\n"
+"* Використайте \"git add --sparse <шляхи>\" для оновлення індексу\n"
+"* Використайте \"git sparse-checkout reapply\", щоб застосувати правила "
+"розрідженості"
+
+msgid "cmdline ends with \\"
+msgstr "cmdline завершується символом \\"
+
+msgid "unclosed quote"
+msgstr "незакриті лапки"
+
+msgid "too many arguments"
+msgstr "забагато аргументів"
+
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "нерозпізнана опція пробільних символів \"%s\""
+
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr "нерозпізнана опція ігнорування пробільних символів \"%s\""
+
+#, c-format
+msgid "options '%s' and '%s' cannot be used together"
+msgstr "опції \"%s\" і \"%s\" не можна використовувати разом"
+
+#, c-format
+msgid "'%s' outside a repository"
+msgstr "\"%s\" поза сховищем"
+
+msgid "failed to read patch"
+msgstr "не вдалося прочитати латку"
+
+msgid "patch too large"
+msgstr "латка занадто велика"
+
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr "Неможливо підготувати регвир позначки часу %s"
+
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr "regexec повернув %d для вводу: %s"
+
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr "не вдалося знайти назву файла в рядку %d латки"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr ""
+"git apply: невірний git-diff - очікувалось /dev/null, отримано %s у рядку %d"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr ""
+"git apply: невірний git-diff - невідповідна назва нового файлу в рядку %d"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr ""
+"git apply: невірний git-diff - невідповідна назва старого файлу в рядку %d"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr "git apply: невірний git-diff - очікувалось /dev/null у рядку %d"
+
+#, c-format
+msgid "invalid mode on line %d: %s"
+msgstr "неприпустимий режим у рядку %d: %s"
+
+#, c-format
+msgid "inconsistent header lines %d and %d"
+msgstr "невідповідні рядки заголовка %d та %d"
+
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
+"component (line %d)"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components (line %d)"
+msgstr[0] ""
+"у заголовку git diff відсутня інформація про назву файла при видаленні %d "
+"провідного компонента назви шляху (рядок %d)"
+msgstr[1] ""
+"у заголовку git diff відсутня інформація про назву файла при видаленні %d "
+"провідних компонентів назви шляху (рядок %d)"
+msgstr[2] ""
+"у заголовку git diff відсутня інформація про назву файла при видаленні %d "
+"провідних компонентів назви шляху (рядок %d)"
+
+#, c-format
+msgid "git diff header lacks filename information (line %d)"
+msgstr "у заголовку git diff відсутня інформація про назву файла (рядок %d)"
+
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr "recount: неочікуваний рядок: %.*s"
+
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr "фрагмент латки без заголовка у рядку %d: %.*s"
+
+msgid "new file depends on old contents"
+msgstr "новий файл залежить від старого вмісту"
+
+msgid "deleted file still has contents"
+msgstr "видалений файл все ще має вміст"
+
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr "пошкоджена латка у рядку %d"
+
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr "новий файл %s залежить від старого вмісту"
+
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr "видалений файл %s все ще має вміст"
+
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr "** попередження: файл %s стане порожнім, але не буде видалений"
+
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr "пошкоджена бінарна латка у рядку %d: %.*s"
+
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr "нерозпізнана бінарна латка у рядку %d"
+
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr "латка, що містить лише непотріб у рядку %d"
+
+#, c-format
+msgid "unable to read symlink %s"
+msgstr "не вдалося прочитати символьне посилання %s"
+
+#, c-format
+msgid "unable to open or read %s"
+msgstr "не вдалося відкрити або прочитати %s"
+
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "неприпустимий початок рядка: \"%c\""
+
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] "Шматок #%d успішно застосовано на позиції %d (зміщення %d рядок)."
+msgstr[1] "Шматок #%d успішно застосовано на позиції %d (зміщення %d рядки)."
+msgstr[2] "Шматок #%d успішно застосовано на позиції %d (зміщення %d рядків)."
+
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr ""
+"Контекст скорочено до (%ld/%ld), щоб застосувати фрагмент на позиції %d"
+
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+"поки йде пошук:\n"
+"%.*s"
+
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr "відсутні дані бінарної латки для \"%s\""
+
+#, c-format
+msgid "cannot reverse-apply a binary patch without the reverse hunk to '%s'"
+msgstr ""
+"неможливо виконати reverse-apply бінарної латки без зворотнього шматка до "
+"\"%s\""
+
+#, c-format
+msgid "cannot apply binary patch to '%s' without full index line"
+msgstr ""
+"неможливо застосувати бінарну латку до \"%s\" без повного індексного рядка"
+
+#, c-format
+msgid ""
+"the patch applies to '%s' (%s), which does not match the current contents."
+msgstr ""
+"латка застосовується до \"%s\" (%s), який не збігається з поточним вмістом."
+
+#, c-format
+msgid "the patch applies to an empty '%s' but it is not empty"
+msgstr "латка застосовується до порожнього \"%s\", який не є порожнім"
+
+#, c-format
+msgid "the necessary postimage %s for '%s' cannot be read"
+msgstr "неможливо прочитати необхідний postimage %s для \"%s\""
+
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr "бінарна латка не застосовується до \"%s\""
+
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr ""
+"бінарна латка для \"%s\" призводить до некоректного результату (очікувалось "
+"%s, отримано %s)"
+
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr "латання не вдалося: %s:%ld"
+
+#, c-format
+msgid "cannot checkout %s"
+msgstr "неможливо переключити стан %s"
+
+#, c-format
+msgid "failed to read %s"
+msgstr "не вдалося прочитати %s"
+
+#, c-format
+msgid "reading from '%s' beyond a symbolic link"
+msgstr "читання з \"%s\" за символьним посиланням"
+
+#, c-format
+msgid "path %s has been renamed/deleted"
+msgstr "шлях %s перейменовано/видалено"
+
+#, c-format
+msgid "%s: does not exist in index"
+msgstr "%s: не існує в індексі"
+
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s: не співпадає з індексом"
+
+msgid "repository lacks the necessary blob to perform 3-way merge."
+msgstr ""
+"у сховищі відсутній необхідний blob для виконання тристороннього злиття."
+
+#, c-format
+msgid "Performing three-way merge...\n"
+msgstr "Виконання тристороннього злиття...\n"
+
+#, c-format
+msgid "cannot read the current contents of '%s'"
+msgstr "неможливо прочитати поточний вміст \"%s\""
+
+#, c-format
+msgid "Failed to perform three-way merge...\n"
+msgstr "Не вдалося виконати тристороннє злиття...\n"
+
+#, c-format
+msgid "Applied patch to '%s' with conflicts.\n"
+msgstr "Латка до \"%s\" застосована з конфліктами.\n"
+
+#, c-format
+msgid "Applied patch to '%s' cleanly.\n"
+msgstr "Латка до \"%s\" застосована чисто.\n"
+
+#, c-format
+msgid "Falling back to direct application...\n"
+msgstr "Повернення до прямого застосування...\n"
+
+msgid "removal patch leaves file contents"
+msgstr "латка видалення залишає вміст файлу"
+
+#, c-format
+msgid "%s: wrong type"
+msgstr "%s: невірний тип"
+
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr "%s має тип %o, очікувалось %o"
+
+#, c-format
+msgid "invalid path '%s'"
+msgstr "неприпустимий шлях \"%s\""
+
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s: вже існує в індексі"
+
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s: вже існує в робочій директорії"
+
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr "новий режим (%o) для %s не відповідає старому режиму (%o)"
+
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr "новий режим (%o) для %s не відповідає старому режиму (%o) для %s"
+
+#, c-format
+msgid "affected file '%s' is beyond a symbolic link"
+msgstr "уражений файл \"%s\" знаходиться за межами символічного посилання"
+
+#, c-format
+msgid "%s: patch does not apply"
+msgstr "%s: латка не може бути застосована"
+
+#, c-format
+msgid "Checking patch %s..."
+msgstr "Перевірка латки %s..."
+
+#, c-format
+msgid "sha1 information is lacking or useless for submodule %s"
+msgstr "sha1 інформація відсутня або марна для підмодуля %s"
+
+#, c-format
+msgid "mode change for %s, which is not in current HEAD"
+msgstr "зміна режиму для %s, якого немає в поточному HEAD"
+
+#, c-format
+msgid "sha1 information is lacking or useless (%s)."
+msgstr "sha1 інформація відсутня або марна (%s)."
+
+#, c-format
+msgid "could not add %s to temporary index"
+msgstr "не вдалося додати %s до тимчасового індексу"
+
+#, c-format
+msgid "could not write temporary index to %s"
+msgstr "не вдалося записати тимчасовий індекс у %s"
+
+#, c-format
+msgid "unable to remove %s from index"
+msgstr "не вдалося видалити %s з індексу"
+
+#, c-format
+msgid "corrupt patch for submodule %s"
+msgstr "пошкоджена латка для підмодуля %s"
+
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "не вдалося отримати інформацію про щойно створений файл \"%s\""
+
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr "не вдалося зробити запис для щойно створеного файлу %s"
+
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr "не вдалося додати запис кешу для %s"
+
+#, c-format
+msgid "failed to write to '%s'"
+msgstr "не вдалося записати в \"%s\""
+
+#, c-format
+msgid "closing file '%s'"
+msgstr "закриття файлу \"%s\""
+
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr "не вдалося записати файл \"%s\" в режимі %o"
+
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr "Латка %s застосована чисто."
+
+msgid "internal error"
+msgstr "внутрішня помилка"
+
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] "Застосування латки %%s з %d відкиданням..."
+msgstr[1] "Застосування латки %%s з %d відкиданнями..."
+msgstr[2] "Застосування латки %%s з %d відкиданнями..."
+
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr "скорочення назви файлу .rej до %.*s.rej"
+
+#, c-format
+msgid "cannot open %s"
+msgstr "неможливо відкрити %s"
+
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "неможливо видалити \"%s\""
+
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr "Шматок #%d застосовано чисто."
+
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr "Шматок #%d відкинуто."
+
+#, c-format
+msgid "Skipped patch '%s'."
+msgstr "Пропущена латка \"%s\"."
+
+msgid "No valid patches in input (allow with \"--allow-empty\")"
+msgstr "Немає коректних латок на вході (дозволити через \"--allow-empty\")"
+
+msgid "unable to read index file"
+msgstr "не вдалося прочитати індексний файл"
+
+#, c-format
+msgid "can't open patch '%s': %s"
+msgstr "неможливо відкрити латку \"%s\": %s"
+
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] "ігнорована %d помилка пробільних символів"
+msgstr[1] "ігноровані %d помилки пробільних символів"
+msgstr[2] "ігноровані %d помилок пробільних символів"
+
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] "%d рядок додає помилки пробільних символів."
+msgstr[1] "%d рядки додають помилки пробільних символів."
+msgstr[2] "%d рядків додають помилки пробільних символів."
+
+#, c-format
+msgid "%d line applied after fixing whitespace errors."
+msgid_plural "%d lines applied after fixing whitespace errors."
+msgstr[0] "%d рядок застосовано після виправлення помилок пробільних символів."
+msgstr[1] "%d рядки застосовано після виправлення помилок пробільних символів."
+msgstr[2] ""
+"%d рядків застосовано після виправлення помилок пробільних символів."
+
+msgid "Unable to write new index file"
+msgstr "Не вдалося записати новий файл індексу"
+
+msgid "don't apply changes matching the given path"
+msgstr "не застосовувати зміни, що відповідають вказаному шляху"
+
+msgid "apply changes matching the given path"
+msgstr "застосувати зміни, що відповідають вказаному шляху"
+
+msgid "num"
+msgstr "число"
+
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr "видалити <num> перших слешів з традиційних diff-шляхів"
+
+msgid "ignore additions made by the patch"
+msgstr "ігнорувати доповнення, зроблені латкою"
+
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr "замість застосування латки вивести diffstat для вхідних даних"
+
+msgid "show number of added and deleted lines in decimal notation"
+msgstr "показати кількість доданих і видалених рядків у десятковій нотації"
+
+msgid "instead of applying the patch, output a summary for the input"
+msgstr "замість застосування латки вивести підсумок для вхідних даних"
+
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr "замість застосування латки перевірити можливість її застосування"
+
+msgid "make sure the patch is applicable to the current index"
+msgstr "переконатися, що латка може бути застосовна до поточного індексу"
+
+msgid "mark new files with `git add --intent-to-add`"
+msgstr "позначити нові файли командою \"git add --intent-to-add\""
+
+msgid "apply a patch without touching the working tree"
+msgstr "застосувати латку, не торкаючись робочого дерева"
+
+msgid "accept a patch that touches outside the working area"
+msgstr "прийняти латку, яка виходить за межі робочого простору"
+
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr "також застосувати латку (використовуйте з --stat/--summary/--check)"
+
+msgid "attempt three-way merge, fall back on normal patch if that fails"
+msgstr ""
+"спробувати тристороннє злиття, повернутися до звичайного латання, якщо це не "
+"вдасться"
+
+msgid "build a temporary index based on embedded index information"
+msgstr "створити тимчасовий індекс на основі вбудованої індексної інформації"
+
+msgid "paths are separated with NUL character"
+msgstr "шляхи відокремлюються символом NUL"
+
+msgid "ensure at least <n> lines of context match"
+msgstr "забезпечити збіг принаймні <n> рядків контексту"
+
+msgid "action"
+msgstr "дія"
+
+msgid "detect new or modified lines that have whitespace errors"
+msgstr "виявляти нові або змінені рядки з помилками пробільних символів"
+
+msgid "ignore changes in whitespace when finding context"
+msgstr "ігнорувати зміни пробільних символів при пошуку контексту"
+
+msgid "apply the patch in reverse"
+msgstr "застосувати латку у зворотному порядку"
+
+msgid "don't expect at least one line of context"
+msgstr "не очікувати на хоча б один рядок контексту"
+
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr "залишити відкинуті шматки у відповідних *.rej файлах"
+
+msgid "allow overlapping hunks"
+msgstr "дозволити перекриття шматків"
+
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr "дозволяти некоректно виявлену відсутність нового рядка в кінці файлу"
+
+msgid "do not trust the line counts in the hunk headers"
+msgstr "не довіряти кількості рядків у заголовках шматків"
+
+msgid "root"
+msgstr "корінь"
+
+msgid "prepend <root> to all filenames"
+msgstr "додати <корінь> до всіх назв файлів"
+
+msgid "don't return error for empty patches"
+msgstr "не повертати помилку для порожніх латок"
+
+#, c-format
+msgid "cannot stream blob %s"
+msgstr "неможливо транслювати blob %s"
+
+#, c-format
+msgid "unsupported file mode: 0%o (SHA1: %s)"
+msgstr "непідтримуваний режим файлу: 0%o (SHA1: %s)"
+
+#, c-format
+msgid "deflate error (%d)"
+msgstr "помилка пакування (%d)"
+
+#, c-format
+msgid "unable to start '%s' filter"
+msgstr "не вдалося запустити \"%s\" фільтр"
+
+msgid "unable to redirect descriptor"
+msgstr "не вдалося перенаправити дескриптор"
+
+#, c-format
+msgid "'%s' filter reported error"
+msgstr "Фільтр \"%s\" повідомив про помилку"
+
+#, c-format
+msgid "path is not valid UTF-8: %s"
+msgstr "шлях не є припустимим UTF-8: %s"
+
+#, c-format
+msgid "path too long (%d chars, SHA1: %s): %s"
+msgstr "шлях занадто довгий (%d символів, SHA1: %s): %s"
+
+#, c-format
+msgid "timestamp too large for this system: %<PRIuMAX>"
+msgstr "позначка часу занадто велика для цієї системи: %<PRIuMAX>"
+
+msgid "git archive [<options>] <tree-ish> [<path>...]"
+msgstr "git archive [<опції>] <деревоподібне-джерело> [<шлях>...]"
+
+msgid ""
+"git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]"
+msgstr ""
+"git archive --remote <сховище> [--exec <команда>] [<опції>] <деревоподібне-"
+"джерело> [<шлях>...]"
+
+msgid "git archive --remote <repo> [--exec <cmd>] --list"
+msgstr "git archive --remote <сховище> [--exec <команда>] --list"
+
+#, c-format
+msgid "cannot read '%s'"
+msgstr "неможливо прочитати \"%s\""
+
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "визначник шляху \"%s\" відповідає файлам поза поточною директорією"
+
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr "визначник шляxу \"%s\" не відповідає жодному файлу"
+
+#, c-format
+msgid "no such ref: %.*s"
+msgstr "немає такого посилання: %.*s"
+
+#, c-format
+msgid "not a valid object name: %s"
+msgstr "невірне ім’я об’єкта: %s"
+
+#, c-format
+msgid "not a tree object: %s"
+msgstr "не є об’єктом дерева: %s"
+
+#, c-format
+msgid "File not found: %s"
+msgstr "Файл не знайдено: %s"
+
+#, c-format
+msgid "Not a regular file: %s"
+msgstr "Не звичайний файл: %s"
+
+#, c-format
+msgid "unclosed quote: '%s'"
+msgstr "незакриті лапки: \"%s\""
+
+#, c-format
+msgid "missing colon: '%s'"
+msgstr "відсутня двокрапка: \"%s\""
+
+#, c-format
+msgid "empty file name: '%s'"
+msgstr "порожня назва файлу: \"%s\""
+
+msgid "fmt"
+msgstr "fmt"
+
+msgid "archive format"
+msgstr "формат архіву"
+
+msgid "prefix"
+msgstr "префікс"
+
+msgid "prepend prefix to each pathname in the archive"
+msgstr "додати префікс до кожної назви шляху в архіві"
+
+msgid "file"
+msgstr "файл"
+
+msgid "add untracked file to archive"
+msgstr "додати невідстежуваний файл до архіву"
+
+msgid "path:content"
+msgstr "шлях:вміст"
+
+msgid "write the archive to this file"
+msgstr "записати архів до цього файлу"
+
+msgid "read .gitattributes in working directory"
+msgstr "прочитати .gitattributes робочої директорії"
+
+msgid "report archived files on stderr"
+msgstr "звітувати про заархівовані файли в stderr"
+
+msgid "time"
+msgstr "час"
+
+msgid "set modification time of archive entries"
+msgstr "встановити час модифікації архівних записів"
+
+msgid "set compression level"
+msgstr "встановити рівень компресії"
+
+msgid "list supported archive formats"
+msgstr "показати список підтримуваних форматів архівів"
+
+msgid "repo"
+msgstr "сховище"
+
+msgid "retrieve the archive from remote repository <repo>"
+msgstr "отримати архів з віддаленого <сховища>"
+
+msgid "command"
+msgstr "команда"
+
+msgid "path to the remote git-upload-archive command"
+msgstr "шлях до віддаленої команди git-upload-archive"
+
+msgid "Unexpected option --remote"
+msgstr "Неочікувана опція --remote"
+
+#, c-format
+msgid "the option '%s' requires '%s'"
+msgstr "опція \"%s\" потребує \"%s\""
+
+msgid "Unexpected option --output"
+msgstr "Неочікувана опція --output"
+
+#, c-format
+msgid "Unknown archive format '%s'"
+msgstr "Невідомий формат архіву \"%s\""
+
+#, c-format
+msgid "Argument not supported for format '%s': -%d"
+msgstr "Аргумент не підтримується для формату \"%s\": -%d"
+
+#, c-format
+msgid "%.*s is not a valid attribute name"
+msgstr "%.*s не є припустимою назвою атрибута"
+
+msgid "unable to add additional attribute"
+msgstr "не вдалося додати додатковий атрибут"
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "ігнорування надто довгого рядка атрибутів %d"
+
+#, c-format
+msgid "%s not allowed: %s:%d"
+msgstr "%s не дозволено: %s:%d"
+
+msgid ""
+"Negative patterns are ignored in git attributes\n"
+"Use '\\!' for literal leading exclamation."
+msgstr ""
+"Негативні шаблони ігноруються в git атрибутах\n"
+"Використовуйте \"\\!\" для додання знака оклику."
+
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "неможливо виконати fstat для файла git атрибутів \"%s\""
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "ігнорування надто великого файлу gitattributes \"%s\""
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "ігнорування надто великих gitattributes blob \"%s\""
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "невірний --attr-source або GIT_ATTR_SOURCE"
+
+#, c-format
+msgid "Badly quoted content in file '%s': %s"
+msgstr "Невірно процитований вміст у файлі \"%s\": %s"
+
+#, c-format
+msgid "We cannot bisect more!\n"
+msgstr "Подальше бісектування неможливо!\n"
+
+#, c-format
+msgid "Not a valid commit name %s"
+msgstr "Не є дійсною назвою коміту %s"
+
+#, c-format
+msgid ""
+"The merge base %s is bad.\n"
+"This means the bug has been fixed between %s and [%s].\n"
+msgstr ""
+"База злиття %s є невірною.\n"
+"Це означає, що помилку було виправлено між %s та [%s].\n"
+
+#, c-format
+msgid ""
+"The merge base %s is new.\n"
+"The property has changed between %s and [%s].\n"
+msgstr ""
+"База злиття %s є новою.\n"
+"Властивість змінилася між %s та [%s].\n"
+
+#, c-format
+msgid ""
+"The merge base %s is %s.\n"
+"This means the first '%s' commit is between %s and [%s].\n"
+msgstr ""
+"Базою злиття %s є %s.\n"
+"Це означає, що перший \"%s\" коміт знаходиться між %s і [%s].\n"
+
+#, c-format
+msgid ""
+"Some %s revs are not ancestors of the %s rev.\n"
+"git bisect cannot work properly in this case.\n"
+"Maybe you mistook %s and %s revs?\n"
+msgstr ""
+"Деякі %s revs не є предками %s rev.\n"
+"git bisect не може працювати належним чином в таких випадках.\n"
+"Можливо, ви переплутали %s і %s revs?\n"
+
+#, c-format
+msgid ""
+"the merge base between %s and [%s] must be skipped.\n"
+"So we cannot be sure the first %s commit is between %s and %s.\n"
+"We continue anyway."
+msgstr ""
+"базу злиття між %s і [%s] потрібно пропустити.\n"
+"Тому ми не можемо бути впевнені, що перший коміт %s знаходиться між %s і "
+"%s.\n"
+"Ми все одно продовжимо."
+
+#, c-format
+msgid "Bisecting: a merge base must be tested\n"
+msgstr "Бісекція: база злиття повинна бути протестована\n"
+
+#, c-format
+msgid "a %s revision is needed"
+msgstr "необхідна ревізія %s"
+
+#, c-format
+msgid "could not create file '%s'"
+msgstr "не вдалося створити файл \"%s\""
+
+#, c-format
+msgid "could not read file '%s'"
+msgstr "не вдалося прочитати файл \"%s\""
+
+msgid "reading bisect refs failed"
+msgstr "не вдалося прочитати бісекційні посилання"
+
+#, c-format
+msgid "%s was both %s and %s\n"
+msgstr "%s був одночасно і %s, і %s\n"
+
+#, c-format
+msgid ""
+"No testable commit found.\n"
+"Maybe you started with bad path arguments?\n"
+msgstr ""
+"Не знайдено коміт для тестування.\n"
+"Можливо, ви почали з невірних аргументів шляху?\n"
+
+#, c-format
+msgid "(roughly %d step)"
+msgid_plural "(roughly %d steps)"
+msgstr[0] "(приблизно %d крок)"
+msgstr[1] "(приблизно %d кроки)"
+msgstr[2] "(приблизно %d кроків)"
+
+#. TRANSLATORS: the last %s will be replaced with "(roughly %d
+#. steps)" translation.
+#.
+#, c-format
+msgid "Bisecting: %d revision left to test after this %s\n"
+msgid_plural "Bisecting: %d revisions left to test after this %s\n"
+msgstr[0] "Бісекція: залишилося протестувати %d ревізію після цього %s\n"
+msgstr[1] "Бісекція: залишилося протестувати %d ревізії після цього %s\n"
+msgstr[2] "Бісекція: залишилося протестувати %d ревізій після цього %s\n"
+
+msgid "--contents and --reverse do not blend well."
+msgstr "--contents і --reverse поєднуються не дуже добре."
+
+msgid "--reverse and --first-parent together require specified latest commit"
+msgstr "--reverse і --first-parent разом вимагають вказівки останнього коміту"
+
+msgid "revision walk setup failed"
+msgstr "не вдалося налаштувати проходження по ревізіям"
+
+msgid ""
+"--reverse --first-parent together require range along first-parent chain"
+msgstr ""
+"--reverse --first-parent разом вимагають вказівки діапазону вздовж ланцюжка "
+"першого батька"
+
+#, c-format
+msgid "no such path %s in %s"
+msgstr "немає шляху %s в %s"
+
+#, c-format
+msgid "cannot read blob %s for path %s"
+msgstr "неможливо прочитати blob %s для шляху %s"
+
+msgid ""
+"cannot inherit upstream tracking configuration of multiple refs when "
+"rebasing is requested"
+msgstr ""
+"неможливо успадкувати першоджерельну конфігурацію відстежування кількох "
+"посилань при запиті перебазування"
+
+#, c-format
+msgid "not setting branch '%s' as its own upstream"
+msgstr "гілку \"%s\" не встановлено власним першоджерельним сховищем"
+
+#, c-format
+msgid "branch '%s' set up to track '%s' by rebasing."
+msgstr "гілку \"%s\" налаштовано на відстежування \"%s\" через перебазування."
+
+#, c-format
+msgid "branch '%s' set up to track '%s'."
+msgstr "гілку \"%s\" налаштовано на відстежування \"%s\"."
+
+#, c-format
+msgid "branch '%s' set up to track:"
+msgstr "гілку \"%s\" налаштовано на відстежування:"
+
+msgid "unable to write upstream branch configuration"
+msgstr "не вдалося записати конфігурацію висхідної гілки"
+
+msgid ""
+"\n"
+"After fixing the error cause you may try to fix up\n"
+"the remote tracking information by invoking:"
+msgstr ""
+"\n"
+"Після усунення причини помилки ви можете спробувати виправити\n"
+"інформацію про віддалене відстежування здійснівши виклик:"
+
+#, c-format
+msgid "asked to inherit tracking from '%s', but no remote is set"
+msgstr ""
+"просили успадкувати відстежування з \"%s\", але віддалене призначення не "
+"встановлено"
+
+#, c-format
+msgid "asked to inherit tracking from '%s', but no merge configuration is set"
+msgstr ""
+"просили успадкувати відстежування з \"%s\", але конфігурацію злиття не задано"
+
+#, c-format
+msgid "not tracking: ambiguous information for ref '%s'"
+msgstr "не відстежується: неоднозначна інформація для посилання \"%s\""
+
+#. TRANSLATORS: This is a line listing a remote with duplicate
+#. refspecs in the advice message below. For RTL languages you'll
+#. probably want to swap the "%s" and leading "  " space around.
+#.
+#. TRANSLATORS: This is line item of ambiguous object output
+#. from describe_ambiguous_object() above. For RTL languages
+#. you'll probably want to swap the "%s" and leading " " space
+#. around.
+#.
+#, c-format
+msgid "  %s\n"
+msgstr "  %s\n"
+
+#. TRANSLATORS: The second argument is a \n-delimited list of
+#. duplicate refspecs, composed above.
+#.
+#, c-format
+msgid ""
+"There are multiple remotes whose fetch refspecs map to the remote\n"
+"tracking ref '%s':\n"
+"%s\n"
+"This is typically a configuration error.\n"
+"\n"
+"To support setting up tracking branches, ensure that\n"
+"different remotes' fetch refspecs map into different\n"
+"tracking namespaces."
+msgstr ""
+"Існує кілька віддалених призначень, визначники отримання посилань яких "
+"розвʼязуються у віддалено \n"
+"відстежуване посилання \"%s\":\n"
+"%s\n"
+"Зазвичай це помилка конфігурації.\n"
+"\n"
+"Щоб підтримувати налаштування гілок відстежування, переконайтеся, що\n"
+"визначники отримання посилань різних віддалених призначень розвʼязуються у "
+"різні\n"
+"простори імен відстежування."
+
+#, c-format
+msgid "'%s' is not a valid branch name"
+msgstr "\"%s\" не є допустимою назвою гілки"
+
+#, c-format
+msgid "a branch named '%s' already exists"
+msgstr "гілка з ім’ям \"%s\" вже існує"
+
+#, c-format
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr ""
+"неможливо примусово оновити гілку \"%s\", яка використовується робочим "
+"деревом у \"%s\""
+
+#, c-format
+msgid "cannot set up tracking information; starting point '%s' is not a branch"
+msgstr ""
+"неможливо налаштувати інформацію про відстежування; початкова точка \"%s\" "
+"не є гілкою"
+
+#, c-format
+msgid "the requested upstream branch '%s' does not exist"
+msgstr "запитувана висхідна гілка \"%s\" не існує"
+
+msgid ""
+"\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push."
+msgstr ""
+"\n"
+"Якщо ви плануєте базувати свою роботу на основі першоджерельної\n"
+"гілки, яка вже існує на віддаленому призначенні, вам може знадобитися\n"
+"запустити \"git fetch\", щоб отримати її.\n"
+"\n"
+"Якщо ви плануєте надіслати нову локальну гілку, яка\n"
+"буде відстежувати свою віддалену гілку, вам слід скористатися командою\n"
+"\"git push -u\", щоб встановити конфігурацію першоджерельного сховища під "
+"час надсилання."
+
+#, c-format
+msgid "not a valid object name: '%s'"
+msgstr "не є допустимою назвою об’єкта: \"%s\""
+
+#, c-format
+msgid "ambiguous object name: '%s'"
+msgstr "неоднозначна назва об’єкта: \"%s\""
+
+#, c-format
+msgid "not a valid branch point: '%s'"
+msgstr "не є допустимою точкою розгалуження: \"%s\""
+
+#, c-format
+msgid "submodule '%s': unable to find submodule"
+msgstr "підмодуль \"%s\": не вдалося знайти підмодуль"
+
+#, c-format
+msgid ""
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
+msgstr ""
+"Ви можете спробувати оновити підмодулі за допомогою \"git checkout --no-"
+"recurse-submodules %s && git submodule update --init\"."
+
+#, c-format
+msgid "submodule '%s': cannot create branch '%s'"
+msgstr "підмодуль \"%s\": неможливо створити гілку \"%s\""
+
+#, c-format
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "\"%s\" вже використовується робочим деревом в \"%s\""
+
+msgid "git add [<options>] [--] <pathspec>..."
+msgstr "git add [<опції>] [--] <визначник шляху>..."
+
+#, c-format
+msgid "cannot chmod %cx '%s'"
+msgstr "неможливо виконати chmod %cx \"%s\""
+
+msgid "Unstaged changes after refreshing the index:"
+msgstr "Неіндексовані зміни після оновлення індексу:"
+
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"параметр add.interactive.useBuiltin було видалено!\n"
+"Дивіться запис у \"git help config\" для більш детальної інформації."
+
+msgid "could not read the index"
+msgstr "не вдалося прочитати індекс"
+
+msgid "editing patch failed"
+msgstr "не вдалося відредагувати латку"
+
+#, c-format
+msgid "could not stat '%s'"
+msgstr "не вдалося виконати stat \"%s\""
+
+msgid "empty patch. aborted"
+msgstr "порожня латка. перервано"
+
+#, c-format
+msgid "could not apply '%s'"
+msgstr "не вдалося застосувати \"%s\""
+
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr "Наступні шляхи ігноруються одним з ваших .gitignore файлів:\n"
+
+msgid "dry run"
+msgstr "пробний запуск"
+
+msgid "be verbose"
+msgstr "розгорнутий вивід"
+
+msgid "interactive picking"
+msgstr "інтерактивний вибір"
+
+msgid "select hunks interactively"
+msgstr "обирати шматки інтерактивно"
+
+msgid "edit current diff and apply"
+msgstr "відредагувати поточну різницю і застосувати"
+
+msgid "allow adding otherwise ignored files"
+msgstr "дозволити додавання ігнорованих файлів"
+
+msgid "update tracked files"
+msgstr "оновити відстежувані файли"
+
+msgid "renormalize EOL of tracked files (implies -u)"
+msgstr "перенормувати EOL відстежуваних файлів (мається на увазі -u)"
+
+msgid "record only the fact that the path will be added later"
+msgstr "записати лише той факт, що шлях буде додано пізніше"
+
+msgid "add changes from all tracked and untracked files"
+msgstr "додати зміни з усіх відстежуваних і невідстежуваних файлів"
+
+msgid "ignore paths removed in the working tree (same as --no-all)"
+msgstr "ігнорувати шляхи, видалені у робочому дереві (те саме, що й --no-all)"
+
+msgid "don't add, only refresh the index"
+msgstr "не додавати, лише оновити індекс"
+
+msgid "just skip files which cannot be added because of errors"
+msgstr "просто пропустити файли, які не можуть бути додані через помилки"
+
+msgid "check if - even missing - files are ignored in dry run"
+msgstr ""
+"перевірити, чи ігноруються файли, навіть якщо вони відсутні, під час "
+"пробного запуску"
+
+msgid "allow updating entries outside of the sparse-checkout cone"
+msgstr "дозволити оновлення записів за межами конуса розрідженого переходу"
+
+msgid "override the executable bit of the listed files"
+msgstr "перевизначити біт виконання для зазначених файлів"
+
+msgid "warn when adding an embedded repository"
+msgstr "попереджувати при додаванні вбудованого сховища"
+
+#, c-format
+msgid ""
+"You've added another git repository inside your current repository.\n"
+"Clones of the outer repository will not contain the contents of\n"
+"the embedded repository and will not know how to obtain it.\n"
+"If you meant to add a submodule, use:\n"
+"\n"
+"\tgit submodule add <url> %s\n"
+"\n"
+"If you added this path by mistake, you can remove it from the\n"
+"index with:\n"
+"\n"
+"\tgit rm --cached %s\n"
+"\n"
+"See \"git help submodule\" for more information."
+msgstr ""
+"Ви додали ще одне git сховище всередині вашого поточного сховища.\n"
+"Клони зовнішнього сховища не міститимуть вмісту\n"
+"вбудованого сховища і не знатимуть, як його отримати.\n"
+"Якщо ви хочете додати підмодуль, скористайтесь командою\n"
+"\n"
+"\tgit submodule add <url> %s\n"
+"\n"
+"Якщо ви додали цей шлях помилково, ви можете видалити його з\n"
+"індексу за допомогою:\n"
+"\n"
+"\tgit rm --cached %s\n"
+"\n"
+"Докладніше дивіться у \"git help submodule\"."
+
+#, c-format
+msgid "adding embedded git repository: %s"
+msgstr "додавання вбудованого git сховища: %s"
+
+msgid ""
+"Use -f if you really want to add them.\n"
+"Turn this message off by running\n"
+"\"git config advice.addIgnoredFile false\""
+msgstr ""
+"Використовуйте -f, якщо ви дійсно хочете їх додати.\n"
+"Щоб вимкнути це повідомлення, виконайте\n"
+"\"git config advice.addIgnoredFile false\""
+
+msgid "adding files failed"
+msgstr "додавання файлів завершилося невдало"
+
+#, c-format
+msgid "--chmod param '%s' must be either -x or +x"
+msgstr "--chmod параметр \"%s\" має бути -x або +x"
+
+#, c-format
+msgid "'%s' and pathspec arguments cannot be used together"
+msgstr "\"%s\" та аргументи визначника шляху не можна використовувати разом"
+
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr "Нічого не зазначено, нічого не додано.\n"
+
+msgid ""
+"Maybe you wanted to say 'git add .'?\n"
+"Turn this message off by running\n"
+"\"git config advice.addEmptyPathspec false\""
+msgstr ""
+"Можливо, ви хотіли вказати \"git add .\"?\n"
+"Щоб вимкнути це повідомлення, виконайте\n"
+"\"git config advice.addEmptyPathspec false\""
+
+msgid "index file corrupt"
+msgstr "індексний файл пошкоджено"
+
+msgid "unable to write new index file"
+msgstr "не вдалося записати новий файл індексу"
+
+#, c-format
+msgid "bad action '%s' for '%s'"
+msgstr "невірна дія \"%s\" для \"%s\""
+
+#, c-format
+msgid "invalid value for '%s': '%s'"
+msgstr "неприпустиме значення для \"%s\": \"%s\""
+
+#, c-format
+msgid "could not read '%s'"
+msgstr "не вдалося прочитати \"%s\""
+
+msgid "could not parse author script"
+msgstr "не вдалося розібрати author script"
+
+#, c-format
+msgid "could not parse %s"
+msgstr "не вдалося розібрати %s"
+
+#, c-format
+msgid "'%s' was deleted by the applypatch-msg hook"
+msgstr "\"%s\" було видалено applypatch-msg гачком"
+
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "Невірно сформований рядок вводу: \"%s\"."
+
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "Не вдалося скопіювати нотатки з \"%s\" в \"%s\""
+
+msgid "fseek failed"
+msgstr "fseek завершився невдало"
+
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "не вдалося відкрити \"%s\" для читання"
+
+#, c-format
+msgid "could not open '%s' for writing"
+msgstr "не вдалося відкрити \"%s\" для запису"
+
+#, c-format
+msgid "could not parse patch '%s'"
+msgstr "не вдалося розібрати латку \"%s\""
+
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Можна застосовувати лише одну серію StGIT латок одразу"
+
+msgid "invalid timestamp"
+msgstr "неприпустима позначка часу"
+
+msgid "invalid Date line"
+msgstr "неприпустимий рядок дати"
+
+msgid "invalid timezone offset"
+msgstr "неприпустиме зміщення часового поясу"
+
+msgid "Patch format detection failed."
+msgstr "Не вдалося визначити формат латки."
+
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "не вдалося створити директорію \"%s\""
+
+msgid "Failed to split patches."
+msgstr "Не вдалося розщепити латки."
+
+#, c-format
+msgid "When you have resolved this problem, run \"%s --continue\"."
+msgstr "Коли ви вирішите цю проблему, виконайте \"%s --continue\"."
+
+#, c-format
+msgid "If you prefer to skip this patch, run \"%s --skip\" instead."
+msgstr ""
+"Якщо ви бажаєте пропустити цю латку, виконайте \"%s --skip\" замість цього."
+
+#, c-format
+msgid "To record the empty patch as an empty commit, run \"%s --allow-empty\"."
+msgstr ""
+"Щоб записати порожню латку як порожній коміт, виконайте \"%s --allow-empty\"."
+
+#, c-format
+msgid "To restore the original branch and stop patching, run \"%s --abort\"."
+msgstr ""
+"Щоб повернути гілку до початкового стану і зупинити латання, виконайте \"%s "
+"--abort\"."
+
+msgid "Patch sent with format=flowed; space at the end of lines might be lost."
+msgstr ""
+"Латку надіслано з параметром format=flowed; пробіли в кінці рядків можуть "
+"бути втрачені."
+
+#, c-format
+msgid "missing author line in commit %s"
+msgstr "відсутній рядок автора в коміті %s"
+
+#, c-format
+msgid "invalid ident line: %.*s"
+msgstr "неприпустимий ідентифікаційний рядок: %.*s"
+
+#, c-format
+msgid "unable to parse commit %s"
+msgstr "не вдалося розібрати коміт %s"
+
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+"У сховищі не вистачає необхідних blob-обʼєктів, щоб повернутися до "
+"тристороннього злиття."
+
+msgid "Using index info to reconstruct a base tree..."
+msgstr ""
+"Використання індексної інформації для реконструювання базового дерева..."
+
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"Ви відредагували свою латку вручну?\n"
+"Це не стосується blob-обʼєктів, записаних у його індексі."
+
+msgid "Falling back to patching base and 3-way merge..."
+msgstr "Повернення до бази латки та тристороннього злиття..."
+
+msgid "Failed to merge in the changes."
+msgstr "Не вдалося злити зміни."
+
+msgid "git write-tree failed to write a tree"
+msgstr "git write-tree не вдалося записати дерево"
+
+msgid "applying to an empty history"
+msgstr "застосування до порожньої історії"
+
+msgid "failed to write commit object"
+msgstr "не вдалося записати об’єкт коміту"
+
+#, c-format
+msgid "cannot resume: %s does not exist."
+msgstr "неможливо продовжити: %s не існує."
+
+msgid "Commit Body is:"
+msgstr "Тіло коміту:"
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#.
+#, c-format
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "
+msgstr ""
+"Застосувати? [y]-так / [n]-ні / [e]-редагувати / [v]-переглянути латку / [a]-"
+"застосувати все: "
+
+msgid "unable to write index file"
+msgstr "не вдалося записати індексний файл"
+
+#, c-format
+msgid "Dirty index: cannot apply patches (dirty: %s)"
+msgstr "Індекс забруднено: неможливо застосувати латки (забруднено: %s)"
+
+#, c-format
+msgid "Skipping: %.*s"
+msgstr "Пропуск: %.*s"
+
+#, c-format
+msgid "Creating an empty commit: %.*s"
+msgstr "Створення порожнього коміту: %.*s"
+
+msgid "Patch is empty."
+msgstr "Латка порожня."
+
+#, c-format
+msgid "Applying: %.*s"
+msgstr "Застосування: %.*s"
+
+msgid "No changes -- Patch already applied."
+msgstr "Без змін - латку вже застосовано."
+
+#, c-format
+msgid "Patch failed at %s %.*s"
+msgstr "Латання не вдалося на %s %.*s"
+
+msgid "Use 'git am --show-current-patch=diff' to see the failed patch"
+msgstr ""
+"Скористайтесь \"git am --show-current-patch=diff\", щоб побачити невдалу "
+"латку"
+
+msgid "No changes - recorded it as an empty commit."
+msgstr "Без змін - записаний як порожній коміт."
+
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+"Без змін - ви забули використати \"git add\"?\n"
+"Якщо нічого індексувати, є ймовірність того, що хтось інший\n"
+"вже вніс ті самі зміни; можливо, ви захочете пропустити цю латку."
+
+msgid ""
+"You still have unmerged paths in your index.\n"
+"You should 'git add' each file with resolved conflicts to mark them as "
+"such.\n"
+"You might run `git rm` on a file to accept \"deleted by them\" for it."
+msgstr ""
+"У вашому індексі все ще залишаються не злиті шляхи.\n"
+"Вам слід виконати \"git add\" для кожного файлу з розвʼязаними конфліктами, "
+"щоб позначити їх як такі.\n"
+"Ви можете виконати \"git rm\" для файлу, щоб прийняти \"видалено ними\"."
+
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Не вдалося розібрати об'єкт '%s'."
+
+msgid "failed to clean index"
+msgstr "не вдалося очистити індекс"
+
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr ""
+"Здається, ви перемістили HEAD після останньої невдачі з \"am\".\n"
+"Перемотування вперед до ORIG_HEAD не виконується"
+
+#, c-format
+msgid "failed to read '%s'"
+msgstr "не вдалося прочитати \"%s\""
+
+msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
+msgstr "git am [<опції>] [(<скринька> [<поштова директорія>)...]"
+
+msgid "git am [<options>] (--continue | --skip | --abort)"
+msgstr "git am [<опції>] (--continue | --skip | --abort)"
+
+msgid "run interactively"
+msgstr "запустити інтерактивно"
+
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "обійти pre-applypatch та applypatch-msg гачки"
+
+msgid "historical option -- no-op"
+msgstr "стара опція -- не працює"
+
+msgid "allow fall back on 3way merging if needed"
+msgstr "дозволити повернутися до тристороннього злиття у разі потреби"
+
+msgid "be quiet"
+msgstr "працювати тихесенько"
+
+msgid "add a Signed-off-by trailer to the commit message"
+msgstr "додати Signed-off-by причіп у допис до коміту"
+
+msgid "recode into utf8 (default)"
+msgstr "перекодувати в utf8 (за замовчуванням)"
+
+msgid "pass -k flag to git-mailinfo"
+msgstr "передати -k прапорець до git-mailinfo"
+
+msgid "pass -b flag to git-mailinfo"
+msgstr "передати -b прапорець до git-mailinfo"
+
+msgid "pass -m flag to git-mailinfo"
+msgstr "передати -m прапорець до git-mailinfo"
+
+msgid "pass --keep-cr flag to git-mailsplit for mbox format"
+msgstr "передати --keep-cr прапорець до git-mailsplit для формату mbox"
+
+msgid "strip everything before a scissors line"
+msgstr "прибрати все раніше відрізної лінії"
+
+msgid "pass it through git-mailinfo"
+msgstr "передати через git-mailinfo"
+
+msgid "pass it through git-apply"
+msgstr "передати через git-apply"
+
+msgid "n"
+msgstr "н"
+
+msgid "format"
+msgstr "формат"
+
+msgid "format the patch(es) are in"
+msgstr "формат латки(-ок)"
+
+msgid "override error message when patch failure occurs"
+msgstr "перевизначити повідомлення про помилку при збої латання"
+
+msgid "continue applying patches after resolving a conflict"
+msgstr "продовжити застосування латки після вирішення конфлікту"
+
+msgid "synonyms for --continue"
+msgstr "те ж саме, що й --continue"
+
+msgid "skip the current patch"
+msgstr "пропустити поточну латку"
+
+msgid "restore the original branch and abort the patching operation"
+msgstr "відновити початкову гілку і перервати операцію латання"
+
+msgid "abort the patching operation but keep HEAD where it is"
+msgstr "перервати латання, але залишити HEAD на тому місці, де він знаходиться"
+
+msgid "show the patch being applied"
+msgstr "показати латку, що застосовується"
+
+msgid "record the empty patch as an empty commit"
+msgstr "записати порожню латку як порожній коміт"
+
+msgid "lie about committer date"
+msgstr "брехати про дату комітера"
+
+msgid "use current timestamp for author date"
+msgstr "використовувати поточну мітку часу для дати автора"
+
+msgid "key-id"
+msgstr "key-id"
+
+msgid "GPG-sign commits"
+msgstr "підписати коміти GPG-підписом"
+
+msgid "how to handle empty patches"
+msgstr "як обробляти порожні латки"
+
+msgid "(internal use for git-rebase)"
+msgstr "(внутрішнє використання для git-rebase)"
+
+msgid ""
+"The -b/--binary option has been a no-op for long time, and\n"
+"it will be removed. Please do not use it anymore."
+msgstr ""
+"Опція -b/--binary вже давно не працює і\n"
+"буде видалена. Будь ласка, не використовуйте її більше."
+
+msgid "failed to read the index"
+msgstr "не вдалося прочитати індекс"
+
+#, c-format
+msgid "previous rebase directory %s still exists but mbox given."
+msgstr ""
+"попередня директорія перебазування %s все ще існує, але було надано mbox."
+
+#, c-format
+msgid ""
+"Stray %s directory found.\n"
+"Use \"git am --abort\" to remove it."
+msgstr ""
+"Знайдено блукаючу директорію %s.\n"
+"скористайтесь командою \"git am --abort\", щоб вилучити її."
+
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr "Наразі не виконується операція вирішення, не поновлено."
+
+msgid "interactive mode requires patches on the command line"
+msgstr "інтерактивний режим потребує латки у командному рядку"
+
+msgid "git apply [<options>] [<patch>...]"
+msgstr "git apply [<опції>] [<латка>...]"
+
+msgid "could not redirect output"
+msgstr "неможливо перенаправити вивід"
+
+msgid "git archive: Remote with no URL"
+msgstr "git archive: віддалене призначення без URL"
+
+msgid "git archive: expected ACK/NAK, got a flush packet"
+msgstr "git archive: очікувалось ACK/NAK, отримано flush-пакет"
+
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive: NACK %s"
+
+msgid "git archive: protocol error"
+msgstr "git archive: помилка протоколу"
+
+msgid "git archive: expected a flush"
+msgstr "git archive: очікувалось flush"
+
+msgid ""
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
+msgstr ""
+"git bisect start [--term-{new,bad}=<термін> --term-{old,good}=<термін>]    "
+"[--no-checkout] [--first-parent] [<поганий> [<добрий>...]] [--]    "
+"[<визначник шляху>...]"
+
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (добрий|поганий) [<ревізія>...]"
+
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<ревізія>|<діапазон>)...]"
+
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<коміт>]"
+
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <лог файл>"
+
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <команда> [<аргумент>...]"
+
+#, c-format
+msgid "cannot open file '%s' in mode '%s'"
+msgstr "неможливо відкрити файл \"%s\" у режимі \"%s\""
+
+#, c-format
+msgid "could not write to file '%s'"
+msgstr "не вдалося записати в файл \"%s\""
+
+#, c-format
+msgid "cannot open file '%s' for reading"
+msgstr "неможливо відкрити файл \"%s\" для читання"
+
+#, c-format
+msgid "'%s' is not a valid term"
+msgstr "\"%s\" не є допустимим терміном"
+
+#, c-format
+msgid "can't use the builtin command '%s' as a term"
+msgstr "неможливо використати вбудовану команду \"%s\" як термін"
+
+#, c-format
+msgid "can't change the meaning of the term '%s'"
+msgstr "неможливо змінити значення терміну \"%s\""
+
+msgid "please use two different terms"
+msgstr "будь ласка, використовуйте два різних терміна"
+
+#, c-format
+msgid "We are not bisecting.\n"
+msgstr "Ми не робимо бісекцію.\n"
+
+#, c-format
+msgid "'%s' is not a valid commit"
+msgstr "\"%s\" не є дійсним комітом"
+
+#, c-format
+msgid ""
+"could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
+msgstr ""
+"не вдалося переключитися на початковий HEAD \"%s\". Спробуйте \"git bisect "
+"reset <коміт>\"."
+
+#, c-format
+msgid "Bad bisect_write argument: %s"
+msgstr "Невірний bisect_write аргумент: %s"
+
+#, c-format
+msgid "couldn't get the oid of the rev '%s'"
+msgstr "не вдалося отримати oid для rev \"%s\""
+
+#, c-format
+msgid "couldn't open the file '%s'"
+msgstr "не вдалося відкрити файл \"%s\""
+
+#, c-format
+msgid "Invalid command: you're currently in a %s/%s bisect"
+msgstr "Неприпустима команда: наразі ви в процесі %s/%s бісекції"
+
+#, c-format
+msgid ""
+"You need to give me at least one %s and %s revision.\n"
+"You can use \"git bisect %s\" and \"git bisect %s\" for that."
+msgstr ""
+"Ви маєте надати принаймні одну %s та %s ревізію.\n"
+"Для цього ви можете скористатися командами \"git bisect %s\" і \"git bisect "
+"%s\"."
+
+#, c-format
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one %s and %s revision.\n"
+"You can use \"git bisect %s\" and \"git bisect %s\" for that."
+msgstr ""
+"Вам потрібно почати з \"git bisect start\".\n"
+"Потім вам потрібно надати принаймні одну %s і %s ревізію.\n"
+"Для цього ви можете скористатися командами \"git bisect %s\" і \"git bisect "
+"%s\"."
+
+#, c-format
+msgid "bisecting only with a %s commit"
+msgstr "бісекція лише з %s комітом"
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#.
+msgid "Are you sure [Y/n]? "
+msgstr "Ви впевнені [Y/n]? "
+
+msgid "status: waiting for both good and bad commits\n"
+msgstr "статус: чекаємо на добрий і поганий коміти\n"
+
+#, c-format
+msgid "status: waiting for bad commit, %d good commit known\n"
+msgid_plural "status: waiting for bad commit, %d good commits known\n"
+msgstr[0] "статус: чекаємо на поганий коміт, відомий %d добрий коміт\n"
+msgstr[1] "статус: чекаємо на поганий коміт, відомі %d добрих коміти\n"
+msgstr[2] "статус: чекаємо на поганий коміт, відомі %d добрих комітів\n"
+
+msgid "status: waiting for good commit(s), bad commit known\n"
+msgstr "статус: чекаємо на добрий коміт(и), поганий коміт відомий\n"
+
+msgid "no terms defined"
+msgstr "терміни не визначені"
+
+#, c-format
+msgid ""
+"Your current terms are %s for the old state\n"
+"and %s for the new state.\n"
+msgstr ""
+"Ваші поточні терміни: %s для старого стану\n"
+"і %s для нового стану.\n"
+
+#, c-format
+msgid ""
+"invalid argument %s for 'git bisect terms'.\n"
+"Supported options are: --term-good|--term-old and --term-bad|--term-new."
+msgstr ""
+"неприпустимий аргумент %s для \"git bisect terms\".\n"
+"Підтримувані опції: --term-good|--term-old і --term-bad|--term-new."
+
+msgid "revision walk setup failed\n"
+msgstr "не вдалося налаштувати проходження по ревізіям\n"
+
+#, c-format
+msgid "could not open '%s' for appending"
+msgstr "не вдалося відкрити \"%s\" для додавання"
+
+msgid "'' is not a valid term"
+msgstr "\"\" не є допустимим терміном"
+
+#, c-format
+msgid "unrecognized option: '%s'"
+msgstr "нерозпізнана опція: \"%s\""
+
+#, c-format
+msgid "'%s' does not appear to be a valid revision"
+msgstr "\"%s\" не є дійсною ревізією"
+
+msgid "bad HEAD - I need a HEAD"
+msgstr "невірний HEAD - потрібен HEAD"
+
+#, c-format
+msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
+msgstr ""
+"переключення на \"%s\" завершилося невдало. Спробуйте \"git bisect start "
+"<дійсна-гілка>\"."
+
+msgid "bad HEAD - strange symbolic ref"
+msgstr "невірний HEAD - дивне символьне посилання"
+
+#, c-format
+msgid "invalid ref: '%s'"
+msgstr "неприпустиме посилання: \"%s\""
+
+msgid "You need to start by \"git bisect start\"\n"
+msgstr "Вам потрібно почати з \"git bisect start\"\n"
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#.
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "Ви хочете, щоб я зробив це для вас [Y/n]? "
+
+msgid "Please call `--bisect-state` with at least one argument"
+msgstr ""
+"Будь ласка, використовуйте \"--bisect-state\" з принаймні одним аргументом"
+
+#, c-format
+msgid "'git bisect %s' can take only one argument."
+msgstr "\"git bisect %s\" приймає лише один аргумент."
+
+#, c-format
+msgid "Bad rev input: %s"
+msgstr "Невірне значення ревізії: %s"
+
+#, c-format
+msgid "Bad rev input (not a commit): %s"
+msgstr "Невірне значення ревізії (не є комітом): %s"
+
+msgid "We are not bisecting."
+msgstr "Наразі ви не робите бісекцію."
+
+#, c-format
+msgid "'%s'?? what are you talking about?"
+msgstr "\"%s\"? Що ви маєте на увазі?"
+
+#, c-format
+msgid "cannot read file '%s' for replaying"
+msgstr "не вдалося прочитати файл \"%s\" для відтворення"
+
+#, c-format
+msgid "running %s\n"
+msgstr "виконання %s\n"
+
+msgid "bisect run failed: no command provided."
+msgstr "бісекція завершилася невдало: не надано команду."
+
+#, c-format
+msgid "unable to verify %s on good revision"
+msgstr "не вдалося розпізнати %s як припустиму ревізію"
+
+#, c-format
+msgid "bogus exit code %d for good revision"
+msgstr "хибний код виходу %d для доброї ревізії"
+
+#, c-format
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "бісекція завершилася невдало: код виходу %d з %s є < 0 або >= 128"
+
+#, c-format
+msgid "cannot open file '%s' for writing"
+msgstr "не вдалося відкрити файл \"%s\" для запису"
+
+msgid "bisect run cannot continue any more"
+msgstr "неможливо продовжити бісекцію"
+
+msgid "bisect run success"
+msgstr "бісекцію завершено успішно"
+
+msgid "bisect found first bad commit"
+msgstr "бісекція знайшла перший поганий коміт"
+
+#, c-format
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr ""
+"бісекція завершилася невдало: \"git bisect %s\" завершено з кодом помилки %d"
+
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "\"%s\" потребує або відсутності аргументу, або коміт"
+
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "\"%s\" потребує 0 або 1 аргумент"
+
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "\"%s\" потребує 0 аргументів"
+
+msgid "no logfile given"
+msgstr "не надано файл журналу"
+
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "\"%s\" завершено невдало: не надано команду."
+
+msgid "need a command"
+msgstr "потрібна команда"
+
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "невідома команда: \"%s\""
+
+msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git blame [<опції>] [<rev-опції>] [<ревізія>] [--] <файл>"
+
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<опції>] [<rev-опції>] [<ревізія>] [--] <файл>"
+
+msgid "<rev-opts> are documented in git-rev-list(1)"
+msgstr "<rev-опції> задокументовані у git-rev-list(1)"
+
+#, c-format
+msgid "expecting a color: %s"
+msgstr "очікування кольору: %s"
+
+msgid "must end with a color"
+msgstr "повинно закінчуватися кольором"
+
+#, c-format
+msgid "cannot find revision %s to ignore"
+msgstr "неможливо знайти ревізію %s для ігнорування"
+
+msgid "show blame entries as we find them, incrementally"
+msgstr "показувати blame записи по мірі того, як ми їх знаходимо, поступово"
+
+msgid "do not show object names of boundary commits (Default: off)"
+msgstr "не показувати назви об’єктів межевих комітів (за замовчуванням: off)"
+
+msgid "do not treat root commits as boundaries (Default: off)"
+msgstr "не обробляти кореневі коміти як межі (за замовчуванням: off)"
+
+msgid "show work cost statistics"
+msgstr "показати статистику вартості робіт"
+
+msgid "force progress reporting"
+msgstr "примусово звітувати про хід виконання"
+
+msgid "show output score for blame entries"
+msgstr "показати вихідний показник для blame записів"
+
+msgid "show original filename (Default: auto)"
+msgstr "показати оригінальне ім’я файлу (за замовчуванням: auto)"
+
+msgid "show original linenumber (Default: off)"
+msgstr "показувати початковий номер рядка (за замовчуванням: off)"
+
+msgid "show in a format designed for machine consumption"
+msgstr "показувати у форматі, призначеному для машинного споживання"
+
+msgid "show porcelain format with per-line commit information"
+msgstr ""
+"показати у porcelain форматі з інформацією про кожен коміт в окремому рядку"
+
+msgid "use the same output mode as git-annotate (Default: off)"
+msgstr ""
+"використовувати той самий режим виводу, що і git-annotate (за замовчуванням: "
+"off)"
+
+msgid "show raw timestamp (Default: off)"
+msgstr "показувати необроблену мітку часу (за замовчуванням: off)"
+
+msgid "show long commit SHA1 (Default: off)"
+msgstr "показувати довгу версію SHA1 коміту (за замовчуванням: off)"
+
+msgid "suppress author name and timestamp (Default: off)"
+msgstr "приховати ім’я автора та мітку часу (за замовчуванням: off)"
+
+msgid "show author email instead of name (Default: off)"
+msgstr "показувати email автора замість імені (за замовчуванням: off)"
+
+msgid "ignore whitespace differences"
+msgstr "ігнорувати різницю між пробільними символами"
+
+msgid "rev"
+msgstr "ревізія"
+
+msgid "ignore <rev> when blaming"
+msgstr "ігнорувати <ревізію> при blame"
+
+msgid "ignore revisions from <file>"
+msgstr "ігнорувати ревізії з <файлу>"
+
+msgid "color redundant metadata from previous line differently"
+msgstr "розфарбувати надлишкові метадані з попереднього рядка інакше"
+
+msgid "color lines by age"
+msgstr "розфарбувати рядки за віком"
+
+msgid "spend extra cycles to find better match"
+msgstr "витрачати додаткові цикли для пошуку кращого варіанта"
+
+msgid "use revisions from <file> instead of calling git-rev-list"
+msgstr "використовувати ревізії з <файлу> замість виклику git-rev-list"
+
+msgid "use <file>'s contents as the final image"
+msgstr "використовувати вміст <файлу> як кінцевий образ"
+
+msgid "score"
+msgstr "показник"
+
+msgid "find line copies within and across files"
+msgstr "знаходити копії рядків у файлах та між ними"
+
+msgid "find line movements within and across files"
+msgstr "знаходити переміщення рядків у файлі та між ними"
+
+msgid "range"
+msgstr "діапазон"
+
+msgid "process only line range <start>,<end> or function :<funcname>"
+msgstr ""
+"обробити тільки діапазон рядків <початок>,<кінець> або функцію :<назва-"
+"функції>"
+
+msgid "--progress can't be used with --incremental or porcelain formats"
+msgstr ""
+"--progress не можна використовувати з форматами --incremental або porcelain"
+
+#. TRANSLATORS: This string is used to tell us the
+#. maximum display width for a relative timestamp in
+#. "git blame" output.  For C locale, "4 years, 11
+#. months ago", which takes 22 places, is the longest
+#. among various forms of relative timestamps, but
+#. your language may need more or fewer display
+#. columns.
+#.
+msgid "4 years, 11 months ago"
+msgstr "4 роки, 11 місяців тому"
+
+#, c-format
+msgid "file %s has only %lu line"
+msgid_plural "file %s has only %lu lines"
+msgstr[0] "файл %s містить лише %lu рядок"
+msgstr[1] "файл %s містить лише %lu рядки"
+msgstr[2] "файл %s містить лише %lu рядків"
+
+msgid "Blaming lines"
+msgstr "Blaming рядки"
+
+msgid "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
+msgstr "git branch [<опції>] [-r | -a] [--merged] [--no-merged]"
+
+msgid ""
+"git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
+"point>]"
+msgstr ""
+"git branch [<опції>] [-f] [--recurse-submodules] <назва-гілки> [<початкова-"
+"точка>]"
+
+msgid "git branch [<options>] [-l] [<pattern>...]"
+msgstr "git branch [<опції>] [-l] [<шаблон>...]"
+
+msgid "git branch [<options>] [-r] (-d | -D) <branch-name>..."
+msgstr "git branch [<опції>] [-r] (-d | -D) <имʼя-гілки>..."
+
+msgid "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"
+msgstr "git branch [<опції>] (-m | -M) [<стара-гілка>] <нова-гілка>"
+
+msgid "git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"
+msgstr "git branch [<опції>] (-c | -C) [<стара-гілка>] <нова-гілка>"
+
+msgid "git branch [<options>] [-r | -a] [--points-at]"
+msgstr "git branch [<опції>] [-r | -a] [--points-at]"
+
+msgid "git branch [<options>] [-r | -a] [--format]"
+msgstr "git branch [<опції>] [-r | -a] [--format]"
+
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD"
+msgstr ""
+"видалення гілки \"%s\", яка була злита з\n"
+"         \"%s\", але ще не злита з HEAD"
+
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD"
+msgstr ""
+"утримання гілки \"%s\", яка ще не була злита з\n"
+"         \"%s\", незважаючи на те, що вже була злита з HEAD"
+
+#, c-format
+msgid "couldn't look up commit object for '%s'"
+msgstr "не вдалося знайти об’єкт коміту для \"%s\""
+
+#, c-format
+msgid ""
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr ""
+"гілка \"%s\" злита не повністю.\n"
+"Якщо ви впевнені, що хочете її видалити, виконайте \"git branch -D %s\""
+
+msgid "update of config-file failed"
+msgstr "не вдалося оновити конфігураційний файл"
+
+msgid "cannot use -a with -d"
+msgstr "не можна використовувати -a з -d"
+
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"неможливо видалити гілку \"%s\", яка використовується робочим деревом у "
+"\"%s\""
+
+#, c-format
+msgid "remote-tracking branch '%s' not found"
+msgstr "віддалено відстежувана гілка \"%s\" не знайдена"
+
+#, c-format
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"гілка \"%s\" не знайдена.\n"
+"Ви забули --remote?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "гілка \"%s\" не знайдена"
+
+#, c-format
+msgid "Deleted remote-tracking branch %s (was %s).\n"
+msgstr "Видалено гілку віддаленого відстеження %s (була %s).\n"
+
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Видалено гілку %s (була %s).\n"
+
+msgid "unable to parse format string"
+msgstr "не вдалося розібрати рядок форматування"
+
+msgid "could not resolve HEAD"
+msgstr "не вдалося розвʼязати HEAD"
+
+#, c-format
+msgid "HEAD (%s) points outside of refs/heads/"
+msgstr "HEAD (%s) пунктів за межами refs/heads/"
+
+#, c-format
+msgid "branch %s is being rebased at %s"
+msgstr "гілка %s перебазується на %s"
+
+#, c-format
+msgid "branch %s is being bisected at %s"
+msgstr "гілка %s бісектується в %s"
+
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD робочого дерева %s не оновлено"
+
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "неприпустима назва гілки: \"%s\""
+
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "поки що немає комітів в гілці \"%s\""
+
+#, c-format
+msgid "no branch named '%s'"
+msgstr "немає гілки з ім’ям \"%s\""
+
+msgid "branch rename failed"
+msgstr "не вдалося перейменувати гілку"
+
+msgid "branch copy failed"
+msgstr "не вдалося скопіювати гілку"
+
+#, c-format
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "створено копію невірно названої гілки \"%s\""
+
+#, c-format
+msgid "renamed a misnamed branch '%s' away"
+msgstr "перейменовано невірно названу гілку \"%s\""
+
+#, c-format
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "гілку перейменовано на %s, але HEAD не оновлено"
+
+msgid "branch is renamed, but update of config-file failed"
+msgstr "гілку перейменовано, але не вдалося оновити конфігураційний файл"
+
+msgid "branch is copied, but update of config-file failed"
+msgstr "гілку скопійовано, але не вдалося оновити конфігураційний файл"
+
+#, c-format
+msgid ""
+"Please edit the description for the branch\n"
+"  %s\n"
+"Lines starting with '%c' will be stripped.\n"
+msgstr ""
+"Будь ласка, відредагуйте опис гілки\n"
+"  %s\n"
+"Рядки, що починаються з \"%c\", будуть вилучені.\n"
+
+msgid "Generic options"
+msgstr "Загальні опції"
+
+msgid "show hash and subject, give twice for upstream branch"
+msgstr ""
+"показувати хеш і тему, додайте двічі, щоб застосувати для висхідної гілки"
+
+msgid "suppress informational messages"
+msgstr "приховати інформаційні повідомлення"
+
+msgid "set branch tracking configuration"
+msgstr "налаштувати конфігурацію відстежуваних гілок"
+
+msgid "do not use"
+msgstr "не використовувати"
+
+msgid "upstream"
+msgstr "першоджерельне сховище"
+
+msgid "change the upstream info"
+msgstr "змінити інформацію щодо першоджерельного сховища"
+
+msgid "unset the upstream info"
+msgstr "скинути інформацію щодо першоджерельного сховища"
+
+msgid "use colored output"
+msgstr "використовувати кольоровий вивід"
+
+msgid "act on remote-tracking branches"
+msgstr "працювати з віддалено відстежуваними гілками"
+
+msgid "print only branches that contain the commit"
+msgstr "виводити тільки гілки, що містять цей коміт"
+
+msgid "print only branches that don't contain the commit"
+msgstr "виводити тільки гілки, що не містять цей коміт"
+
+msgid "Specific git-branch actions:"
+msgstr "Специфічні git-branch дії:"
+
+msgid "list both remote-tracking and local branches"
+msgstr "показати як віддалено відстежувані, так і локальні гілки"
+
+msgid "delete fully merged branch"
+msgstr "видалити повністю злиту гілку"
+
+msgid "delete branch (even if not merged)"
+msgstr "видалити гілку (навіть не повністю злиту)"
+
+msgid "move/rename a branch and its reflog"
+msgstr "перемістити/перейменувати гілку та її журнал посилань"
+
+msgid "move/rename a branch, even if target exists"
+msgstr "перемістити/перейменувати гілку, навіть якщо призначення існує"
+
+msgid "do not output a newline after empty formatted refs"
+msgstr "не виводити новий рядок після порожніх форматованих посилань"
+
+msgid "copy a branch and its reflog"
+msgstr "скопіювати гілку та її журнал посилань"
+
+msgid "copy a branch, even if target exists"
+msgstr "скопіювати гілку, навіть якщо призначення існує"
+
+msgid "list branch names"
+msgstr "показати назви гілок"
+
+msgid "show current branch name"
+msgstr "показати назву поточної гілки"
+
+msgid "create the branch's reflog"
+msgstr "створити журнал посилань гілки"
+
+msgid "edit the description for the branch"
+msgstr "редагувати опис гілки"
+
+msgid "force creation, move/rename, deletion"
+msgstr "примусове створення, переміщення/перейменування, видалення"
+
+msgid "print only branches that are merged"
+msgstr "вивести тільки злиті гілки"
+
+msgid "print only branches that are not merged"
+msgstr "вивести тільки не злиті гілки"
+
+msgid "list branches in columns"
+msgstr "виводити гілки в стовпчиках"
+
+msgid "object"
+msgstr "обʼєкт"
+
+msgid "print only branches of the object"
+msgstr "виводити тільки гілки об’єкта"
+
+msgid "sorting and filtering are case insensitive"
+msgstr "сортувати та фільтрувати незалежно від регістру"
+
+msgid "recurse through submodules"
+msgstr "рекурсивно через підмодулі"
+
+msgid "format to use for the output"
+msgstr "формат, що використовувати для виводу"
+
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "не вдалося розпізнати HEAD як дійсне посилання"
+
+msgid "HEAD not found below refs/heads!"
+msgstr "HEAD не знайдено під refs/heads!"
+
+msgid ""
+"branch with --recurse-submodules can only be used if submodule."
+"propagateBranches is enabled"
+msgstr ""
+"гілку з --recurse-submodules можна використовувати лише якщо увімкнено "
+"submodule.propagateBranches"
+
+msgid "--recurse-submodules can only be used to create branches"
+msgstr "--recurse-submodules можна використовувати лише для створення гілок"
+
+msgid "branch name required"
+msgstr "назва гілки є обовʼязковою"
+
+msgid "cannot give description to detached HEAD"
+msgstr "неможливо надати опис відокремленому HEAD"
+
+msgid "cannot edit description of more than one branch"
+msgstr "неможливо редагувати опис більш ніж однієї гілки"
+
+msgid "cannot copy the current branch while not on any"
+msgstr "неможливо скопіювати поточну гілку, не перебуваючи на жодній з них"
+
+msgid "cannot rename the current branch while not on any"
+msgstr "неможливо перейменувати поточну гілку, не перебуваючи на жодній з них"
+
+msgid "too many branches for a copy operation"
+msgstr "забагато гілок для операції копіювання"
+
+msgid "too many arguments for a rename operation"
+msgstr "забагато аргументів для операції перейменування"
+
+msgid "too many arguments to set new upstream"
+msgstr "забагато аргументів для встановлення нового першоджерельного сховища"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to %s when it does not point to any branch"
+msgstr ""
+"не вдалося встановити першождерельне сховище HEAD у %s, який не вказує на "
+"жодну гілку"
+
+#, c-format
+msgid "no such branch '%s'"
+msgstr "немає такої гілки \"%s\""
+
+#, c-format
+msgid "branch '%s' does not exist"
+msgstr "гілка \"%s\" не існує"
+
+msgid "too many arguments to unset upstream"
+msgstr "забагато аргументів для скидання значення першоджерельного сховища"
+
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr ""
+"не вдалося скинути значення першоджерельного сховища для HEAD, який не "
+"вказує на жодну гілку"
+
+#, c-format
+msgid "branch '%s' has no upstream information"
+msgstr "гілка \"%s\" не має інформації щодо першоджерельного сховища"
+
+msgid ""
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
+"Did you mean to use: -a|-r --list <pattern>?"
+msgstr ""
+"опції -a та -r для \"git branch\" не приймають назву гілки.\n"
+"Ви хотіли використати -a|-r --list <шаблон>?"
+
+msgid ""
+"the '--set-upstream' option is no longer supported. Please use '--track' or "
+"'--set-upstream-to' instead"
+msgstr ""
+"опція \"--set-upstream\" більше не підтримується. Будь ласка, використовуйте "
+"\"--track\" або \"--set-upstream-to\""
+
+msgid "git version:\n"
+msgstr "версія git:\n"
+
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() завершився невдало з помилкою \"%s\" (%d)\n"
+
+msgid "compiler info: "
+msgstr "інформація щодо компілятора: "
+
+msgid "libc info: "
+msgstr "информація щодо libc: "
+
+msgid "not run from a git repository - no hooks to show\n"
+msgstr "запущено не з git сховища - немає гачків для показу\n"
+
+msgid ""
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
+msgstr ""
+"git bugreport [(-o | --output-directory) <шлях>] [(-s | --suffix) <формат>]\n"
+"              [--diagnose[=<режим>]]"
+
+msgid ""
+"Thank you for filling out a Git bug report!\n"
+"Please answer the following questions to help us understand your issue.\n"
+"\n"
+"What did you do before the bug happened? (Steps to reproduce your issue)\n"
+"\n"
+"What did you expect to happen? (Expected behavior)\n"
+"\n"
+"What happened instead? (Actual behavior)\n"
+"\n"
+"What's different between what you expected and what actually happened?\n"
+"\n"
+"Anything else you want to add:\n"
+"\n"
+"Please review the rest of the bug report below.\n"
+"You can delete any lines you don't wish to share.\n"
+msgstr ""
+"Дякуємо, що заповнили звіт про помилку в Git!\n"
+"Будь ласка, дайте відповіді на наступні питання, щоб допомогти нам зрозуміти "
+"вашу проблему.\n"
+"\n"
+"Що ви робили до того, як сталася помилка (кроки для відтворення вашої "
+"проблеми)\n"
+"\n"
+"Який результат ви очікували? (Очікувана поведінка)\n"
+"\n"
+"Що сталося замість цього? (Фактична поведінка)\n"
+"\n"
+"Чим відрізняється те, що ви очікували, від того, що сталося насправді?\n"
+"\n"
+"Будь-що інше, що ви хочете додати:\n"
+"\n"
+"Будь ласка, перегляньте решту повідомлення про ваду нижче.\n"
+"Ви можете видалити будь-які рядки, якими не хочете ділитися.\n"
+
+msgid "mode"
+msgstr "режим"
+
+msgid ""
+"create an additional zip archive of detailed diagnostics (default 'stats')"
+msgstr ""
+"створити додатковий zip-архів з детальною діагностикою (за замовчуванням "
+"\"stats\")"
+
+msgid "specify a destination for the bugreport file(s)"
+msgstr "вказати місце призначення для файла(-ів) звіта про помилку"
+
+msgid "specify a strftime format suffix for the filename(s)"
+msgstr "вказати суфікс формату strftime для назви файла(-ів)"
+
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "невідомий аргумент \"%s\""
+
+#, c-format
+msgid "could not create leading directories for '%s'"
+msgstr "не вдалося створити провідні каталоги для \"%s\""
+
+#, c-format
+msgid "unable to create diagnostics archive %s"
+msgstr "не вдалося створити архів діагностики %s"
+
+msgid "System Info"
+msgstr "Інформація про систему"
+
+msgid "Enabled Hooks"
+msgstr "Увімкнені гачки"
+
+#, c-format
+msgid "unable to write to %s"
+msgstr "не вдалося записати до %s"
+
+#, c-format
+msgid "Created new report at '%s'.\n"
+msgstr "Створено новий звіт в \"%s\".\n"
+
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<версія>] <файл> <git-rev-list-агрументи>"
+
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <файл>"
+
+msgid "git bundle list-heads <file> [<refname>...]"
+msgstr "git bundle list-heads <файл> [<назва-посилання>...]"
+
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <файл> [<назва-посилання>...]"
+
+msgid "need a <file> argument"
+msgstr "потрібен аргумент <файл>"
+
+msgid "do not show progress meter"
+msgstr "не показувати хід виконання"
+
+msgid "show progress meter"
+msgstr "показувати хід виконання"
+
+msgid "historical; same as --progress"
+msgstr "історичний; те саме, що --progress"
+
+msgid "historical; does nothing"
+msgstr "історична; не робить нічогісенько"
+
+msgid "specify bundle format version"
+msgstr "вказати версію формату пакунка"
+
+msgid "Need a repository to create a bundle."
+msgstr "Для створення пакунка потрібне сховище."
+
+msgid "do not show bundle details"
+msgstr "не показувати деталі пакунка"
+
+#, c-format
+msgid "%s is okay\n"
+msgstr "%s у порядку\n"
+
+msgid "Need a repository to unbundle."
+msgstr "Потрібне сховище для розділення."
+
+msgid "Unbundling objects"
+msgstr "Розділення об’єктів"
+
+#, c-format
+msgid "cannot read object %s '%s'"
+msgstr "неможливо прочитати об’єкт %s \"%s\""
+
+msgid "flush is only for --buffer mode"
+msgstr "flush тільки для режиму --buffer"
+
+msgid "empty command in input"
+msgstr "порожня команда на вході"
+
+#, c-format
+msgid "whitespace before command: '%s'"
+msgstr "пробільний символ перед командою: \"%s\""
+
+#, c-format
+msgid "%s requires arguments"
+msgstr "%s потребує аргументів"
+
+#, c-format
+msgid "%s takes no arguments"
+msgstr "%s не потребує аргументів"
+
+msgid "only one batch option may be specified"
+msgstr "можна вказати лише одну групову опцію"
+
+msgid "git cat-file <type> <object>"
+msgstr "git cat-file <тип> <об’єкт>"
+
+msgid "git cat-file (-e | -p) <object>"
+msgstr "git cat-file (-e | -p) <об’єкт>"
+
+msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
+msgstr "git cat-file (-t | -s) [--allow-unknown-type] <об’єкт>"
+
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<ревізія>:<шлях|деревоподібне-джерело> | --path=<шлях|"
+"деревоподібне-джерело> <ревізія>]"
+
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+
+msgid "Check object existence or emit object contents"
+msgstr "Перевірити існування об’єкта або видати вміст об’єкта"
+
+msgid "check if <object> exists"
+msgstr "перевірити, чи існує <об’єкт>"
+
+msgid "pretty-print <object> content"
+msgstr "pretty-print вміст <об’єкта>"
+
+msgid "Emit [broken] object attributes"
+msgstr "Видати [пошкоджені] атрибути об’єкта"
+
+msgid "show object type (one of 'blob', 'tree', 'commit', 'tag', ...)"
+msgstr ""
+"показати тип об’єкта (один з \"blob\", \"tree\", \"commit\", \"tag\", ...)"
+
+msgid "show object size"
+msgstr "показати розмір об’єкта"
+
+msgid "allow -s and -t to work with broken/corrupt objects"
+msgstr "дозволити -s та -t працювати з пошкодженими/зіпсованими об’єктами"
+
+msgid "use mail map file"
+msgstr "використовувати файл відповідності поштових адрес"
+
+msgid "Batch objects requested on stdin (or --batch-all-objects)"
+msgstr "Пакетний запит об’єктів з stdin (або --batch-all-objects)"
+
+msgid "show full <object> or <rev> contents"
+msgstr "показати повний вміст <об’єкта> або <ревізії>"
+
+msgid "like --batch, but don't emit <contents>"
+msgstr "як --batch, але не виводить <вміст>"
+
+msgid "stdin is NUL-terminated"
+msgstr "stdin закінчується NUL"
+
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin та stdout закінчуються NUL"
+
+msgid "read commands from stdin"
+msgstr "читати команди з stdin"
+
+msgid "with --batch[-check]: ignores stdin, batches all known objects"
+msgstr "з --batch[-check]: ігнорує stdin, групує всі відомі об’єкти"
+
+msgid "Change or optimize batch output"
+msgstr "Змінити або оптимізувати груповий вивід"
+
+msgid "buffer --batch output"
+msgstr "буферизувати --batch вивід"
+
+msgid "follow in-tree symlinks"
+msgstr "розвʼязувати символьні посилання в дереві"
+
+msgid "do not order objects before emitting them"
+msgstr "не упорядковувати об’єкти перед випуском"
+
+msgid ""
+"Emit object (blob or tree) with conversion or filter (stand-alone, or with "
+"batch)"
+msgstr ""
+"Видавати об’єкт (blob або tree) з перетворенням або фільтром (окремо або з "
+"групою)"
+
+msgid "run textconv on object's content"
+msgstr "виконати textconv над вмістом об’єкта"
+
+msgid "run filters on object's content"
+msgstr "запустити фільтри на вміст об’єкта"
+
+msgid "blob|tree"
+msgstr "blob|дерево"
+
+msgid "use a <path> for (--textconv | --filters); Not with 'batch'"
+msgstr "використати <шлях> для (--textconv | --filters); не з \"batch\""
+
+#, c-format
+msgid "'%s=<%s>' needs '%s' or '%s'"
+msgstr "\"%s=<%s>\" потребує \"%s\" або \"%s\""
+
+msgid "path|tree-ish"
+msgstr "шлях|деревоподібне-джерело"
+
+#, c-format
+msgid "'%s' requires a batch mode"
+msgstr "\"%s\" потребує групового режиму"
+
+#, c-format
+msgid "'-%c' is incompatible with batch mode"
+msgstr "\"-%c\" несумісний з груповим режимом"
+
+msgid "batch modes take no arguments"
+msgstr "групові режими не потребують аргументів"
+
+#, c-format
+msgid "<rev> required with '%s'"
+msgstr "<ревізія> є обов’язковою для \"%s\""
+
+#, c-format
+msgid "<object> required with '-%c'"
+msgstr "<об’єкт> є обов’язковим для \"-%c\""
+
+#, c-format
+msgid "only two arguments allowed in <type> <object> mode, not %d"
+msgstr "у режимі <тип> <об’єкт> дозволено лише два аргументи, а не %d"
+
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <деревоподібне-джерело>] [-a | --all | "
+"<атрибут>...] [--] <назва-шляху>..."
+
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <деревоподібне-джерело>] [-a | --all | "
+"<атрибут>...]"
+
+msgid "report all attributes set on file"
+msgstr "звітувати про всі атрибути, встановлені у файлі"
+
+msgid "use .gitattributes only from the index"
+msgstr "використовувати .gitattributes тільки з індексу"
+
+msgid "read file names from stdin"
+msgstr "зчитувати назви файлів з stdin"
+
+msgid "terminate input and output records by a NUL character"
+msgstr "завершувати вхідні та вихідні записи символом NUL"
+
+msgid "<tree-ish>"
+msgstr "<деревоподібне-джерело>"
+
+msgid "which tree-ish to check attributes at"
+msgstr "атрибути якого деревоподібного джерела перевіряти"
+
+msgid "suppress progress reporting"
+msgstr "не звітувати про хід виконання"
+
+msgid "show non-matching input paths"
+msgstr "показати неспівпадіння вхідних шляхів"
+
+msgid "ignore index when checking"
+msgstr "ігнорувати індекс під час перевірки"
+
+msgid "cannot specify pathnames with --stdin"
+msgstr "неможливо вказувати назви шляхів із --stdin"
+
+msgid "-z only makes sense with --stdin"
+msgstr "-z має сенс тільки з --stdin"
+
+msgid "no path specified"
+msgstr "шлях не зазначено"
+
+msgid "--quiet is only valid with a single pathname"
+msgstr "--quiet дійсний лише з однією назвою шляху"
+
+msgid "cannot have both --quiet and --verbose"
+msgstr "неможливо мати одночасно --quiet і --verbose"
+
+msgid "--non-matching is only valid with --verbose"
+msgstr "--non-matching діє тільки з --verbose"
+
+msgid "git check-mailmap [<options>] <contact>..."
+msgstr "git check-mailmap [<опції>] <контакт>..."
+
+msgid "also read contacts from stdin"
+msgstr "також читати контакти з stdin"
+
+#, c-format
+msgid "unable to parse contact: %s"
+msgstr "не вдалося розібрати контакт: %s"
+
+msgid "no contacts specified"
+msgstr "контакти не вказані"
+
+msgid "git checkout--worker [<options>]"
+msgstr "git checkout--worker [<опції>]"
+
+msgid "string"
+msgstr "строка"
+
+msgid "when creating files, prepend <string>"
+msgstr "при створенні файлів додавати <строку> напочатку"
+
+msgid "git checkout-index [<options>] [--] [<file>...]"
+msgstr "git checkout-index [<опції>] [—] [<файл>...]"
+
+msgid "stage should be between 1 and 3 or all"
+msgstr "стадія має бути від 1 до 3 або all"
+
+msgid "check out all files in the index"
+msgstr "переключити стан для всіх файлів в індексі"
+
+msgid "do not skip files with skip-worktree set"
+msgstr "не пропускати файли зі встановленим skip-worktree"
+
+msgid "force overwrite of existing files"
+msgstr "примусово перезаписати існуючі файли"
+
+msgid "no warning for existing files and files not in index"
+msgstr "не попереджувати про існуючі файли та файли, яких немає в індексі"
+
+msgid "don't checkout new files"
+msgstr "не переключати стан для нових файлів"
+
+msgid "update stat information in the index file"
+msgstr "оновити статистичну інформацію в індексному файлі"
+
+msgid "read list of paths from the standard input"
+msgstr "зчитати список шляхів зі стандартного вводу"
+
+msgid "write the content to temporary files"
+msgstr "записати вміст у тимчасові файли"
+
+msgid "copy out the files from named stage"
+msgstr "скопіювати файли з іменованої стадії"
+
+msgid "git checkout [<options>] <branch>"
+msgstr "git checkout [<опції>] <гілка>"
+
+msgid "git checkout [<options>] [<branch>] -- <file>..."
+msgstr "git checkout [<опції>] [<гілка>] -- <файл>..."
+
+msgid "git switch [<options>] [<branch>]"
+msgstr "git switch [<опції>] [<гілка>]"
+
+msgid "git restore [<options>] [--source=<branch>] <file>..."
+msgstr "git restore [<опції>] [--source=<гілка>] <файл>..."
+
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr "шлях \"%s\" не містить нашої версії"
+
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "шлях \"%s\" не містить їхньої версії"
+
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr "шлях \"%s\" не містить всіх необхідних версій"
+
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "шлях \"%s\" не містить необхідних версій"
+
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr "шлях \"%s\": неможливо злити"
+
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr "Неможливо додати результат злиття для \"%s\""
+
+#, c-format
+msgid "Recreated %d merge conflict"
+msgid_plural "Recreated %d merge conflicts"
+msgstr[0] "Відтворено %d конфлікт злиття"
+msgstr[1] "Відтворено %d конфлікта злиття"
+msgstr[2] "Відтворено %d конфліктів злиття"
+
+#, c-format
+msgid "Updated %d path from %s"
+msgid_plural "Updated %d paths from %s"
+msgstr[0] "Оновлено %d шлях з %s"
+msgstr[1] "Оновлено %d шляхи з %s"
+msgstr[2] "Оновлено %d шляхів з %s"
+
+#, c-format
+msgid "Updated %d path from the index"
+msgid_plural "Updated %d paths from the index"
+msgstr[0] "Оновлено %d шлях з індексу"
+msgstr[1] "Оновлено %d шляхи з індексу"
+msgstr[2] "Оновлено %d шляхів з індексу"
+
+#, c-format
+msgid "'%s' cannot be used with updating paths"
+msgstr "\"%s\" не можна використовувати зі шляхами оновлення"
+
+#, c-format
+msgid "Cannot update paths and switch to branch '%s' at the same time."
+msgstr "Неможливо одночасно оновити шляхи та переключитись на гілку \"%s\"."
+
+#, c-format
+msgid "neither '%s' or '%s' is specified"
+msgstr "не вказано ні \"%s\", ні \"%s\""
+
+#, c-format
+msgid "'%s' must be used when '%s' is not specified"
+msgstr "\"%s\" повинен використовуватися, якщо не вказано \"%s\""
+
+#, c-format
+msgid "'%s' or '%s' cannot be used with %s"
+msgstr "\"%s\" або \"%s\" не можна використовувати з %s"
+
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr ""
+"\"%s\", \"%s\" або \"%s\" не можна використовувати при переключенні стану"
+
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr "шлях '%s' не злитий"
+
+msgid "you need to resolve your current index first"
+msgstr "спочатку потрібно розібратись з вашим поточним індексом"
+
+#, c-format
+msgid ""
+"cannot continue with staged changes in the following files:\n"
+"%s"
+msgstr ""
+"неможливо продовжити з індексованими змінами в наступних файлах:\n"
+"%s"
+
+#, c-format
+msgid "Can not do reflog for '%s': %s\n"
+msgstr "Неможливо виконати reflog для \"%s\": %s\n"
+
+msgid "HEAD is now at"
+msgstr "HEAD зараз на"
+
+msgid "unable to update HEAD"
+msgstr "не вдалося оновити HEAD"
+
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Скинути гілку \"%s\"\n"
+
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Вже на \"%s\"\n"
+
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr "Переключено на та скинуто гілку '%s'\n"
+
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "Переключено на нову гілку \"%s\"\n"
+
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Переключено на гілку \"%s\"\n"
+
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... та ще %d.\n"
+
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+"Попередження: ви залишаєте позаду %d коміт, не підключений до\n"
+"жодної з ваших гілок:\n"
+"\n"
+"%s\n"
+msgstr[1] ""
+"Попередження: ви залишаєте позаду %d коміти, не підключених до\n"
+"жодної з ваших гілок:\n"
+"\n"
+"%s\n"
+msgstr[2] ""
+"Попередження: ви залишаєте позаду %d комітів, не підключених до\n"
+"жодної з ваших гілок:\n"
+"\n"
+"%s\n"
+
+#, c-format
+msgid ""
+"If you want to keep it by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch <new-branch-name> %s\n"
+"\n"
+msgid_plural ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch <new-branch-name> %s\n"
+"\n"
+msgstr[0] ""
+"Якщо ви хочете зберегти його, створивши нову гілку, то зараз є чудова "
+"нагода\n"
+"зробити це за допомогою:\n"
+"git branch <назва-нової-гілки> %s\n"
+"\n"
+msgstr[1] ""
+"Якщо ви хочете зберегти їх, створивши нову гілку, то зараз є чудова нагода\n"
+"зробити це за допомогою:\n"
+"git branch <назва-нової-гілки> %s\n"
+"\n"
+msgstr[2] ""
+"Якщо ви хочете зберегти їх, створивши нову гілку, то зараз є чудова нагода\n"
+"зробити це за допомогою:\n"
+"git branch <назва-нової-гілки> %s\n"
+"\n"
+
+msgid "internal error in revision walk"
+msgstr "внутрішня помилка при проходженні по ревізіям"
+
+msgid "Previous HEAD position was"
+msgstr "Попередня позиція HEAD була"
+
+msgid "You are on a branch yet to be born"
+msgstr "Ви на гілці, яка ще не iснує"
+
+#, c-format
+msgid ""
+"'%s' could be both a local file and a tracking branch.\n"
+"Please use -- (and optionally --no-guess) to disambiguate"
+msgstr ""
+"\"%s\" може бути як локальним файлом, так і відстежуваною гілкою.\n"
+"Будь ласка, використовуйте -- (і, за бажанням, --no-guess), для визначення"
+
+msgid ""
+"If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
+"you can do so by fully qualifying the name with the --track option:\n"
+"\n"
+"    git checkout --track origin/<name>\n"
+"\n"
+"If you'd like to always have checkouts of an ambiguous <name> prefer\n"
+"one remote, e.g. the 'origin' remote, consider setting\n"
+"checkout.defaultRemote=origin in your config."
+msgstr ""
+"Якщо ви хотіли переключитись на віддалено відстежувану гілку, наприклад, "
+"'origin',\n"
+"ви можете зробити це, повністю вказавши назву з опцією --track:\n"
+"\n"
+"    git checkout --track origin/<назва>.\n"
+"\n"
+"Якщо при переключенні з неоднозначною <назвою> ви бажаєте завжди віддавати "
+"перевагу\n"
+"певному віддаленому сховищу, наприклад, 'origin', розгляньте можливість "
+"додання\n"
+"checkout.defaultRemote=origin до вашого конфігураційного файлу."
+
+#, c-format
+msgid "'%s' matched multiple (%d) remote tracking branches"
+msgstr "\"%s\" відповідає кільком (%d) гілкам віддаленого відстежування"
+
+msgid "only one reference expected"
+msgstr "очікувалось тільки одне посилання"
+
+#, c-format
+msgid "only one reference expected, %d given."
+msgstr "очікувалось тільки одне посилання, надано %d."
+
+#, c-format
+msgid "invalid reference: %s"
+msgstr "неприпустиме посилання: %s"
+
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr "посилання не є деревом: %s"
+
+#, c-format
+msgid "a branch is expected, got tag '%s'"
+msgstr "очікувалась гілка, надано тег \"%s\""
+
+#, c-format
+msgid "a branch is expected, got remote branch '%s'"
+msgstr "очікувалась гілка, надана віддалена гілка \"%s\""
+
+#, c-format
+msgid "a branch is expected, got '%s'"
+msgstr "очікувалась гілка, надано \"%s\""
+
+#, c-format
+msgid "a branch is expected, got commit '%s'"
+msgstr "очікувалась гілка, надано коміт \"%s\""
+
+msgid ""
+"If you want to detach HEAD at the commit, try again with the --detach option."
+msgstr ""
+"Якщо ви хочете від'єднати HEAD на цьому коміті, спробуйте ще раз з опцією --"
+"detach."
+
+msgid ""
+"cannot switch branch while merging\n"
+"Consider \"git merge --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час злиття\n"
+"Спробуйте \"git merge --quit\" або \"git worktree add\"."
+
+msgid ""
+"cannot switch branch in the middle of an am session\n"
+"Consider \"git am --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час am сеансу\n"
+"Спробуйте \"git am --quit\" або \"git worktree add\"."
+
+msgid ""
+"cannot switch branch while rebasing\n"
+"Consider \"git rebase --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час перебазування\n"
+"Спробуйте \"git rebase --quit\" або \"git worktree add\"."
+
+msgid ""
+"cannot switch branch while cherry-picking\n"
+"Consider \"git cherry-pick --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час висмикування\n"
+"Спробуйте \"git cherry-pick --quit\" або \"git worktree add\"."
+
+msgid ""
+"cannot switch branch while reverting\n"
+"Consider \"git revert --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час вивертання\n"
+"Спробуйте \"git revert --quit\" або \"git worktree add\"."
+
+msgid "you are switching branch while bisecting"
+msgstr "ви переключаєте гілку під час бісекції"
+
+msgid "paths cannot be used with switching branches"
+msgstr "шляхи не можуть використовуватись при переключенні гілок"
+
+#, c-format
+msgid "'%s' cannot be used with switching branches"
+msgstr "'%s' не може використовуватись при переключенні гілок"
+
+#, c-format
+msgid "'%s' cannot be used with '%s'"
+msgstr "'%s' не може використовуватись з '%s'"
+
+#, c-format
+msgid "'%s' cannot take <start-point>"
+msgstr "'%s' не може прийняти <стартова-точка>"
+
+#, c-format
+msgid "Cannot switch branch to a non-commit '%s'"
+msgstr "Неможливо переключити гілку на не коміт '%s'"
+
+msgid "missing branch or commit argument"
+msgstr "відсутня гілка або коміт"
+
+msgid "perform a 3-way merge with the new branch"
+msgstr "здійснити 3-стороннє злиття з новою гілкою"
+
+msgid "style"
+msgstr "стиль"
+
+msgid "conflict style (merge, diff3, or zdiff3)"
+msgstr "конфлікт стилю (merge, diff3 або zdiff3)"
+
+msgid "detach HEAD at named commit"
+msgstr "відʼєднати HEAD на вказаному коміті"
+
+msgid "force checkout (throw away local modifications)"
+msgstr "переключити примусово (викинути локальні зміни)"
+
+msgid "new-branch"
+msgstr "нова-гілка"
+
+msgid "new unparented branch"
+msgstr "нова гілка без джерела"
+
+msgid "update ignored files (default)"
+msgstr "оновити ігноровані файли (за замовчуванням)"
+
+msgid "do not check if another worktree is holding the given ref"
+msgstr "не перевіряти, чи інше робоче дерево містить дане посилання"
+
+msgid "checkout our version for unmerged files"
+msgstr "використовувати нашу версію для не злитих файлів"
+
+msgid "checkout their version for unmerged files"
+msgstr "використовувати їхню версію для не злитих файлів"
+
+msgid "do not limit pathspecs to sparse entries only"
+msgstr "не обмежувати визначники шляхів лише розрідженими записами"
+
+#, c-format
+msgid "options '-%c', '-%c', and '%s' cannot be used together"
+msgstr "опції \"-%c\", \"-%c\" та \"%s\" не можна використовувати разом"
+
+msgid "--track needs a branch name"
+msgstr "--track потребує назви гілки"
+
+#, c-format
+msgid "missing branch name; try -%c"
+msgstr "відсутня назва гілки; спробуйте -%c"
+
+#, c-format
+msgid "could not resolve %s"
+msgstr "не вдалося розвʼязати %s"
+
+msgid "invalid path specification"
+msgstr "неприпустиме зазначення шляху"
+
+#, c-format
+msgid "'%s' is not a commit and a branch '%s' cannot be created from it"
+msgstr "'%s' не є комітом, і з нього не можна створити гілку '%s'"
+
+#, c-format
+msgid "git checkout: --detach does not take a path argument '%s'"
+msgstr "git checkout: --detach не приймає аргумент шляху '%s'"
+
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+"git checkout: --ours/--theirs, --force та --merge несумісні при\n"
+"при прееключенні індексу."
+
+msgid "you must specify path(s) to restore"
+msgstr "необхідно вказати шлях(и) для відновлення"
+
+msgid "branch"
+msgstr "гілка"
+
+msgid "create and checkout a new branch"
+msgstr "створити та перейти до нової гілки"
+
+msgid "create/reset and checkout a branch"
+msgstr "створити/скинути та перейти до нової гілки"
+
+msgid "create reflog for new branch"
+msgstr "створити журнал посилань для нової гілки"
+
+msgid "second guess 'git checkout <no-such-branch>' (default)"
+msgstr "друга здогадка \"git checkout <гілка-не-існує>\" (за замовчуванням)"
+
+msgid "use overlay mode (default)"
+msgstr "використовувати режим накладення (за замовчуванням)"
+
+msgid "create and switch to a new branch"
+msgstr "створити та переключити на нову гілку"
+
+msgid "create/reset and switch to a branch"
+msgstr "створити/скинути та переключити на гілку"
+
+msgid "second guess 'git switch <no-such-branch>'"
+msgstr "друга здогадка \"git switch <гілка-не-існує>\" (за замовчуванням)"
+
+msgid "throw away local modifications"
+msgstr "викинути локальні зміни"
+
+msgid "which tree-ish to checkout from"
+msgstr "з якого деревоподібного джерела створювати"
+
+msgid "restore the index"
+msgstr "відновити індекс"
+
+msgid "restore the working tree (default)"
+msgstr "відновити робоче дерево (за замовчуванням)"
+
+msgid "ignore unmerged entries"
+msgstr "ігнорувати не злиті записи"
+
+msgid "use overlay mode"
+msgstr "використовувати режим накладення"
+
+msgid ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <шаблон>] [-x | -X] [--] [<визначник "
+"шляху>...]"
+
+#, c-format
+msgid "Removing %s\n"
+msgstr "Видалення %s\n"
+
+#, c-format
+msgid "Would remove %s\n"
+msgstr "Було б видалено %s\n"
+
+#, c-format
+msgid "Skipping repository %s\n"
+msgstr "Пропуск сховища %s\n"
+
+#, c-format
+msgid "Would skip repository %s\n"
+msgstr "Було б пропущено сховище %s\n"
+
+#, c-format
+msgid "failed to remove %s"
+msgstr "не вдалося видалити %s"
+
+#, c-format
+msgid "could not lstat %s\n"
+msgstr "не вдалося виконати lstat %s\n"
+
+msgid "Refusing to remove current working directory\n"
+msgstr "Відмовлено у видаленні поточної робочої директорії\n"
+
+msgid "Would refuse to remove current working directory\n"
+msgstr "Було б відмовлено у видаленні поточної робочої директорії\n"
+
+#, c-format
+msgid ""
+"Prompt help:\n"
+"1          - select a numbered item\n"
+"foo        - select item based on unique prefix\n"
+"           - (empty) select nothing\n"
+msgstr ""
+"Підказка по опціям:\n"
+"1          - вибрати пронумерований елемент\n"
+"foo        - вибрати елемент за унікальним префіксом\n"
+"           - (порожньо) - не вибирати нічого\n"
+
+#, c-format
+msgid ""
+"Prompt help:\n"
+"1          - select a single item\n"
+"3-5        - select a range of items\n"
+"2-3,6-9    - select multiple ranges\n"
+"foo        - select item based on unique prefix\n"
+"-...       - unselect specified items\n"
+"*          - choose all items\n"
+"           - (empty) finish selecting\n"
+msgstr ""
+"Підказка по опціям:\n"
+"1          - вибрати один елемент\n"
+"3-5        - вибрати діапазон елементів\n"
+"2-3,6-9    - вибрати кілька діапазонів\n"
+"foo        - вибрати елемент за унікальним префіксом\n"
+"-...       - скасувати вибір вказаних елементів\n"
+"*          - вибрати всі елементи\n"
+"           - (порожньо) завершити вибір\n"
+
+#, c-format
+msgid "Huh (%s)?\n"
+msgstr "Га (%s)?\n"
+
+#, c-format
+msgid "Input ignore patterns>> "
+msgstr "Введіть шаблони ігнорування>> "
+
+#, c-format
+msgid "WARNING: Cannot find items matched by: %s"
+msgstr "ПОПЕРЕДЖЕННЯ: Не вдалося знайти елементи, що відповідають: %s"
+
+msgid "Select items to delete"
+msgstr "Виберіть елементи для видалення"
+
+#. TRANSLATORS: Make sure to keep [y/N] as is
+#, c-format
+msgid "Remove %s [y/N]? "
+msgstr "Видалити %s [y/N]? "
+
+msgid ""
+"clean               - start cleaning\n"
+"filter by pattern   - exclude items from deletion\n"
+"select by numbers   - select items to be deleted by numbers\n"
+"ask each            - confirm each deletion (like \"rm -i\")\n"
+"quit                - stop cleaning\n"
+"help                - this screen\n"
+"?                   - help for prompt selection"
+msgstr ""
+"clean               - розпочати прибирання\n"
+"filter by pattern   - виключити елементи з прибирання\n"
+"select by numbers   - вибрати елементи для прибирання за номерами\n"
+"ask each            - підтверджувати кожне видалення (як \"rm -i\")\n"
+"quit                - припинити прибирання\n"
+"help                - показати цей екран\n"
+"?                   - підказка для швидкого вибору"
+
+msgid "Would remove the following item:"
+msgid_plural "Would remove the following items:"
+msgstr[0] "Видалить наступний елемент:"
+msgstr[1] "Видалить наступні елементи:"
+msgstr[2] "Видалить наступні елементи:"
+
+msgid "No more files to clean, exiting."
+msgstr "Більше немає файлів для прибирання, вихід."
+
+msgid "do not print names of files removed"
+msgstr "не виводити назви видалених файлів"
+
+msgid "force"
+msgstr "примусово"
+
+msgid "interactive cleaning"
+msgstr "інтерактивне прибирання"
+
+msgid "remove whole directories"
+msgstr "видаляти цілі директорії"
+
+msgid "pattern"
+msgstr "шаблон"
+
+msgid "add <pattern> to ignore rules"
+msgstr "додати <шаблон> в правила ігнорування"
+
+msgid "remove ignored files, too"
+msgstr "видалити також ігноровані файли"
+
+msgid "remove only ignored files"
+msgstr "видалити лише ігноровані файли"
+
+msgid ""
+"clean.requireForce set to true and neither -i, -n, nor -f given; refusing to "
+"clean"
+msgstr ""
+"clean.requireForce встановлено у true і не задано ні -i, ні -n, ні -f; "
+"відмовлено в прибиранні"
+
+msgid ""
+"clean.requireForce defaults to true and neither -i, -n, nor -f given; "
+"refusing to clean"
+msgstr ""
+"clean.requireForce встановлено у true за замовчуванням і не задано ні -i, ні "
+"-n, ні -f; відмовлено в прибиранні"
+
+msgid "-x and -X cannot be used together"
+msgstr "-x та -X не можна використовувати разом"
+
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<опції>] [--] <сховище> [<директорія>]"
+
+msgid "don't clone shallow repository"
+msgstr "не клонувати неглибоке сховище"
+
+msgid "don't create a checkout"
+msgstr "не переходити на гілку"
+
+msgid "create a bare repository"
+msgstr "створити порожнє сховище"
+
+msgid "create a mirror repository (implies bare)"
+msgstr "створити дзеркальне сховище (включає опцію bare)"
+
+msgid "to clone from a local repository"
+msgstr "клонувати з локального сховища"
+
+msgid "don't use local hardlinks, always copy"
+msgstr "не використовувати локальні жорсткі посилання, завжди копіювати"
+
+msgid "setup as shared repository"
+msgstr "налаштувати як спільне сховище"
+
+msgid "pathspec"
+msgstr "визначник шляху"
+
+msgid "initialize submodules in the clone"
+msgstr "ініціалізувати підмодулі в клоні"
+
+msgid "number of submodules cloned in parallel"
+msgstr "кількість підмодулів, що клонуються паралельно"
+
+msgid "template-directory"
+msgstr "директорія-шаблонів"
+
+msgid "directory from which templates will be used"
+msgstr "директорія, з якої брати шаблони"
+
+msgid "reference repository"
+msgstr "посилання на сховище"
+
+msgid "use --reference only while cloning"
+msgstr "використовуйте --reference тільки під час клонування"
+
+msgid "name"
+msgstr "назва"
+
+msgid "use <name> instead of 'origin' to track upstream"
+msgstr ""
+"використовуйте <назва> замість \"origin\" для відстежування першоджерельного "
+"сховища"
+
+msgid "checkout <branch> instead of the remote's HEAD"
+msgstr "перейти до <гілки> замість HEAD віддаленого сховища"
+
+msgid "path to git-upload-pack on the remote"
+msgstr "шлях до git-upload-pack на віддаленому сервері"
+
+msgid "depth"
+msgstr "глибина"
+
+msgid "create a shallow clone of that depth"
+msgstr "створити неглибокий клон вказаної глибини"
+
+msgid "create a shallow clone since a specific time"
+msgstr "створити неглибокий клон з певного часу"
+
+msgid "revision"
+msgstr "ревізія"
+
+msgid "deepen history of shallow clone, excluding rev"
+msgstr "поглибити історію неглибокого клону, за винятком ревізії"
+
+msgid "clone only one branch, HEAD or --branch"
+msgstr "клонувати лише одну гілку, HEAD або --branch"
+
+msgid "don't clone any tags, and make later fetches not to follow them"
+msgstr ""
+"не клонувати жодних тегів і не слідувати за ними під час отримувань пізніше"
+
+msgid "any cloned submodules will be shallow"
+msgstr "будь-які клоновані підмодулі будуть неглибокими"
+
+msgid "gitdir"
+msgstr "git директорія"
+
+msgid "separate git dir from working tree"
+msgstr "відокремити git-директорію від робочого дерева"
+
+msgid "key=value"
+msgstr "ключ=значення"
+
+msgid "set config inside the new repository"
+msgstr "встановити конфігурацію всередині нового сховища"
+
+msgid "server-specific"
+msgstr "тільки для сервера"
+
+msgid "option to transmit"
+msgstr "опція для передачі"
+
+msgid "apply partial clone filters to submodules"
+msgstr "застосувати фільтри часткового клонування до підмодулів"
+
+msgid "any cloned submodules will use their remote-tracking branch"
+msgstr ""
+"будь-які клоновані підмодулі будуть використовувати свою віддалено "
+"відстежувану гілку"
+
+msgid "initialize sparse-checkout file to include only files at root"
+msgstr ""
+"ініціалізувати, щоб файл розрідженого переходу включав лише файли в корені"
+
+msgid "uri"
+msgstr "uri"
+
+msgid "a URI for downloading bundles before fetching from origin remote"
+msgstr "URI для завантаження пакунків перед отриманням з віддаленого джерела"
+
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "инфо: Не вдалося додати запозичений обʼєкт для \"%s\": %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "не вдалося виконати stat \"%s\""
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s існує і не є директорією"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "\"%s\" є символьним посиланням, відмовлено в клонуванні з --local"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "не вдалося запустити перебір для \"%s\""
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "символьне посилання \"%s\" існує, не можу клонувати з --local"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "не вдалося створити посилання \"%s\""
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "не вдалося скопіювати файл у \"%s\""
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "не вдалося перебрати \"%s\""
+
+#, c-format
+msgid "done.\n"
+msgstr "готово.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Клонування пройшло успішно, але не вдалося перейти на гілку.\n"
+"Ви можете перевірити, що було додано за допомогою 'git status'\n"
+"і повторити спробу за допомогою 'git restore --source=HEAD :/'\n"
+
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "Не вдалося знайти віддалену гілку %s для клонування."
+
+msgid "remote did not send all necessary objects"
+msgstr "віддалене сховище не надіслало всі необхідні обʼєкти"
+
+#, c-format
+msgid "unable to update %s"
+msgstr "не вдалося оновити %s"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "не вдалося ініціалізувати розріджений перехід"
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "віддалений HEAD посилається на неіснуючого рефа, неможливо перейти"
+
+msgid "unable to checkout working tree"
+msgstr "не вдалося завантажити стан робочої директорії"
+
+msgid "unable to write parameters to config file"
+msgstr "не вдалося записати параметри до конфігураційного файлу"
+
+msgid "cannot repack to clean up"
+msgstr "неможливо перепакувати, щоб очистити"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "неможливо видалити тимчасовий файл запозичених обʼєктів"
+
+msgid "Too many arguments."
+msgstr "Забагато аргументів."
+
+msgid "You must specify a repository to clone."
+msgstr "Треба вказати сховище для клонування."
+
+msgid ""
+"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
+"exclude"
+msgstr ""
+"--bundle-uri несумісний з --depth, --shallow-since та --shallow-exclude"
+
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "сховище \"%s\" не існує"
+
+#, c-format
+msgid "depth %s is not a positive number"
+msgstr "глибина %s не є додатнім числом"
+
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr "шлях призначення \"%s\" вже існує і не є порожньою директорією."
+
+#, c-format
+msgid "repository path '%s' already exists and is not an empty directory."
+msgstr "шлях до сховища \"%s\" вже існує і не є порожньою директорією."
+
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr "робоче дерево \"%s\" вже існує."
+
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr "не вдалося створити провідні директорії для \"%s\""
+
+#, c-format
+msgid "could not create work tree dir '%s'"
+msgstr "не вдалося створити директорію робочого дерева \"%s\""
+
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Клонування у порожнє сховище \"%s\"...\n"
+
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "Клонування в \"%s\"..\n"
+
+msgid ""
+"clone --recursive is not compatible with both --reference and --reference-if-"
+"able"
+msgstr ""
+"clone --recursive не сумісне з --reference та --reference-if-able одночасно"
+
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "\"%s\" не є дійсною назвою віддаленого сховища"
+
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+"--depth ігнорується у локальних клонах; використовуйте file:// замість цього."
+
+msgid "--shallow-since is ignored in local clones; use file:// instead."
+msgstr ""
+"--shallow-since ігнорується у локальних клонах; використовуйте file:// "
+"замість цього."
+
+msgid "--shallow-exclude is ignored in local clones; use file:// instead."
+msgstr ""
+"--shallow-exclude ігнорується у локальних клонах; використовуйте file:// "
+"замість цього."
+
+msgid "--filter is ignored in local clones; use file:// instead."
+msgstr ""
+"--filter ігнорується у локальних клонах; використовуйте file:// замість "
+"нього."
+
+msgid "source repository is shallow, reject to clone."
+msgstr "джерельне сховище є неглибоким, клонування відхилено."
+
+msgid "source repository is shallow, ignoring --local"
+msgstr "джерельне сховище є неглибоким, --local ігноровано"
+
+msgid "--local is ignored"
+msgstr "--local ігноровано"
+
+msgid "cannot clone from filtered bundle"
+msgstr "неможливо клонувати з відфільтрованого пакунка"
+
+msgid "failed to initialize the repo, skipping bundle URI"
+msgstr "не вдалося ініціалізувати сховище, URI пакунка пропущено"
+
+#, c-format
+msgid "failed to fetch objects from bundle URI '%s'"
+msgstr "не вдалося отримати обʼєкти з пакунка URI '%s'"
+
+msgid "failed to fetch advertised bundles"
+msgstr "не вдалося отримати обіцяні пакунки"
+
+msgid "remote transport reported error"
+msgstr "операція віддаленого отримання повідомила про помилку"
+
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "Віддалену гілку %s не знайдено у першоджерельному сховищі %s"
+
+msgid "You appear to have cloned an empty repository."
+msgstr "Здається, ви клонували порожнє сховище."
+
+msgid "git column [<options>]"
+msgstr "git column [<опції>]"
+
+msgid "lookup config vars"
+msgstr "пошук параметрів конфігурації"
+
+msgid "layout to use"
+msgstr "схема розташування"
+
+msgid "maximum width"
+msgstr "максимальна ширина"
+
+msgid "padding space on left border"
+msgstr "відступ по лівому краю"
+
+msgid "padding space on right border"
+msgstr "відступ по правому краю"
+
+msgid "padding space between columns"
+msgstr "відступ між стовпчиками"
+
+msgid "--command must be the first argument"
+msgstr "--command має бути першим аргументом"
+
+msgid ""
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
+msgstr ""
+"git commit-graph verify [--object-dir <директорія>] [--shallow] [--"
+"[no-]progress]"
+
+msgid ""
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
+msgstr ""
+"git commit-graph write [--object-dir <директорія>] [--append] [--object-dir "
+"<директорія>] [--append] [--object-dir <директорія>] [--append\n"
+"                       [--split[=<стратегія>]] [--reachable | --stdin-packs "
+"| --stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <число>] [--"
+"[no-]progress]\n"
+"                       <опції розділення>"
+
+msgid "dir"
+msgstr "директорія"
+
+msgid "the object directory to store the graph"
+msgstr "директорія об’єктів для зберігання графу"
+
+msgid "if the commit-graph is split, only verify the tip file"
+msgstr "якщо коміт-граф розщеплено, перевіряти тільки файл підказок"
+
+#, c-format
+msgid "Could not open commit-graph '%s'"
+msgstr "Не вдалося відкрити коміт-граф \"%s\""
+
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "не вдалося відкрити ланцюжок коміт-графа \"%s\""
+
+#, c-format
+msgid "unrecognized --split argument, %s"
+msgstr "нерозпізнаний --split аргумент, %s"
+
+#, c-format
+msgid "unexpected non-hex object ID: %s"
+msgstr "неочікуваний не hex ідентифікатор обʼєкта: %s"
+
+#, c-format
+msgid "invalid object: %s"
+msgstr "неприпустимий об’єкт: %s"
+
+#, c-format
+msgid "option `%s' expects a numerical value"
+msgstr "опція \"%s\" очікує числове значення"
+
+msgid "start walk at all refs"
+msgstr "розпочати проходження по всім посиланням"
+
+msgid "scan pack-indexes listed by stdin for commits"
+msgstr "просканувати pack-indexes зазначені через stdin для комітів"
+
+msgid "start walk at commits listed by stdin"
+msgstr "розпочати проходження по всім комітам зазначеним через stdin"
+
+msgid "include all commits already in the commit-graph file"
+msgstr "включити всі коміти, які вже є у файлі коміт-графа"
+
+msgid "enable computation for changed paths"
+msgstr "увімкнути обчислення для змінених шляхів"
+
+msgid "allow writing an incremental commit-graph file"
+msgstr "дозволити запис інкрементного файлу коміт-графа"
+
+msgid "maximum number of commits in a non-base split commit-graph"
+msgstr "максимальна кількість комітів у безбазовому розщепленому коміт-графі"
+
+msgid "maximum ratio between two levels of a split commit-graph"
+msgstr "максимальне співвідношення між двома рівнями розщепленого коміт-графа"
+
+msgid "only expire files older than a given date-time"
+msgstr "видалити лише файли, старіші за вказану дату"
+
+msgid "maximum number of changed-path Bloom filters to compute"
+msgstr "максимальна кількість фільтрів Блума зміненого шляху для обчислення"
+
+msgid "use at most one of --reachable, --stdin-commits, or --stdin-packs"
+msgstr ""
+"використовувати щонайбільше один з --reachable, --stdin-commits або --stdin-"
+"packs"
+
+msgid "Collecting commits from input"
+msgstr "Збирання комітів з вхідних даних"
+
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <дерево> [(-p <батько>)...]"
+
+msgid ""
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
+msgstr ""
+"git commit-tree [(-p <батько>)...] [-S[<ідентифікатор-ключа>]] [(-m "
+"<допис>)...]\n"
+"                [(-F <файл>)...] <дерево>"
+
+#, c-format
+msgid "duplicate parent %s ignored"
+msgstr "проігноровано дубльований батьківський %s"
+
+#, c-format
+msgid "not a valid object name %s"
+msgstr "невірне ім’я об’єкта %s"
+
+#, c-format
+msgid "git commit-tree: failed to read '%s'"
+msgstr "git commit-tree: не вдалося прочитати \"%s\""
+
+#, c-format
+msgid "git commit-tree: failed to close '%s'"
+msgstr "git commit-tree: не вдалося закрити \"%s\""
+
+msgid "parent"
+msgstr "батько"
+
+msgid "id of a parent commit object"
+msgstr "ідентифікатор обʼєкта батьківського коміту"
+
+msgid "message"
+msgstr "допис"
+
+msgid "commit message"
+msgstr "допис до коміту"
+
+msgid "read commit log message from file"
+msgstr "читати допис до коміту з файлу"
+
+msgid "GPG sign commit"
+msgstr "підписати коміт за допомогою GPG"
+
+msgid "must give exactly one tree"
+msgstr "має бути надано лишень одне дерево"
+
+msgid "git commit-tree: failed to read"
+msgstr "git commit-tree: не вдалося прочитати"
+
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<режим>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <коміт> | --fixup [(amend|"
+"reword):]<коміт>)]\n"
+"           [-F <файл> | -m <допис>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<автор>]\n"
+"           [--date=<дата>] [--cleanup=<режим>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<файл> [--pathspec-file-nul]]\n"
+"           [(--trailer <токен>[(=|:)<значення>])...] [-S[<ідентифікатор "
+"ключа>]]\n"
+"           [--] [<визначник шляху>...]"
+
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<опції>] [--] [<визначник шляху>...]"
+
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+"Ви просили внести зміни до останнього коміту, але це зробить його\n"
+"порожнім. Ви можете повторити команду, скориставшись --allow-empty, або\n"
+"повністю видалити коміт за допомогою \"git reset HEAD^\".\n"
+
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+msgstr ""
+"Попереднє висмикування зараз порожнє, можливо, через вирішення конфлікту.\n"
+"Якщо ви все одно бажаєте додати його, скористайтесь командою\n"
+"\n"
+"    git commit --allow-empty\n"
+
+msgid "Otherwise, please use 'git rebase --skip'\n"
+msgstr "В іншому випадку, будь ласка, скористайтесь \"git rebase --skip\"\n"
+
+msgid "Otherwise, please use 'git cherry-pick --skip'\n"
+msgstr ""
+"В іншому випадку, будь ласка, скористайтесь \"git cherry-pick --skip\"\n"
+
+msgid ""
+"and then use:\n"
+"\n"
+"    git cherry-pick --continue\n"
+"\n"
+"to resume cherry-picking the remaining commits.\n"
+"If you wish to skip this commit, use:\n"
+"\n"
+"    git cherry-pick --skip\n"
+"\n"
+msgstr ""
+"а потім скористайтесь:\n"
+"\n"
+"    git cherry-pick --continue\n"
+"\n"
+"щоб продовжити висмикування решти комітів.\n"
+"Якщо ви хочете пропустити цей коміт, скористайтесь\n"
+"\n"
+"    git cherry-pick --skip\n"
+
+msgid "updating files failed"
+msgstr "не вдалося оновити файли"
+
+msgid "failed to unpack HEAD tree object"
+msgstr "не вдалося розпакувати HEAD обʼєкт дерева"
+
+msgid "No paths with --include/--only does not make sense."
+msgstr "Ніякі шляхи з --include/--only не мають сенсу."
+
+msgid "unable to create temporary index"
+msgstr "не вдалося створити тимчасовий індекс"
+
+msgid "interactive add failed"
+msgstr "інтерактивне додавання не вдалося"
+
+msgid "unable to update temporary index"
+msgstr "не вдалося оновити тимчасовий індекс"
+
+msgid "Failed to update main cache tree"
+msgstr "Не вдалося оновити головне дерево кешу"
+
+msgid "cannot do a partial commit during a merge."
+msgstr "неможливо зробити частковий коміт під час злиття."
+
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr "неможливо зробити частковий коміт під час висмикування."
+
+msgid "cannot do a partial commit during a rebase."
+msgstr "неможливо зробити частковий коміт під час перебазування."
+
+msgid "cannot read the index"
+msgstr "неможливо прочитати індекс"
+
+msgid "unable to write temporary index file"
+msgstr "не вдалося записати тимчасовий файл індексу"
+
+#, c-format
+msgid "commit '%s' lacks author header"
+msgstr "у коміті \"%s\" немає заголовка автора"
+
+#, c-format
+msgid "commit '%s' has malformed author line"
+msgstr "у коміті \"%s\" невірно сформовано рядок автора"
+
+msgid "malformed --author parameter"
+msgstr "невірно сформований --author параметр"
+
+#, c-format
+msgid "invalid date format: %s"
+msgstr "неприпустимий формат дати: %s"
+
+msgid ""
+"unable to select a comment character that is not used\n"
+"in the current commit message"
+msgstr ""
+"не вдалося вибрати символ коментаря, який не використовується\n"
+"у поточному дописі до коміту"
+
+#, c-format
+msgid "could not lookup commit '%s'"
+msgstr "не вдалося знайти коміт \"%s\""
+
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(читання допису журналу зі стандартного вводу)\n"
+
+msgid "could not read log from standard input"
+msgstr "не вдалося прочитати допис зі стандартного вводу"
+
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "не вдалося прочитати допис з файлу \"%s\""
+
+#, c-format
+msgid "options '%s' and '%s:%s' cannot be used together"
+msgstr "опції \"%s\" та \"%s:%s\" не можна використовувати разом"
+
+msgid "could not read SQUASH_MSG"
+msgstr "не вдалося прочитати SQUASH_MSG"
+
+msgid "could not read MERGE_MSG"
+msgstr "не вдалося прочитати MERGE_MSG"
+
+#, c-format
+msgid "could not open '%s'"
+msgstr "не вдалося відкрити \"%s\""
+
+msgid "could not write commit template"
+msgstr "не вдалося записати шаблон комітів"
+
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be ignored.\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту для ваших змін. Рядки, що починаються з\n"
+" \"%c\" будуть проігноровані.\n"
+
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту для ваших змін. Рядки, що починаються з\n"
+" \"%c\" будуть проігноровані, а порожній допис перерве коміт.\n"
+
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be kept; you may remove them yourself if you want to.\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту для ваших змін. Рядки, що починаються з\n"
+" \"%c\" будуть збережені; ви можете вилучити їх самостійно, якщо захочете.\n"
+
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту для ваших змін. Рядки, що починаються з\n"
+" \"%c\" будуть збережені; ви можете вилучити їх самостійно, якщо захочете.\n"
+"Порожній допис перериває коміт.\n"
+
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please run\n"
+"\tgit update-ref -d MERGE_HEAD\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Схоже, що ви робите злиття.\n"
+"Якщо це не так, виконайте команду\n"
+"\tgit update-ref -d MERGE_HEAD\n"
+"і спробуйте ще раз.\n"
+
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please run\n"
+"\tgit update-ref -d CHERRY_PICK_HEAD\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Схоже, що ви робите висмикування.\n"
+"Якщо це не так, виконайте\n"
+"\tgit update-ref -d CHERRY_PICK_HEAD\n"
+"і спробуйте ще раз.\n"
+
+#, c-format
+msgid "%sAuthor:    %.*s <%.*s>"
+msgstr "%sАвтор:    %.*s <%.*s>"
+
+#, c-format
+msgid "%sDate:      %s"
+msgstr "%sДата:      %s"
+
+#, c-format
+msgid "%sCommitter: %.*s <%.*s>"
+msgstr "%sКомітер: %.*s <%.*s>"
+
+msgid "Cannot read index"
+msgstr "Неможливо прочитати індекс"
+
+msgid "unable to pass trailers to --trailers"
+msgstr "не вдалося передати причепи до --trailers"
+
+msgid "Error building trees"
+msgstr "Помилка при побудові дерев"
+
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "Будь ласка, надайте допис, використовуючи опцію -m або -F.\n"
+
+#, c-format
+msgid "--author '%s' is not 'Name <email>' and matches no existing author"
+msgstr ""
+"--author \"%s\" не в форматі \"Імʼя <адреса електронної пошти>\" і не "
+"відповідає жодному існуючому автору"
+
+#, c-format
+msgid "Invalid ignored mode '%s'"
+msgstr "Неприпустимий режим ігнорування \"%s\""
+
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr "Неприпустимий режим невідстежуваних файлів \"%s\""
+
+msgid "You are in the middle of a merge -- cannot reword."
+msgstr "Ви перебуваєте в процесі злиття -- неможливо перефразувати."
+
+msgid "You are in the middle of a cherry-pick -- cannot reword."
+msgstr "Ви перебуваєте в процесі висмикування -- неможливо перефразувати."
+
+#, c-format
+msgid "reword option of '%s' and path '%s' cannot be used together"
+msgstr ""
+"параметр перефразування \"%s\" і шлях \"%s\" не можуть бути використані разом"
+
+#, c-format
+msgid "reword option of '%s' and '%s' cannot be used together"
+msgstr "опцію перефразування \"%s\" і \"%s\" не можна використовувати разом"
+
+msgid "You have nothing to amend."
+msgstr "Вам немає до чого вносити зміни."
+
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr "Ви перебуваєте в процесі злиття -- неможливо внести зміни."
+
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr "Ви перебуваєте в процесі висмикування - неможливо внести зміни."
+
+msgid "You are in the middle of a rebase -- cannot amend."
+msgstr "Ви перебуваєте в процесі перебазуавння -- неможливо внести зміни."
+
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "--reset-author можна використовувати лише з -C, -c або --amend."
+
+#, c-format
+msgid "unknown option: --fixup=%s:%s"
+msgstr "невідомий параметр: --fixup=%s:%s"
+
+#, c-format
+msgid "paths '%s ...' with -a does not make sense"
+msgstr "шляхи \"%s ...\" з -a не мають сенсу"
+
+msgid "show status concisely"
+msgstr "показувати статус стисло"
+
+msgid "show branch information"
+msgstr "показати інформацію про гілку"
+
+msgid "show stash information"
+msgstr "показати інформацію про схов"
+
+msgid "compute full ahead/behind values"
+msgstr "обчислювати повні попереду/позаду значення"
+
+msgid "version"
+msgstr "версія"
+
+msgid "machine-readable output"
+msgstr "машинозчитуваний вивід"
+
+msgid "show status in long format (default)"
+msgstr "показувати статус у повному форматі (за замовчуванням)"
+
+msgid "terminate entries with NUL"
+msgstr "завершувати записи символом NUL"
+
+msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
+msgstr ""
+"показувати невідстежувані файли, варіанти режимів: all, normal, no (за "
+"замовчуванням: all)"
+
+msgid ""
+"show ignored files, optional modes: traditional, matching, no. (Default: "
+"traditional)"
+msgstr ""
+"показувати ігноровані файли, варіанти режимів: traditional, matching, no (за "
+"замовчуванням: traditional)."
+
+msgid "when"
+msgstr "коли"
+
+msgid ""
+"ignore changes to submodules, optional when: all, dirty, untracked. "
+"(Default: all)"
+msgstr ""
+"ігнорувати зміни підмодулів, опціонально, якщо: all, dirty, untracked (за "
+"замовчуванням: all)."
+
+msgid "list untracked files in columns"
+msgstr "показати невідстежувані файли в стовпчиках"
+
+msgid "do not detect renames"
+msgstr "не виявляти перейменування"
+
+msgid "detect renames, optionally set similarity index"
+msgstr "виявляти перейменування, опціонально встановлювати індекс схожості"
+
+msgid "Unsupported combination of ignored and untracked-files arguments"
+msgstr ""
+"Непідтримувана комбінація аргументів для ігнорованих та невідстежуваних "
+"файлів"
+
+msgid "suppress summary after successful commit"
+msgstr "не показувати підсумок після успішного коміту"
+
+msgid "show diff in commit message template"
+msgstr "показувати різницю в шаблоні дописа до коміту"
+
+msgid "Commit message options"
+msgstr "Опції дописа до коміту"
+
+msgid "read message from file"
+msgstr "читати текст дописа з файлу"
+
+msgid "author"
+msgstr "автор"
+
+msgid "override author for commit"
+msgstr "перевизначити автора коміту"
+
+msgid "date"
+msgstr "дата"
+
+msgid "override date for commit"
+msgstr "перевизначити дату коміту"
+
+msgid "commit"
+msgstr "коміт"
+
+msgid "reuse and edit message from specified commit"
+msgstr "повторно використати та редагувати допис зі вказаного коміту"
+
+msgid "reuse message from specified commit"
+msgstr "повторно використати допис зі вказаного коміту"
+
+#. TRANSLATORS: Leave "[(amend|reword):]" as-is,
+#. and only translate <commit>.
+#.
+msgid "[(amend|reword):]commit"
+msgstr "[(amend|reword):]коміт"
+
+msgid ""
+"use autosquash formatted message to fixup or amend/reword specified commit"
+msgstr ""
+"використовувати допис у форматі autosquash для виправлення або зміни/"
+"редагування вказаного коміту"
+
+msgid "use autosquash formatted message to squash specified commit"
+msgstr ""
+"використовувати допис у форматі autosquash для зчавлювання вказаного коміту"
+
+msgid "the commit is authored by me now (used with -C/-c/--amend)"
+msgstr "автором коміту тепер є я (використовується з -C/-c/--amend)"
+
+msgid "trailer"
+msgstr "причіп"
+
+msgid "add custom trailer(s)"
+msgstr "додати нестандартний причіп"
+
+msgid "add a Signed-off-by trailer"
+msgstr "додати Signed-off-by причіп"
+
+msgid "use specified template file"
+msgstr "використати зазначений файл шаблону"
+
+msgid "force edit of commit"
+msgstr "редагувати коміт примусово"
+
+msgid "include status in commit message template"
+msgstr "включити статус у шаблон дописа до коміту"
+
+msgid "Commit contents options"
+msgstr "Опції вмісту коміту"
+
+msgid "commit all changed files"
+msgstr "закомітити всі змінені файли"
+
+msgid "add specified files to index for commit"
+msgstr "додати вказані файли до індексу для коміту"
+
+msgid "interactively add files"
+msgstr "додавати файли інтерактивно"
+
+msgid "interactively add changes"
+msgstr "додавати зміни інтерактивно"
+
+msgid "commit only specified files"
+msgstr "комітити лише вказані файли"
+
+msgid "bypass pre-commit and commit-msg hooks"
+msgstr "обходити pre-commit та commit-msg гачки"
+
+msgid "show what would be committed"
+msgstr "показати, що буде закомічено"
+
+msgid "amend previous commit"
+msgstr "внести зміни до попереднього коміту"
+
+msgid "bypass post-rewrite hook"
+msgstr "обійти post-rewrite гачок"
+
+msgid "ok to record an empty change"
+msgstr "дозволити записати порожню зміну"
+
+msgid "ok to record a change with an empty message"
+msgstr "дозволити записати зміну з порожнім дописом"
+
+msgid "could not parse HEAD commit"
+msgstr "не вдалося розібрати HEAD коміт"
+
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr "Пошкоджений MERGE_HEAD (%s)"
+
+msgid "could not read MERGE_MODE"
+msgstr "не вдалося прочитати MERGE_MODE"
+
+#, c-format
+msgid "could not read commit message: %s"
+msgstr "не вдалося прочитати допис до коміту: %s"
+
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr "Переривання коміту через порожній допис до коміту.\n"
+
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "Переривання коміту; ви не відредагували допис.\n"
+
+#, c-format
+msgid "Aborting commit due to empty commit message body.\n"
+msgstr "Переривання коміту через порожнє тіло дописа до коміту.\n"
+
+msgid ""
+"repository has been updated, but unable to write\n"
+"new index file. Check that disk is not full and quota is\n"
+"not exceeded, and then \"git restore --staged :/\" to recover."
+msgstr ""
+"сховище було оновлено, але не вдалося записати\n"
+"новий файл індексу. Переконайтеся, що диск не переповнений і квота\n"
+"не перевищена, а потім виконайте \"git restore --staged :/\" для відновлення."
+
+msgid "git config [<options>]"
+msgstr "git config [<опції>]"
+
+#, c-format
+msgid "unrecognized --type argument, %s"
+msgstr "нерозпізнаний аргумент --type, %s"
+
+msgid "only one type at a time"
+msgstr "лише один тип за раз"
+
+msgid "Config file location"
+msgstr "Розташування файлу конфігурації"
+
+msgid "use global config file"
+msgstr "використовувати глобальний файл конфігурації"
+
+msgid "use system config file"
+msgstr "використовувати файл конфігурації системи"
+
+msgid "use repository config file"
+msgstr "використовувати файл конфігурації сховища"
+
+msgid "use per-worktree config file"
+msgstr "використовувати файл конфігурації робочого дерева"
+
+msgid "use given config file"
+msgstr "використовувати наданий файл конфігурації"
+
+msgid "blob-id"
+msgstr "blob-id"
+
+msgid "read config from given blob object"
+msgstr "прочитати конфігурацію з наданого blob-обʼєкту"
+
+msgid "Action"
+msgstr "Дія"
+
+msgid "get value: name [value-pattern]"
+msgstr "отримати значення: назва [шаблон-значення]"
+
+msgid "get all values: key [value-pattern]"
+msgstr "отримати всі значення: ключ [шаблон-значення]"
+
+msgid "get values for regexp: name-regex [value-pattern]"
+msgstr "отримати значення для регвиру: регвир-назви [шаблон-значення]"
+
+msgid "get value specific for the URL: section[.var] URL"
+msgstr "отримати значення для конкретної URL-адреси: розділ[.var] URL-адреса"
+
+msgid "replace all matching variables: name value [value-pattern]"
+msgstr "замінити всі відповідні змінні: назва значення [шаблон-значення]"
+
+msgid "add a new variable: name value"
+msgstr "додати нову змінну: назва значення"
+
+msgid "remove a variable: name [value-pattern]"
+msgstr "видалити змінну: назва [шаблон-значення]"
+
+msgid "remove all matches: name [value-pattern]"
+msgstr "видалити всі збіги: назва [шаблон-значення]"
+
+msgid "rename section: old-name new-name"
+msgstr "перейменувати розділ: стара-назва нова-назва"
+
+msgid "remove a section: name"
+msgstr "видалити розділ: назва"
+
+msgid "list all"
+msgstr "показати всі змінні"
+
+msgid "use string equality when comparing values to 'value-pattern'"
+msgstr ""
+"використовувати рівність строк при порівнянні значень з \"шаблон-значенням\""
+
+msgid "open an editor"
+msgstr "відкрити редактор"
+
+msgid "find the color configured: slot [default]"
+msgstr "знайти налаштований колір: слот [за замовчуванням]"
+
+msgid "find the color setting: slot [stdout-is-tty]"
+msgstr "знайти налаштування кольору: slot [stdout-is-tty]"
+
+msgid "Type"
+msgstr "Тип"
+
+msgid "type"
+msgstr "тип"
+
+msgid "value is given this type"
+msgstr "тип значення"
+
+msgid "value is \"true\" or \"false\""
+msgstr "значення \"true\" або \"false\""
+
+msgid "value is decimal number"
+msgstr "значення десяткове число"
+
+msgid "value is --bool or --int"
+msgstr "значення --bool або --int"
+
+msgid "value is --bool or string"
+msgstr "значення --bool або string"
+
+msgid "value is a path (file or directory name)"
+msgstr "значення шлях (файл або назва директорії)"
+
+msgid "value is an expiry date"
+msgstr "значення - дата закінчення терміну дії"
+
+msgid "Other"
+msgstr "Інше"
+
+msgid "terminate values with NUL byte"
+msgstr "завершити значення байтом NUL"
+
+msgid "show variable names only"
+msgstr "показувати тільки назви змінних"
+
+msgid "respect include directives on lookup"
+msgstr "дотримуватись директив включення при пошуку"
+
+msgid "show origin of config (file, standard input, blob, command line)"
+msgstr ""
+"показати походження конфігурації (файл, стандартний ввід, blob, командний "
+"рядок)"
+
+msgid "show scope of config (worktree, local, global, system, command)"
+msgstr ""
+"показати межі дії конфігурації (робоче дерево, локально, глобально, система, "
+"команда)"
+
+msgid "value"
+msgstr "значення"
+
+msgid "with --get, use default value when missing entry"
+msgstr ""
+"з --get використовувати значення за замовчуванням, якщо запис відсутній"
+
+#, c-format
+msgid "wrong number of arguments, should be %d"
+msgstr "невірна кількість аргументів, має бути %d"
+
+#, c-format
+msgid "wrong number of arguments, should be from %d to %d"
+msgstr "невірна кількість аргументів, має бути від %d до %d"
+
+#, c-format
+msgid "invalid key pattern: %s"
+msgstr "неприпустимий шаблон ключа: %s"
+
+#, c-format
+msgid "invalid pattern: %s"
+msgstr "неприпустимий шаблон: %s"
+
+#, c-format
+msgid "failed to format default config value: %s"
+msgstr "не вдалося відформатувати початкове значення конфігурації: %s"
+
+#, c-format
+msgid "cannot parse color '%s'"
+msgstr "не вдалося розібрати колір \"%s\""
+
+msgid "unable to parse default color value"
+msgstr "не вдалося розібрати початкове значення кольору"
+
+msgid "not in a git directory"
+msgstr "не в git директорії"
+
+msgid "writing to stdin is not supported"
+msgstr "запис до stdin не підтримується"
+
+msgid "writing config blobs is not supported"
+msgstr "запис blob конфігурації не підтримується"
+
+#, c-format
+msgid ""
+"# This is Git's per-user configuration file.\n"
+"[user]\n"
+"# Please adapt and uncomment the following lines:\n"
+"#\tname = %s\n"
+"#\temail = %s\n"
+msgstr ""
+"# Це файл конфігурації Git для кожного користувача.\n"
+"[user]\n"
+"# Будь ласка, адаптуйте та розкоментуйте наступні рядки:\n"
+"# name = %s\n"
+"# email = %s\n"
+
+msgid "only one config file at a time"
+msgstr "лише один конфігураційний файл за раз"
+
+msgid "--local can only be used inside a git repository"
+msgstr "--local можна використовувати лише всередині git сховища"
+
+msgid "--blob can only be used inside a git repository"
+msgstr "--blob можна використовувати лише всередині git сховища"
+
+msgid "--worktree can only be used inside a git repository"
+msgstr "--worktree можна використовувати лише всередині git сховища"
+
+msgid "$HOME not set"
+msgstr "$HOME не встановлено"
+
+msgid ""
+"--worktree cannot be used with multiple working trees unless the config\n"
+"extension worktreeConfig is enabled. Please read \"CONFIGURATION FILE\"\n"
+"section in \"git help worktree\" for details"
+msgstr ""
+"--worktree неможливо використовувати з кількома робочими деревами, якщо не "
+"увімкнено розширення\n"
+"конфігурації worktreeConfig. Будь ласка, прочитайте розділ \"КОНФІГУРАЦІЙНИЙ "
+"ФАЙЛ\"\n"
+"у \"git help worktree\" для більш детальної інформації"
+
+msgid "--get-color and variable type are incoherent"
+msgstr "--get-color і тип змінної не узгоджуються"
+
+msgid "only one action at a time"
+msgstr "лише одна дія за раз"
+
+msgid "--name-only is only applicable to --list or --get-regexp"
+msgstr "--name-only застосовується лише до --list або --get-regexp"
+
+msgid ""
+"--show-origin is only applicable to --get, --get-all, --get-regexp, and --"
+"list"
+msgstr ""
+"--show-origin застосовується лише до --get, --get-all, --get-regexp та --list"
+
+msgid "--default is only applicable to --get"
+msgstr "--default застосовується лише до --get"
+
+msgid "--fixed-value only applies with 'value-pattern'"
+msgstr "--fixed-value застосовується лише з \"шаблоном-значення\""
+
+#, c-format
+msgid "unable to read config file '%s'"
+msgstr "не вдалося прочитати файл конфігурації \"%s\""
+
+msgid "error processing config file(s)"
+msgstr "помилка при обробці файлу(ів) конфігурації"
+
+msgid "editing stdin is not supported"
+msgstr "редагування stdin не підтримується"
+
+msgid "editing blobs is not supported"
+msgstr "редагування blobs не підтримується"
+
+#, c-format
+msgid "cannot create configuration file %s"
+msgstr "неможливо створити конфігураційний файл %s"
+
+#, c-format
+msgid ""
+"cannot overwrite multiple values with a single value\n"
+"       Use a regexp, --add or --replace-all to change %s."
+msgstr ""
+"неможливо перезаписати кілька значень одним значенням\n"
+"       Використовуйте regexp, --add або --replace-all для зміни %s."
+
+#, c-format
+msgid "no such section: %s"
+msgstr "немає такого розділу: %s"
+
+msgid "print sizes in human readable format"
+msgstr "показувати розмір у зручному для читання форматі"
+
+#, c-format
+msgid ""
+"The permissions on your socket directory are too loose; other\n"
+"users may be able to read your cached credentials. Consider running:\n"
+"\n"
+"\tchmod 0700 %s"
+msgstr ""
+"Дозволи на вашу сокет директорію занадто вільні; інші\n"
+"користувачі можуть прочитати ваші кешовані облікові дані. Подумайте про "
+"запуск:\n"
+"\n"
+"\tchmod 0700 %s"
+
+msgid "print debugging messages to stderr"
+msgstr "виводити відлагоджувальні повідомлення в stderr"
+
+msgid "credential-cache--daemon unavailable; no unix socket support"
+msgstr "credential-cache--daemon недоступний; немає підтримки unix-сокетів"
+
+msgid "credential-cache unavailable; no unix socket support"
+msgstr "credential-cache недоступний; немає підтримки unix-сокетів"
+
+#, c-format
+msgid "unable to get credential storage lock in %d ms"
+msgstr "не вдалося отримати файл блокування сховища облікових даних за %d мс"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<н>] "
+"[<комітоподібне>...]"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<н>] --"
+"dirty[=<позначка>]"
+
+msgid "git describe <blob>"
+msgstr "git describe <blob>"
+
+msgid "head"
+msgstr "head"
+
+msgid "lightweight"
+msgstr "lightweight"
+
+msgid "annotated"
+msgstr "annotated"
+
+#, c-format
+msgid "annotated tag %s not available"
+msgstr "анотований тег %s недоступний"
+
+#, c-format
+msgid "tag '%s' is externally known as '%s'"
+msgstr "тег \"%s\" відомий зовні як \"%s\""
+
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr "жоден тег не збігається точно з \"%s\""
+
+#, c-format
+msgid "No exact match on refs or tags, searching to describe\n"
+msgstr "Немає точного збігу за посиланнями або тегами, пошук для опису\n"
+
+#, c-format
+msgid "finished search at %s\n"
+msgstr "пошук завершено на %s\n"
+
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+"Жоден анотований тег не може описати \"%s\".\n"
+"Однак існують неанотовані теги: спробуйте --tags."
+
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+"Жоден тег не може описати \"%s\".\n"
+"Спробуйте --always або створіть кілька тегів."
+
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr "пройдено через %lu комітів\n"
+
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+"знайдено більше %i тегів; показані %i останніх\n"
+"припинено пошук на %s\n"
+
+#, c-format
+msgid "describe %s\n"
+msgstr "описати %s\n"
+
+#, c-format
+msgid "Not a valid object name %s"
+msgstr "Неприпустиме ім’я об’єкта %s"
+
+#, c-format
+msgid "%s is neither a commit nor blob"
+msgstr "%s не є commit чи blob"
+
+msgid "find the tag that comes after the commit"
+msgstr "знайти тег, що йде після коміту"
+
+msgid "debug search strategy on stderr"
+msgstr "виводити відлагоджувальні повідомлення стратегії пошуку в stderr"
+
+msgid "use any ref"
+msgstr "використати будь-яке посилання"
+
+msgid "use any tag, even unannotated"
+msgstr "використати будь-який тег, навіть неанотований"
+
+msgid "always use long format"
+msgstr "завжди використовувати довгий формат"
+
+msgid "only follow first parent"
+msgstr "слідувати тільки за першим з батьків"
+
+msgid "only output exact matches"
+msgstr "виводити лише точні збіги"
+
+msgid "consider <n> most recent tags (default: 10)"
+msgstr "враховувати <н> останніх тегів (за замовчуванням: 10)"
+
+msgid "only consider tags matching <pattern>"
+msgstr "враховувати лише теги, що відповідають <шаблону>"
+
+msgid "do not consider tags matching <pattern>"
+msgstr "не враховувати теги, що відповідають <шаблону>"
+
+msgid "show abbreviated commit object as fallback"
+msgstr "показувати скорочений обʼєкт коміту як запасний варіант"
+
+msgid "mark"
+msgstr "позначка"
+
+msgid "append <mark> on dirty working tree (default: \"-dirty\")"
+msgstr ""
+"додати <позначку> до брудного робочого дерева (за замовчуванням: \\”-"
+"dirty\\”)"
+
+msgid "append <mark> on broken working tree (default: \"-broken\")"
+msgstr ""
+"додати <позначку> до пошкодженого робочого дерева (за замовчуванням: \"-"
+"broken\")"
+
+msgid "No names found, cannot describe anything."
+msgstr "Назв не знайдено, неможливо нічого описати."
+
+#, c-format
+msgid "option '%s' and commit-ishes cannot be used together"
+msgstr "опцію \"%s\" не можна використовувати разом з комітоподібними"
+
+msgid ""
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
+msgstr ""
+"git diagnose [(-o | --output-directory) <шлях>] [(-s | --suffix) <формат>]\n"
+"             [--mode=<режим>]"
+
+msgid "specify a destination for the diagnostics archive"
+msgstr "вказати місце призначення архіву діагностики"
+
+msgid "specify a strftime format suffix for the filename"
+msgstr "вказати суфікс формату strftime для назви файлу"
+
+msgid "specify the content of the diagnostic archive"
+msgstr "вказати вміст архіву діагностики"
+
+msgid "--merge-base only works with two commits"
+msgstr "--merge-base працює лише з двома комітами"
+
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr "\"%s\": не звичайний файл або символьне посилання"
+
+msgid "no merge given, only parents."
+msgstr "злиття не надано, лише батьки."
+
+#, c-format
+msgid "invalid option: %s"
+msgstr "неприпустима опція %s"
+
+#, c-format
+msgid "%s...%s: no merge base"
+msgstr "%s...%s: немає бази злиття"
+
+msgid "Not a git repository"
+msgstr "Не є git сховищем"
+
+#, c-format
+msgid "invalid object '%s' given."
+msgstr "надано неприпустимий об’єкт \"%s\"."
+
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr "надано більше двох blob: \"%s\""
+
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr "надано необроблений об’єкт \"%s\"."
+
+#, c-format
+msgid "%s...%s: multiple merge bases, using %s"
+msgstr "%s...%s: кілька баз злиття, використання %s"
+
+msgid "git difftool [<options>] [<commit> [<commit>]] [--] [<path>...]"
+msgstr "git difftool [<опції>] [<коміт> [<коміт>]] [--] [<шлях>...]"
+
+#, c-format
+msgid "could not read symlink %s"
+msgstr "не вдалося прочитати символьне посилання %s"
+
+#, c-format
+msgid "could not read symlink file %s"
+msgstr "не вдалося прочитати файл символьного посилання %s"
+
+#, c-format
+msgid "could not read object %s for symlink %s"
+msgstr "не вдалося прочитати об’єкт %s для символьного посилання %s"
+
+msgid ""
+"combined diff formats ('-c' and '--cc') are not supported in\n"
+"directory diff mode ('-d' and '--dir-diff')."
+msgstr ""
+"комбіновані формати diff (\"-c\" і \"--cc\") не підтримуються у\n"
+"режимі порівняння директорій (\"-d\" і \"--dir-diff\")."
+
+#, c-format
+msgid "both files modified: '%s' and '%s'."
+msgstr "обидва файли змінено: \"%s\" і \"%s\"."
+
+msgid "working tree file has been left."
+msgstr "файл робочого дерева було залишено."
+
+#, c-format
+msgid "could not copy '%s' to '%s'"
+msgstr "не вдалося скопіювати \"%s\" в \"%s\""
+
+#, c-format
+msgid "temporary files exist in '%s'."
+msgstr "тимчасові файли існують в \"%s\"."
+
+msgid "you may want to cleanup or recover these."
+msgstr "можливо, ви захочете очистити або відновити їх."
+
+#, c-format
+msgid "failed: %d"
+msgstr "завершилось невдало: %d"
+
+msgid "use `diff.guitool` instead of `diff.tool`"
+msgstr "використовувати \"diff.guitool\" замість \"diff.tool\""
+
+msgid "perform a full-directory diff"
+msgstr "виконати порівняння всіх директорій"
+
+msgid "do not prompt before launching a diff tool"
+msgstr "не запитувати перед запуском diff"
+
+msgid "use symlinks in dir-diff mode"
+msgstr "використовувати символьні посилання у режимі dir-diff"
+
+msgid "tool"
+msgstr "засіб"
+
+msgid "use the specified diff tool"
+msgstr "використовувати вказаний diff засіб"
+
+msgid "print a list of diff tools that may be used with `--tool`"
+msgstr "показати список diff засобів, які можна використовувати з \"--tool\""
+
+msgid ""
+"make 'git-difftool' exit when an invoked diff tool returns a non-zero exit "
+"code"
+msgstr ""
+"змусити \"git-difftool\" завершувати роботу, коли викликаний diff засіб "
+"повертає ненульовий код завершення"
+
+msgid "specify a custom command for viewing diffs"
+msgstr "вказати нестандартну команду для перегляду різниць"
+
+msgid "passed to `diff`"
+msgstr "передається до \"diff\""
+
+msgid "difftool requires worktree or --no-index"
+msgstr "difftool потребує worktree або --no-index"
+
+msgid "no <tool> given for --tool=<tool>"
+msgstr "не надано <засіб> для --tool=<засіб>"
+
+msgid "no <cmd> given for --extcmd=<cmd>"
+msgstr "не надана <команда> для --extcmd=<команда>"
+
+msgid "git fast-export [<rev-list-opts>]"
+msgstr "git fast-export [<rev-list-опції>]"
+
+msgid "Error: Cannot export nested tags unless --mark-tags is specified."
+msgstr ""
+"Помилка: неможливо експортувати вкладені теги, якщо не вказано --mark-tags."
+
+msgid "--anonymize-map token cannot be empty"
+msgstr "--anonymize-map токен не може бути порожнім"
+
+msgid "show progress after <n> objects"
+msgstr "показати прогрес після <н> обʼєктів"
+
+msgid "select handling of signed tags"
+msgstr "вибрати обробку підписаних тегів"
+
+msgid "select handling of tags that tag filtered objects"
+msgstr "вибрати обробку тегів, якими позначено відфільтровані обʼєкти"
+
+msgid "select handling of commit messages in an alternate encoding"
+msgstr "вибрати обробку дописів до комітів в іншому кодуванні"
+
+msgid "dump marks to this file"
+msgstr "експортувати позначки в цей файл"
+
+msgid "import marks from this file"
+msgstr "імпортувати позначки з цього файлу"
+
+msgid "import marks from this file if it exists"
+msgstr "імпортувати позначки з цього файлу, якщо він існує"
+
+msgid "fake a tagger when tags lack one"
+msgstr "підробляти теггера, коли його не вказано для тега"
+
+msgid "output full tree for each commit"
+msgstr "виводити повне дерево для кожного коміту"
+
+msgid "use the done feature to terminate the stream"
+msgstr "використовувати особливість done для завершення потоку"
+
+msgid "skip output of blob data"
+msgstr "пропускати виведення blob-даних"
+
+msgid "refspec"
+msgstr "визначник посилання"
+
+msgid "apply refspec to exported refs"
+msgstr "застосувати визначник посилання до експортованих посилань"
+
+msgid "anonymize output"
+msgstr "анонімізувати вивід"
+
+msgid "from:to"
+msgstr "від:до"
+
+msgid "convert <from> to <to> in anonymized output"
+msgstr "конвертувати <від> в <до> в анонімізованому виводі"
+
+msgid "reference parents which are not in fast-export stream by object id"
+msgstr ""
+"батьки посилання, яких не знайдено в fast-export потоці за ідентифікатором "
+"обʼєкта"
+
+msgid "show original object ids of blobs/commits"
+msgstr "показувати оригінальні ідентифікатори обʼєктів blob/комітів"
+
+msgid "label tags with mark ids"
+msgstr "позначати теги ідентифікаторами позначок"
+
+#, c-format
+msgid "Missing from marks for submodule '%s'"
+msgstr "Відсутні \"від\" позначки для підмодуля \"%s\""
+
+#, c-format
+msgid "Missing to marks for submodule '%s'"
+msgstr "Відсутні \"до\" позначки для підмодуля \"%s\""
+
+#, c-format
+msgid "Expected 'mark' command, got %s"
+msgstr "Очікувалась команда \"mark\", отримано %s"
+
+#, c-format
+msgid "Expected 'to' command, got %s"
+msgstr "Очікувалась команда \"to\", отримано %s"
+
+msgid "Expected format name:filename for submodule rewrite option"
+msgstr "Очікуваний формат назва:назва файлу для параметра перезапису підмодуля"
+
+#, c-format
+msgid "feature '%s' forbidden in input without --allow-unsafe-features"
+msgstr ""
+"особливість \"%s\" не можна використовувати без --allow-unsafe-features"
+
+#, c-format
+msgid "Lockfile created but not reported: %s"
+msgstr "Файл блокування створено, але не звітовано: %s"
+
+msgid "git fetch [<options>] [<repository> [<refspec>...]]"
+msgstr "git fetch [<опції>] [<сховище> [<визначник посилання>...]]"
+
+msgid "git fetch [<options>] <group>"
+msgstr "git fetch [<опції>] [<група>]"
+
+msgid "git fetch --multiple [<options>] [(<repository> | <group>)...]"
+msgstr "git fetch --multiple [<опції>] [(<сховище> | <група>)...]"
+
+msgid "git fetch --all [<options>]"
+msgstr "git fetch --all [<опції>]"
+
+msgid "fetch.parallel cannot be negative"
+msgstr "fetch.parallel не може бути відʼємним"
+
+msgid "couldn't find remote ref HEAD"
+msgstr "не вдалося знайти посилання для віддаленого HEAD"
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "Від %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "об’єкт %s не знайдено"
+
+msgid "[up to date]"
+msgstr "[в актуальному стані]"
+
+msgid "[rejected]"
+msgstr "[відхилено]"
+
+msgid "can't fetch into checked-out branch"
+msgstr "неможливо виконати отримання в активну гілку"
+
+msgid "[tag update]"
+msgstr "[оновлення тегу]"
+
+msgid "unable to update local ref"
+msgstr "не вдалося оновити локальне посилання"
+
+msgid "would clobber existing tag"
+msgstr "зруйнує існуючий тег"
+
+msgid "[new tag]"
+msgstr "[новий тег]"
+
+msgid "[new branch]"
+msgstr "[нова гілка]"
+
+msgid "[new ref]"
+msgstr "[нове посилання]"
+
+msgid "forced update"
+msgstr "примусове оновлення"
+
+msgid "non-fast-forward"
+msgstr "без перемотування вперед"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "неможливо відкрити \"%s\""
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"fetch зазвичай показує, які гілки було примусово оновлено,\n"
+"але цю перевірку було вимкнено; щоб увімкнути її знову, скористайтесь \"--"
+"show-forced-updates\"\n"
+"або виконайте \"git config fetch.showForcedUpdates true\""
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"перевірка примусових оновлень зайняла %.2f секунд; ви можете скористатися\n"
+"\"--no-show-forced-updates\" або виконати \"git config fetch."
+"showForcedUpdates false\"\n"
+"щоб уникнути цієї перевірки\n"
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s не надіслав всіх необхідних обʼєктів\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr "відхилено %s, оскільки неглибокі корені не можна оновлювати"
+
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"не вдалося оновити деякі локальні посилання; спробуйте виконати\n"
+" \"git remote prune %s\", щоб видалити всі старі конфліктні гілки"
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s стануть висячими)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s став висячим)"
+
+msgid "[deleted]"
+msgstr "[видалено]"
+
+msgid "(none)"
+msgstr "(нічого)"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "відмовлено в отримані в гілку \"%s\", що знаходиться в \"%s\""
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "значення \"%s\" опції \"%s\" неприпустиме для %s"
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "опція \"%s\" ігнорується для %s\n"
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s не є припустимим об’єктом"
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr "об’єкт %s не існує"
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "виявлено кілька гілок, несумісних з --set-upstream"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"не вдалося встановити першоджерельне сховище для HEAD в \"%s\" з \"%s\", "
+"коли воно не вказує на жодну гілку."
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr ""
+"не встановлено першоджерельне сховище для віддалено відстежуваної гілки"
+
+msgid "not setting upstream for a remote tag"
+msgstr "не встановлено першоджерельне сховище для віддаленого тега"
+
+msgid "unknown branch type"
+msgstr "невідомий тип гілки"
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"джерельна гілка не знайдена;\n"
+"потрібно вказати лишень одну гілку з опцією --set-upstream"
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Отримання %s\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr "не вдалося отримати %s"
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "не вдалося отримати \"%s\" (код завершення: %d)\n"
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"віддалене сховище не вказано; будь ласка, вкажіть або URL або\n"
+"назву віддаленого сховища, з якого слід отримувати нові ревізії"
+
+msgid "you need to specify a tag name"
+msgstr "потрібно вказати назву тегу"
+
+msgid "fetch from all remotes"
+msgstr "отримати з усіх віддалених призначень"
+
+msgid "set upstream for git pull/fetch"
+msgstr "встановити першоджерельне сховище для git pull/fetch"
+
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "додати до .git/FETCH_HEAD замість перезапису"
+
+msgid "use atomic transaction to update references"
+msgstr "використати атомарну транзакцію для оновлення посилань"
+
+msgid "path to upload pack on remote end"
+msgstr "шлях для завантаження пакунка на віддаленій стороні"
+
+msgid "force overwrite of local reference"
+msgstr "примусовий перезапис локального посилання"
+
+msgid "fetch from multiple remotes"
+msgstr "отримати з кількох віддалених призначень"
+
+msgid "fetch all tags and associated objects"
+msgstr "отримати всі теги і повʼязані з ними обʼєкти"
+
+msgid "do not fetch all tags (--no-tags)"
+msgstr "не отримувати всі теги (--no-tags)"
+
+msgid "number of submodules fetched in parallel"
+msgstr "кількість підмодулів, що завантажуються паралельно"
+
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"змінити визначник посилання, щоб помістити всі посилання в refs/prefetch/"
+
+msgid "prune remote-tracking branches no longer on remote"
+msgstr ""
+"видалити віддалено відстежувані гілки, що більше не існують у віддаленому "
+"призначенні"
+
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr ""
+"видалити локальні теги, що більше не існують у віддаленому призначенні, і "
+"зруйнувати змінені теги"
+
+msgid "on-demand"
+msgstr "на вимогу"
+
+msgid "control recursive fetching of submodules"
+msgstr "контролювати рекурсивне отримання підмодулів"
+
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "записувати отримані посилання у файл FETCH_HEAD"
+
+msgid "keep downloaded pack"
+msgstr "зберегти завантажений пакунок"
+
+msgid "allow updating of HEAD ref"
+msgstr "дозволити оновлення HEAD посилання"
+
+msgid "deepen history of shallow clone"
+msgstr "поглибити історію неглибокого клону"
+
+msgid "deepen history of shallow repository based on time"
+msgstr "поглибити історію неглибокого клону залежно від часу"
+
+msgid "convert to a complete repository"
+msgstr "перетворити на повне сховище"
+
+msgid "re-fetch without negotiating common commits"
+msgstr "повторне отримання без узгодження спільних комітів"
+
+msgid "prepend this to submodule path output"
+msgstr "додавати це напочатку шляху до підмодуля при виведенні"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"поведінка за замовчуванням для рекурсивного отримання підмодулів (нижчий "
+"пріоритет, ніж у конфігураційних файлів)"
+
+msgid "accept refs that update .git/shallow"
+msgstr "приймати посилання, які оновлюють .git/shallow"
+
+msgid "refmap"
+msgstr "refmap"
+
+msgid "specify fetch refmap"
+msgstr "вказати мапу посилань для fetch"
+
+msgid "report that we have only objects reachable from this object"
+msgstr "звітувати, що у нас є тільки обʼєкти, доступні з цього обʼєкта"
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+"не отримувати файл пакунка; замість цього роздрукувати предків верхівок для "
+"узгодження"
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr "виконати \"maintenance --auto\" після отримуваання"
+
+msgid "check for forced-updates on all updated branches"
+msgstr "перевірити на примусове оновлення для всіх оновлених гілок"
+
+msgid "write the commit-graph after fetching"
+msgstr "записати коміт-граф після отримання"
+
+msgid "accept refspecs from stdin"
+msgstr "приймати визначники посилань з stdin"
+
+msgid "--negotiate-only needs one or more --negotiation-tip=*"
+msgstr "--negotiate-only потребує одного або кількох --negotiation-tip=*"
+
+msgid "negative depth in --deepen is not supported"
+msgstr "відʼємна глибина в --deepen не підтримується"
+
+msgid "--unshallow on a complete repository does not make sense"
+msgstr "--unshallow на повному сховищі не має сенсу"
+
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "не вдалося отримати пакунки з \"%s\""
+
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all не приймає сховище як аргумент"
+
+msgid "fetch --all does not make sense with refspecs"
+msgstr "fetch --all не має сенсу з визначниками посилань"
+
+#, c-format
+msgid "no such remote or remote group: %s"
+msgstr "немає такого віддаленого призначення або віддаленої групи: %s"
+
+msgid "fetching a group and specifying refspecs does not make sense"
+msgstr "отримання групи і вказівка визначників посилань не має сенсу"
+
+msgid "must supply remote when using --negotiate-only"
+msgstr ""
+"необхідно вказати віддалене призначення при використанні --negotiate-only"
+
+msgid "protocol does not support --negotiate-only, exiting"
+msgstr "протокол не підтримує --negotiate-only, вихід"
+
+msgid ""
+"--filter can only be used with the remote configured in extensions."
+"partialclone"
+msgstr ""
+"--filter можна використовувати лише з віддаленим призначенням, налаштованим "
+"у extensions.partialclone"
+
+msgid "--atomic can only be used when fetching from one remote"
+msgstr ""
+"--atomic може бути використано лише при отриманні з одного віддаленого "
+"джерела"
+
+msgid "--stdin can only be used when fetching from one remote"
+msgstr ""
+"--stdin можна використовувати лише при отриманні одного віддаленого джерела"
+
+msgid ""
+"git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"
+msgstr ""
+"git fmt-merge-msg [-m <допис>] [--log[=<н>] | --no-log] [--file <файл>]"
+
+msgid "populate log with at most <n> entries from shortlog"
+msgstr "заповнити журнал не більше ніж <н> записами з shortlog"
+
+msgid "alias for --log (deprecated)"
+msgstr "псевдонім для --log (застарілий)"
+
+msgid "text"
+msgstr "текст"
+
+msgid "use <text> as start of message"
+msgstr "використовувати <текст> як початок допису"
+
+msgid "use <name> instead of the real target branch"
+msgstr "використовувати <назва> замість реальної цільової гілки"
+
+msgid "file to read from"
+msgstr "файл, з якого читати"
+
+msgid "git for-each-ref [<options>] [<pattern>]"
+msgstr "git for-each-ref [<опції>] [<шаблон>]"
+
+msgid "git for-each-ref [--points-at <object>]"
+msgstr "git for-each-ref [--points-at <обʼєкт>]"
+
+msgid "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"
+msgstr "git for-each-ref [--merged [<коміт>]] [--no-merged [<коміт>]]"
+
+msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"
+msgstr "git for-each-ref [--contains [<коміт>]] [--no-contains [<коміт>]]"
+
+msgid "quote placeholders suitably for shells"
+msgstr "заповнювачі лапок для shell"
+
+msgid "quote placeholders suitably for perl"
+msgstr "заповнювачі лапок для perl"
+
+msgid "quote placeholders suitably for python"
+msgstr "заповнювачі лапок для python"
+
+msgid "quote placeholders suitably for Tcl"
+msgstr "заповнювачі лапок для Tcl"
+
+msgid "show only <n> matched refs"
+msgstr "показати тільки <н> відповідних посилань"
+
+msgid "respect format colors"
+msgstr "дотримуватися кольорів формату"
+
+msgid "print only refs which points at the given object"
+msgstr "виводити тільки посилання, які вказують на заданий об’єкт"
+
+msgid "print only refs that are merged"
+msgstr "виводити тільки злиті посилання"
+
+msgid "print only refs that are not merged"
+msgstr "виводити тільки не злиті посилання"
+
+msgid "print only refs which contain the commit"
+msgstr "виводити тільки ті посилання, що містять коміт"
+
+msgid "print only refs which don't contain the commit"
+msgstr "виводити тільки ті посилання, що не містять коміту"
+
+msgid "read reference patterns from stdin"
+msgstr "читати шаблони посилань з stdin"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "невідомі аргументи надані через --stdin"
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<конфіг> [--] <аргументи>"
+
+msgid "config"
+msgstr "конфіг"
+
+msgid "config key storing a list of repository paths"
+msgstr "ключ конфігурації, в якому зберігається список шляхів до сховищ"
+
+msgid "missing --config=<config>"
+msgstr "відсутній --config=<конфіг>"
+
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "невірно задано параметр --config=%s"
+
+msgid "unknown"
+msgstr "невідомо"
+
+#. TRANSLATORS: e.g. error in tree 01bfda: <more explanation>
+#, c-format
+msgid "error in %s %s: %s"
+msgstr "помилка в %s %s: %s"
+
+#. TRANSLATORS: e.g. warning in tree 01bfda: <more explanation>
+#, c-format
+msgid "warning in %s %s: %s"
+msgstr "попередження в %s %s: %s"
+
+#, c-format
+msgid "broken link from %7s %s"
+msgstr "пошкоджене посилання з %7s %s"
+
+msgid "wrong object type in link"
+msgstr "невірний тип об’єкта в посиланні"
+
+#, c-format
+msgid ""
+"broken link from %7s %s\n"
+"              to %7s %s"
+msgstr ""
+"пошкоджене посилання з %7s %s\n"
+"                    на %7s %s"
+
+msgid "Checking connectivity"
+msgstr "Перевірка підключення"
+
+#, c-format
+msgid "missing %s %s"
+msgstr "відсутній %s %s"
+
+#, c-format
+msgid "unreachable %s %s"
+msgstr "недосяжний %s %s"
+
+#, c-format
+msgid "dangling %s %s"
+msgstr "висячий %s %s"
+
+msgid "could not create lost-found"
+msgstr "не вдалося створити lost-found"
+
+#, c-format
+msgid "could not write '%s'"
+msgstr "не вдалося записати \"%s\""
+
+#, c-format
+msgid "could not finish '%s'"
+msgstr "не вдалося завершити \"%s\""
+
+#, c-format
+msgid "Checking %s"
+msgstr "Перевірка %s"
+
+#, c-format
+msgid "Checking connectivity (%d objects)"
+msgstr "Перевірка підключення (%d обʼєктів)"
+
+#, c-format
+msgid "Checking %s %s"
+msgstr "Перевірка %s %s"
+
+msgid "broken links"
+msgstr "пошкоджені посилання"
+
+#, c-format
+msgid "root %s"
+msgstr "корінь %s"
+
+#, c-format
+msgid "tagged %s %s (%s) in %s"
+msgstr "з тегом %s %s (%s) в %s"
+
+#, c-format
+msgid "%s: object corrupt or missing"
+msgstr "%s: об’єкт пошкоджений або відсутній"
+
+#, c-format
+msgid "%s: invalid reflog entry %s"
+msgstr "%s: неприпустимий запис журналу посилань %s"
+
+#, c-format
+msgid "Checking reflog %s->%s"
+msgstr "Перевірка журналу посилань %s->%s"
+
+#, c-format
+msgid "%s: invalid sha1 pointer %s"
+msgstr "%s: невірний вказівник sha1 %s"
+
+#, c-format
+msgid "%s: not a commit"
+msgstr "%s не є комітом"
+
+msgid "notice: No default references"
+msgstr "повідомлення: Немає посилань за замовчуванням"
+
+#, c-format
+msgid "%s: hash-path mismatch, found at: %s"
+msgstr "%s: невідповідність хеш/шлях знайдена в: %s"
+
+#, c-format
+msgid "%s: object corrupt or missing: %s"
+msgstr "%s: об’єкт пошкоджений або відсутній: %s"
+
+#, c-format
+msgid "%s: object is of unknown type '%s': %s"
+msgstr "%s: об’єкт невідомого типу \"%s\": %s"
+
+#, c-format
+msgid "%s: object could not be parsed: %s"
+msgstr "%s: неможливо розібрати об’єкт: %s"
+
+#, c-format
+msgid "bad sha1 file: %s"
+msgstr "невірний sha1 файл: %s"
+
+msgid "Checking object directory"
+msgstr "Перевірка директорії об’єкта"
+
+msgid "Checking object directories"
+msgstr "Перевірка директорій обʼєкта"
+
+#, c-format
+msgid "Checking %s link"
+msgstr "Перевірка %s посилання"
+
+#, c-format
+msgid "invalid %s"
+msgstr "неприпустимий %s"
+
+#, c-format
+msgid "%s points to something strange (%s)"
+msgstr "%s вказує на щось дивне (%s)"
+
+#, c-format
+msgid "%s: detached HEAD points at nothing"
+msgstr "%s: відокремлений HEAD вказує на ніщо"
+
+#, c-format
+msgid "notice: %s points to an unborn branch (%s)"
+msgstr "повідомлення: %s вказує на ненароджену гілку (%s)"
+
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Перевірка дерева кеша для \"%s\""
+
+#, c-format
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: невірний sha1 вказівник в cache-tree для %s"
+
+msgid "non-tree in cache-tree"
+msgstr "non-tree в cache-tree"
+
+#, c-format
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: невірний sha1 вказівник в resolve-undo для %s"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "не вдалося завантажити rev-index для пакунку \"%s\""
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "неприпустимий rev-index для \"%s\""
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<обʼєкт>...]"
+
+msgid "show unreachable objects"
+msgstr "показати недосяжні об’єкти"
+
+msgid "show dangling objects"
+msgstr "показати висячі об’єкти"
+
+msgid "report tags"
+msgstr "звітувати про теги"
+
+msgid "report root nodes"
+msgstr "звітувати про кореневі вузли"
+
+msgid "make index objects head nodes"
+msgstr "побудувати головні вузли об’єктів індексу"
+
+msgid "make reflogs head nodes (default)"
+msgstr "побудувати головні вузли журналу посилань (за замовчуванням)"
+
+msgid "also consider packs and alternate objects"
+msgstr "також розглядати пакунки та запозичені об’єкти"
+
+msgid "check only connectivity"
+msgstr "перевірити лише звʼязність"
+
+msgid "enable more strict checking"
+msgstr "увімкнути більш сувору перевірку"
+
+msgid "write dangling objects in .git/lost-found"
+msgstr "записувати висячі об’єкти в .git/lost-found"
+
+msgid "show progress"
+msgstr "показувати прогрес"
+
+msgid "show verbose names for reachable objects"
+msgstr "показувати докладні назви для доступних об’єктів"
+
+msgid "Checking objects"
+msgstr "Перевірка обʼєктів"
+
+#, c-format
+msgid "%s: object missing"
+msgstr "%s: об’єкт відсутній"
+
+#, c-format
+msgid "invalid parameter: expected sha1, got '%s'"
+msgstr "неприпустимий параметр: очікувалось sha1, надано \"%s\""
+
+msgid "git fsmonitor--daemon start [<options>]"
+msgstr "git fsmonitor--daemon start [<опції>]"
+
+msgid "git fsmonitor--daemon run [<options>]"
+msgstr "git fsmonitor--daemon run [<опції>]"
+
+#, c-format
+msgid "value of '%s' out of range: %d"
+msgstr "значення \"%s\" за межами діапазону: %d"
+
+#, c-format
+msgid "value of '%s' not bool or int: %d"
+msgstr "значення \"%s\" не є bool або int: %d"
+
+#, c-format
+msgid "fsmonitor-daemon is watching '%s'\n"
+msgstr "fsmonitor-daemon стежить за \"%s\"\n"
+
+#, c-format
+msgid "fsmonitor-daemon is not watching '%s'\n"
+msgstr "fsmonitor-daemon не стежить за \"%s\"\n"
+
+#, c-format
+msgid "could not create fsmonitor cookie '%s'"
+msgstr "не вдалося створити fsmonitor cookie \"%s\""
+
+#, c-format
+msgid "fsmonitor: cookie_result '%d' != SEEN"
+msgstr "fsmonitor: cookie_result \"%d\" != SEEN"
+
+#, c-format
+msgid "could not start IPC thread pool on '%s'"
+msgstr "не вдалося запустити пул потоків IPC на \"%s\""
+
+msgid "could not start fsmonitor listener thread"
+msgstr "не вдалося запустити потік слухача fsmonitor"
+
+msgid "could not start fsmonitor health thread"
+msgstr "не вдалося запустити потік стану fsmonitor"
+
+msgid "could not initialize listener thread"
+msgstr "не вдалося ініціалізувати потік слухача"
+
+msgid "could not initialize health thread"
+msgstr "не вдалося ініціалізувати потік стану"
+
+#, c-format
+msgid "could not cd home '%s'"
+msgstr "не вдалося виконати cd home \"%s\""
+
+#, c-format
+msgid "fsmonitor--daemon is already running '%s'"
+msgstr "fsmonitor--daemon вже запущений \"%s\""
+
+#, c-format
+msgid "running fsmonitor-daemon in '%s'\n"
+msgstr "запуск fsmonitor-daemon в \"%s\"\n"
+
+#, c-format
+msgid "starting fsmonitor-daemon in '%s'\n"
+msgstr "старт fsmonitor-daemon в \"%s\"\n"
+
+msgid "daemon failed to start"
+msgstr "не вдалося запустити демон"
+
+msgid "daemon not online yet"
+msgstr "демон ще не онлайн"
+
+msgid "daemon terminated"
+msgstr "роботу демона припинено"
+
+msgid "detach from console"
+msgstr "від’єднати від консолі"
+
+msgid "use <n> ipc worker threads"
+msgstr "використовувати <н> потоків IPC працівника"
+
+msgid "max seconds to wait for background daemon startup"
+msgstr "максимальна кількість секунд для очікування запуску фонового демона"
+
+#, c-format
+msgid "invalid 'ipc-threads' value (%d)"
+msgstr "невірне значення \"ipc-threads\" (%d)"
+
+#, c-format
+msgid "Unhandled subcommand '%s'"
+msgstr "Необроблена підкоманда \"%s\""
+
+msgid "fsmonitor--daemon not supported on this platform"
+msgstr "fsmonitor--daemon не підтримується на цій платформі"
+
+msgid "git gc [<options>]"
+msgstr "git gc [<опції>]"
+
+#, c-format
+msgid "Failed to fstat %s: %s"
+msgstr "Не вдалося виконати fstat %s: %s"
+
+#, c-format
+msgid "failed to parse '%s' value '%s'"
+msgstr "не вдалося розібрати \"%s\" значення \"%s\""
+
+#, c-format
+msgid "cannot stat '%s'"
+msgstr "неможливо виконати stat \"%s\""
+
+#, c-format
+msgid ""
+"The last gc run reported the following. Please correct the root cause\n"
+"and remove %s\n"
+"Automatic cleanup will not be performed until the file is removed.\n"
+"\n"
+"%s"
+msgstr ""
+"Попередній запуск gc показав наступне. Будь ласка, виправте першопричину\n"
+"і видаліть %s\n"
+"Автоматичне очищення не буде виконано, доки файл не буде вилучено.\n"
+"\n"
+"%s"
+
+msgid "prune unreferenced objects"
+msgstr "видалити об’єкти, на які немає посилань"
+
+msgid "pack unreferenced objects separately"
+msgstr "пакувати об’єкти, на які немає посилань, окремо"
+
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "з --cruft, обмежити розмір нових марних пакунків"
+
+msgid "be more thorough (increased runtime)"
+msgstr "працювати ретельніше (збільшує час виконання)"
+
+msgid "enable auto-gc mode"
+msgstr "увімкнути режим автоматичного збору сміття"
+
+msgid "force running gc even if there may be another gc running"
+msgstr "примусово запускати збирач сміття, навіть якщо інший збирач вже працює"
+
+msgid "repack all other packs except the largest pack"
+msgstr "перепакувати всі пакунки, крім найбільшого"
+
+#, c-format
+msgid "failed to parse gc.logExpiry value %s"
+msgstr "не вдалося розібрати gc.logExpiry значення %s"
+
+#, c-format
+msgid "failed to parse prune expiry value %s"
+msgstr ""
+"не вдалося розібрати значення дати закінчення терміну дії для видалення %s"
+
+#, c-format
+msgid "Auto packing the repository in background for optimum performance.\n"
+msgstr ""
+"Автоматичне пакування сховища у фоновому режимі для оптимальної "
+"продуктивності.\n"
+
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr "Автоматичне пакування сховища для оптимальної продуктивності.\n"
+
+#, c-format
+msgid "See \"git help gc\" for manual housekeeping.\n"
+msgstr ""
+"Дивіться \"git help gc\" для отримання інформації про налагодження вручну.\n"
+
+#, c-format
+msgid ""
+"gc is already running on machine '%s' pid %<PRIuMAX> (use --force if not)"
+msgstr ""
+"збір сміття вже запущено на машині \"%s\" pid %<PRIuMAX> (скористайтесь --"
+"force, якщо ні)"
+
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+"Занадто багато недосяжних вільних об’єктів; запустіть \"git prune\", щоб "
+"вилучити їх."
+
+msgid ""
+"git maintenance run [--auto] [--[no-]quiet] [--task=<task>] [--schedule]"
+msgstr ""
+"git maintenance run [--auto] [--[no-]quiet] [--task=<завдання>] [--schedule]"
+
+msgid "--no-schedule is not allowed"
+msgstr "--no-schedule не дозволяється"
+
+#, c-format
+msgid "unrecognized --schedule argument '%s'"
+msgstr "нерозпізнаний аргумент --schedule \"%s\""
+
+msgid "failed to write commit-graph"
+msgstr "не вдалося записати граф комітів"
+
+msgid "failed to prefetch remotes"
+msgstr "не вдалося виконати попереднє отримання з віддалених сховищ"
+
+msgid "failed to start 'git pack-objects' process"
+msgstr "не вдалося запустити \"git pack-objects\" процес"
+
+msgid "failed to finish 'git pack-objects' process"
+msgstr "не вдалося завершити \"git pack-objects\" процес"
+
+msgid "failed to write multi-pack-index"
+msgstr "не вдалося записати multi-pack-index"
+
+msgid "'git multi-pack-index expire' failed"
+msgstr "\"git multi-pack-index expire\" завершився невдало"
+
+msgid "'git multi-pack-index repack' failed"
+msgstr "\"git multi-pack-index repack\" завершився невдало"
+
+msgid ""
+"skipping incremental-repack task because core.multiPackIndex is disabled"
+msgstr ""
+"пропуск incremental-repack завдання, оскільки core.multiPackIndex вимкнено"
+
+#, c-format
+msgid "lock file '%s' exists, skipping maintenance"
+msgstr "файл блокування \"%s\" існує, пропуск обслуговування"
+
+#, c-format
+msgid "task '%s' failed"
+msgstr "завдання \"%s\" завершилося невдало"
+
+#, c-format
+msgid "'%s' is not a valid task"
+msgstr "\"%s\" не є припустимим завданням"
+
+#, c-format
+msgid "task '%s' cannot be selected multiple times"
+msgstr "завдання \"%s\" не можна вибрати кілька разів"
+
+msgid "run tasks based on the state of the repository"
+msgstr "запускати завдання на основі стану сховища"
+
+msgid "frequency"
+msgstr "частота"
+
+msgid "run tasks based on frequency"
+msgstr "запускати задачі на основі частоти"
+
+msgid "do not report progress or other information over stderr"
+msgstr "не показувати хід виконання та іншу інформацію в stderr"
+
+msgid "task"
+msgstr "завдання"
+
+msgid "run a specific task"
+msgstr "запустити певне завдання"
+
+msgid "use at most one of --auto and --schedule=<frequency>"
+msgstr "використовуйте щонайбільше одну з опцій --auto та --schedule=<частота>"
+
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "не вдалося додати \"%s\" значення до \"%s\""
+
+msgid "return success even if repository was not registered"
+msgstr "повертати успіх, навіть якщо сховище не було зареєстровано"
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "не вдалося скинути \"%s\" значення для \"%s\""
+
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "сховище \"%s\" не зареєстровано"
+
+#, c-format
+msgid "failed to expand path '%s'"
+msgstr "не вдалося розгорнути шлях \"%s\""
+
+msgid "failed to start launchctl"
+msgstr "не вдалося запустити launchctl"
+
+#, c-format
+msgid "failed to create directories for '%s'"
+msgstr "не вдалося створити директорії для \"%s\""
+
+#, c-format
+msgid "failed to bootstrap service %s"
+msgstr "не вдалося розгорнути службу %s"
+
+msgid "failed to create temp xml file"
+msgstr "не вдалося створити тимчасовий xml файл"
+
+msgid "failed to start schtasks"
+msgstr "не вдалося запустити schtasks"
+
+msgid "failed to run 'crontab -l'; your system might not support 'cron'"
+msgstr ""
+"не вдалося запустити \"crontab -l\"; можливо, ваша система не підтримує "
+"\"cron\""
+
+msgid "failed to create crontab temporary file"
+msgstr "не вдалося створити тимчасовий crontab файл"
+
+msgid "failed to open temporary file"
+msgstr "не вдалося відкрити тимчасовий файл"
+
+msgid "failed to run 'crontab'; your system might not support 'cron'"
+msgstr ""
+"не вдалося запустити \"crontab\"; можливо, ваша система не підтримує \"cron\""
+
+msgid "'crontab' died"
+msgstr "\"crontab\" завершився невдало"
+
+#, c-format
+msgid "failed to delete '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+#, c-format
+msgid "failed to flush '%s'"
+msgstr "не вдалося очистити \"%s\""
+
+msgid "failed to start systemctl"
+msgstr "не вдалося стартувати systemctl"
+
+msgid "failed to run systemctl"
+msgstr "не вдалося запустити systemctl"
+
+#, c-format
+msgid "unrecognized --scheduler argument '%s'"
+msgstr "нерозпізнаний --scheduler аргумент \"%s\""
+
+msgid "neither systemd timers nor crontab are available"
+msgstr "недоступні ні systemd таймери, ні crontab"
+
+#, c-format
+msgid "%s scheduler is not available"
+msgstr "%s планувальник недоступний"
+
+msgid "another process is scheduling background maintenance"
+msgstr "ще один процес планує фонове обслуговування"
+
+msgid "git maintenance start [--scheduler=<scheduler>]"
+msgstr "git maintenance start [--scheduler=<планувальник>]"
+
+msgid "scheduler"
+msgstr "планувальник"
+
+msgid "scheduler to trigger git maintenance run"
+msgstr "планувальник для запуску обслуговування git"
+
+msgid "failed to set up maintenance schedule"
+msgstr "не вдалося встановити графік обслуговування"
+
+msgid "failed to add repo to global config"
+msgstr "не вдалося додати сховище до глобальної конфігурації"
+
+msgid "git maintenance <subcommand> [<options>]"
+msgstr "git maintenance <підкоманда> [<опції>]"
+
+msgid "git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"
+msgstr "git grep [<опції>] [-e] <шаблон> [<ревізія>...] [[--] <шлях>...]"
+
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr "grep: не вдалося створити потік: %s"
+
+#, c-format
+msgid "invalid number of threads specified (%d) for %s"
+msgstr "невірно вказана кількість потоків (%d) для %s"
+
+#. TRANSLATORS: %s is the configuration
+#. variable for tweaking threads, currently
+#. grep.threads
+#.
+#, c-format
+msgid "no threads support, ignoring %s"
+msgstr "немає підтримки потоків, ігнорування %s"
+
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr "не вдалося прочитати дерево (%s)"
+
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr "не вдалося виконати grep для об’єкта типу %s"
+
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr "switch \"%c\" очікує числове значення"
+
+msgid "search in index instead of in the work tree"
+msgstr "шукати в індексі замість робочого дерева"
+
+msgid "find in contents not managed by git"
+msgstr "шукати у вмісті, не індексованому git"
+
+msgid "search in both tracked and untracked files"
+msgstr "шукати як у відстежуваних, так і в невідстежуваних файлах"
+
+msgid "ignore files specified via '.gitignore'"
+msgstr "ігнорувати файли, вказані через \".gitignore\""
+
+msgid "recursively search in each submodule"
+msgstr "шукати рекурсивно в кожному підмодулі"
+
+msgid "show non-matching lines"
+msgstr "показувати рядки, що не збігаються"
+
+msgid "case insensitive matching"
+msgstr "нечутливе до регістру зіставлення"
+
+msgid "match patterns only at word boundaries"
+msgstr "зіставляти шаблони тільки на межі слів"
+
+msgid "process binary files as text"
+msgstr "обробляти бінарні файли як текст"
+
+msgid "don't match patterns in binary files"
+msgstr "не зіставляти шаблони в бінарних файлах"
+
+msgid "process binary files with textconv filters"
+msgstr "обробляти бінарні файли за допомогою textconv фільтрів"
+
+msgid "search in subdirectories (default)"
+msgstr "шукати в піддиректоріях (за замовчуванням)"
+
+msgid "descend at most <n> levels"
+msgstr "спускатися не більше ніж на <н> рівнів"
+
+msgid "use extended POSIX regular expressions"
+msgstr "використовувати розширені POSIX регулярні вирази"
+
+msgid "use basic POSIX regular expressions (default)"
+msgstr "використовувати базові регулярні вирази POSIX (за замовчуванням)"
+
+msgid "interpret patterns as fixed strings"
+msgstr "інтерпретувати шаблони як фіксовані строки"
+
+msgid "use Perl-compatible regular expressions"
+msgstr "використовувати Perl-сумісні регулярні вирази"
+
+msgid "show line numbers"
+msgstr "показувати номери рядків"
+
+msgid "show column number of first match"
+msgstr "показувати номер стовпця першого збігу"
+
+msgid "don't show filenames"
+msgstr "не показувати назви файлів"
+
+msgid "show filenames"
+msgstr "показувати назви файлів"
+
+msgid "show filenames relative to top directory"
+msgstr "показувати назви файлів відносно верхнього каталогу"
+
+msgid "show only filenames instead of matching lines"
+msgstr "показувати лише назви файлів замість відповідних рядків"
+
+msgid "synonym for --files-with-matches"
+msgstr "синонім для --files-with-matches"
+
+msgid "show only the names of files without match"
+msgstr "показувати лише назви файлів без збігу"
+
+msgid "print NUL after filenames"
+msgstr "друкувати NUL після назв файлів"
+
+msgid "show only matching parts of a line"
+msgstr "показувати лише частини рядка, що збігаються"
+
+msgid "show the number of matches instead of matching lines"
+msgstr "показувати кількість збігів замість рядків, що збігаються"
+
+msgid "highlight matches"
+msgstr "виділяти збіги"
+
+msgid "print empty line between matches from different files"
+msgstr "друкувати порожній рядок між збігами з різних файлів"
+
+msgid "show filename only once above matches from same file"
+msgstr "показувати назву файлу лише один раз над збігами з того файлу"
+
+msgid "show <n> context lines before and after matches"
+msgstr "показувати <н> рядків до і після збігу"
+
+msgid "show <n> context lines before matches"
+msgstr "показувати <н> рядків до збігу"
+
+msgid "show <n> context lines after matches"
+msgstr "показувати <н> рядків після збігу"
+
+msgid "use <n> worker threads"
+msgstr "використати <н> робочих потоків"
+
+msgid "shortcut for -C NUM"
+msgstr "скорочення для -C номер"
+
+msgid "show a line with the function name before matches"
+msgstr "показувати рядок з назвою функції перед збігами"
+
+msgid "show the surrounding function"
+msgstr "показати навколишню функцію"
+
+msgid "read patterns from file"
+msgstr "зчитувати шаблони з файлу"
+
+msgid "match <pattern>"
+msgstr "зіставляти <шаблон>"
+
+msgid "combine patterns specified with -e"
+msgstr "об’єднати шаблони, вказані через -e"
+
+msgid "indicate hit with exit status without output"
+msgstr "позначати збіг кодом виходу без виводу"
+
+msgid "show only matches from files that match all patterns"
+msgstr "показувати збіги лише з файлів, які відповідають усім шаблонам"
+
+msgid "pager"
+msgstr "пейджер"
+
+msgid "show matching files in the pager"
+msgstr "показати відповідні файли в пейджері"
+
+msgid "allow calling of grep(1) (ignored by this build)"
+msgstr "дозволяти виклик grep(1) (ігнорується у цій збірці)"
+
+msgid "maximum number of results per file"
+msgstr "максимальна кількість результатів у файлі"
+
+msgid "no pattern given"
+msgstr "шаблон не надано"
+
+msgid "--no-index or --untracked cannot be used with revs"
+msgstr "--no-index або --untracked не можна використовувати з ревізіями"
+
+#, c-format
+msgid "unable to resolve revision: %s"
+msgstr "не вдалося розвʼязати ревізію: %s"
+
+msgid "--untracked not supported with --recurse-submodules"
+msgstr "--untracked не підтримується з --recurse-submodules"
+
+msgid "invalid option combination, ignoring --threads"
+msgstr "неприпустима комбінація опцій, ігнорування --threads"
+
+msgid "no threads support, ignoring --threads"
+msgstr "немає підтримки потоків, ігнорування --threads"
+
+#, c-format
+msgid "invalid number of threads specified (%d)"
+msgstr "вказано неприпустиму кількість потоків (%d)"
+
+msgid "--open-files-in-pager only works on the worktree"
+msgstr "--open-files-in-pager працює тільки в робочому дереві"
+
+msgid "--[no-]exclude-standard cannot be used for tracked contents"
+msgstr ""
+"--[no-]exclude-standard не можна використовувати для відстежуваного вмісту"
+
+msgid "both --cached and trees are given"
+msgstr "надані як --cached, так і дерева"
+
+msgid ""
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
+msgstr ""
+"git hash-object [-t <тип>] [-w] [--path=<файл> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <файл>..."
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <тип>] [-w] --stdin-paths [--no-filters]"
+
+msgid "object type"
+msgstr "тип обʼєкта"
+
+msgid "write the object into the object database"
+msgstr "записати об’єкт до бази даних об’єктів"
+
+msgid "read the object from stdin"
+msgstr "прочитати об’єкт з stdin"
+
+msgid "store file as is without filters"
+msgstr "зберегти файл як є без фільтрів"
+
+msgid ""
+"just hash any random garbage to create corrupt objects for debugging Git"
+msgstr ""
+"просто хешувати будь-який випадковий непотріб, щоб створити пошкоджені "
+"об’єкти для відлагодження Git"
+
+msgid "process file as it were from this path"
+msgstr "оброблювати файл, наче він з цього шляху"
+
+msgid "print all available commands"
+msgstr "показати всі доступні команди"
+
+msgid "show external commands in --all"
+msgstr "показати зовнішні команди в --all"
+
+msgid "show aliases in --all"
+msgstr "показати псевдоніми в --all"
+
+msgid "exclude guides"
+msgstr "виключити посібники"
+
+msgid "show man page"
+msgstr "показати сторінку керівництва користувача"
+
+msgid "show manual in web browser"
+msgstr "показати сторінку керівництва користувача в веб-браузері"
+
+msgid "show info page"
+msgstr "показати інформаційну сторінку"
+
+msgid "print command description"
+msgstr "показати опис команди"
+
+msgid "print list of useful guides"
+msgstr "показати список корисних посібників"
+
+msgid "print list of user-facing repository, command and file interfaces"
+msgstr "показати список інтерфейсів користувача для сховищ, команд та файлів"
+
+msgid "print list of file formats, protocols and other developer interfaces"
+msgstr ""
+"показати список форматів файлів, протоколів та інших інтерфейсів розробника"
+
+msgid "print all configuration variable names"
+msgstr "показати всі назви конфігураційних змінних"
+
+msgid "git help [[-i|--info] [-m|--man] [-w|--web]] [<command>|<doc>]"
+msgstr "git help [[-i|--info] [-m|--man] [-w|--web]] [<команда>|<док>]"
+
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "нерозпізнаний формат довідки \"%s\""
+
+msgid "Failed to start emacsclient."
+msgstr "Не вдалося запустити emacsclient."
+
+msgid "Failed to parse emacsclient version."
+msgstr "Не вдалося розібрати версію emacsclient."
+
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr "версія emacsclient \"%d\" застаріла (< 22)."
+
+#, c-format
+msgid "failed to exec '%s'"
+msgstr "не вдалося виконати \"%s\""
+
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+"\"%s\": шлях до непідтримуваного переглядача керівництва користувача.\n"
+"Будь ласка, скористайтесь \"man.<tool>.cmd\" замість цього."
+
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+"\"%s\": команда для підтримуваного переглядача керівництва користувача.\n"
+"Будь ласка, скористайтесь \"man.<tool>.path\" замість цього."
+
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr "\"%s\": невідомий переглядач керівництва користувача."
+
+msgid "no man viewer handled the request"
+msgstr "жоден з переглядачів керівництва користувача не обробив запит"
+
+msgid "no info viewer handled the request"
+msgstr "жоден з переглядачів інформації не обробив запит"
+
+#, c-format
+msgid "'%s' is aliased to '%s'"
+msgstr "\"%s\" є псевдонімом для \"%s\""
+
+#, c-format
+msgid "bad alias.%s string: %s"
+msgstr "невірний псевдонім.%s рядок: %s"
+
+#, c-format
+msgid "the '%s' option doesn't take any non-option arguments"
+msgstr "опція \"%s\" не приймає жодних неопціональних аргументів"
+
+msgid ""
+"the '--no-[external-commands|aliases]' options can only be used with '--all'"
+msgstr ""
+"опції \"--no-[external-commands|aliases]\" можна використовувати лише з \"--"
+"all\""
+
+#, c-format
+msgid "usage: %s%s"
+msgstr "використання: %s%s"
+
+msgid "'git help config' for more information"
+msgstr "\"git help config\" для додаткової інформації"
+
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<шлях>] <назва-гачка> [-- "
+"<аргументи-гачка>]"
+
+msgid "silently ignore missing requested <hook-name>"
+msgstr "мовчки ігнорувати відсутній <назва-гачка>"
+
+msgid "file to read into hooks' stdin"
+msgstr "файл, з якого читати stdin хука"
+
+#, c-format
+msgid "object type mismatch at %s"
+msgstr "не співпадає тип обʼєкта для %s"
+
+#, c-format
+msgid "did not receive expected object %s"
+msgstr "очікуваних обʼєктів не отримано %s"
+
+#, c-format
+msgid "object %s: expected type %s, found %s"
+msgstr "обʼєкт %s: очікувався тип %s, знайдено %s"
+
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] "не вдається заповнити %d байт"
+msgstr[1] "не вдається заповнити %d байти"
+msgstr[2] "не вдається заповнити %d байтів"
+
+msgid "early EOF"
+msgstr "ранній EOF"
+
+msgid "read error on input"
+msgstr "помилка зчитування на вході"
+
+msgid "used more bytes than were available"
+msgstr "використано більше байтів, ніж було доступно"
+
+msgid "pack too large for current definition of off_t"
+msgstr "пакунок занадто великий для поточного визначення off_t"
+
+#, c-format
+msgid "pack exceeds maximum allowed size (%s)"
+msgstr "пакунок перевищує максимально дозволений розмір (%s)"
+
+msgid "pack signature mismatch"
+msgstr "не співпадає підпис пакунка"
+
+#, c-format
+msgid "pack version %<PRIu32> unsupported"
+msgstr "версія пакунка %<PRIu32> не підтримується"
+
+#, c-format
+msgid "pack has bad object at offset %<PRIuMAX>: %s"
+msgstr "пакунок має невірний обʼєкт зі зміщенням %<PRIuMAX>: %s"
+
+#, c-format
+msgid "inflate returned %d"
+msgstr "розпаковувач повернув %d"
+
+msgid "offset value overflow for delta base object"
+msgstr "переповнення значення зсуву для обʼєкту дельти з базою"
+
+msgid "delta base offset is out of bound"
+msgstr "зсув дельти з базою виходить за межі"
+
+#, c-format
+msgid "unknown object type %d"
+msgstr "невідомий тип обʼєкта %d"
+
+msgid "cannot pread pack file"
+msgstr "неможливо прочитати файл пакунка"
+
+#, c-format
+msgid "premature end of pack file, %<PRIuMAX> byte missing"
+msgid_plural "premature end of pack file, %<PRIuMAX> bytes missing"
+msgstr[0] "передчасний кінець файлу пакунка, %<PRIuMAX> байт відсутній"
+msgstr[1] "передчасний кінець файлу пакунка, %<PRIuMAX> байта відсутні"
+msgstr[2] "передчасний кінець файлу пакунка, %<PRIuMAX> байтів відсутні"
+
+msgid "serious inflate inconsistency"
+msgstr "серйозне неспівпадіння під час розпакування"
+
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr "ВИЯВЛЕНО SHA1 КОЛІЗІЮ З %s!"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "не вдалося прочитати %s"
+
+#, c-format
+msgid "cannot read existing object info %s"
+msgstr "неможливо прочитати інформацію про існуючий об’єкт %s"
+
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "неможливо прочитати існуючий об’єкт %s"
+
+#, c-format
+msgid "invalid blob object %s"
+msgstr "неприпустимий об’єкт blob %s"
+
+msgid "fsck error in packed object"
+msgstr "помилка fsck у запакованому обʼєкті"
+
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr "Не всі дочірні об’єкти %s доступні"
+
+msgid "failed to apply delta"
+msgstr "не вдалося застосувати дельту"
+
+msgid "Receiving objects"
+msgstr "Отримання об’єктів"
+
+msgid "Indexing objects"
+msgstr "Індексація об’єктів"
+
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr "пакунок пошкоджено (SHA1 не співпадає)"
+
+msgid "cannot fstat packfile"
+msgstr "неможливо зробити fstat файла пакунка"
+
+msgid "pack has junk at the end"
+msgstr "мотлох наприкінці пакунка"
+
+msgid "confusion beyond insanity in parse_pack_objects()"
+msgstr "плутанина на межі пекельного борошна в parse_pack_objects()"
+
+msgid "Resolving deltas"
+msgstr "Розв’язання дельт"
+
+#, c-format
+msgid "unable to create thread: %s"
+msgstr "не вдалося створити потік: %s"
+
+msgid "confusion beyond insanity"
+msgstr "плутанина на межі пекельного борошна"
+
+#, c-format
+msgid "completed with %d local object"
+msgid_plural "completed with %d local objects"
+msgstr[0] "завершено з %d локальним об’єктом"
+msgstr[1] "завершено з %d локальними об’єктами"
+msgstr[2] "завершено з %d локальними об’єктами"
+
+#, c-format
+msgid "Unexpected tail checksum for %s (disk corruption?)"
+msgstr "Неочікувана контрольна сума наприкінці %s (пошкодження диска?)"
+
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] "пакунок має %d нерозвʼязану дельту"
+msgstr[1] "пакунок має %d нерозвʼязаних дельти"
+msgstr[2] "пакунок має %d нерозвʼязаних дельт"
+
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr "не вдалося запакувати доданий об’єкт (%d)"
+
+#, c-format
+msgid "local object %s is corrupt"
+msgstr "локальний обʼєкт %s пошкоджено"
+
+#, c-format
+msgid "packfile name '%s' does not end with '.%s'"
+msgstr "ім’я файла пакунка '%s' не закінчується на '.%s'"
+
+#, c-format
+msgid "cannot write %s file '%s'"
+msgstr "неможливо записати %s файл '%s'"
+
+#, c-format
+msgid "cannot close written %s file '%s'"
+msgstr "неможливо закрити записаний %s файл '%s'"
+
+#, c-format
+msgid "unable to rename temporary '*.%s' file to '%s'"
+msgstr "не вдається перейменувати тимчасовий файл '*.%s' на '%s'"
+
+msgid "error while closing pack file"
+msgstr "помилка під час закриття файлу пакунка"
+
+#, c-format
+msgid "bad pack.indexVersion=%<PRIu32>"
+msgstr "невірний pack.indexVersion=%<PRIu32>"
+
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "Неможливо відкрити існуючий файл пакунка '%s"
+
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr "Неможливо відкрити існуючий індексний файл пакунка для '%s"
+
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] "не дельта: %d об’єкт"
+msgstr[1] "не дельта: %d об’єкта"
+msgstr[2] "не дельта: %d об’єктів"
+
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] "довжина ланцюжка = %d: %lu об’єкт"
+msgstr[1] "довжина ланцюжка = %d: %lu об’єкти"
+msgstr[2] "довжина ланцюжка = %d: %lu об’єктів"
+
+msgid "Cannot come back to cwd"
+msgstr "Неможливо повернутися до поточної робочої директорії"
+
+#, c-format
+msgid "bad %s"
+msgstr "невірний %s"
+
+#, c-format
+msgid "unknown hash algorithm '%s'"
+msgstr "невідомий хеш-алгоритм '%s'"
+
+msgid "--stdin requires a git repository"
+msgstr "--stdin потребує наявності git сховища"
+
+msgid "--verify with no packfile name given"
+msgstr "--verify без зазначення имені файла пакунка"
+
+msgid "fsck error in pack objects"
+msgstr "помилка fsck в об’єктах пакунка"
+
+msgid ""
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
+msgstr ""
+"git init [-q | --quiet] [--bare] [--template=<шаблон-директорія>]\n"
+"         [--separate-git-dir <git-директорія>] [--object-format=<формат>]\n"
+"         [-b <назва-гілки> | --initial-branch=<назва-гілки>]\n"
+"         [--shared[=<дозволи>]] [<директорія>]"
+
+msgid "permissions"
+msgstr "дозволи"
+
+msgid "specify that the git repository is to be shared amongst several users"
+msgstr "вказати, що git сховище буде спільним для кількох користувачів"
+
+msgid "override the name of the initial branch"
+msgstr "перевизначити назву початкової гілки"
+
+msgid "hash"
+msgstr "хеш"
+
+msgid "specify the hash algorithm to use"
+msgstr "вказати, який алгоритм хешування використовувати"
+
+#, c-format
+msgid "cannot mkdir %s"
+msgstr "не вдалося виконати mkdir %s"
+
+#, c-format
+msgid "cannot chdir to %s"
+msgstr "не вдалося виконати chdir %s"
+
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+"%s (або --work-tree=<директорія>) не дозволено без вказівки %s (або --git-"
+"dir=<каталог>)"
+
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr "Неможливо отримати доступ до робочого дерева \"%s\""
+
+msgid "--separate-git-dir incompatible with bare repository"
+msgstr "--separate-git-dir несумісна з порожнім сховищем"
+
+msgid ""
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
+msgstr ""
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<ключ>|<аліасКлюча>)"
+"[(=|:)<значення>])...]\n"
+"                       [--parse] [<файл>...]"
+
+msgid "edit files in place"
+msgstr "редагувати файли на місцях"
+
+msgid "trim empty trailers"
+msgstr "обрізати порожні причепи"
+
+msgid "placement"
+msgstr "розташування"
+
+msgid "where to place the new trailer"
+msgstr "де розмістити новий причіп"
+
+msgid "action if trailer already exists"
+msgstr "що робити, якщо причіп вже існує"
+
+msgid "action if trailer is missing"
+msgstr "що робити, якщо причіп відсутній"
+
+msgid "output only the trailers"
+msgstr "виводити лише причепи"
+
+msgid "do not apply trailer.* configuration variables"
+msgstr "не застосовувати конфігураційні змінні trailer.*"
+
+msgid "reformat multiline trailer values as single-line values"
+msgstr "переформатувати багаторядкові значення причепів в однорядкові"
+
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "аліас для --only-trailers --only-input --unfold"
+
+msgid "do not treat \"---\" as the end of input"
+msgstr "не обробляти \"---\" як кінець вводу"
+
+msgid "trailer(s) to add"
+msgstr "причіп(и) для додавання"
+
+msgid "--trailer with --only-input does not make sense"
+msgstr "--trailer з --only-input не має сенсу"
+
+msgid "no input file given for in-place editing"
+msgstr "не надано вхідного файлу для редагування на місці"
+
+msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
+msgstr "git log [<опції>] [<діапазон-ревізій>] [[--] <шлях>...]"
+
+msgid "git show [<options>] <object>..."
+msgstr "git show [<опції>] <обʼєкт>..."
+
+#, c-format
+msgid "invalid --decorate option: %s"
+msgstr "неприпустима --decorate опція: %s"
+
+msgid "suppress diff output"
+msgstr "приховати вивід diff"
+
+msgid "show source"
+msgstr "показати джерело"
+
+msgid "clear all previously-defined decoration filters"
+msgstr "очистити всі раніше визначені фільтри оздоблення"
+
+msgid "only decorate refs that match <pattern>"
+msgstr "оздоблювати лише посилання, що відповідають <шаблону>"
+
+msgid "do not decorate refs that match <pattern>"
+msgstr "не оздоблювати посилання, що відповідають <шаблону>"
+
+msgid "decorate options"
+msgstr "опції оздоблення"
+
+msgid ""
+"trace the evolution of line range <start>,<end> or function :<funcname> in "
+"<file>"
+msgstr ""
+"простежити еволюцію діапазону рядків <початок>,<кінець> або функції :<назва-"
+"функції> в <файлі>"
+
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "нерозпізнаний аргумент: %s"
+
+msgid "-L<range>:<file> cannot be used with pathspec"
+msgstr "-L<діапазон>:<файл> не можна використовувати з визначником шляху"
+
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr "Кінцевий результат: %d %s\n"
+
+msgid "unable to create temporary object directory"
+msgstr "не вдалося створити тимчасову директорію об’єкта"
+
+#, c-format
+msgid "git show %s: bad file"
+msgstr "git show %s: невірний файл"
+
+#, c-format
+msgid "could not read object %s"
+msgstr "не вдалося прочитати об’єкт %s"
+
+#, c-format
+msgid "unknown type: %d"
+msgstr "невідомий тип: %d"
+
+#, c-format
+msgid "%s: invalid cover from description mode"
+msgstr "%s: невірна обкладинка з режиму опису"
+
+msgid "format.headers without value"
+msgstr "format.headers без значення"
+
+#, c-format
+msgid "cannot open patch file %s"
+msgstr "не вдається відкрити файл латки %s"
+
+msgid "need exactly one range"
+msgstr "потрібен лишень один діапазон"
+
+msgid "not a range"
+msgstr "не діапазон"
+
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "не вдалося прочитати файл опису гілки \"%s\""
+
+msgid "cover letter needs email format"
+msgstr "супровідний лист має бути у форматі електронної пошти"
+
+msgid "failed to create cover-letter file"
+msgstr "не вдалося створити файл супровідного листа"
+
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr "неприпустимий in-reply-to: %s"
+
+msgid "git format-patch [<options>] [<since> | <revision-range>]"
+msgstr "git format-patch [<опції>] [<відколи> | <діапазон-ревізій>]"
+
+msgid "two output directories?"
+msgstr "дві вихідні директорії?"
+
+#, c-format
+msgid "unknown commit %s"
+msgstr "невідомий коміт %s"
+
+#, c-format
+msgid "failed to resolve '%s' as a valid ref"
+msgstr "не вдалося розпізнати \"%s\" як припустиме посилання"
+
+msgid "could not find exact merge base"
+msgstr "не вдалося знайти точну базу для злиття"
+
+msgid ""
+"failed to get upstream, if you want to record base commit automatically,\n"
+"please use git branch --set-upstream-to to track a remote branch.\n"
+"Or you could specify base commit by --base=<base-commit-id> manually"
+msgstr ""
+"не вдалося отримати першоджерельне сховище. Якщо ви хочете записати базовий "
+"коміт автоматично,\n"
+"будь ласка, скористайтесь git branch --set-upstream-to для відстежування "
+"віддаленої гілки.\n"
+"Або ви можете вказати базовий коміт вручну за допомогою --"
+"base=<ідентифікатор-базового-коміту>"
+
+msgid "failed to find exact merge base"
+msgstr "не вдалося знайти точну базу для злиття"
+
+msgid "base commit should be the ancestor of revision list"
+msgstr "базовий коміт має бути предком списку ревізій"
+
+msgid "base commit shouldn't be in revision list"
+msgstr "базового коміту не має бути в списку ревізій"
+
+msgid "cannot get patch id"
+msgstr "неможливо отримати ідентифікатор латки"
+
+msgid "failed to infer range-diff origin of current series"
+msgstr "не вдалося визначити походження різниці діапазонів поточного ряду"
+
+#, c-format
+msgid "using '%s' as range-diff origin of current series"
+msgstr "використання \"%s\" як походження різниці діапазонів поточного ряду"
+
+msgid "use [PATCH n/m] even with a single patch"
+msgstr "використовуйте [PATCH n/m] навіть з однією латкою"
+
+msgid "use [PATCH] even with multiple patches"
+msgstr "використовуйте [PATCH] навіть з кількома латками"
+
+msgid "print patches to standard out"
+msgstr "вивести латки на стандартний вивід"
+
+msgid "generate a cover letter"
+msgstr "скласти супровідний лист"
+
+msgid "use simple number sequence for output file names"
+msgstr "використати просту послідовність чисел для назв вихідних файлів"
+
+msgid "sfx"
+msgstr "суфікс"
+
+msgid "use <sfx> instead of '.patch'"
+msgstr "використати <суфікс> замість \".patch\""
+
+msgid "start numbering patches at <n> instead of 1"
+msgstr "почати нумерацію латок з <н> замість 1"
+
+msgid "reroll-count"
+msgstr "кількість перекидань"
+
+msgid "mark the series as Nth re-roll"
+msgstr "позначити ряд як N-не перекидання"
+
+msgid "max length of output filename"
+msgstr "максимальна довжина назви вихідного файлу"
+
+msgid "use [RFC PATCH] instead of [PATCH]"
+msgstr "використати [RFC PATCH] замість [PATCH]"
+
+msgid "cover-from-description-mode"
+msgstr "cover-from-description-mode"
+
+msgid "generate parts of a cover letter based on a branch's description"
+msgstr "скласти частини супровідного листа на основі опису гілки"
+
+msgid "use branch description from file"
+msgstr "використовувати опис гілки з файлу"
+
+msgid "use [<prefix>] instead of [PATCH]"
+msgstr "використати [<префікс>] замість [PATCH]"
+
+msgid "store resulting files in <dir>"
+msgstr "зберегти результуючі файли в <директорії>"
+
+msgid "don't strip/add [PATCH]"
+msgstr "не видаляти/додавати [PATCH]"
+
+msgid "don't output binary diffs"
+msgstr "не виводити бінарні різниці"
+
+msgid "output all-zero hash in From header"
+msgstr "вивести хеш з усіма нулями в заголовку From"
+
+msgid "don't include a patch matching a commit upstream"
+msgstr ""
+"не включати латки, які мають відповідні коміти в першоджерельному сховищі"
+
+msgid "show patch format instead of default (patch + stat)"
+msgstr "показати формат латки замість стандартного (латка + підсумок)"
+
+msgid "Messaging"
+msgstr "Повідомлення"
+
+msgid "header"
+msgstr "заголовок"
+
+msgid "add email header"
+msgstr "додати заголовок листа"
+
+msgid "email"
+msgstr "електронна адреса"
+
+msgid "add To: header"
+msgstr "додати To: заголовок"
+
+msgid "add Cc: header"
+msgstr "додати Cc: заголовок"
+
+msgid "ident"
+msgstr "особистість"
+
+msgid "set From address to <ident> (or committer ident if absent)"
+msgstr ""
+"встановити From адресу в <особистість> (або особистість комітера, якщо "
+"відсутня)"
+
+msgid "message-id"
+msgstr "ідентифікатор-повідомлення"
+
+msgid "make first mail a reply to <message-id>"
+msgstr "зробити перший лист відповіддю на <id-повідомлення>"
+
+msgid "boundary"
+msgstr "межа"
+
+msgid "attach the patch"
+msgstr "прикріпити латку"
+
+msgid "inline the patch"
+msgstr "вставити латку"
+
+msgid "enable message threading, styles: shallow, deep"
+msgstr "увімкнути потік повідомлень, стилі: дрібний, глибокий"
+
+msgid "signature"
+msgstr "підпис"
+
+msgid "add a signature"
+msgstr "додати підпис"
+
+msgid "base-commit"
+msgstr "базовий коміт"
+
+msgid "add prerequisite tree info to the patch series"
+msgstr "додати інформацію про дерево передумов до серії латок"
+
+msgid "add a signature from a file"
+msgstr "додати підпис з файлу"
+
+msgid "don't print the patch filenames"
+msgstr "не виводити назви файлів латок"
+
+msgid "show progress while generating patches"
+msgstr "показувати прогрес під час генерації патчів"
+
+msgid "show changes against <rev> in cover letter or single patch"
+msgstr ""
+"показувати зміни проти <ревізія> у супровідному листі або окремій латці"
+
+msgid "show changes against <refspec> in cover letter or single patch"
+msgstr ""
+"показувати зміни проти <визначник-посилання> у супровідному листі або "
+"окремій латці"
+
+msgid "percentage by which creation is weighted"
+msgstr "відсоток, за яким зважується створення"
+
+msgid "show in-body From: even if identical to the e-mail header"
+msgstr "показувати in-body From: навіть якщо він ідентичний заголовку листа"
+
+#, c-format
+msgid "invalid ident line: %s"
+msgstr "неприпустимий рядок особистості: %s"
+
+msgid "--name-only does not make sense"
+msgstr "--name-only не має сенсу"
+
+msgid "--name-status does not make sense"
+msgstr "--name-status не має сенсу"
+
+msgid "--check does not make sense"
+msgstr "--check не має сенсу"
+
+msgid "--remerge-diff does not make sense"
+msgstr "--remerge-diff не має сенсу"
+
+#, c-format
+msgid "could not create directory '%s'"
+msgstr "не вдалося створити директорію \"%s\""
+
+msgid "--interdiff requires --cover-letter or single patch"
+msgstr "--interdiff вимагає --cover-letter або окремої латки"
+
+msgid "Interdiff:"
+msgstr "Різниця з попередньою серією латок:"
+
+#, c-format
+msgid "Interdiff against v%d:"
+msgstr "Різниця з v%d:"
+
+msgid "--range-diff requires --cover-letter or single patch"
+msgstr "--range-diff потребує --cover-letter або окремої латки"
+
+msgid "Range-diff:"
+msgstr "Різниця діапазону з попередньою серією латок:"
+
+#, c-format
+msgid "Range-diff against v%d:"
+msgstr "Різниця діапазону з v%d:"
+
+#, c-format
+msgid "unable to read signature file '%s'"
+msgstr "не вдалося прочитати файл підпису \"%s\""
+
+msgid "Generating patches"
+msgstr "Створення латок"
+
+msgid "failed to create output files"
+msgstr "не вдалося створити вихідні файли"
+
+msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
+msgstr "git cherry [-v] [<першоджерельне-сховище> [<голова> [<ліміт>]]]]"
+
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+"Не вдалося знайти віддалено відстежувану гілку, будь ласка, вкажіть "
+"<першоджерельне-сховище> вручну.\n"
+
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "не вдалося отримати інформацію про обʼєкт \"%s\""
+
+#, c-format
+msgid "bad ls-files format: element '%s' does not start with '('"
+msgstr "невірний ls-files формат: елемент \"%s\" не починається з \"(\""
+
+#, c-format
+msgid "bad ls-files format: element '%s' does not end in ')'"
+msgstr "невірний ls-files формат: елемент \"%s\" не закінчується на \")\""
+
+#, c-format
+msgid "bad ls-files format: %%%.*s"
+msgstr "невірний ls-files формат: %%%.*s"
+
+msgid "git ls-files [<options>] [<file>...]"
+msgstr "git ls-files [<опції>] [<файл>...]"
+
+msgid "separate paths with the NUL character"
+msgstr "відокремити шляхи символом NUL"
+
+msgid "identify the file status with tags"
+msgstr "визначити стан файлу за допомогою тегів"
+
+msgid "use lowercase letters for 'assume unchanged' files"
+msgstr "використати малі літери для \"вважати незмінними\" файлів"
+
+msgid "use lowercase letters for 'fsmonitor clean' files"
+msgstr "використати малі літери для \"fsmonitor clean\" файлів"
+
+msgid "show cached files in the output (default)"
+msgstr "показати кешовані файли у виводі (за замовчуванням)"
+
+msgid "show deleted files in the output"
+msgstr "показати видалені файли у виводі"
+
+msgid "show modified files in the output"
+msgstr "показати змінені файли у виводі"
+
+msgid "show other files in the output"
+msgstr "показати інші файли у виводі"
+
+msgid "show ignored files in the output"
+msgstr "показати ігноровані файли у виводі"
+
+msgid "show staged contents' object name in the output"
+msgstr "показати назву обʼєкта індексованого вмісту у виводі"
+
+msgid "show files on the filesystem that need to be removed"
+msgstr "показати файли файлової системи, які потрібно видалити"
+
+msgid "show 'other' directories' names only"
+msgstr "показати тільки назви \"інших\" директорій"
+
+msgid "show line endings of files"
+msgstr "показати закінчення рядків файлів"
+
+msgid "don't show empty directories"
+msgstr "не показувати порожні директорії"
+
+msgid "show unmerged files in the output"
+msgstr "показати не злиті файли у виводі"
+
+msgid "show resolve-undo information"
+msgstr "показати resolve-undo інформацію"
+
+msgid "skip files matching pattern"
+msgstr "пропустити файли, які відповідають шаблону"
+
+msgid "read exclude patterns from <file>"
+msgstr "читати шаблони виключення з <файлу>"
+
+msgid "read additional per-directory exclude patterns in <file>"
+msgstr "читати додаткові шаблони виключення для кожної директорії з <файлу>"
+
+msgid "add the standard git exclusions"
+msgstr "додати стандартні git виключення"
+
+msgid "make the output relative to the project top directory"
+msgstr "зробити виведення відносно верхньої директорії проекту"
+
+msgid "if any <file> is not in the index, treat this as an error"
+msgstr "якщо якогось <файлу> немає в індексі, вважати це помилкою"
+
+msgid "tree-ish"
+msgstr "деревоподібне-джерело"
+
+msgid "pretend that paths removed since <tree-ish> are still present"
+msgstr ""
+"вдавати, що шляхи, видалені після <деревоподібного-джерела>, все ще присутні"
+
+msgid "show debugging data"
+msgstr "показати дані відлагодження"
+
+msgid "suppress duplicate entries"
+msgstr "не показувати дублікати записів"
+
+msgid "show sparse directories in the presence of a sparse index"
+msgstr "показувати розріджені директорії при наявності розрідженого індексу"
+
+msgid ""
+"--format cannot be used with -s, -o, -k, -t, --resolve-undo, --deduplicate, "
+"--eol"
+msgstr ""
+"--format не можна використовувати з -s, -o, -k, -t, --resolve-undo, --"
+"deduplicate, --eol"
+
+msgid ""
+"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
+msgstr ""
+"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<виконавчий-файл>]\n"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<ключ>]\n"
+"              [--symref] [<сховище> [<шаблони>...]]"
+
+msgid "do not print remote URL"
+msgstr "не виводити віддалену URL-адресу"
+
+msgid "exec"
+msgstr "виконавчий файл"
+
+msgid "path of git-upload-pack on the remote host"
+msgstr "шлях до git-upload-pack на віддаленому сервері"
+
+msgid "limit to tags"
+msgstr "обмежити до тегів"
+
+msgid "limit to heads"
+msgstr "обмежити до голів"
+
+msgid "do not show peeled tags"
+msgstr "не показувати очищені теги"
+
+msgid "take url.<base>.insteadOf into account"
+msgstr "враховувати url.<база>.insteadOf"
+
+msgid "exit with exit code 2 if no matching refs are found"
+msgstr "вийти з кодом виходу 2, якщо не знайдено відповідних посилань"
+
+msgid "show underlying ref in addition to the object pointed by it"
+msgstr "показувати базове посилання на додаток до об’єкта, на який воно вказує"
+
+msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
+msgstr "git ls-tree [<опції>] <деревоподібне-джерело> [<шлях>...]"
+
+#, c-format
+msgid "bad ls-tree format: element '%s' does not start with '('"
+msgstr "невірний формат ls-tree: елемент \"%s\" не починається з \"(\""
+
+#, c-format
+msgid "bad ls-tree format: element '%s' does not end in ')'"
+msgstr "невірний формат ls-tree: елемент \"%s\" не закінчується на \")\""
+
+#, c-format
+msgid "bad ls-tree format: %%%.*s"
+msgstr "невірний формат ls-tree: %%%.*s"
+
+msgid "only show trees"
+msgstr "показувати тільки дерева"
+
+msgid "recurse into subtrees"
+msgstr "рекурсивно в піддеревах"
+
+msgid "show trees when recursing"
+msgstr "відображати дерева при рекурсії"
+
+msgid "terminate entries with NUL byte"
+msgstr "завершувати записи байтом NUL"
+
+msgid "include object size"
+msgstr "включити розмір об’єкта"
+
+msgid "list only filenames"
+msgstr "показувати лише назви файлів"
+
+msgid "list only objects"
+msgstr "показати лише обʼєкти"
+
+msgid "use full path names"
+msgstr "використовувати повні назви шляхів"
+
+msgid "list entire tree; not just current directory (implies --full-name)"
+msgstr ""
+"показувати все дерево, а не лише поточну директорію (мається на увазі --full-"
+"name)."
+
+msgid "--format can't be combined with other format-altering options"
+msgstr "--format не можна комбінувати з іншими опціями зміни формату"
+
+#. TRANSLATORS: keep <> in "<" mail ">" info.
+msgid "git mailinfo [<options>] <msg> <patch> < mail >info"
+msgstr "git mailinfo [<опції>] <допис> <латка> < mail >info"
+
+msgid "keep subject"
+msgstr "не змінювати тему"
+
+msgid "keep non patch brackets in subject"
+msgstr "зберігати дужки, що не стосуються латок, в темі"
+
+msgid "copy Message-ID to the end of commit message"
+msgstr "копіювати Message-ID в кінець допису до коміту"
+
+msgid "re-code metadata to i18n.commitEncoding"
+msgstr "перекодувати метадані в i18n.commitEncoding"
+
+msgid "disable charset re-coding of metadata"
+msgstr "вимкнути перекодування метаданих"
+
+msgid "encoding"
+msgstr "кодування"
+
+msgid "re-code metadata to this encoding"
+msgstr "перекодувати метадані в це кодування"
+
+msgid "use scissors"
+msgstr "використовувати ножиці"
+
+msgid "<action>"
+msgstr "<дія>"
+
+msgid "action when quoted CR is found"
+msgstr "дія при знаходженні цитованого CR"
+
+msgid "use headers in message's body"
+msgstr "використовувати заголовки в тілі повідомлення"
+
+msgid "reading patches from stdin/tty..."
+msgstr "читання латок з stdin/tty..."
+
+#, c-format
+msgid "empty mbox: '%s'"
+msgstr "порожній mbox: \"%s\""
+
+msgid "git merge-base [-a | --all] <commit> <commit>..."
+msgstr "git merge-base [-a | --all] <коміт> <коміт>..."
+
+msgid "git merge-base [-a | --all] --octopus <commit>..."
+msgstr "git merge-base [-a | --all] --octopus <коміт>..."
+
+msgid "git merge-base --is-ancestor <commit> <commit>"
+msgstr "git merge-base --is-ancestor <коміт> <коміт>"
+
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <коміт>..."
+
+msgid "git merge-base --fork-point <ref> [<commit>]"
+msgstr "git merge-base --fork-point <посилання> [<коміт>]"
+
+msgid "output all common ancestors"
+msgstr "вивести всіх спільних предків"
+
+msgid "find ancestors for a single n-way merge"
+msgstr "знайти предків для одного n-стороннього злиття"
+
+msgid "list revs not reachable from others"
+msgstr "показати ревізії, недоступні з інших джерел"
+
+msgid "is the first one ancestor of the other?"
+msgstr "чи є перша з них предком другої?"
+
+msgid "find where <commit> forked from reflog of <ref>"
+msgstr "знайти, де <коміт> відгалузився від журналу посилань <посилання>"
+
+msgid ""
+"git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> "
+"<orig-file> <file2>"
+msgstr ""
+"git merge-file [<опції>] [-L <назва1> [-L <оріг> [-L <назва2>]]] <файл1> "
+"<оріг-файл> <файл2>"
+
+msgid "send results to standard output"
+msgstr "надсилати результати до стандартного виводу"
+
+msgid "use object IDs instead of filenames"
+msgstr "використовувати ID обʼєктів замість назв файлів"
+
+msgid "use a diff3 based merge"
+msgstr "використовувати злиття на основі diff3"
+
+msgid "use a zealous diff3 based merge"
+msgstr "використовувати ретельне злиття на основі diff3"
+
+msgid "for conflicts, use our version"
+msgstr "у разі конфліктів використовувати нашу версію"
+
+msgid "for conflicts, use their version"
+msgstr "у разі конфліктів використовувати їхню версію"
+
+msgid "for conflicts, use a union version"
+msgstr "у разі конфліктів використовувати об’єднану версію"
+
+msgid "for conflicts, use this marker size"
+msgstr "у разі конфліктів використовувати цей розмір маркера"
+
+msgid "do not warn about conflicts"
+msgstr "не попереджати про конфлікти"
+
+msgid "set labels for file1/orig-file/file2"
+msgstr "встановити мітки для файл1/оріг-файл/файл2"
+
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "обʼєкт \"%s\" не існує"
+
+msgid "Could not write object file"
+msgstr "Не вдалося записати файл обʼєкта"
+
+#, c-format
+msgid "unknown option %s"
+msgstr "невідома опція %s"
+
+#, c-format
+msgid "could not parse object '%s'"
+msgstr "не вдалося розібрати обʼєкт \"%s\""
+
+#, c-format
+msgid "cannot handle more than %d base. Ignoring %s."
+msgid_plural "cannot handle more than %d bases. Ignoring %s."
+msgstr[0] "неможливо обробити більш ніж %d базу. Ігнорування %s."
+msgstr[1] "неможливо обробити більш ніж %d бази. Ігнорування %s."
+msgstr[2] "неможливо обробити більш ніж %d баз. Ігнорування %s."
+
+msgid "not handling anything other than two heads merge."
+msgstr "не оброблюється нічого, окрім злиття двох верхівок."
+
+#, c-format
+msgid "could not resolve ref '%s'"
+msgstr "не вдалося розвʼязати посилання \"%s\""
+
+#, c-format
+msgid "Merging %s with %s\n"
+msgstr "Злиття %s з %s\n"
+
+msgid "not something we can merge"
+msgstr "не те, що ми в змозі об’єднати"
+
+msgid "refusing to merge unrelated histories"
+msgstr "відмовлено в об’єднанні непов’язаних історій"
+
+msgid "failure to merge"
+msgstr "не вдалося злити"
+
+msgid "git merge-tree [--write-tree] [<options>] <branch1> <branch2>"
+msgstr "git merge-tree [--write-tree] [<опції>] <гілка1> <гілка2>"
+
+msgid "git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"
+msgstr "git merge-tree [--trivial-merge] <базове-дерево> <гілка1> <гілка2>"
+
+msgid "do a real merge instead of a trivial merge"
+msgstr "зробити справжнє злиття замість тривіального злиття"
+
+msgid "do a trivial merge only"
+msgstr "зробити лише тривіальне злиття"
+
+msgid "also show informational/conflict messages"
+msgstr "також показувати інформаційні/конфліктні повідомлення"
+
+msgid "list filenames without modes/oids/stages"
+msgstr "вивести назви файлів без режимів/oid/стадій"
+
+msgid "allow merging unrelated histories"
+msgstr "дозволити злиття непов’язаних історій"
+
+msgid "perform multiple merges, one per line of input"
+msgstr "виконати кілька злиттів, по одному на кожен рядок вводу"
+
+msgid "specify a merge-base for the merge"
+msgstr "вказати базу для злиття"
+
+msgid "option=value"
+msgstr "опція=значення"
+
+msgid "option for selected merge strategy"
+msgstr "опція для обраної стратегії злиття"
+
+msgid "--trivial-merge is incompatible with all other options"
+msgstr "--trivial-merge несумісна з усіма іншими опціями"
+
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "невідомий варіант стратегії: -X%s"
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base несумісна з --stdin"
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "невірно сформований рядок вводу: \"%s\"."
+
+#, c-format
+msgid "merging cannot continue; got unclean result of %d"
+msgstr "неможливо продовжити злиття; отримано брудний результат для %d"
+
+msgid "git merge [<options>] [<commit>...]"
+msgstr "git merge [<опції>] [<коміт>...]"
+
+msgid "switch `m' requires a value"
+msgstr "перемикач \"m\" потребує значення"
+
+#, c-format
+msgid "option `%s' requires a value"
+msgstr "опція \"%s\" потребує значення"
+
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr "Не вдалося знайти стратегію злиття \"%s\".\n"
+
+#, c-format
+msgid "Available strategies are:"
+msgstr "Доступні стратегії:"
+
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "Доступні спеціальні стратегії:"
+
+msgid "do not show a diffstat at the end of the merge"
+msgstr "не показувати diffstat наприкінці злиття"
+
+msgid "show a diffstat at the end of the merge"
+msgstr "показувати diffstat наприкінці злиття"
+
+msgid "(synonym to --stat)"
+msgstr "(синонім до --stat)"
+
+msgid "add (at most <n>) entries from shortlog to merge commit message"
+msgstr ""
+"додати (не більше <н>) записів з короткого журналу у допис до коміту злиття"
+
+msgid "create a single commit instead of doing a merge"
+msgstr "створити єдиний коміт замість злиття"
+
+msgid "perform a commit if the merge succeeds (default)"
+msgstr "виконати коміт, якщо злиття пройшло успішно (за замовчуванням)"
+
+msgid "edit message before committing"
+msgstr "редагувати допис перед комітом"
+
+msgid "allow fast-forward (default)"
+msgstr "дозволити перемотування вперед (за замовчуванням)"
+
+msgid "abort if fast-forward is not possible"
+msgstr "перервати, якщо перемотування вперед неможливе"
+
+msgid "verify that the named commit has a valid GPG signature"
+msgstr "перевіряти, чи має коміт дійсний GPG-підпис"
+
+msgid "strategy"
+msgstr "стратегія"
+
+msgid "merge strategy to use"
+msgstr "яку стратегію злиття використовувати"
+
+msgid "merge commit message (for a non-fast-forward merge)"
+msgstr "допис до коміту злиття (для злиття без перемотування вперед)"
+
+msgid "use <name> instead of the real target"
+msgstr "використовувати <назву> замість реальної цілі"
+
+msgid "abort the current in-progress merge"
+msgstr "перервати поточне злиття"
+
+msgid "--abort but leave index and working tree alone"
+msgstr "--abort, але зберегти стан індексу і робочого дерева"
+
+msgid "continue the current in-progress merge"
+msgstr "продовжити поточний процес злиття"
+
+msgid "bypass pre-merge-commit and commit-msg hooks"
+msgstr "обходити pre-merge-commit та commit-msg гачки"
+
+msgid "could not run stash."
+msgstr "не вдалося виконати stash."
+
+msgid "stash failed"
+msgstr "не вдалося додати до схову"
+
+#, c-format
+msgid "not a valid object: %s"
+msgstr "не є припустимим обʼєктом: %s"
+
+msgid "read-tree failed"
+msgstr "read-tree завершився невдало"
+
+msgid "Already up to date. (nothing to squash)"
+msgstr "Вже в актуальному стані. (нічого зчавлювати)"
+
+msgid "Already up to date."
+msgstr "Вже в актуальному стані."
+
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr "Коміт зчавлювання -- HEAD не оновлюється\n"
+
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr "Немає допису до злиття -- HEAD не оновлюється\n"
+
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr "\"%s\" не вказує на коміт"
+
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr "Невірна branch.%s.mergeoptions строка: %s"
+
+msgid "Unable to write index."
+msgstr "Не вдалося записати індекс."
+
+msgid "Not handling anything other than two heads merge."
+msgstr "Не оброблюється нічого, окрім злиття двох верхівок."
+
+#, c-format
+msgid "unable to write %s"
+msgstr "не вдалося записати %s"
+
+#, c-format
+msgid "Could not read from '%s'"
+msgstr "Не вдалося прочитати з \"%s\""
+
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+"Не додано коміт злиття; скористайтесь \"git commit\", щоб завершити злиття.\n"
+
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту, щоб пояснити, чому це злиття є "
+"необхідним,\n"
+"особливо якщо воно об’єднує першоджерельні оновлення з тематичною гілкою.\n"
+
+msgid "An empty message aborts the commit.\n"
+msgstr "Порожній допис перерве процес коміту.\n"
+
+#, c-format
+msgid ""
+"Lines starting with '%c' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+"Рядки, що починаються з \"%c\", будуть проігноровані, а порожній допис "
+"перерве\n"
+"процес коміту.\n"
+
+msgid "Empty commit message."
+msgstr "Порожній допис до коміту."
+
+#, c-format
+msgid "Wonderful.\n"
+msgstr "Чудово.\n"
+
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+"Автоматичне злиття не вдалося; виправте конфлікти і потім зробіть коміт "
+"результату.\n"
+
+msgid "No current branch."
+msgstr "Немає поточної гілки."
+
+msgid "No remote for the current branch."
+msgstr "Немає віддаленого призначення для поточної гілки."
+
+msgid "No default upstream defined for the current branch."
+msgstr ""
+"Для поточної гілки не визначено першоджерельне сховище за замовчуванням."
+
+#, c-format
+msgid "No remote-tracking branch for %s from %s"
+msgstr "Немає віддалено відстежуваної гілки для %s з %s"
+
+#, c-format
+msgid "Bad value '%s' in environment '%s'"
+msgstr "Невірне значення \"%s\" в оточенні \"%s\""
+
+#, c-format
+msgid "could not close '%s'"
+msgstr "не вдалося закрити \"%s\""
+
+#, c-format
+msgid "not something we can merge in %s: %s"
+msgstr "не те, що можна злити в %s: %s"
+
+msgid "--abort expects no arguments"
+msgstr "--abort не очікує жодних аргументів"
+
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr "Неможливо перервати злиття (відсутній MERGE_HEAD)."
+
+msgid "--quit expects no arguments"
+msgstr "--quit не очікує жодних аргументів"
+
+msgid "--continue expects no arguments"
+msgstr "--continue не очікує жодних аргументів"
+
+msgid "There is no merge in progress (MERGE_HEAD missing)."
+msgstr "Злиття не виконується (відсутній MERGE_HEAD)."
+
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you merge."
+msgstr ""
+"Ви не завершили злиття (існує MERGE_HEAD).\n"
+"Будь ласка, зробіть коміт для ваших змін перед злиттям."
+
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you merge."
+msgstr ""
+"Ви не завершили висмикування (існує CHERRY_PICK_HEAD).\n"
+"Будь ласка, зробіть коміт для ваших змін перед злиттям."
+
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr "Ви не завершили висмикування (існує CHERRY_PICK_HEAD)."
+
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr "Не вказано коміт і не встановлено merge.defaultToUpstream."
+
+msgid "Squash commit into empty head not supported yet"
+msgstr "Поки що немає підтримки комітів зчавлювання в порожню верхівку"
+
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr "Коміт без перемотування вперед у порожню верхівку не має сенсу"
+
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - не те, що можна злити"
+
+msgid "Can merge only exactly one commit into empty head"
+msgstr "Можна злити лишень один коміт у порожню верхівку"
+
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "Оновлення %s..%s\n"
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by merge:\n"
+"  %s"
+msgstr ""
+"Ваші локальні зміни у наступних файлах буде замінено злиттям:\n"
+"  %s"
+
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr "Спроба справді тривіального внутрішньо-індексного злиття...\n"
+
+#, c-format
+msgid "Nope.\n"
+msgstr "Ні.\n"
+
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr "Приведення дерева до початкового стану...\n"
+
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr "Спроба стратегії злиття %s...\n"
+
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr "Жодна стратегія злиття не впоралася зі злиттям.\n"
+
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "Злиття зі стратегією %s не вдалося.\n"
+
+#, c-format
+msgid "Using the %s strategy to prepare resolving by hand.\n"
+msgstr "Використання стратегії %s для підготовки вирішення вручну.\n"
+
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+"Автоматичне злиття пройшло добре; зупинка перед комітом, як було вказано\n"
+
+#, c-format
+msgid "When finished, apply stashed changes with `git stash pop`\n"
+msgstr ""
+"Закінчивши, застосуйте збережені зміни за допомогою \"git stash pop\"\n"
+
+#, c-format
+msgid "warning: tag input does not pass fsck: %s"
+msgstr "попередження: вхідний тег не пройшов fsck: %s"
+
+#, c-format
+msgid "error: tag input does not pass fsck: %s"
+msgstr "помилка: вхідний тег не пройшов fsck: %s"
+
+#, c-format
+msgid "%d (FSCK_IGNORE?) should never trigger this callback"
+msgstr "%d (FSCK_IGNORE?) ніколи не мав спричинити цей зворотній виклик"
+
+#, c-format
+msgid "could not read tagged object '%s'"
+msgstr "не вдалося прочитати тегований об’єкт \"%s\""
+
+#, c-format
+msgid "object '%s' tagged as '%s', but is a '%s' type"
+msgstr "об’єкт \"%s\", позначений як \"%s\", але має тип \"%s\""
+
+msgid "could not read from stdin"
+msgstr "не вдалося прочитати з stdin"
+
+msgid "tag on stdin did not pass our strict fsck check"
+msgstr "тег з stdin не пройшов нашу сувору перевірку fsck"
+
+msgid "tag on stdin did not refer to a valid object"
+msgstr "тег з stdin не посилався на дійсний об’єкт"
+
+msgid "unable to write tag file"
+msgstr "не вдалося записати файл тегів"
+
+msgid "input is NUL terminated"
+msgstr "ввід завершено символом NUL"
+
+msgid "allow missing objects"
+msgstr "дозволяти відсутні об’єкти"
+
+msgid "allow creation of more than one tree"
+msgstr "дозволяти створення більш ніж одного дерева"
+
+msgid ""
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
+"snapshot=<path>]"
+msgstr ""
+"git multi-pack-index [<опції>] write [--preferred-pack=<пакунок>] [--refs-"
+"snapshot=<шлях>]"
+
+msgid "git multi-pack-index [<options>] verify"
+msgstr "git multi-pack-index [<опції>] verify"
+
+msgid "git multi-pack-index [<options>] expire"
+msgstr "git multi-pack-index [<опції>] expire"
+
+msgid "git multi-pack-index [<options>] repack [--batch-size=<size>]"
+msgstr "git multi-pack-index [<опції>] repack [--batch-size=<розмір>]"
+
+msgid "directory"
+msgstr "директорія"
+
+msgid "object directory containing set of packfile and pack-index pairs"
+msgstr "директорія об’єктів, що містить набір packfile та pack-index пар"
+
+msgid "preferred-pack"
+msgstr "preferred-pack"
+
+msgid "pack for reuse when computing a multi-pack bitmap"
+msgstr ""
+"пакунок для повторного використання під час обчислення multi-pack bitmap"
+
+msgid "write multi-pack bitmap"
+msgstr "записати multi-pack bitmap"
+
+msgid "write multi-pack index containing only given indexes"
+msgstr "записати multi-pack індекс, що містить лише задані індекси"
+
+msgid "refs snapshot for selecting bitmap commits"
+msgstr "знімок посилань для вибору bitmap комітів"
+
+msgid ""
+"during repack, collect pack-files of smaller size into a batch that is "
+"larger than this size"
+msgstr ""
+"під час перепакування збирати пакувальні файли меншого розміру в партію, "
+"більшу за цей розмір"
+
+msgid "git mv [<options>] <source>... <destination>"
+msgstr "git mv [<опції>] <джерело>... <призначення>"
+
+#, c-format
+msgid "Directory %s is in index and no submodule?"
+msgstr "Директорія %s в індексі, але без підмодуля?"
+
+msgid "Please stage your changes to .gitmodules or stash them to proceed"
+msgstr ""
+"Будь ласка, додайте змінений .gitmodules до індексу або до схову, щоб "
+"продовжити"
+
+#, c-format
+msgid "%.*s is in index"
+msgstr "%.*s в індексі"
+
+msgid "force move/rename even if target exists"
+msgstr "примусово переміщувати/перейменовувати, навіть якщо ціль існує"
+
+msgid "skip move/rename errors"
+msgstr "пропускати помилки переміщення/перейменування"
+
+#, c-format
+msgid "destination '%s' is not a directory"
+msgstr "місце призначення \"%s\" не є директорією"
+
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr "Перевірка перейменування \"%s\" на \"%s\"\n"
+
+msgid "bad source"
+msgstr "невірне джерело"
+
+msgid "destination exists"
+msgstr "призначення існує"
+
+msgid "can not move directory into itself"
+msgstr "неможливо перемістити директорію в саму себе"
+
+msgid "destination already exists"
+msgstr "призначення вже існує"
+
+msgid "source directory is empty"
+msgstr "директорія джерела порожня"
+
+msgid "not under version control"
+msgstr "не під контролем версій"
+
+msgid "conflicted"
+msgstr "конфлікт"
+
+#, c-format
+msgid "overwriting '%s'"
+msgstr "перезапис \"%s\""
+
+msgid "Cannot overwrite"
+msgstr "Неможливо перезаписати"
+
+msgid "multiple sources for the same target"
+msgstr "кілька джерел для однієї цілі"
+
+msgid "destination directory does not exist"
+msgstr "директорія призначення не існує"
+
+msgid "destination exists in the index"
+msgstr "призначення існує в індексі"
+
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s, джерело=%s, призначення=%s"
+
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "Перейменування %s на %s\n"
+
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "перейменування \"%s\" завершилося невдало"
+
+msgid "git name-rev [<options>] <commit>..."
+msgstr "git name-rev [<опції>] <коміт>..."
+
+msgid "git name-rev [<options>] --all"
+msgstr "git name-rev [<опції>] --all"
+
+msgid "git name-rev [<options>] --annotate-stdin"
+msgstr "git name-rev [<опції>] --annotate-stdin"
+
+msgid "print only ref-based names (no object names)"
+msgstr "виводити тільки назви на основі посилань (без назв об’єктів)"
+
+msgid "only use tags to name the commits"
+msgstr "використовувати теги лише для назв комітів"
+
+msgid "only use refs matching <pattern>"
+msgstr "використовувати лише посилання, що збігаються з <шаблоном>"
+
+msgid "ignore refs matching <pattern>"
+msgstr "ігнорувати посилання, що збігаються з <шаблоном>"
+
+msgid "list all commits reachable from all refs"
+msgstr "вивести всі коміти, доступні з усіх посилань"
+
+msgid "deprecated: use --annotate-stdin instead"
+msgstr "застаріле: замість цього використовуйте --annotate-stdin"
+
+msgid "annotate text from stdin"
+msgstr "анотувати текст зі stdin"
+
+msgid "allow to print `undefined` names (default)"
+msgstr "дозволити виводити \"невизначені\" назви (за замовчуванням)"
+
+msgid "dereference tags in the input (internal use)"
+msgstr "розіменувати теги на вході (внутрішнє використання)"
+
+msgid "git notes [--ref <notes-ref>] [list [<object>]]"
+msgstr "git notes [--ref <посилання-нотатки>] [list [<об’єкт>]]"
+
+msgid ""
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
+msgstr ""
+"git notes [--ref <посилання-нотатки>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<розділювач-абзаців>] [--[no-]stripspace] [-m "
+"<допис> | -F <файл> | (-c | -C) <обʼєкт>] [<обʼєкт>]"
+
+msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
+msgstr ""
+"git notes [--ref <посилання-нотатки>] copy [-f] <з-об’єкта> <до-об’єкта>"
+
+msgid ""
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
+msgstr ""
+"git notes [--ref <посилання-нотатки>] append [--allow-empty] [--"
+"[no-]separator|--separator=<розділювач-абзаців>] [--[no-]stripspace] [-m "
+"<допис> | -F <файл> | (-c | -C) <обʼєкт>] [<обʼєкт>]"
+
+msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
+msgstr "git notes [--ref <посилання-нотатки>] edit [--allow-empty] [<об’єкт>]"
+
+msgid "git notes [--ref <notes-ref>] show [<object>]"
+msgstr "git notes [--ref <посилання-нотатки>] show [<об’єкт>]"
+
+msgid ""
+"git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"
+msgstr ""
+"git notes [--ref <посилання-нотатки>] merge [-v | -q] [-s <стратегія>] "
+"<посилання-нотатки>"
+
+msgid "git notes [--ref <notes-ref>] remove [<object>...]"
+msgstr "git notes [--ref <посилання-нотатки>] remove [<об’єкт>...]"
+
+msgid "git notes [--ref <notes-ref>] prune [-n] [-v]"
+msgstr "git notes [--ref <посилання-нотатки>] prune [-n] [-v]."
+
+msgid "git notes [--ref <notes-ref>] get-ref"
+msgstr "git notes [--ref <посилання-нотатки>] get-ref"
+
+msgid "git notes [list [<object>]]"
+msgstr "git notes [list [<об’єкт>]]"
+
+msgid "git notes add [<options>] [<object>]"
+msgstr "git notes add [<опції>] [<обʼєкт>]"
+
+msgid "git notes copy [<options>] <from-object> <to-object>"
+msgstr "git notes copy [<опції>] <з-об’єкта> <до-об’єкта"
+
+msgid "git notes copy --stdin [<from-object> <to-object>]..."
+msgstr "git notes copy --stdin [<з-об’єкта> <до-об’єкта>]..."
+
+msgid "git notes append [<options>] [<object>]"
+msgstr "git notes append [<опції>] [<обʼєкт>]"
+
+msgid "git notes edit [<object>]"
+msgstr "git notes edit [<об’єкт>]"
+
+msgid "git notes show [<object>]"
+msgstr "git notes show [<об’єкт>]"
+
+msgid "git notes merge [<options>] <notes-ref>"
+msgstr "git notes merge [<опції>] <посилання-нотатки>"
+
+msgid "git notes merge --commit [<options>]"
+msgstr "git notes merge --commit [<опції>]"
+
+msgid "git notes merge --abort [<options>]"
+msgstr "git notes merge --abort [<опції>]"
+
+msgid "git notes remove [<object>]"
+msgstr "git notes remove [<об’єкт>]"
+
+msgid "git notes prune [<options>]"
+msgstr "git notes prune [<опції>]"
+
+msgid "Write/edit the notes for the following object:"
+msgstr "Записати/відредагувати нотатки для наступного обʼєкта:"
+
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr "не вдалося запустити \"show\" для об’єкта \"%s\""
+
+msgid "could not read 'show' output"
+msgstr "не вдалося прочитати вивід \"show\""
+
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr "не вдалося завершити \"show\" для об’єкта \"%s\""
+
+msgid "please supply the note contents using either -m or -F option"
+msgstr "будь ласка, надайте зміст нотатки, використовуючи опцію -m або -F"
+
+msgid "unable to write note object"
+msgstr "не вдалося записати об’єкт нотатки"
+
+#, c-format
+msgid "the note contents have been left in %s"
+msgstr "вміст нотатки залишено в %s"
+
+#, c-format
+msgid "could not open or read '%s'"
+msgstr "не вдалося відкрити або прочитати \"%s\""
+
+#, c-format
+msgid "failed to resolve '%s' as a valid ref."
+msgstr "не вдалося розпізнати \"%s\" як припустиме посилання."
+
+#, c-format
+msgid "failed to read object '%s'."
+msgstr "не вдалося прочитати обʼєкт \"%s\"."
+
+#, c-format
+msgid "cannot read note data from non-blob object '%s'."
+msgstr "неможливо прочитати дані нотатки з не-blob обʼєкту \"%s\"."
+
+#, c-format
+msgid "failed to copy notes from '%s' to '%s'"
+msgstr "не вдалося скопіювати нотатки з \"%s\" в \"%s\""
+
+#. TRANSLATORS: the first %s will be replaced by a git
+#. notes command: 'add', 'merge', 'remove', etc.
+#.
+#, c-format
+msgid "refusing to %s notes in %s (outside of refs/notes/)"
+msgstr "відмовлено в %s нотаток у %s (за межами refs/notes/)"
+
+#, c-format
+msgid "no note found for object %s."
+msgstr "для обʼєкта %s не знайдено жодної нотатки."
+
+msgid "note contents as a string"
+msgstr "вміст нотатки як строка"
+
+msgid "note contents in a file"
+msgstr "вміст нотатки у файлі"
+
+msgid "reuse and edit specified note object"
+msgstr "повторно використати та редагувати вказаний обʼєкт нотатки"
+
+msgid "reuse specified note object"
+msgstr "повторно використати вказаний обʼєкт нотатки"
+
+msgid "allow storing empty note"
+msgstr "дозволити збереження порожньої нотатки"
+
+msgid "replace existing notes"
+msgstr "замінити існуючі нотатки"
+
+msgid "<paragraph-break>"
+msgstr "<розділювач-абзаців>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "вставити <розділювач-абзаців> між абзацами"
+
+msgid "remove unnecessary whitespace"
+msgstr "видалити зайві пробіли"
+
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Неможливо додати нотатки. Знайдено існуючі нотатки для обʼєкта %s. "
+"Скористайтесь \"-f\" для перезапису існуючих нотаток"
+
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr "Перезапис існуючих нотаток для обʼєкта %s\n"
+
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr "Видалення нотатки для обʼєкта %s\n"
+
+msgid "read objects from stdin"
+msgstr "зчитати обʼєкти з stdin"
+
+msgid "load rewriting config for <command> (implies --stdin)"
+msgstr ""
+"завантажити перезапис конфігурації для <команда> (мається на увазі --stdin)"
+
+msgid "too few arguments"
+msgstr "замало аргументів"
+
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Неможливо скопіювати нотатки. Знайдено існуючі нотатки для обʼєкта %s. "
+"Скористайтесь \"-f\", щоб перезаписати існуючі нотатки"
+
+#, c-format
+msgid "missing notes on source object %s. Cannot copy."
+msgstr "нотатки джерельного обʼєкта %s відсутні. Неможливо скопіювати."
+
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+"Опції -m/-F/-c/-C застаріли для підкоманди \"edit\".\n"
+"Будь ласка, скористайтесь \"git notes add -f -m/-F/-c/-C\" замість цього.\n"
+
+msgid "failed to delete ref NOTES_MERGE_PARTIAL"
+msgstr "не вдалося видалити посилання NOTES_MERGE_PARTIAL"
+
+msgid "failed to delete ref NOTES_MERGE_REF"
+msgstr "не вдалося видалити посилання NOTES_MERGE_REF"
+
+msgid "failed to remove 'git notes merge' worktree"
+msgstr "не вдалося видалити \"git notes merge\" робоче дерево"
+
+msgid "failed to read ref NOTES_MERGE_PARTIAL"
+msgstr "не вдалося прочитати посилання NOTES_MERGE_PARTIAL"
+
+msgid "could not find commit from NOTES_MERGE_PARTIAL."
+msgstr "не вдалося знайти коміт з NOTES_MERGE_PARTIAL."
+
+msgid "could not parse commit from NOTES_MERGE_PARTIAL."
+msgstr "не вдалося розібрати коміт з NOTES_MERGE_PARTIAL."
+
+msgid "failed to resolve NOTES_MERGE_REF"
+msgstr "не вдалося розвʼязати NOTES_MERGE_REF"
+
+msgid "failed to finalize notes merge"
+msgstr "не вдалося завершити злиття нотаток"
+
+#, c-format
+msgid "unknown notes merge strategy %s"
+msgstr "невідома стратегія злиття нотаток %s"
+
+msgid "General options"
+msgstr "Основні опції"
+
+msgid "Merge options"
+msgstr "Опції злиття"
+
+msgid ""
+"resolve notes conflicts using the given strategy (manual/ours/theirs/union/"
+"cat_sort_uniq)"
+msgstr ""
+"вирішити конфлікти нотаток, використовуючи задану стратегію (manual/our/"
+"their/union/cat_sort_uniq)"
+
+msgid "Committing unmerged notes"
+msgstr "Виконнання коміту не злитих нотаток"
+
+msgid "finalize notes merge by committing unmerged notes"
+msgstr "завершити злиття комітом не злитих нотаток"
+
+msgid "Aborting notes merge resolution"
+msgstr "Переривання злиття нотаток"
+
+msgid "abort notes merge"
+msgstr "перервати злиття нотаток"
+
+msgid "cannot mix --commit, --abort or -s/--strategy"
+msgstr "не можна змішувати --commit, --abort або -s/--strategy"
+
+msgid "must specify a notes ref to merge"
+msgstr "необхідно вказати посилання нотаток для злиття"
+
+#, c-format
+msgid "unknown -s/--strategy: %s"
+msgstr "невідома -s/--strategy: %s"
+
+#, c-format
+msgid "a notes merge into %s is already in-progress at %s"
+msgstr "злиття нотаток у %s вже виконується в %s"
+
+#, c-format
+msgid "failed to store link to current notes ref (%s)"
+msgstr "не вдалося зберегти ланку на поточні нотатки посилання (%s)"
+
+#, c-format
+msgid ""
+"Automatic notes merge failed. Fix conflicts in %s and commit the result with "
+"'git notes merge --commit', or abort the merge with 'git notes merge --"
+"abort'.\n"
+msgstr ""
+"Автоматичне злиття нотаток не вдалося. Виправте конфлікти у %s і зробіть "
+"коміт змін командою \"git notes merge --commit\" або перервіть злиття "
+"командою \"git notes merge --abort\".\n"
+
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr "Не вдалося розвʼязати \"%s\" як припустиме посилання."
+
+#, c-format
+msgid "Object %s has no note\n"
+msgstr "Обʼєкт %s не має нотатки\n"
+
+msgid "attempt to remove non-existent note is not an error"
+msgstr "спроба видалити неіснуючу нотатку не є помилкою"
+
+msgid "read object names from the standard input"
+msgstr "зчитати імена обʼєктів зі стандартного вводу"
+
+msgid "do not remove, show only"
+msgstr "не видаляти, тільки показувати"
+
+msgid "report pruned notes"
+msgstr "звітувати про видалені нотатки"
+
+msgid "notes-ref"
+msgstr "посилання-нотатки"
+
+msgid "use notes from <notes-ref>"
+msgstr "використовувати нотатки з <посилання-нотатки>"
+
+#, c-format
+msgid "unknown subcommand: `%s'"
+msgstr "невідома підкоманда: \"%s\""
+
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
+msgstr ""
+"git pack-objects --stdout [<опції>] [< <список-посилань> | < <список-"
+"обʼєктів>]"
+
+msgid ""
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
+msgstr ""
+"git pack-objects [<опції>] <базова-назва> [< <список-посилань> | < <список-"
+"обʼєктів>]"
+
+#, c-format
+msgid ""
+"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
+"pack %s"
+msgstr ""
+"write_reuse_object: не вдалося знайти %s, очікуваний зі зміщенням %<PRIuMAX> "
+"у пакунку %s"
+
+#, c-format
+msgid "bad packed object CRC for %s"
+msgstr "невірна CRC запакованого обʼєкта %s"
+
+#, c-format
+msgid "corrupt packed object for %s"
+msgstr "пошкоджений запакований обʼєкт для %s"
+
+#, c-format
+msgid "recursive delta detected for object %s"
+msgstr "виявлено рекурсивну дельту для обʼєкта %s"
+
+#, c-format
+msgid "ordered %u objects, expected %<PRIu32>"
+msgstr "замовлено %u обʼєктів, очікувалось %<PRIu32>"
+
+#, c-format
+msgid "expected object at offset %<PRIuMAX> in pack %s"
+msgstr "очікувався обʼєкт на зміщенні %<PRIuMAX> пакунка %s"
+
+msgid "disabling bitmap writing, packs are split due to pack.packSizeLimit"
+msgstr ""
+"вимкнення bitmap запису, пакунки розбиваються на частини через pack."
+"packSizeLimit"
+
+msgid "Writing objects"
+msgstr "Запис обʼєктів"
+
+#, c-format
+msgid "failed to stat %s"
+msgstr "не вдалося виконати stat %s"
+
+#, c-format
+msgid "failed utime() on %s"
+msgstr "не вдалося виконати utime() на %s"
+
+msgid "failed to write bitmap index"
+msgstr "не вдалося записати bitmap індекс"
+
+#, c-format
+msgid "wrote %<PRIu32> objects while expecting %<PRIu32>"
+msgstr "записано %<PRIu32> обʼєкти при очікуванні %<PRIu32>"
+
+msgid "disabling bitmap writing, as some objects are not being packed"
+msgstr "вимкнення bitmap запису, оскільки деякі обʼєкти не упаковуються"
+
+#, c-format
+msgid "delta base offset overflow in pack for %s"
+msgstr "переповнення зміщення дельти бази у пакунку для %s"
+
+#, c-format
+msgid "delta base offset out of bound for %s"
+msgstr "зміщення бази дельти виходить за межі для %s"
+
+msgid "Counting objects"
+msgstr "Підрахунок обʼєктів"
+
+#, c-format
+msgid "unable to get size of %s"
+msgstr "не вдалося отримати розмір %s"
+
+#, c-format
+msgid "unable to parse object header of %s"
+msgstr "не вдалося розібрати заголовок обʼєкта %s"
+
+#, c-format
+msgid "object %s cannot be read"
+msgstr "обʼєкт %s не може бути прочитаний"
+
+#, c-format
+msgid "object %s inconsistent object length (%<PRIuMAX> vs %<PRIuMAX>)"
+msgstr "обʼєкт %s має невідповідну довжину (%<PRIuMAX> проти %<PRIuMAX>)"
+
+msgid "suboptimal pack - out of memory"
+msgstr "неоптимальний пакунок - не вистачає памʼяті"
+
+#, c-format
+msgid "Delta compression using up to %d threads"
+msgstr "Дельта компресія з використанням до %d потоків"
+
+#, c-format
+msgid "unable to pack objects reachable from tag %s"
+msgstr "не вдалося запакувати обʼєкти, доступні з тегу %s"
+
+#, c-format
+msgid "unable to get type of object %s"
+msgstr "не вдалося отримати тип обʼєкта %s"
+
+msgid "Compressing objects"
+msgstr "Компресія обʼєктів"
+
+msgid "inconsistency with delta count"
+msgstr "неспівпадіння з підрахунком дельти"
+
+#, c-format
+msgid ""
+"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
+"hash> <uri>' (got '%s')"
+msgstr ""
+"значення uploadpack.blobpackfileuri повинно мати вигляд \"<хеш-обʼєкта> <хеш-"
+"пакунка> <uri>\" (отримано \"%s\")"
+
+#, c-format
+msgid ""
+"object already configured in another uploadpack.blobpackfileuri (got '%s')"
+msgstr ""
+"обʼєкт вже сконфігуровано в іншому uploadpack.blobpackfileuri (отримано "
+"\"%s\")"
+
+#, c-format
+msgid "could not get type of object %s in pack %s"
+msgstr "не вдалося отримати тип обʼєкта %s у пакунку %s"
+
+#, c-format
+msgid "could not find pack '%s'"
+msgstr "не вдалося знайти пакунок \"%s\""
+
+#, c-format
+msgid "packfile %s cannot be accessed"
+msgstr "неможливо отримати доступ до файлу пакунку %s"
+
+msgid "Enumerating cruft objects"
+msgstr "Перерахування марних обʼєктів"
+
+msgid "unable to add cruft objects"
+msgstr "не вдалося додати марні обʼєкти"
+
+msgid "Traversing cruft objects"
+msgstr "Проходження по марним обʼєктам"
+
+#, c-format
+msgid ""
+"expected edge object ID, got garbage:\n"
+" %s"
+msgstr ""
+"очікувався ідентифікатор реберного обʼєкту, отримано непотріб:\n"
+" %s"
+
+#, c-format
+msgid ""
+"expected object ID, got garbage:\n"
+" %s"
+msgstr ""
+"очікувався ідентифікатор обʼєкта, отримано непотріб:\n"
+" %s"
+
+msgid "could not load cruft pack .mtimes"
+msgstr "не вдалося завантажити марний пакунок .mtimes"
+
+msgid "cannot open pack index"
+msgstr "не вдалося відкрити індекс пакунка"
+
+#, c-format
+msgid "loose object at %s could not be examined"
+msgstr "не вдалося розглянути вільний обʼєкт у %s"
+
+msgid "unable to force loose object"
+msgstr "не вдалося примусово вивільнити обʼєкт"
+
+#, c-format
+msgid "not a rev '%s'"
+msgstr "не є ревізією \"%s\""
+
+#, c-format
+msgid "bad revision '%s'"
+msgstr "невірна ревізія \"%s\""
+
+msgid "unable to add recent objects"
+msgstr "не вдалося додати нещодавні обʼєкти"
+
+#, c-format
+msgid "unsupported index version %s"
+msgstr "непідтримувана версія індексу %s"
+
+#, c-format
+msgid "bad index version '%s'"
+msgstr "невірна версія індексу \"%s\""
+
+msgid "show progress meter during object writing phase"
+msgstr "показувати хід виконання під час фази запису обʼєкта"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "подібно до --all-progress, коли показано хід виконання"
+
+msgid "<version>[,<offset>]"
+msgstr "<версія>[, <зміщення>]"
+
+msgid "write the pack index file in the specified idx format version"
+msgstr "записати індексний файл пакунка у вказаній версії idx формату"
+
+msgid "maximum size of each output pack file"
+msgstr "максимальний розмір кожного файлу вихідного пакунку"
+
+msgid "ignore borrowed objects from alternate object store"
+msgstr "ігнорувати запозичені обʼєкти з місця збереження запозичених об’єктів"
+
+msgid "ignore packed objects"
+msgstr "ігнорувати запаковані обʼєкти"
+
+msgid "limit pack window by objects"
+msgstr "обмежити вікно пакування за обʼєктами"
+
+msgid "limit pack window by memory in addition to object limit"
+msgstr "обмежити вікно пакування за памʼяттю на додаток до ліміту обʼєкта"
+
+msgid "maximum length of delta chain allowed in the resulting pack"
+msgstr "максимальна довжина дельта ланцюжка, дозволена в результуючому пакунку"
+
+msgid "reuse existing deltas"
+msgstr "повторно використати існуючі дельти"
+
+msgid "reuse existing objects"
+msgstr "повторно використати існуючі обʼєкти"
+
+msgid "use OFS_DELTA objects"
+msgstr "використати OFS_DELTA обʼєкти"
+
+msgid "use threads when searching for best delta matches"
+msgstr "використовувати потоки під час пошуку найкращих дельта збігів"
+
+msgid "do not create an empty pack output"
+msgstr "не створювати вивід порожнього пакунка"
+
+msgid "read revision arguments from standard input"
+msgstr "зчитувати аргументи ревізії зі стандартного вводу"
+
+msgid "limit the objects to those that are not yet packed"
+msgstr "обмежувати обʼєкти тільки тими, які ще не запаковані"
+
+msgid "include objects reachable from any reference"
+msgstr "включати обʼєкти, досяжні з будь-якого посилання"
+
+msgid "include objects referred by reflog entries"
+msgstr "включати обʼєкти, на які посилаються записи журналу посилань"
+
+msgid "include objects referred to by the index"
+msgstr "включати обʼєкти, на які посилається індекс"
+
+msgid "read packs from stdin"
+msgstr "прочитати пакунки з stdin"
+
+msgid "output pack to stdout"
+msgstr "вивести пакунок у stdout"
+
+msgid "include tag objects that refer to objects to be packed"
+msgstr ""
+"включати обʼєкти тегів, які посилаються на обʼєкти, що будуть запаковані"
+
+msgid "keep unreachable objects"
+msgstr "зберігати недосяжні обʼєкти"
+
+msgid "pack loose unreachable objects"
+msgstr "запакувати вільні недосяжні обʼєкти"
+
+msgid "unpack unreachable objects newer than <time>"
+msgstr "розпакувати недосяжні обʼєкти, новіші за <час>"
+
+msgid "create a cruft pack"
+msgstr "створити марний пакунок"
+
+msgid "expire cruft objects older than <time>"
+msgstr "видалити марні обʼєкти старіші за <час>"
+
+msgid "use the sparse reachability algorithm"
+msgstr "використовувати алгоритм розрідженої досяжності"
+
+msgid "create thin packs"
+msgstr "створити тонкі пакунки"
+
+msgid "create packs suitable for shallow fetches"
+msgstr "створювати пакунки, придатні для неглибокого отримання"
+
+msgid "ignore packs that have companion .keep file"
+msgstr "ігнорувати пакунки, які мають супровідний .keep файл"
+
+msgid "ignore this pack"
+msgstr "ігнорувати цей пакунок"
+
+msgid "pack compression level"
+msgstr "рівень стиснення пакунка"
+
+msgid "do not hide commits by grafts"
+msgstr "не приховувати коміти через прищепи"
+
+msgid "use a bitmap index if available to speed up counting objects"
+msgstr ""
+"використовувати bitmap індекс, якщо він доступний, для прискорення "
+"підрахунку обʼєктів"
+
+msgid "write a bitmap index together with the pack index"
+msgstr "записати bitmap індекс разом з індексом пакунка"
+
+msgid "write a bitmap index if possible"
+msgstr "записати bitmap індекс, якщо це можливо"
+
+msgid "handling for missing objects"
+msgstr "обробка для відсутніх обʼєктів"
+
+msgid "do not pack objects in promisor packfiles"
+msgstr "не пакувати обʼєкти у promisor пакунки"
+
+msgid "respect islands during delta compression"
+msgstr "поважати острови під час дельта компресії"
+
+msgid "protocol"
+msgstr "протокол"
+
+msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
+msgstr "вилучити всі налаштовані uploadpack.blobpackfileuri з цим протоколом"
+
+#, c-format
+msgid "delta chain depth %d is too deep, forcing %d"
+msgstr "глибина дельта ланцюжка %d занадто глибока, примусове %d"
+
+#, c-format
+msgid "pack.deltaCacheLimit is too high, forcing %d"
+msgstr "pack.deltaCacheLimit занадто великий, примусове %d"
+
+#, c-format
+msgid "bad pack compression level %d"
+msgstr "невірний рівень стиснення пакунка %d"
+
+msgid "--max-pack-size cannot be used to build a pack for transfer"
+msgstr ""
+"--max-pack-size не можна використовувати для створення пакунка для передачі"
+
+msgid "minimum pack size limit is 1 MiB"
+msgstr "мінімальний розмір пакунка - 1 МіБ"
+
+msgid "--thin cannot be used to build an indexable pack"
+msgstr "--thin не можна використовувати для створення індексованого пакунка"
+
+msgid "cannot use --filter with --stdin-packs"
+msgstr "неможливо використовувати --filter з --stdin-packs"
+
+msgid "cannot use internal rev list with --stdin-packs"
+msgstr "неможливо використовувати внутрішній список ревізій з --stdin-packs"
+
+msgid "cannot use internal rev list with --cruft"
+msgstr "неможливо використовувати внутрішній список ревізій з --cruft"
+
+msgid "cannot use --stdin-packs with --cruft"
+msgstr "неможливо використовувати --stdin-packs з --cruft"
+
+msgid "Enumerating objects"
+msgstr "Перерахування обʼєктів"
+
+#, c-format
+msgid ""
+"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
+"reused %<PRIu32>"
+msgstr ""
+"Всього %<PRIu32> (дельта %<PRIu32>), повторно використано %<PRIu32> (дельта "
+"%<PRIu32>), повторно використано пакунків %<PRIu32>"
+
+msgid ""
+"'git pack-redundant' is nominated for removal.\n"
+"If you still use this command, please add an extra\n"
+"option, '--i-still-use-this', on the command line\n"
+"and let us know you still use it by sending an e-mail\n"
+"to <git@vger.kernel.org>.  Thanks.\n"
+msgstr ""
+"Команду \"git pack-redundant\" номіновано на вилучення.\n"
+"Якщо ви все ще використовуєте цю команду, будь ласка, додайте додатковий "
+"параметр\n"
+"\"--i-still-use-this\" у командному рядку\n"
+"і повідомте нам, що ви все ще використовуєте її, надіславши листа\n"
+"на адресу <git@vger.kernel.org>.  Дякуємо.\n"
+
+msgid "refusing to run without --i-still-use-this"
+msgstr "відмовлено в запуску без --i-still-use-this"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <шаблон>] [--exclude <шаблон>]"
+
+msgid "pack everything"
+msgstr "запакувати все"
+
+msgid "prune loose refs (default)"
+msgstr "видалити вивільнені посилання (за замовчуванням)"
+
+msgid "references to include"
+msgstr "посилання для включення"
+
+msgid "references to exclude"
+msgstr "посилання для виключення"
+
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+msgid "use the unstable patch-id algorithm"
+msgstr "використовувати нестабільний алгоритм ідентифікатора латки"
+
+msgid "use the stable patch-id algorithm"
+msgstr "використовувати стабільний алгоритм ідентифікатора латки"
+
+msgid "don't strip whitespace from the patch"
+msgstr "не прибирати пробіли з латки"
+
+msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
+msgstr "git prune [-n] [-v] [--progress] [--expire <час>] [--] [<верхівка>...]"
+
+msgid "report pruned objects"
+msgstr "повідомляти про видалені обʼєкти"
+
+msgid "expire objects older than <time>"
+msgstr "видалити обʼєкти старіші за <час>"
+
+msgid "limit traversal to objects outside promisor packfiles"
+msgstr "обмежити обхід об’єктами за межами promisor пакунків"
+
+msgid "cannot prune in a precious-objects repo"
+msgstr "неможливо виконати видалення в precious-objects сховищі"
+
+msgid "git pull [<options>] [<repository> [<refspec>...]]"
+msgstr "git pull [<опції>] [<сховище> [<визначник посилання>...]]"
+
+msgid "control for recursive fetching of submodules"
+msgstr "управління рекурсивним отриманням підмодулів"
+
+msgid "Options related to merging"
+msgstr "Опції, повʼязані зі злиттям"
+
+msgid "incorporate changes by rebasing rather than merging"
+msgstr "вносити зміни через перебазування, а не злиття"
+
+msgid "allow fast-forward"
+msgstr "дозволити перемотування вперед"
+
+msgid "control use of pre-merge-commit and commit-msg hooks"
+msgstr "контролювати використання гачків pre-merge-commit та commit-msg"
+
+msgid "automatically stash/stash pop before and after"
+msgstr "автоматично додавати до та забирати зі схову перед і після"
+
+msgid "Options related to fetching"
+msgstr "Опції, пов’язані з отриманням"
+
+msgid "force overwrite of local branch"
+msgstr "примусовий перезапис локальної гілки"
+
+msgid "number of submodules pulled in parallel"
+msgstr "кількість підмодулів, що затягуються паралельно"
+
+msgid "use IPv4 addresses only"
+msgstr "використовувати тільки IPv4 адреси"
+
+msgid "use IPv6 addresses only"
+msgstr "використовувати тільки IPv6 адреси"
+
+msgid ""
+"There is no candidate for rebasing against among the refs that you just "
+"fetched."
+msgstr ""
+"Серед щойно отриманих посилань немає кандидата, відносно якого можна "
+"виконати перебазування."
+
+msgid ""
+"There are no candidates for merging among the refs that you just fetched."
+msgstr "Серед щойно отриманих посилань немає кандидатів на злиття."
+
+msgid ""
+"Generally this means that you provided a wildcard refspec which had no\n"
+"matches on the remote end."
+msgstr ""
+"Зазвичай це означає, що для визначника посилання, ви ввели символ "
+"підстановки, який не має збігів на віддаленому призначені."
+
+#, c-format
+msgid ""
+"You asked to pull from the remote '%s', but did not specify\n"
+"a branch. Because this is not the default configured remote\n"
+"for your current branch, you must specify a branch on the command line."
+msgstr ""
+"Ви попросили затягнути з віддаленого \"%s\", але не вказали\n"
+"гілку. Оскільки це не типове віддалене призначення\n"
+"для вашої поточної гілки, вам треба вказати гілку у командному рядку."
+
+msgid "You are not currently on a branch."
+msgstr "Наразі ви не на гілці."
+
+msgid "Please specify which branch you want to rebase against."
+msgstr ""
+"Будь ласка, вкажіть, відносно якої гілки ви хочете виконати перебазування."
+
+msgid "Please specify which branch you want to merge with."
+msgstr "Будь ласка, вкажіть, з якою гілкою ви хочете виконати злиття."
+
+msgid "See git-pull(1) for details."
+msgstr "Дивіться git-pull(1) для більш детальної інформації."
+
+msgid "<remote>"
+msgstr "<віддалене-призначення>"
+
+msgid "<branch>"
+msgstr "<гілка>"
+
+msgid "There is no tracking information for the current branch."
+msgstr "Немає інформації для відстежування поточної гілки."
+
+msgid ""
+"If you wish to set tracking information for this branch you can do so with:"
+msgstr ""
+"Якщо ви бажаєте встановити інформацію для відстежування цієї гілки, ви "
+"можете зробити це за допомогою:"
+
+#, c-format
+msgid ""
+"Your configuration specifies to merge with the ref '%s'\n"
+"from the remote, but no such ref was fetched."
+msgstr ""
+"У вашій конфігурації вказано робити злиття з посиланням \"%s\"\n"
+"з віддаленого призначення, але такого посилання не було отримано."
+
+#, c-format
+msgid "unable to access commit %s"
+msgstr "не вдалося отримати доступ до коміту %s"
+
+msgid "ignoring --verify-signatures for rebase"
+msgstr "ігнорування --verify-signatures для перебазування"
+
+msgid ""
+"You have divergent branches and need to specify how to reconcile them.\n"
+"You can do so by running one of the following commands sometime before\n"
+"your next pull:\n"
+"\n"
+"  git config pull.rebase false  # merge\n"
+"  git config pull.rebase true   # rebase\n"
+"  git config pull.ff only       # fast-forward only\n"
+"\n"
+"You can replace \"git config\" with \"git config --global\" to set a "
+"default\n"
+"preference for all repositories. You can also pass --rebase, --no-rebase,\n"
+"or --ff-only on the command line to override the configured default per\n"
+"invocation.\n"
+msgstr ""
+"У вас є розбіжні гілки, і вам потрібно вказати, як їх узгодити.\n"
+"Ви можете зробити це, виконавши одну з наступних команд \n"
+"до вашого наступного затягування:\n"
+"\n"
+"  git config pull.rebase false # merge\n"
+"  git config pull.rebase true # rebase\n"
+"  git config pull.ff only # fast-forward only\n"
+"\n"
+"Ви можете замінити \"git config\" на \"git config --global\", щоб встановити "
+"налаштування за замовчуванням\n"
+"для всіх сховищ. Ви також можете передати --rebase, --no-rebase,\n"
+"або --ff-only у командному рядку, щоб перевизначити налаштування за "
+"замовчуванням для кожного\n"
+"виклику.\n"
+
+msgid "Updating an unborn branch with changes added to the index."
+msgstr "Оновлення ненародженої гілки зі змінами, доданими до індексу."
+
+msgid "pull with rebase"
+msgstr "затягнути з перебазуванням"
+
+msgid "Please commit or stash them."
+msgstr "Будь ласка, зробіть коміт або додайте зміни до схову."
+
+#, c-format
+msgid ""
+"fetch updated the current branch head.\n"
+"fast-forwarding your working tree from\n"
+"commit %s."
+msgstr ""
+"отримання оновило верхівку поточної гілки.\n"
+"перемотування вперед вашого робочого дерева з\n"
+"коміту %s."
+
+#, c-format
+msgid ""
+"Cannot fast-forward your working tree.\n"
+"After making sure that you saved anything precious from\n"
+"$ git diff %s\n"
+"output, run\n"
+"$ git reset --hard\n"
+"to recover."
+msgstr ""
+"Неможливо перемотати вперед ваше робоче дерево.\n"
+"Переконавшись, що ви зберегли все цінне з\n"
+"$ git diff %s\n"
+"виводу, виконайте\n"
+"$ git reset --hard\n"
+"для відновлення."
+
+msgid "Cannot merge multiple branches into empty head."
+msgstr "Неможливо злити кілька гілок до порожньої верхівки."
+
+msgid "Cannot rebase onto multiple branches."
+msgstr "Неможливо перебазувати на кілька гілок."
+
+msgid "Cannot fast-forward to multiple branches."
+msgstr "Неможливо перемотати вперед кілька гілок."
+
+msgid "Need to specify how to reconcile divergent branches."
+msgstr "Потрібно вказати, як узгоджувати розбіжні гілки."
+
+msgid "cannot rebase with locally recorded submodule modifications"
+msgstr "неможливо перебазувати з локально записаними модифікаціями підмодуля"
+
+msgid "git push [<options>] [<repository> [<refspec>...]]"
+msgstr "git push [<опції>] [<сховище> [<визначник посилання>...]]"
+
+msgid "tag shorthand without <tag>"
+msgstr "скорочення тегу без <тег>"
+
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete приймає лише прості назви посилань"
+
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'.\n"
+msgstr ""
+"\n"
+"Щоб обрати будь-яку з опцій постійною, скористайтесь опцією push.default у "
+"'git help config'.\n"
+
+msgid ""
+"\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
+"in 'git help config'.\n"
+msgstr ""
+"\n"
+"Щоб уникнути автоматичного налаштування висхідної гілки, якщо її назва\n"
+"не збігається з назвою локальної гілки, скористайтесь опцією 'simple' для "
+"branch.autoSetupMerge\n"
+"у “git help config”.\n"
+
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s HEAD\n"
+"%s%s"
+msgstr ""
+"Назва висхідної гілки для вашої поточної гілки не збігається з\n"
+"назвою вашої поточної гілки.  Щоб надіслати до висхідної гілки,\n"
+"скористайтесь командою\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"Щоб надіслати до однойменної гілки на віддаленому сервері скористайтесь\n"
+"\n"
+"    git push %s HEAD\n"
+"%s%s"
+
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+"Ви зараз не перебуваєте на гілці.\n"
+"Щоб надіслати історію, що веде до поточного (відокремленого HEAD)\n"
+"стану, скористайтесь командою\n"
+"\n"
+"    git push %s HEAD:<назва-віддаленої-гілки>\n"
+
+msgid ""
+"\n"
+"To have this happen automatically for branches without a tracking\n"
+"upstream, see 'push.autoSetupRemote' in 'git help config'.\n"
+msgstr ""
+"\n"
+"Щоб це відбувалося автоматично для невідстежуваних гілок\n"
+"першоджерельного сховища, дивіться 'push.autoSetupRemote' у 'git help "
+"config'.\n"
+
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+"%s"
+msgstr ""
+"Поточна гілка %s не має висхідної гілки.\n"
+"Щоб надіслати поточну гілку і встановити віддалене призначення "
+"першоджерельним сховищем, скористайтесь\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+"%s"
+
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr "Поточна гілка %s має кілька висхідних гілок, відмовлено в надсиланні."
+
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+"Ви не вказали жодних визначників посилань для надсилання, і push.default "
+"дорівнює \"nothing\"."
+
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+"Ви надсилаєте до віддаленої гілки \"%s\", яка не є першоджерелом\n"
+"вашої поточної гілки \"%s\", не кажучі мені, що надсилати\n"
+"і яку віддалену гілку оновлювати."
+
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Оновлення були відхилені, оскільки верхівка вашої поточної гілки знаходиться "
+"позаду\n"
+"її віддаленого аналога. Якщо ви хочете інтегрувати віддалені зміни,\n"
+"скористайтесь командою \"git pull\" перед повторним надсиланням.\n"
+"Докладні відомості наведено у розділі \"Зауваження щодо перемотування "
+"вперед\" команди \"git push --help\"."
+
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Оновлення були відхилені, оскільки верхівка надісланої гілки знаходиться "
+"позаду\n"
+"її віддаленого аналога. Якщо ви хочете інтегрувати віддалені зміни, "
+"скористайтесь командою \"git pull\"\n"
+"перед повторним надсиланням.\n"
+"Докладні відомості наведено у розділі \"Зауваження щодо перемотування "
+"вперед\" команди \"git push --help\"."
+
+msgid ""
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Оновлення були відхилені, оскільки у віддаленому сховищі міститься робота, "
+"якої\n"
+"немає локально. Зазвичай це стається, коли інше сховище надсилає\n"
+"до того самого посилання. Якщо ви хочете інтегрувати віддалені зміни,\n"
+"скористайтесь командою \"git pull\" перед повторним надсиланням.\n"
+"Докладні відомості наведено у розділі \"Зауваження щодо перемотування "
+"вперед\" команди \"git push --help\"."
+
+msgid "Updates were rejected because the tag already exists in the remote."
+msgstr ""
+"Оновлення були відхилені, оскільки тег вже існує на віддаленому сховищі."
+
+msgid ""
+"You cannot update a remote ref that points at a non-commit object,\n"
+"or update a remote ref to make it point at a non-commit object,\n"
+"without using the '--force' option.\n"
+msgstr ""
+"Ви не можете оновити віддалене посилання, яке вказує на об’єкт, що не є "
+"об’єктом коміту,\n"
+"або оновити віддалене посилання так, щоб воно вказувало на об’єкт, що не є "
+"об’єктом коміту,\n"
+"без використання опції \"--force\".\n"
+
+msgid ""
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Оновлення були відхилені, оскільки верхівка віддалено відстежуваної гілки "
+"була\n"
+"оновлена з часу останньої синхронізації. Якщо ви хочете інтегрувати "
+"віддалені зміни,\n"
+"скористайтесь командою \"git pull\" перед повторним надсиланням.\n"
+"Докладні відомості наведено у розділі \"Зауваження щодо перемотування "
+"вперед\" команди \"git push --help\"."
+
+#, c-format
+msgid "Pushing to %s\n"
+msgstr "Надсилання до %s\n"
+
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr "не вдалося надіслати деякі посилання до '%s'"
+
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"рекурсія в підмодулі з push.recurseSubmodules=only; натомість використовую "
+"on-demand"
+
+#, c-format
+msgid "invalid value for '%s'"
+msgstr "неприпустиме значення для '%s'"
+
+msgid "repository"
+msgstr "сховище"
+
+msgid "push all branches"
+msgstr "надіслати всі гілки"
+
+msgid "mirror all refs"
+msgstr "віддзеркалити всі посилання"
+
+msgid "delete refs"
+msgstr "видалити посилання"
+
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"надіслати теги (не можна використовувати з --all, --branches або --mirror)"
+
+msgid "force updates"
+msgstr "оновити примусово"
+
+msgid "<refname>:<expect>"
+msgstr "<refname>:<expect>"
+
+msgid "require old value of ref to be at this value"
+msgstr "вимагати, щоб старе значення посилання було рівним цьому значенню"
+
+msgid "require remote updates to be integrated locally"
+msgstr "вимагати локальної інтеграції віддалених оновлень"
+
+msgid "control recursive pushing of submodules"
+msgstr "контролювати рекурсивне надсилання підмодулів"
+
+msgid "use thin pack"
+msgstr "використовувати тонке пакування"
+
+msgid "receive pack program"
+msgstr "отримати пакетну програму"
+
+msgid "set upstream for git pull/status"
+msgstr "встановити першоджерельне сховище для git pull/status"
+
+msgid "prune locally removed refs"
+msgstr "обрізати локально видалені посилання"
+
+msgid "bypass pre-push hook"
+msgstr "обійти pre-push гачок"
+
+msgid "push missing but relevant tags"
+msgstr "надсилання відсутнє, але є релевантні теги"
+
+msgid "GPG sign the push"
+msgstr "підписати надсилання за допомогою GPG"
+
+msgid "request atomic transaction on remote side"
+msgstr "запросити атомарну транзакцію на віддаленій стороні"
+
+msgid "--delete doesn't make sense without any refs"
+msgstr "--delete не має сенсу без посилань"
+
+#, c-format
+msgid "bad repository '%s'"
+msgstr "невірне сховище \"%s\""
+
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+"Немає налаштованого місця призначення надсилання.\n"
+"Або зазначте URL-адресу з командного рядка, або налаштуйте віддалене сховище "
+"за допомогою\n"
+"\n"
+"    git remote add <назва> <адреса>\n"
+"\n"
+"а потім виконайте надсилання, використовуючи назву віддаленого сховища\n"
+"\n"
+"    git push <ім'я>\n"
+
+msgid "--all can't be combined with refspecs"
+msgstr "--all не можна комбінувати з визначниками посилань"
+
+msgid "--mirror can't be combined with refspecs"
+msgstr "--mirror не можна комбінувати з визначниками посилань"
+
+msgid "push options must not have new line characters"
+msgstr "опції push не можуть містити символи нового рядка"
+
+msgid "git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"
+msgstr ""
+"git range-diff [<опції>] <стара-база>..<стара-верхівка> <нова-база>..<нова-"
+"верхівка>"
+
+msgid "git range-diff [<options>] <old-tip>...<new-tip>"
+msgstr "git range-diff [<опції>] <стара-верхівка>...<нова-верхівка>"
+
+msgid "git range-diff [<options>] <base> <old-tip> <new-tip>"
+msgstr "git range-diff [<опції>] <база> <стара-верхівка> <нова-верхівка>"
+
+msgid "use simple diff colors"
+msgstr "використовувати прості кольори diff"
+
+msgid "notes"
+msgstr "нотатки"
+
+msgid "passed to 'git log'"
+msgstr "передано до \"git log\""
+
+msgid "only emit output related to the first range"
+msgstr "видати тільки вивід, що відноситься до першого діапазону"
+
+msgid "only emit output related to the second range"
+msgstr "видати тільки вивід, що відноситься до другого діапазону"
+
+#, c-format
+msgid "not a revision: '%s'"
+msgstr "не є ревізією: \"%s\""
+
+#, c-format
+msgid "not a commit range: '%s'"
+msgstr "не діапазон комітів: \"%s\""
+
+#, c-format
+msgid "not a symmetric range: '%s'"
+msgstr "не симетричний діапазон: \"%s\""
+
+msgid "need two commit ranges"
+msgstr "потрібно два діапазони комітів"
+
+msgid ""
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
+msgstr ""
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<префікс>)\n"
+"              [-u | -i]] [--index-output=<файл>] [--no-sparse-checkout]\n"
+"              (--empty | <деревоподібне-джерело1> [<деревоподібне-джерело2> "
+"[<деревоподібне-джерело3>]])"
+
+msgid "write resulting index to <file>"
+msgstr "записати отриманий індекс у <файл>"
+
+msgid "only empty the index"
+msgstr "тільки очистити індекс"
+
+msgid "Merging"
+msgstr "Злиття"
+
+msgid "perform a merge in addition to a read"
+msgstr "виконати злиття на додачу до читання"
+
+msgid "3-way merge if no file level merging required"
+msgstr "3-стороннє злиття, при відсутності потреби в злитті на рівні файлів"
+
+msgid "3-way merge in presence of adds and removes"
+msgstr "3-стороннє злиття за наявності додавання та видалення"
+
+msgid "same as -m, but discard unmerged entries"
+msgstr "те саме, що й -m, але відкидає не злиті записи"
+
+msgid "<subdirectory>/"
+msgstr "<піддиректорія>/"
+
+msgid "read the tree into the index under <subdirectory>/"
+msgstr "зчитати дерево в індекс під <піддиректорію>/"
+
+msgid "update working tree with merge result"
+msgstr "оновити робоче дерево результатом злиття"
+
+msgid "gitignore"
+msgstr "gitignore"
+
+msgid "allow explicitly ignored files to be overwritten"
+msgstr "дозволити перезапис явно проігнорованих файлів"
+
+msgid "don't check the working tree after merging"
+msgstr "не перевіряти робоче дерево після злиття"
+
+msgid "don't update the index or the work tree"
+msgstr "не оновлювати індекс або робоче дерево"
+
+msgid "skip applying sparse checkout filter"
+msgstr "пропустити застосування sparse checkout фільтра"
+
+msgid "debug unpack-trees"
+msgstr "відлагодити unpack-trees"
+
+msgid "suppress feedback messages"
+msgstr "не показувати повідомлення зворотного звʼязку"
+
+msgid "You need to resolve your current index first"
+msgstr "Спочатку вам потрібно розвʼязати поточний індекс"
+
+msgid ""
+"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase> | --keep-base] "
+"[<upstream> [<branch>]]"
+msgstr ""
+"git rebase [-i] [опції] [--exec <команда>] [--onto <нова-база> | --keep-"
+"base] [<першоджерельне-сховище> [<гілка>]]"
+
+msgid ""
+"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]"
+msgstr ""
+"git rebase [-i] [опції] [--exec <команда>] [--onto <нова-база>] --root "
+"[<гілка>]"
+
+#, c-format
+msgid "could not read '%s'."
+msgstr "не вдалося прочитати \"%s\"."
+
+#, c-format
+msgid "could not create temporary %s"
+msgstr "не вдалося створити тимчасовий %s"
+
+msgid "could not mark as interactive"
+msgstr "не вдалося позначити як інтерактивний"
+
+msgid "could not generate todo list"
+msgstr "не вдалося створити список справ"
+
+msgid "a base commit must be provided with --upstream or --onto"
+msgstr "базовий коміт має бути наданий з --upstream або --onto"
+
+#, c-format
+msgid "%s requires the merge backend"
+msgstr "%s потребує обробника злиття"
+
+#, c-format
+msgid "invalid onto: '%s'"
+msgstr "неприпустимий onto: \"%s\""
+
+#, c-format
+msgid "invalid orig-head: '%s'"
+msgstr "неприпустимий orig-head: \"%s\""
+
+#, c-format
+msgid "ignoring invalid allow_rerere_autoupdate: '%s'"
+msgstr "ігнорування неприпустимого allow_rerere_autoupdate: \"%s\""
+
+#, c-format
+msgid "could not remove '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+msgid ""
+"Resolve all conflicts manually, mark them as resolved with\n"
+"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
+"You can instead skip this commit: run \"git rebase --skip\".\n"
+"To abort and get back to the state before \"git rebase\", run \"git rebase --"
+"abort\"."
+msgstr ""
+"Вирішіть усі конфлікти вручну, позначте їх як вирішені за допомогою\n"
+"\"git add/rm <конфліктні_файли>\", а потім виконайте \"git rebase --"
+"continue\".\n"
+"Замість цього ви можете пропустити цей коміт: виконайте \"git rebase --"
+"skip\".\n"
+"Щоб перервати процес і повернутися до стану перед \"git rebase\", виконайте "
+"\"git rebase --abort\"."
+
+#, c-format
+msgid ""
+"\n"
+"git encountered an error while preparing the patches to replay\n"
+"these revisions:\n"
+"\n"
+"    %s\n"
+"\n"
+"As a result, git cannot rebase them."
+msgstr ""
+"\n"
+"git зіткнувся з помилкою під час підготовки латок для відтворення\n"
+"цих ревізій:\n"
+"\n"
+"    %s\n"
+"\n"
+"Внаслідок цього git не може їх перебазувати."
+
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Невідомий режим перебазування-злиття: %s"
+
+#, c-format
+msgid "could not switch to %s"
+msgstr "не вдалося переключитись на %s"
+
+msgid "apply options and merge options cannot be used together"
+msgstr "apply опції не можна використовувати разом з merge опціями"
+
+#, c-format
+msgid ""
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
+msgstr ""
+"нерозпізнаний порожній тип \"%s\"; припустимими значеннями є \"drop\", "
+"\"keep\" та \"ask\"."
+
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"Команда --rebase-merges з порожнім строковим аргументом є застарілою і "
+"перестане працювати у наступній версії Git. Замість цього скористайтесь "
+"командою --rebase-merges без аргументу, який робить те саме."
+
+#, c-format
+msgid ""
+"%s\n"
+"Please specify which branch you want to rebase against.\n"
+"See git-rebase(1) for details.\n"
+"\n"
+"    git rebase '<branch>'\n"
+"\n"
+msgstr ""
+"%s\n"
+"Будь ласка, вкажіть гілку, відносно якої ви хочете виконати перебазування.\n"
+"Дивіться git-rebase(1) для детальної інформації.\n"
+"\n"
+"    git rebase \"<гілка>\"\n"
+"\n"
+"\n"
+
+#, c-format
+msgid ""
+"If you wish to set tracking information for this branch you can do so with:\n"
+"\n"
+"    git branch --set-upstream-to=%s/<branch> %s\n"
+"\n"
+msgstr ""
+"Якщо ви бажаєте налаштувати відстежування для цієї гілки, ви можете зробити "
+"це за допомогою:\n"
+"\n"
+"    git branch --set-upstream-to=%s/<гілка> %s\n"
+"\n"
+
+msgid "exec commands cannot contain newlines"
+msgstr "команди exec не можуть містити символи нового рядка"
+
+msgid "empty exec command"
+msgstr "порожня команда exec"
+
+msgid "rebase onto given branch instead of upstream"
+msgstr "перебазувати на задану гілку замість першоджерельного сховища"
+
+msgid "use the merge-base of upstream and branch as the current base"
+msgstr ""
+"використовувати базу злиття першоджерельного сховища та гілки як поточну базу"
+
+msgid "allow pre-rebase hook to run"
+msgstr "дозволити запуск pre-rebase гачка"
+
+msgid "be quiet. implies --no-stat"
+msgstr "працювати тихесенько. Мається на увазі --no-stat"
+
+msgid "display a diffstat of what changed upstream"
+msgstr "відображати diffstat того, що змінилося у першоджерельному сховищі"
+
+msgid "do not show diffstat of what changed upstream"
+msgstr "не відображати diffstat того, що змінилося у першоджерельному сховищі"
+
+msgid "add a Signed-off-by trailer to each commit"
+msgstr "додати Signed-off-by причіп до кожного коміту"
+
+msgid "make committer date match author date"
+msgstr "зробити так, щоб дата комітера збігалася з датою автора"
+
+msgid "ignore author date and use current date"
+msgstr "ігнорувати дату автора та використати поточну дату"
+
+msgid "synonym of --reset-author-date"
+msgstr "синонім --reset-author-date"
+
+msgid "passed to 'git apply'"
+msgstr "передано в \"git apply\""
+
+msgid "ignore changes in whitespace"
+msgstr "ігнорувати зміни в пробілах"
+
+msgid "cherry-pick all commits, even if unchanged"
+msgstr "висмикнути всі коміти, навіть якщо вони не змінені"
+
+msgid "continue"
+msgstr "продовжити"
+
+msgid "skip current patch and continue"
+msgstr "пропустити поточну латку і продовжити"
+
+msgid "abort and check out the original branch"
+msgstr "перервати і перейти до початкової гілки"
+
+msgid "abort but keep HEAD where it is"
+msgstr "перервати, але залишити HEAD на місці"
+
+msgid "edit the todo list during an interactive rebase"
+msgstr "редагувати список справ під час інтерактивного перебазування"
+
+msgid "show the patch file being applied or merged"
+msgstr "показати файл латки, який застосовується або зливається"
+
+msgid "use apply strategies to rebase"
+msgstr "використовувати стратегії застосування для перебазування"
+
+msgid "use merging strategies to rebase"
+msgstr "використовувати стратегії злиття для перебазування"
+
+msgid "let the user edit the list of commits to rebase"
+msgstr "дозволити користувачеві редагувати список комітів для перебазування"
+
+msgid "(REMOVED) was: try to recreate merges instead of ignoring them"
+msgstr ""
+"(ВИДАЛЕНО) було: спробувати відтворити злиття замість того, щоб ігнорувати їх"
+
+msgid "how to handle commits that become empty"
+msgstr "як обробляти порожні коміти"
+
+msgid "keep commits which start empty"
+msgstr "зберігати коміти, які починаються порожніми"
+
+msgid "move commits that begin with squash!/fixup! under -i"
+msgstr "перемістити коміти, які починаються з squash!/fixup! під -i"
+
+msgid "update branches that point to commits that are being rebased"
+msgstr ""
+"оновити гілки, які вказують на коміти, що знаходяться в стані перебазування"
+
+msgid "add exec lines after each commit of the editable list"
+msgstr "додати exec рядки після кожного коміту редагованого списку"
+
+msgid "allow rebasing commits with empty messages"
+msgstr "дозволити перебазування комітів з порожніми дописами"
+
+msgid "try to rebase merges instead of skipping them"
+msgstr "спробувати перебазувати злиття замість того, щоб пропускати їх"
+
+msgid "use 'merge-base --fork-point' to refine upstream"
+msgstr ""
+"скористайтесь \"merge-base --fork-point\", щоб зазначиити першоджерельне "
+"сховище"
+
+msgid "use the given merge strategy"
+msgstr "використати задану стратегію злиття"
+
+msgid "option"
+msgstr "опція"
+
+msgid "pass the argument through to the merge strategy"
+msgstr "передати аргумент до стратегії злиття"
+
+msgid "rebase all reachable commits up to the root(s)"
+msgstr "перебазувати всі доступні коміти до кореня(ів)."
+
+msgid "automatically re-schedule any `exec` that fails"
+msgstr "автоматично переносити будь-який \"exec\", який завершився невдало"
+
+msgid "apply all changes, even those already present upstream"
+msgstr ""
+"застосовувати всі зміни, навіть ті, що вже існують у першоджерельному сховищі"
+
+msgid "It looks like 'git am' is in progress. Cannot rebase."
+msgstr "Схоже, що виконується команда \"git am\". Неможливо перебазувати."
+
+msgid ""
+"`rebase --preserve-merges` (-p) is no longer supported.\n"
+"Use `git rebase --abort` to terminate current rebase.\n"
+"Or downgrade to v2.33, or earlier, to complete the rebase."
+msgstr ""
+"Команда \"rebase --preserve-merges\" (-p) більше не підтримується.\n"
+"Скористайтесь \"git rebase --abort\" для завершення поточного "
+"перебазування.\n"
+"Або поверніться до v2.33 або більш ранньої версії, щоб завершити "
+"перебазування."
+
+msgid ""
+"--preserve-merges was replaced by --rebase-merges\n"
+"Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
+"which is no longer supported; use 'merges' instead"
+msgstr ""
+"--preserve-merges було замінено на --rebase-merges\n"
+"Примітка: У вашій конфігурації \"pull.rebase\" також може бути встановлено у "
+"значення \"preserve\",\n"
+"який більше не підтримується; натомість використовуйте \"merges\""
+
+msgid "No rebase in progress?"
+msgstr "Перебазування не відбувається?"
+
+msgid "The --edit-todo action can only be used during interactive rebase."
+msgstr ""
+"Дію --edit-todo можна використовувати лише під час інтерактивного "
+"перебазування."
+
+msgid "Cannot read HEAD"
+msgstr "Неможливо прочитати HEAD"
+
+msgid ""
+"You must edit all merge conflicts and then\n"
+"mark them as resolved using git add"
+msgstr ""
+"Ви повинні відредагувати всі конфлікти злиття, а потім\n"
+"позначити їх як вирішені за допомогою git add"
+
+msgid "could not discard worktree changes"
+msgstr "не вдалося відкинути зміни робочого дерева"
+
+#, c-format
+msgid "could not move back to %s"
+msgstr "не вдалося повернутися до %s"
+
+#, c-format
+msgid ""
+"It seems that there is already a %s directory, and\n"
+"I wonder if you are in the middle of another rebase.  If that is the\n"
+"case, please try\n"
+"\t%s\n"
+"If that is not the case, please\n"
+"\t%s\n"
+"and run me again.  I am stopping in case you still have something\n"
+"valuable there.\n"
+msgstr ""
+"Здається, що директорія %s вже існує, і\n"
+"можливо, ви перебуваєте у процесі іншого перебазування.  Якщо це так,\n"
+"будь ласка, спробуйте\n"
+"\t%s\n"
+"Якщо це не так, спробуйте\n"
+"\t%s\n"
+"і запустіть команду ще раз.  Зупинка на випадок, якщо там у вас все ще є "
+"щось\n"
+"цінне.\n"
+
+msgid "switch `C' expects a numerical value"
+msgstr "перемикач \"C\" очікує числове значення"
+
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+"apply опції несумісні з rebase.autoSquash.  Розгляньте можливість додавання "
+"--no-autosquash"
+
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+"apply опції несумісні з rebase.rebaseMerges.  Розгляньте можливість "
+"додавання --no-rebase-merges"
+
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr ""
+"apply опції несумісні з rebase.updateRefs.  Розгляньте можливість додавання "
+"--no-update-refs"
+
+#, c-format
+msgid "Unknown rebase backend: %s"
+msgstr "Невідомий обробник перебазування: %s"
+
+msgid "--reschedule-failed-exec requires --exec or --interactive"
+msgstr "--reschedule-failed-exec потребує --exec або --interactive"
+
+#, c-format
+msgid "invalid upstream '%s'"
+msgstr "неприпустиме першоджерельне сховище \"%s\""
+
+msgid "Could not create new root commit"
+msgstr "Не вдалося створити новий кореневий коміт"
+
+#, c-format
+msgid "no such branch/commit '%s'"
+msgstr "немає такої гілки/коміту \"%s\""
+
+#, c-format
+msgid "No such ref: %s"
+msgstr "Немає такого посилання: %s"
+
+msgid "Could not resolve HEAD to a commit"
+msgstr "Не вдалося розвʼязати HEAD у коміт"
+
+#, c-format
+msgid "'%s': need exactly one merge base with branch"
+msgstr "\"%s\": потрібна лишень одна база злиття з гілкою"
+
+#, c-format
+msgid "'%s': need exactly one merge base"
+msgstr "\"%s\": потрібна лишень одна база злиття"
+
+#, c-format
+msgid "Does not point to a valid commit '%s'"
+msgstr "Не вказує на припустимий коміт \"%s\""
+
+msgid "HEAD is up to date."
+msgstr "HEAD знаходиться в актуальному стані."
+
+#, c-format
+msgid "Current branch %s is up to date.\n"
+msgstr "Поточна гілка %s знаходиться в актуальному стані.\n"
+
+msgid "HEAD is up to date, rebase forced."
+msgstr "HEAD знаходиться в актуальному стані, примусове перебазування."
+
+#, c-format
+msgid "Current branch %s is up to date, rebase forced.\n"
+msgstr ""
+"Поточна гілка %s знаходиться в актуальному стані, перебазовуйте примусово.\n"
+
+msgid "The pre-rebase hook refused to rebase."
+msgstr "Гачок pre-rebase відмовився перебазувати."
+
+#, c-format
+msgid "Changes to %s:\n"
+msgstr "Зміни у %s:\n"
+
+#, c-format
+msgid "Changes from %s to %s:\n"
+msgstr "Зміна з %s на %s:\n"
+
+#, c-format
+msgid "First, rewinding head to replay your work on top of it...\n"
+msgstr "Спочатку перемотуємо HEAD, щоб відтворити вашу роботу поверх того...\n"
+
+msgid "Could not detach HEAD"
+msgstr "Не вдалося відʼєднати HEAD"
+
+#, c-format
+msgid "Fast-forwarded %s to %s.\n"
+msgstr "Перемотано вперед %s на %s.\n"
+
+msgid "git receive-pack <git-dir>"
+msgstr "git receive-pack <git-директорія>"
+
+msgid ""
+"By default, updating the current branch in a non-bare repository\n"
+"is denied, because it will make the index and work tree inconsistent\n"
+"with what you pushed, and will require 'git reset --hard' to match\n"
+"the work tree to HEAD.\n"
+"\n"
+"You can set the 'receive.denyCurrentBranch' configuration variable\n"
+"to 'ignore' or 'warn' in the remote repository to allow pushing into\n"
+"its current branch; however, this is not recommended unless you\n"
+"arranged to update its work tree to match what you pushed in some\n"
+"other way.\n"
+"\n"
+"To squelch this message and still keep the default behaviour, set\n"
+"'receive.denyCurrentBranch' configuration variable to 'refuse'."
+msgstr ""
+"За замовчуванням, оновлення поточної гілки у непустому сховищі\n"
+"заборонено, оскільки це зробить індекс і робоче дерево невідповідними\n"
+"з тим, що ви надіслали, і вимагатиме виконання команди \"git reset --hard\" "
+"щоб співставити робоче дерево і HEAD.\n"
+"\n"
+"Ви можете встановити конфігураційну змінну \"receive.denyCurrentBranch\"\n"
+"на \"ignore\" або \"warn\" у віддаленому сховищі, щоб дозволити надсилання "
+"до поточної гілки; однак, це не рекомендується, якщо ви не\n"
+"налагодили оновлення його робочого дерева відповідно до того, що ви "
+"надіслали якимось іншим способом.\n"
+"\n"
+"Щоб ігнорувати це повідомлення і зберегти поведінку за замовчуванням, "
+"встановіть параметр \"receive.denyCurrentBranch\" у значення \"refuse\"."
+
+msgid ""
+"By default, deleting the current branch is denied, because the next\n"
+"'git clone' won't result in any file checked out, causing confusion.\n"
+"\n"
+"You can set 'receive.denyDeleteCurrent' configuration variable to\n"
+"'warn' or 'ignore' in the remote repository to allow deleting the\n"
+"current branch, with or without a warning message.\n"
+"\n"
+"To squelch this message, you can set it to 'refuse'."
+msgstr ""
+"За замовчуванням, видалення поточної гілки заборонено, оскільки наступний\n"
+"\"git clone\" не призведе до переключення стану жодного файлу, що спричинить "
+"плутанину.\n"
+"\n"
+"Ви можете встановити конфігураційну змінну \"receive.denyDeleteCurrent\" у "
+"значення\n"
+"\"warn\" або \"ignore\" у віддаленому сховищі, щоб дозволити видалення\n"
+"поточної гілки, з попереджувальним повідомленням або без нього.\n"
+"\n"
+"Щоб ігнорувати це повідомлення, ви можете встановити значення \"refuse\"."
+
+msgid "quiet"
+msgstr "тихо"
+
+msgid "you must specify a directory"
+msgstr "необхідно вказати директорію"
+
+msgid "git reflog [show] [<log-options>] [<ref>]"
+msgstr "git reflog [show] [<лог-опції>] [<посилання>]"
+
+msgid ""
+"git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n"
+"                  [--rewrite] [--updateref] [--stale-fix]\n"
+"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
+"<refs>...]"
+msgstr ""
+"git reflog expire [--expire=<час>] [--expire-unreachable=<час>]\n"
+"                  [--rewrite] [--updateref] [--stale-fix]\n"
+"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
+"<посилання>...]"
+
+msgid ""
+"git reflog delete [--rewrite] [--updateref]\n"
+"                  [--dry-run | -n] [--verbose] <ref>@{<specifier>}..."
+msgstr ""
+"git reflog delete [--rewrite] [--updateref]\n"
+"                  [--dry-run | -n] [--verbose] <посилання>@{<визначник>}..."
+
+msgid "git reflog exists <ref>"
+msgstr "git reflog exists <посилання>"
+
+#, c-format
+msgid "invalid timestamp '%s' given to '--%s'"
+msgstr "неприпустима позначка часу \"%s\" передана до \"--%s\""
+
+msgid "do not actually prune any entries"
+msgstr "насправді не видаляти жодного запису"
+
+msgid ""
+"rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"
+msgstr "перезаписати старий SHA1 на новий SHA1 запису, який тепер передує йому"
+
+msgid "update the reference to the value of the top reflog entry"
+msgstr "оновити посилання на значення верхнього запису журналу посилань"
+
+msgid "print extra information on screen"
+msgstr "вивести додаткову інформацію на екран"
+
+msgid "timestamp"
+msgstr "позначка часу"
+
+msgid "prune entries older than the specified time"
+msgstr "видалити записи, старіші за вказаний час"
+
+msgid ""
+"prune entries older than <time> that are not reachable from the current tip "
+"of the branch"
+msgstr ""
+"видалити записи, старіші за <час>, які недоступні з поточної верхівки гілки"
+
+msgid "prune any reflog entries that point to broken commits"
+msgstr ""
+"видалити всі записи журналу посилань, які вказують на пошкоджені коміти"
+
+msgid "process the reflogs of all references"
+msgstr "обробляти журнали посилань всіх посилань"
+
+msgid "limits processing to reflogs from the current worktree only"
+msgstr "обмежити обробку журналами посилань лише з поточного робочого дерева"
+
+#, c-format
+msgid "Marking reachable objects..."
+msgstr "Позначення досяжних обʼєктів..."
+
+#, c-format
+msgid "%s points nowhere!"
+msgstr "%s вказує в нікуди!"
+
+msgid "no reflog specified to delete"
+msgstr "не вказано журнал посилань для видалення"
+
+#, c-format
+msgid "invalid ref format: %s"
+msgstr "неприпустимий формат посилання: %s"
+
+msgid ""
+"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--"
+"mirror=<fetch|push>] <name> <url>"
+msgstr ""
+"git remote add [-t <гілка>] [-m <мастер>] [-f] [--tags | --no-tags] [--"
+"mirror=<fetch|push>] <назва> <URL-адреса>"
+
+msgid "git remote rename [--[no-]progress] <old> <new>"
+msgstr "git remote rename [--[no-]progress] <стара-назва> <нова-назва>"
+
+msgid "git remote remove <name>"
+msgstr "git remote remove <назвa>"
+
+msgid "git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"
+msgstr "git remote set-head <назва> (-a | --auto | -d | --delete | <гілка>)"
+
+msgid "git remote [-v | --verbose] show [-n] <name>"
+msgstr "git remote [-v | --verbose] show [-n] <назва>"
+
+msgid "git remote prune [-n | --dry-run] <name>"
+msgstr "git remote prune [-n | --dry-run] <назва>"
+
+msgid ""
+"git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"
+msgstr ""
+"git remote [-v | --verbose] update [-p | --prune] [(<група> | <віддалене-"
+"призначення>)...]"
+
+msgid "git remote set-branches [--add] <name> <branch>..."
+msgstr "git remote set-branches [--add] <назва> <гілка>..."
+
+msgid "git remote get-url [--push] [--all] <name>"
+msgstr "git remote get-url [--push] [--all] <назва>"
+
+msgid "git remote set-url [--push] <name> <newurl> [<oldurl>]"
+msgstr ""
+"git remote set-url [--push] <назва> <нова-url-адреса> [<стара-url-адреса>]"
+
+msgid "git remote set-url --add <name> <newurl>"
+msgstr "git remote set-url --add <назва> <нова-url-адреса>"
+
+msgid "git remote set-url --delete <name> <url>"
+msgstr "git remote set-url --delete <назва> <url-адреса>"
+
+msgid "git remote add [<options>] <name> <url>"
+msgstr "git remote add [<опції>] <назва> <url>"
+
+msgid "git remote set-branches <name> <branch>..."
+msgstr "git remote set-branches <назва> <гілка>..."
+
+msgid "git remote set-branches --add <name> <branch>..."
+msgstr "git remote set-branches --add <назва> <гілка>..."
+
+msgid "git remote show [<options>] <name>"
+msgstr "git remote show [<опції>] <назва>"
+
+msgid "git remote prune [<options>] <name>"
+msgstr "git remote prune [<опції>] <назва>"
+
+msgid "git remote update [<options>] [<group> | <remote>]..."
+msgstr "git remote update [<опції>] [<група> | <віддаленe-призначення>]..."
+
+#, c-format
+msgid "Updating %s"
+msgstr "Оновлення %s"
+
+#, c-format
+msgid "Could not fetch %s"
+msgstr "Не вдалося отримати %s"
+
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+"--mirror небезпечний і застарілий; будь ласка, \n"
+"\t скористайтесь --mirror=fetch або --mirror=push"
+
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "невідомий аргумент дзеркала: %s"
+
+msgid "fetch the remote branches"
+msgstr "отримати віддалені гілки"
+
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"імпортувати всі теги та повʼязані з ними обʼєкти під час отримання\n"
+"або не отримувати жодного тегу (--no-tags)"
+
+msgid "branch(es) to track"
+msgstr "гілка(и) для відстежування"
+
+msgid "master branch"
+msgstr "master гілка"
+
+msgid "set up remote as a mirror to push to or fetch from"
+msgstr ""
+"налаштувати віддалене призначення як дзеркало, щоб надсилати до нього або "
+"отримувати з нього дані"
+
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr "вказівка головної гілки не має сенсу з --mirror"
+
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr ""
+"вказівка гілок для відстежування має сенс тільки з дзеркалами отримання"
+
+#, c-format
+msgid "remote %s already exists."
+msgstr "віддалений %s вже існує."
+
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Не вдалося налаштувати master \"%s\""
+
+#, c-format
+msgid "more than one %s"
+msgstr "більше одного %s"
+
+#, c-format
+msgid "unhandled branch.%s.rebase=%s; assuming 'true'"
+msgstr "unhandled branch.%s.rebase=%s; припускаю \"true\""
+
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr "Не вдалося забрати карту отримання для визначника посилання %s"
+
+msgid "(matching)"
+msgstr "(збіг)"
+
+msgid "(delete)"
+msgstr "(видалити)"
+
+#, c-format
+msgid "could not set '%s'"
+msgstr "не вдалося встановити \"%s\""
+
+#, c-format
+msgid "could not unset '%s'"
+msgstr "не вдалося скинути \"%s\""
+
+#, c-format
+msgid ""
+"The %s configuration remote.pushDefault in:\n"
+"\t%s:%d\n"
+"now names the non-existent remote '%s'"
+msgstr ""
+"Конфігурація %s remote.pushDefault in:\n"
+"\t%s:%d\n"
+"тепер називає неіснуюче віддалене посилання \"%s\""
+
+#, c-format
+msgid "No such remote: '%s'"
+msgstr "Немає такого віддаленого призначення: \"%s\""
+
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Не вдалося перейменувати розділ конфігурації з \"%s\" на \"%s\""
+
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+"Не оновлюється нестандартний визначник отримання посилань\n"
+"%s\n"
+"Будь ласка, за потреби оновіть конфігурацію вручну."
+
+msgid "Renaming remote references"
+msgstr "Перейменування віддалених посилань"
+
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "не вдалося видалити \"%s\""
+
+#, c-format
+msgid "creating '%s' failed"
+msgstr "не вдалося створити \"%s\""
+
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+"Примітка: гілка поза межами refs/remotes/ ієрархії не була видалена;\n"
+"щоб видалити її, скористайтесь:"
+msgstr[1] ""
+"Примітка: деякі гілки поза межами refs/remotes/ ієрархії не були видалені;\n"
+"щоб видалити їх, скористайтесь:"
+msgstr[2] ""
+"Примітка: деякі гілки поза межами refs/remotes/ ієрархії не були видалені;\n"
+"щоб видалити їх, скористайтесь:"
+
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Не вдалося видалити секцію конфігурації \"%s\""
+
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr " нова (наступне отримання зберігатиметься у віддалених remotes/%s)"
+
+msgid " tracked"
+msgstr " відстежується"
+
+msgid " skipped"
+msgstr " пропущена"
+
+msgid " stale (use 'git remote prune' to remove)"
+msgstr " застаріла (скористайтесь \"git remote prune\", щоб видалити)"
+
+msgid " ???"
+msgstr " ???"
+
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr "неприпустиме branch.%s.merge; неможливо перебазувати на > 1 гілку"
+
+#, c-format
+msgid "rebases interactively onto remote %s"
+msgstr "перебазувати інтерактивно на віддалене призначення %s"
+
+#, c-format
+msgid "rebases interactively (with merges) onto remote %s"
+msgstr "перебазувати інтерактивно (зі злиттям) на віддалене призначення %s"
+
+#, c-format
+msgid "rebases onto remote %s"
+msgstr "перебазувати на віддалене призначення %s"
+
+#, c-format
+msgid " merges with remote %s"
+msgstr " зливається з віддаленим призначенням %s"
+
+#, c-format
+msgid "merges with remote %s"
+msgstr "зливається з віддаленим призначенням %s"
+
+#, c-format
+msgid "%-*s    and with remote %s\n"
+msgstr "%-*s    та з віддаленим призначенням %s\n"
+
+msgid "create"
+msgstr "створити"
+
+msgid "delete"
+msgstr "видалити"
+
+msgid "up to date"
+msgstr "в актуальному стані"
+
+msgid "fast-forwardable"
+msgstr "з можливістю перемотування вперед"
+
+msgid "local out of date"
+msgstr "локальне сховище застаріло"
+
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr "    %-*s примусово надіслати до %-*s (%s)"
+
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr "    %-*s надіслати до %-*s (%s)"
+
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr "    %-*s примусово надіслати до %s"
+
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr "    %-*s надіслати до %s"
+
+msgid "do not query remotes"
+msgstr "не запитувати віддалені призначення"
+
+#, c-format
+msgid "* remote %s"
+msgstr "* віддалене %s"
+
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr "  URL-адреса отримання: %s"
+
+msgid "(no URL)"
+msgstr "(без URL-адреси)"
+
+#. TRANSLATORS: the colon ':' should align
+#. with the one in " Fetch URL: %s"
+#. translation.
+#.
+#, c-format
+msgid "  Push  URL: %s"
+msgstr "  URL-адреса надсилання: %s"
+
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "  HEAD гілка: %s"
+
+msgid "(not queried)"
+msgstr "(не запитувалось)"
+
+msgid "(unknown)"
+msgstr "(невідомо)"
+
+#, c-format
+msgid ""
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr ""
+"  HEAD гілка (віддалений HEAD неоднозначний, може бути одним з наступних):\n"
+
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] " Віддалена гілка:%s"
+msgstr[1] " Віддалені гілки:%s"
+msgstr[2] " Віддалених гілок:%s"
+
+msgid " (status not queried)"
+msgstr " (статус не запитувався)"
+
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] "  Локальну гілку налаштовано на \"git pull\":"
+msgstr[1] "  Локальні гілки налаштовано на \"git pull\":"
+msgstr[2] "  Локальних гілок налаштовано на \"git pull\":"
+
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr "  Локальні посилання будуть віддзеркалені за допомогою \"git push\""
+
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] "  Локальне посилання налаштовано для \"git push\"%s:"
+msgstr[1] "  Локальних посилання налаштовано для \"git push\"%s:"
+msgstr[2] "  Локальних посилань налаштовано для \"git push\"%s:"
+
+msgid "set refs/remotes/<name>/HEAD according to remote"
+msgstr ""
+"встановити refs/remotes/<назва>/HEAD відповідно до віддаленого призначення"
+
+msgid "delete refs/remotes/<name>/HEAD"
+msgstr "видалити refs/remotes/<назва>/HEAD"
+
+msgid "Cannot determine remote HEAD"
+msgstr "Не вдалося визначити віддалений HEAD"
+
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr ""
+"Існує кілька віддалених HEAD гілок. Будь ласка, виберіть одну з них, "
+"скориставшись:"
+
+#, c-format
+msgid "Could not delete %s"
+msgstr "Не вдалося видалити %s"
+
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr "Не є припустимим посиланням: %s"
+
+#, c-format
+msgid "Could not setup %s"
+msgstr "Не вдалося налаштувати %s"
+
+#, c-format
+msgid " %s will become dangling!"
+msgstr " %s стануть висячими!"
+
+#, c-format
+msgid " %s has become dangling!"
+msgstr " %s став висячим!"
+
+#, c-format
+msgid "Pruning %s"
+msgstr "Видалення %s"
+
+#, c-format
+msgid "URL: %s"
+msgstr "URL-адреса: %s"
+
+#, c-format
+msgid " * [would prune] %s"
+msgstr " * [буде видалено] %s"
+
+#, c-format
+msgid " * [pruned] %s"
+msgstr " * [видалено] %s"
+
+msgid "prune remotes after fetching"
+msgstr "видалити віддалені призначення після отримання"
+
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Немає такого віддаленого призначення \"%s\""
+
+msgid "add branch"
+msgstr "додати гілку"
+
+msgid "no remote specified"
+msgstr "віддалене призначення не вказано"
+
+msgid "query push URLs rather than fetch URLs"
+msgstr "запитувати URL-адреси надсилань замість URL-адрес отримання"
+
+msgid "return all URLs"
+msgstr "повернути всі URL-адреси"
+
+#, c-format
+msgid "no URLs configured for remote '%s'"
+msgstr "не налаштовано URL-адреси для віддаленого \"%s\""
+
+msgid "manipulate push URLs"
+msgstr "маніпулювати URL-адресами надсилання"
+
+msgid "add URL"
+msgstr "додати URL-адресу"
+
+msgid "delete URLs"
+msgstr "видалити URL-адреси"
+
+msgid "--add --delete doesn't make sense"
+msgstr "--add --delete не має сенсу"
+
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr "Неприпустимий шаблон старої URL-адреси: %s"
+
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Такої URL-адреси не знайдено: %s"
+
+msgid "Will not delete all non-push URLs"
+msgstr "Не видалятиме всі URL-адреси, що не є призначенням надсилань"
+
+msgid "be verbose; must be placed before a subcommand"
+msgstr "розгорнутий вивід; має стояти перед підкомандою"
+
+msgid "git repack [<options>]"
+msgstr "git repack [<опції>]"
+
+msgid ""
+"Incremental repacks are incompatible with bitmap indexes.  Use\n"
+"--no-write-bitmap-index or disable the pack.writeBitmaps configuration."
+msgstr ""
+"Поступові перепакування несумісні з bitmap індексами.  Скористайтесь "
+"параметром\n"
+"--no-write-bitmap-index або вимкніть конфігурацію pack.writeBitmaps."
+
+msgid "could not start pack-objects to repack promisor objects"
+msgstr "не вдалося розпочати pack-objects для перепакування promisor обʼєктів"
+
+msgid "repack: Expecting full hex object ID lines only from pack-objects."
+msgstr ""
+"перепакування: очікуються повні рядки hex ідентифікаторів обʼєктів тільки "
+"від pack-objects."
+
+msgid "could not finish pack-objects to repack promisor objects"
+msgstr "не вдалося завершити pack-objects для перепакування promisor обʼєктів"
+
+#, c-format
+msgid "cannot open index for %s"
+msgstr "неможливо відкрити індекс для %s"
+
+#, c-format
+msgid "pack %s too large to consider in geometric progression"
+msgstr "пакунок %s занадто великий, щоб враховувати в геометричній прогресії"
+
+#, c-format
+msgid "pack %s too large to roll up"
+msgstr "пакунок %s занадто великий для згортання"
+
+#, c-format
+msgid "could not open tempfile %s for writing"
+msgstr "не вдалося відкрити тимчасовий файл %s для запису"
+
+msgid "could not close refs snapshot tempfile"
+msgstr "не вдалося закрити тимчасовий файл знімка посилань"
+
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "не вдалося видалити застарілий bitmap: %s"
+
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "префікс пакунку %s не починається з objdir %s"
+
+msgid "pack everything in a single pack"
+msgstr "запакувати все в один пакунок"
+
+msgid "same as -a, and turn unreachable objects loose"
+msgstr "те саме, що й -a, та звільняє недосяжні обʼєкти"
+
+msgid "same as -a, pack unreachable cruft objects separately"
+msgstr "те саме, що й -a, пакує недосяжні марні обʼєкти окремо"
+
+msgid "approxidate"
+msgstr "приблизна дата"
+
+msgid "with --cruft, expire objects older than this"
+msgstr "з --cruft видалити обʼєкти, старіші за цей термін"
+
+msgid "remove redundant packs, and run git-prune-packed"
+msgstr "видалити зайві пакунки і запустити git-prune-packed"
+
+msgid "pass --no-reuse-delta to git-pack-objects"
+msgstr "передати --no-reuse-delta до git-pack-objects"
+
+msgid "pass --no-reuse-object to git-pack-objects"
+msgstr "передати --no-reuse-object до git-pack-objects"
+
+msgid "do not run git-update-server-info"
+msgstr "не запускати git-update-server-info"
+
+msgid "pass --local to git-pack-objects"
+msgstr "передати --local до git-pack-objects"
+
+msgid "write bitmap index"
+msgstr "записати bitmap індекс"
+
+msgid "pass --delta-islands to git-pack-objects"
+msgstr "передати --delta-islands до git-pack-objects"
+
+msgid "with -A, do not loosen objects older than this"
+msgstr "з -A, не послабляти обʼєкти, старіші за це значення"
+
+msgid "with -a, repack unreachable objects"
+msgstr "з -a, перепакувати недоступні обʼєкти"
+
+msgid "size of the window used for delta compression"
+msgstr "розмір вікна, що використовується для дельта компресії"
+
+msgid "bytes"
+msgstr "байти"
+
+msgid "same as the above, but limit memory size instead of entries count"
+msgstr ""
+"те саме, що й вище, але обмежує розмір памʼяті замість кількості записів"
+
+msgid "limits the maximum delta depth"
+msgstr "обмежує максимальну глибину дельти"
+
+msgid "limits the maximum number of threads"
+msgstr "обмежує максимальну кількість потоків"
+
+msgid "maximum size of each packfile"
+msgstr "максимальний розмір кожного файла пакунка"
+
+msgid "repack objects in packs marked with .keep"
+msgstr "перепакувати обʼєкти в пакунках, позначених .keep"
+
+msgid "do not repack this pack"
+msgstr "не перепаковувати цей пакунок"
+
+msgid "find a geometric progression with factor <N>"
+msgstr "знайти геометричну прогресію з фактором <Н>"
+
+msgid "write a multi-pack index of the resulting packs"
+msgstr "записати multi-pack-index результуючих пакунків"
+
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "префікс пакунка для зберігання пакунка з обрізаними обʼєктами"
+
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "префікс для зберігання пакунка з відфільтрованими  обʼєктами"
+
+msgid "cannot delete packs in a precious-objects repo"
+msgstr "неможливо видалити пакунки в precious-objects сховищі"
+
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "опцію \"%s\" можна використовувати тільки разом з \"%s\""
+
+msgid "Nothing new to pack."
+msgstr "Немає нічого нового для пакування."
+
+#, c-format
+msgid "renaming pack to '%s' failed"
+msgstr "перейменування пакунка на \"%s\" завершилося невдало"
+
+#, c-format
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects не записав файл \"%s\" для пакунка %s-%s"
+
+#, c-format
+msgid "could not unlink: %s"
+msgstr "не вдалося видалити: %s"
+
+msgid "git replace [-f] <object> <replacement>"
+msgstr "git replace [-f] <обʼєкт> <заміна>"
+
+msgid "git replace [-f] --edit <object>"
+msgstr "git replace [-f] --edit <обʼєкт>"
+
+msgid "git replace [-f] --graft <commit> [<parent>...]"
+msgstr "git replace [-f] --graft <коміт> [<батько>...]"
+
+msgid "git replace -d <object>..."
+msgstr "git replace -d <обʼєкт>..."
+
+msgid "git replace [--format=<format>] [-l [<pattern>]]"
+msgstr "git replace [--format=<формат>] [-l [<шаблон>]]"
+
+#, c-format
+msgid ""
+"invalid replace format '%s'\n"
+"valid formats are 'short', 'medium' and 'long'"
+msgstr ""
+"невірний формат заміни \"%s\"\n"
+"допустимі формати \"short\", \"medium\" та \"long\""
+
+#, c-format
+msgid "replace ref '%s' not found"
+msgstr "заміна посилання \"%s\" не знайдена"
+
+#, c-format
+msgid "Deleted replace ref '%s'"
+msgstr "Видалена заміна посилання \"%s\""
+
+#, c-format
+msgid "'%s' is not a valid ref name"
+msgstr "\"%s\" не є припустимою назвою посилання"
+
+#, c-format
+msgid "replace ref '%s' already exists"
+msgstr "заміна посилання \"%s\" вже існує"
+
+#, c-format
+msgid ""
+"Objects must be of the same type.\n"
+"'%s' points to a replaced object of type '%s'\n"
+"while '%s' points to a replacement object of type '%s'."
+msgstr ""
+"Обʼєкти повинні бути одного типу.\n"
+"\"%s\" вказує на замінений обʼєкт типу \"%s\", \n"
+"тоді як \"%s\" вказує на обʼєкт заміни типу \"%s\"."
+
+#, c-format
+msgid "unable to open %s for writing"
+msgstr "не вдалося відкрити %s для запису"
+
+msgid "cat-file reported failure"
+msgstr "cat-file повідомила про збій"
+
+#, c-format
+msgid "unable to open %s for reading"
+msgstr "не вдалося відкрити %s для читання"
+
+msgid "unable to spawn mktree"
+msgstr "не вдалося породити mktree"
+
+msgid "unable to read from mktree"
+msgstr "не вдалося прочитати з mktree"
+
+msgid "mktree reported failure"
+msgstr "mktree повідомила про збій"
+
+msgid "mktree did not return an object name"
+msgstr "mktree не повернув назву обʼєкта"
+
+#, c-format
+msgid "unable to fstat %s"
+msgstr "не вдалося виконати fstat %s"
+
+msgid "unable to write object to database"
+msgstr "не вдалося записати обʼєкт до бази даних"
+
+#, c-format
+msgid "unable to get object type for %s"
+msgstr "не вдалося отримати тип обʼєкта для %s"
+
+msgid "editing object file failed"
+msgstr "редагування файла обʼєкта завершилося невдало"
+
+#, c-format
+msgid "new object is the same as the old one: '%s'"
+msgstr "новий обʼєкт такий самий, як і старий: \"%s\""
+
+#, c-format
+msgid "could not parse %s as a commit"
+msgstr "не вдалося розібрати %s як коміт"
+
+#, c-format
+msgid "bad mergetag in commit '%s'"
+msgstr "невірний mergetag в коміті \"%s\""
+
+#, c-format
+msgid "malformed mergetag in commit '%s'"
+msgstr "невірно сформований тег злиття у коміті \"%s\""
+
+#, c-format
+msgid ""
+"original commit '%s' contains mergetag '%s' that is discarded; use --edit "
+"instead of --graft"
+msgstr ""
+"початковий коміт \"%s\" містить тег злиття \"%s\", який було відкинуто; "
+"скористайтесь --edit замість --graft"
+
+#, c-format
+msgid "the original commit '%s' has a gpg signature"
+msgstr "оригінальний коміт \"%s\" має підпис gpg"
+
+msgid "the signature will be removed in the replacement commit!"
+msgstr "підпис буде видалено в коміті заміни!"
+
+#, c-format
+msgid "could not write replacement commit for: '%s'"
+msgstr "не вдалося записати коміт заміни для: \"%s\""
+
+#, c-format
+msgid "graft for '%s' unnecessary"
+msgstr "прищепа для \"%s\" не потрібна"
+
+#, c-format
+msgid "new commit is the same as the old one: '%s'"
+msgstr "новий коміт такий самий, як і старий: \"%s\""
+
+#, c-format
+msgid ""
+"could not convert the following graft(s):\n"
+"%s"
+msgstr ""
+"не вдалося конвертувати наступні прищепи:\n"
+"%s"
+
+msgid "list replace refs"
+msgstr "показати заміни посилань"
+
+msgid "delete replace refs"
+msgstr "видалити заміни посиланнь"
+
+msgid "edit existing object"
+msgstr "редагувати існуючий обʼєкт"
+
+msgid "change a commit's parents"
+msgstr "змінити батьків коміту"
+
+msgid "convert existing graft file"
+msgstr "конвертувати існуючий файл щеплення"
+
+msgid "replace the ref if it exists"
+msgstr "замінити посилання, якщо воно існує"
+
+msgid "do not pretty-print contents for --edit"
+msgstr "не прикрашати вивід вмісту для --edit"
+
+msgid "use this format"
+msgstr "використати цей формат"
+
+msgid "--format cannot be used when not listing"
+msgstr "--format не можна використовувати без list"
+
+msgid "-f only makes sense when writing a replacement"
+msgstr "-f має сенс тільки при записі заміни"
+
+msgid "--raw only makes sense with --edit"
+msgstr "--raw має сенс тільки з --edit"
+
+msgid "-d needs at least one argument"
+msgstr "-d потребує принаймні одного аргументу"
+
+msgid "bad number of arguments"
+msgstr "невірна кількість аргументів"
+
+msgid "-e needs exactly one argument"
+msgstr "-e потребує лишень один аргумент"
+
+msgid "-g needs at least one argument"
+msgstr "-g потребує принаймні одного аргументу"
+
+msgid "--convert-graft-file takes no argument"
+msgstr "--convert-graft-file не потребує аргументів"
+
+msgid "only one pattern can be given with -l"
+msgstr "тільки один шаблон може бути заданий з -l"
+
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <визначник-шляху>... | diff | status | remaining "
+"| gc]"
+
+msgid "register clean resolutions in index"
+msgstr "зареєструвати чисті вирішення в індексі"
+
+msgid "'git rerere forget' without paths is deprecated"
+msgstr "команда \"git rerere forget\" без шляхів застаріла"
+
+#, c-format
+msgid "unable to generate diff for '%s'"
+msgstr "не вдалося згенерувати diff для \"%s\""
+
+msgid ""
+"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
+msgstr ""
+"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
+
+msgid "git reset [-q] [<tree-ish>] [--] <pathspec>..."
+msgstr "git reset [-q] [<деревоподібне-джерело>] [--] <визначник шляху>..."
+
+msgid ""
+"git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"
+msgstr ""
+"git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<деревоподібне-"
+"джерело>]"
+
+msgid "git reset --patch [<tree-ish>] [--] [<pathspec>...]"
+msgstr ""
+"git reset --patch [<деревоподібне-джерело>] [--] [<визначник шляху>...]"
+
+msgid "mixed"
+msgstr "змішане"
+
+msgid "soft"
+msgstr "м’яке"
+
+msgid "hard"
+msgstr "жорстке"
+
+msgid "merge"
+msgstr "злити"
+
+msgid "keep"
+msgstr "зберегти"
+
+msgid "You do not have a valid HEAD."
+msgstr "У вас немає дійсного HEAD."
+
+msgid "Failed to find tree of HEAD."
+msgstr "Не вдалося знайти дерево HEAD."
+
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr "Не вдалося знайти дерево %s."
+
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "HEAD зараз на %s"
+
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr "Неможливо виконати %s скидання посеред злиття."
+
+msgid "be quiet, only report errors"
+msgstr "тихесенько, повідомляти лише про помилки"
+
+msgid "skip refreshing the index after reset"
+msgstr "пропустити оновлення індексу після скидання"
+
+msgid "reset HEAD and index"
+msgstr "скинути HEAD та індекс"
+
+msgid "reset only HEAD"
+msgstr "скинути тільки HEAD"
+
+msgid "reset HEAD, index and working tree"
+msgstr "скинути HEAD, індекс та робоче дерево"
+
+msgid "reset HEAD but keep local changes"
+msgstr "скинути HEAD, але зберегти локальні зміни"
+
+msgid "record only the fact that removed paths will be added later"
+msgstr "записати лише той факт, що вилучені шляхи будуть додані пізніше"
+
+#, c-format
+msgid "Failed to resolve '%s' as a valid revision."
+msgstr "Не вдалося розпізнати '%s' як припустиму ревізію."
+
+#, c-format
+msgid "Failed to resolve '%s' as a valid tree."
+msgstr "Не вдалося розпізнати '%s' як припустиме дерево."
+
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+"використання --mixed зі шляхами застаріло; використовуйте 'git reset -- "
+"<paths>' замість цього."
+
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr "Неможливо виконати %s скидання зі шляхами."
+
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "%s скидання не дозволяється у порожньому сховищі"
+
+msgid "Unstaged changes after reset:"
+msgstr "Неіндексовані зміни після скидання:"
+
+#, c-format
+msgid ""
+"It took %.2f seconds to refresh the index after reset.  You can use\n"
+"'--no-refresh' to avoid this."
+msgstr ""
+"Оновлення індексу після скидання зайняло %.2f секунд.  Ви можете "
+"скористатися параметром\n"
+"'--no-refresh', щоб уникнути цього."
+
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr "Не вдалося скинути індексний файл до ревізії '%s'."
+
+msgid "Could not write new index file."
+msgstr "Не вдалося записати новий індексний файл."
+
+#, c-format
+msgid "unable to get disk usage of %s"
+msgstr "не вдалося отримати дані про використання диска %s"
+
+#, c-format
+msgid "invalid value for '%s': '%s', the only allowed format is '%s'"
+msgstr ""
+"неприпустиме значення для \"%s\": \"%s\", єдиним допустимим форматом є \"%s\""
+
+msgid "rev-list does not support display of notes"
+msgstr "rev-list не підтримує відображення нотаток"
+
+#, c-format
+msgid "marked counting and '%s' cannot be used together"
+msgstr "позначений підрахунок та \"%s\" не можна використовувати разом"
+
+msgid "git rev-parse --parseopt [<options>] -- [<args>...]"
+msgstr "git rev-parse --parseopt [<опції>] -- [<аргументи>...]"
+
+msgid "keep the `--` passed as an arg"
+msgstr "залишити \"--\" переданим як аргумент"
+
+msgid "stop parsing after the first non-option argument"
+msgstr "зупинити розбір після першого неопціонального аргументу"
+
+msgid "output in stuck long form"
+msgstr "виведення в застряглій довгій формі"
+
+msgid "premature end of input"
+msgstr "передчасне закінчення вхідних даних"
+
+msgid "no usage string given before the `--' separator"
+msgstr "не вказана строка використання перед \"--\" розділювачем"
+
+msgid "missing opt-spec before option flags"
+msgstr "відсутній opt-spec перед прапорцями опції"
+
+msgid "Needed a single revision"
+msgstr "Необхідна одна ревізія"
+
+msgid ""
+"git rev-parse --parseopt [<options>] -- [<args>...]\n"
+"   or: git rev-parse --sq-quote [<arg>...]\n"
+"   or: git rev-parse [<options>] [<arg>...]\n"
+"\n"
+"Run \"git rev-parse --parseopt -h\" for more information on the first usage."
+msgstr ""
+"git rev-parse --parseopt [<опції>] -- [<аргументи>...]\n"
+"   або: git rev-parse --sq-quote [<аргумент>...]\n"
+"   або: git rev-parse [<опції>] [<аргумент>...]\n"
+"\n"
+"Запустіть \"git rev-parse --parseopt -h\" для отримання додаткової "
+"інформації про перше використання."
+
+msgid "--resolve-git-dir requires an argument"
+msgstr "--resolve-git-dir потребує аргументу"
+
+#, c-format
+msgid "not a gitdir '%s'"
+msgstr "не є git директорією \"%s\""
+
+msgid "--git-path requires an argument"
+msgstr "--git-path потребує аргументу"
+
+msgid "-n requires an argument"
+msgstr "-n потребує аргументу"
+
+msgid "--path-format requires an argument"
+msgstr "--path-format потребує аргументу"
+
+#, c-format
+msgid "unknown argument to --path-format: %s"
+msgstr "невідомий аргумент до --path-format: %s"
+
+msgid "--default requires an argument"
+msgstr "--default потребує аргументу"
+
+msgid "--prefix requires an argument"
+msgstr "--prefix потребує аргументу"
+
+#, c-format
+msgid "unknown mode for --abbrev-ref: %s"
+msgstr "невідомий режим для --abbrev-ref: %s"
+
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden не можна використовувати разом з --branches"
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden неможливо використовувати разом з --tags"
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden неможливо використовувати разом з --remotes"
+
+msgid "this operation must be run in a work tree"
+msgstr "цю операцію треба виконувати в робочому дереві"
+
+msgid "Could not read the index"
+msgstr "Не вдалося прочитати індекс"
+
+#, c-format
+msgid "unknown mode for --show-object-format: %s"
+msgstr "невідомий режим для --show-object-format: %s"
+
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <номер-батька>] [-s] [-S[<ідентифікатор-"
+"ключа>]] <коміт>..."
+
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
+
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <номер-батька>] [-s] [-x] [--ff].\n"
+"                [-S[<ідентифікатор-ключа>]] <коміт>..."
+
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
+
+#, c-format
+msgid "option `%s' expects a number greater than zero"
+msgstr "опція \"%s\" очікує число більше нуля"
+
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s: %s неможливо використовувати з %s"
+
+msgid "end revert or cherry-pick sequence"
+msgstr "завершити процес вивертання або висмикування"
+
+msgid "resume revert or cherry-pick sequence"
+msgstr "відновити процес вивертання або висмикування"
+
+msgid "cancel revert or cherry-pick sequence"
+msgstr "скасувати процес вивертання або висмикування"
+
+msgid "skip current commit and continue"
+msgstr "пропустити поточний коміт і продовжити"
+
+msgid "don't automatically commit"
+msgstr "не комітити автоматично"
+
+msgid "edit the commit message"
+msgstr "редагувати допис до коміту"
+
+msgid "parent-number"
+msgstr "номер батька"
+
+msgid "select mainline parent"
+msgstr "вибрати основну батьківську лінію"
+
+msgid "merge strategy"
+msgstr "стратегія злиття"
+
+msgid "option for merge strategy"
+msgstr "опція для стратегії злиття"
+
+msgid "append commit name"
+msgstr "додати назву коміту"
+
+msgid "preserve initially empty commits"
+msgstr "зберігати первинно порожні коміти"
+
+msgid "allow commits with empty messages"
+msgstr "дозволити коміти з порожніми дописами"
+
+msgid "keep redundant, empty commits"
+msgstr "зберігати зайві порожні коміти"
+
+msgid "use the 'reference' format to refer to commits"
+msgstr "використовувати \"reference\" формат для посилань на коміти"
+
+msgid "revert failed"
+msgstr "вивертання не вдалося"
+
+msgid "cherry-pick failed"
+msgstr "висмикування не вдалося"
+
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--ignore-"
+"unmatch\n"
+"       [--quiet] [--pathspec-from-file=<файл> [--pathspec-file-nul]]\n"
+"       [--] [<визначник-шляху>...]"
+
+msgid ""
+"the following file has staged content different from both the\n"
+"file and the HEAD:"
+msgid_plural ""
+"the following files have staged content different from both the\n"
+"file and the HEAD:"
+msgstr[0] ""
+"наступний файл має доданий до індексу вміст, відмінний як від файла\n"
+"так й від HEAD:"
+msgstr[1] ""
+"наступні файли мають доданий до індексу вміст, відмінний як від файла\n"
+"так й від HEAD:"
+msgstr[2] ""
+"наступні файли мають доданий до індексу вміст, відмінний як від файла\n"
+"так й від HEAD:"
+
+msgid ""
+"\n"
+"(use -f to force removal)"
+msgstr ""
+"\n"
+"(використовуйте -f щоб видалити примусово)"
+
+msgid "the following file has changes staged in the index:"
+msgid_plural "the following files have changes staged in the index:"
+msgstr[0] "у наступному файлі є зміни, додані до індексу:"
+msgstr[1] "у наступних файлах є зміни, додані до індексу:"
+msgstr[2] "у наступних файлах є зміни, додані до індексу:"
+
+msgid ""
+"\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"\n"
+"(використовуйте --cached, щоб зберегти файл, або -f, щоб видалити примусово)"
+
+msgid "the following file has local modifications:"
+msgid_plural "the following files have local modifications:"
+msgstr[0] "наступний файл має локальні зміни:"
+msgstr[1] "наступні файли мають локальні зміни:"
+msgstr[2] "наступних файлів мають локальні зміни:"
+
+msgid "do not list removed files"
+msgstr "не показувати видалені файли"
+
+msgid "only remove from the index"
+msgstr "видалити тільки з індексу"
+
+msgid "override the up-to-date check"
+msgstr "перевизначити перевірку на актуальність"
+
+msgid "allow recursive removal"
+msgstr "дозволити рекурсивне видалення"
+
+msgid "exit with a zero status even if nothing matched"
+msgstr "виходити з нульовим статусом, навіть якщо нічого не збігається"
+
+msgid "No pathspec was given. Which files should I remove?"
+msgstr "Не було вказано визначник шляху. Які файли слід видалити?"
+
+msgid "please stage your changes to .gitmodules or stash them to proceed"
+msgstr ""
+"будь ласка, додайте ваші зміни до .gitmodules або схову, щоб продовжити"
+
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr "не видалено \"%s\" рекурсивно без -r"
+
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm: не вдалося видалити %s"
+
+msgid ""
+"git send-pack [--mirror] [--dry-run] [--force]\n"
+"              [--receive-pack=<git-receive-pack>]\n"
+"              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
+"              [<host>:]<directory> (--all | <ref>...)"
+msgstr ""
+"git send-pack [--mirror] [--dry-run] [--force]\n"
+"              [--receive-pack=<git-пакунок-отримання>]\n"
+"              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
+"              [<хост>:]<директорія> (--all | <посилання>...)"
+
+msgid "remote name"
+msgstr "віддалена назва"
+
+msgid "push all refs"
+msgstr "надіслати всі посилання"
+
+msgid "use stateless RPC protocol"
+msgstr "використовувати протокол RPC без збереження стану"
+
+msgid "read refs from stdin"
+msgstr "прочитати посилання з stdin"
+
+msgid "print status from remote helper"
+msgstr "вивести статус з віддаленого помічника"
+
+msgid "git shortlog [<options>] [<revision-range>] [[--] <path>...]"
+msgstr "git shortlog [<опції>] [<діапазон-ревізій>] [[--] <шлях>...]"
+
+msgid "git log --pretty=short | git shortlog [<options>]"
+msgstr "git log --pretty=short | git shortlog [<опції>]"
+
+msgid "using multiple --group options with stdin is not supported"
+msgstr "використання кількох --group опцій з stdin не підтримується"
+
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "використання %s з stdin не підтримується"
+
+#, c-format
+msgid "unknown group type: %s"
+msgstr "невідомий тип групи: %s"
+
+msgid "group by committer rather than author"
+msgstr "групувати за комітером, а не за автором"
+
+msgid "sort output according to the number of commits per author"
+msgstr "сортувати виведення за кількістю комітів на автора"
+
+msgid "suppress commit descriptions, only provides commit count"
+msgstr "приховати описи комітів, показати лише кількість комітів"
+
+msgid "show the email address of each author"
+msgstr "показати адресу електронної пошти кожного автора"
+
+msgid "<w>[,<i1>[,<i2>]]"
+msgstr "<w>[,<i1>[,<i2>]]"
+
+msgid "linewrap output"
+msgstr "обгортати рядки виводу"
+
+msgid "field"
+msgstr "поле"
+
+msgid "group by field"
+msgstr "групувати за полем"
+
+msgid "too many arguments given outside repository"
+msgstr "занадто багато аргументів надано поза сховищем"
+
+msgid ""
+"git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
+"                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
+"                [--more=<n> | --list | --independent | --merge-base]\n"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
+msgstr ""
+"git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
+"                [--current] [--color[=<коли>] | --no-color] [--sparse]\n"
+"                [--more=<н> | --list | --independent | --merge-base]\n"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<ревізія> | <глоб>)...]"
+
+msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
+msgstr "git show-branch (-g | --reflog)[=<н>[,<база>]] [--list] [<посилання>]"
+
+#, c-format
+msgid "ignoring %s; cannot handle more than %d ref"
+msgid_plural "ignoring %s; cannot handle more than %d refs"
+msgstr[0] "ігнорування %s; неможливо обробити більше %d посилання"
+msgstr[1] "ігнорування %s; неможливо обробити більше %d посилань"
+msgstr[2] "ігнорування %s; неможливо обробити більше %d посилань"
+
+#, c-format
+msgid "no matching refs with %s"
+msgstr "немає співпадаючих посилань з %s"
+
+msgid "show remote-tracking and local branches"
+msgstr "показати віддалено відстежувані та локальні гілки"
+
+msgid "show remote-tracking branches"
+msgstr "показати віддалено відстежувані гілки"
+
+msgid "color '*!+-' corresponding to the branch"
+msgstr "колір \"*!+-\" відповідно на гілку"
+
+msgid "show <n> more commits after the common ancestor"
+msgstr "показати ще <н> комітів після спільного предка"
+
+msgid "synonym to more=-1"
+msgstr "синонім до more=-1"
+
+msgid "suppress naming strings"
+msgstr "не показувати назву"
+
+msgid "include the current branch"
+msgstr "включити поточну гілку"
+
+msgid "name commits with their object names"
+msgstr "називати коміти за іменами обʼєктів"
+
+msgid "show possible merge bases"
+msgstr "показати можливі бази злиття"
+
+msgid "show refs unreachable from any other ref"
+msgstr "показати посилання, недосяжні з жодного іншого посилання"
+
+msgid "show commits in topological order"
+msgstr "показати коміти в топологічному порядку"
+
+msgid "show only commits not on the first branch"
+msgstr "показати лише коміти не з найпершої гілки"
+
+msgid "show merges reachable from only one tip"
+msgstr "показати злиття досяжні лише з однієї верхівки"
+
+msgid "topologically sort, maintaining date order where possible"
+msgstr "сортувати топологічно, зберігаючи порядок дат, якщо це можливо"
+
+msgid "<n>[,<base>]"
+msgstr "<н>[,<база>]"
+
+msgid "show <n> most recent ref-log entries starting at base"
+msgstr "показати <н> останніх записів лога посилань, починаючи з бази"
+
+msgid "no branches given, and HEAD is not valid"
+msgstr "не надано гілок, і HEAD не є дійсним"
+
+msgid "--reflog option needs one branch name"
+msgstr "--reflog потребує одну назву гілки"
+
+#, c-format
+msgid "only %d entry can be shown at one time."
+msgid_plural "only %d entries can be shown at one time."
+msgstr[0] "одночасно може бути показаний лише %d запис."
+msgstr[1] "одночасно можуть бути показані лише %d записи."
+msgstr[2] "одночасно можуть бути показані лише %d записів."
+
+#, c-format
+msgid "no such ref %s"
+msgstr "немає такого посилання %s"
+
+#, c-format
+msgid "cannot handle more than %d rev."
+msgid_plural "cannot handle more than %d revs."
+msgstr[0] "неможливо обробити більше %d ревізії."
+msgstr[1] "неможливо обробити більше %d ревізій."
+msgstr[2] "неможливо обробити більше %d ревізій."
+
+#, c-format
+msgid "'%s' is not a valid ref."
+msgstr "\"%s\" не є припустимим посиланням."
+
+#, c-format
+msgid "cannot find commit %s (%s)"
+msgstr "не вдалося знайти коміт %s (%s)"
+
+msgid "hash-algorithm"
+msgstr "хеш-алгоритм"
+
+msgid "Unknown hash algorithm"
+msgstr "Невідомий хеш-алгоритм"
+
+msgid ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<н>]] [--abbrev[=<н>]] [--tags]\n"
+"             [--heads] [--] [<шаблон>...]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<н>]] [--abbrev[=<н>]]\n"
+"             [--] [<посилання>...]"
+
+msgid "git show-ref --exclude-existing[=<pattern>]"
+msgstr "git show-ref --exclude-existing[=<шаблон>]"
+
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <посилання>"
+
+msgid "reference does not exist"
+msgstr "посилання не існує"
+
+msgid "failed to look up reference"
+msgstr "не вдалося знайти посилання"
+
+msgid "only show tags (can be combined with heads)"
+msgstr "показати тільки теги (можна комбінувати з верхівками)"
+
+msgid "only show heads (can be combined with tags)"
+msgstr "показати тільки верхівки (можна комбінувати з тегами)"
+
+msgid "check for reference existence without resolving"
+msgstr "перевіряти наявність посилання без розвʼязання"
+
+msgid "stricter reference checking, requires exact ref path"
+msgstr "більш сувора перевірка посилань, потребує точного шляху до посилання"
+
+msgid "show the HEAD reference, even if it would be filtered out"
+msgstr "показати HEAD посилання, навіть якщо воно було б відфільтроване"
+
+msgid "dereference tags into object IDs"
+msgstr "розіменувати теги в ідентифікатори обʼєктів"
+
+msgid "only show SHA1 hash using <n> digits"
+msgstr "показати тільки SHA1 хеш з використанням <н> цифр"
+
+msgid "do not print results to stdout (useful with --verify)"
+msgstr "не виводити результати у stdout (корисно з --verify)"
+
+msgid "show refs from stdin that aren't in local repository"
+msgstr "показати посилання з stdin, яких немає в локальному сховищі"
+
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "можна вказати тільки один з \"%s\", \"%s\" або \"%s\""
+
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<опції>]"
+
+msgid "this worktree is not sparse"
+msgstr "це робоче дерево не є розрідженим"
+
+msgid "this worktree is not sparse (sparse-checkout file may not exist)"
+msgstr ""
+"це робоче дерево не є розрідженим (файл розрідженого переходу може не "
+"існувати)"
+
+#, c-format
+msgid ""
+"directory '%s' contains untracked files, but is not in the sparse-checkout "
+"cone"
+msgstr ""
+"директорія \"%s\" містить невідстежувані файли, але не входить до конуса "
+"розрідженого переходу"
+
+#, c-format
+msgid "failed to remove directory '%s'"
+msgstr "не вдалося видалити директорію \"%s\""
+
+msgid "failed to create directory for sparse-checkout file"
+msgstr "не вдалося створити директорію для файлу розрідженого переходу"
+
+msgid "failed to initialize worktree config"
+msgstr "не вдалося ініціалізувати конфігурацію робочого дерева"
+
+msgid "failed to modify sparse-index config"
+msgstr "не вдалося змінити sparse-index конфігурацію"
+
+msgid "initialize the sparse-checkout in cone mode"
+msgstr "ініціалізувати розріджений перехід в режимі конуса"
+
+msgid "toggle the use of a sparse index"
+msgstr "перемкнути використання розрідженого індексу"
+
+#, c-format
+msgid "unable to create leading directories of %s"
+msgstr "не вдалося створити провідні директорії %s"
+
+#, c-format
+msgid "failed to open '%s'"
+msgstr "не вдалося відкрити \"%s\""
+
+#, c-format
+msgid "could not normalize path %s"
+msgstr "не вдалося нормалізувати шлях %s"
+
+#, c-format
+msgid "unable to unquote C-style string '%s'"
+msgstr "неможливо прибрати лапки з C-style строки \"%s\""
+
+msgid "unable to load existing sparse-checkout patterns"
+msgstr "не вдалося завантажити існуючі шаблони розріджених переходів"
+
+msgid "existing sparse-checkout patterns do not use cone mode"
+msgstr "існуючі шаблони розрідженого переходу не використовують режим конуса"
+
+msgid "please run from the toplevel directory in non-cone mode"
+msgstr ""
+"будь ласка, запускайте з директорії верхнього рівня в не конусномі режимі"
+
+msgid "specify directories rather than patterns (no leading slash)"
+msgstr "вказати директорії замість шаблонів (без першого слешу)"
+
+msgid ""
+"specify directories rather than patterns.  If your directory starts with a "
+"'!', pass --skip-checks"
+msgstr ""
+"вказати директорії замість шаблонів.  Якщо ваша директорія починається з \"!"
+"\", додайте --skip-checks"
+
+msgid ""
+"specify directories rather than patterns.  If your directory really has any "
+"of '*?[]\\' in it, pass --skip-checks"
+msgstr ""
+"вказати директорії замість шаблонів.  Якщо в назві вашої директорії дійсно є "
+"символи \"*?[]\\\", додайте --skip-checks"
+
+#, c-format
+msgid ""
+"'%s' is not a directory; to treat it as a directory anyway, rerun with --"
+"skip-checks"
+msgstr ""
+"\"%s\" не є директорією; щоб вважати його директорією, повторіть запуск з --"
+"skip-checks"
+
+#, c-format
+msgid ""
+"pass a leading slash before paths such as '%s' if you want a single file "
+"(see NON-CONE PROBLEMS in the git-sparse-checkout manual)."
+msgstr ""
+"додайте перший слеш перед такими шляхами, як \"%s\", якщо вам потрібен один "
+"файл (див. розділ НЕ КОНУСНІ ПРОБЛЕМИ у посібнику з git-sparse-checkout)."
+
+msgid "git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"
+msgstr "git sparse-checkout add [--skip-checks] (--stdin | <шаблони>)"
+
+msgid ""
+"skip some sanity checks on the given paths that might give false positives"
+msgstr ""
+"пропустити деякі перевірки на заданих шляхах, які можуть давати хибні "
+"результати"
+
+msgid "read patterns from standard in"
+msgstr "читати шаблони зі стандартного вводу"
+
+msgid "no sparse-checkout to add to"
+msgstr "немає розрідженого переходу для додавання"
+
+msgid ""
+"git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] [--skip-checks] "
+"(--stdin | <patterns>)"
+msgstr ""
+"git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] [--skip-checks] "
+"(--stdin | <шаблони>)"
+
+msgid "must be in a sparse-checkout to reapply sparsity patterns"
+msgstr ""
+"має перебувати в розрідженому переході для повторного застосування шаблонів "
+"розрідженості"
+
+msgid "error while refreshing working directory"
+msgstr "помилка під час оновлення робочої директорії"
+
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <файл>]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr "завершити вхідні та вихідні файли символом NUL"
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"при використанні з --rules-file інтерпретувати шаблони як шаблони конусного "
+"режиму"
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr "використовувати шаблони з <file> замість поточних."
+
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<лог-опції>]"
+
+msgid ""
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
+msgstr ""
+"git stash show [-u | --include-untracked | --only-untracked] [<опції-"
+"різниці>] [<схов>]"
+
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<схов>]"
+
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<схов>]"
+
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<схов>]"
+
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <назва-гілки> [<схов>]"
+
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr "git stash store [(-m | --message) <допис>] [-q | --quiet] <коміт>"
+
+msgid ""
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"          [--] [<pathspec>...]]"
+msgstr ""
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<допис>]\n"
+"          [--pathspec-from-file=<файл> [--pathspec-file-nul]]\n"
+"          [--] [<визначник-шляху>...]]"
+
+msgid ""
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
+msgstr ""
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<допис>]"
+
+msgid "git stash create [<message>]"
+msgstr "git stash create [<допис>]"
+
+#, c-format
+msgid "'%s' is not a stash-like commit"
+msgstr "\"%s\" не є сховоподібним комітом"
+
+#, c-format
+msgid "Too many revisions specified:%s"
+msgstr "Вказано забагато ревізій:%s"
+
+msgid "No stash entries found."
+msgstr "Записи схова не знайдені."
+
+#, c-format
+msgid "%s is not a valid reference"
+msgstr "%s не є припустимим посиланням"
+
+msgid "git stash clear with arguments is unimplemented"
+msgstr "git stash clear з аргументами не реалізовано"
+
+#, c-format
+msgid ""
+"WARNING: Untracked file in way of tracked file!  Renaming\n"
+"            %s -> %s\n"
+"         to make room.\n"
+msgstr ""
+"ПОПЕРЕДЖЕННЯ: Невідстежуваний файл на шляху відстежуваного!  Перейменування\n"
+"            %s -> %s\n"
+"         щоб звільнити місце.\n"
+
+msgid "cannot apply a stash in the middle of a merge"
+msgstr "неможливо застосувати схов посеред злиття"
+
+#, c-format
+msgid "could not generate diff %s^!."
+msgstr "не вдалося згенерувати різницю %s^!."
+
+msgid "conflicts in index. Try without --index."
+msgstr "конфлікти в індексі. Спробуйте без --index."
+
+msgid "could not save index tree"
+msgstr "не вдалося зберегти дерево індекса"
+
+#, c-format
+msgid "Merging %s with %s"
+msgstr "Злиття %s з %s"
+
+msgid "Index was not unstashed."
+msgstr "Індекс не було вилучено зі схову."
+
+msgid "could not restore untracked files from stash"
+msgstr "не вдалося відновити невідстежувані файли зі схову"
+
+msgid "attempt to recreate the index"
+msgstr "спроба відтворити індекс"
+
+#, c-format
+msgid "Dropped %s (%s)"
+msgstr "Скинуто %s (%s)"
+
+#, c-format
+msgid "%s: Could not drop stash entry"
+msgstr "%s: Не вдалося скинути запис схову"
+
+#, c-format
+msgid "'%s' is not a stash reference"
+msgstr "\"%s\" не є посиланням схова"
+
+msgid "The stash entry is kept in case you need it again."
+msgstr "Запис схову збережено на випадок, якщо він вам знову знадобиться."
+
+msgid "No branch name specified"
+msgstr "Не вказана назва гілки"
+
+msgid "failed to parse tree"
+msgstr "не вдалося розібрати дерево"
+
+msgid "failed to unpack trees"
+msgstr "не вдалося розпакувати дерева"
+
+msgid "include untracked files in the stash"
+msgstr "в тому числі невідстежувані файли схову"
+
+msgid "only show untracked files in the stash"
+msgstr "показувати тільки невідстежувані файли схову"
+
+#, c-format
+msgid "Cannot update %s with %s"
+msgstr "Неможливо оновити %s з %s"
+
+msgid "stash message"
+msgstr "допис до запису схова"
+
+msgid "\"git stash store\" requires one <commit> argument"
+msgstr "\"git stash store\" потребує одного <коміт> аргументу"
+
+msgid "No staged changes"
+msgstr "Немає індексованих змін"
+
+msgid "No changes selected"
+msgstr "Не обрано жодних змін"
+
+msgid "You do not have the initial commit yet"
+msgstr "У вас ще немає початкового коміту"
+
+msgid "Cannot save the current index state"
+msgstr "Неможливо зберегти поточний стан індексу"
+
+msgid "Cannot save the untracked files"
+msgstr "Неможливо зберегти невідстежувані файли"
+
+msgid "Cannot save the current worktree state"
+msgstr "Не вдалося зберегти поточний стан робочого дерева"
+
+msgid "Cannot save the current staged state"
+msgstr "Неможливо зберегти поточний індексований стан"
+
+msgid "Cannot record working tree state"
+msgstr "Неможливо записати стан робочого дерева"
+
+msgid "Can't use --patch and --include-untracked or --all at the same time"
+msgstr ""
+"Не можна використовувати --patch і --include-untracked або --all одночасно"
+
+msgid "Can't use --staged and --include-untracked or --all at the same time"
+msgstr ""
+"Не можна використовувати --staged і --include-untracked або --all одночасно"
+
+msgid "Did you forget to 'git add'?"
+msgstr "Ви забули \"git add\"?"
+
+msgid "No local changes to save"
+msgstr "Немає локальних змін для збереження"
+
+msgid "Cannot initialize stash"
+msgstr "Неможливо ініціалізувати схов"
+
+msgid "Cannot save the current status"
+msgstr "Неможливо зберегти поточний стан"
+
+#, c-format
+msgid "Saved working directory and index state %s"
+msgstr "Збережено робочу директорію та стан індексу %s"
+
+msgid "Cannot remove worktree changes"
+msgstr "Неможливо видалити зміни робочого дерева"
+
+msgid "keep index"
+msgstr "зберегти індекс"
+
+msgid "stash staged changes only"
+msgstr "додати до схову тільки індексовані зміни"
+
+msgid "stash in patch mode"
+msgstr "додати до схову у режимі латання"
+
+msgid "quiet mode"
+msgstr "тихий режим"
+
+msgid "include untracked files in stash"
+msgstr "в тому числі невідстежувані файли схову"
+
+msgid "include ignore files"
+msgstr "в тому числи файли ігнорування"
+
+msgid "skip and remove all lines starting with comment character"
+msgstr "пропустити та видалити всі рядки, що починаються з символу коментаря"
+
+msgid "prepend comment character and space to each line"
+msgstr "додати символ коментаря та пробіл до кожного рядка"
+
+#, c-format
+msgid "Expecting a full ref name, got %s"
+msgstr "Очікувалась повна назва посилання, отримано %s"
+
+#, c-format
+msgid "could not get a repository handle for submodule '%s'"
+msgstr "не вдалося отримати обʼєкт сховища для підмодуля \"%s\""
+
+#, c-format
+msgid ""
+"could not look up configuration '%s'. Assuming this repository is its own "
+"authoritative upstream."
+msgstr ""
+"не вдалося знайти конфігурацію \"%s\". Мабуть, це сховище має власне "
+"першоджерельне сховище."
+
+#, c-format
+msgid "No url found for submodule path '%s' in .gitmodules"
+msgstr "Не знайдено URL для шляху підмодуля \"%s\" у .gitmodules"
+
+#, c-format
+msgid "Entering '%s'\n"
+msgstr "Введення \"%s\"\n"
+
+#, c-format
+msgid ""
+"run_command returned non-zero status for %s\n"
+"."
+msgstr ""
+"run_command повернула ненульовий статус для %s\n"
+"."
+
+#, c-format
+msgid ""
+"run_command returned non-zero status while recursing in the nested "
+"submodules of %s\n"
+"."
+msgstr ""
+"run_command повернула ненульовий статус під час рекурсії у вкладених "
+"підмодулях %s\n"
+"."
+
+msgid "suppress output of entering each submodule command"
+msgstr "приховати вивід введення кожної команди підмодуля"
+
+msgid "recurse into nested submodules"
+msgstr "рекурсивно у вкладених підмодулях"
+
+msgid "git submodule foreach [--quiet] [--recursive] [--] <command>"
+msgstr "git submodule foreach [--quiet] [--recursive] [--] <команда>"
+
+#, c-format
+msgid "Failed to register url for submodule path '%s'"
+msgstr "Не вдалося зареєструвати url для підмодуля за шляхом \"%s\""
+
+#, c-format
+msgid "Submodule '%s' (%s) registered for path '%s'\n"
+msgstr "Підмодуль \"%s\" (%s) зареєстровано для шляху \"%s\"\n"
+
+#, c-format
+msgid "warning: command update mode suggested for submodule '%s'\n"
+msgstr ""
+"попередження: запропоновано режим оновлення команд для підмодуля \"%s\"\n"
+
+#, c-format
+msgid "Failed to register update mode for submodule path '%s'"
+msgstr ""
+"Не вдалося зареєструвати режим оновлення для підмодуля за шляхом \"%s\""
+
+msgid "suppress output for initializing a submodule"
+msgstr "приховати вивід для ініціалізації підмодуля"
+
+msgid "git submodule init [<options>] [<path>]"
+msgstr "git submodule init [<опції>] [<шлях>]"
+
+#, c-format
+msgid "no submodule mapping found in .gitmodules for path '%s'"
+msgstr ""
+"відповідне відображення підмодуля не знайдено у .gitmodules для шляху \"%s\""
+
+#, c-format
+msgid "could not resolve HEAD ref inside the submodule '%s'"
+msgstr "не вдалося розвʼязати HEAD посилання всередині підмодуля \"%s\""
+
+#, c-format
+msgid "failed to recurse into submodule '%s'"
+msgstr "не вдалося обробити рекурсивно підмодуль \"%s\""
+
+msgid "suppress submodule status output"
+msgstr "приховати вивід стану підмодуля"
+
+msgid ""
+"use commit stored in the index instead of the one stored in the submodule "
+"HEAD"
+msgstr ""
+"використати коміт, що зберігається в індексі, замість того, що зберігається "
+"в HEAD підмодуля"
+
+msgid "git submodule status [--quiet] [--cached] [--recursive] [<path>...]"
+msgstr "git submodule status [--quiet] [--cached] [--recursive] [<шлях>...]"
+
+#, c-format
+msgid "* %s %s(blob)->%s(submodule)"
+msgstr "* %s %s(blob)->%s(підмодуль)"
+
+#, c-format
+msgid "* %s %s(submodule)->%s(blob)"
+msgstr "* %s %s(підмодуль)->%s(blob)"
+
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#, c-format
+msgid "couldn't hash object from '%s'"
+msgstr "не вдалося хешувати обʼєкт з \"%s\""
+
+#, c-format
+msgid "unexpected mode %o\n"
+msgstr "неочікуваний режим %o\n"
+
+msgid "use the commit stored in the index instead of the submodule HEAD"
+msgstr "використати коміт, збережений в індексі, замість підмодуля HEAD"
+
+msgid "compare the commit in the index with that in the submodule HEAD"
+msgstr "порівняти коміт в індексі з комітом у HEAD підмодуля"
+
+msgid "skip submodules with 'ignore_config' value set to 'all'"
+msgstr ""
+"пропускати підмодулі зі значенням \"ignore_config\" встановленим у \"all\""
+
+msgid "limit the summary size"
+msgstr "обмежити розмір підсумку"
+
+msgid "git submodule summary [<options>] [<commit>] [--] [<path>]"
+msgstr "git summary submodule [<опції>] [<коміт>] [--] [<шлях>]"
+
+msgid "could not fetch a revision for HEAD"
+msgstr "не вдалося отримати ревізію для HEAD"
+
+#, c-format
+msgid "Synchronizing submodule url for '%s'\n"
+msgstr "Синхронізація url підмодуля для \"%s\"\n"
+
+#, c-format
+msgid "failed to register url for submodule path '%s'"
+msgstr "не вдалося зареєструвати url для підмодуля за шляхом \"%s\""
+
+#, c-format
+msgid "failed to update remote for submodule '%s'"
+msgstr "не вдалося оновити віддалене призначення для підмодуля \"%s\""
+
+msgid "suppress output of synchronizing submodule url"
+msgstr "приховати вивід процесу синхронізації url-адреси підмодуля"
+
+msgid "git submodule sync [--quiet] [--recursive] [<path>]"
+msgstr "git submodule sync [--quiet] [--recursive] [<шлях>]"
+
+#, c-format
+msgid ""
+"Submodule work tree '%s' contains a .git directory. This will be replaced "
+"with a .git file by using absorbgitdirs."
+msgstr ""
+"Робоче дерево підмодуля \"%s\" містить директорію .git. Її буде замінено на ."
+"git файл за допомогою absorbgitdirs."
+
+#, c-format
+msgid ""
+"Submodule work tree '%s' contains local modifications; use '-f' to discard "
+"them"
+msgstr ""
+"Робоче дерево підмодуля \"%s\" містить локальні модифікації; скористайтесь "
+"\"-f\", щоб скасувати їх"
+
+#, c-format
+msgid "Cleared directory '%s'\n"
+msgstr "Очищено директорію \"%s\"\n"
+
+#, c-format
+msgid "Could not remove submodule work tree '%s'\n"
+msgstr "Не вдалося видалити робоче дерево підмодуля \"%s\"\n"
+
+#, c-format
+msgid "could not create empty submodule directory %s"
+msgstr "не вдалося створити порожню директорію підмодуля %s"
+
+#, c-format
+msgid "Submodule '%s' (%s) unregistered for path '%s'\n"
+msgstr "Підмодуль \"%s\" (%s) не зареєстровано за шляхом \"%s\"\n"
+
+msgid "remove submodule working trees even if they contain local changes"
+msgstr ""
+"видалити робочі дерева підмодулів, навіть якщо вони містять локальні зміни"
+
+msgid "unregister all submodules"
+msgstr "скасувати реєстрацію всіх підмодулів"
+
+msgid ""
+"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"
+msgstr ""
+"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<шлях>...]]"
+
+msgid "Use '--all' if you really want to deinitialize all submodules"
+msgstr ""
+"Скористайтесь \"--all\", якщо ви дійсно хочете деініціалізувати всі підмодулі"
+
+msgid ""
+"An alternate computed from a superproject's alternate is invalid.\n"
+"To allow Git to clone without an alternate in such a case, set\n"
+"submodule.alternateErrorStrategy to 'info' or, equivalently, clone with\n"
+"'--reference-if-able' instead of '--reference'."
+msgstr ""
+"Запозичений обʼєкт, обчислений з запозиченого обʼєкту батьківського проекту, "
+"не є дійсним.\n"
+"Щоб дозволити Git клонувати без запозиченого обʼєкту в такому випадку, "
+"встановіть\n"
+"submodule.alternateErrorStrategy на \"info\" або, що еквівалентно, клонуйте "
+"з\n"
+"\"--reference-if-able\" замість \"--reference\"."
+
+#, c-format
+msgid "could not get a repository handle for gitdir '%s'"
+msgstr "не вдалося отримати дескриптор сховища для git директорії \"%s\""
+
+#, c-format
+msgid "submodule '%s' cannot add alternate: %s"
+msgstr "підмодуль \"%s\" не може додати запозичених обʼєкт: %s"
+
+#, c-format
+msgid "Value '%s' for submodule.alternateErrorStrategy is not recognized"
+msgstr "Значення \"%s\" для submodule.alternateErrorStrategy не розпізнано"
+
+#, c-format
+msgid "Value '%s' for submodule.alternateLocation is not recognized"
+msgstr "Значення \"%s\" для submodule.alternateLocation не розпізнано"
+
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's git dir"
+msgstr ""
+"відмовлено в створенні/використанні \"%s\" у git директорії іншого підмодуля"
+
+#, c-format
+msgid "clone of '%s' into submodule path '%s' failed"
+msgstr "не вдалося клонувати \"%s\" у шлях підмодуля \"%s\""
+
+#, c-format
+msgid "directory not empty: '%s'"
+msgstr "директорія не порожня: \"%s\""
+
+#, c-format
+msgid "could not get submodule directory for '%s'"
+msgstr "не вдалося отримати директорію підмодуля для \"%s\""
+
+msgid "alternative anchor for relative paths"
+msgstr "альтернативний якір для відносних шляхів"
+
+msgid "where the new submodule will be cloned to"
+msgstr "куди буде клоновано новий підмодуль"
+
+msgid "name of the new submodule"
+msgstr "назва нового підмодуля"
+
+msgid "url where to clone the submodule from"
+msgstr "url адреса, звідки клонувати підмодуль"
+
+msgid "depth for shallow clones"
+msgstr "глибина для неглибоких клонів"
+
+msgid "force cloning progress"
+msgstr "примусово звітувати прогрес клонування"
+
+msgid "disallow cloning into non-empty directory"
+msgstr "заборонити клонування у непорожню директорію"
+
+msgid ""
+"git submodule--helper clone [--prefix=<path>] [--quiet] [--reference "
+"<repository>] [--name <name>] [--depth <depth>] [--single-branch] [--filter "
+"<filter-spec>] --url <url> --path <path>"
+msgstr ""
+"git submodule--helper clone [--prefix=<шлях>] [--quiet] [--reference "
+"<сховище>] [--name <назва>] [--depth <глибина>] [--single-branch] [--filter "
+"<визначник-фільтру>] --url <url> --path <шлях>"
+
+#, c-format
+msgid "Invalid update mode '%s' configured for submodule path '%s'"
+msgstr ""
+"Неприпустимий режим оновлення \"%s\" налаштовано для підмодуля за шляхом "
+"\"%s\""
+
+#, c-format
+msgid "Submodule path '%s' not initialized"
+msgstr "Підмодуль за шляхом \"%s\" не ініціалізовано"
+
+msgid "Maybe you want to use 'update --init'?"
+msgstr "Можливо, ви хочете скористатись командою \"update --init\"?"
+
+#, c-format
+msgid "Skipping unmerged submodule %s"
+msgstr "Пропуск незлитого підмодуля %s"
+
+#, c-format
+msgid "Skipping submodule '%s'"
+msgstr "Пропуск підмодуля \"%s\""
+
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "неможливо клонувати підмодуль \"%s\" без URL-адреси"
+
+#, c-format
+msgid "Failed to clone '%s'. Retry scheduled"
+msgstr "Не вдалося клонувати \"%s\". Запланована повторна спроба"
+
+#, c-format
+msgid "Failed to clone '%s' a second time, aborting"
+msgstr "Не вдалося клонувати \"%s\" вдруге, переривання"
+
+#, c-format
+msgid "Unable to checkout '%s' in submodule path '%s'"
+msgstr "Не вдалося переключитися на \"%s\" у підмодулі за шляхом \"%s\""
+
+#, c-format
+msgid "Unable to rebase '%s' in submodule path '%s'"
+msgstr "Не вдалося перебазувати \"%s\" в підмодулі за шляхом \"%s\""
+
+#, c-format
+msgid "Unable to merge '%s' in submodule path '%s'"
+msgstr "Не вдалося злити \"%s\" в підмодулі за шляхом \"%s\""
+
+#, c-format
+msgid "Execution of '%s %s' failed in submodule path '%s'"
+msgstr "Не вдалося виконати \"%s %s\" у шляху підмодуля \"%s\""
+
+#, c-format
+msgid "Submodule path '%s': checked out '%s'\n"
+msgstr "Шлях підмодуля \"%s\": переключено стан \"%s\"\n"
+
+#, c-format
+msgid "Submodule path '%s': rebased into '%s'\n"
+msgstr "Шлях підмодуля \"%s\": перебазовано в \"%s\"\n"
+
+#, c-format
+msgid "Submodule path '%s': merged in '%s'\n"
+msgstr "Шлях підмодуля \"%s\": злито в \"%s\"\n"
+
+#, c-format
+msgid "Submodule path '%s': '%s %s'\n"
+msgstr "Шлях підмодуля \"%s\": \"%s %s\"\n"
+
+#, c-format
+msgid "Unable to fetch in submodule path '%s'; trying to directly fetch %s:"
+msgstr ""
+"Неможливо виконати отримання шляху підмодуля \"%s\"; спроба отримати напряму "
+"%s:"
+
+#, c-format
+msgid ""
+"Fetched in submodule path '%s', but it did not contain %s. Direct fetching "
+"of that commit failed."
+msgstr ""
+"Отримано у шляху до підмодуля \"%s\", але він не містить %s. Пряме отримання "
+"цього коміту не вдалося."
+
+#, c-format
+msgid "could not initialize submodule at path '%s'"
+msgstr "не вдалося ініціалізувати підмодуль за шляхом \"%s\""
+
+#, c-format
+msgid ""
+"Submodule (%s) branch configured to inherit branch from superproject, but "
+"the superproject is not on any branch"
+msgstr ""
+"Гілку підмодуля (%s) налаштовано на успадкування гілки від суперпроекту, але "
+"суперпроект не знаходиться у жодній гілці"
+
+#, c-format
+msgid "Unable to find current revision in submodule path '%s'"
+msgstr "Не вдалося знайти поточну ревізію у шляху підмодуля \"%s\""
+
+#, c-format
+msgid "Unable to fetch in submodule path '%s'"
+msgstr "Не вдалося отримати шлях підмодуля \"%s\""
+
+#, c-format
+msgid "Unable to find %s revision in submodule path '%s'"
+msgstr "Не вдалося знайти ревізію %s у шляху підмодуля \"%s\""
+
+#, c-format
+msgid "Failed to recurse into submodule path '%s'"
+msgstr "Не вдалося обробити рекурсивно підмодуль за шляхом \"%s\""
+
+msgid "force checkout updates"
+msgstr "переключитися на оновлення примусово"
+
+msgid "initialize uninitialized submodules before update"
+msgstr "ініціалізувати неініціалізовані підмодулі перед оновленням"
+
+msgid "use SHA-1 of submodule's remote tracking branch"
+msgstr "використовувати SHA-1 гілки віддаленого відстежування підмодуля"
+
+msgid "traverse submodules recursively"
+msgstr "обходити підмодулі рекурсивно"
+
+msgid "don't fetch new objects from the remote site"
+msgstr "не отримувати нові обʼєкти з віддаленої сторони"
+
+msgid "use the 'checkout' update strategy (default)"
+msgstr "використати стратегію оновлення \"checkout \" (за замовчуванням)"
+
+msgid "use the 'merge' update strategy"
+msgstr "використати стратегію оновлення \"merge\""
+
+msgid "use the 'rebase' update strategy"
+msgstr "використати стратегію оновлення \"rebase\""
+
+msgid "create a shallow clone truncated to the specified number of revisions"
+msgstr "створити неглибокий клон, урізаний до вказаної кількості ревізій"
+
+msgid "parallel jobs"
+msgstr "паралельні потоки"
+
+msgid "whether the initial clone should follow the shallow recommendation"
+msgstr "чи повинен початковий клон слідувати неглибоким рекомендаціям"
+
+msgid "don't print cloning progress"
+msgstr "не виводити хід клонування"
+
+msgid "disallow cloning into non-empty directory, implies --init"
+msgstr "заборонити клонування у непорожній каталог, мається на увазі --init"
+
+msgid ""
+"git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] "
+"[-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-"
+"shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
+"[--] [<path>...]"
+msgstr ""
+"git submodule [--quiet] update [--init [--filter=<визначник-фільтру>]] [--"
+"remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--"
+"[no-]recommend-shallow] [--reference <сховище>] [--recursive] [--[no-]single-"
+"branch] [--] [<шлях>...]"
+
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Не вдалося розпізнати HEAD як дійсне посилання."
+
+msgid "git submodule absorbgitdirs [<options>] [<path>...]"
+msgstr "git submodule absorbgitdirs [<опції>] [<шлях>...]"
+
+msgid "suppress output for setting url of a submodule"
+msgstr "приховати вивід при встановленні url підмодуля"
+
+msgid "git submodule set-url [--quiet] <path> <newurl>"
+msgstr "git submodule set-url [--quiet] <шлях> <новий-url>"
+
+msgid "set the default tracking branch to master"
+msgstr "встановити гілку відстежування за замовчуванням на master"
+
+msgid "set the default tracking branch"
+msgstr "встановити гілку відстежування за замовчуванням"
+
+msgid "git submodule set-branch [-q|--quiet] (-d|--default) <path>"
+msgstr "git submodule set-branch [-q|--quiet] (-d|--default) <шлях>"
+
+msgid "git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"
+msgstr "git submodule set-branch [-q|--quiet] (-b|--branch) <гілка> <шлях>"
+
+msgid "--branch or --default required"
+msgstr "Потрібно вказати --branch або --default"
+
+msgid "print only error messages"
+msgstr "виводити тільки повідомлення про помилки"
+
+msgid "force creation"
+msgstr "примусове створення"
+
+msgid "show whether the branch would be created"
+msgstr "показати, чи буде створено гілку"
+
+msgid ""
+"git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--"
+"quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>"
+msgstr ""
+"git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--"
+"quiet] [-t|--track] [-n|--dry-run] <назва> <початковий-oid> <початкова-назва>"
+
+#, c-format
+msgid "creating branch '%s'"
+msgstr "створення гілки \"%s\""
+
+#, c-format
+msgid "Adding existing repo at '%s' to the index\n"
+msgstr "Додавання існуючого сховища за адресою \"%s\" до індексу\n"
+
+#, c-format
+msgid "'%s' already exists and is not a valid git repo"
+msgstr "\"%s\" вже існує і не є припустимим git сховищем"
+
+#, c-format
+msgid "A git directory for '%s' is found locally with remote(s):\n"
+msgstr ""
+"Git директорію для \"%s\" знайдено локально з віддаленим(и) "
+"призначенням(и):\n"
+
+#, c-format
+msgid ""
+"If you want to reuse this local git directory instead of cloning again from\n"
+"  %s\n"
+"use the '--force' option. If the local git directory is not the correct "
+"repo\n"
+"or you are unsure what this means choose another name with the '--name' "
+"option."
+msgstr ""
+"Якщо ви хочете повторно використати цю локальну git директорію замість того, "
+"щоб знову клонувати з\n"
+"  %s\n"
+"скористайтесь опцією \"--force\". Якщо локальна директорія git не є "
+"правильним сховищем\n"
+"або ви не впевнені, що це означає, виберіть іншу назву за допомогою опції "
+"\"--name\"."
+
+#, c-format
+msgid "Reactivating local git directory for submodule '%s'\n"
+msgstr "Повторна активація локальної git директорії для підмодуля \"%s\"\n"
+
+#, c-format
+msgid "unable to checkout submodule '%s'"
+msgstr "не вдалося переключитись на підмодуль \"%s\""
+
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr ""
+"будь ласка, переконайтеся, що файл .gitmodules знаходиться у робочому дереві"
+
+#, c-format
+msgid "Failed to add submodule '%s'"
+msgstr "Не вдалося додати підмодуль \"%s\""
+
+#, c-format
+msgid "Failed to register submodule '%s'"
+msgstr "Не вдалося зареєструвати підмодуль \"%s\""
+
+#, c-format
+msgid "'%s' already exists in the index"
+msgstr "\"%s\" вже існує в індексі"
+
+#, c-format
+msgid "'%s' already exists in the index and is not a submodule"
+msgstr "\"%s\" вже існує в індексі і не є підмодулем"
+
+#, c-format
+msgid "'%s' does not have a commit checked out"
+msgstr "\"%s\" не має активного коміту"
+
+msgid "branch of repository to add as submodule"
+msgstr "гілка сховища, яку потрібно додати як підмодуль"
+
+msgid "allow adding an otherwise ignored submodule path"
+msgstr "дозволити додавання шляху до підмодуля, який інакше ігнорується"
+
+msgid "borrow the objects from reference repositories"
+msgstr "запозичити обʼєкти з репозиторіїв посилань"
+
+msgid ""
+"sets the submodule's name to the given string instead of defaulting to its "
+"path"
+msgstr ""
+"встановлює назву підмодуля на вказане значення замість використання його "
+"шляху"
+
+msgid "git submodule add [<options>] [--] <repository> [<path>]"
+msgstr "git submodule add [<опції>] [--] <сховище> [<шлях>]"
+
+msgid "Relative path can only be used from the toplevel of the working tree"
+msgstr ""
+"Відносний шлях можна використовувати лише з верхнього рівня робочого дерева"
+
+#, c-format
+msgid "repo URL: '%s' must be absolute or begin with ./|../"
+msgstr "URL сховища: \"%s\" має бути абсолютним або починатися з ./|../."
+
+#, c-format
+msgid "'%s' is not a valid submodule name"
+msgstr "\"%s\" не є припустимою назвою підмодуля"
+
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <команда>"
+
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <причина>] <назва> <посилання>"
+
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <назва>"
+
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <назва>"
+
+msgid "suppress error message for non-symbolic (detached) refs"
+msgstr ""
+"приховати повідомлення про помилку для несимвольних (відокремлених) посилань"
+
+msgid "delete symbolic ref"
+msgstr "видалити символьне посилання"
+
+msgid "shorten ref output"
+msgstr "скоротити вивід посилань"
+
+msgid "recursively dereference (default)"
+msgstr "рекурсивне розіменування (за замовчуванням)"
+
+msgid "reason"
+msgstr "причина"
+
+msgid "reason of the update"
+msgstr "причина оновлення"
+
+msgid ""
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
+msgstr ""
+"git tag [-a | -s | -u <ідентифікатор-ключа>] [-f] [-m <допис> | -F <файл>] [-"
+"e]\n"
+"        <назва-тегу> [<коміт> | <об’єкт>]"
+
+msgid "git tag -d <tagname>..."
+msgstr "git tag -d <назва-тега>..."
+
+msgid ""
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
+msgstr ""
+"git tag [-n[<число>]] -l [--contains <коміт>] [--no-contains <коміт>]\n"
+"        [--points-at <обʼєкт>] [--column[=<опціі>] | --no-column]\n"
+"        [--create-reflog] [--sort=<ключ>] [--format=<формат>]\n"
+"        [--merged <коміт>] [--no-merged <коміт>] [<шаблон>...]"
+
+msgid "git tag -v [--format=<format>] <tagname>..."
+msgstr "git tag -v [--format=<формат>] <назва-тега>..."
+
+#, c-format
+msgid "tag '%s' not found."
+msgstr "тег \"%s\" не знайдено."
+
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "Видалено тег \"%s\" (було %s)\n"
+
+#, c-format
+msgid ""
+"\n"
+"Write a message for tag:\n"
+"  %s\n"
+"Lines starting with '%c' will be ignored.\n"
+msgstr ""
+"\n"
+"Напишіть допис до тегу:\n"
+"  %s\n"
+"Рядки, що починаються з \"%c\", будуть проігноровані.\n"
+
+#, c-format
+msgid ""
+"\n"
+"Write a message for tag:\n"
+"  %s\n"
+"Lines starting with '%c' will be kept; you may remove them yourself if you "
+"want to.\n"
+msgstr ""
+"\n"
+"Напишіть допис до тегу:\n"
+"  %s\n"
+"Рядки, що починаються з \"%c\", будуть збережені; ви можете вилучити їх "
+"самостійно, якщо захочете.\n"
+
+msgid "unable to sign the tag"
+msgstr "не вдалося підписати тег"
+
+#, c-format
+msgid ""
+"You have created a nested tag. The object referred to by your new tag is\n"
+"already a tag. If you meant to tag the object that it points to, use:\n"
+"\n"
+"\tgit tag -f %s %s^{}"
+msgstr ""
+"Ви створили вкладений тег. Обʼєкт, на який посилається ваш новий тег\n"
+"вже є тегом. Якщо ви хотіли позначити обʼєкт, на який він вказує, "
+"скористайтесь\n"
+"\n"
+"\tgit tag -f %s %s^{}"
+
+msgid "bad object type."
+msgstr "невірний тип обʼєкта."
+
+msgid "no tag message?"
+msgstr "немає допису до тегу?"
+
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "Допис до тегу було залишено в %s\n"
+
+msgid "list tag names"
+msgstr "показати назви тегів"
+
+msgid "print <n> lines of each tag message"
+msgstr "вивести <н> рядків кожного допису до тегу"
+
+msgid "delete tags"
+msgstr "видалити теги"
+
+msgid "verify tags"
+msgstr "перевірити теги"
+
+msgid "Tag creation options"
+msgstr "Опції створення тегів"
+
+msgid "annotated tag, needs a message"
+msgstr "анотований тег потребує допису"
+
+msgid "tag message"
+msgstr "допис до тегу"
+
+msgid "force edit of tag message"
+msgstr "примусове редагування допису до тегу"
+
+msgid "annotated and GPG-signed tag"
+msgstr "анотований та підписаний GPG-підписом тег"
+
+msgid "use another key to sign the tag"
+msgstr "використати інший ключ для підпису тегу"
+
+msgid "replace the tag if exists"
+msgstr "замінити тег, якщо він існує"
+
+msgid "create a reflog"
+msgstr "створити журнал посилань"
+
+msgid "Tag listing options"
+msgstr "Опції виводу тегів"
+
+msgid "show tag list in columns"
+msgstr "показати список тегів в стовпчиках"
+
+msgid "print only tags that contain the commit"
+msgstr "вивести тільки теги, що містять коміти"
+
+msgid "print only tags that don't contain the commit"
+msgstr "вивести тільки теги, що не містять комітів"
+
+msgid "print only tags that are merged"
+msgstr "вивести тільки злиті теги"
+
+msgid "print only tags that are not merged"
+msgstr "вивести тільки не злиті теги"
+
+msgid "print only tags of the object"
+msgstr "вивести тільки теги обʼєкта"
+
+#, c-format
+msgid "the '%s' option is only allowed in list mode"
+msgstr "опція \"%s\" дозволена лише в режимі виводу"
+
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "\"%s\" не є припустимою назвою тега."
+
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "тег \"%s\" вже існує"
+
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr "Неприпустимий режим очищення %s"
+
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "Оновлено тег \"%s\" (було %s)\n"
+
+msgid "pack exceeds maximum allowed size"
+msgstr "пакунок перевищує максимально дозволений розмір"
+
+msgid "failed to write object in stream"
+msgstr "не вдалося записати обʼєкт потоку"
+
+#, c-format
+msgid "inflate returned (%d)"
+msgstr "розпаковувач повернув (%d)"
+
+msgid "invalid blob object from stream"
+msgstr "неприпустимий обʼєкт blob з потоку"
+
+msgid "Unpacking objects"
+msgstr "Розпакування обʼєктів"
+
+#, c-format
+msgid "failed to create directory %s"
+msgstr "не вдалося створити директорію %s"
+
+#, c-format
+msgid "failed to delete file %s"
+msgstr "не вдалося видалити файл %s"
+
+#, c-format
+msgid "failed to delete directory %s"
+msgstr "не вдалося видалити директорію %s"
+
+#, c-format
+msgid "Testing mtime in '%s' "
+msgstr "Тестування mtime в \"%s\" "
+
+msgid "directory stat info does not change after adding a new file"
+msgstr ""
+"статистична інформація директорії не змінилась після додання нового файла"
+
+msgid "directory stat info does not change after adding a new directory"
+msgstr ""
+"статистична інформація директорії не змінилась після додання нової директорії"
+
+msgid "directory stat info changes after updating a file"
+msgstr "статистична інформація директорії змінилась після оновлення файла"
+
+msgid "directory stat info changes after adding a file inside subdirectory"
+msgstr ""
+"статистична інформація директорії змінилась після додання файла до "
+"піддиректорії"
+
+msgid "directory stat info does not change after deleting a file"
+msgstr "статистична інформація директорії не змінилась після видалення файла"
+
+msgid "directory stat info does not change after deleting a directory"
+msgstr ""
+"статистична інформація директорії не змінилась після видалення директорії"
+
+msgid " OK"
+msgstr " OK"
+
+msgid "git update-index [<options>] [--] [<file>...]"
+msgstr "git update-index [<опції>] [--] [<файл>...]"
+
+msgid "continue refresh even when index needs update"
+msgstr "продовжити оновлення, навіть якщо індекс потребує змін"
+
+msgid "refresh: ignore submodules"
+msgstr "оновити: ігнорувати підмодулі"
+
+msgid "do not ignore new files"
+msgstr "не ігнорувати нові файли"
+
+msgid "let files replace directories and vice-versa"
+msgstr "дозволити файлам замінювати директорії і навпаки"
+
+msgid "notice files missing from worktree"
+msgstr "повідомляти про файли, відсутні в робочому дереві"
+
+msgid "refresh even if index contains unmerged entries"
+msgstr "оновити, навіть якщо індекс містить не злиті записи"
+
+msgid "refresh stat information"
+msgstr "оновити статистичну інформацію"
+
+msgid "like --refresh, but ignore assume-unchanged setting"
+msgstr "на кшталт --refresh, але ігнорує assume-unchanged опцію"
+
+msgid "<mode>,<object>,<path>"
+msgstr "<режим>,<обʼєкт>,<шлях>"
+
+msgid "add the specified entry to the index"
+msgstr "додати вказаний запис до індексу"
+
+msgid "mark files as \"not changing\""
+msgstr "позначити файли як \"не змінені\""
+
+msgid "clear assumed-unchanged bit"
+msgstr "очистити assumed-unchanged біт"
+
+msgid "mark files as \"index-only\""
+msgstr "позначити файли як \"тільки для індексу\""
+
+msgid "clear skip-worktree bit"
+msgstr "очистити skip-worktree біт"
+
+msgid "do not touch index-only entries"
+msgstr "ігнорувати файли, призначені тільки для індексу"
+
+msgid "add to index only; do not add content to object database"
+msgstr "додати лише до індексу; не додавати вміст до бази даних обʼєкта"
+
+msgid "remove named paths even if present in worktree"
+msgstr "видалити іменовані шляхи, навіть якщо вони присутні у робочому дереві"
+
+msgid "with --stdin: input lines are terminated by null bytes"
+msgstr "з --stdin: вхідні рядки завершуються нульовими байтами"
+
+msgid "read list of paths to be updated from standard input"
+msgstr "прочитати список шляхів для оновлення зі стандартного вводу"
+
+msgid "add entries from standard input to the index"
+msgstr "додати записи зі стандартного вводу до індексу"
+
+msgid "repopulate stages #2 and #3 for the listed paths"
+msgstr "перезаповнити етапи №2 і №3 для перелічених шляхів"
+
+msgid "only update entries that differ from HEAD"
+msgstr "оновити тільки ті записи, які відрізняються від HEAD"
+
+msgid "ignore files missing from worktree"
+msgstr "ігнорувати файли відсутні в робочому дереві"
+
+msgid "report actions to standard output"
+msgstr "звітувати про дії на стандартний вивід"
+
+msgid "(for porcelains) forget saved unresolved conflicts"
+msgstr "(для високорівневих команд) забути збережені невирішені конфлікти"
+
+msgid "write index in this format"
+msgstr "записати індекс у цьому форматі"
+
+msgid "report on-disk index format version"
+msgstr "звітувати про версію формату індексу на диску"
+
+msgid "enable or disable split index"
+msgstr "увімкнути або вимкнути розділений індекс"
+
+msgid "enable/disable untracked cache"
+msgstr "увімкнути/вимкнути невідстежуваний кеш"
+
+msgid "test if the filesystem supports untracked cache"
+msgstr "перевірити, чи підтримує файлова система невідстежуваний кеш"
+
+msgid "enable untracked cache without testing the filesystem"
+msgstr "увімкнути невідстежуваний кеш без тестування файлової системи"
+
+msgid "write out the index even if is not flagged as changed"
+msgstr "записати індекс, навіть якщо він не позначений як змінений"
+
+msgid "enable or disable file system monitor"
+msgstr "увімкнути або вимкнути монітор файлової системи"
+
+msgid "mark files as fsmonitor valid"
+msgstr "позначити файли придатними для fsmonitor"
+
+msgid "clear fsmonitor valid bit"
+msgstr "очистити біт придатності для fsmonitor"
+
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: було %d, стало %d"
+
+msgid ""
+"core.splitIndex is set to false; remove or change it, if you really want to "
+"enable split index"
+msgstr ""
+"core.splitIndex встановлено в false; видаліть або змініть його, якщо ви "
+"дійсно хочете увімкнути розділений індекс"
+
+msgid ""
+"core.splitIndex is set to true; remove or change it, if you really want to "
+"disable split index"
+msgstr ""
+"core.splitIndex встановлено в true; видаліть або змініть його, якщо ви "
+"дійсно хочете вимкнути розділений індекс"
+
+msgid ""
+"core.untrackedCache is set to true; remove or change it, if you really want "
+"to disable the untracked cache"
+msgstr ""
+"core.untrackedCache встановлено в true; видаліть або змініть його, якщо ви "
+"дійсно хочете вимкнути невідстежуваний кеш"
+
+msgid "Untracked cache disabled"
+msgstr "Невідстежуваний кеш вимкнено"
+
+msgid ""
+"core.untrackedCache is set to false; remove or change it, if you really want "
+"to enable the untracked cache"
+msgstr ""
+"core.untrackedCache встановлено в false; видаліть або змініть його, якщо ви "
+"дійсно хочете увімкнути невідстежуваний кеш"
+
+#, c-format
+msgid "Untracked cache enabled for '%s'"
+msgstr "Увімкнено невідстежуваний кеш для \"%s\""
+
+msgid "core.fsmonitor is unset; set it if you really want to enable fsmonitor"
+msgstr ""
+"core.fsmonitor не встановлено; встановіть його, якщо ви дійсно хочете "
+"увімкнути fsmonitor"
+
+msgid "fsmonitor enabled"
+msgstr "fsmonitor увімкнено"
+
+msgid ""
+"core.fsmonitor is set; remove it if you really want to disable fsmonitor"
+msgstr ""
+"core.fsmonitor встановлено; видаліть його, якщо ви дійсно хочете вимкнути "
+"fsmonitor"
+
+msgid "fsmonitor disabled"
+msgstr "fsmonitor вимкнено"
+
+msgid "git update-ref [<options>] -d <refname> [<old-val>]"
+msgstr "git update-ref [<опції>] -d <назва посилання> [<старе значення>]"
+
+msgid "git update-ref [<options>]    <refname> <new-val> [<old-val>]"
+msgstr ""
+"git update-ref [<опції>] <назва-посилання> <нове-значення> [<старе-значення>]"
+
+msgid "git update-ref [<options>] --stdin [-z]"
+msgstr "git update-ref [<опції>] --stdin [-z]"
+
+msgid "delete the reference"
+msgstr "видалити посилання"
+
+msgid "update <refname> not the one it points to"
+msgstr "оновити <назвау-посилання>, а не те, на яке воно вказує"
+
+msgid "stdin has NUL-terminated arguments"
+msgstr "stdin має аргументи, що закінчуються NUL"
+
+msgid "read updates from stdin"
+msgstr "читати оновлення з stdin"
+
+msgid "update the info files from scratch"
+msgstr "оновити інформаційні файли з чистого аркуша"
+
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<н>] [--stateless-rpc]\n"
+"                [--advertise-refs] <директорія>"
+
+msgid "quit after a single request/response exchange"
+msgstr "припинити роботу після першого запит/відповідь обміну"
+
+msgid "serve up the info/refs for git-http-backend"
+msgstr "надати info/refs для git-http-backend"
+
+msgid "do not try <directory>/.git/ if <directory> is no Git directory"
+msgstr ""
+"не пробувати <директорія>/.git/, якщо <директорія> не є директорією Git"
+
+msgid "interrupt transfer after <n> seconds of inactivity"
+msgstr "перервати передачу після <н> секунд неактивності"
+
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | —verbose] [--raw] <коміт>..."
+
+msgid "print commit contents"
+msgstr "показати вміст коміту"
+
+msgid "print raw gpg status output"
+msgstr "показати необроблений вивід стану gpg"
+
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr ""
+"git verify-pack [-v | --verbose] [-s | --stat-only] [--] <пакунок>.idx..."
+
+msgid "verbose"
+msgstr "розгорнутий вивід"
+
+msgid "show statistics only"
+msgstr "показати тільки статистику"
+
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<формат>] [--raw] <тег>..."
+
+msgid "print tag contents"
+msgstr "показати вміст тегу"
+
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <строка>]]\n"
+"                 [--orphan] [(-b | -B) <нова-гілка>] <шлях> [<комітоподібне>]"
+
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
+
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <строка>] <робоче дерево>"
+
+msgid "git worktree move <worktree> <new-path>"
+msgstr "git worktree move <робоче-дерево> <новий-шлях>"
+
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <термін-дії>]"
+
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <робоче дерево>"
+
+msgid "git worktree repair [<path>...]"
+msgstr "git worktree repair [<шлях>...]"
+
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <робоче дерево>"
+
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Немає можливої джерельної гілки, що означає \"--orphan\""
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Якщо ви хочете створити робоче дерево, що містить нову сирітську гілку\n"
+"(гілку без комітів) для цього сховища, ви можете зробити це\n"
+"за допомогою прапорця --orphan:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Якщо ви хочете створити робоче дерево, що містить нову сирітську гілку\n"
+"(гілку без комітів) для цього сховища, ви можете зробити це\n"
+"за допомогою прапорця --orphan:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
+#, c-format
+msgid "Removing %s/%s: %s"
+msgstr "Видалення %s/%s: %s"
+
+msgid "report pruned working trees"
+msgstr "звітувати про видалення робочих дерев"
+
+msgid "expire working trees older than <time>"
+msgstr "видалити робочі дерева, старіші за <час>"
+
+#, c-format
+msgid "'%s' already exists"
+msgstr "\"%s\" вже існує"
+
+#, c-format
+msgid "unusable worktree destination '%s'"
+msgstr "непридатне місце призначення робочого дерева \"%s\""
+
+#, c-format
+msgid ""
+"'%s' is a missing but locked worktree;\n"
+"use '%s -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"
+msgstr ""
+"\"%s\" - є відсутнім, але заблокованим робочим деревом;\n"
+"скористайтесь командою \"%s -f -f\", щоб перевизначити або \"unlock\" і "
+"\"prune\" або \"remove\", щоб очистити"
+
+#, c-format
+msgid ""
+"'%s' is a missing but already registered worktree;\n"
+"use '%s -f' to override, or 'prune' or 'remove' to clear"
+msgstr ""
+"\"%s\" - є відсутнім, але вже зареєстрованим робочим деревом;\n"
+"скористайтесь командою \"%s -f\", щоб перевизначити, або \"prune\" чи "
+"\"remove\", щоб очистити"
+
+#, c-format
+msgid "failed to copy '%s' to '%s'; sparse-checkout may not work correctly"
+msgstr ""
+"не вдалося скопіювати \"%s\" в \"%s\"; розріджений перехід може працювати "
+"некоректно"
+
+#, c-format
+msgid "failed to copy worktree config from '%s' to '%s'"
+msgstr "не вдалося скопіювати конфігурацію робочого дерева з \"%s\" до \"%s\""
+
+#, c-format
+msgid "failed to unset '%s' in '%s'"
+msgstr "не вдалося скинути \"%s\" в \"%s\""
+
+#, c-format
+msgid "could not create directory of '%s'"
+msgstr "не вдалося створити директорію \"%s\""
+
+msgid "initializing"
+msgstr "ініціалізація"
+
+#, c-format
+msgid "Preparing worktree (new branch '%s')"
+msgstr "Підготовка робочого дерева (нова гілка \"%s\")"
+
+#, c-format
+msgid "Preparing worktree (resetting branch '%s'; was at %s)"
+msgstr "Підготовка робочого дерева (скидання гілки \"%s\"; була на %s)"
+
+#, c-format
+msgid "Preparing worktree (checking out '%s')"
+msgstr "Підготовка робочого дерева (перехід до \"%s\")"
+
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "недосяжне: неприпустиме посилання: %s"
+
+#, c-format
+msgid "Preparing worktree (detached HEAD %s)"
+msgstr "Підготовка робочого дерева (відокремлений HEAD %s)"
+
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD вказує на неприпустиме (або осиротіле) посилання.\n"
+"HEAD шлях: \"%s\"\n"
+"HEAD вміст: \"%s\""
+
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"Не існує локальних або віддалених посилань, незважаючи на наявність "
+"принаймні одного віддаленого\n"
+"призначення, зупинка; скористайтесь \"add -f\" для перевизначення або "
+"спочатку виконайте отримання віддаленого посилання"
+
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "\"%s\" і \"%s\" не можна використовувати разом"
+
+msgid "checkout <branch> even if already checked out in other worktree"
+msgstr ""
+"перейти на <гілку>, навіть якщо вона вже активна в іншому робочому дереві"
+
+msgid "create a new branch"
+msgstr "створити нову гілку"
+
+msgid "create or reset a branch"
+msgstr "створити або скинути гілку"
+
+msgid "create unborn/orphaned branch"
+msgstr "створити ненароджену/сирітську гілку"
+
+msgid "populate the new working tree"
+msgstr "заповнити нове робоче дерево"
+
+msgid "keep the new working tree locked"
+msgstr "зберегти нове робоче дерево зафіксованим"
+
+msgid "reason for locking"
+msgstr "причина блокування"
+
+msgid "set up tracking mode (see git-branch(1))"
+msgstr "налаштувати режим відстежування (дивіться git-branch(1))"
+
+msgid "try to match the new branch name with a remote-tracking branch"
+msgstr ""
+"спробуйте співставити нову назву гілки з назвою віддалено відстежуваної гілки"
+
+#, c-format
+msgid "options '%s', '%s', and '%s' cannot be used together"
+msgstr "опції \"%s\", \"%s\" та \"%s\" не можна використовувати разом"
+
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "опції \"%s\" і \"%s\" не можна використовувати разом"
+
+msgid "<commit-ish>"
+msgstr "<комітоподібне>"
+
+msgid "added with --lock"
+msgstr "додано з --lock"
+
+msgid "--[no-]track can only be used if a new branch is created"
+msgstr "--[no-]track можна використовувати лише при створенні нової гілки"
+
+msgid "show extended annotations and reasons, if available"
+msgstr "показувати розширені анотації та причини, якщо вони доступні"
+
+msgid "add 'prunable' annotation to worktrees older than <time>"
+msgstr "додати анотацію \"prunable\" до робочих дерев, старіших за <час>"
+
+msgid "terminate records with a NUL character"
+msgstr "завершувати записи символом NUL"
+
+#, c-format
+msgid "'%s' is not a working tree"
+msgstr "\"%s\" не є робочим деревом"
+
+msgid "The main working tree cannot be locked or unlocked"
+msgstr "Головне робоче дерево неможливо заблокувати або розблокувати"
+
+#, c-format
+msgid "'%s' is already locked, reason: %s"
+msgstr "\"%s\" вже заблоковано, причина: %s"
+
+#, c-format
+msgid "'%s' is already locked"
+msgstr "\"%s\" вже заблоковано"
+
+#, c-format
+msgid "'%s' is not locked"
+msgstr "\"%s\" не зафіксовано"
+
+msgid "working trees containing submodules cannot be moved or removed"
+msgstr ""
+"робочі дерева, що містять підмодулі, не можуть бути переміщені або видалені"
+
+msgid "force move even if worktree is dirty or locked"
+msgstr ""
+"перемістити примусово, навіть якщо робоче дерево забруднене або заблоковане"
+
+#, c-format
+msgid "'%s' is a main working tree"
+msgstr "\"%s\" є головним робочим деревом"
+
+#, c-format
+msgid "could not figure out destination name from '%s'"
+msgstr "не вдалося визначити назву призначення з \"%s\""
+
+#, c-format
+msgid ""
+"cannot move a locked working tree, lock reason: %s\n"
+"use 'move -f -f' to override or unlock first"
+msgstr ""
+"неможливо перемістити заблоковане робоче дерево, причина блокування: %s\n"
+"скористайтесь командою \"move -f -f\", щоб спочатку перевизначити або "
+"розблокувати його"
+
+msgid ""
+"cannot move a locked working tree;\n"
+"use 'move -f -f' to override or unlock first"
+msgstr ""
+"неможливо перемістити заблоковане робоче дерево;\n"
+"скористайтесь командою \"move -f -f\", щоб спочатку перевизначити або "
+"розблокувати його"
+
+#, c-format
+msgid "validation failed, cannot move working tree: %s"
+msgstr "валідація завершилася невдало, неможливо перемістити робоче дерево: %s"
+
+#, c-format
+msgid "failed to move '%s' to '%s'"
+msgstr "не вдалося перенести \"%s\" до \"%s\""
+
+#, c-format
+msgid "failed to run 'git status' on '%s'"
+msgstr "не вдалося виконати \"git status\" на \"%s\""
+
+#, c-format
+msgid "'%s' contains modified or untracked files, use --force to delete it"
+msgstr ""
+"\"%s\" містить змінені або невідстежувані файли, скористайтесь --force, щоб "
+"видалити їх"
+
+#, c-format
+msgid "failed to run 'git status' on '%s', code %d"
+msgstr "не вдалося виконати \"git status\" на \"%s\", код %d"
+
+msgid "force removal even if worktree is dirty or locked"
+msgstr ""
+"видалити примусово, навіть якщо робоче дерево забруднене або заблоковане"
+
+#, c-format
+msgid ""
+"cannot remove a locked working tree, lock reason: %s\n"
+"use 'remove -f -f' to override or unlock first"
+msgstr ""
+"неможливо видалити заблоковане робоче дерево, причина блокування: %s\n"
+"скористайтесь \"remove -f -f\", щоб перевизначити або розблокувати"
+
+msgid ""
+"cannot remove a locked working tree;\n"
+"use 'remove -f -f' to override or unlock first"
+msgstr ""
+"неможливо видалити заблоковане робоче дерево;\n"
+"скористайтесь \"remove -f -f\", щоб перевизначити або розблокувати"
+
+#, c-format
+msgid "validation failed, cannot remove working tree: %s"
+msgstr "валідація завершилася невдало, неможливо видалити робоче дерево: %s"
+
+#, c-format
+msgid "repair: %s: %s"
+msgstr "відремонтувати: %s: %s"
+
+#, c-format
+msgid "error: %s: %s"
+msgstr "помилка: %s: %s"
+
+msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
+msgstr "git write-tree [--missing-ok] [--prefix=<префікс>/]"
+
+msgid "<prefix>/"
+msgstr "<префікс>/"
+
+msgid "write tree object for a subdirectory <prefix>"
+msgstr "записати обʼєкт дерева для піддиректорії <префікс>"
+
+msgid "only useful for debugging"
+msgstr "корисно лише для відлагодження"
+
+msgid "core.fsyncMethod = batch is unsupported on this platform"
+msgstr "core.fsyncMethod = batch не підтримується на цій платформі"
+
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "не вдалося розібрати ключ списку пакунків %s зі значенням \"%s\""
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "список пакунків на \"%s\" не має режиму"
+
+msgid "failed to create temporary file"
+msgstr "не вдалося створити тимчасовий файл"
+
+msgid "insufficient capabilities"
+msgstr "недостатні здібності"
+
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "файл, завантажений з \"%s\", не є пакунком"
+
+msgid "failed to store maximum creation token"
+msgstr "не вдалося зберегти токен максимального створення"
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "нерозпізнаний режим пакунка з URI \"%s\""
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "перевищено ліміт рекурсії URI пакунка (%d)"
+
+#, c-format
+msgid "failed to download bundle from URI '%s'"
+msgstr "не вдалося завантажити пакунок з URI \"%s\""
+
+#, c-format
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "файл з URI \"%s\" не є пакунком або списком пакунків"
+
+#, c-format
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: неочікуваний аргумент: \"%s\""
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: очікувалось flush після аргументів"
+
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: отримано порожній рядок"
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: рядок не відповідає формату \"key=value\""
+
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: рядок має порожній ключ або значення"
+
+#, c-format
+msgid "unrecognized bundle hash algorithm: %s"
+msgstr "нерозпізнаний хеш-алгоритм пакунка: %s"
+
+#, c-format
+msgid "unknown capability '%s'"
+msgstr "невідома властивість \"%s\""
+
+#, c-format
+msgid "'%s' does not look like a v2 or v3 bundle file"
+msgstr "\"%s\" не схожий на файл пакунка v2 або v3"
+
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "нерозпізнаний заголовок: %s%s (%d)"
+
+msgid "Repository lacks these prerequisite commits:"
+msgstr "У сховищі не вистачає обовʼязкових комітів:"
+
+msgid "need a repository to verify a bundle"
+msgstr "потрібне сховище для перевірки пакунка"
+
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"деякі попередні коміти існують у місці збереження обʼєктів, але не повʼязані "
+"з історією сховища"
+
+#, c-format
+msgid "The bundle contains this ref:"
+msgid_plural "The bundle contains these %<PRIuMAX> refs:"
+msgstr[0] "Пакунок містить це %<PRIuMAX> посилання:"
+msgstr[1] "Пакунок містить ці %<PRIuMAX> посилання:"
+msgstr[2] "Пакунок містить ці %<PRIuMAX> посилань:"
+
+msgid "The bundle records a complete history."
+msgstr "Пакунок записує повну історію."
+
+#, c-format
+msgid "The bundle requires this ref:"
+msgid_plural "The bundle requires these %<PRIuMAX> refs:"
+msgstr[0] "Пакунок потребує це %<PRIuMAX> посилання:"
+msgstr[1] "Пакунок потребує ці %<PRIuMAX> посилання:"
+msgstr[2] "Пакунок потребує ці %<PRIuMAX> посилань:"
+
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Пакунок використовує такий алгоритм хешування: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Пакунок використовує такий фільтр: %s"
+
+msgid "unable to dup bundle descriptor"
+msgstr "не вдалося продублювати дескриптор пакунка"
+
+msgid "Could not spawn pack-objects"
+msgstr "Не вдалося розмножити об’єкти пакунків"
+
+msgid "pack-objects died"
+msgstr "pack-objects завершився невдало"
+
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr "посилання \"%s\" виключено опціями rev-list"
+
+#, c-format
+msgid "unsupported bundle version %d"
+msgstr "непідтримувана версія пакунка %d"
+
+#, c-format
+msgid "cannot write bundle version %d with algorithm %s"
+msgstr "неможливо записати версію пакунка %d з алгоритмом %s"
+
+msgid "Refusing to create empty bundle."
+msgstr "Відмовлено в створенні порожнього пакунка."
+
+#, c-format
+msgid "cannot create '%s'"
+msgstr "неможливо створити \"%s\""
+
+msgid "index-pack died"
+msgstr "index-pack завершився невдало"
+
+msgid "terminating chunk id appears earlier than expected"
+msgstr "ідентифікатор завершення фрагмента зʼявився раніше, ніж очікувалось"
+
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "шматок в id %<PRIx32> не %d-byte впорядкований"
+
+#, c-format
+msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
+msgstr "невірне зміщення шматка(ів) %<PRIx64> та %<PRIx64>"
+
+#, c-format
+msgid "duplicate chunk ID %<PRIx32> found"
+msgstr "знайдено дубльований шматок з ідентифікатором %<PRIx32>"
+
+#, c-format
+msgid "final chunk has non-zero id %<PRIx32>"
+msgstr "заключний шматок має ненульовий ідентифікатор %<PRIx32>"
+
+msgid "invalid hash version"
+msgstr "неприпустима версія хешу"
+
+#, c-format
+msgid "invalid color value: %.*s"
+msgstr "неприпустиме значення кольору: %.*s"
+
+msgid "Add file contents to the index"
+msgstr "Додати вміст файлу до індексу"
+
+msgid "Apply a series of patches from a mailbox"
+msgstr "Застосувати серію латок з поштової скриньки"
+
+msgid "Annotate file lines with commit information"
+msgstr "Анотувати рядки файлів з інформацією про коміти"
+
+msgid "Apply a patch to files and/or to the index"
+msgstr "Застосувати латку до файлів та/або до індексу"
+
+msgid "Import a GNU Arch repository into Git"
+msgstr "Імпортувати GNU Arch сховище до Git"
+
+msgid "Create an archive of files from a named tree"
+msgstr "Створити архів файлів з названого дерева"
+
+msgid "Use binary search to find the commit that introduced a bug"
+msgstr "Використати бінарний пошук, щоб знайти коміт, який вніс помилку"
+
+msgid "Show what revision and author last modified each line of a file"
+msgstr "Показати, яка ревізія та автор востаннє змінювали кожен рядок файлу"
+
+msgid "List, create, or delete branches"
+msgstr "Показати, створити або видалити гілки"
+
+msgid "Collect information for user to file a bug report"
+msgstr "Зібрати інформацію, щоб користувач міг подати звіт про помилку"
+
+msgid "Move objects and refs by archive"
+msgstr "Перенести архів обʼєктів та посилань"
+
+msgid "Provide contents or details of repository objects"
+msgstr "Показати вміст або інформацію про обʼєкти сховища"
+
+msgid "Display gitattributes information"
+msgstr "Відобразити інформацію про gitattributes"
+
+msgid "Debug gitignore / exclude files"
+msgstr "Відлагодити gitignore / вилучити файли"
+
+msgid "Show canonical names and email addresses of contacts"
+msgstr "Показати канонічні імена та адреси електронної пошти контактів"
+
+msgid "Ensures that a reference name is well formed"
+msgstr "Забезпечує правильне формування назви посилання"
+
+msgid "Switch branches or restore working tree files"
+msgstr "Перемкнути гілки або відновити файли робочого дерева"
+
+msgid "Copy files from the index to the working tree"
+msgstr "Скопіювати файли з індексу в робоче дерево"
+
+msgid "Find commits yet to be applied to upstream"
+msgstr "Знайти коміти, які ще не були застосовані до першоджерельного сховища"
+
+msgid "Apply the changes introduced by some existing commits"
+msgstr "Застосувати зміни, внесені деякими існуючими комітами"
+
+msgid "Graphical alternative to git-commit"
+msgstr "Графічна альтернатива git-commit"
+
+msgid "Remove untracked files from the working tree"
+msgstr "Видалити невідстежувані файли з робочого дерева"
+
+msgid "Clone a repository into a new directory"
+msgstr "Клонувати сховище в нову директорію"
+
+msgid "Display data in columns"
+msgstr "Відобразити дані в стовпчиках"
+
+msgid "Record changes to the repository"
+msgstr "Записати зміни у сховище"
+
+msgid "Write and verify Git commit-graph files"
+msgstr "Записати та перевірити файли коміт-графа Git"
+
+msgid "Create a new commit object"
+msgstr "Створити новий обʼєкт коміту"
+
+msgid "Get and set repository or global options"
+msgstr "Отримати та встановити параметри сховища або глобальні параметри"
+
+msgid "Count unpacked number of objects and their disk consumption"
+msgstr "Підрахувати кількість розпакованих обʼєктів та їхнє використання диска"
+
+msgid "Retrieve and store user credentials"
+msgstr "Отримати та зберегти облікові дані користувача"
+
+msgid "Helper to temporarily store passwords in memory"
+msgstr "Помічник для тимчасового зберігання паролів у памʼяті"
+
+msgid "Helper to store credentials on disk"
+msgstr "Помічник для зберігання облікових даних на диску"
+
+msgid "Export a single commit to a CVS checkout"
+msgstr "Експортувати окремий коміт до CVS стану"
+
+msgid "Salvage your data out of another SCM people love to hate"
+msgstr "Врятувати свої дані з іншої SCM, яку всі так неполюбляють"
+
+msgid "A CVS server emulator for Git"
+msgstr "Емулятор CVS сервера для Git"
+
+msgid "A really simple server for Git repositories"
+msgstr "Дуже простий сервер для Git сховищ"
+
+msgid "Give an object a human readable name based on an available ref"
+msgstr ""
+"Надати обʼєкту читабельну для людини назву на основі наявного посилання"
+
+msgid "Generate a zip archive of diagnostic information"
+msgstr "Згенерувати zip-архів з діагностичною інформацією"
+
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr "Показати зміни між комітами, комітом та робочим деревом, тощо"
+
+msgid "Compares files in the working tree and the index"
+msgstr "Порівнює файли в робочому дереві та індексі"
+
+msgid "Compare a tree to the working tree or index"
+msgstr "Порівняти дерево з робочим деревом або індексом"
+
+msgid "Compares the content and mode of blobs found via two tree objects"
+msgstr ""
+"Порівнює вміст та режим blob обʼєктів, знайдених у двох обʼєктах дерева"
+
+msgid "Show changes using common diff tools"
+msgstr "Показати зміни за допомогою звичайних diff засобів"
+
+msgid "Git data exporter"
+msgstr "Експортер даних Git"
+
+msgid "Backend for fast Git data importers"
+msgstr "Обробник для швидких імпортерів даних Git"
+
+msgid "Download objects and refs from another repository"
+msgstr "Завантажити обʼєкти та посилання з іншого сховища"
+
+msgid "Receive missing objects from another repository"
+msgstr "Отримати відсутні обʼєкти з іншого сховища"
+
+msgid "Rewrite branches"
+msgstr "Перезаписати гілки"
+
+msgid "Produce a merge commit message"
+msgstr "Створити допис до коміту злиття"
+
+msgid "Output information on each ref"
+msgstr "Вивести інформацію про кожне посилання"
+
+msgid "Run a Git command on a list of repositories"
+msgstr "Запустити команду Git на списку сховищ"
+
+msgid "Prepare patches for e-mail submission"
+msgstr "Підготувати латки для надсилання електронною поштою"
+
+msgid "Verifies the connectivity and validity of the objects in the database"
+msgstr "Перевіряє звʼязність та валідність обʼєктів у базі даних"
+
+msgid "Cleanup unnecessary files and optimize the local repository"
+msgstr "Видалити непотрібні файли та оптимізувати локальне сховище"
+
+msgid "Extract commit ID from an archive created using git-archive"
+msgstr ""
+"Витягти ідентифікатор коміту з архіву, створеного за допомогою git-archive"
+
+msgid "Print lines matching a pattern"
+msgstr "Вивести рядки, що відповідають шаблону"
+
+msgid "A portable graphical interface to Git"
+msgstr "Портативний графічний інтерфейс до Git"
+
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "Обчислити ідентифікатор обʼєкта та за бажанням створити обʼєкт з файлу"
+
+msgid "Display help information about Git"
+msgstr "Відобразити довідкову інформацію про Git"
+
+msgid "Run git hooks"
+msgstr "Запустити git гачки"
+
+msgid "Server side implementation of Git over HTTP"
+msgstr "Серверна реалізація Git через HTTP"
+
+msgid "Download from a remote Git repository via HTTP"
+msgstr "Завантажити з віддаленого Git сховища через HTTP"
+
+msgid "Push objects over HTTP/DAV to another repository"
+msgstr "Надіслати обʼєкти через HTTP/DAV до іншого сховища"
+
+msgid "Send a collection of patches from stdin to an IMAP folder"
+msgstr "Надіслати збірку латок з stdin до теки IMAP"
+
+msgid "Build pack index file for an existing packed archive"
+msgstr "Побудувати індексний файл пакунка для існуючого запакованого архіву"
+
+msgid "Create an empty Git repository or reinitialize an existing one"
+msgstr "Створити порожнє Git сховище або переініціалізувати існуюче"
+
+msgid "Instantly browse your working repository in gitweb"
+msgstr "Миттєво переглянути своє робоче сховище в gitweb"
+
+msgid "Add or parse structured information in commit messages"
+msgstr "Додати або розібрати структуровану інформацію в дописах до комітів"
+
+msgid "Show commit logs"
+msgstr "Показати журнал коміту"
+
+msgid "Show information about files in the index and the working tree"
+msgstr "Показати інформацію про файли в індексі та робочому дереві"
+
+msgid "List references in a remote repository"
+msgstr "Показати посилання у віддаленому сховищі"
+
+msgid "List the contents of a tree object"
+msgstr "Показати вміст обʼєкта дерева"
+
+msgid "Extracts patch and authorship from a single e-mail message"
+msgstr "Витягує латку та авторство з одного повідомлення електронної пошти"
+
+msgid "Simple UNIX mbox splitter program"
+msgstr "Проста програма-розщеплювач UNIX mbox"
+
+msgid "Run tasks to optimize Git repository data"
+msgstr "Запустити завдання для оптимізації даних Git сховища"
+
+msgid "Join two or more development histories together"
+msgstr "Обʼєднати дві або більше історій розробки"
+
+msgid "Find as good common ancestors as possible for a merge"
+msgstr "Знайти якомога кращого спільного предка для злиття"
+
+msgid "Run a three-way file merge"
+msgstr "Запустити тристороннє злиття файлів"
+
+msgid "Run a merge for files needing merging"
+msgstr "Запустити злиття для файлів, які потрібно обʼєднати"
+
+msgid "The standard helper program to use with git-merge-index"
+msgstr "Стандартна допоміжна програма для використання з git-merge-index"
+
+msgid "Perform merge without touching index or working tree"
+msgstr "Виконати злиття, не торкаючись індексу або робочого дерева"
+
+msgid "Run merge conflict resolution tools to resolve merge conflicts"
+msgstr ""
+"Запустити інструменти вирішення конфліктів, щоб вирішити конфлікти злиття"
+
+msgid "Creates a tag object with extra validation"
+msgstr "Створює обʼєкт тегу з додатковою перевіркою"
+
+msgid "Build a tree-object from ls-tree formatted text"
+msgstr "Побудувати обʼєкт дерева з ls-tree форматованого тексту"
+
+msgid "Write and verify multi-pack-indexes"
+msgstr "Записати і перевірити multi-pack-indexes"
+
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr "Перемістити або перейменувати файл, директорію або символьне посилання"
+
+msgid "Find symbolic names for given revs"
+msgstr "Знайти символьни назви для заданих ревізій"
+
+msgid "Add or inspect object notes"
+msgstr "Додати або перевірити нотатки до обʼєктів"
+
+msgid "Import from and submit to Perforce repositories"
+msgstr "Імпортувати з та надсилати до Perforce сховищ"
+
+msgid "Create a packed archive of objects"
+msgstr "Створити запакований архів обʼєктів"
+
+msgid "Find redundant pack files"
+msgstr "Знайти зайві файли пакунків"
+
+msgid "Pack heads and tags for efficient repository access"
+msgstr "Запакувати верхівки та теги для ефективного доступу до сховища"
+
+msgid "Compute unique ID for a patch"
+msgstr "Обчислити унікальний ідентифікатор для патчу"
+
+msgid "Prune all unreachable objects from the object database"
+msgstr "Видалити всі недосяжні обʼєкти з бази даних обʼєктів"
+
+msgid "Remove extra objects that are already in pack files"
+msgstr "Видалити зайві обʼєкти, які вже є у файлах пакунків"
+
+msgid "Fetch from and integrate with another repository or a local branch"
+msgstr "Отримати та інтегрувати з іншим сховищем або локальною гілкою"
+
+msgid "Update remote refs along with associated objects"
+msgstr "Оновити віддалені посилання разом з асоційованими обʼєктами"
+
+msgid "Applies a quilt patchset onto the current branch"
+msgstr "Застосовує набір латок до поточної гілки"
+
+msgid "Compare two commit ranges (e.g. two versions of a branch)"
+msgstr "Порівняти два діапазони комітів (наприклад, дві версії гілки)"
+
+msgid "Reads tree information into the index"
+msgstr "Зчитує інформацію про дерево в індекс"
+
+msgid "Reapply commits on top of another base tip"
+msgstr "Застосувати коміти поверх іншої верхівки бази"
+
+msgid "Receive what is pushed into the repository"
+msgstr "Отримати те, що надсилається до сховища"
+
+msgid "Manage reflog information"
+msgstr "Керування інформацією журналу посилань"
+
+msgid "Manage set of tracked repositories"
+msgstr "Керувати набором відстежуваних сховищ"
+
+msgid "Pack unpacked objects in a repository"
+msgstr "Запакувати розпаковані обʼєкти у сховищі"
+
+msgid "Create, list, delete refs to replace objects"
+msgstr "Створити, показати, видалити посилання для об’єктів заміни"
+
+msgid "Generates a summary of pending changes"
+msgstr "Створює підсумок змін для розгляду"
+
+msgid "Reuse recorded resolution of conflicted merges"
+msgstr "Використати повторно записане розв’язання конфліктних злиттів"
+
+msgid "Reset current HEAD to the specified state"
+msgstr "Скинути поточний HEAD до вказаного стану"
+
+msgid "Restore working tree files"
+msgstr "Відновити файли робочого дерева"
+
+msgid "Lists commit objects in reverse chronological order"
+msgstr "Показувати обʼєкти коміту у зворотному хронологічному порядку"
+
+msgid "Pick out and massage parameters"
+msgstr "Визначення та обробка параметрів"
+
+msgid "Revert some existing commits"
+msgstr "Вивернути деякі існуючі коміти"
+
+msgid "Remove files from the working tree and from the index"
+msgstr "Видалити файли з робочого дерева та з індексу"
+
+msgid "Send a collection of patches as emails"
+msgstr "Надіслати колекцію латок у вигляді електронних листів"
+
+msgid "Push objects over Git protocol to another repository"
+msgstr "Надіслати обʼєкти за протоколом Git до іншого сховища"
+
+msgid "Git's i18n setup code for shell scripts"
+msgstr "Код налаштування i18n у Git для скриптів оболонки"
+
+msgid "Common Git shell script setup code"
+msgstr "Загальний код налаштування скриптів оболонки Git"
+
+msgid "Restricted login shell for Git-only SSH access"
+msgstr "Обмежений логін оболонки для доступу по SSH лише для Git"
+
+msgid "Summarize 'git log' output"
+msgstr "Підсумувати вивід \"git log\""
+
+msgid "Show various types of objects"
+msgstr "Показати різні типи об’єктів"
+
+msgid "Show branches and their commits"
+msgstr "Показати гілки та їхні коміти"
+
+msgid "Show packed archive index"
+msgstr "Показати індекс запакованого архіву"
+
+msgid "List references in a local repository"
+msgstr "Показати посилання у локальному сховищі"
+
+msgid "Reduce your working tree to a subset of tracked files"
+msgstr "Скоротити робоче дерево до підмножини відстежуваних файлів"
+
+msgid "Add file contents to the staging area"
+msgstr "Додати вміст файлу до області індексації"
+
+msgid "Stash the changes in a dirty working directory away"
+msgstr "Перенести зміни в брудній робочій директорії до схову"
+
+msgid "Show the working tree status"
+msgstr "Показати стан робочого дерева"
+
+msgid "Remove unnecessary whitespace"
+msgstr "Видалити зайві пробіли"
+
+msgid "Initialize, update or inspect submodules"
+msgstr "Ініціалізувати, оновити або перевірити підмодулі"
+
+msgid "Bidirectional operation between a Subversion repository and Git"
+msgstr "Двонаправлена операція між Subversion сховищем та Git"
+
+msgid "Switch branches"
+msgstr "Переключити гілки"
+
+msgid "Read, modify and delete symbolic refs"
+msgstr "Прочитати, змінити та видалити символьні посилання"
+
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr ""
+"Створити, показати, видалити або перевірити обʼєкт тегу, підписаний GPG"
+
+msgid "Creates a temporary file with a blob's contents"
+msgstr "Створює тимчасовий файл із вмістом blob"
+
+msgid "Unpack objects from a packed archive"
+msgstr "Розпакувати обʼєкти із запакованого архіву"
+
+msgid "Register file contents in the working tree to the index"
+msgstr "Зареєструвати вміст файлу робочого дерева в індексі"
+
+msgid "Update the object name stored in a ref safely"
+msgstr "Безпечно оновити назву обʼєкта, що зберігається в посиланні"
+
+msgid "Update auxiliary info file to help dumb servers"
+msgstr "Оновити додатковий інформаційний файл для допомоги \"тупим\" серверам"
+
+msgid "Send archive back to git-archive"
+msgstr "Надіслати архів назад до git-archive"
+
+msgid "Send objects packed back to git-fetch-pack"
+msgstr "Надіслати запаковані обʼєкти назад до git-fetch-pack"
+
+msgid "Show a Git logical variable"
+msgstr "Показати логічну змінну Git"
+
+msgid "Check the GPG signature of commits"
+msgstr "Перевірити GPG-підпис комітів"
+
+msgid "Validate packed Git archive files"
+msgstr "Перевірити запаковані файли Git архіву"
+
+msgid "Check the GPG signature of tags"
+msgstr "Перевірити GPG-підпис тегів"
+
+msgid "Display version information about Git"
+msgstr "Показати інформацію про версію Git"
+
+msgid "Show logs with differences each commit introduces"
+msgstr "Показати журнал з різницею, яку вносить кожен з комітів"
+
+msgid "Manage multiple working trees"
+msgstr "Керувати кількома робочими деревами"
+
+msgid "Create a tree object from the current index"
+msgstr "Створити обʼєкт дерева з поточного індексу"
+
+msgid "Defining attributes per path"
+msgstr "Визначення атрибутів для кожного шляху"
+
+msgid "Git command-line interface and conventions"
+msgstr "Інтерфейс та конвенції командного рядка Git"
+
+msgid "A Git core tutorial for developers"
+msgstr "Інструкція по ядру Git для розробників"
+
+msgid "Providing usernames and passwords to Git"
+msgstr "Надання імен користувачів та паролів до Git"
+
+msgid "Git for CVS users"
+msgstr "Git для користувачів CVS"
+
+msgid "Tweaking diff output"
+msgstr "Налаштування виводу різниці"
+
+msgid "A useful minimum set of commands for Everyday Git"
+msgstr "Корисний мінімальний набір команд для повсякденного використання Git"
+
+msgid "Frequently asked questions about using Git"
+msgstr "Поширені запитання про використання Git"
+
+msgid "The bundle file format"
+msgstr "Формат файлу пакунка"
+
+msgid "Chunk-based file formats"
+msgstr "Файлові формати на основі шматків"
+
+msgid "Git commit-graph format"
+msgstr "Формат Git коміт-графа"
+
+msgid "Git index format"
+msgstr "Формат Git індекса"
+
+msgid "Git pack format"
+msgstr "Формат Git пакунка"
+
+msgid "Git cryptographic signature formats"
+msgstr "Формати криптографічного підпису Git"
+
+msgid "A Git Glossary"
+msgstr "Git словник"
+
+msgid "Hooks used by Git"
+msgstr "Гачки, що використовує Git"
+
+msgid "Specifies intentionally untracked files to ignore"
+msgstr "Вказує навмисно невідстежувані файли для ігнорування"
+
+msgid "The Git repository browser"
+msgstr "Браузер сховища Git"
+
+msgid "Map author/committer names and/or E-Mail addresses"
+msgstr "Зробити мапу імен авторів/комітерів та/або адрес електронної пошти"
+
+msgid "Defining submodule properties"
+msgstr "Визначення властивостей підмодулів"
+
+msgid "Git namespaces"
+msgstr "Простори імен Git"
+
+msgid "Protocol v0 and v1 capabilities"
+msgstr "Здібності протоколів v0 та v1"
+
+msgid "Things common to various protocols"
+msgstr "Спільні риси різних протоколів"
+
+msgid "Git HTTP-based protocols"
+msgstr "Git протоколи на основі HTTP"
+
+msgid "How packs are transferred over-the-wire"
+msgstr "Як пакунки передаються по дроту"
+
+msgid "Git Wire Protocol, Version 2"
+msgstr "Протокол Git Wire, версія 2"
+
+msgid "Helper programs to interact with remote repositories"
+msgstr "Програми-помічники для взаємодії з віддаленими сховищами"
+
+msgid "Git Repository Layout"
+msgstr "Схема Git сховища"
+
+msgid "Specifying revisions and ranges for Git"
+msgstr "Визначення ревізій та діапазонів для Git"
+
+msgid "Mounting one repository inside another"
+msgstr "Монтування одного сховища всередині іншого"
+
+msgid "A tutorial introduction to Git"
+msgstr "Навчальний вступ до Git"
+
+msgid "A tutorial introduction to Git: part two"
+msgstr "Навчальний вступ до Git: частина друга"
+
+msgid "Git web interface (web frontend to Git repositories)"
+msgstr "Веб-інтерфейс Git (веб-фронтенд до сховищ Git)"
+
+msgid "An overview of recommended workflows with Git"
+msgstr "Огляд рекомендованих робочих процесів з Git"
+
+msgid "A tool for managing large Git repositories"
+msgstr "Інструмент для керування великими сховищами Git"
+
+msgid "commit-graph file is too small"
+msgstr "файл коміт-графа занадто малий"
+
+#, c-format
+msgid "commit-graph signature %X does not match signature %X"
+msgstr "підпис коміт-графа %X не збігається з підписом %X"
+
+#, c-format
+msgid "commit-graph version %X does not match version %X"
+msgstr "версія коміт-графа %X не збігається з версією %X"
+
+#, c-format
+msgid "commit-graph hash version %X does not match version %X"
+msgstr "хеш версія коміт-графа %X не збігається з версією %X"
+
+#, c-format
+msgid "commit-graph file is too small to hold %u chunks"
+msgstr "файл коміт-графа занадто малий, щоб вмістити %u шматків"
+
+msgid "commit-graph has no base graphs chunk"
+msgstr "коміт-граф не має шматка базових графів"
+
+msgid "commit-graph base graphs chunk is too small"
+msgstr "занадто малий шматок базових графів коміт-графа"
+
+msgid "commit-graph chain does not match"
+msgstr "ланцюжок коміт-графа не співпадає"
+
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "кількість комітів у базовому графі занадто велика: %<PRIuMAX>"
+
+#, c-format
+msgid "invalid commit-graph chain: line '%s' not a hash"
+msgstr "неприпустимий ланцюжок коміт-графа: рядок \"%s\" не є хешем"
+
+msgid "unable to find all commit-graph files"
+msgstr "не вдалося знайти всі файли коміт-графа"
+
+msgid "invalid commit position. commit-graph is likely corrupt"
+msgstr "неприпустима позиція коміту, ймовірно, коміт-граф пошкоджено"
+
+#, c-format
+msgid "could not find commit %s"
+msgstr "не вдалося знайти коміт %s"
+
+msgid "commit-graph requires overflow generation data but has none"
+msgstr "коміт-граф потребує даних генерації переповнення, але їх немаєданих"
+
+msgid "commit-graph overflow generation data is too small"
+msgstr "занадто мало даних про переповнення коміт-граф генерації"
+
+msgid "Loading known commits in commit graph"
+msgstr "Завантаження відомих комітів у коміт-графі"
+
+msgid "Expanding reachable commits in commit graph"
+msgstr "Розширення досяжних комітів у коміт-графі"
+
+msgid "Clearing commit marks in commit graph"
+msgstr "Очищення позначок комітів у коміт-графі"
+
+msgid "Computing commit graph topological levels"
+msgstr "Обчислення топологічних рівнів коміт-графа"
+
+msgid "Computing commit graph generation numbers"
+msgstr "Обчислення номерів генерації коміт-графа"
+
+msgid "Computing commit changed paths Bloom filters"
+msgstr "Обчислення фільтрів Блума для шляхів, змінених комітом"
+
+msgid "Collecting referenced commits"
+msgstr "Збір посилань на коміти"
+
+#, c-format
+msgid "Finding commits for commit graph in %<PRIuMAX> pack"
+msgid_plural "Finding commits for commit graph in %<PRIuMAX> packs"
+msgstr[0] "Пошук комітів для коміт-графа у %<PRIuMAX> пакунку"
+msgstr[1] "Пошук комітів для коміт-графа у %<PRIuMAX> пакунках"
+msgstr[2] "Пошук комітів для коміт-графа у %<PRIuMAX> пакунках"
+
+#, c-format
+msgid "error adding pack %s"
+msgstr "помилка додавання пакету %s"
+
+#, c-format
+msgid "error opening index for %s"
+msgstr "помилка відкриття індексу для %s"
+
+msgid "Finding commits for commit graph among packed objects"
+msgstr "Пошук комітів для коміт-графа серед запакованих обʼєктів"
+
+msgid "Finding extra edges in commit graph"
+msgstr "Пошук додаткових ребер у коміт-графі"
+
+msgid "failed to write correct number of base graph ids"
+msgstr "не вдалося записати правильну кількість ідентифікаторів базових графів"
+
+msgid "unable to create temporary graph layer"
+msgstr "не вдалося створити тимчасовий шар графа"
+
+#, c-format
+msgid "unable to adjust shared permissions for '%s'"
+msgstr "не вдалося налаштувати спільні дозволи для \"%s\""
+
+#, c-format
+msgid "Writing out commit graph in %d pass"
+msgid_plural "Writing out commit graph in %d passes"
+msgstr[0] "Виведення коміт-графа за %d прохід"
+msgstr[1] "Виведення коміт-графа за %d проходи"
+msgstr[2] "Виведення коміт-графа за %d проходів"
+
+msgid "unable to open commit-graph chain file"
+msgstr "не вдалося відкрити ланцюжковий файл коміт-графа"
+
+msgid "failed to rename base commit-graph file"
+msgstr "не вдалося перейменувати базовий файл коміт-графа"
+
+msgid "failed to rename temporary commit-graph file"
+msgstr "не вдалося перейменувати тимчасовий файл коміт-графа"
+
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "неможливо обʼєднати графи з %<PRIuMAX>, %<PRIuMAX> комітів"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "неможливо обʼєднати граф %s, занадто багато комітів: %<PRIuMAX>"
+
+msgid "Scanning merged commits"
+msgstr "Сканування злитих комітів"
+
+msgid "Merging commit-graph"
+msgstr "Злиття коміт-графа"
+
+msgid "attempting to write a commit-graph, but 'core.commitGraph' is disabled"
+msgstr "спроба записати коміт-граф, але \"core.commitGraph\" відключено"
+
+msgid "too many commits to write graph"
+msgstr "занадто багато комітів, щоб записати граф"
+
+msgid "the commit-graph file has incorrect checksum and is likely corrupt"
+msgstr "файл коміт-графа має невірну контрольну суму і, ймовірно, пошкоджений"
+
+#, c-format
+msgid "commit-graph has incorrect OID order: %s then %s"
+msgstr "коміт-граф має невірну OID послідовність: %s потім %s"
+
+#, c-format
+msgid "commit-graph has incorrect fanout value: fanout[%d] = %u != %u"
+msgstr "коміт-граф має невірне fanout значення: fanout[%d] = %u != %u"
+
+#, c-format
+msgid "failed to parse commit %s from commit-graph"
+msgstr "не вдалося розібрати коміт %s з графа комітв"
+
+#, c-format
+msgid "failed to parse commit %s from object database for commit-graph"
+msgstr "не вдалося розібрати коміт %s з бази даних обʼєктів для коміт-графа"
+
+#, c-format
+msgid "root tree OID for commit %s in commit-graph is %s != %s"
+msgstr "OID кореневого дерева для коміту %s у коміт-графі є %s != %s"
+
+#, c-format
+msgid "commit-graph parent list for commit %s is too long"
+msgstr "список батьків для коміту %s коміт-графа занадто довгий"
+
+#, c-format
+msgid "commit-graph parent for %s is %s != %s"
+msgstr "батько для %s коміт-графа є %s != %s"
+
+#, c-format
+msgid "commit-graph parent list for commit %s terminates early"
+msgstr "список батьків коміт-графа для коміту %s завершився передчасно"
+
+#, c-format
+msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
+msgstr "генерація коміт-графа для коміту %s є %<PRIuMAX> < %<PRIuMAX>"
+
+#, c-format
+msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
+msgstr "дата коміту для коміту %s у коміт-графі є %<PRIuMAX> != %<PRIuMAX>"
+
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"коміт-граф має як нульові, так і ненульові генерації (коміти \"%s\" і \"%s\")"
+
+msgid "Verifying commits in commit graph"
+msgstr "Перевірка комітів у коміт-графі"
+
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s не є комітом!"
+
+msgid ""
+"Support for <GIT_DIR>/info/grafts is deprecated\n"
+"and will be removed in a future Git version.\n"
+"\n"
+"Please use \"git replace --convert-graft-file\"\n"
+"to convert the grafts into replace refs.\n"
+"\n"
+"Turn this message off by running\n"
+"\"git config advice.graftFileDeprecated false\""
+msgstr ""
+"Підтримка <GIT_DIR>/info/grafts застаріла\n"
+"і буде вилучена в одній з наступних версій Git.\n"
+"\n"
+"Будь ласка, скористайтесь \"git replace --convert-graft-file\"\n"
+"щоб перетворити щепи на заміни посилань.\n"
+"\n"
+"Щоб вимкнути це повідомлення, виконайте\n"
+"\"git config advice.graftFileDeprecated false\""
+
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr "коміт %s існує в коміт-графі, але не в базі даних обʼєктів"
+
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr "Коміт %s має недостовірний GPG-підпис, нібито від %s."
+
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr "Коміт %s має невірний GPG-підпис, нібито від %s."
+
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr "Коміт %s не має GPG підпису."
+
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr "Коміт %s має добрий GPG-підпис від %s\n"
+
+msgid ""
+"Warning: commit message did not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitEncoding to the encoding your project uses.\n"
+msgstr ""
+"Попередження: допис до коміту не відповідає кодуванню UTF-8.\n"
+"Можливо, ви захочете змінити його після виправлення допису, або встановити "
+"змінну конфігурації\n"
+"i18n.commitEncoding у кодування, яке використовується у вашому проекті.\n"
+
+msgid "no compiler information available\n"
+msgstr "немає інформації про компілятор\n"
+
+msgid "no libc information available\n"
+msgstr "немає інформації про libc\n"
+
+#, c-format
+msgid "could not determine free disk size for '%s'"
+msgstr "не вдалося визначити вільний розмір диска для \"%s\""
+
+#, c-format
+msgid "could not get info for '%s'"
+msgstr "не вдалося отримати інформацію для \"%s\""
+
+#, c-format
+msgid "[GLE %ld] health thread could not open '%ls'"
+msgstr "[GLE %ld] потік стану не зміг відкрити \"%ls\""
+
+#, c-format
+msgid "[GLE %ld] health thread getting BHFI for '%ls'"
+msgstr "[GLE %ld] потік стану отримує BHFI для \"%ls\""
+
+#, c-format
+msgid "could not convert to wide characters: '%s'"
+msgstr "не вдалося перетворити в широкі символи: \"%s\""
+
+#, c-format
+msgid "BHFI changed '%ls'"
+msgstr "BHFI змінено \"%ls\""
+
+#, c-format
+msgid "unhandled case in 'has_worktree_moved': %d"
+msgstr "необроблений випадок у \"has_worktree_moved\": %d"
+
+#, c-format
+msgid "health thread wait failed [GLE %ld]"
+msgstr "очікування потоку стану завершилося невдало [GLE %ld]"
+
+#, c-format
+msgid "Invalid path: %s"
+msgstr "Неприпустимий шлях: %s"
+
+msgid "Unable to create FSEventStream."
+msgstr "Не вдалося створити FSEventStream."
+
+msgid "Failed to start the FSEventStream"
+msgstr "Не вдалося запустити потік FSEventStream"
+
+#, c-format
+msgid "[GLE %ld] could not convert path to UTF-8: '%.*ls'"
+msgstr "[GLE %ld] не вдалося конвертувати шлях у UTF-8: \"%.*ls\""
+
+#, c-format
+msgid "[GLE %ld] could not watch '%s'"
+msgstr "[GLE %ld] стеження завершилось невдало \"%s\""
+
+#, c-format
+msgid "[GLE %ld] could not get longname of '%s'"
+msgstr "[GLE %ld] не вдалося отримати довгу назву \"%s\""
+
+#, c-format
+msgid "ReadDirectoryChangedW failed on '%s' [GLE %ld]"
+msgstr "ReadDirectoryChangedW завершилось невдало на \"%s\" [GLE %ld]"
+
+#, c-format
+msgid "GetOverlappedResult failed on '%s' [GLE %ld]"
+msgstr "GetOverlappedResult завершилось невдало на \"%s\" [GLE %ld]"
+
+#, c-format
+msgid "could not read directory changes [GLE %ld]"
+msgstr "не вдалося прочитати зміни директорії [GLE %ld]"
+
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "opendir(\"%s\") завершився невдало"
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "lstat(\"%s\") завершився невдало"
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "strbuf_readlink(\"%s\") завершився невдало"
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "closedir(\"%s\") завершився невдало"
+
+#, c-format
+msgid "[GLE %ld] unable to open for read '%ls'"
+msgstr "[GLE %ld] не вдалося відкрити для читання \"%ls\""
+
+#, c-format
+msgid "[GLE %ld] unable to get protocol information for '%ls'"
+msgstr "[GLE %ld] не вдалося отримати інформацію про протокол для \"%ls\""
+
+#, c-format
+msgid "failed to copy SID (%ld)"
+msgstr "не вдалося скопіювати SID (%ld)"
+
+#, c-format
+msgid "failed to get owner for '%s' (%ld)"
+msgstr "не вдалося отримати власника для \"%s\" (%ld)"
+
+msgid "memory exhausted"
+msgstr "памʼять вичерпано"
+
+msgid "Success"
+msgstr "Успіх"
+
+msgid "No match"
+msgstr "Немає збігів"
+
+msgid "Invalid regular expression"
+msgstr "Неприпустимий регулярний вираз"
+
+msgid "Invalid collation character"
+msgstr "Неприпустимий символ зведення"
+
+msgid "Invalid character class name"
+msgstr "Неприпустима назва класу символів"
+
+msgid "Trailing backslash"
+msgstr "Кінцева зворотна коса риска"
+
+msgid "Invalid back reference"
+msgstr "Неприпустиме зворотнє посилання"
+
+msgid "Unmatched [ or [^"
+msgstr "Непарна [ або [^"
+
+msgid "Unmatched ( or \\("
+msgstr "Непарна ( або \\("
+
+msgid "Unmatched \\{"
+msgstr "Непарна \\{"
+
+msgid "Invalid content of \\{\\}"
+msgstr "Неприпустимий вміст для \\{\\}"
+
+msgid "Invalid range end"
+msgstr "Неприпустимий кінець діапазону"
+
+msgid "Memory exhausted"
+msgstr "Памʼять вичерпано"
+
+msgid "Invalid preceding regular expression"
+msgstr "Неприпустимий попередній регулярний вираз"
+
+msgid "Premature end of regular expression"
+msgstr "Передчасне завершення регулярного виразу"
+
+msgid "Regular expression too big"
+msgstr "Занадто великий регулярний вираз"
+
+msgid "Unmatched ) or \\)"
+msgstr "Непарна ) або \\)"
+
+msgid "No previous regular expression"
+msgstr "Немає попереднього регулярного виразу"
+
+msgid "could not send IPC command"
+msgstr "не вдалося відправити IPC команду"
+
+msgid "could not read IPC response"
+msgstr "не вдалося прочитати IPC відповідь"
+
+#, c-format
+msgid "could not start accept_thread '%s'"
+msgstr "не вдалося розпочати accept_thread \"%s\""
+
+#, c-format
+msgid "could not start worker[0] for '%s'"
+msgstr "не вдалося запустити worker[0] для \"%s\""
+
+#, c-format
+msgid "ConnectNamedPipe failed for '%s' (%lu)"
+msgstr "ConnectNamedPipe завершився невдало для \"%s\" (%lu)"
+
+#, c-format
+msgid "could not create fd from pipe for '%s'"
+msgstr "не вдалося створити fd з каналу для \"%s\""
+
+#, c-format
+msgid "could not start thread[0] for '%s'"
+msgstr "не вдалося запустити thread[0] для \"%s\""
+
+#, c-format
+msgid "wait for hEvent failed for '%s'"
+msgstr "очікування на hEvent не вдалося для \"%s\""
+
+msgid "cannot resume in the background, please use 'fg' to resume"
+msgstr ""
+"неможливо поновити роботу у фоновому режимі; будь ласка, скористайтесь "
+"\"fg\" для поновлення"
+
+msgid "cannot restore terminal settings"
+msgstr "не вдалося відновити налаштування термінала"
+
+#, c-format
+msgid ""
+"exceeded maximum include depth (%d) while including\n"
+"\t%s\n"
+"from\n"
+"\t%s\n"
+"This might be due to circular includes."
+msgstr ""
+"перевищено максимальну глибину (%d) під час включення\n"
+"\t%s\n"
+"від\n"
+"\t%s\n"
+"Це може бути повʼязано з циклічним включенням."
+
+#, c-format
+msgid "could not expand include path '%s'"
+msgstr "не вдалося визначити include path \"%s\""
+
+msgid "relative config includes must come from files"
+msgstr "відносні конфігураційні включення мають надходити з файлів"
+
+msgid "relative config include conditionals must come from files"
+msgstr ""
+"відносні умовні позначення конфігураційних включень мають надходити з файлів"
+
+msgid ""
+"remote URLs cannot be configured in file directly or indirectly included by "
+"includeIf.hasconfig:remote.*.url"
+msgstr ""
+"віддалені URL-адреси не можуть бути налаштовані у файлі, прямо чи "
+"опосередковано включеному за допомогою includeIf.hasconfig:remote.*.url"
+
+#, c-format
+msgid "invalid config format: %s"
+msgstr "неприпустимий формат конфігурації: %s"
+
+#, c-format
+msgid "missing environment variable name for configuration '%.*s'"
+msgstr "відсутня назва змінної оточення для конфігурації \"%.*s\""
+
+#, c-format
+msgid "missing environment variable '%s' for configuration '%.*s'"
+msgstr "відсутня змінна оточення \"%s\" для конфігурації \"%.*s\""
+
+#, c-format
+msgid "key does not contain a section: %s"
+msgstr "ключ не містить розділу: %s"
+
+#, c-format
+msgid "key does not contain variable name: %s"
+msgstr "ключ не містить назви змінної: %s"
+
+#, c-format
+msgid "invalid key: %s"
+msgstr "неприпустимий ключ: %s"
+
+#, c-format
+msgid "invalid key (newline): %s"
+msgstr "неприпустимий ключ (новий рядок): %s"
+
+msgid "empty config key"
+msgstr "порожній ключ конфігурації"
+
+#, c-format
+msgid "bogus config parameter: %s"
+msgstr "хибний параметр конфігурації: %s"
+
+#, c-format
+msgid "bogus format in %s"
+msgstr "хибний формат у %s"
+
+#, c-format
+msgid "bogus count in %s"
+msgstr "хибна кількість у %s"
+
+#, c-format
+msgid "too many entries in %s"
+msgstr "забагато записів у %s"
+
+#, c-format
+msgid "missing config key %s"
+msgstr "відсутній ключ конфігурації %s"
+
+#, c-format
+msgid "missing config value %s"
+msgstr "відсутнє значення конфігурації %s"
+
+#, c-format
+msgid "bad config line %d in blob %s"
+msgstr "невірний конфігураційний рядок %d у blob %s"
+
+#, c-format
+msgid "bad config line %d in file %s"
+msgstr "невірний конфігураційний рядок %d у файлі %s"
+
+#, c-format
+msgid "bad config line %d in standard input"
+msgstr "невірний конфігураційний рядок %d у стандартному вводі"
+
+#, c-format
+msgid "bad config line %d in submodule-blob %s"
+msgstr "невірний конфігураційний рядок %d у підмодулі-blob %s"
+
+#, c-format
+msgid "bad config line %d in command line %s"
+msgstr "невірний конфігураційний рядок %d у командному рядку %s"
+
+#, c-format
+msgid "bad config line %d in %s"
+msgstr "невірний конфігураційний рядок %d в %s"
+
+msgid "out of range"
+msgstr "поза межами діапазону"
+
+msgid "invalid unit"
+msgstr "неприпустима одиниця виміру"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s': %s"
+msgstr "невірне числове значення конфігурації \"%s\" для \"%s\": %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in blob %s: %s"
+msgstr "невірне числове значення конфігурації \"%s\" для \"%s\" у blob %s: %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in file %s: %s"
+msgstr "невірне числове значення конфігурації \"%s\" для \"%s\" у файлі %s: %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in standard input: %s"
+msgstr ""
+"невірне числове значення конфігурації \"%s\" для \"%s\" у стандартному "
+"вводі: %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in submodule-blob %s: %s"
+msgstr ""
+"невірне числове значення конфігурації \"%s\" для \"%s\" у підмодулі-blob %s: "
+"%s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in command line %s: %s"
+msgstr ""
+"невірне числове значення конфігурації \"%s\" для \"%s\" у командному рядку "
+"%s: %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in %s: %s"
+msgstr "невірне числове значення конфігурації \"%s\" для \"%s\" у \"%s\": %s"
+
+#, c-format
+msgid "invalid value for variable %s"
+msgstr "неприпустиме значення для змінної %s"
+
+#, c-format
+msgid "ignoring unknown core.fsync component '%s'"
+msgstr "ігнорування невідомого компонента core.fsync \"%s\""
+
+#, c-format
+msgid "bad boolean config value '%s' for '%s'"
+msgstr "невірне булеве значення конфігурації \"%s\" для \"%s\""
+
+#, c-format
+msgid "failed to expand user dir in: '%s'"
+msgstr "не вдалося визначити директорію користувача в: \"%s\""
+
+#, c-format
+msgid "'%s' for '%s' is not a valid timestamp"
+msgstr "\"%s\" для \"%s\" не є припустимою міткою часу"
+
+#, c-format
+msgid "abbrev length out of range: %d"
+msgstr "довжина скорочення поза діапазоном: %d"
+
+#, c-format
+msgid "bad zlib compression level %d"
+msgstr "невірний рівень zlib компресії %d"
+
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar має бути лише одним символом ASCII"
+
+#, c-format
+msgid "ignoring unknown core.fsyncMethod value '%s'"
+msgstr "ігнорування невідомого значення core.fsyncMethod \"%s\""
+
+msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead"
+msgstr "core.fsyncObjectFiles застаріла; натомість використовуйте core.fsync"
+
+#, c-format
+msgid "invalid mode for object creation: %s"
+msgstr "неприпустимий режим створення обʼєкта: %s"
+
+#, c-format
+msgid "malformed value for %s"
+msgstr "невірно сформоване значення для %s"
+
+#, c-format
+msgid "malformed value for %s: %s"
+msgstr "невірно сформоване значення для %s: %s"
+
+msgid "must be one of nothing, matching, simple, upstream or current"
+msgstr "має бути одним з nothing, matching, simple, upstream або current"
+
+#, c-format
+msgid "unable to load config blob object '%s'"
+msgstr "не вдалося завантажити config blob обʼєкт \"%s\""
+
+#, c-format
+msgid "reference '%s' does not point to a blob"
+msgstr "посилання \"%s\" не вказує на blob"
+
+#, c-format
+msgid "unable to resolve config blob '%s'"
+msgstr "не вдалося розпізнати config blob \"%s\""
+
+msgid "unable to parse command-line config"
+msgstr "не вдалося розібрати конфігурацію командного рядка"
+
+msgid "unknown error occurred while reading the configuration files"
+msgstr "невідома помилка виникла під час читання конфігураційних файлів"
+
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "Неприпустимий %s: \"%s\""
+
+#, c-format
+msgid "splitIndex.maxPercentChange value '%d' should be between 0 and 100"
+msgstr ""
+"значення splitIndex.maxPercentChange \"%d\" має бути в діапазоні від 0 до 100"
+
+#, c-format
+msgid "unable to parse '%s' from command-line config"
+msgstr "не вдалося розібрати \"%s\" з конфігурації командного рядка"
+
+#, c-format
+msgid "bad config variable '%s' in file '%s' at line %d"
+msgstr "невірна конфігураційна змінна \"%s\" у файлі \"%s\", рядок \"%d\""
+
+#, c-format
+msgid "invalid section name '%s'"
+msgstr "неприпустима назва секції \"%s\""
+
+#, c-format
+msgid "%s has multiple values"
+msgstr "%s має кілька значень"
+
+#, c-format
+msgid "failed to write new configuration file %s"
+msgstr "не вдалося записати новий конфігураційний файл %s"
+
+#, c-format
+msgid "could not lock config file %s"
+msgstr "не вдалося зафіксувати файл конфігурації %s"
+
+#, c-format
+msgid "opening %s"
+msgstr "відкриття %s"
+
+#, c-format
+msgid "invalid config file %s"
+msgstr "неприпустимий конфігураційний файл %s"
+
+#, c-format
+msgid "fstat on %s failed"
+msgstr "не вдалося виконати fstat на %s"
+
+#, c-format
+msgid "unable to mmap '%s'%s"
+msgstr "не вдалося виконати mmap \"%s\"%s"
+
+#, c-format
+msgid "chmod on %s failed"
+msgstr "не вдалося виконати chmod на %s"
+
+#, c-format
+msgid "could not write config file %s"
+msgstr "не вдалося записати конфігураційний файл %s"
+
+#, c-format
+msgid "could not set '%s' to '%s'"
+msgstr "не вдалося встановити \"%s\" в \"%s\""
+
+#, c-format
+msgid "invalid section name: %s"
+msgstr "неприпустима назва секції: %s"
+
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"відмовлено в роботі з занадто довгим рядком у \"%s\", рядок  %<PRIuMAX>"
+
+#, c-format
+msgid "missing value for '%s'"
+msgstr "відсутнє значення для \"%s\""
+
+msgid "the remote end hung up upon initial contact"
+msgstr "віддалене призначеня відключилося при першому контакті"
+
+msgid ""
+"Could not read from remote repository.\n"
+"\n"
+"Please make sure you have the correct access rights\n"
+"and the repository exists."
+msgstr ""
+"Не вдалося прочитати з віддаленого сховища.\n"
+"\n"
+"Будь ласка, переконайтеся, що у вас є правильні права доступу\n"
+"і що сховище існує."
+
+#, c-format
+msgid "server doesn't support '%s'"
+msgstr "сервер не підтримує \"%s\""
+
+#, c-format
+msgid "server doesn't support feature '%s'"
+msgstr "сервер не підтримує особливість \"%s\""
+
+msgid "expected flush after capabilities"
+msgstr "очікувалось flush після здібностей"
+
+#, c-format
+msgid "ignoring capabilities after first line '%s'"
+msgstr "ігнорування здібностей після першого рядка \"%s\""
+
+msgid "protocol error: unexpected capabilities^{}"
+msgstr "помилка протоколу: неочікувані здібності^{}"
+
+#, c-format
+msgid "protocol error: expected shallow sha-1, got '%s'"
+msgstr "помилка протоколу: очікувалось неглибоке sha-1, отримано \"%s\""
+
+msgid "repository on the other end cannot be shallow"
+msgstr "сховище на іншому кінці не може бути неглибоким"
+
+msgid "invalid packet"
+msgstr "неприпустимий пакет"
+
+#, c-format
+msgid "protocol error: unexpected '%s'"
+msgstr "помилка протоколу: неочікуване \"%s\""
+
+#, c-format
+msgid "unknown object format '%s' specified by server"
+msgstr "невідомий формат обʼєкта \"%s\", вказаний сервером"
+
+#, c-format
+msgid "error on bundle-uri response line %d: %s"
+msgstr "помилка у рядку відповіді bundle-uri %d: %s"
+
+msgid "expected flush after bundle-uri listing"
+msgstr "очікувалось flush після виводу bundle-uri"
+
+msgid "expected response end packet after ref listing"
+msgstr "очікувалось response end пакет після виводу посилань"
+
+#, c-format
+msgid "invalid ls-refs response: %s"
+msgstr "неприпустима ls-refs відповідь: %s"
+
+msgid "expected flush after ref listing"
+msgstr "очікувалось flush після виводу посилань"
+
+#, c-format
+msgid "protocol '%s' is not supported"
+msgstr "протокол \"%s\" не підтримується"
+
+msgid "unable to set SO_KEEPALIVE on socket"
+msgstr "не вдалося встановити SO_KEEPALIVE сокету"
+
+#, c-format
+msgid "Looking up %s ... "
+msgstr "Пошук %s ... "
+
+#, c-format
+msgid "unable to look up %s (port %s) (%s)"
+msgstr "не вдалося знайти %s (порт %s) (%s)"
+
+#. TRANSLATORS: this is the end of "Looking up %s ... "
+#, c-format
+msgid ""
+"done.\n"
+"Connecting to %s (port %s) ... "
+msgstr ""
+"готово.\n"
+"Підключення до %s (порт %s) ... "
+
+#, c-format
+msgid ""
+"unable to connect to %s:\n"
+"%s"
+msgstr ""
+"не вдалося приєднатися до %s:\n"
+"%s"
+
+#. TRANSLATORS: this is the end of "Connecting to %s (port %s) ... "
+msgid "done."
+msgstr "готово."
+
+#, c-format
+msgid "unable to look up %s (%s)"
+msgstr "не вдалося знайти %s (%s)"
+
+#, c-format
+msgid "unknown port %s"
+msgstr "невідомий порт %s"
+
+#, c-format
+msgid "strange hostname '%s' blocked"
+msgstr "дивне імʼя хоста \"%s\" заблоковано"
+
+#, c-format
+msgid "strange port '%s' blocked"
+msgstr "дивний порт \"%s\" заблоковано"
+
+#, c-format
+msgid "cannot start proxy %s"
+msgstr "неможливо запустити проксі %s"
+
+msgid "no path specified; see 'git help pull' for valid url syntax"
+msgstr "не вказано шлях; дивіться \"git help pull\" для чинного url синтаксису"
+
+msgid "newline is forbidden in git:// hosts and repo paths"
+msgstr "новий рядок у хостах git:// та шляхах репозиторіїв заборонено"
+
+msgid "ssh variant 'simple' does not support -4"
+msgstr "ssh опція \"simple\" не підтримує -4"
+
+msgid "ssh variant 'simple' does not support -6"
+msgstr "ssh опція \"simple\" не підтримує -6"
+
+msgid "ssh variant 'simple' does not support setting port"
+msgstr "ssh опція \"simple\" не підтримує встановлення порту"
+
+#, c-format
+msgid "strange pathname '%s' blocked"
+msgstr "дивне імʼя шляху \"%s\" заблоковано"
+
+msgid "unable to fork"
+msgstr "неможливо розгалужити"
+
+msgid "Could not run 'git rev-list'"
+msgstr "Не вдалося запустити \"git rev-list\""
+
+msgid "failed write to rev-list"
+msgstr "не вдалося записати до rev-list"
+
+msgid "failed to close rev-list's stdin"
+msgstr "не вдалося закрити stdin для rev-list"
+
+#, c-format
+msgid "illegal crlf_action %d"
+msgstr "неприпустиме crlf_action %d"
+
+#, c-format
+msgid "CRLF would be replaced by LF in %s"
+msgstr "CRLF буде замінено на LF у %s"
+
+#, c-format
+msgid ""
+"in the working copy of '%s', CRLF will be replaced by LF the next time Git "
+"touches it"
+msgstr ""
+"у робочій копії \"%s\" CRLF буде замінено на LF наступного разу, коли Git "
+"доторкнеться до неї"
+
+#, c-format
+msgid "LF would be replaced by CRLF in %s"
+msgstr "LF буде замінено на CRLF у %s"
+
+#, c-format
+msgid ""
+"in the working copy of '%s', LF will be replaced by CRLF the next time Git "
+"touches it"
+msgstr ""
+"у робочій копії \"%s\" LF буде замінено на CRLF наступного разу, коли Git "
+"доторкнеться до неї"
+
+#, c-format
+msgid "BOM is prohibited in '%s' if encoded as %s"
+msgstr "BOM заборонено в \"%s\", якщо закодовано як %s"
+
+#, c-format
+msgid ""
+"The file '%s' contains a byte order mark (BOM). Please use UTF-%.*s as "
+"working-tree-encoding."
+msgstr ""
+"Файл \"%s\" містить позначку порядку байтів (BOM). Будь ласка, "
+"використовуйте UTF-%.*s як кодування робочого дерева."
+
+#, c-format
+msgid "BOM is required in '%s' if encoded as %s"
+msgstr "BOM необхідний в \"%s\", якщо закодовано як %s"
+
+#, c-format
+msgid ""
+"The file '%s' is missing a byte order mark (BOM). Please use UTF-%sBE or UTF-"
+"%sLE (depending on the byte order) as working-tree-encoding."
+msgstr ""
+"У файлі \"%s\" відсутня позначка послідовності байтів (BOM). Будь ласка, "
+"використовуйте UTF-%sBE або UTF-%sLE (залежно від послідовності байтів) як "
+"кодування робочого дерева."
+
+#, c-format
+msgid "failed to encode '%s' from %s to %s"
+msgstr "не вдалося закодувати \"%s\" з %s в %s"
+
+#, c-format
+msgid "encoding '%s' from %s to %s and back is not the same"
+msgstr "кодування \"%s\" з %s в %s і назад не однакове"
+
+#, c-format
+msgid "cannot fork to run external filter '%s'"
+msgstr "неможливо розгалужити для запуску зовнішнього фільтра \"%s\""
+
+#, c-format
+msgid "cannot feed the input to external filter '%s'"
+msgstr "неможливо подати вхідні дані на зовнішній фільтр \"%s\""
+
+#, c-format
+msgid "external filter '%s' failed %d"
+msgstr "помилка зовнішнього фільтра \"%s\", код: %d"
+
+#, c-format
+msgid "read from external filter '%s' failed"
+msgstr "не вдалося прочитати з зовнішнього фільтра \"%s\""
+
+#, c-format
+msgid "external filter '%s' failed"
+msgstr "помилка зовнішнього фільтра \"%s\""
+
+msgid "unexpected filter type"
+msgstr "несподіваний тип фільтра"
+
+msgid "path name too long for external filter"
+msgstr "назва шляху занадто довга для зовнішнього фільтра"
+
+#, c-format
+msgid ""
+"external filter '%s' is not available anymore although not all paths have "
+"been filtered"
+msgstr ""
+"зовнішній фільтр \"%s\" більше не доступний, хоча не всі шляхи були "
+"відфільтровані"
+
+msgid "true/false are no valid working-tree-encodings"
+msgstr "true/false не є допустимими кодуваннями робочого дерева"
+
+#, c-format
+msgid "%s: clean filter '%s' failed"
+msgstr "%s: помилка фільтра очистки \"%s\""
+
+#, c-format
+msgid "%s: smudge filter %s failed"
+msgstr "%s: помилка фільтра розмиття %s"
+
+#, c-format
+msgid "skipping credential lookup for key: credential.%s"
+msgstr "пропуск пошуку облікових даних для ключа: credential.%s"
+
+msgid "refusing to work with credential missing host field"
+msgstr "відмовлено в роботі з відсутнім полем хоста в облікових даних"
+
+msgid "refusing to work with credential missing protocol field"
+msgstr "відмовлено в роботі з відсутнім полем протоколу облікових даних"
+
+#, c-format
+msgid "url contains a newline in its %s component: %s"
+msgstr "url містить новий рядок у %s компоненті: %s"
+
+#, c-format
+msgid "url has no scheme: %s"
+msgstr "url не має схеми: %s"
+
+#, c-format
+msgid "credential url cannot be parsed: %s"
+msgstr "неможливо розібрати url облікових даних: %s"
+
+msgid "in the future"
+msgstr "у майбутньому"
+
+#, c-format
+msgid "%<PRIuMAX> second ago"
+msgid_plural "%<PRIuMAX> seconds ago"
+msgstr[0] "%<PRIuMAX> секунду тому"
+msgstr[1] "%<PRIuMAX> секунди тому"
+msgstr[2] "%<PRIuMAX> секунд тому"
+
+#, c-format
+msgid "%<PRIuMAX> minute ago"
+msgid_plural "%<PRIuMAX> minutes ago"
+msgstr[0] "%<PRIuMAX> хвилину тому"
+msgstr[1] "%<PRIuMAX> хвилини тому"
+msgstr[2] "%<PRIuMAX> хвилин тому"
+
+#, c-format
+msgid "%<PRIuMAX> hour ago"
+msgid_plural "%<PRIuMAX> hours ago"
+msgstr[0] "%<PRIuMAX> годину тому"
+msgstr[1] "%<PRIuMAX> години тому"
+msgstr[2] "%<PRIuMAX> годин тому"
+
+#, c-format
+msgid "%<PRIuMAX> day ago"
+msgid_plural "%<PRIuMAX> days ago"
+msgstr[0] "%<PRIuMAX> день тому"
+msgstr[1] "%<PRIuMAX> дні тому"
+msgstr[2] "%<PRIuMAX> днів тому"
+
+#, c-format
+msgid "%<PRIuMAX> week ago"
+msgid_plural "%<PRIuMAX> weeks ago"
+msgstr[0] "%<PRIuMAX> тиждень тому"
+msgstr[1] "%<PRIuMAX> тижні тому"
+msgstr[2] "%<PRIuMAX> тижнів тому"
+
+#, c-format
+msgid "%<PRIuMAX> month ago"
+msgid_plural "%<PRIuMAX> months ago"
+msgstr[0] "%<PRIuMAX> місяць тому"
+msgstr[1] "%<PRIuMAX> місяці тому"
+msgstr[2] "%<PRIuMAX> місяців тому"
+
+#, c-format
+msgid "%<PRIuMAX> year"
+msgid_plural "%<PRIuMAX> years"
+msgstr[0] "%<PRIuMAX> рік"
+msgstr[1] "%<PRIuMAX> роки"
+msgstr[2] "%<PRIuMAX> років"
+
+#. TRANSLATORS: "%s" is "<n> years"
+#, c-format
+msgid "%s, %<PRIuMAX> month ago"
+msgid_plural "%s, %<PRIuMAX> months ago"
+msgstr[0] "%s, %<PRIuMAX> місяць тому"
+msgstr[1] "%s, %<PRIuMAX> місяці тому"
+msgstr[2] "%s, %<PRIuMAX> місяців тому"
+
+#, c-format
+msgid "%<PRIuMAX> year ago"
+msgid_plural "%<PRIuMAX> years ago"
+msgstr[0] "%<PRIuMAX> рік тому"
+msgstr[1] "%<PRIuMAX> роки тому"
+msgstr[2] "%<PRIuMAX> років тому"
+
+msgid "Propagating island marks"
+msgstr "Розповсюдження острівних позначок"
+
+#, c-format
+msgid "bad tree object %s"
+msgstr "невірний обʼєкт дерева %s"
+
+#, c-format
+msgid "failed to load island regex for '%s': %s"
+msgstr "не вдалося завантажити island регвир для \"%s\": %s"
+
+#, c-format
+msgid "island regex from config has too many capture groups (max=%d)"
+msgstr "регвир острова з конфігурації має забагато груп захоплення (max=%d)"
+
+#, c-format
+msgid "Marked %d islands, done.\n"
+msgstr "Позначено %d островів, готово.\n"
+
+#, c-format
+msgid "invalid --%s value '%s'"
+msgstr "неприпустиме --%s значення \"%s\""
+
+#, c-format
+msgid "could not archive missing directory '%s'"
+msgstr "не вдалося заархівувати відсутню директорію \"%s\""
+
+#, c-format
+msgid "could not open directory '%s'"
+msgstr "не вдалося відкрити директорію \"%s\""
+
+#, c-format
+msgid "skipping '%s', which is neither file nor directory"
+msgstr "пропускання \"%s\", який не є ні файлом, ні директорією"
+
+msgid "could not duplicate stdout"
+msgstr "не вдалося продублювати stdout"
+
+#, c-format
+msgid "could not add directory '%s' to archiver"
+msgstr "не вдалося додати директорію \"%s\" до архіватора"
+
+msgid "failed to write archive"
+msgstr "не вдалося записати архів"
+
+msgid "--merge-base does not work with ranges"
+msgstr "--merge-base не працює з діапазонами"
+
+msgid "unable to get HEAD"
+msgstr "не вдалося отримати HEAD"
+
+msgid "no merge base found"
+msgstr "базу злиття не знайдено"
+
+msgid "multiple merge bases found"
+msgstr "знайдено кілька баз злиття"
+
+msgid "cannot compare stdin to a directory"
+msgstr "неможливо порівняти stdin з директорією"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "неможливо порівняти іменований канал з директорією"
+
+msgid "git diff --no-index [<options>] <path> <path>"
+msgstr "git diff --no-index [<опції>] <шлях> <шлях>"
+
+msgid ""
+"Not a git repository. Use --no-index to compare two paths outside a working "
+"tree"
+msgstr ""
+"Це не git сховище. Скористайтесь --no-index для порівняння двох шляхів поза "
+"робочим деревом"
+
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%s'\n"
+msgstr " Не вдалося розібрати відсоток відсікання dirstat \"%s\"\n"
+
+#, c-format
+msgid "  Unknown dirstat parameter '%s'\n"
+msgstr " Невідомий параметр dirstat \"%s\"\n"
+
+msgid ""
+"color moved setting must be one of 'no', 'default', 'blocks', 'zebra', "
+"'dimmed-zebra', 'plain'"
+msgstr ""
+"значення параметра кольору перенесення має бути одним з \"no\", \"default\", "
+"\"blocks\", \"zebra\", \"dimmed-zebra\", \"plain\""
+
+#, c-format
+msgid ""
+"unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', "
+"'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"
+msgstr ""
+"невідомий режим color-moved-ws \"%s\", можливі значення \"ignore-space-"
+"change\", \"ignore-space-at-eol\", \"ignore-all-space\", \"allow-indentation-"
+"change\""
+
+msgid ""
+"color-moved-ws: allow-indentation-change cannot be combined with other "
+"whitespace modes"
+msgstr ""
+"color-moved-ws: allow-indentation-change не можна комбінувати з іншими "
+"режимами відображення пробілів"
+
+#, c-format
+msgid "Unknown value for 'diff.submodule' config variable: '%s'"
+msgstr "Невідоме значення конфігураційної змінної \"diff.submodule\": \"%s\""
+
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+"Знайдено помилки у конфігураційній змінній \"diff.dirstat\":\n"
+"%s"
+
+#, c-format
+msgid "external diff died, stopping at %s"
+msgstr "зовнішній diff завершився невдало, зупинившись на %s"
+
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow потрібен лишень один визначник шляху"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "магічне значення визначника шляху не підтримується --follow: %s"
+
+#, c-format
+msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
+msgstr "опції \"%s\", \"%s\", \"%s\" та \"%s\" неможливо використовувати разом"
+
+#, c-format
+msgid "options '%s' and '%s' cannot be used together, use '%s' with '%s'"
+msgstr ""
+"опції \"%s\" і \"%s\" неможливо використати разом, використовуйте \"%s\" з "
+"\"%s\""
+
+#, c-format
+msgid ""
+"options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"
+msgstr ""
+"опції \"%s\" і \"%s\" неможливо використати разом, використовуйте \"%s\" з "
+"\"%s\" та \"%s\""
+
+#, c-format
+msgid "invalid --stat value: %s"
+msgstr "неприпустиме --stat значення: %s"
+
+#, c-format
+msgid "%s expects a numerical value"
+msgstr "%s очікує числове значення"
+
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+"Не вдалося розібрати параметр опції --dirstat/-X:\n"
+"%s"
+
+#, c-format
+msgid "unknown change class '%c' in --diff-filter=%s"
+msgstr "невідома зміна класу \"%c\" у --diff-filter=%s"
+
+#, c-format
+msgid "unknown value after ws-error-highlight=%.*s"
+msgstr "невідоме значення після ws-error-highlight=%.*s"
+
+#, c-format
+msgid "unable to resolve '%s'"
+msgstr "не вдалося розпізнати \"%s\""
+
+#, c-format
+msgid "%s expects <n>/<m> form"
+msgstr "%s очікує <n>/<m> форму"
+
+#, c-format
+msgid "%s expects a character, got '%s'"
+msgstr "%s очікує символ, отримано \"%s\""
+
+#, c-format
+msgid "bad --color-moved argument: %s"
+msgstr "невірний --color-moved аргумент: %s"
+
+#, c-format
+msgid "invalid mode '%s' in --color-moved-ws"
+msgstr "неприпустимий режим \"%s\" у --color-moved-ws"
+
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"опція diff-algorithm приймає значення \"myers\", \"minimal\", \"patience\" "
+"та \"histogram\""
+
+#, c-format
+msgid "invalid argument to %s"
+msgstr "неприпустимий аргумент до %s"
+
+#, c-format
+msgid "invalid regex given to -I: '%s'"
+msgstr "неприпустимий regex, переданий до -I: \"%s\""
+
+#, c-format
+msgid "failed to parse --submodule option parameter: '%s'"
+msgstr "не вдалося розібрати параметр опції --submodule: \"%s\""
+
+#, c-format
+msgid "bad --word-diff argument: %s"
+msgstr "невірний --word-diff аргумент: %s"
+
+msgid "Diff output format options"
+msgstr "Варіанти формату виводу різниці"
+
+msgid "generate patch"
+msgstr "згенерувати латку"
+
+msgid "<n>"
+msgstr "<n>"
+
+msgid "generate diffs with <n> lines context"
+msgstr "згенерувати різниці з контекстом в <n> рядків"
+
+msgid "generate the diff in raw format"
+msgstr "згенерувати різницю у форматі raw"
+
+msgid "synonym for '-p --raw'"
+msgstr "синонім для \"-p --raw\""
+
+msgid "synonym for '-p --stat'"
+msgstr "синонім для \"-p --stat\""
+
+msgid "machine friendly --stat"
+msgstr "машинний вивід --stat"
+
+msgid "output only the last line of --stat"
+msgstr "вивести лише останній рядок --stat"
+
+msgid "<param1,param2>..."
+msgstr "<параметр1,параметр2>..."
+
+msgid ""
+"output the distribution of relative amount of changes for each sub-directory"
+msgstr "вивести розподіл відносної кількості змін для кожної піддиректорії"
+
+msgid "synonym for --dirstat=cumulative"
+msgstr "синонім для --dirstat=cumulative"
+
+msgid "synonym for --dirstat=files,param1,param2..."
+msgstr "синонім для --dirstat=files,параметр1,параметр2..."
+
+msgid "warn if changes introduce conflict markers or whitespace errors"
+msgstr ""
+"попереджати, якщо зміни призводять до появи конфліктних маркерів або помилок "
+"пробільних символів"
+
+msgid "condensed summary such as creations, renames and mode changes"
+msgstr "стислі підсумки, такі як створення, перейменування та зміни режимів"
+
+msgid "show only names of changed files"
+msgstr "показувати тільки назви змінених файлів"
+
+msgid "show only names and status of changed files"
+msgstr "показувати тільки назви та статус змінених файлів"
+
+msgid "<width>[,<name-width>[,<count>]]"
+msgstr "<ширина>[,<ширина-назви>[,<кількість>]]"
+
+msgid "generate diffstat"
+msgstr "згенерувати diffstat"
+
+msgid "<width>"
+msgstr "<ширина>"
+
+msgid "generate diffstat with a given width"
+msgstr "згенерувати diffstat із заданою шириною"
+
+msgid "generate diffstat with a given name width"
+msgstr "згенерувати diffstat із заданою шириною назви"
+
+msgid "generate diffstat with a given graph width"
+msgstr "згенерувати diffstat із заданою шириною графіка"
+
+msgid "<count>"
+msgstr "<кількість>"
+
+msgid "generate diffstat with limited lines"
+msgstr "згенерувати diffstat з обмеженою кількістю рядків"
+
+msgid "generate compact summary in diffstat"
+msgstr "згенерувати компактний підсумок у diffstat"
+
+msgid "output a binary diff that can be applied"
+msgstr "вивести бінарну різницю, яку можна застосувати"
+
+msgid "show full pre- and post-image object names on the \"index\" lines"
+msgstr ""
+"показувати повні назви обʼєктів до та після зображення в \"index\" рядках"
+
+msgid "show colored diff"
+msgstr "показати кольорову різницю"
+
+msgid "<kind>"
+msgstr "<тип>"
+
+msgid ""
+"highlight whitespace errors in the 'context', 'old' or 'new' lines in the "
+"diff"
+msgstr ""
+"виділяти помилки пробільних символів у \"context\", \"old\" або \"new\" "
+"рядках різниці"
+
+msgid ""
+"do not munge pathnames and use NULs as output field terminators in --raw or "
+"--numstat"
+msgstr ""
+"не змінювати імена шляхів і використовувати NUL як термінатори полів виводу "
+"в --raw або --numstat"
+
+msgid "<prefix>"
+msgstr "<префікс>"
+
+msgid "show the given source prefix instead of \"a/\""
+msgstr "показувати заданий префікс джерела замість \"a/\""
+
+msgid "show the given destination prefix instead of \"b/\""
+msgstr "показувати заданий префікс призначення замість \"b/\""
+
+msgid "prepend an additional prefix to every line of output"
+msgstr "додавати префікс до кожного рядка виводу"
+
+msgid "do not show any source or destination prefix"
+msgstr "не показувати жодного префікса джерела або призначення"
+
+msgid "use default prefixes a/ and b/"
+msgstr "використовувати префікси за замовчуванням a/ та b/"
+
+msgid "show context between diff hunks up to the specified number of lines"
+msgstr "показувати контекст між шматками різниці до вказаної кількості рядків"
+
+msgid "<char>"
+msgstr "<символ>"
+
+msgid "specify the character to indicate a new line instead of '+'"
+msgstr "вкажіть символ для позначення нового рядка замість \"+\""
+
+msgid "specify the character to indicate an old line instead of '-'"
+msgstr "вкажіть символ для позначення старого рядка замість \"-\""
+
+msgid "specify the character to indicate a context instead of ' '"
+msgstr "вкажіть символ для позначення контексту замість \" \""
+
+msgid "Diff rename options"
+msgstr "Варіанти перейменування різниці"
+
+msgid "<n>[/<m>]"
+msgstr "<n>[/<m>]"
+
+msgid "break complete rewrite changes into pairs of delete and create"
+msgstr "розбити повний перезапис змін на пари delete та create"
+
+msgid "detect renames"
+msgstr "виявляти перейменування"
+
+msgid "omit the preimage for deletes"
+msgstr "пропускати попереднє зображення для видалень"
+
+msgid "detect copies"
+msgstr "виявляти копії"
+
+msgid "use unmodified files as source to find copies"
+msgstr "використовувати незмінені файли як джерело для пошуку копій"
+
+msgid "disable rename detection"
+msgstr "вимкнути виявлення перейменування"
+
+msgid "use empty blobs as rename source"
+msgstr "використовувати порожні blobs як джерело перейменування"
+
+msgid "continue listing the history of a file beyond renames"
+msgstr "продовжити виведення історії файлу після перейменування"
+
+msgid ""
+"prevent rename/copy detection if the number of rename/copy targets exceeds "
+"given limit"
+msgstr ""
+"запобігати виявленню перейменування/копіювання, якщо кількість обʼєктів "
+"перейменування/копіювання перевищує заданий ліміт"
+
+msgid "Diff algorithm options"
+msgstr "Варіанти алгоритмів різниці"
+
+msgid "produce the smallest possible diff"
+msgstr "створювати найменшу можливу різницю"
+
+msgid "ignore whitespace when comparing lines"
+msgstr "ігнорувати пробіли при порівнянні рядків"
+
+msgid "ignore changes in amount of whitespace"
+msgstr "ігнорувати зміни кількості пробілів"
+
+msgid "ignore changes in whitespace at EOL"
+msgstr "ігнорувати зміни пробілів у EOL"
+
+msgid "ignore carrier-return at the end of line"
+msgstr "ігнорувати повернення каретки в кінці рядка"
+
+msgid "ignore changes whose lines are all blank"
+msgstr "ігнорувати зміни, всі рядки яких порожні"
+
+msgid "<regex>"
+msgstr "<регвир>"
+
+msgid "ignore changes whose all lines match <regex>"
+msgstr "ігнорувати зміни, всі рядки яких збігаються з <регвир>"
+
+msgid "heuristic to shift diff hunk boundaries for easy reading"
+msgstr "евристичне визначення зміщення меж шматка різниці для зручного читання"
+
+msgid "generate diff using the \"patience diff\" algorithm"
+msgstr "згенерувати різницю за алгоритмом \"patience diff\""
+
+msgid "generate diff using the \"histogram diff\" algorithm"
+msgstr "згенерувати різницю за алгоритмом \"histogram diff\""
+
+msgid "<algorithm>"
+msgstr "<алгоритм>"
+
+msgid "choose a diff algorithm"
+msgstr "вибрати алгоритм різниці"
+
+msgid "<text>"
+msgstr "<текст>"
+
+msgid "generate diff using the \"anchored diff\" algorithm"
+msgstr "згенерувати різницю за алгоритмом \"anchored diff\""
+
+msgid "<mode>"
+msgstr "<режим>"
+
+msgid "show word diff, using <mode> to delimit changed words"
+msgstr ""
+"показати різницю по словам, використовуючи <режим> для відокремлення "
+"змінених слів"
+
+msgid "use <regex> to decide what a word is"
+msgstr "скористайтесь <регвир>, щоб визначити, що це за слово"
+
+msgid "equivalent to --word-diff=color --word-diff-regex=<regex>"
+msgstr "еквівалентно --word-diff=color --word-diff-regex=<регвир>"
+
+msgid "moved lines of code are colored differently"
+msgstr "переміщені рядки коду мають інший колір"
+
+msgid "how white spaces are ignored in --color-moved"
+msgstr "як ігноруються пробіли в --color-moved"
+
+msgid "Other diff options"
+msgstr "Інші опції різниці"
+
+msgid "when run from subdir, exclude changes outside and show relative paths"
+msgstr ""
+"при запуску з піддиректорії не враховувати зміни ззовні та показувати "
+"відносні шляхи"
+
+msgid "treat all files as text"
+msgstr "обробляти всі файли як текстові"
+
+msgid "swap two inputs, reverse the diff"
+msgstr "поміняти місцями два вводи, змінити різницю на протилежну"
+
+msgid "exit with 1 if there were differences, 0 otherwise"
+msgstr ""
+"завершити виконання з кодом 1, якщо були різниці, або з кодом 0 в іншому "
+"випадку"
+
+msgid "disable all output of the program"
+msgstr "вимкнути весь вивід програми"
+
+msgid "allow an external diff helper to be executed"
+msgstr "дозволити виконання зовнішнього помічника різниці"
+
+msgid "run external text conversion filters when comparing binary files"
+msgstr ""
+"запускати зовнішні фільтри конвертації тексту під час порівняння бінарних "
+"файлів"
+
+msgid "<when>"
+msgstr "<коли>"
+
+msgid "ignore changes to submodules in the diff generation"
+msgstr "ігнорувати зміни підмодулів у генерації різниці"
+
+msgid "<format>"
+msgstr "<формат>"
+
+msgid "specify how differences in submodules are shown"
+msgstr "вказати як відображати різниці в підмодулях"
+
+msgid "hide 'git add -N' entries from the index"
+msgstr "приховати записи \"git add -N\" з індексу"
+
+msgid "treat 'git add -N' entries as real in the index"
+msgstr "обробляти \"git add -N\" записи як дійсні в індексі"
+
+msgid "<string>"
+msgstr "<строка>"
+
+msgid ""
+"look for differences that change the number of occurrences of the specified "
+"string"
+msgstr "шукати різниці, які змінюють кількість входжень вказаного рядка"
+
+msgid ""
+"look for differences that change the number of occurrences of the specified "
+"regex"
+msgstr "шукати різниці, які змінюють кількість входжень вказаного регвиру"
+
+msgid "show all changes in the changeset with -S or -G"
+msgstr "показати всі зміни у змінному наборі з -S або -G"
+
+msgid "treat <string> in -S as extended POSIX regular expression"
+msgstr "обробляти <рядок> у -S як розширений POSIX регулярний вираз"
+
+msgid "control the order in which files appear in the output"
+msgstr "керувати порядком, у якому файли зʼявляються у виводі"
+
+msgid "<path>"
+msgstr "<шлях>"
+
+msgid "show the change in the specified path first"
+msgstr "спочатку показати зміну для зазначеного шляху"
+
+msgid "skip the output to the specified path"
+msgstr "пропустити вивід для зазначеного шляху"
+
+msgid "<object-id>"
+msgstr "<id-обʼєкта>"
+
+msgid ""
+"look for differences that change the number of occurrences of the specified "
+"object"
+msgstr "шукати різниці, які змінюють кількість входжень вказаного обʼєкта"
+
+msgid "[(A|C|D|M|R|T|U|X|B)...[*]]"
+msgstr "[(A|C|D|M|R|T|U|X|B)...[*]]"
+
+msgid "select files by diff type"
+msgstr "вибрати файли за типом різниці"
+
+msgid "<file>"
+msgstr "<файл>"
+
+msgid "output to a specific file"
+msgstr "виводити до певного файла"
+
+msgid "exhaustive rename detection was skipped due to too many files."
+msgstr ""
+"вичерпне виявлення перейменування було пропущено через занадто велику "
+"кількість файлів."
+
+msgid "only found copies from modified paths due to too many files."
+msgstr ""
+"знайдено лише копії зі змінених шляхів через занадто велику кількість файлів."
+
+#, c-format
+msgid ""
+"you may want to set your %s variable to at least %d and retry the command."
+msgstr ""
+"можливо, вам слід встановити значення змінної %s принаймні у %d і повторити "
+"команду."
+
+#, c-format
+msgid "failed to read orderfile '%s'"
+msgstr "не вдалося прочитати orderfile \"%s\""
+
+msgid "Performing inexact rename detection"
+msgstr "Виявлення неточного перейменування"
+
+#, c-format
+msgid "No such path '%s' in the diff"
+msgstr "Немає такого шляху \"%s\" у різниці"
+
+#, c-format
+msgid "pathspec '%s' did not match any file(s) known to git"
+msgstr "визначник шляху \"%s\" не збігається з жодним файлом, відомим git"
+
+#, c-format
+msgid "unrecognized pattern: '%s'"
+msgstr "нерозпізнаний шаблон: \"%s\""
+
+#, c-format
+msgid "unrecognized negative pattern: '%s'"
+msgstr "нерозпізнаний негативний шаблон: \"%s\""
+
+#, c-format
+msgid "your sparse-checkout file may have issues: pattern '%s' is repeated"
+msgstr ""
+"ваш файл розрідженого переходу може мати певні проблеми: шаблон \"%s\" "
+"повторюється"
+
+msgid "disabling cone pattern matching"
+msgstr "вимкнення зіставлення шаблону конуса"
+
+#, c-format
+msgid "cannot use %s as an exclude file"
+msgstr "неможливо використовувати %s як файл виключення"
+
+msgid "failed to get kernel name and information"
+msgstr "не вдалося отримати назву та інформацію про ядро"
+
+msgid "untracked cache is disabled on this system or location"
+msgstr "невідстежуваний кеш вимкнено на цій системі або в цьому місці"
+
+msgid ""
+"No directory name could be guessed.\n"
+"Please specify a directory on the command line"
+msgstr ""
+"Hе вдалося вгадати назву директорії.\n"
+"Будь ласка, вкажіть директорію в командному рядку"
+
+#, c-format
+msgid "index file corrupt in repo %s"
+msgstr "пошкоджено індексний файл у сховищі %s"
+
+#, c-format
+msgid "could not create directories for %s"
+msgstr "не вдалося створити директорії для %s"
+
+#, c-format
+msgid "could not migrate git directory from '%s' to '%s'"
+msgstr "не вдалося перенести git директорію з \"%s\" до \"%s\""
+
+#, c-format
+msgid "hint: Waiting for your editor to close the file...%c"
+msgstr "підказка: чекаємо, поки редактор закриє файл...%c"
+
+#, c-format
+msgid "could not write to '%s'"
+msgstr "не вдалося записати в \"%s\""
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "не вдалося відредагувати \"%s\""
+
+msgid "Filtering content"
+msgstr "Фільтрація вмісту"
+
+#, c-format
+msgid "could not stat file '%s'"
+msgstr "не вдалося виконати stat для %s"
+
+#, c-format
+msgid "bad git namespace path \"%s\""
+msgstr "невірний шлях до простору імен git \"%s\""
+
+#, c-format
+msgid "too many args to run %s"
+msgstr "забагато аргументів для запуску %s"
+
+msgid "git fetch-pack: expected shallow list"
+msgstr "git fetch-pack: очікувався неглибокий список"
+
+msgid "git fetch-pack: expected a flush packet after shallow list"
+msgstr "git fetch-pack: очікувалось flush пакет після неглибокого списку"
+
+msgid "git fetch-pack: expected ACK/NAK, got a flush packet"
+msgstr "git fetch-pack: очікувалось ACK/NAK, отримано flush пакет"
+
+#, c-format
+msgid "git fetch-pack: expected ACK/NAK, got '%s'"
+msgstr "git fetch-pack: очікувалось ACK/NAK, отримано \"%s\""
+
+msgid "unable to write to remote"
+msgstr "не вдалося записати до віддаленого сховища"
+
+msgid "Server supports filter"
+msgstr "Сервер підтримує фільтр"
+
+#, c-format
+msgid "invalid shallow line: %s"
+msgstr "неприпустимий shallow рядок: %s"
+
+#, c-format
+msgid "invalid unshallow line: %s"
+msgstr "неприпустимий unshallow рядок: %s"
+
+#, c-format
+msgid "object not found: %s"
+msgstr "обʼєкт не знайдено: %s"
+
+#, c-format
+msgid "error in object: %s"
+msgstr "помилка в обʼєкті: %s"
+
+#, c-format
+msgid "no shallow found: %s"
+msgstr "не знайдено неглибоких: %s"
+
+#, c-format
+msgid "expected shallow/unshallow, got %s"
+msgstr "очікувалось shallow/unshallow, отримано %s"
+
+#, c-format
+msgid "got %s %d %s"
+msgstr "отримано %s %d %s"
+
+#, c-format
+msgid "invalid commit %s"
+msgstr "неприпустимий коміт %s"
+
+msgid "giving up"
+msgstr "здаюся"
+
+msgid "done"
+msgstr "готово"
+
+#, c-format
+msgid "got %s (%d) %s"
+msgstr "отримано %s (%d) %s"
+
+#, c-format
+msgid "Marking %s as complete"
+msgstr "Позначення %s як завершеного"
+
+#, c-format
+msgid "already have %s (%s)"
+msgstr "вже є %s (%s)"
+
+msgid "fetch-pack: unable to fork off sideband demultiplexer"
+msgstr "fetch-pack: не вдалося розгалужити sideband demultiplexer"
+
+msgid "protocol error: bad pack header"
+msgstr "помилка протоколу: невірний заголовок пакунка"
+
+#, c-format
+msgid "fetch-pack: unable to fork off %s"
+msgstr "fetch-pack: не вдалося розгалужити %s"
+
+msgid "fetch-pack: invalid index-pack output"
+msgstr "fetch-pack: неприпустиме виведення індексного пакунка"
+
+#, c-format
+msgid "%s failed"
+msgstr "%s завершився невдало"
+
+msgid "error in sideband demultiplexer"
+msgstr "помилка в sideband demultiplexer"
+
+#, c-format
+msgid "Server version is %.*s"
+msgstr "Версія сервера %.*s"
+
+#, c-format
+msgid "Server supports %s"
+msgstr "Сервер підтримує %s"
+
+msgid "Server does not support shallow clients"
+msgstr "Сервер не підтримує неглибоких клієнтів"
+
+msgid "Server does not support --shallow-since"
+msgstr "Сервер не підтримує параметр --shallow-since"
+
+msgid "Server does not support --shallow-exclude"
+msgstr "Сервер не підтримує параметр --shallow-exclude"
+
+msgid "Server does not support --deepen"
+msgstr "Сервер не підтримує параметр --deepen"
+
+msgid "Server does not support this repository's object format"
+msgstr "Сервер не підтримує формат об’єктів цього сховища"
+
+msgid "no common commits"
+msgstr "немає спільних комітів"
+
+msgid "git fetch-pack: fetch failed."
+msgstr "git fetch-pack: помилка отримання."
+
+#, c-format
+msgid "mismatched algorithms: client %s; server %s"
+msgstr "невідповідність алгоритмів: клієнт %s; сервер %s"
+
+#, c-format
+msgid "the server does not support algorithm '%s'"
+msgstr "сервер не підтримує алгоритм \"%s\""
+
+msgid "Server does not support shallow requests"
+msgstr "Сервер не підтримує неглибокі запити"
+
+msgid "unable to write request to remote"
+msgstr "не вдалося записати запит до віддаленого сховища"
+
+#, c-format
+msgid "expected '%s', received '%s'"
+msgstr "очікувалось \"%s\", отримано \"%s\""
+
+#, c-format
+msgid "expected '%s'"
+msgstr "очікувалось \"%s\""
+
+#, c-format
+msgid "unexpected acknowledgment line: '%s'"
+msgstr "несподіваний рядок підтвердження: \"%s\""
+
+#, c-format
+msgid "error processing acks: %d"
+msgstr "помилка при обробці підтверджень: %d"
+
+#. TRANSLATORS: The parameter will be 'ready', a protocol
+#. keyword.
+#.
+#, c-format
+msgid "expected packfile to be sent after '%s'"
+msgstr "очікувалось надсилання файла пакунка після \"%s\""
+
+#. TRANSLATORS: The parameter will be 'ready', a protocol
+#. keyword.
+#.
+#, c-format
+msgid "expected no other sections to be sent after no '%s'"
+msgstr "не очікувалось надсилання жодної секції для статусу не \"%s\""
+
+#, c-format
+msgid "error processing shallow info: %d"
+msgstr "помилка при обробці неглибокої інформації: %d"
+
+#, c-format
+msgid "expected wanted-ref, got '%s'"
+msgstr "очікувалось wanted-ref, отримано \"%s\""
+
+#, c-format
+msgid "unexpected wanted-ref: '%s'"
+msgstr "несподіване запитуване посилання: \"%s\""
+
+#, c-format
+msgid "error processing wanted refs: %d"
+msgstr "помилка під час обробки запитуваних посилань: %d"
+
+msgid "git fetch-pack: expected response end packet"
+msgstr "git fetch-pack: очікувався response end пакет"
+
+msgid "no matching remote head"
+msgstr "немає відповідного віддаленого head"
+
+msgid "unexpected 'ready' from remote"
+msgstr "несподіване \"ready\" з віддаленого призначення"
+
+#, c-format
+msgid "no such remote ref %s"
+msgstr "немає такого віддаленого посилання %s"
+
+#, c-format
+msgid "Server does not allow request for unadvertised object %s"
+msgstr "Сервер забороняє запити до неоголошених обʼєктів %s"
+
+#, c-format
+msgid "fsmonitor_ipc__send_query: invalid path '%s'"
+msgstr "fsmonitor_ipc__send_query: неприпустимий шлях \"%s\""
+
+#, c-format
+msgid "fsmonitor_ipc__send_query: unspecified error on '%s'"
+msgstr "fsmonitor_ipc__send_query: невизначена помилка на \"%s\""
+
+msgid "fsmonitor--daemon is not running"
+msgstr "fsmonitor--daemon не запущено"
+
+#, c-format
+msgid "could not send '%s' command to fsmonitor--daemon"
+msgstr "не вдалося відправити команду \"%s\" до fsmonitor--daemon"
+
+#, c-format
+msgid "bare repository '%s' is incompatible with fsmonitor"
+msgstr "порожнє сховище \"%s\" несумісне з fsmonitor"
+
+#, c-format
+msgid "repository '%s' is incompatible with fsmonitor due to errors"
+msgstr "сховище \"%s\" несумісне з fsmonitor через помилки"
+
+#, c-format
+msgid "remote repository '%s' is incompatible with fsmonitor"
+msgstr "віддалене сховище \"%s\" несумісне з fsmonitor"
+
+#, c-format
+msgid "virtual repository '%s' is incompatible with fsmonitor"
+msgstr "віртуальне сховище \"%s\" несумісне з fsmonitor"
+
+#, c-format
+msgid ""
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
+msgstr ""
+"директорія сокетів \"%s\" несумісна з fsmonitor через відсутність підтримки "
+"сокетів Unix"
+
+msgid ""
+"git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
+"           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
+"           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
+"bare]\n"
+"           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
+msgstr ""
+"git [-v | --version] [-h | --help] [-C <шлях>] [-c <назва>=<значення>]\n"
+"           [--exec-path[=<шлях>]] [--html-path] [--man-path] [--info-path]\n"
+"           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
+"bare]\n"
+"           [--git-dir=<шлях>] [--work-tree=<шлях>] [--namespace=<назва>]\n"
+"[--config-env=<назва>=<змінна-оточення>] <команда> [<аргументи>]"
+
+msgid ""
+"'git help -a' and 'git help -g' list available subcommands and some\n"
+"concept guides. See 'git help <command>' or 'git help <concept>'\n"
+"to read about a specific subcommand or concept.\n"
+"See 'git help git' for an overview of the system."
+msgstr ""
+"\"git help -a\" і \"git help -g\" виводять доступні підкоманди та деякі\n"
+"посібники з понять. Дивіться \"git help <команда>\" або \"git help "
+"<поняття>\n"
+"щоб прочитати про конкретну підкоманду або поняття.\n"
+"Дивіться \"git help git\" для ознайомлення з системою."
+
+#, c-format
+msgid "unsupported command listing type '%s'"
+msgstr "непідтримуваний тип виводу команд \"%s\""
+
+#, c-format
+msgid "no directory given for '%s' option\n"
+msgstr "не вказано директорію для \"%s\" опціі\n"
+
+#, c-format
+msgid "no namespace given for --namespace\n"
+msgstr "не вказано простір імен для --namespace\n"
+
+#, c-format
+msgid "-c expects a configuration string\n"
+msgstr "-c очікує на рядок конфігурації\n"
+
+#, c-format
+msgid "no config key given for --config-env\n"
+msgstr "не вказано ключ конфігурації для --config-env\n"
+
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "для --attr-source не вказано джерело атрибутів\n"
+
+#, c-format
+msgid "unknown option: %s\n"
+msgstr "невідома опція: %s\n"
+
+#, c-format
+msgid "while expanding alias '%s': '%s'"
+msgstr "під час розгортання псевдоніма \"%s\": \"%s\""
+
+#, c-format
+msgid ""
+"alias '%s' changes environment variables.\n"
+"You can use '!git' in the alias to do this"
+msgstr ""
+"псевдонім \"%s\" перемінює змінні оточення.\n"
+"Ви можете використовувати \"!git\" в псевдонімі для цього"
+
+#, c-format
+msgid "empty alias for %s"
+msgstr "порожній псевдонім для %s"
+
+#, c-format
+msgid "recursive alias: %s"
+msgstr "рекурсивний псевдонім: %s"
+
+msgid "write failure on standard output"
+msgstr "помилка запису в стандартний вивід"
+
+msgid "unknown write failure on standard output"
+msgstr "невідома помилка запису в стандартний вивід"
+
+msgid "close failed on standard output"
+msgstr "не вдалося закрити стандартний вивід"
+
+#, c-format
+msgid "alias loop detected: expansion of '%s' does not terminate:%s"
+msgstr "виявлено цикл псевдонімів: розширення \"%s\" не припиняє:%s"
+
+#, c-format
+msgid "cannot handle %s as a builtin"
+msgstr "неможливо обробити %s як вбудовану команду"
+
+#, c-format
+msgid ""
+"usage: %s\n"
+"\n"
+msgstr "використання: %s\n"
+
+#, c-format
+msgid "expansion of alias '%s' failed; '%s' is not a git command\n"
+msgstr "не вдалося розширити псевдонім \"%s\"; \"%s\" не є командою git\n"
+
+#, c-format
+msgid "failed to run command '%s': %s\n"
+msgstr "не вдалося виконати команду \"%s\": %s\n"
+
+msgid "could not create temporary file"
+msgstr "не вдалося створити тимчасовий файл"
+
+#, c-format
+msgid "failed writing detached signature to '%s'"
+msgstr "не вдалося записати відʼєднаний підпис до \"%s\""
+
+msgid ""
+"gpg.ssh.allowedSignersFile needs to be configured and exist for ssh "
+"signature verification"
+msgstr ""
+"gpg.ssh.allowedSignersFile має бути налаштований та існувати для перевірки "
+"ssh підпису"
+
+msgid ""
+"ssh-keygen -Y find-principals/verify is needed for ssh signature "
+"verification (available in openssh version 8.2p1+)"
+msgstr ""
+"ssh-keygen -Y find-principals/verify необхідно для перевірки підпису ssh "
+"(доступно у версії openssh 8.2p1+)"
+
+#, c-format
+msgid "ssh signing revocation file configured but not found: %s"
+msgstr "файл скасування підпису ssh налаштовано, але не знайдено: %s"
+
+#, c-format
+msgid "bad/incompatible signature '%s'"
+msgstr "невірний/несумісний підпис \"%s\""
+
+#, c-format
+msgid "failed to get the ssh fingerprint for key '%s'"
+msgstr "не вдалося отримати ssh відбиток для ключа \"%s\""
+
+msgid ""
+"either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"
+msgstr ""
+"потрібно налаштувати або user.signingkey, або gpg.ssh.defaultKeyCommand"
+
+#, c-format
+msgid "gpg.ssh.defaultKeyCommand succeeded but returned no keys: %s %s"
+msgstr ""
+"gpg.ssh.defaultKeyCommand команда виконалася успішно, але не повернула "
+"жодного ключа: %s %s"
+
+#, c-format
+msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
+msgstr "gpg.ssh.defaultKeyCommand завершився невдало: %s %s"
+
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg не вдалося підписати дані:\n"
+"%s"
+
+msgid "user.signingKey needs to be set for ssh signing"
+msgstr "user.signingKey має бути налаштований для ssh підписання"
+
+#, c-format
+msgid "failed writing ssh signing key to '%s'"
+msgstr "не вдалося записати ssh ключ підпису до \"%s\""
+
+#, c-format
+msgid "failed writing ssh signing key buffer to '%s'"
+msgstr "не вдалося записати буфер ssh ключа підпису в \"%s\""
+
+msgid ""
+"ssh-keygen -Y sign is needed for ssh signing (available in openssh version "
+"8.2p1+)"
+msgstr ""
+"ssh-keygen -Y знак потрібен для ssh підписання (доступно у openssh версії "
+"8.2p1+)"
+
+#, c-format
+msgid "failed reading ssh signing data buffer from '%s'"
+msgstr "не вдалося прочитати буфер даних ssh підписання з \"%s\""
+
+#, c-format
+msgid "ignored invalid color '%.*s' in log.graphColors"
+msgstr "проігноровано неприпустимий колір \"%.*s\" у log.graphColors"
+
+msgid ""
+"given pattern contains NULL byte (via -f <file>). This is only supported "
+"with -P under PCRE v2"
+msgstr ""
+"заданий шаблон містить NULL байт (через -f <файл>). Це підтримується лише з "
+"опцією -P у PCRE v2"
+
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "\"%s\": не вдалося прочитати %s"
+
+#, c-format
+msgid "'%s': short read"
+msgstr "\"%s\": помилка считування"
+
+msgid "start a working area (see also: git help tutorial)"
+msgstr "запустити робочу область (дивіться також: git help tutorial)"
+
+msgid "work on the current change (see also: git help everyday)"
+msgstr "працювати над поточними змінами (дивіться також: git help everyday)"
+
+msgid "examine the history and state (see also: git help revisions)"
+msgstr "переглянути історію та стан (дивіться також: git help revisions)"
+
+msgid "grow, mark and tweak your common history"
+msgstr "розвивати, позначати та підправляти вашу спільну історію"
+
+msgid "collaborate (see also: git help workflows)"
+msgstr "співпрацювати (дивіться також: git help workflows)"
+
+msgid "Main Porcelain Commands"
+msgstr "Основні високорівневі команди"
+
+msgid "Ancillary Commands / Manipulators"
+msgstr "Допоміжні команди / Маніпулятори"
+
+msgid "Ancillary Commands / Interrogators"
+msgstr "Допоміжні команди / Допитувачі"
+
+msgid "Interacting with Others"
+msgstr "Взаємодія з іншими"
+
+msgid "Low-level Commands / Manipulators"
+msgstr "Низькорівневі команди / Маніпулятори"
+
+msgid "Low-level Commands / Interrogators"
+msgstr "Низькорівневі команди / Допитувачі"
+
+msgid "Low-level Commands / Syncing Repositories"
+msgstr "Низькорівневі команди / Сховища синхронізації"
+
+msgid "Low-level Commands / Internal Helpers"
+msgstr "Низькорівневі команди / Внутрішні помічники"
+
+msgid "User-facing repository, command and file interfaces"
+msgstr "Інтерфейси користувача для сховищ, команд та файлів"
+
+msgid "Developer-facing file formats, protocols and other interfaces"
+msgstr "Формати файлів, протоколи та інші інтерфейси розробника"
+
+#, c-format
+msgid "available git commands in '%s'"
+msgstr "доступні команди git в \"%s\""
+
+msgid "git commands available from elsewhere on your $PATH"
+msgstr "команди git доступні з будь-якого місця на вашому $PATH"
+
+msgid "These are common Git commands used in various situations:"
+msgstr "Це загальні команди Git, які використовуються в різних ситуаціях:"
+
+msgid "The Git concept guides are:"
+msgstr "Посібники з понять Git:"
+
+msgid "User-facing repository, command and file interfaces:"
+msgstr "Інтерфейси користувача для сховищ, команд та файлів:"
+
+msgid "File formats, protocols and other developer interfaces:"
+msgstr "Формати файлів, протоколи та інші інтерфейси розробника:"
+
+msgid "External commands"
+msgstr "Зовнішні команди"
+
+msgid "Command aliases"
+msgstr "Псевдоніми команд"
+
+msgid "See 'git help <command>' to read about a specific subcommand"
+msgstr ""
+"Дивіться \"git help <команда>\", щоб прочитати про конкретну підкоманду"
+
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+"Схоже, що \"%s\" є git командою, але ми не змогли\n"
+"виконати її. Можливо, git-%s не працює?"
+
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr "git: \"%s\" не є командою git. Дивітья git --help."
+
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr "Ой-ой. Ваша система повідомляє про повну відсутність Git команд."
+
+#, c-format
+msgid "WARNING: You called a Git command named '%s', which does not exist."
+msgstr "ПОПЕРЕДЖЕННЯ: Ви викликали Git команду \"%s\", якої не існує."
+
+#, c-format
+msgid "Continuing under the assumption that you meant '%s'."
+msgstr "Продовжую, припускаючи, що ви мали на увазі \"%s\"."
+
+#, c-format
+msgid "Run '%s' instead [y/N]? "
+msgstr "Запустити натомість \"%s\" [y/N]? "
+
+#, c-format
+msgid "Continuing in %0.1f seconds, assuming that you meant '%s'."
+msgstr "Продовжую через %0.1f секунд, припускаючи, що ви мали на увазі \"%s\"."
+
+msgid ""
+"\n"
+"The most similar command is"
+msgid_plural ""
+"\n"
+"The most similar commands are"
+msgstr[0] ""
+"\n"
+"Найбільш схожою командою є"
+msgstr[1] ""
+"\n"
+"Найбільш схожими командами є"
+msgstr[2] ""
+"\n"
+"Найбільш схожими командами є"
+
+msgid "git version [--build-options]"
+msgstr "git версія [--build-options]"
+
+#, c-format
+msgid "%s: %s - %s"
+msgstr "%s: %s - %s"
+
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+"\n"
+"Ви це мали на увазі?"
+msgstr[1] ""
+"\n"
+"Ви мали на увазі одну з цих?"
+msgstr[2] ""
+"\n"
+"Ви мали на увазі одну з цих?"
+
+#, c-format
+msgid ""
+"The '%s' hook was ignored because it's not set as executable.\n"
+"You can disable this warning with `git config advice.ignoredHook false`."
+msgstr ""
+"Гачок \"%s\" було проігноровано, оскільки він не визначений як виконуваний.\n"
+"Ви можете вимкнути це попередження за допомогою \"git config advice."
+"ignoredHook false\"."
+
+#, c-format
+msgid "argument to --packfile must be a valid hash (got '%s')"
+msgstr "аргумент до --packfile має бути коректним хешем (отримано \"%s\")"
+
+msgid "not a git repository"
+msgstr "не є git сховищем"
+
+#, c-format
+msgid "negative value for http.postBuffer; defaulting to %d"
+msgstr ""
+"відʼємне значення для http.postBuffer; зміна до значеня за замовчуванням %d"
+
+msgid "Delegation control is not supported with cURL < 7.22.0"
+msgstr "Контроль делегування не підтримується з cURL < 7.22.0"
+
+msgid "Public key pinning not supported with cURL < 7.39.0"
+msgstr "Закріплення відкритих ключів не підтримується з cURL < 7.39.0"
+
+msgid "CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"
+msgstr "CURLSSLOPT_NO_REVOKE не підтримується з cURL < 7.44.0"
+
+#, c-format
+msgid "Unsupported SSL backend '%s'. Supported SSL backends:"
+msgstr "Непідтримуваний SSL обробник \"%s\". Підтримувані SSL обробники:"
+
+#, c-format
+msgid "Could not set SSL backend to '%s': cURL was built without SSL backends"
+msgstr ""
+"Не вдалося встановити SSL обробник в \"%s\": cURL було зібрано без підтримки "
+"SSL обробників"
+
+#, c-format
+msgid "Could not set SSL backend to '%s': already set"
+msgstr "Не вдалося встановити SSL обробник в \"%s\": вже встановлено"
+
+#, c-format
+msgid ""
+"unable to update url base from redirection:\n"
+"  asked for: %s\n"
+"   redirect: %s"
+msgstr ""
+"не вдалося оновити базу url з перенаправлення:\n"
+"  запитували: %s\n"
+"   перенаправлення: %s"
+
+msgid "Author identity unknown\n"
+msgstr "Особистість автора невідома\n"
+
+msgid "Committer identity unknown\n"
+msgstr "Особистість комітера невідома\n"
+
+msgid ""
+"\n"
+"*** Please tell me who you are.\n"
+"\n"
+"Run\n"
+"\n"
+"  git config --global user.email \"you@example.com\"\n"
+"  git config --global user.name \"Your Name\"\n"
+"\n"
+"to set your account's default identity.\n"
+"Omit --global to set the identity only in this repository.\n"
+"\n"
+msgstr ""
+"\n"
+"*** Будь ласка, скажіть мені, хто ви.\n"
+"\n"
+"Виконайте\n"
+"\n"
+"  git config --global user.email \"you@example.com\"\n"
+"  git config --global user.name \"Ваше Імʼя\"\n"
+"\n"
+"щоб встановити особистість акаунту за замовчуванням.\n"
+"Пропустіть --global, щоб встановити особистість лише для цього сховища.\n"
+
+msgid "no email was given and auto-detection is disabled"
+msgstr "не вказана адреса електронної пошти і автоматичне визначення вимкнено"
+
+#, c-format
+msgid "unable to auto-detect email address (got '%s')"
+msgstr ""
+"не вдалося автоматично визначити адресу електронної пошти (отримано \"%s\")"
+
+msgid "no name was given and auto-detection is disabled"
+msgstr "імʼя не вказано і автоматичне визначення вимкнено"
+
+#, c-format
+msgid "unable to auto-detect name (got '%s')"
+msgstr "не вдалося автоматично визначити назву (отримано \"%s\")"
+
+#, c-format
+msgid "empty ident name (for <%s>) not allowed"
+msgstr "порожнє імʼя особистості (для <%s>) заборонено"
+
+#, c-format
+msgid "name consists only of disallowed characters: %s"
+msgstr "імʼя складається лише з заборонених символів: %s"
+
+msgid "expected 'tree:<depth>'"
+msgstr "очікувалось \"tree:<глибина>\""
+
+msgid "sparse:path filters support has been dropped"
+msgstr "підтримку фільтрів sparse:path було припинено"
+
+#, c-format
+msgid "'%s' for 'object:type=<type>' is not a valid object type"
+msgstr "\"%s\" для \"object:type=<type>\" не є припустимим типом обʼєкта"
+
+#, c-format
+msgid "invalid filter-spec '%s'"
+msgstr "неприпустимий визначник фільтра \"%s\""
+
+#, c-format
+msgid "must escape char in sub-filter-spec: '%c'"
+msgstr "необхідно екранувати символ у sub-filter-spec: \"%c\""
+
+msgid "expected something after combine:"
+msgstr "щоcь очікувалось після комбінування:"
+
+msgid "multiple filter-specs cannot be combined"
+msgstr "не можна комбінувати кілька визначників фільтра"
+
+msgid "unable to upgrade repository format to support partial clone"
+msgstr "не вдалося оновити формат сховища для підтримки часткового клонування"
+
+msgid "args"
+msgstr "аргументи"
+
+msgid "object filtering"
+msgstr "фільтрація обʼєктів"
+
+#, c-format
+msgid "unable to access sparse blob in '%s'"
+msgstr "не вдалося отримати доступ до розрідженого blob в \"%s\""
+
+#, c-format
+msgid "unable to parse sparse filter data in %s"
+msgstr "не вдалося розібрати дані розрідженого фільтра в %s"
+
+#, c-format
+msgid "entry '%s' in tree %s has tree mode, but is not a tree"
+msgstr "запис \"%s\" у дереві %s має режим tree, але не є деревом"
+
+#, c-format
+msgid "entry '%s' in tree %s has blob mode, but is not a blob"
+msgstr "запис \"%s\" у дереві %s має режим blob, але не є blob"
+
+#, c-format
+msgid "unable to load root tree for commit %s"
+msgstr "не вдалося завантажити кореневе дерево для коміту %s"
+
+#, c-format
+msgid ""
+"Unable to create '%s.lock': %s.\n"
+"\n"
+"Another git process seems to be running in this repository, e.g.\n"
+"an editor opened by 'git commit'. Please make sure all processes\n"
+"are terminated then try again. If it still fails, a git process\n"
+"may have crashed in this repository earlier:\n"
+"remove the file manually to continue."
+msgstr ""
+"Не вдалося створити \"%s.lock\": %s.\n"
+"\n"
+"Схоже, у цьому сховищі запущено інший git процес, наприклад\n"
+"редактор, відкритий командою \"git commit\". Будь ласка, переконайтеся, що "
+"всі процеси\n"
+"завершено, а потім спробуйте ще раз. Якщо все одно не вдасться, можливо, "
+"раніше \n"
+"у цьому сховищі аварійно завершився git процес:\n"
+"видаліть файл вручну, щоб продовжити."
+
+#, c-format
+msgid "Unable to create '%s.lock': %s"
+msgstr "Не вдалося створити \"%s.lock\": %s"
+
+#, c-format
+msgid "unexpected line: '%s'"
+msgstr "неочікуваний рядок: \"%s\""
+
+msgid "expected flush after ls-refs arguments"
+msgstr "очікувався flush після ls-refs аргументів"
+
+msgid "quoted CRLF detected"
+msgstr "виявлено цитований CRLF"
+
+#, c-format
+msgid "Failed to merge submodule %s (not checked out)"
+msgstr "Не вдалося обʼєднати підмодуль %s (не активне)"
+
+#, c-format
+msgid "Failed to merge submodule %s (no merge base)"
+msgstr "Не вдалося злити підмодуль %s (немає бази злиття)"
+
+#, c-format
+msgid "Failed to merge submodule %s (commits not present)"
+msgstr "Не вдалося злити підмодуль %s (відсутні коміти)"
+
+#, c-format
+msgid "Failed to merge submodule %s (commits don't follow merge-base)"
+msgstr "Не вдалося злити підмодуль %s (коміти не відповідають базі)"
+
+#, c-format
+msgid "Note: Fast-forwarding submodule %s to %s"
+msgstr "Примітка: перемотування підмодуля %s вперед до %s"
+
+#, c-format
+msgid "Failed to merge submodule %s"
+msgstr "Не вдалося злити підмодуль %s"
+
+#, c-format
+msgid ""
+"Failed to merge submodule %s, but a possible merge resolution exists: %s"
+msgstr "Не вдалося злити підмодуль %s, але існує можливе вирішення злиття: %s"
+
+#, c-format
+msgid ""
+"Failed to merge submodule %s, but multiple possible merges exist:\n"
+"%s"
+msgstr ""
+"Не вдалося злити підмодуль %s, але існує кілька можливих варіантів злиття:\n"
+"%s"
+
+msgid "failed to execute internal merge"
+msgstr "не вдалося виконати внутрішнє злиття"
+
+#, c-format
+msgid "unable to add %s to database"
+msgstr "не вдалося додати %s до бази даних"
+
+#, c-format
+msgid "Auto-merging %s"
+msgstr "Автоматичне злиття %s"
+
+#, c-format
+msgid ""
+"CONFLICT (implicit dir rename): Existing file/dir at %s in the way of "
+"implicit directory rename(s) putting the following path(s) there: %s."
+msgstr ""
+"КОНФЛІКТ (неявне перейменування каталогу): існуючий файл/директорія в %s "
+"заважає неявному перейменуванню директорії, додавши наступні шляхи до: %s."
+
+#, c-format
+msgid ""
+"CONFLICT (implicit dir rename): Cannot map more than one path to %s; "
+"implicit directory renames tried to put these paths there: %s"
+msgstr ""
+"КОНФЛІКТ (неявне перейменування директорії): Неможливо зіставити більше "
+"одного шляху з %s; неявне перейменування директорій намагалося помістити "
+"туди ці шляхи: %s"
+
+#, c-format
+msgid ""
+"CONFLICT (directory rename split): Unclear where to rename %s to; it was "
+"renamed to multiple other directories, with no destination getting a "
+"majority of the files."
+msgstr ""
+"КОНФЛІКТ (розбіжність під час перейменування директорії): Неясно, як "
+"перейменувати директорію %s; її було перейменовано в кілька інших "
+"директорій, жодна з яких не отримала більшості файлів."
+
+#, c-format
+msgid ""
+"WARNING: Avoiding applying %s -> %s rename to %s, because %s itself was "
+"renamed."
+msgstr ""
+"ПОПЕРЕДЖЕННЯ: уникнення застосування %s -> %s перейменування на %s, оскільки "
+"%s було перейменовано."
+
+#, c-format
+msgid ""
+"Path updated: %s added in %s inside a directory that was renamed in %s; "
+"moving it to %s."
+msgstr ""
+"Шлях було оновлено: %s додано до %s всередині директорії, яку було "
+"перейменовано в %s; переміщення до %s."
+
+#, c-format
+msgid ""
+"Path updated: %s renamed to %s in %s, inside a directory that was renamed in "
+"%s; moving it to %s."
+msgstr ""
+"Шлях було оновлено: %s перейменовано на %s в %s, всередині директорії, яку "
+"було перейменовано в %s; переміщення до %s."
+
+#, c-format
+msgid ""
+"CONFLICT (file location): %s added in %s inside a directory that was renamed "
+"in %s, suggesting it should perhaps be moved to %s."
+msgstr ""
+"КОНФЛІКТ (розташування файлу): %s додано до %s всередині директорії, яку "
+"було перейменовано в %s, що свідчить про те, що його слід перемістити до %s."
+
+#, c-format
+msgid ""
+"CONFLICT (file location): %s renamed to %s in %s, inside a directory that "
+"was renamed in %s, suggesting it should perhaps be moved to %s."
+msgstr ""
+"КОНФЛІКТ (розташування файлу): %s перейменовано на %s в %s всередині "
+"директорії, яку було перейменовано в %s, що свідчить про те, що його слід "
+"перемістити до %s."
+
+#, c-format
+msgid "CONFLICT (rename/rename): %s renamed to %s in %s and to %s in %s."
+msgstr ""
+"КОНФЛІКТ (перейменовано/перейменовано): %s перейменовано на %s в %s та на %s "
+"в %s."
+
+#, c-format
+msgid ""
+"CONFLICT (rename involved in collision): rename of %s -> %s has content "
+"conflicts AND collides with another path; this may result in nested conflict "
+"markers."
+msgstr ""
+"КОНФЛІКТ (перейменування призвело до колізії): перейменування %s -> %s має "
+"конфлікт вмісту та зіткається з іншим шляхом; це може призвести до появи "
+"вкладених маркерів конфлікту."
+
+#, c-format
+msgid "CONFLICT (rename/delete): %s renamed to %s in %s, but deleted in %s."
+msgstr ""
+"КОНФЛІКТ (перейменовано/видалено): %s перейменовано на %s в %s, але видалено "
+"в %s."
+
+#, c-format
+msgid "cannot read object %s"
+msgstr "неможливо прочитати обʼєкт %s"
+
+#, c-format
+msgid "object %s is not a blob"
+msgstr "обʼєкт %s не є blob"
+
+#, c-format
+msgid ""
+"CONFLICT (file/directory): directory in the way of %s from %s; moving it to "
+"%s instead."
+msgstr ""
+"КОНФЛІКТ (файл/директорія): директорія на шляху %s від %s; переміщення її до "
+"%s замість цього."
+
+#, c-format
+msgid ""
+"CONFLICT (distinct types): %s had different types on each side; renamed both "
+"of them so each can be recorded somewhere."
+msgstr ""
+"КОНФЛІКТ (різні типи): %s мали різні типи з обох боків; перейменовано "
+"обидва, щоб кожен можна було десь записати."
+
+#, c-format
+msgid ""
+"CONFLICT (distinct types): %s had different types on each side; renamed one "
+"of them so each can be recorded somewhere."
+msgstr ""
+"КОНФЛІКТ (різні типи): %s мали різні типи з обох боків; перейменовано один з "
+"них, щоб кожен можна було десь записати."
+
+msgid "content"
+msgstr "вміст"
+
+msgid "add/add"
+msgstr "додано/додано"
+
+msgid "submodule"
+msgstr "підмодуль"
+
+#, c-format
+msgid "CONFLICT (%s): Merge conflict in %s"
+msgstr "КОНФЛІКТ (%s): конфлікт злиття в %s"
+
+#, c-format
+msgid ""
+"CONFLICT (modify/delete): %s deleted in %s and modified in %s.  Version %s "
+"of %s left in tree."
+msgstr ""
+"КОНФЛІКТ (змінено/видалено): %s видалено в %s та змінено в %s.  Версію %s з "
+"%s залишено у дереві."
+
+#. TRANSLATORS: This is a line of advice to resolve a merge
+#. conflict in a submodule. The first argument is the submodule
+#. name, and the second argument is the abbreviated id of the
+#. commit that needs to be merged.  For example:
+#. - go to submodule (mysubmodule), and either merge commit abc1234"
+#.
+#, c-format
+msgid ""
+" - go to submodule (%s), and either merge commit %s\n"
+"   or update to an existing commit which has merged those changes\n"
+msgstr ""
+" - перейдіть до підмодуля (%s), щоб або злити коміт %s\n"
+"   або оновитись до існуючого коміту, в якому ці зміни вже злиті\n"
+
+#, c-format
+msgid ""
+"Recursive merging with submodules currently only supports trivial cases.\n"
+"Please manually handle the merging of each conflicted submodule.\n"
+"This can be accomplished with the following steps:\n"
+"%s - come back to superproject and run:\n"
+"\n"
+"      git add %s\n"
+"\n"
+"   to record the above merge or update\n"
+" - resolve any other conflicts in the superproject\n"
+" - commit the resulting index in the superproject\n"
+msgstr ""
+"Рекурсивне злиття з підмодулями наразі підтримує лише тривіальні випадки.\n"
+"Будь ласка, обробіть злиття кожного конфліктного підмодуля вручну.\n"
+"Це можна зробити за допомогою наступних кроків:\n"
+"%s - поверніться до суперпроекту і виконайте:\n"
+"\n"
+"      git add %s\n"
+"\n"
+"   щоб записати вищезгадане злиття або оновлення\n"
+"- розвʼяжіть будь-які інші конфлікти в суперпроекті\n"
+"- зробіть коміт підсумкового індекса в суперпроекті\n"
+
+#. TRANSLATORS: The %s arguments are: 1) tree hash of a merge
+#. base, and 2-3) the trees for the two trees we're merging.
+#.
+#, c-format
+msgid "collecting merge info failed for trees %s, %s, %s"
+msgstr "збирання інформації про злиття не вдалося для дерев %s, %s, %s"
+
+msgid "(bad commit)\n"
+msgstr "(невірний коміт)\n"
+
+#, c-format
+msgid "add_cacheinfo failed for path '%s'; merge aborting."
+msgstr "невдала спроба add_cacheinfo для шляху \"%s\"; переривання злиття."
+
+#, c-format
+msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting."
+msgstr ""
+"процесу add_cacheinfo не вдалося зробити оновлення для шляху \"%s\"; "
+"переривання злиття."
+
+#, c-format
+msgid "failed to create path '%s'%s"
+msgstr "не вдалося створити шлях \"%s\"%s"
+
+#, c-format
+msgid "Removing %s to make room for subdirectory\n"
+msgstr "Видалення %s, щоб звільнити місце для піддиректорії\n"
+
+msgid ": perhaps a D/F conflict?"
+msgstr ": можливо, D/F конфлікт?"
+
+#, c-format
+msgid "refusing to lose untracked file at '%s'"
+msgstr "відмовлено у втраті невідстежуваного файла \"%s\""
+
+#, c-format
+msgid "blob expected for %s '%s'"
+msgstr "blob очікується для %s \"%s\""
+
+#, c-format
+msgid "failed to open '%s': %s"
+msgstr "не вдалося відкрити \"%s\": %s"
+
+#, c-format
+msgid "failed to symlink '%s': %s"
+msgstr "не вдалося зробити символьне посилання \"%s\": %s"
+
+#, c-format
+msgid "do not know what to do with %06o %s '%s'"
+msgstr "не знаю, що робити з %06o %s \"%s\""
+
+#, c-format
+msgid "Fast-forwarding submodule %s to the following commit:"
+msgstr "Перемотування підмодуля %s вперед до наступного коміту:"
+
+#, c-format
+msgid "Fast-forwarding submodule %s"
+msgstr "Перемотування підмодуля %s вперед"
+
+#, c-format
+msgid "Failed to merge submodule %s (merge following commits not found)"
+msgstr "Не вдалося злити підмодуль %s (злиття наступних комітів не знайдено)"
+
+#, c-format
+msgid "Failed to merge submodule %s (not fast-forward)"
+msgstr "Не вдалося злити підмодуль %s (не вдалося перемотати вперед)"
+
+msgid "Found a possible merge resolution for the submodule:\n"
+msgstr "Знайдено можливе вирішення злиття для підмодуля:\n"
+
+#, c-format
+msgid ""
+"If this is correct simply add it to the index for example\n"
+"by using:\n"
+"\n"
+"  git update-index --cacheinfo 160000 %s \"%s\"\n"
+"\n"
+"which will accept this suggestion.\n"
+msgstr ""
+"Якщо все вірно, просто додайте це до індексу, наприклад,\n"
+"використавши:\n"
+"\n"
+"  git update-index --cacheinfo 160000 %s \"%s\"\n"
+"\n"
+"який прийме цю пропозицію.\n"
+
+#, c-format
+msgid "Failed to merge submodule %s (multiple merges found)"
+msgstr "Не вдалося злити підмодуль %s (знайдено більше одного злиття)"
+
+#, c-format
+msgid "Error: Refusing to lose untracked file at %s; writing to %s instead."
+msgstr ""
+"Помилка: відмовлено у втраті невідстежуваного файла %s; натомість записуємо "
+"до %s."
+
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
+"in tree."
+msgstr ""
+"КОНФЛІКТ (%s/видалено): %s видалено в %s та %s в %s. Версію %s з %s залишено "
+"в дереві."
+
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s "
+"left in tree."
+msgstr ""
+"КОНФЛІКТ (%s/видалено): %s видалено в %s і %s в %s в %s. Версію %s з %s "
+"залишено у дереві."
+
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
+"in tree at %s."
+msgstr ""
+"КОНФЛІКТ (%s/видалено): %s видалено в %s і %s в %s. Версію %s з %s залишено "
+"у дереві в %s."
+
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s "
+"left in tree at %s."
+msgstr ""
+"КОНФЛІКТ (%s/видалено): %s видалено в %s і %s в %s в %s. Версію %s з %s "
+"залишено в дереві в %s."
+
+msgid "rename"
+msgstr "перейменувати"
+
+msgid "renamed"
+msgstr "перейменовано"
+
+#, c-format
+msgid "Refusing to lose dirty file at %s"
+msgstr "Відмовлено у втраті брудного файла %s"
+
+#, c-format
+msgid "Refusing to lose untracked file at %s, even though it's in the way."
+msgstr ""
+"Відмовлено у втраті невідстежуваного файла %s, не дивлячись на те, що він "
+"знаходиться на шляху."
+
+#, c-format
+msgid "CONFLICT (rename/add): Rename %s->%s in %s.  Added %s in %s"
+msgstr ""
+"КОНФЛІКТ (перейменовано/додано): перейменовано %s->%s в %s.  Додано %s в %s"
+
+#, c-format
+msgid "%s is a directory in %s adding as %s instead"
+msgstr "%s є директорією в %s, натомість додаємо як %s"
+
+#, c-format
+msgid "Refusing to lose untracked file at %s; adding as %s instead"
+msgstr "Відмовлено у втраті невідстежуваного файла %s; натомість додаємо як %s"
+
+#, c-format
+msgid ""
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
+msgstr ""
+"КОНФЛІКТ (перейменовано/перейменовано): перейменовано \"%s\"->\"%s\" у гілці "
+"\"%s\" перейменовано \"%s\"->\"%s\" у \"%s\"%s"
+
+msgid " (left unresolved)"
+msgstr " (залишилося нерозвʼязаним)"
+
+#, c-format
+msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
+msgstr ""
+"КОНФЛІКТ (перейменовано/перейменовано): перейменовано %s->%s в %s. "
+"Перейменовано %s->%s в %s"
+
+#, c-format
+msgid ""
+"CONFLICT (directory rename split): Unclear where to place %s because "
+"directory %s was renamed to multiple other directories, with no destination "
+"getting a majority of the files."
+msgstr ""
+"КОНФЛІКТ (розбіжність під час перейменування директорії): Неясно, куди "
+"помістити %s, оскільки директорію %s було перейменовано в кілька інших "
+"директорій,  жодна з яких не отримала більшості файлів."
+
+#, c-format
+msgid ""
+"CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory %s-"
+">%s in %s"
+msgstr ""
+"КОНФЛІКТ (перейменовано/перейменовано): перейменовано директорію %s->%s в "
+"%s. Перейменовано директорію %s->%s в %s"
+
+msgid "modify"
+msgstr "змінити"
+
+msgid "modified"
+msgstr "змінено"
+
+#, c-format
+msgid "Skipped %s (merged same as existing)"
+msgstr "Пропущено %s (злите - те саме, що й існуюче)"
+
+#, c-format
+msgid "Adding as %s instead"
+msgstr "Додавання як %s замість цього"
+
+#, c-format
+msgid "Removing %s"
+msgstr "Видалення %s"
+
+msgid "file/directory"
+msgstr "файл/директорія"
+
+msgid "directory/file"
+msgstr "директорія/файл"
+
+#, c-format
+msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
+msgstr "КОНФЛІКТ (%s): в %s існує директорія з іменем %s. Додавання %s як %s"
+
+#, c-format
+msgid "Adding %s"
+msgstr "Додавання %s"
+
+#, c-format
+msgid "CONFLICT (add/add): Merge conflict in %s"
+msgstr "КОНФЛІКТ (додано/додано): Конфлікт злиття у %s"
+
+#, c-format
+msgid "merging of trees %s and %s failed"
+msgstr "не вдалося злити дерева %s та %s"
+
+msgid "Merging:"
+msgstr "Злиття:"
+
+#, c-format
+msgid "found %u common ancestor:"
+msgid_plural "found %u common ancestors:"
+msgstr[0] "знайшли %u спільного предка:"
+msgstr[1] "знайшли %u спільних предків:"
+msgstr[2] "знайшли %u спільних предків:"
+
+msgid "merge returned no commit"
+msgstr "злиття не повернуло коміт"
+
+#, c-format
+msgid "Could not parse object '%s'"
+msgstr "Не вдалося розібрати обʼєкт \"%s\""
+
+msgid "failed to read the cache"
+msgstr "не вдалося прочитати кеш"
+
+msgid "multi-pack-index OID fanout is of the wrong size"
+msgstr "multi-pack-index OID розсіювання має невірний розмір"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "multi-pack-index шматок пошуку OID має невірний розмір"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr ""
+"multi-pack-index необхідній шматок зміщення обʼєкта має невірний розмір"
+
+#, c-format
+msgid "multi-pack-index file %s is too small"
+msgstr "multi-pack-index файл %s занадто малий"
+
+#, c-format
+msgid "multi-pack-index signature 0x%08x does not match signature 0x%08x"
+msgstr "multi-pack-index підпис 0x%08x не збігається з підписом 0x%08x"
+
+#, c-format
+msgid "multi-pack-index version %d not recognized"
+msgstr "multi-pack-index версія %d не розпізнана"
+
+#, c-format
+msgid "multi-pack-index hash version %u does not match version %u"
+msgstr "версія хешу multi-pack-index %u не збігається з версією %u"
+
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"multi-pack-index необхідний шматок імені пакунка відсутній або пошкоджений"
+
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"multi-pack-index необхідний шматок розсіювання OID відсутній або пошкоджений"
+
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"multi-pack-index необхідний шматок пошуку OID відсутній або пошкоджений"
+
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"multi-pack-index необхідний шматок зміщення обʼєктів відсутній або "
+"пошкоджений"
+
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "multi-pack-index  pack-name шматок занадто малий"
+
+#, c-format
+msgid "multi-pack-index pack names out of order: '%s' before '%s'"
+msgstr ""
+"multi-pack-index назви пакунків знаходяться у невірній послідовності: \"%s\" "
+"перед \"%s\""
+
+#, c-format
+msgid "bad pack-int-id: %u (%u total packs)"
+msgstr "невірний pack-int-id: %u (%u всього пакунків)"
+
+msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
+msgstr ""
+"multi-pack-index зберігає 64-бітне зміщення, але значення off_t занадто мале"
+
+msgid "multi-pack-index large offset out of bounds"
+msgstr "multi-pack-index large зсув виходить за межі"
+
+#, c-format
+msgid "failed to add packfile '%s'"
+msgstr "не вдалося додати packfile \"%s\""
+
+#, c-format
+msgid "failed to open pack-index '%s'"
+msgstr "не вдалося відкрити pack-index \"%s\""
+
+#, c-format
+msgid "failed to locate object %d in packfile"
+msgstr "не вдалося знайти обʼєкт %d у файлі пакунка"
+
+msgid "cannot store reverse index file"
+msgstr "неможливо зберегти файл зворотнього індексу"
+
+#, c-format
+msgid "could not parse line: %s"
+msgstr "не вдалося розібрати рядок: %s"
+
+#, c-format
+msgid "malformed line: %s"
+msgstr "невірно сформований рядок: %s"
+
+msgid "ignoring existing multi-pack-index; checksum mismatch"
+msgstr ""
+"ігнорування існуючого multi-pack-index; невідповідність контрольних сум"
+
+msgid "could not load pack"
+msgstr "не вдалося завантажити пакет"
+
+#, c-format
+msgid "could not open index for %s"
+msgstr "не вдалося відкрити індекс для %s"
+
+msgid "Adding packfiles to multi-pack-index"
+msgstr "Додавання пакунків до multi-pack-index"
+
+#, c-format
+msgid "unknown preferred pack: '%s'"
+msgstr "невідомий бажаний пакунок: \"%s\""
+
+#, c-format
+msgid "cannot select preferred pack %s with no objects"
+msgstr "неможливо вибрати бажаний пакунок %s за відсутності об’єктів"
+
+#, c-format
+msgid "did not see pack-file %s to drop"
+msgstr "не побачив файл пакунка %s, який потрібно скинути"
+
+#, c-format
+msgid "preferred pack '%s' is expired"
+msgstr "у бажаного пакета \"%s\" закінчився термін дії"
+
+msgid "no pack files to index."
+msgstr "немає файлів пакунків для індексації."
+
+msgid "refusing to write multi-pack .bitmap without any objects"
+msgstr "відмовлено в записі мультіпакункового .bitmap без обʼєктів"
+
+msgid "could not write multi-pack bitmap"
+msgstr "не вдалося записати мультіпакунковий bitmap"
+
+msgid "could not write multi-pack-index"
+msgstr "не вдалося записати multi-pack-index"
+
+#, c-format
+msgid "failed to clear multi-pack-index at %s"
+msgstr "не вдалося очистити multi-pack-index при %s"
+
+msgid "multi-pack-index file exists, but failed to parse"
+msgstr "multi-pack-index файл існує, але його не вдалося розібрати"
+
+msgid "incorrect checksum"
+msgstr "невірна контрольна сума"
+
+msgid "Looking for referenced packfiles"
+msgstr "Пошук файлів пакунків, на які є посилання"
+
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"невірна послідовність oid розсіювання: fanout[%d] = %<PRIx32> > %<PRIx32> = "
+"fanout[%d]"
+
+msgid "the midx contains no oid"
+msgstr "midx не містить oid"
+
+msgid "Verifying OID order in multi-pack-index"
+msgstr "Перевірка OID послідовності в multi-pack-index"
+
+#, c-format
+msgid "oid lookup out of order: oid[%d] = %s >= %s = oid[%d]"
+msgstr "невірна послідовність oid lookup: oid[%d] = %s >= %s = oid[%d]"
+
+msgid "Sorting objects by packfile"
+msgstr "Сортування обʼєктів за файлом пакунка"
+
+msgid "Verifying object offsets"
+msgstr "Перевірка зміщень обʼєкта"
+
+#, c-format
+msgid "failed to load pack entry for oid[%d] = %s"
+msgstr "не вдалося завантажити запис пакунка для oid[%d] = %s"
+
+#, c-format
+msgid "failed to load pack-index for packfile %s"
+msgstr "не вдалося завантажити індекс файла пакунка %s"
+
+#, c-format
+msgid "incorrect object offset for oid[%d] = %s: %<PRIx64> != %<PRIx64>"
+msgstr "невірне зміщення обʼєкта для oid[%d] = %s: %<PRIx64> != %<PRIx64>"
+
+msgid "Counting referenced objects"
+msgstr "Підрахунок обʼєктів, на які є посилання"
+
+msgid "Finding and deleting unreferenced packfiles"
+msgstr "Пошук і видалення файлів пакунків без посилань"
+
+msgid "could not start pack-objects"
+msgstr "не вдалося розпочати pack-objects"
+
+msgid "could not finish pack-objects"
+msgstr "не вдалося завершити pack-objects"
+
+#, c-format
+msgid "unable to create lazy_dir thread: %s"
+msgstr "не вдалося створити lazy_dir потік: %s"
+
+#, c-format
+msgid "unable to create lazy_name thread: %s"
+msgstr "не вдалося створити lazy_name потік: %s"
+
+#, c-format
+msgid "unable to join lazy_name thread: %s"
+msgstr "не вдалося приєднатися до lazy_name потоку: %s"
+
+#, c-format
+msgid ""
+"You have not concluded your previous notes merge (%s exists).\n"
+"Please, use 'git notes merge --commit' or 'git notes merge --abort' to "
+"commit/abort the previous merge before you start a new notes merge."
+msgstr ""
+"Ви не завершили попереднє злиття нотаток (існує %s).\n"
+"Будь ласка, скористайтесь командами \"git notes merge --commit\" або \"git "
+"notes merge --abort\", щоб зробити коміт/скасувати попереднє злиття, перш "
+"ніж розпочати нове злиття нотаток."
+
+#, c-format
+msgid "You have not concluded your notes merge (%s exists)."
+msgstr "Ви не завершили злиття ваших нотаток (%s існує)."
+
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "Неможливо зробити коміт неініціалізованого/непосиланого дерева нотаток"
+
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Невірнe notes.rewriteMode значення: \"%s\""
+
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "Відмовлено в перезаписі нотаток у %s (за межами refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of
+#. the environment variable, the second %s is
+#. its value.
+#.
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Невірне %s значення: \"%s\""
+
+#, c-format
+msgid "object directory %s does not exist; check .git/objects/info/alternates"
+msgstr "директорія об’єкта %s не існує; перевірте .git/objects/info/alternates"
+
+#, c-format
+msgid "unable to normalize alternate object path: %s"
+msgstr "не вдалося нормалізувати шлях запозиченого обʼєкта: %s"
+
+#, c-format
+msgid "%s: ignoring alternate object stores, nesting too deep"
+msgstr ""
+"%s: ігнорування місць збереження запозичених об’єктів, надто глибока "
+"вкладеність"
+
+msgid "unable to fdopen alternates lockfile"
+msgstr "не вдалося виконати fdopen для файла блокування запозичених обʼєктів"
+
+msgid "unable to read alternates file"
+msgstr "не вдалося прочитати файл запозичених обʼєктів"
+
+msgid "unable to move new alternates file into place"
+msgstr "не вдалося перемістити файл нових запозичених обʼєктів на місце"
+
+#, c-format
+msgid "path '%s' does not exist"
+msgstr "шлях \"%s\" не існує"
+
+#, c-format
+msgid "reference repository '%s' as a linked checkout is not supported yet."
+msgstr "посилання на сховище \"%s\" як повʼязане поки що не підтримується."
+
+#, c-format
+msgid "reference repository '%s' is not a local repository."
+msgstr "сховище посилання \"%s\" не є локальним сховищем."
+
+#, c-format
+msgid "reference repository '%s' is shallow"
+msgstr "сховище посилання \"%s\" є неглибоким"
+
+#, c-format
+msgid "reference repository '%s' is grafted"
+msgstr "сховище посилання \"%s\" прищеплено"
+
+#, c-format
+msgid "could not find object directory matching %s"
+msgstr "не вдалося знайти директорію обʼєктів, що відповідає %s"
+
+#, c-format
+msgid "invalid line while parsing alternate refs: %s"
+msgstr "неприпустимий рядок при розбиранні посилань запозичених обʼєктів: %s"
+
+#, c-format
+msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>"
+msgstr "спроба виконати mmap %<PRIuMAX> за межею %<PRIuMAX>"
+
+#, c-format
+msgid "mmap failed%s"
+msgstr "операція mmap не вдалася%s"
+
+#, c-format
+msgid "object file %s is empty"
+msgstr "обʼєктний файл %s порожній"
+
+#, c-format
+msgid "corrupt loose object '%s'"
+msgstr "пошкоджено вільний обʼєкт \"%s\""
+
+#, c-format
+msgid "garbage at end of loose object '%s'"
+msgstr "непотріб в кінці вільного обʼєкта \"%s\""
+
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "не вдалося відкрити вільний обʼєкт %s"
+
+#, c-format
+msgid "unable to parse %s header"
+msgstr "не вдалося розібрати заголовок %s"
+
+msgid "invalid object type"
+msgstr "неприпустимий тип обʼєкта"
+
+#, c-format
+msgid "unable to unpack %s header"
+msgstr "не вдалося розпакувати %s заголовок"
+
+#, c-format
+msgid "header for %s too long, exceeds %d bytes"
+msgstr "заголовок для %s занадто довгий, перевищує %d байт"
+
+#, c-format
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "вільний обʼєкт %s (що зберігається у %s) пошкоджено"
+
+#, c-format
+msgid "replacement %s not found for %s"
+msgstr "заміна %s не знайдена для %s"
+
+#, c-format
+msgid "packed object %s (stored in %s) is corrupt"
+msgstr "упакований обʼєкт %s (що зберігається у %s) пошкоджено"
+
+#, c-format
+msgid "unable to write file %s"
+msgstr "не вдалося записати файл %s"
+
+#, c-format
+msgid "unable to set permission to '%s'"
+msgstr "не вдалося встановити дозволи для \"%s\""
+
+msgid "error when closing loose object file"
+msgstr "помилка при закритті файла вільного об’єкта"
+
+#, c-format
+msgid "insufficient permission for adding an object to repository database %s"
+msgstr "недостатньо дозволу для додавання обʼєкта до бази даних сховища %s"
+
+msgid "unable to create temporary file"
+msgstr "не вдалося створити тимчасовий файл"
+
+msgid "unable to write loose object file"
+msgstr "не вдалося записати файл вільного об’єкта"
+
+#, c-format
+msgid "unable to deflate new object %s (%d)"
+msgstr "не вдалося запакувати новий обʼєкт %s (%d)"
+
+#, c-format
+msgid "deflateEnd on object %s failed (%d)"
+msgstr "операція deflateEnd на обʼєкті %s завершилася невдало (%d)"
+
+#, c-format
+msgid "confused by unstable object source data for %s"
+msgstr "збентежений нестабільними початковими даними обʼєкта для %s"
+
+#, c-format
+msgid "write stream object %ld != %<PRIuMAX>"
+msgstr "запис обʼєкта потока %ld != %<PRIuMAX>"
+
+#, c-format
+msgid "unable to stream deflate new object (%d)"
+msgstr "не вдалося запакувати новий обʼєкт (%d) при передачі"
+
+#, c-format
+msgid "deflateEnd on stream object failed (%d)"
+msgstr "операція deflateEnd на обʼєкті потоку завершилася невдало (%d)"
+
+#, c-format
+msgid "unable to create directory %s"
+msgstr "не вдалося створити директорію %s"
+
+#, c-format
+msgid "cannot read object for %s"
+msgstr "неможливо прочитати обʼєкт для %s"
+
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "об’єкт не пройшов перевірку fsck: %s"
+
+msgid "refusing to create malformed object"
+msgstr "відмовлено в створенні невірно сформованого обʼєкта"
+
+#, c-format
+msgid "read error while indexing %s"
+msgstr "помилка считування при індексаціі %s"
+
+#, c-format
+msgid "short read while indexing %s"
+msgstr "помилка считування замалого вмісту при індексаціі %s"
+
+#, c-format
+msgid "%s: failed to insert into database"
+msgstr "%s: не вдалося внести до бази даних"
+
+#, c-format
+msgid "%s: unsupported file type"
+msgstr "%s: непідтримуваний тип файлу"
+
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s не є допустимим \"%s\" обʼєктом"
+
+#, c-format
+msgid "unable to open %s"
+msgstr "не вдалося відкрити %s"
+
+#, c-format
+msgid "hash mismatch for %s (expected %s)"
+msgstr "невідповідність хешу для %s (очікувалось %s)"
+
+#, c-format
+msgid "unable to mmap %s"
+msgstr "не вдалося виконати mmap %s"
+
+#, c-format
+msgid "unable to unpack header of %s"
+msgstr "не вдалося розпакувати заголовок %s"
+
+#, c-format
+msgid "unable to parse header of %s"
+msgstr "не вдалося розібрати заголовок %s"
+
+#, c-format
+msgid "unable to unpack contents of %s"
+msgstr "не вдалося розпакувати вміст %s"
+
+#. TRANSLATORS: This is a line of ambiguous object
+#. output shown when we cannot look up or parse the
+#. object in question. E.g. "deadbeef [bad object]".
+#.
+#, c-format
+msgid "%s [bad object]"
+msgstr "%s [невірний обʼект]"
+
+#. TRANSLATORS: This is a line of ambiguous commit
+#. object output. E.g.:
+#. *
+#. "deadbeef commit 2021-01-01 - Some Commit Message"
+#.
+#, c-format
+msgid "%s commit %s - %s"
+msgstr "%s коміт %s - %s"
+
+#. TRANSLATORS: This is a line of ambiguous
+#. tag object output. E.g.:
+#. *
+#. "deadbeef tag 2022-01-01 - Some Tag Message"
+#. *
+#. The second argument is the YYYY-MM-DD found
+#. in the tag.
+#. *
+#. The third argument is the "tag" string
+#. from object.c.
+#.
+#, c-format
+msgid "%s tag %s - %s"
+msgstr "%s тег %s - %s"
+
+#. TRANSLATORS: This is a line of ambiguous
+#. tag object output where we couldn't parse
+#. the tag itself. E.g.:
+#. *
+#. "deadbeef [bad tag, could not parse it]"
+#.
+#, c-format
+msgid "%s [bad tag, could not parse it]"
+msgstr "%s [невірний тег, не вдалося розібрати]"
+
+#. TRANSLATORS: This is a line of ambiguous <type>
+#. object output. E.g. "deadbeef tree".
+#.
+#, c-format
+msgid "%s tree"
+msgstr "%s дерево"
+
+#. TRANSLATORS: This is a line of ambiguous <type>
+#. object output. E.g. "deadbeef blob".
+#.
+#, c-format
+msgid "%s blob"
+msgstr "%s blob"
+
+#, c-format
+msgid "short object ID %s is ambiguous"
+msgstr "короткий ідентифікатор обʼєкта %s неоднозначний"
+
+#. TRANSLATORS: The argument is the list of ambiguous
+#. objects composed in show_ambiguous_object(). See
+#. its "TRANSLATORS" comments for details.
+#.
+#, c-format
+msgid ""
+"The candidates are:\n"
+"%s"
+msgstr ""
+"Кандидати такі:\n"
+"%s"
+
+msgid ""
+"Git normally never creates a ref that ends with 40 hex characters\n"
+"because it will be ignored when you just specify 40-hex. These refs\n"
+"may be created by mistake. For example,\n"
+"\n"
+"  git switch -c $br $(git rev-parse ...)\n"
+"\n"
+"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+"examine these refs and maybe delete them. Turn this message off by\n"
+"running \"git config advice.objectNameWarning false\""
+msgstr ""
+"Зазвичай Git ніколи не створює посилання, яке закінчується 40-hex "
+"символами,\n"
+"тому що воно буде проігноровано, якщо ви просто вкажете 40-hex. Такі "
+"посилання\n"
+"можуть бути створені помилково. Наприклад\n"
+"\n"
+"  git switch -c $br $(git rev-parse ...)\n"
+"\n"
+"де \"$br\" якимось чином порожнє, створює 40-hex посилання. Будь ласка\n"
+"перевірте ці посилання і, можливо, видаліть їх. Щоб вимкнути це "
+"повідомлення, виконайте\n"
+"\"git config advice.objectNameWarning false\""
+
+#, c-format
+msgid "log for '%.*s' only goes back to %s"
+msgstr "лог для \"%.*s\" містить записи тільки до %s"
+
+#, c-format
+msgid "log for '%.*s' only has %d entries"
+msgstr "лог для \"%.*s\" містить лише %d записів"
+
+#, c-format
+msgid "path '%s' exists on disk, but not in '%.*s'"
+msgstr "шлях \"%s\" існує на диску, але не в \"%.*s\""
+
+#, c-format
+msgid ""
+"path '%s' exists, but not '%s'\n"
+"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?"
+msgstr ""
+"шлях \"%s\" існує, але не \"%s\"\n"
+"підказка: ви мали на увазі \"%.*s:%s\" або \"%.*s:./%s\"?"
+
+#, c-format
+msgid "path '%s' does not exist in '%.*s'"
+msgstr "шлях \"%s\" не існує в \"%.*s\""
+
+#, c-format
+msgid ""
+"path '%s' is in the index, but not at stage %d\n"
+"hint: Did you mean ':%d:%s'?"
+msgstr ""
+"шлях \"%s\" є в індексі, але відсутній на стадії %d\n"
+"підказка: ви мали на увазі \":%d:%s\"?"
+
+#, c-format
+msgid ""
+"path '%s' is in the index, but not '%s'\n"
+"hint: Did you mean ':%d:%s' aka ':%d:./%s'?"
+msgstr ""
+"шлях \"%s\" є в індексі, але не \"%s\"\n"
+"підказка: ви мали на увазі \"%d:%s\" або \"%d:./%s\"?"
+
+#, c-format
+msgid "path '%s' exists on disk, but not in the index"
+msgstr "шлях \"%s\" існує на диску, але відсутній в індексі"
+
+#, c-format
+msgid "path '%s' does not exist (neither on disk nor in the index)"
+msgstr "шлях \"%s\" не існує (ні на диску, ні в індексі)"
+
+msgid "relative path syntax can't be used outside working tree"
+msgstr ""
+"синтаксис відносного шляху не можна використовувати поза робочим деревом"
+
+#, c-format
+msgid "<object>:<path> required, only <object> '%s' given"
+msgstr "потрібно <обʼєкт>:<шлях>, надано лише <обʼєкт> \"%s\""
+
+#, c-format
+msgid "invalid object name '%.*s'."
+msgstr "неприпустима назва обʼєкта \"%.*s\"."
+
+#, c-format
+msgid "invalid object type \"%s\""
+msgstr "неприпустимий тип обʼєкту \"%s\""
+
+#, c-format
+msgid "object %s is a %s, not a %s"
+msgstr "обʼєкт %s є %s, а не %s"
+
+#, c-format
+msgid "object %s has unknown type id %d"
+msgstr "обʼєкт %s має невідомий тип ідентифікатора %d"
+
+#, c-format
+msgid "unable to parse object: %s"
+msgstr "не вдалося розібрати обʼєкт: %s"
+
+#, c-format
+msgid "hash mismatch %s"
+msgstr "невідповідність хешу %s"
+
+msgid "trying to write commit not in index"
+msgstr "спроба записати коміт, якого немає в індексі"
+
+msgid "failed to load bitmap index (corrupted?)"
+msgstr "не вдалося завантажити bitmap індекс (пошкоджено?)"
+
+msgid "corrupted bitmap index (too small)"
+msgstr "пошкоджений bitmap індекс (занадто малий)"
+
+msgid "corrupted bitmap index file (wrong header)"
+msgstr "пошкоджений файл bitmap індексу (занадто малий)"
+
+#, c-format
+msgid "unsupported version '%d' for bitmap index file"
+msgstr "непідтримувана версія \"%d\" для файлу bitmap індексу"
+
+msgid "corrupted bitmap index file (too short to fit hash cache)"
+msgstr ""
+"пошкоджений файл bitmap індексу (занадто короткий щоб умістити кеш хеша)"
+
+msgid "corrupted bitmap index file (too short to fit lookup table)"
+msgstr ""
+"пошкоджений файл bitmap індексу (занадто короткий, щоб вмістити таблицю "
+"пошуку)"
+
+#, c-format
+msgid "duplicate entry in bitmap index: '%s'"
+msgstr "дубльований запис в bitmap індексі: \"%s\""
+
+#, c-format
+msgid "corrupt ewah bitmap: truncated header for entry %d"
+msgstr "пошкоджений ewah bitmap: урізаний заголовок для запису %d"
+
+#, c-format
+msgid "corrupt ewah bitmap: commit index %u out of range"
+msgstr "пошкоджений ewah bitmap: індекс коміту %u поза межами діапазону"
+
+msgid "corrupted bitmap pack index"
+msgstr "пошкоджений bitmap індекс пакунка"
+
+msgid "invalid XOR offset in bitmap pack index"
+msgstr "невірне XOR зміщення в bitmap індексі пакунка"
+
+msgid "cannot fstat bitmap file"
+msgstr "неможливо виконати fstat для bitmap файла"
+
+msgid "checksum doesn't match in MIDX and bitmap"
+msgstr "контрольна сума не збігається в MIDX і bitmap"
+
+msgid "multi-pack bitmap is missing required reverse index"
+msgstr "у мультіпакунковому bitmap відсутній необхідний зворотний індекс"
+
+#, c-format
+msgid "could not open pack %s"
+msgstr "не вдалося відкрити пакунок %s"
+
+#, c-format
+msgid "preferred pack (%s) is invalid"
+msgstr "бажаний пакунок (%s) є неприпустимим"
+
+msgid "corrupt bitmap lookup table: triplet position out of index"
+msgstr "пошкоджена bitmap таблиця пошуку: триплетна позиція поза індексом"
+
+msgid "corrupt bitmap lookup table: xor chain exceeds entry count"
+msgstr ""
+"пошкоджена bitmap таблиця пошуку: xor ланцюжок перевищує кількість записів"
+
+#, c-format
+msgid "corrupt bitmap lookup table: commit index %u out of range"
+msgstr ""
+"пошкоджена bitmap таблиця пошуку: індекс коміту %u поза межами діапазону"
+
+#, c-format
+msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
+msgstr "пошкоджений ewah bitmap: урізаний заголовок для bitmap коміту \"%s\""
+
+#, c-format
+msgid "object '%s' not found in type bitmaps"
+msgstr "обʼєкт \"%s\" не знайдено в типах bitmap"
+
+#, c-format
+msgid "object '%s' does not have a unique type"
+msgstr "обʼєкт \"%s\" не має унікального типу"
+
+#, c-format
+msgid "object '%s': real type '%s', expected: '%s'"
+msgstr "обʼєкт \"%s\": дійсний тип \"%s\", очікуваний: \"%s\""
+
+#, c-format
+msgid "object not in bitmap: '%s'"
+msgstr "обʼєкт не у bitmap: \"%s\""
+
+msgid "failed to load bitmap indexes"
+msgstr "не вдалося завантажити bitmap індекси"
+
+msgid "you must specify exactly one commit to test"
+msgstr "ви маєте вказати лишень один коміт для тестування"
+
+#, c-format
+msgid "commit '%s' doesn't have an indexed bitmap"
+msgstr "коміт \"%s\" не має індексованого bitmap"
+
+msgid "mismatch in bitmap results"
+msgstr "розбіжність в bitmap результатах"
+
+#, c-format
+msgid "could not find '%s' in pack '%s' at offset %<PRIuMAX>"
+msgstr "не вдалося знайти \"%s\" у пакунку \"%s\" зі зміщенням %<PRIuMAX>"
+
+#, c-format
+msgid "unable to get disk usage of '%s'"
+msgstr ""
+"не вдалося отримати дані про використання дискового простору для \"%s\""
+
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "bitmap файл \"%s\" має невірну контрольну суму"
+
+#, c-format
+msgid "mtimes file %s is too small"
+msgstr "mtimes файл %s занадто малий"
+
+#, c-format
+msgid "mtimes file %s has unknown signature"
+msgstr "mtimes файл %s має невідомий підпис"
+
+#, c-format
+msgid "mtimes file %s has unsupported version %<PRIu32>"
+msgstr "mtimes файл %s має непідтримувану версію %<PRIu32>"
+
+#, c-format
+msgid "mtimes file %s has unsupported hash id %<PRIu32>"
+msgstr "mtimes файл %s має непідтримуваний хеш-ідентифікатор %<PRIu32>"
+
+#, c-format
+msgid "mtimes file %s is corrupt"
+msgstr "mtimes файл %s пошкоджено"
+
+#, c-format
+msgid "reverse-index file %s is too small"
+msgstr "файл зворотного індексу %s занадто малий"
+
+#, c-format
+msgid "reverse-index file %s is corrupt"
+msgstr "файл зворотнього індексу %s пошкоджено"
+
+#, c-format
+msgid "reverse-index file %s has unknown signature"
+msgstr "файл зворотного індексу %s має невідомий підпис"
+
+#, c-format
+msgid "reverse-index file %s has unsupported version %<PRIu32>"
+msgstr "файл зворотного індексу %s має непідтримувану версію %<PRIu32>"
+
+#, c-format
+msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
+msgstr ""
+"файл зворотного індексу %s має непідтримуваний хеш-ідентифікатор %<PRIu32>"
+
+msgid "invalid checksum"
+msgstr "неприпустима контрольна сума"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr ""
+"невірна позиція зворортного індексу у %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "multi-pack-index reverse-index шматок має невірний розмір"
+
+msgid "cannot both write and verify reverse index"
+msgstr "неможливо одночасно записувати та звіряти зворотний індекс"
+
+#, c-format
+msgid "could not stat: %s"
+msgstr "не вдалося виконати stat: %s"
+
+#, c-format
+msgid "failed to make %s readable"
+msgstr "не вдалося зробити %s читабельним"
+
+#, c-format
+msgid "could not write '%s' promisor file"
+msgstr "не вдалося записати promisor файл \"%s\""
+
+msgid "offset before end of packfile (broken .idx?)"
+msgstr "зміщення перед кінцем файла пакунка (пошкоджений .idx?)"
+
+#, c-format
+msgid "packfile %s cannot be mapped%s"
+msgstr "файл пакунка %s не може бути mapped%s"
+
+#, c-format
+msgid "offset before start of pack index for %s (corrupt index?)"
+msgstr "зміщення перед початком індекса пакунка для %s (пошкоджений індекс?)"
+
+#, c-format
+msgid "offset beyond end of pack index for %s (truncated index?)"
+msgstr "зміщення за межою кінця індекса пакунка для %s (зрізаний індекс?)"
+
+#, c-format
+msgid "malformed expiration date '%s'"
+msgstr "невірно сформована дата закінчення терміну дії \"%s\""
+
+#, c-format
+msgid "option `%s' expects \"always\", \"auto\", or \"never\""
+msgstr "опція \"%s\" очікує \"always\", \"auto\" або \"never\""
+
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "невірно сформована назва обʼєкта \"%s\""
+
+#, c-format
+msgid "option `%s' expects \"%s\" or \"%s\""
+msgstr "опція \"%s\" очікує \"%s\" або \"%s\""
+
+#, c-format
+msgid "%s requires a value"
+msgstr "%s потребує значення"
+
+#, c-format
+msgid "%s takes no value"
+msgstr "%s не приймає значення"
+
+#, c-format
+msgid "%s isn't available"
+msgstr "%s недоступний"
+
+#, c-format
+msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
+msgstr "%s очікує невід'ємне ціле значення з опціональним суфіксом k/m/g"
+
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s несумісний з %s"
+
+#, c-format
+msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
+msgstr "неоднозначна опція: %s (може бути --%s%s або --%s%s)"
+
+#, c-format
+msgid "did you mean `--%s` (with two dashes)?"
+msgstr "ви мали на увазі \"--%s\" (з двома рисками)?"
+
+#, c-format
+msgid "alias of --%s"
+msgstr "псевдонім для --%s"
+
+msgid "need a subcommand"
+msgstr "потрібна підкоманда"
+
+#, c-format
+msgid "unknown option `%s'"
+msgstr "невідома опція \"%s\""
+
+#, c-format
+msgid "unknown switch `%c'"
+msgstr "невідомий перемикач \"%c\""
+
+#, c-format
+msgid "unknown non-ascii option in string: `%s'"
+msgstr "невідомий non-ascii параметр у рядку: \"%s\""
+
+msgid "..."
+msgstr "..."
+
+#, c-format
+msgid "usage: %s"
+msgstr "використання: %s"
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation.
+#.
+#, c-format
+msgid "   or: %s"
+msgstr "         або: %s"
+
+#. TRANSLATORS: You should only need to translate this format
+#. string if your language is a RTL language (e.g. Arabic,
+#. Hebrew etc.), not if it's a LTR language (e.g. German,
+#. Russian, Chinese etc.).
+#. *
+#. When a translated usage string has an embedded "\n" it's
+#. because options have wrapped to the next line. The line
+#. after the "\n" will then be padded to align with the
+#. command name, such as N_("git cmd [opt]\n<8
+#. spaces>[opt2]"), where the 8 spaces are the same length as
+#. "git cmd ".
+#. *
+#. This format string prints out that already-translated
+#. line. The "%*s" is whitespace padding to account for the
+#. padding at the start of the line that we add in this
+#. function. The "%s" is a line in the (hopefully already
+#. translated) N_() usage string, which contained embedded
+#. newlines before we split it up.
+#.
+#, c-format
+msgid "%*s%s"
+msgstr "%*s%s"
+
+#, c-format
+msgid "    %s"
+msgstr "    %s"
+
+msgid "-NUM"
+msgstr "-NUM"
+
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "протилежне --no-%s"
+
+msgid "expiry-date"
+msgstr "закінчення строку дії"
+
+msgid "no-op (backward compatibility)"
+msgstr "не працює (зворотна сумісність)"
+
+msgid "be more verbose"
+msgstr "надавати більш розгорнутий вивід"
+
+msgid "be more quiet"
+msgstr "надавати менш розгорнутий вивід"
+
+msgid "use <n> digits to display object names"
+msgstr "використовувати <n> цифр для відображення назв обʼєктів"
+
+msgid "prefixed path to initial superproject"
+msgstr "префіксний шлях до початкового суперпроекту"
+
+msgid "how to strip spaces and #comments from message"
+msgstr "як прибирати пробіли та #коментарі з допису"
+
+msgid "read pathspec from file"
+msgstr "прочитати визначник шляху з файлу"
+
+msgid ""
+"with --pathspec-from-file, pathspec elements are separated with NUL character"
+msgstr ""
+"за допомогою --pathspec-from-file елементи визначника шляху відокремлюються "
+"символом NUL"
+
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "невірне булеве значення конфігурації \"%s\" для \"%s\""
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "не вдалося розібрати %s"
+
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr "Не вдалося зробити %s доступним для запису групою"
+
+msgid "Escape character '\\' not allowed as last character in attr value"
+msgstr ""
+"Символ екранування \"\\\" не дозволяється як останній символ у значенні "
+"атрибута"
+
+msgid "Only one 'attr:' specification is allowed."
+msgstr "Дозволений лише один визначник \"attr:\"."
+
+msgid "attr spec must not be empty"
+msgstr "визначник атрибута повинен бути не порожнім"
+
+#, c-format
+msgid "invalid attribute name %s"
+msgstr "невірна назва атрибуту %s"
+
+msgid "global 'glob' and 'noglob' pathspec settings are incompatible"
+msgstr "глобальні параметри визначника шляху \"glob\" та \"noglob\" несумісні"
+
+msgid ""
+"global 'literal' pathspec setting is incompatible with all other global "
+"pathspec settings"
+msgstr ""
+"глобальний параметр \"literal\" визначника шляху несумісний з усіма іншими "
+"глобальними параметрами визначника шляху"
+
+msgid "invalid parameter for pathspec magic 'prefix'"
+msgstr ""
+"неприпустимий параметр для магічного значення \"prefix\" визначника шляху"
+
+#, c-format
+msgid "Invalid pathspec magic '%.*s' in '%s'"
+msgstr "Неприпустиме магічне значення \"%.*s\" для визначника шляху в \"%s\""
+
+#, c-format
+msgid "Missing ')' at the end of pathspec magic in '%s'"
+msgstr ""
+"Пропущено \")\" наприкінці магічного значення для визначника шляху в \"%s\""
+
+#, c-format
+msgid "Unimplemented pathspec magic '%c' in '%s'"
+msgstr "Нереалізоване магічне значення \"%c\" для визначника шляху в \"%s\""
+
+#, c-format
+msgid "%s: 'literal' and 'glob' are incompatible"
+msgstr "%s: \"literal\" та \"glob\" несумісні"
+
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "\"%s\" знаходиться поза деревом директорій"
+
+#, c-format
+msgid "%s: '%s' is outside repository at '%s'"
+msgstr "%s: \"%s\" знаходиться за межами сховища за адресою \"%s\""
+
+#, c-format
+msgid "'%s' (mnemonic: '%c')"
+msgstr "\"%s\" (мнемонічно: \"%c\")"
+
+#, c-format
+msgid "%s: pathspec magic not supported by this command: %s"
+msgstr ""
+"%s: магічне значення для визначника шляху не підтримується цією командою: %s"
+
+#, c-format
+msgid "pathspec '%s' is beyond a symbolic link"
+msgstr "визначник шляху \"%s\" знаходиться за межами символьного посилання"
+
+#, c-format
+msgid "line is badly quoted: %s"
+msgstr "рядок взято в лапки невірно: %s"
+
+msgid "unable to write flush packet"
+msgstr "не вдалося записати flush пакет"
+
+msgid "unable to write delim packet"
+msgstr "не вдалося записати delim пакет"
+
+msgid "unable to write response end packet"
+msgstr "не вдалося записати response end пакет"
+
+msgid "flush packet write failed"
+msgstr "не вдалося записати flush пакет"
+
+msgid "protocol error: impossibly long line"
+msgstr "помилка протоколу: неймовірно довгий рядок"
+
+msgid "packet write with format failed"
+msgstr "не вдалося записати пакет з форматуванням"
+
+msgid "packet write failed - data exceeds max packet size"
+msgstr ""
+"не вдалося записати пакет - дані перевищують максимальний розмір пакета"
+
+#, c-format
+msgid "packet write failed: %s"
+msgstr "не вдалося записати пакет: %s"
+
+msgid "read error"
+msgstr "помилка зчитування"
+
+msgid "the remote end hung up unexpectedly"
+msgstr "віддалене призначення несподівано відключилося"
+
+#, c-format
+msgid "protocol error: bad line length character: %.4s"
+msgstr "помилка протоколу: невірний символ довжини рядка: %.4s"
+
+#, c-format
+msgid "protocol error: bad line length %d"
+msgstr "помилка протоколу: невірна довжина рядка %d"
+
+#, c-format
+msgid "remote error: %s"
+msgstr "помилка віддаленого призначення: %s"
+
+msgid "Refreshing index"
+msgstr "Оновлення індекса"
+
+#, c-format
+msgid "unable to create threaded lstat: %s"
+msgstr "не вдалося створити потоковий lstat: %s"
+
+msgid "unable to parse --pretty format"
+msgstr "не вдалося розібрати --pretty формат"
+
+msgid "promisor-remote: unable to fork off fetch subprocess"
+msgstr "promisor-remote: не вдалося розгалужити підпроцес отримання"
+
+msgid "promisor-remote: could not write to fetch subprocess"
+msgstr "promisor-remote: не вдалося записати до підпроцесу отримання"
+
+msgid "promisor-remote: could not close stdin to fetch subprocess"
+msgstr "promisor-remote: не вдалося закрити stdin для підпроцесу отримання"
+
+#, c-format
+msgid "promisor remote name cannot begin with '/': %s"
+msgstr "назва віддаленого promisor не може починатися з \"/\": %s"
+
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "не вдалося отримати %s з віддаленого promisor"
+
+msgid "object-info: expected flush after arguments"
+msgstr "object-info: очікувався flush після аргументів"
+
+msgid "Removing duplicate objects"
+msgstr "Видалення дублікатів обʼєктів"
+
+msgid "could not start `log`"
+msgstr "не вдалося розпочати \"log\""
+
+msgid "could not read `log` output"
+msgstr "не вдалося прочитати \"log\" вивід"
+
+#, c-format
+msgid "could not parse commit '%s'"
+msgstr "не вдалося розібрати коміт \"%s\""
+
+#, c-format
+msgid ""
+"could not parse first line of `log` output: did not start with 'commit ': "
+"'%s'"
+msgstr ""
+"не вдалося розібрати перший рядок \"log\" виводу: не починався з \"commit "
+"\"': \"%s\""
+
+#, c-format
+msgid "could not parse git header '%.*s'"
+msgstr "не вдалося розібрати git заголовок \"%.*s\""
+
+msgid "failed to generate diff"
+msgstr "не вдалося згенерувати різницю"
+
+#, c-format
+msgid "could not parse log for '%s'"
+msgstr "не вдалося розібрати журнал для \"%s\""
+
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "неприпустима додаткова марна верхівка: \"%s\""
+
+msgid "unable to enumerate additional recent objects"
+msgstr "не вдалося перерахувати додаткові нещодавні обʼєкти"
+
+#, c-format
+msgid "will not add file alias '%s' ('%s' already exists in index)"
+msgstr "не додаємо псевдонім файлу \"%s\" (\"%s\" вже існує в індексі)"
+
+msgid "cannot create an empty blob in the object database"
+msgstr "неможливо створити порожній blob в базі даних обʼєктів"
+
+#, c-format
+msgid "%s: can only add regular files, symbolic links or git-directories"
+msgstr ""
+"%s: може додавати лише звичайні файли, символьні посилання або git-директорії"
+
+#, c-format
+msgid "unable to index file '%s'"
+msgstr "не вдалося проіндексувати файл \"%s\""
+
+#, c-format
+msgid "unable to add '%s' to index"
+msgstr "не вдалося додати \"%s\" до індексу"
+
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "не вдалося виконати stat для \"%s\""
+
+#, c-format
+msgid "'%s' appears as both a file and as a directory"
+msgstr "\"%s\" відображається як файл і як каталог"
+
+msgid "Refresh index"
+msgstr "Оновити індекс"
+
+#, c-format
+msgid ""
+"index.version set, but the value is invalid.\n"
+"Using version %i"
+msgstr ""
+"index.version встановлено, але значення неприпустиме.\n"
+"Використання версії %i"
+
+#, c-format
+msgid ""
+"GIT_INDEX_VERSION set, but the value is invalid.\n"
+"Using version %i"
+msgstr ""
+"GIT_INDEX_VERSION встановлено, але значення неприпустиме.\n"
+"Використання версії %i"
+
+#, c-format
+msgid "bad signature 0x%08x"
+msgstr "невірний підпис 0x%08x"
+
+#, c-format
+msgid "bad index version %d"
+msgstr "невірна версія індексу %d"
+
+msgid "bad index file sha1 signature"
+msgstr "невірний sha1 підпис індексного файлу"
+
+#, c-format
+msgid "index uses %.4s extension, which we do not understand"
+msgstr "індекс використовує %.4s розширення, яке ми не розуміємо"
+
+#, c-format
+msgid "ignoring %.4s extension"
+msgstr "ігнорування %.4s розширення"
+
+#, c-format
+msgid "unknown index entry format 0x%08x"
+msgstr "невідомий формат запису індексу 0x%08x"
+
+#, c-format
+msgid "malformed name field in the index, near path '%s'"
+msgstr "невірно сформоване поле назви в індексі біля шляху \"%s\""
+
+msgid "unordered stage entries in index"
+msgstr "невпорядковані записи в індексі"
+
+#, c-format
+msgid "multiple stage entries for merged file '%s'"
+msgstr "багатоступеневі записи для злитого файлу \"%s\""
+
+#, c-format
+msgid "unordered stage entries for '%s'"
+msgstr "невпорядковані записи індексу для '%s'"
+
+#, c-format
+msgid "unable to create load_cache_entries thread: %s"
+msgstr "не вдалося створити потік load_cache_entries: %s"
+
+#, c-format
+msgid "unable to join load_cache_entries thread: %s"
+msgstr "не вдалося приєднатися до потоку load_cache_entries: %s"
+
+#, c-format
+msgid "%s: index file open failed"
+msgstr "%s: не вдалося відкрити індексний файл"
+
+#, c-format
+msgid "%s: cannot stat the open index"
+msgstr "%s: не вдалося виконати stat відкритого індексу"
+
+#, c-format
+msgid "%s: index file smaller than expected"
+msgstr "%s: індексний файл менший, ніж очікувався"
+
+#, c-format
+msgid "%s: unable to map index file%s"
+msgstr "%s: не вдалося зіставити індексний файл%s"
+
+#, c-format
+msgid "unable to create load_index_extensions thread: %s"
+msgstr "не вдалося створити потік load_index_extensions: %s"
+
+#, c-format
+msgid "unable to join load_index_extensions thread: %s"
+msgstr "не вдалося приєднатися до потоку load_index_extensions: %s"
+
+#, c-format
+msgid "could not freshen shared index '%s'"
+msgstr "не вдалося оновити спільний індекс \"%s\""
+
+#, c-format
+msgid "broken index, expect %s in %s, got %s"
+msgstr "пошкоджений індекс, очікувався %s у %s, отримано %s"
+
+msgid "cannot write split index for a sparse index"
+msgstr "неможливо записати розділений індекс для розрідженого індексу"
+
+msgid "failed to convert to a sparse-index"
+msgstr "не вдалося перетворити в sparse-index"
+
+#, c-format
+msgid "unable to open git dir: %s"
+msgstr "не вдалося відкрити git-директорію: %s"
+
+#, c-format
+msgid "unable to unlink: %s"
+msgstr "не вдалося видалити: %s"
+
+#, c-format
+msgid "cannot fix permission bits on '%s'"
+msgstr "неможливо виправити біти дозволу на \"%s\""
+
+#, c-format
+msgid "%s: cannot drop to stage #0"
+msgstr "%s: неможливо скинути індекс до стану #0"
+
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "неочікуваний статус diff %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "видалити \"%s\"\n"
+
+msgid ""
+"You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
+"continue'.\n"
+"Or you can abort the rebase with 'git rebase --abort'.\n"
+msgstr ""
+"Ви можете виправити це командою \"git rebase --edit-todo\", а потім "
+"запустити \"git rebase --continue\".\n"
+"Або ви можете перервати перебазування за допомогою \"git rebase --abort\".\n"
+
+#, c-format
+msgid ""
+"unrecognized setting %s for option rebase.missingCommitsCheck. Ignoring."
+msgstr ""
+"нерозпізнане значення %s для опції rebase.missingCommitsCheck. Ігнорується."
+
+msgid ""
+"\n"
+"Commands:\n"
+"p, pick <commit> = use commit\n"
+"r, reword <commit> = use commit, but edit the commit message\n"
+"e, edit <commit> = use commit, but stop for amending\n"
+"s, squash <commit> = use commit, but meld into previous commit\n"
+"f, fixup [-C | -c] <commit> = like \"squash\" but keep only the previous\n"
+"                   commit's log message, unless -C is used, in which case\n"
+"                   keep only this commit's message; -c is same as -C but\n"
+"                   opens the editor\n"
+"x, exec <command> = run command (the rest of the line) using shell\n"
+"b, break = stop here (continue rebase later with 'git rebase --continue')\n"
+"d, drop <commit> = remove commit\n"
+"l, label <label> = label current HEAD with a name\n"
+"t, reset <label> = reset HEAD to a label\n"
+"m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]\n"
+"        create a merge commit using the original merge commit's\n"
+"        message (or the oneline, if no original merge commit was\n"
+"        specified); use -c <commit> to reword the commit message\n"
+"u, update-ref <ref> = track a placeholder for the <ref> to be updated\n"
+"                      to this position in the new commits. The <ref> is\n"
+"                      updated at the end of the rebase\n"
+"\n"
+"These lines can be re-ordered; they are executed from top to bottom.\n"
+msgstr ""
+"\n"
+"Команди:\n"
+"p, pick <коміт> = використати коміт\n"
+"r, reword <коміт> = використати коміт, але відредагувати допис до коміту\n"
+"e, edit <коміт> = використати коміт, але зупинитись для внесення поправок\n"
+"s, squash <коміт> = використати коміт, але обʼєднати з попереднім комітом\n"
+"f, fixup [-C | -c] <коміт> = як \"squash\", але зберігає лише попередній\n"
+"                   допис до коміту, якщо тільки не вказана опція -С, у якому "
+"разі\n"
+"                   зберігається лише допис до цього коміту; -c - те саме, що "
+"й -C, але\n"
+"                   відкриває редактор\n"
+"x, exec <команда> = виконати команду (решту рядка) за допомогою shell\n"
+"b, break = зупинитись тут (продовжити перебазування пізніше за допомогою "
+"'git rebase --continue')\n"
+"d, drop <коміт> = видалити коміт\n"
+"l, label <мітка> = помітити поточний HEAD назвою\n"
+"t, reset <мітка> = скинути HEAD до мітки\n"
+"m, merge [-C <коміт> | -c <коміт>] <мітка> [# <однорядковий допис>]\n"
+"        створити коміт злиття, використовуючи допис до початкового коміту\n"
+"        злиття (або однорядковий допис, якщо не було вказано коміт\n"
+"        злиття); використовуйте -c <коміт>, щоб перефразувати допис до "
+"коміту\n"
+"u, update-ref <посилання> = відстежити заповнювач для <посилання>, яке "
+"потрібно оновити\n"
+"                      до цієї позиції у нових комітах. <посилання>\n"
+"                      оновлюється в кінці перебазування\n"
+"\n"
+"Ці рядки можна переупорядкувати; вони виконуються зверху вниз.\n"
+
+#, c-format
+msgid "Rebase %s onto %s (%d command)"
+msgid_plural "Rebase %s onto %s (%d commands)"
+msgstr[0] "Перебазування %s на %s (%d команда)"
+msgstr[1] "Перебазування %s на %s (%d команди)"
+msgstr[2] "Перебазування %s на %s (%d команд)"
+
+msgid ""
+"\n"
+"Do not remove any line. Use 'drop' explicitly to remove a commit.\n"
+msgstr ""
+"\n"
+"Не видаляйте жодного рядка. Використовуйте \"drop\", щоб явно видалити "
+"коміт.\n"
+
+msgid ""
+"\n"
+"If you remove a line here THAT COMMIT WILL BE LOST.\n"
+msgstr ""
+"\n"
+"Якщо ви видалите тут рядок, ЦЕЙ КОМІТ БУДЕ ВТРАЧЕНО.\n"
+
+msgid ""
+"\n"
+"You are editing the todo file of an ongoing interactive rebase.\n"
+"To continue rebase after editing, run:\n"
+"    git rebase --continue\n"
+"\n"
+msgstr ""
+"\n"
+"Ви редагуєте файл todo поточного інтерактивного перебазування.\n"
+"Щоб продовжити перебазування після редагування, виконайте:\n"
+"    git rebase --continue\n"
+
+msgid ""
+"\n"
+"However, if you remove everything, the rebase will be aborted.\n"
+"\n"
+msgstr ""
+"\n"
+"Однак, якщо ви видалите все, перебазування буде перервано.\n"
+"\n"
+
+#, c-format
+msgid "could not write '%s'."
+msgstr "не вдалося записати \"%s\"."
+
+#, c-format
+msgid ""
+"Warning: some commits may have been dropped accidentally.\n"
+"Dropped commits (newer to older):\n"
+msgstr ""
+"Попередження: деякі коміти могли бути видалені випадково.\n"
+"Скинуті коміти (від новіших до старіших):\n"
+
+#, c-format
+msgid ""
+"To avoid this message, use \"drop\" to explicitly remove a commit.\n"
+"\n"
+"Use 'git config rebase.missingCommitsCheck' to change the level of "
+"warnings.\n"
+"The possible behaviours are: ignore, warn, error.\n"
+"\n"
+msgstr ""
+"Щоб уникнути цього повідомлення, використовуйте \"drop\" для явного "
+"видалення коміту.\n"
+"\n"
+"Використовуйте \"git config rebase.missingCommitsCheck\" для зміни рівня "
+"попереджень.\n"
+"Можливі варіанти поведінки: ignore, warn, error.\n"
+
+#, c-format
+msgid "%s: 'preserve' superseded by 'merges'"
+msgstr "%s: \"preserve\" замінено на \"merges\""
+
+msgid "gone"
+msgstr "відсутнє"
+
+#, c-format
+msgid "ahead %d"
+msgstr "попереду %d"
+
+#, c-format
+msgid "behind %d"
+msgstr "позаду %d"
+
+#, c-format
+msgid "ahead %d, behind %d"
+msgstr "попереду %d, позаду %d"
+
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) не приймає аргументів"
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "нерозпізнаний %%(%.*s) аргумент: %s"
+
+#, c-format
+msgid "expected format: %%(color:<color>)"
+msgstr "очікуваний формат: %%(color:<колір>)"
+
+#, c-format
+msgid "unrecognized color: %%(color:%s)"
+msgstr "нерозпізнаний колір: %%(color:%s)"
+
+#, c-format
+msgid "Integer value expected refname:lstrip=%s"
+msgstr "Очікувалось ціле число refname:lstrip=%s"
+
+#, c-format
+msgid "Integer value expected refname:rstrip=%s"
+msgstr "Очікувалось ціле число refname:rstrip=%s"
+
+#, c-format
+msgid "expected %%(trailers:key=<value>)"
+msgstr "очікувалось %%(trailers:key=<значення>)"
+
+#, c-format
+msgid "unknown %%(trailers) argument: %s"
+msgstr "невідомий %%(trailers) аргумент: %s"
+
+#, c-format
+msgid "positive value expected contents:lines=%s"
+msgstr "очікувалось додатне значення contents:lines=%s"
+
+#, c-format
+msgid "argument expected for %s"
+msgstr "очікувався аргумент для %s"
+
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "очікувалось додатне значення %s=%s"
+
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "неможливо повністю розібрати %s=%s"
+
+#, c-format
+msgid "value expected %s="
+msgstr "очікувалось значення %s="
+
+#, c-format
+msgid "positive value expected '%s' in %%(%s)"
+msgstr "очікувалось додатне значення \"%s\" в %%(%s)"
+
+#, c-format
+msgid "expected format: %%(align:<width>,<position>)"
+msgstr "очікуваний формат: %%(align:<ширина>,<позиція>)"
+
+#, c-format
+msgid "unrecognized position:%s"
+msgstr "нерозпізнана позиція:%s"
+
+#, c-format
+msgid "unrecognized width:%s"
+msgstr "нерозпізнана ширина:%s"
+
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "нерозпізнаний %%(%s) аргумент: %s"
+
+#, c-format
+msgid "positive width expected with the %%(align) atom"
+msgstr "очікувалась додатна ширина з %%(align) часткою"
+
+#, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "очікуваний формат: %%(ahead-behind:<комітоподібне>)"
+
+#, c-format
+msgid "malformed field name: %.*s"
+msgstr "невірно сформована назва поля: %.*s"
+
+#, c-format
+msgid "unknown field name: %.*s"
+msgstr "невідома назва поля: %.*s"
+
+#, c-format
+msgid ""
+"not a git repository, but the field '%.*s' requires access to object data"
+msgstr "не git сховище, але поле \"%.*s\" потребує доступу до даних обʼєкта"
+
+#, c-format
+msgid "format: %%(%s) atom used without a %%(%s) atom"
+msgstr "формат: частка %%(%s) використано без частки %%(%s)"
+
+#, c-format
+msgid "format: %%(then) atom used more than once"
+msgstr "формат: %%(then) частка використана більше одного разу"
+
+#, c-format
+msgid "format: %%(then) atom used after %%(else)"
+msgstr "формат: %%(then) частка використана після %%(else)"
+
+#, c-format
+msgid "format: %%(else) atom used more than once"
+msgstr "формат: %%(else) частка використана більше одного разу"
+
+#, c-format
+msgid "format: %%(end) atom used without corresponding atom"
+msgstr "формат: %%(end) частка використана без відповідної частки"
+
+#, c-format
+msgid "malformed format string %s"
+msgstr "невірно сформований рядок форматування %s"
+
+#, c-format
+msgid "this command reject atom %%(%.*s)"
+msgstr "ця команда відхиляє частку %%(%.*s)"
+
+#, c-format
+msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
+msgstr "--format=%.*s не може бути використаний з --python, --shell, --tcl"
+
+msgid "failed to run 'describe'"
+msgstr "не вдалося запустити команду \"describe\""
+
+#, c-format
+msgid "(no branch, rebasing %s)"
+msgstr "(немає гілки, перебазування %s)"
+
+#, c-format
+msgid "(no branch, rebasing detached HEAD %s)"
+msgstr "(немає гілки, перебазування відокремленого HEAD %s)"
+
+#, c-format
+msgid "(no branch, bisect started on %s)"
+msgstr "(немає гілки, бісекцію розпочато на %s)"
+
+#, c-format
+msgid "(HEAD detached at %s)"
+msgstr "(HEAD відʼєднано на %s)"
+
+#, c-format
+msgid "(HEAD detached from %s)"
+msgstr "(HEAD відʼєднано від %s)"
+
+msgid "(no branch)"
+msgstr "(немає гілки)"
+
+#, c-format
+msgid "missing object %s for %s"
+msgstr "відсутній обʼєкт %s для %s"
+
+#, c-format
+msgid "parse_object_buffer failed on %s for %s"
+msgstr "parse_object_buffer завершився невдало на %s для %s"
+
+#, c-format
+msgid "malformed object at '%s'"
+msgstr "невірно сформований обʼєкт за адресою \"%s\""
+
+#, c-format
+msgid "ignoring ref with broken name %s"
+msgstr "ігнорування посилання з пошкодженою назвою %s"
+
+#, c-format
+msgid "ignoring broken ref %s"
+msgstr "ігнорування пошкоджених посилань %s"
+
+#, c-format
+msgid "format: %%(end) atom missing"
+msgstr "формат: %%(end) частка відсутня"
+
+#, c-format
+msgid "malformed object name %s"
+msgstr "невірно сформована назва обʼєкта %s"
+
+#, c-format
+msgid "option `%s' must point to a commit"
+msgstr "опція \"%s\" повинна вказувати на коміт"
+
+msgid "key"
+msgstr "ключ"
+
+msgid "field name to sort on"
+msgstr "назва поля для сортування"
+
+msgid "exclude refs which match pattern"
+msgstr "виключити посилання, які збігаються з шаблоном"
+
+#, c-format
+msgid "not a reflog: %s"
+msgstr "не є журналом посилань: %s"
+
+#, c-format
+msgid "no reflog for '%s'"
+msgstr "немає журналу посилань для \"%s\""
+
+#, c-format
+msgid "%s does not point to a valid object!"
+msgstr "%s не вказує на допустимий обʼєкт!"
+
+#, c-format
+msgid ""
+"Using '%s' as the name for the initial branch. This default branch name\n"
+"is subject to change. To configure the initial branch name to use in all\n"
+"of your new repositories, which will suppress this warning, call:\n"
+"\n"
+"\tgit config --global init.defaultBranch <name>\n"
+"\n"
+"Names commonly chosen instead of 'master' are 'main', 'trunk' and\n"
+"'development'. The just-created branch can be renamed via this command:\n"
+"\n"
+"\tgit branch -m <name>\n"
+msgstr ""
+"Використання \"%s\" як назви початкової гілки. Назву гілки за замовчуванням\n"
+"може бути змінено. Щоб налаштувати назву початкової гілки для використання у "
+"всіх\n"
+"ваших нових сховищах, що приховає це попередження, викличте\n"
+"\n"
+"\tgit config --global init.defaultBranch <назва>.\n"
+"\n"
+"Замість \"master\" зазвичай використовують такі назви, як \"main\", "
+"\"trunk\" та\n"
+"\"development\". Щойно створену гілку можна перейменувати за допомогою цієї "
+"команди:\n"
+"\n"
+"\tgit branch -m <назва>\n"
+
+#, c-format
+msgid "could not retrieve `%s`"
+msgstr "не вдалося отримати \"%s\""
+
+#, c-format
+msgid "invalid branch name: %s = %s"
+msgstr "неприпустима назва гілки: %s = %s"
+
+#, c-format
+msgid "ignoring dangling symref %s"
+msgstr "ігнорування висячих symref %s"
+
+#, c-format
+msgid "log for ref %s has gap after %s"
+msgstr "лог для посилання %s має пропуск після %s"
+
+#, c-format
+msgid "log for ref %s unexpectedly ended on %s"
+msgstr "лог для посилання %s несподівано завершився на %s"
+
+#, c-format
+msgid "log for %s is empty"
+msgstr "лог для %s порожній"
+
+#, c-format
+msgid "refusing to update ref with bad name '%s'"
+msgstr "відмовлено в оновленні посилання з невірною назвою \"%s\""
+
+#, c-format
+msgid "update_ref failed for ref '%s': %s"
+msgstr "update_ref завершився невдало для посилання \"%s\": %s"
+
+#, c-format
+msgid "multiple updates for ref '%s' not allowed"
+msgstr "багатократні оновлення для посилання \"%s\" заборонені"
+
+msgid "ref updates forbidden inside quarantine environment"
+msgstr "оновлення посилань заборонено у карантинному середовищі"
+
+msgid "ref updates aborted by hook"
+msgstr "оновлення посилань перервано гачком"
+
+#, c-format
+msgid "'%s' exists; cannot create '%s'"
+msgstr "\"%s\" існує; неможливо створити \"%s\""
+
+#, c-format
+msgid "cannot process '%s' and '%s' at the same time"
+msgstr "неможливо обробити \"%s\" і \"%s\" одночасно"
+
+#, c-format
+msgid "could not remove reference %s"
+msgstr "не вдалося видалити посилання %s"
+
+#, c-format
+msgid "could not delete reference %s: %s"
+msgstr "не вдалося видалити посилання %s: %s"
+
+#, c-format
+msgid "could not delete references: %s"
+msgstr "не вдалося видалити посилання: %s"
+
+#, c-format
+msgid "invalid refspec '%s'"
+msgstr "неприпустимий визначник посилання \"%s\""
+
+#, c-format
+msgid "invalid quoting in push-option value: '%s'"
+msgstr "неприпустимі лапки у значенні push-опції: \"%s\""
+
+#, c-format
+msgid "%sinfo/refs not valid: is this a git repository?"
+msgstr "%sinfo/refs не дійсні: це git сховище?"
+
+msgid "invalid server response; expected service, got flush packet"
+msgstr ""
+"неприпустима відповідь сервера; очікувалось service, отримано flush пакет"
+
+#, c-format
+msgid "invalid server response; got '%s'"
+msgstr "неприпустима відповідь сервера; отримано \"%s\""
+
+#, c-format
+msgid "repository '%s' not found"
+msgstr "сховище \"%s\" не знайдено"
+
+#, c-format
+msgid "Authentication failed for '%s'"
+msgstr "Невдала аутентифікація для \"%s\""
+
+#, c-format
+msgid "unable to access '%s' with http.pinnedPubkey configuration: %s"
+msgstr ""
+"не вдалося отримати доступ до \"%s\" з конфігурацією http.pinnedPubkey: %s"
+
+#, c-format
+msgid "unable to access '%s': %s"
+msgstr "не вдалося отримати доступ до \"%s\": %s"
+
+#, c-format
+msgid "redirecting to %s"
+msgstr "перенаправлення на %s"
+
+msgid "shouldn't have EOF when not gentle on EOF"
+msgstr "не повинен мати EOF, якщо не є обережним з EOF"
+
+msgid "remote server sent unexpected response end packet"
+msgstr "віддалений сервер надіслав неочікуваний response end пакет"
+
+msgid "unable to rewind rpc post data - try increasing http.postBuffer"
+msgstr ""
+"не вдалося перемотати  вперед rpc post дані - спробуйте збільшити http."
+"postBuffer"
+
+#, c-format
+msgid "remote-curl: bad line length character: %.4s"
+msgstr "remote-curl: невірний символ довжини рядка: %.4s"
+
+msgid "remote-curl: unexpected response end packet"
+msgstr "remote-curl: неочікуваний response end пакет"
+
+#, c-format
+msgid "RPC failed; %s"
+msgstr "RPC завершився невдало; %s"
+
+msgid "cannot handle pushes this big"
+msgstr "неможливо впоратися з такими великими надсиланнями"
+
+#, c-format
+msgid "cannot deflate request; zlib deflate error %d"
+msgstr "неможливо запакувати запит; zlib помилка пакування %d"
+
+#, c-format
+msgid "cannot deflate request; zlib end error %d"
+msgstr "неможливо запакувати запит; zlib помилка завершення %d"
+
+#, c-format
+msgid "%d bytes of length header were received"
+msgstr "%d байтів заголовку було отримано"
+
+#, c-format
+msgid "%d bytes of body are still expected"
+msgstr "%d байтів тіла все ще очікуються"
+
+msgid "dumb http transport does not support shallow capabilities"
+msgstr "засіб передачі dumb http не підтримує shallow здібності"
+
+msgid "fetch failed."
+msgstr "отримання завершилось невдало."
+
+msgid "cannot fetch by sha1 over smart http"
+msgstr "неможливо отримати дані за допомогою sha1 через smart http"
+
+#, c-format
+msgid "protocol error: expected sha/ref, got '%s'"
+msgstr "помилка протоколу: очікувалось sha/ref, отримано \"%s\""
+
+#, c-format
+msgid "http transport does not support %s"
+msgstr "http засіб передачі не підтримує %s"
+
+msgid "protocol error: expected '<url> <path>', missing space"
+msgstr "помилка протоколу: очікувалось \"<url> <шлях>\", пропущено пробіл"
+
+#, c-format
+msgid "failed to download file at URL '%s'"
+msgstr "не вдалося завантажити файл за URL-адресою \"%s\""
+
+msgid "git-http-push failed"
+msgstr "git-http-push завершився невдало"
+
+msgid "remote-curl: usage: git remote-curl <remote> [<url>]"
+msgstr ""
+"remote-curl: використання: git remote-curl <віддаленe-призначення> [<url>]"
+
+msgid "remote-curl: error reading command stream from git"
+msgstr "remote-curl: помилка зчитування потоку команд з git"
+
+msgid "remote-curl: fetch attempted without a local repo"
+msgstr "remote-curl: спроба отримання без локального сховища"
+
+#, c-format
+msgid "remote-curl: unknown command '%s' from git"
+msgstr "remote-curl: невідома команда \"%s\" з git"
+
+#, c-format
+msgid "config remote shorthand cannot begin with '/': %s"
+msgstr "скорочення віддаленої конфігураціі не може починатися з '/': %s"
+
+msgid "more than one receivepack given, using the first"
+msgstr "надано більше одного пакунка для отримання, використано перший"
+
+msgid "more than one uploadpack given, using the first"
+msgstr "надано більше одного пакунка для завантаження, використано перший"
+
+#, c-format
+msgid "unrecognized value transfer.credentialsInUrl: '%s'"
+msgstr "нерозпізнане значення transfer.credentialsInUrl: '%s'"
+
+#, c-format
+msgid "URL '%s' uses plaintext credentials"
+msgstr "URL-адреса \"%s\" використовує облікові дані у відкритому тексті"
+
+#, c-format
+msgid "Cannot fetch both %s and %s to %s"
+msgstr "Неможливо отримати як %s, так і %s до %s"
+
+#, c-format
+msgid "%s usually tracks %s, not %s"
+msgstr "%s зазвичай відстежує %s, а не %s"
+
+#, c-format
+msgid "%s tracks both %s and %s"
+msgstr "%s відстежує як %s, так і %s"
+
+#, c-format
+msgid "key '%s' of pattern had no '*'"
+msgstr "ключ '%s' шаблону не містив '*'"
+
+#, c-format
+msgid "value '%s' of pattern has no '*'"
+msgstr "значення '%s' шаблону не містить '*'"
+
+#, c-format
+msgid "src refspec %s does not match any"
+msgstr "визначник посилання джерела %s не збігається з жодним"
+
+#, c-format
+msgid "src refspec %s matches more than one"
+msgstr "визначник посилання джерела %s збігається з більш ніж одним"
+
+#. TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
+#. <remote> <src>:<dst>" push, and "being pushed ('%s')" is
+#. the <src>.
+#.
+#, c-format
+msgid ""
+"The destination you provided is not a full refname (i.e.,\n"
+"starting with \"refs/\"). We tried to guess what you meant by:\n"
+"\n"
+"- Looking for a ref that matches '%s' on the remote side.\n"
+"- Checking if the <src> being pushed ('%s')\n"
+"  is a ref in \"refs/{heads,tags}/\". If so we add a corresponding\n"
+"  refs/{heads,tags}/ prefix on the remote side.\n"
+"\n"
+"Neither worked, so we gave up. You must fully qualify the ref."
+msgstr ""
+"Призначення, що ви вказали, не є повною назвою посилання (такою,\n"
+"що починається з \"refs/\"). Ми спробували вгадати, що ви мали на увазі "
+"через:\n"
+"\n"
+"- Пошук посилання, яке відповідає \"%s\" на віддаленій стороні.\n"
+"- Перевірку, чи є <джерело> надсилання, (\"%s\")\n"
+"  посиланням у \"refs/{heads,tags}/\". Якщо так, то ми додаємо відповідний\n"
+"  refs/{heads,tags}/ префікс на віддаленій стороні.\n"
+"\n"
+"Ні те, ні інше не спрацювало, тому ми здалися. Ви повинні повністю "
+"кваліфікувати посилання."
+
+#, c-format
+msgid ""
+"The <src> part of the refspec is a commit object.\n"
+"Did you mean to create a new branch by pushing to\n"
+"'%s:refs/heads/%s'?"
+msgstr ""
+"Частина <джерело> визаначника посилання є об’єктом коміту.\n"
+"Ви хотіли створити нову гілку, надіславши зміни до\n"
+"\"%s:refs/heads/%s\"?"
+
+#, c-format
+msgid ""
+"The <src> part of the refspec is a tag object.\n"
+"Did you mean to create a new tag by pushing to\n"
+"'%s:refs/tags/%s'?"
+msgstr ""
+"Частина <джерело> визаначника посилання є об’єктом тегу.\n"
+"Ви хотіли створити новий тег, надіславши зміни до\n"
+"\"%s:refs/tags/%s\"?"
+
+#, c-format
+msgid ""
+"The <src> part of the refspec is a tree object.\n"
+"Did you mean to tag a new tree by pushing to\n"
+"'%s:refs/tags/%s'?"
+msgstr ""
+"Частина <джерело> визаначника посилання є об’єктом дерева.\n"
+"Ви хотіли позначити нове дерево, надіславши зміни до\n"
+"\"%s:refs/tags/%s\"?"
+
+#, c-format
+msgid ""
+"The <src> part of the refspec is a blob object.\n"
+"Did you mean to tag a new blob by pushing to\n"
+"'%s:refs/tags/%s'?"
+msgstr ""
+"Частина <джерело> визаначника посилання є об’єктом blob.\n"
+"Ви хотіли позначити новий blob, надіславши зміни до\n"
+"\"%s:refs/tags/%s\"?"
+
+#, c-format
+msgid "%s cannot be resolved to branch"
+msgstr "%s неможливо розвʼязати в гілку"
+
+#, c-format
+msgid "unable to delete '%s': remote ref does not exist"
+msgstr "не вдалося видалити \"%s\": віддалене посилання не існує"
+
+#, c-format
+msgid "dst refspec %s matches more than one"
+msgstr "визаначник посилання призначення %s збігається з більш ніж одним"
+
+#, c-format
+msgid "dst ref %s receives from more than one src"
+msgstr "посилання призначення %s отримує з більш ніж одного src"
+
+msgid "HEAD does not point to a branch"
+msgstr "HEAD не вказує на гілку"
+
+#, c-format
+msgid "no such branch: '%s'"
+msgstr "немає такої гілки: '%s'"
+
+#, c-format
+msgid "no upstream configured for branch '%s'"
+msgstr "першоджерельне сховище не налаштовано для гілки '%s'"
+
+#, c-format
+msgid "upstream branch '%s' not stored as a remote-tracking branch"
+msgstr "висхідна гілка \"%s\" не збережена як віддалено відстежувана гілка"
+
+#, c-format
+msgid "push destination '%s' on remote '%s' has no local tracking branch"
+msgstr ""
+"шлях призначення надсилання \"%s\" на віддаленому \"%s\" не має гілки "
+"локального відстежування"
+
+#, c-format
+msgid "branch '%s' has no remote for pushing"
+msgstr "гілка \"%s\" не має віддаленого призначення для надсилання"
+
+#, c-format
+msgid "push refspecs for '%s' do not include '%s'"
+msgstr "надіслані визначники посилань для \"%s\" не включають \"%s\""
+
+msgid "push has no destination (push.default is 'nothing')"
+msgstr "надсилання не має призначення (push.default дорівнює 'nothing')"
+
+msgid "cannot resolve 'simple' push to a single destination"
+msgstr ""
+"неможливо розвʼязати \"simple\" надсилання до єдиного пункту призначення"
+
+#, c-format
+msgid "couldn't find remote ref %s"
+msgstr "не вдалося знайти віддалене посилання %s"
+
+#, c-format
+msgid "* Ignoring funny ref '%s' locally"
+msgstr "* Ігнорування кумедних посилань \"%s\" локально"
+
+#, c-format
+msgid "Your branch is based on '%s', but the upstream is gone.\n"
+msgstr "Ваша гілка базується на \"%s\", але першоджерельне сховище зникло.\n"
+
+msgid "  (use \"git branch --unset-upstream\" to fixup)\n"
+msgstr ""
+"  (використовуйте команду \"git branch --unset-upstream\", щоб виправити)\n"
+
+#, c-format
+msgid "Your branch is up to date with '%s'.\n"
+msgstr "Ваша гілка не відрізняється від \"%s\".\n"
+
+#, c-format
+msgid "Your branch and '%s' refer to different commits.\n"
+msgstr "Ваша гілка та гілка \"%s\" посилаються до різних комітів.\n"
+
+#, c-format
+msgid "  (use \"%s\" for details)\n"
+msgstr "  (використовуйте \"%s\" для отримання детальної інформації)\n"
+
+#, c-format
+msgid "Your branch is ahead of '%s' by %d commit.\n"
+msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
+msgstr[0] "Ваша гілка випереджає \"%s\" на %d коміт.\n"
+msgstr[1] "Ваша гілка випереджає \"%s\" на %d коміти.\n"
+msgstr[2] "Ваша гілка випереджає \"%s\" на %d комітів.\n"
+
+msgid "  (use \"git push\" to publish your local commits)\n"
+msgstr "  (скористайтесь \"git push\", щоб надіслати локальні коміти)\n"
+
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+"Ваша гілка відстає від \"%s\" на %d коміт, і її можна перемотати вперед.\n"
+msgstr[1] ""
+"Ваша гілка відстає від \"%s\" на %d коміти, і її можна перемотати вперед.\n"
+msgstr[2] ""
+"Ваша гілка відстає від гілки '%s' на %d комітів, і її можна перемотати "
+"вперед.\n"
+
+msgid "  (use \"git pull\" to update your local branch)\n"
+msgstr "  (використовуйте \"git pull\", щоб оновити вашу локальну гілку)\n"
+
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+"Ваша гілка і гілка '%s' розійшлися,\n"
+"і мають %d і %d різний коміт відповідно.\n"
+msgstr[1] ""
+"Ваша гілка і гілка \"%s\" розійшлися,\n"
+"і мають %d і %d різних коміти відповідно.\n"
+msgstr[2] ""
+"Ваша гілка і гілка \"%s\" розійшлися,\n"
+"і мають %d і %d різних комітів відповідно.\n"
+
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr ""
+"  (скористайтесь \"git pull\", якщо ви хочете інтегрувати віддалену гілку зі "
+"своєю)\n"
+
+#, c-format
+msgid "cannot parse expected object name '%s'"
+msgstr "неможливо розібрати очікувану назву обʼєкту \"%s\""
+
+#, c-format
+msgid "cannot strip one component off url '%s'"
+msgstr "неможливо вилучити один компонент з url '%s'"
+
+#, c-format
+msgid "bad replace ref name: %s"
+msgstr "неприпустима назва заміни посилання: %s"
+
+#, c-format
+msgid "duplicate replace ref: %s"
+msgstr "дубльована заміна посилання: %s"
+
+#, c-format
+msgid "replace depth too high for object %s"
+msgstr "глибина заміни занадто висока для обʼєкта %s"
+
+msgid "corrupt MERGE_RR"
+msgstr "пошкоджений MERGE_RR"
+
+msgid "unable to write rerere record"
+msgstr "неможливо зробити rerere запис"
+
+#, c-format
+msgid "there were errors while writing '%s' (%s)"
+msgstr "виникли помилки під час запису \"%s\" (%s)"
+
+#, c-format
+msgid "could not parse conflict hunks in '%s'"
+msgstr "не вдалося розібрати конфліктні шматки в \"%s\""
+
+#, c-format
+msgid "failed utime() on '%s'"
+msgstr "невдалий utime() на \"%s\""
+
+#, c-format
+msgid "writing '%s' failed"
+msgstr "запис \"%s\" завершився невдало"
+
+#, c-format
+msgid "Staged '%s' using previous resolution."
+msgstr "Індексовано \"%s\" з використанням попереднього вирішення."
+
+#, c-format
+msgid "Recorded resolution for '%s'."
+msgstr "Записано вирішення для \"%s\"."
+
+#, c-format
+msgid "Resolved '%s' using previous resolution."
+msgstr "Вирішено \"%s\" з використанням попереднього вирішення."
+
+#, c-format
+msgid "cannot unlink stray '%s'"
+msgstr "неможливо видалити блукаючий \"%s\""
+
+#, c-format
+msgid "Recorded preimage for '%s'"
+msgstr "Записано попереднє зображення для \"%s\""
+
+#, c-format
+msgid "failed to update conflicted state in '%s'"
+msgstr "не вдалося оновити конфліктний стан у \"%s\""
+
+#, c-format
+msgid "no remembered resolution for '%s'"
+msgstr "немає записаного вирішення для \"%s\""
+
+#, c-format
+msgid "Updated preimage for '%s'"
+msgstr "Оновлено попереднє зображення для \"%s\""
+
+#, c-format
+msgid "Forgot resolution for '%s'\n"
+msgstr "Забуто вирішення для \"%s\"\n"
+
+msgid "unable to open rr-cache directory"
+msgstr "неможливо відкрити rr-cache директорію"
+
+msgid "update the index with reused conflict resolution if possible"
+msgstr ""
+"оновити індекс з повторним використанням вирішення конфліктів, якщо це "
+"можливо"
+
+msgid "could not determine HEAD revision"
+msgstr "не вдалося визначити ревізію HEAD"
+
+#, c-format
+msgid "failed to find tree of %s"
+msgstr "не вдалося знайти дерево %s"
+
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "непідтримувана секція для прихованих посилань: %s"
+
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= передано більше одного разу"
+
+#, c-format
+msgid "resolve-undo records `%s` which is missing"
+msgstr "resolve-undo записує \"%s\", який відсутній"
+
+#, c-format
+msgid "could not get commit for ancestry-path argument %s"
+msgstr "не вдалося отримати коміт для ancestry-path аргументу %s"
+
+msgid "--unpacked=<packfile> no longer supported"
+msgstr "--unpacked=<файл пакунка> більше не підтримується"
+
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "неприпустима опція \"%s\" у --stdin режимі"
+
+msgid "your current branch appears to be broken"
+msgstr "ваша поточна гілка виглядає пошкодженою"
+
+#, c-format
+msgid "your current branch '%s' does not have any commits yet"
+msgstr "ваша поточна гілка \"%s\" ще не має жодних комітів"
+
+msgid "object filtering requires --objects"
+msgstr "для фільтрації обʼєктів потрібен параметр --objects"
+
+msgid "-L does not yet support diff formats besides -p and -s"
+msgstr "-L поки що не підтримує diff формати, окрім -p та -s"
+
+#, c-format
+msgid "cannot create async thread: %s"
+msgstr "неможливо створити асинхронний потік: %s"
+
+#, c-format
+msgid "'%s' does not exist"
+msgstr "\"%s\" не існує"
+
+#, c-format
+msgid "could not switch to '%s'"
+msgstr "не вдалося переключитися на \"%s\""
+
+msgid "need a working directory"
+msgstr "потрібна робоча директорія"
+
+msgid "Scalar enlistments require a worktree"
+msgstr "Коренева директорія проекту потребує робоче дерево"
+
+#, c-format
+msgid "could not configure %s=%s"
+msgstr "не вдалося налаштувати %s=%s"
+
+msgid "could not configure log.excludeDecoration"
+msgstr "не вдалося налаштувати log.excludeDecoration"
+
+msgid "could not add enlistment"
+msgstr "не вдалося додати кореневу директорію проекту"
+
+msgid "could not set recommended config"
+msgstr "не вдалося встановити рекомендовану конфігурацію"
+
+msgid "could not turn on maintenance"
+msgstr "не вдалося ввімкнути технічне обслуговування"
+
+msgid "could not start the FSMonitor daemon"
+msgstr "не вдалося запустити FSMonitor демон"
+
+msgid "could not turn off maintenance"
+msgstr "не вдалося вимкнути технічне обслуговування"
+
+msgid "could not remove enlistment"
+msgstr "не вдалося видалити директорію верхнього рівня"
+
+#, c-format
+msgid "remote HEAD is not a branch: '%.*s'"
+msgstr "віддалений HEAD не є гілкою: \"%.*s\""
+
+msgid "failed to get default branch name from remote; using local default"
+msgstr ""
+"не вдалося отримати назву гілки за замовчуванням з віддаленого сховища; "
+"використано локальне значення за замовчуванням"
+
+msgid "failed to get default branch name"
+msgstr "не вдалося отримати назву гілки за замовчуванням"
+
+msgid "failed to unregister repository"
+msgstr "не вдалося скасувати реєстрацію сховища"
+
+msgid "failed to stop the FSMonitor daemon"
+msgstr "не вдалося зупинити FSMonitor демон"
+
+msgid "failed to delete enlistment directory"
+msgstr "не вдалося видалити кореневу директорію проекту"
+
+msgid "branch to checkout after clone"
+msgstr "гілка, на яку перейти після клонування"
+
+msgid "when cloning, create full working directory"
+msgstr "при клонуванні створювати повну робочу директорію"
+
+msgid "only download metadata for the branch that will be checked out"
+msgstr ""
+"завантажити метадані тільки для гілки, на яку буде здійснюватися перехід"
+
+msgid "create repository within 'src' directory"
+msgstr "створити сховище в директорії \"src\""
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <головна-гілка>] [--full-clone]\n"
+"\t[--[no-]src] <URL-адреса> [<коренева-директорія>]"
+
+#, c-format
+msgid "cannot deduce worktree name from '%s'"
+msgstr "неможливо вивести назву робочого дерева з \"%s\""
+
+#, c-format
+msgid "directory '%s' exists already"
+msgstr "директорія \"%s\" вже існує"
+
+#, c-format
+msgid "failed to get default branch for '%s'"
+msgstr "не вдалося отримати гілку за замовчуванням для \"%s\""
+
+#, c-format
+msgid "could not configure remote in '%s'"
+msgstr "не вдалося налаштувати віддалене сховище в \"%s\""
+
+#, c-format
+msgid "could not configure '%s'"
+msgstr "не вдалося налаштувати \"%s\""
+
+msgid "partial clone failed; attempting full clone"
+msgstr "не вдалося зробити розріджений клон; спроба зробити повний клон"
+
+msgid "could not configure for full clone"
+msgstr "не вдалося налаштувати для повного клона"
+
+msgid "scalar diagnose [<enlistment>]"
+msgstr "scalar diagnose [<коренева-директорія-проекту>]"
+
+msgid "`scalar list` does not take arguments"
+msgstr "\"scalar list\" не приймає аргументів"
+
+msgid "scalar register [<enlistment>]"
+msgstr "scalar register [<коренева-директорія-проекту>]"
+
+msgid "reconfigure all registered enlistments"
+msgstr "переналаштувати всі зареєстровані кореневі директорії проекту"
+
+msgid "scalar reconfigure [--all | <enlistment>]"
+msgstr "scalar reconfigure [--all | <коренева-директорія-проекту>]"
+
+msgid "--all or <enlistment>, but not both"
+msgstr "--all або <коренева-директорія-проекту>, але не обидва"
+
+#, c-format
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "неможливо видалити застаріле scalar.repo \"%s\""
+
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "видалено застаріле scalar.repo \"%s\""
+
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "у сховища \"%s\" інший власник"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "невірній формат сховища \"%s\""
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "сховище \"%s\" не знайдено"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"щоб скасувати реєстрацію цього репозиторію в Scalar, виконайте\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+
+msgid ""
+"scalar run <task> [<enlistment>]\n"
+"Tasks:\n"
+msgstr ""
+"scalar run <завдання> [<коренева-директорія-проекту>]\n"
+"Завдання:\n"
+
+#, c-format
+msgid "no such task: '%s'"
+msgstr "немає такого завдання: \"%s\""
+
+msgid "scalar unregister [<enlistment>]"
+msgstr "scalar unregister [<коренева-директорія-проекту>]"
+
+msgid "scalar delete <enlistment>"
+msgstr "scalar delete <коренева-директорія-проекту>"
+
+msgid "refusing to delete current working directory"
+msgstr "відмовлено у видаленні поточної робочої директорії"
+
+msgid "include Git version"
+msgstr "включити версію Git"
+
+msgid "include Git's build options"
+msgstr "включити опції збірки Git"
+
+msgid "scalar verbose [-v | --verbose] [--build-options]"
+msgstr "scalar verbose [-v | --verbose] [--build-options]"
+
+msgid "-C requires a <directory>"
+msgstr "-C потребує наявності <директорії>"
+
+#, c-format
+msgid "could not change to '%s'"
+msgstr "не вдалося змінити на \"%s\""
+
+msgid "-c requires a <key>=<value> argument"
+msgstr "-c потребує <ключ>=<значення> аргумент"
+
+msgid ""
+"scalar [-C <directory>] [-c <key>=<value>] <command> [<options>]\n"
+"\n"
+"Commands:\n"
+msgstr ""
+"scalar [-C <директорія>] [-c <ключ>=<значення>] <команда> [<опції>]\n"
+"\n"
+"Команди:\n"
+
+msgid "unexpected flush packet while reading remote unpack status"
+msgstr ""
+"несподіваний flush пакет під час зчитування статусу віддаленого розпакування"
+
+#, c-format
+msgid "unable to parse remote unpack status: %s"
+msgstr "не вдалося розібрати стан розпакування віддаленого сховища: %s"
+
+#, c-format
+msgid "remote unpack failed: %s"
+msgstr "віддалене розпакування не вдалося: %s"
+
+msgid "failed to sign the push certificate"
+msgstr "не вдалося підписати сертифікат надсилання"
+
+msgid "send-pack: unable to fork off fetch subprocess"
+msgstr "send-pack: не вдалося розгалужити підпроцес отримання"
+
+msgid "push negotiation failed; proceeding anyway with push"
+msgstr "перемовини з надсилання не вдалися; все одно продовжуємо надсилання"
+
+msgid "the receiving end does not support this repository's hash algorithm"
+msgstr "отримуюча сторона не підтримує хеш-алгоритм цього сховища"
+
+msgid "the receiving end does not support --signed push"
+msgstr "отримуюча сторона не підтримує --signed push"
+
+msgid ""
+"not sending a push certificate since the receiving end does not support --"
+"signed push"
+msgstr ""
+"сертифікат надсилання не відправлено, оскільки отримуюча сторона не "
+"підтримує --signed push"
+
+msgid "the receiving end does not support --atomic push"
+msgstr "отримуюча сторона не підтримує --atomic push"
+
+msgid "the receiving end does not support push options"
+msgstr "отримуюча сторона не підтримує опції push"
+
+#, c-format
+msgid "invalid commit message cleanup mode '%s'"
+msgstr "неприпустимий режим очищення дописів до коміту \"%s\""
+
+#, c-format
+msgid "could not delete '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+msgid "revert"
+msgstr "вивертання"
+
+msgid "cherry-pick"
+msgstr "висмикування"
+
+msgid "rebase"
+msgstr "перебазування"
+
+#, c-format
+msgid "unknown action: %d"
+msgstr "невідома дія: %d"
+
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+"після вирішення конфліктів позначте виправлені шляхи\n"
+"за допомогою \"git add <шляхи>\" або \"git rm <шляхи>\""
+
+msgid ""
+"After resolving the conflicts, mark them with\n"
+"\"git add/rm <pathspec>\", then run\n"
+"\"git cherry-pick --continue\".\n"
+"You can instead skip this commit with \"git cherry-pick --skip\".\n"
+"To abort and get back to the state before \"git cherry-pick\",\n"
+"run \"git cherry-pick --abort\"."
+msgstr ""
+"Після вирішення конфліктів позначте їх за допомогою\n"
+"\"git add/rm <визначник шляху>\", а потім виконайте\n"
+"\"git cherry-pick --continue\".\n"
+"Замість цього ви можете пропустити цей коміт за допомогою \"git cherry-pick "
+"--skip\".\n"
+"Щоб перервати процес і повернутися до стану перед \"git cherry-pick\",\n"
+"виконайте \"git cherry-pick --abort\"."
+
+msgid ""
+"After resolving the conflicts, mark them with\n"
+"\"git add/rm <pathspec>\", then run\n"
+"\"git revert --continue\".\n"
+"You can instead skip this commit with \"git revert --skip\".\n"
+"To abort and get back to the state before \"git revert\",\n"
+"run \"git revert --abort\"."
+msgstr ""
+"Після вирішення конфліктів позначте їх за допомогою\n"
+"\"git add/rm <визначник шляху>\", а потім виконайте\n"
+"\"git revert --continue\".\n"
+"Замість цього ви можете пропустити цей коміт за допомогою \"git revert --"
+"skip\".\n"
+"Щоб перервати і повернутися до стану перед \"git revert\",\n"
+"виконайте \"git revert --abort\"."
+
+#, c-format
+msgid "could not lock '%s'"
+msgstr "не вдалося зафіксувати \"%s\""
+
+#, c-format
+msgid "could not write eol to '%s'"
+msgstr "не вдалося записати eol в \"%s\""
+
+#, c-format
+msgid "failed to finalize '%s'"
+msgstr "не вдалося завершити \"%s\""
+
+#, c-format
+msgid "your local changes would be overwritten by %s."
+msgstr "ваші локальні зміни будуть перезаписані %s."
+
+msgid "commit your changes or stash them to proceed."
+msgstr "зробіть коміт або додайте ваші зміни до схову, щоб продовжити."
+
+#. TRANSLATORS: %s will be "revert", "cherry-pick" or
+#. "rebase".
+#.
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s: Не вдалося записати новий індексний файл"
+
+msgid "unable to update cache tree"
+msgstr "не вдалося оновити дерево кешу"
+
+msgid "could not resolve HEAD commit"
+msgstr "не вдалося розпізнати HEAD коміт"
+
+#, c-format
+msgid "no key present in '%.*s'"
+msgstr "немає ключа у \"%.*s\""
+
+#, c-format
+msgid "unable to dequote value of '%s'"
+msgstr "не вдалося вилучити лапки зі значення \"%s\""
+
+msgid "'GIT_AUTHOR_NAME' already given"
+msgstr "\"GIT_AUTHOR_NAME\" вже надано"
+
+msgid "'GIT_AUTHOR_EMAIL' already given"
+msgstr "\"GIT_AUTHOR_EMAIL\" вже надано"
+
+msgid "'GIT_AUTHOR_DATE' already given"
+msgstr "\"GIT_AUTHOR_DATE\" вже надано"
+
+#, c-format
+msgid "unknown variable '%s'"
+msgstr "невідома змінна \"%s\""
+
+msgid "missing 'GIT_AUTHOR_NAME'"
+msgstr "бракує \"GIT_AUTHOR_NAME\""
+
+msgid "missing 'GIT_AUTHOR_EMAIL'"
+msgstr "бракує \"GIT_AUTHOR_EMAIL\""
+
+msgid "missing 'GIT_AUTHOR_DATE'"
+msgstr "бракує \"GIT_AUTHOR_DATE\""
+
+#, c-format
+msgid ""
+"you have staged changes in your working tree\n"
+"If these changes are meant to be squashed into the previous commit, run:\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"If they are meant to go into a new commit, run:\n"
+"\n"
+"  git commit %s\n"
+"\n"
+"In both cases, once you're done, continue with:\n"
+"\n"
+"  git rebase --continue\n"
+msgstr ""
+"ви маєте індексовані зміни у вашому робочому дереві\n"
+"Якщо ці зміни мають бути стиснуті у попередній коміт, запустіть:\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"Якщо вони мають потрапити до нового коміту, виконайте:\n"
+"\n"
+"  git commit %s\n"
+"\n"
+"В обох випадках, як тільки ви закінчите, продовжуйте за допомогою:\n"
+"\n"
+"  git rebase --continue\n"
+
+msgid "'prepare-commit-msg' hook failed"
+msgstr "\"prepare-commit-msg\" гачок завершився невдало"
+
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Ваше імʼя та адреса електронної пошти були налаштовані автоматично на "
+"основі\n"
+"вашого імені користувача та назви хосту. Будь ласка, перевірте їх "
+"правильність.\n"
+"Ви можете приховати це повідомлення, встановивши їх явно. Запустіть\n"
+"наступну команду і дотримуйтесь інструкцій у вашому редакторі, щоб "
+"відредагувати\n"
+"ваш конфігураційний файл:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"Після цього ви можете виправити особистість автора, використану для цього "
+"коміту, за допомогою\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Ваше імʼя та адреса електронної пошти були налаштовані автоматично на "
+"основі\n"
+"вашого імені користувача та назви хосту. Будь ласка, перевірте їх "
+"правильність.\n"
+"Ви можете приховати це повідомлення, вказавши їх явно:\n"
+"\n"
+"    git config --global user.name \"Ваше Імʼя\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"Після цього ви можете виправити особистість автора, використану для цього "
+"коміту, за допомогою\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+msgid "couldn't look up newly created commit"
+msgstr "не вдалося знайти новостворений коміт"
+
+msgid "could not parse newly created commit"
+msgstr "не вдалося розібрати новостворений коміт"
+
+msgid "unable to resolve HEAD after creating commit"
+msgstr "не вдалося розвʼязати HEAD після створення коміту"
+
+msgid "detached HEAD"
+msgstr "відʼєднаний HEAD"
+
+msgid " (root-commit)"
+msgstr " (кореневий коміт)"
+
+msgid "could not parse HEAD"
+msgstr "неможливо розібрати HEAD"
+
+#, c-format
+msgid "HEAD %s is not a commit!"
+msgstr "HEAD %s не є комітом!"
+
+msgid "unable to parse commit author"
+msgstr "не вдалося розібрати автора коміту"
+
+#, c-format
+msgid "unable to read commit message from '%s'"
+msgstr "не вдалося прочитати допис до коміту від \"%s\""
+
+#, c-format
+msgid "invalid author identity '%s'"
+msgstr "неприпустима особистість автора \"%s\""
+
+msgid "corrupt author: missing date information"
+msgstr "пошкоджені дані про автора: відсутня інформація про дату"
+
+#, c-format
+msgid "could not update %s"
+msgstr "не вдалося оновити %s"
+
+#, c-format
+msgid "could not parse commit %s"
+msgstr "не вдалося розібрати коміт %s"
+
+#, c-format
+msgid "could not parse parent commit %s"
+msgstr "не вдалося розібрати джерельний коміт %s"
+
+#, c-format
+msgid "unknown command: %d"
+msgstr "невідома команда: %d"
+
+msgid "This is the 1st commit message:"
+msgstr "Це перший допис до коміту:"
+
+#, c-format
+msgid "This is the commit message #%d:"
+msgstr "Це допис до коміту #%d:"
+
+msgid "The 1st commit message will be skipped:"
+msgstr "Перший допис до коміту буде пропущено:"
+
+#, c-format
+msgid "The commit message #%d will be skipped:"
+msgstr "Допис до коміту #%d буде пропущено:"
+
+#, c-format
+msgid "This is a combination of %d commits."
+msgstr "Це комбінація %d комітів."
+
+#, c-format
+msgid "cannot write '%s'"
+msgstr "неможливо записати \"%s\""
+
+msgid "need a HEAD to fixup"
+msgstr "потрібен HEAD, щоб виправити"
+
+msgid "could not read HEAD"
+msgstr "не вдалося прочитати HEAD"
+
+msgid "could not read HEAD's commit message"
+msgstr "не вдалося прочитати допис до коміту HEAD"
+
+#, c-format
+msgid "could not read commit message of %s"
+msgstr "не вдалося прочитати допис до коміту %s"
+
+msgid "your index file is unmerged."
+msgstr "ваш індексний файл не злитий."
+
+msgid "cannot fixup root commit"
+msgstr "не вдалося виправити кореневий коміт"
+
+#, c-format
+msgid "commit %s is a merge but no -m option was given."
+msgstr "коміт %s - це злиття, але опція -m не була вказана."
+
+#, c-format
+msgid "commit %s does not have parent %d"
+msgstr "коміт %s не має джерела %d"
+
+#, c-format
+msgid "cannot get commit message for %s"
+msgstr "неможливо отримати допис до коміту для %s"
+
+#. TRANSLATORS: The first %s will be a "todo" command like
+#. "revert" or "pick", the second %s a SHA1.
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr "%s: не вдалося розібрати джерельний коміт %s"
+
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "не вдалося зробити вивертання %s... %s"
+
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "не вдалося застосувати %s... %s"
+
+#, c-format
+msgid "dropping %s %s -- patch contents already upstream\n"
+msgstr ""
+"скидання %s %s -- вміст латки вже знаходиться у першоджерельному сховищі\n"
+
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s: не вдалося прочитати індекс"
+
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s: не вдалося оновити індекс"
+
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "\"%s\" не є припустимою міткою"
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "\"%s\" не є припустимою назвою посилання"
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr "update-ref потребує повної назви посилання, наприклад, refs/heads/%s"
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "неприпустима команда \"%.*s\""
+
+#, c-format
+msgid "%s does not accept arguments: '%s'"
+msgstr "%s не приймає аргументи: \"%s\""
+
+#, c-format
+msgid "missing arguments for %s"
+msgstr "відсутні аргументи для %s"
+
+#, c-format
+msgid "could not parse '%s'"
+msgstr "не вдалося розібрати \"%s\""
+
+#, c-format
+msgid "invalid line %d: %.*s"
+msgstr "невірний рядок %d: %.*s"
+
+#, c-format
+msgid "cannot '%s' without a previous commit"
+msgstr "неможливо зробити \"%s\" без попереднього коміту"
+
+msgid "cancelling a cherry picking in progress"
+msgstr "наразі йде скасування висмикування"
+
+msgid "cancelling a revert in progress"
+msgstr "наразі йде скасування вивертання"
+
+msgid "please fix this using 'git rebase --edit-todo'."
+msgstr "будь ласка, виправте це за допомогою \"git rebase --edit-todo\"."
+
+#, c-format
+msgid "unusable instruction sheet: '%s'"
+msgstr "непридатна інструкція: \"%s\""
+
+msgid "no commits parsed."
+msgstr "не розібрано жодного коміту."
+
+msgid "cannot cherry-pick during a revert."
+msgstr "неможливо зробити висмикування під час вивертання."
+
+msgid "cannot revert during a cherry-pick."
+msgstr "неможливо зробити вивертання під час висмикування."
+
+msgid "unusable squash-onto"
+msgstr "непридатний squash-onto"
+
+#, c-format
+msgid "malformed options sheet: '%s'"
+msgstr "невірно сформований список опцій: \"%s\""
+
+msgid "empty commit set passed"
+msgstr "передано порожній набір комітів"
+
+msgid "revert is already in progress"
+msgstr "наразі вже триває вивертання"
+
+#, c-format
+msgid "try \"git revert (--continue | %s--abort | --quit)\""
+msgstr "спробуйте \"git revert (--continue | %s--abort | --quit)\""
+
+msgid "cherry-pick is already in progress"
+msgstr "наразі вже триває висмикування"
+
+#, c-format
+msgid "try \"git cherry-pick (--continue | %s--abort | --quit)\""
+msgstr "спробуйте \"git cherry-pick (--continue | %s--abort | --quit)\""
+
+#, c-format
+msgid "could not create sequencer directory '%s'"
+msgstr "не вдалося створити директорію секвенсора \"%s\""
+
+msgid "no cherry-pick or revert in progress"
+msgstr "наразі не триває ні скасування, ні висмикування"
+
+msgid "cannot resolve HEAD"
+msgstr "неможливо розпізнати HEAD"
+
+msgid "cannot abort from a branch yet to be born"
+msgstr "неможливо перервати з гілки, яка ще не народилася"
+
+#, c-format
+msgid "cannot read '%s': %s"
+msgstr "неможливо прочитати \"%s\": %s"
+
+msgid "unexpected end of file"
+msgstr "несподіваний кінець файлу"
+
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr "збережений pre-cherry-pick HEAD файл \"%s\" пошкоджено"
+
+msgid "You seem to have moved HEAD. Not rewinding, check your HEAD!"
+msgstr ""
+"Здається, ви перемістили HEAD. Перемотування назад не виконується, перевірте "
+"свій HEAD!"
+
+msgid "no revert in progress"
+msgstr "наразі не виконується вивертання"
+
+msgid "no cherry-pick in progress"
+msgstr "наразі не виконується висмикування"
+
+msgid "failed to skip the commit"
+msgstr "не вдалося пропустити коміт"
+
+msgid "there is nothing to skip"
+msgstr "немає чого пропускати"
+
+#, c-format
+msgid ""
+"have you committed already?\n"
+"try \"git %s --continue\""
+msgstr ""
+"ви вже зробили коміт?\n"
+"спробуйте \"git %s --continue\""
+
+msgid "cannot read HEAD"
+msgstr "неможливо прочитати HEAD"
+
+#, c-format
+msgid "unable to copy '%s' to '%s'"
+msgstr "не вдалося скопіювати \"%s\" в \"%s\""
+
+#, c-format
+msgid ""
+"You can amend the commit now, with\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"Once you are satisfied with your changes, run\n"
+"\n"
+"  git rebase --continue\n"
+msgstr ""
+"Ви можете внести зміни до коміту зараз за допомогою\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"Після того, як ви будете задоволені своїми змінами, виконайте\n"
+"\n"
+"  git rebase --continue\n"
+
+#, c-format
+msgid "Could not apply %s... %.*s"
+msgstr "Не вдалося застосувати %s... %.*s"
+
+#, c-format
+msgid "Could not merge %.*s"
+msgstr "Не вдалося злити %.*s"
+
+#, c-format
+msgid "Executing: %s\n"
+msgstr "Виконання: %s\n"
+
+#, c-format
+msgid ""
+"execution failed: %s\n"
+"%sYou can fix the problem, and then run\n"
+"\n"
+"  git rebase --continue\n"
+"\n"
+msgstr ""
+"не вдалося виконати : %s\n"
+"%sВи можете виправити проблему, а потім виконати\n"
+"\n"
+"  git rebase --continue\n"
+
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "та внесли зміни до індексу та/або робочого дерева\n"
+
+#, c-format
+msgid ""
+"execution succeeded: %s\n"
+"but left changes to the index and/or the working tree.\n"
+"Commit or stash your changes, and then run\n"
+"\n"
+"  git rebase --continue\n"
+"\n"
+msgstr ""
+"виконання пройшло успішно: %s\n"
+"але залишило зміни в індексі та/або робочому дереві\n"
+"Зробіть коміт або додайте зміни до схову, а потім запустіть\n"
+"\n"
+"  git rebase --continue\n"
+
+#, c-format
+msgid "illegal label name: '%.*s'"
+msgstr "неприпустима назва мітки: \"%.*s\""
+
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "не вдалося розпізнати \"%s\""
+
+msgid "writing fake root commit"
+msgstr "написання підробленого кореневого коміту"
+
+msgid "writing squash-onto"
+msgstr "запис squash-onto"
+
+msgid "cannot merge without a current revision"
+msgstr "неможливо злити без поточної ревізії"
+
+#, c-format
+msgid "unable to parse '%.*s'"
+msgstr "не вдалося розібрати \"%.*s\""
+
+#, c-format
+msgid "nothing to merge: '%.*s'"
+msgstr "нічого зливати: \"%.*s\""
+
+msgid "octopus merge cannot be executed on top of a [new root]"
+msgstr "octopus злиття неможливо виконати поверх [new root]"
+
+#, c-format
+msgid "could not get commit message of '%s'"
+msgstr "не вдалося отримати допис до коміту \"%s\""
+
+#, c-format
+msgid "could not even attempt to merge '%.*s'"
+msgstr "не вдалося навіть спробувати злити \"%.*s\""
+
+msgid "merge: Unable to write new index file"
+msgstr "злиття: Не вдалося записати новий файл індексу"
+
+#, c-format
+msgid ""
+"another 'rebase' process appears to be running; '%s.lock' already exists"
+msgstr "здається, запущено інший процес \"rebase\"; \"%s.lock\" вже існує"
+
+#, c-format
+msgid ""
+"Updated the following refs with %s:\n"
+"%s"
+msgstr ""
+"Оновлені наступні посилання з %s:\n"
+"%s"
+
+#, c-format
+msgid ""
+"Failed to update the following refs with %s:\n"
+"%s"
+msgstr ""
+"Не вдалося оновити наступні посилання з %s:\n"
+"%s"
+
+msgid "Cannot autostash"
+msgstr "Неможливо зробити автосхов"
+
+#, c-format
+msgid "Unexpected stash response: '%s'"
+msgstr "Неочікувана відповідь схову: \"%s\""
+
+#, c-format
+msgid "Could not create directory for '%s'"
+msgstr "Не вдалося створити директорію для \"%s\""
+
+#, c-format
+msgid "Created autostash: %s\n"
+msgstr "Створено автосхов: %s\n"
+
+msgid "could not reset --hard"
+msgstr "не вдалося виконати скидання з --hard"
+
+#, c-format
+msgid "Applied autostash.\n"
+msgstr "Застосовано автосхов.\n"
+
+#, c-format
+msgid "cannot store %s"
+msgstr "неможливо зберегти %s"
+
+#, c-format
+msgid ""
+"%s\n"
+"Your changes are safe in the stash.\n"
+"You can run \"git stash pop\" or \"git stash drop\" at any time.\n"
+msgstr ""
+"%s\n"
+"Ваші зміни в безпеці у схову.\n"
+"Ви можете запустити \"git stash pop\" або \"git stash drop\" у будь-який "
+"час.\n"
+
+msgid "Applying autostash resulted in conflicts."
+msgstr "Застосування автосхову призвело до конфліктів."
+
+msgid "Autostash exists; creating a new stash entry."
+msgstr "Автосхов існує; створення нового запису схову."
+
+msgid "could not detach HEAD"
+msgstr "не вдалося відʼєднати HEAD"
+
+#, c-format
+msgid "Stopped at HEAD\n"
+msgstr "Зупинено на HEAD\n"
+
+#, c-format
+msgid "Stopped at %s\n"
+msgstr "Зупинено на %s\n"
+
+#, c-format
+msgid ""
+"Could not execute the todo command\n"
+"\n"
+"    %.*s\n"
+"It has been rescheduled; To edit the command before continuing, please\n"
+"edit the todo list first:\n"
+"\n"
+"    git rebase --edit-todo\n"
+"    git rebase --continue\n"
+msgstr ""
+"Не вдалося виконати команду todo\n"
+"\n"
+"    %.*s\n"
+"Її було перенесено; Щоб відредагувати команду перед продовженням, будь "
+"ласка\n"
+"спочатку відредагуйте список справ:\n"
+"\n"
+"    git rebase --edit-todo\n"
+"    git rebase --continue\n"
+
+#, c-format
+msgid "Stopped at %s...  %.*s\n"
+msgstr "Зупинено на %s... %.*s\n"
+
+#, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Перебазування (%d/%d)%s"
+
+#, c-format
+msgid "unknown command %d"
+msgstr "невідома команда %d"
+
+msgid "could not read orig-head"
+msgstr "не вдалося прочитати orig-head"
+
+msgid "could not read 'onto'"
+msgstr "не вдалося прочитати  \"onto\""
+
+#, c-format
+msgid "could not update HEAD to %s"
+msgstr "не вдалося оновити HEAD до %s"
+
+#, c-format
+msgid "Successfully rebased and updated %s.\n"
+msgstr "Успішно перебазовано та оновлено %s.\n"
+
+msgid "cannot rebase: You have unstaged changes."
+msgstr "неможливо перебазувати: у вас є неіндексовані зміни."
+
+msgid "cannot amend non-existing commit"
+msgstr "неможливо виправити неіснуючий коміт"
+
+#, c-format
+msgid "invalid file: '%s'"
+msgstr "неприпустимий файл: \"%s\""
+
+#, c-format
+msgid "invalid contents: '%s'"
+msgstr "неприпустимий вміст: \"%s\""
+
+msgid ""
+"\n"
+"You have uncommitted changes in your working tree. Please, commit them\n"
+"first and then run 'git rebase --continue' again."
+msgstr ""
+"\n"
+"У вас є незакомічені зміни у вашому робочому дереві. Будь ласка, спочатку "
+"зробіть коміт,\n"
+"а потім знову виконайте \"git rebase --continue\"."
+
+#, c-format
+msgid "could not write file: '%s'"
+msgstr "не вдалося записати файл: \"%s\""
+
+msgid "could not remove CHERRY_PICK_HEAD"
+msgstr "не вдалося видалити CHERRY_PICK_HEAD"
+
+msgid "could not commit staged changes."
+msgstr "не вдалося записати індексовані зміни."
+
+#, c-format
+msgid "%s: can't cherry-pick a %s"
+msgstr "%s: неможливо зробити висмикування %s"
+
+#, c-format
+msgid "%s: bad revision"
+msgstr "%s: невірна ревізія"
+
+msgid "can't revert as initial commit"
+msgstr "неможливо зробити вивертання як початковий коміт"
+
+#, c-format
+msgid "skipped previously applied commit %s"
+msgstr "пропущено попередньо застосований коміт %s"
+
+msgid "use --reapply-cherry-picks to include skipped commits"
+msgstr "використовуйте --reapply-cherry-picks, щоб включити пропущені коміти"
+
+msgid "make_script: unhandled options"
+msgstr "make_script: необроблені опції"
+
+msgid "make_script: error preparing revisions"
+msgstr "make_script: помилка при підготовці ревізій"
+
+msgid "nothing to do"
+msgstr "нічого робити"
+
+msgid "could not skip unnecessary pick commands"
+msgstr "не вдалося пропустити непотрібні команди вибору"
+
+msgid "the script was already rearranged."
+msgstr "скрипт вже був перероблений."
+
+#, c-format
+msgid "update-refs file at '%s' is invalid"
+msgstr "файл update-refs за адресою \"%s\" є неприпустимим"
+
+#, c-format
+msgid "'%s' is outside repository at '%s'"
+msgstr "\"%s\" зовнішнє сховище в \"%s\""
+
+#, c-format
+msgid ""
+"%s: no such path in the working tree.\n"
+"Use 'git <command> -- <path>...' to specify paths that do not exist locally."
+msgstr ""
+"%s: немає такого шляху у робочому дереві.\n"
+"Використовуйте \"git <команда> -- <шлях>...\", щоб вказати шляхи, які не "
+"існують локально."
+
+#, c-format
+msgid ""
+"ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+"Use '--' to separate paths from revisions, like this:\n"
+"'git <command> [<revision>...] -- [<file>...]'"
+msgstr ""
+"неоднозначний аргумент \"%s\": невідома ревізія або шлях не у робочому "
+"дереві.\n"
+"Використовуйте \"--\", щоб відокремити шлях від ревізій, наприклад, так:\n"
+"\"git <команда> [<ревізія>...] -- [<файл>...]\""
+
+#, c-format
+msgid "option '%s' must come before non-option arguments"
+msgstr "опція \"%s\" повинна стояти перед аргументами без опцій"
+
+#, c-format
+msgid ""
+"ambiguous argument '%s': both revision and filename\n"
+"Use '--' to separate paths from revisions, like this:\n"
+"'git <command> [<revision>...] -- [<file>...]'"
+msgstr ""
+"неоднозначний аргумент \"%s\": і ревізія, і назва файлу\n"
+"Використовуйте \"--\", щоб відокремити шляхи від ревізій, наприклад, так:\n"
+"\"git <команда> [<ревізія>...] -- [<файл>...]\""
+
+msgid "unable to set up work tree using invalid config"
+msgstr ""
+"не вдалося налаштувати робоче дерево, використовуючи неприпустиму "
+"конфігурацію"
+
+#, c-format
+msgid "Expected git repo version <= %d, found %d"
+msgstr "Очікувалось git сховищe версії <= %d, знайдено %d"
+
+msgid "unknown repository extension found:"
+msgid_plural "unknown repository extensions found:"
+msgstr[0] "знайдено невідомих розширень сховища:"
+msgstr[1] "знайдено невідомих розширень сховища:"
+msgstr[2] "знайдено невідомих розширень сховища:"
+
+msgid "repo version is 0, but v1-only extension found:"
+msgid_plural "repo version is 0, but v1-only extensions found:"
+msgstr[0] "версія сховища дорівнює 0, але знайдено v1-only розширень:"
+msgstr[1] "версія сховища дорівнює 0, але знайдено v1-only розширень:"
+msgstr[2] "версія сховища дорівнює 0, але знайдено v1-only розширень:"
+
+#, c-format
+msgid "error opening '%s'"
+msgstr "помилка при відкритті \"%s\""
+
+#, c-format
+msgid "too large to be a .git file: '%s'"
+msgstr "занадто великий для .git-файлу: \"%s\""
+
+#, c-format
+msgid "error reading %s"
+msgstr "помилка зчитування %s"
+
+#, c-format
+msgid "invalid gitfile format: %s"
+msgstr "неприпустимий формат git файлу: %s"
+
+#, c-format
+msgid "no path in gitfile: %s"
+msgstr "немає шляху в gitfile: %s"
+
+#, c-format
+msgid "not a git repository: %s"
+msgstr "не є git сховищем: %s"
+
+#, c-format
+msgid "'$%s' too big"
+msgstr "\"$%s\" занадто великий"
+
+#, c-format
+msgid "not a git repository: '%s'"
+msgstr "не є git сховищем: \"%s\""
+
+#, c-format
+msgid "cannot chdir to '%s'"
+msgstr "неможливо змінити директорію на \"%s\""
+
+msgid "cannot come back to cwd"
+msgstr "неможливо повернутися до поточної робочої директорії"
+
+#, c-format
+msgid "failed to stat '%*s%s%s'"
+msgstr "не вдалося записати \"%*s%s%s\""
+
+msgid "Unable to read current working directory"
+msgstr "Не вдалося прочитати поточну робочу директорію"
+
+#, c-format
+msgid "cannot change to '%s'"
+msgstr "неможливо змінити на \"%s\""
+
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "не є git сховищем (як і жодна з батьківських директорій): %s"
+
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"не є git сховищем (як і жодна з батьківських директорій до місця монтування "
+"%s)\n"
+"Зупинка на межі файлової системи (GIT_DISCOVERY_ACROSS_FILESYSTEM не "
+"встановлено)."
+
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"виявлено сумнівне право власності у сховищі за адресою \"%s\"\n"
+"%sЩоб додати виняток для цієї директорії, виконайте:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr ""
+"неможливо використати порожнє сховище \"%s\" (safe.bareRepository "
+"встановлено в \"%s\")"
+
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"проблема зі значенням файлового режиму core.sharedRepository (0%.3o).\n"
+"Власник файлів завжди повинен мати дозвіл на читання та запис."
+
+msgid "fork failed"
+msgstr "fork завершився невдало"
+
+msgid "setsid failed"
+msgstr "setsid завершився невдало"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "неможливо виконати stat шаблона \"%s\""
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "неможливо виконати opendir \"%s\""
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "неможливо виконати readlink \"%s\""
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "неможливо виконати symlink \"%s\" \"%s\""
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "неможливо скопіювати \"%s\" до \"%s\""
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "ігнорування шаблону %s"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "шаблонів не знайдено в %s"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "не копіюються шаблони з \"%s\": %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "неприпустиме початкове ім’я гілки: \"%s\""
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "не вдалося обробити тип файлу %d"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "не вдалося перемістити %s на %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "спроба переініціалізувати репозиторій з іншим хеш-алгоритмом"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s вже існує"
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: ігноровано --initial-branch=%s"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Переініціалізовано існуюче спільне Git сховище в %s%s\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Переініціалізовано існуюче Git сховище в %s%s\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Ініціалізовано порожнє спільне Git сховище в %s%s\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Ініціалізовано порожнє Git сховище в %s%s\n"
+
+#, c-format
+msgid "index entry is a directory, but not sparse (%08x)"
+msgstr "запис індексу є директорією, але не розрідженою (%08x)"
+
+msgid "cannot use split index with a sparse index"
+msgstr "неможливо використовувати розділений індекс з розрідженим індексом"
+
+#. TRANSLATORS: IEC 80000-13:2008 gibibyte
+#, c-format
+msgid "%u.%2.2u GiB"
+msgstr "%u.%2.2u ГіБ"
+
+#. TRANSLATORS: IEC 80000-13:2008 gibibyte/second
+#, c-format
+msgid "%u.%2.2u GiB/s"
+msgstr "%u.%2.2u ГіБ/с"
+
+#. TRANSLATORS: IEC 80000-13:2008 mebibyte
+#, c-format
+msgid "%u.%2.2u MiB"
+msgstr "%u.%2.2u МіБ"
+
+#. TRANSLATORS: IEC 80000-13:2008 mebibyte/second
+#, c-format
+msgid "%u.%2.2u MiB/s"
+msgstr "%u.%2.2u МіБ/с"
+
+#. TRANSLATORS: IEC 80000-13:2008 kibibyte
+#, c-format
+msgid "%u.%2.2u KiB"
+msgstr "%u.%2.2u КіБ"
+
+#. TRANSLATORS: IEC 80000-13:2008 kibibyte/second
+#, c-format
+msgid "%u.%2.2u KiB/s"
+msgstr "%u.%2.2u КіБ/с"
+
+#. TRANSLATORS: IEC 80000-13:2008 byte
+#, c-format
+msgid "%u byte"
+msgid_plural "%u bytes"
+msgstr[0] "%u байт"
+msgstr[1] "%u байти"
+msgstr[2] "%u байтів"
+
+#. TRANSLATORS: IEC 80000-13:2008 byte/second
+#, c-format
+msgid "%u byte/s"
+msgid_plural "%u bytes/s"
+msgstr[0] "%u байт/s"
+msgstr[1] "%u байти/s"
+msgstr[2] "%u байтів/s"
+
+#, c-format
+msgid "ignoring suspicious submodule name: %s"
+msgstr "ігнорування підозрілої назви підмодуля: %s"
+
+msgid "negative values not allowed for submodule.fetchJobs"
+msgstr "негативні значення не дозволені для submodule.fetchJobs"
+
+#, c-format
+msgid "ignoring '%s' which may be interpreted as a command-line option: %s"
+msgstr ""
+"ігнорування \"%s\", який може бути інтерпретований як параметр командного "
+"рядка: %s"
+
+#, c-format
+msgid "Could not update .gitmodules entry %s"
+msgstr "Не вдалося оновити запис %s у .gitmodules"
+
+msgid "Cannot change unmerged .gitmodules, resolve merge conflicts first"
+msgstr ""
+"Неможливо змінити не злиті .gitmodules, спочатку розвʼяжіть конфлікти злиття"
+
+#, c-format
+msgid "Could not find section in .gitmodules where path=%s"
+msgstr "Не вдалося знайти розділ у .gitmodules де path=%s"
+
+#, c-format
+msgid "Could not remove .gitmodules entry for %s"
+msgstr "Не вдалося видалити запис .gitmodules для %s"
+
+msgid "staging updated .gitmodules failed"
+msgstr "індексація оновленого .gitmodules завершилася невдало"
+
+#, c-format
+msgid "in unpopulated submodule '%s'"
+msgstr "у незаповненому підмодулі \"%s\""
+
+#, c-format
+msgid "Pathspec '%s' is in submodule '%.*s'"
+msgstr "Визначник шляху \"%s\" знаходиться у підмодулі \"%.*s\""
+
+#, c-format
+msgid "bad --ignore-submodules argument: %s"
+msgstr "невірний --ignore-submodules аргумент: %s"
+
+#, c-format
+msgid ""
+"Submodule in commit %s at path: '%s' collides with a submodule named the "
+"same. Skipping it."
+msgstr ""
+"Підмодуль у коміті %s за шляхом \"%s\" зіткнувся з підмодулем з такою самою "
+"назвою. Пропускаємо його."
+
+#, c-format
+msgid "submodule entry '%s' (%s) is a %s, not a commit"
+msgstr "запис підмодуля \"%s\" (%s) є %s, а не комітом"
+
+#, c-format
+msgid ""
+"Could not run 'git rev-list <commits> --not --remotes -n 1' command in "
+"submodule %s"
+msgstr ""
+"Не вдалося виконати команду \"git rev-list <коміти> --not --remotes -n 1\" у "
+"підмодулі %s"
+
+#, c-format
+msgid "process for submodule '%s' failed"
+msgstr "процес для підмодуля \"%s\" завершився невдало"
+
+#, c-format
+msgid "Pushing submodule '%s'\n"
+msgstr "Надсилання підмодуля \"%s\"\n"
+
+#, c-format
+msgid "Unable to push submodule '%s'\n"
+msgstr "Не вдалося надіслати підмодуль \"%s\"\n"
+
+#, c-format
+msgid "Fetching submodule %s%s\n"
+msgstr "Отримання підмодуля %s%s\n"
+
+#, c-format
+msgid "Could not access submodule '%s'\n"
+msgstr "Не вдалося отримати доступ до підмодуля \"%s\"\n"
+
+#, c-format
+msgid "Could not access submodule '%s' at commit %s\n"
+msgstr "Не вдалося отримати доступ до підмодуля \"%s\" для коміту %s\n"
+
+#, c-format
+msgid "Fetching submodule %s%s at commit %s\n"
+msgstr "Отримання підмодуля %s%s для коміту %s\n"
+
+#, c-format
+msgid ""
+"Errors during submodule fetch:\n"
+"%s"
+msgstr ""
+"Помилки під час отримання підмодуля:\n"
+"%s"
+
+#, c-format
+msgid "'%s' not recognized as a git repository"
+msgstr "\"%s\" не розпізнано як git сховище"
+
+#, c-format
+msgid "Could not run 'git status --porcelain=2' in submodule %s"
+msgstr ""
+"Не вдалося виконати команду \"git status --porcelain=2\" у підмодулі %s"
+
+#, c-format
+msgid "'git status --porcelain=2' failed in submodule %s"
+msgstr "\"git status --porcelain=2\" завершився невдало у підмодулі %s"
+
+#, c-format
+msgid "could not start 'git status' in submodule '%s'"
+msgstr "не вдалося запустити \"git status\" у підмодулі \"%s\""
+
+#, c-format
+msgid "could not run 'git status' in submodule '%s'"
+msgstr "не вдалося запустити \"git status\" у підмодулі \"%s\""
+
+#, c-format
+msgid "Could not unset core.worktree setting in submodule '%s'"
+msgstr "Не вдалося скинути налаштування core.worktree у підмодулі \"%s\""
+
+#, c-format
+msgid "could not recurse into submodule '%s'"
+msgstr "не вдалося рекурсивно перейти в підмодуль \"%s\""
+
+msgid "could not reset submodule index"
+msgstr "не вдалося скинути індекс підмодуля"
+
+#, c-format
+msgid "submodule '%s' has dirty index"
+msgstr "підмодуль \"%s\" має брудний індекс"
+
+#, c-format
+msgid "Submodule '%s' could not be updated."
+msgstr "Не вдалося оновити підмодуль \"%s\"."
+
+#, c-format
+msgid "submodule git dir '%s' is inside git dir '%.*s'"
+msgstr "підмодуль git dir \"%s\" знаходиться всередині git директорії \"%.*s\""
+
+#, c-format
+msgid ""
+"relocate_gitdir for submodule '%s' with more than one worktree not supported"
+msgstr ""
+"relocate_gitdir для підмодуля \"%s\" з більш ніж одним робочим деревом не "
+"підтримується"
+
+#, c-format
+msgid "could not lookup name for submodule '%s'"
+msgstr "не вдалося знайти назву для підмодуля \"%s\""
+
+#, c-format
+msgid "refusing to move '%s' into an existing git dir"
+msgstr "відмовлено в переміщенні \"%s\" до існуючої git директорії"
+
+#, c-format
+msgid ""
+"Migrating git directory of '%s%s' from\n"
+"'%s' to\n"
+"'%s'\n"
+msgstr ""
+"Міграція git-директорії \"%s%s\" з\n"
+"\"%s\" до\n"
+"\"%s\"\n"
+
+msgid "could not start ls-files in .."
+msgstr "не вдалося запустити ls-файли в .."
+
+#, c-format
+msgid "ls-tree returned unexpected return code %d"
+msgstr "ls-tree повернув неочікуваний код повернення %d"
+
+#, c-format
+msgid "failed to lstat '%s'"
+msgstr "не вдалося виконати lstat для \"%s\""
+
+msgid "no remote configured to get bundle URIs from"
+msgstr ""
+"немає налаштованого віддаленого призначення для отримання URI пакунків з "
+"нього"
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "віддалений \"%s\" не має налаштованої URL-адреси"
+
+msgid "could not get the bundle-uri list"
+msgstr "не вдалося отримати список bundle-uri"
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <опції> (control|prime|update)"
+
+msgid "clear the cache tree before each iteration"
+msgstr "очищати дерево кешу перед кожною ітерацією"
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr ""
+"кількість записів у дереві кешу, які потрібно анулювати (за замовчуванням 0)"
+
+msgid "unhandled options"
+msgstr "необроблені опції"
+
+msgid "error preparing revisions"
+msgstr "помилка при підготовці ревізій"
+
+#, c-format
+msgid "commit %s is not marked reachable"
+msgstr "коміт %s не позначений як досяжний"
+
+msgid "too many commits marked reachable"
+msgstr "забагато комітів позначено як досяжні"
+
+msgid "test-tool serve-v2 [<options>]"
+msgstr "test-tool serve-v2 [<опції>]"
+
+msgid "exit immediately after advertising capabilities"
+msgstr "вихід відразу після показу здібностей"
+
+msgid "test-helper simple-ipc is-active    [<name>] [<options>]"
+msgstr "test-helper simple-ipc is-active    [<назва>] [<опції>]"
+
+msgid "test-helper simple-ipc run-daemon   [<name>] [<threads>]"
+msgstr "test-helper simple-ipc run-daemon   [<назва>] [<потоки>]"
+
+msgid "test-helper simple-ipc start-daemon [<name>] [<threads>] [<max-wait>]"
+msgstr ""
+"test-helper simple-ipc start-daemon [<назва>] [<потоки>] [<максимальний час "
+"очікування>]"
+
+msgid "test-helper simple-ipc stop-daemon  [<name>] [<max-wait>]"
+msgstr ""
+"test-helper simple-ipc stop-daemon  [<назва>] [<максимальний час очікування>]"
+
+msgid "test-helper simple-ipc send         [<name>] [<token>]"
+msgstr "test-helper simple-ipc send         [<назва>] [<токен>]"
+
+msgid "test-helper simple-ipc sendbytes    [<name>] [<bytecount>] [<byte>]"
+msgstr ""
+"test-helper simple-ipc sendbytes    [<назва>] [<кількість байт>] [<байт>]"
+
+msgid ""
+"test-helper simple-ipc multiple     [<name>] [<threads>] [<bytecount>] "
+"[<batchsize>]"
+msgstr ""
+"test-helper simple-ipc multiple     [<назва>] [<потоки>] [<кількість байт>] "
+"[<розмір пакетів>]"
+
+msgid "name or pathname of unix domain socket"
+msgstr "назва або назва шляху сокета домену unix"
+
+msgid "named-pipe name"
+msgstr "назва іменованого канала"
+
+msgid "number of threads in server thread pool"
+msgstr "кількість потоків у пулі потоків сервера"
+
+msgid "seconds to wait for daemon to start or stop"
+msgstr "секунд чекати на запуск або зупинку демона"
+
+msgid "number of bytes"
+msgstr "кількість байтів"
+
+msgid "number of requests per thread"
+msgstr "кількість запитів на потік"
+
+msgid "byte"
+msgstr "байт"
+
+msgid "ballast character"
+msgstr "баластний символ"
+
+msgid "token"
+msgstr "токен"
+
+msgid "command token to send to the server"
+msgstr "токен команди для відправки на сервер"
+
+#, c-format
+msgid "running trailer command '%s' failed"
+msgstr "не вдалося виконати команду трейлера \"%s\""
+
+#, c-format
+msgid "unknown value '%s' for key '%s'"
+msgstr "невідоме значення \"%s\" для ключа \"%s\""
+
+#, c-format
+msgid "empty trailer token in trailer '%.*s'"
+msgstr "порожній токен трейлера в трейлері \"%.*s\""
+
+#, c-format
+msgid "could not read input file '%s'"
+msgstr "не вдалося прочитати вхідний файл \"%s\""
+
+#, c-format
+msgid "could not stat %s"
+msgstr "не вдалося прочитати %s"
+
+#, c-format
+msgid "file %s is not a regular file"
+msgstr "файл %s не є звичайним файлом"
+
+#, c-format
+msgid "file %s is not writable by user"
+msgstr "файл %s не доступний для запису користувачем"
+
+msgid "could not open temporary file"
+msgstr "не вдалося відкрити тимчасовий файл"
+
+#, c-format
+msgid "could not rename temporary file to %s"
+msgstr "не вдалося перейменувати тимчасовий файл на %s"
+
+msgid "full write to remote helper failed"
+msgstr "не вдалося виконати повний запис до віддаленого помічника"
+
+#, c-format
+msgid "unable to find remote helper for '%s'"
+msgstr "не вдалося знайти віддаленого помічника для \"%s\""
+
+msgid "can't dup helper output fd"
+msgstr "неможливо зробити копію дескриптора файла виводу помічника"
+
+#, c-format
+msgid ""
+"unknown mandatory capability %s; this remote helper probably needs newer "
+"version of Git"
+msgstr ""
+"невідома обовʼязкова здібність %s; цей віддалений помічник напевно потребує "
+"новішої версії Git"
+
+msgid "this remote helper should implement refspec capability"
+msgstr "цей віддалений помічник має реалізовувати здібність refspec"
+
+#, c-format
+msgid "%s unexpectedly said: '%s'"
+msgstr "%s несподівано сказав: \"%s\""
+
+#, c-format
+msgid "%s also locked %s"
+msgstr "%s також заблокувало %s"
+
+msgid "couldn't run fast-import"
+msgstr "не вдалося запустити fast-import"
+
+msgid "error while running fast-import"
+msgstr "помилка під час виконання швидкого імпорту"
+
+#, c-format
+msgid "could not read ref %s"
+msgstr "не вдалося прочитати посилання %s"
+
+#, c-format
+msgid "unknown response to connect: %s"
+msgstr "невідома відповідь на зʼєднання: %s"
+
+msgid "setting remote service path not supported by protocol"
+msgstr "встановлення шляху до віддаленого сервісу не підтримується протоколом"
+
+msgid "invalid remote service path"
+msgstr "неприпустимий шлях до віддаленої служби"
+
+msgid "operation not supported by protocol"
+msgstr "операція не підтримується протоколом"
+
+#, c-format
+msgid "can't connect to subservice %s"
+msgstr "неможливо підключитися до підсервісу %s"
+
+msgid "--negotiate-only requires protocol v2"
+msgstr "--negotiate-only потребує протоколу v2"
+
+msgid "'option' without a matching 'ok/error' directive"
+msgstr "\"option\" без відповідної директиви \"ok/error\""
+
+#, c-format
+msgid "expected ok/error, helper said '%s'"
+msgstr "очікувалось ok/error, помічник сказав \"%s\""
+
+#, c-format
+msgid "helper reported unexpected status of %s"
+msgstr "helper повідомив про неочікуваний статус %s"
+
+#, c-format
+msgid "helper %s does not support dry-run"
+msgstr "помічник %s не підтримує dry-run"
+
+#, c-format
+msgid "helper %s does not support --signed"
+msgstr "помічник %s не підтримує --signed"
+
+#, c-format
+msgid "helper %s does not support --signed=if-asked"
+msgstr "помічник %s не підтримує --signed=if-asked"
+
+#, c-format
+msgid "helper %s does not support --atomic"
+msgstr "помічник %s не підтримує --atomic"
+
+#, c-format
+msgid "helper %s does not support --%s"
+msgstr "помічник %s не підтримує --%s"
+
+#, c-format
+msgid "helper %s does not support 'push-option'"
+msgstr "помічник %s не підтримує \"push-option\""
+
+msgid "remote-helper doesn't support push; refspec needed"
+msgstr "remote-helper не підтримує push; потрібен refspec"
+
+#, c-format
+msgid "helper %s does not support 'force'"
+msgstr "помічник %s не підтримує \"force\""
+
+msgid "couldn't run fast-export"
+msgstr "не вдалося запустити fast-export"
+
+msgid "error while running fast-export"
+msgstr "помилка під час виконання швидкого експорту"
+
+#, c-format
+msgid ""
+"No refs in common and none specified; doing nothing.\n"
+"Perhaps you should specify a branch.\n"
+msgstr ""
+"Немає спільних посилань і жодного не вказано; нічого не робиться.\n"
+"Можливо, вам слід вказати гілку.\n"
+
+#, c-format
+msgid "unsupported object format '%s'"
+msgstr "непідтримуваний обʼєкт формата \"%s\""
+
+#, c-format
+msgid "malformed response in ref list: %s"
+msgstr "невірно сформована відповідь у списку посилань: %s"
+
+#, c-format
+msgid "read(%s) failed"
+msgstr "read(%s) завершився невдало"
+
+#, c-format
+msgid "write(%s) failed"
+msgstr "write(%s) завершився невдало"
+
+#, c-format
+msgid "%s thread failed"
+msgstr "%s потік завершився невдало"
+
+#, c-format
+msgid "%s thread failed to join: %s"
+msgstr "%s потік не зміг приєднатись: %s"
+
+#, c-format
+msgid "can't start thread for copying data: %s"
+msgstr "неможливо запустити потік для копіювання даних: %s"
+
+#, c-format
+msgid "%s process failed to wait"
+msgstr "%s процес не зміг дочекатися"
+
+#, c-format
+msgid "%s process failed"
+msgstr "%s процес завершився невдало"
+
+msgid "can't start thread for copying data"
+msgstr "неможливо запустити потік для копіювання даних"
+
+#, c-format
+msgid "Would set upstream of '%s' to '%s' of '%s'\n"
+msgstr "Встановить першоджерельне сховище для \"%s\" на \"%s\" в \"%s\"\n"
+
+#, c-format
+msgid "could not read bundle '%s'"
+msgstr "не вдалося прочитати пакунок \"%s\""
+
+#, c-format
+msgid "transport: invalid depth option '%s'"
+msgstr "transport: неприпустимий параметр глибини \"%s\""
+
+msgid "see protocol.version in 'git help config' for more details"
+msgstr ""
+"дивіться protocol.version у \"git help config\" для більш детальної "
+"інформації"
+
+msgid "server options require protocol version 2 or later"
+msgstr "опції сервера вимагають протокол версії 2 або пізнішої"
+
+msgid "server does not support wait-for-done"
+msgstr "сервер не підтримує wait-for-done"
+
+msgid "could not parse transport.color.* config"
+msgstr "не вдалося розібрати конфігурацію transport.color.*"
+
+msgid "support for protocol v2 not implemented yet"
+msgstr "підтримка протоколу v2 ще не запроваджена"
+
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "невідоме значення для конфігурації \"%s\": %s"
+
+#, c-format
+msgid "transport '%s' not allowed"
+msgstr "засіб передачі \"%s\" не дозволений"
+
+msgid "git-over-rsync is no longer supported"
+msgstr "git-over-rsync більше не підтримується"
+
+#, c-format
+msgid ""
+"The following submodule paths contain changes that can\n"
+"not be found on any remote:\n"
+msgstr ""
+"Наступні шляхи до підмодулів містять зміни, які неможливо\n"
+"знайти в жодному віддаленому призначенні:\n"
+
+#, c-format
+msgid ""
+"\n"
+"Please try\n"
+"\n"
+"\tgit push --recurse-submodules=on-demand\n"
+"\n"
+"or cd to the path and use\n"
+"\n"
+"\tgit push\n"
+"\n"
+"to push them to a remote.\n"
+"\n"
+msgstr ""
+"\n"
+"Будь ласка, спробуйте\n"
+"\n"
+"\tgit push --recurse-submodules=on-demand\n"
+"\n"
+"або перейдіть до директорії та використайте\n"
+"\n"
+"\tgit push\n"
+"\n"
+"щоб надіслати їх до віддаленого призначення.\n"
+
+msgid "Aborting."
+msgstr "Відміна."
+
+msgid "failed to push all needed submodules"
+msgstr "не вдалося надіслати всі необхідні підмодулі"
+
+msgid "bundle-uri operation not supported by protocol"
+msgstr "bundle-uri операція не підтримується протоколом"
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "не вдалося отримати список адрес пакетів, оголошений сервером"
+
+msgid "too-short tree object"
+msgstr "занадто короткий обʼєкт дерева"
+
+msgid "malformed mode in tree entry"
+msgstr "невірно визначений режим у записі дерева"
+
+msgid "empty filename in tree entry"
+msgstr "порожня назва файлу у записі дерева"
+
+msgid "too-short tree file"
+msgstr "занадто короткий файл дерева"
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by checkout:\n"
+"%%sPlease commit your changes or stash them before you switch branches."
+msgstr ""
+"Ваші локальні зміни в наступних файлах будуть перезаписані при переключенні "
+"стану:\n"
+"%%sБудь ласка, зробіть коміт або додайте їх до схову перед переходом до "
+"іншої гілки."
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by checkout:\n"
+"%%s"
+msgstr ""
+"Ваші локальні зміни в наступних файлах будуть перезаписані при переключенні "
+"стану:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by merge:\n"
+"%%sPlease commit your changes or stash them before you merge."
+msgstr ""
+"Ваші локальні зміни у наступних файлах будуть перезаписані під час злиття:\n"
+"%%sБудь ласка, зробіть коміт або додайте їх до схову перед злиттям."
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by merge:\n"
+"%%s"
+msgstr ""
+"Ваші локальні зміни у наступних файлах будуть перезаписані під час злиття:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by %s:\n"
+"%%sPlease commit your changes or stash them before you %s."
+msgstr ""
+"Ваші локальні зміни у наступних файлах будуть перезаписані %s:\n"
+"%%sБудь ласка, зробіть коміт або додайте їх до схову перед %s."
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by %s:\n"
+"%%s"
+msgstr ""
+"Ваші локальні зміни у наступних файлах будуть перезаписані %s:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"Updating the following directories would lose untracked files in them:\n"
+"%s"
+msgstr ""
+"Оновлення наступних директорій призведе до втрати невідстежуваних файлів у "
+"цих директоріях:\n"
+"%s"
+
+#, c-format
+msgid ""
+"Refusing to remove the current working directory:\n"
+"%s"
+msgstr ""
+"Відмовлено у видаленні поточної робочої директорії:\n"
+"%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by checkout:\n"
+"%%sPlease move or remove them before you switch branches."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені під час "
+"переключення:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед переходом до іншої гілки."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by checkout:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені під час "
+"переключення:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by merge:\n"
+"%%sPlease move or remove them before you merge."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені при виконанні "
+"злиття:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед злиттям."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by merge:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені при виконанні "
+"злиття:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by %s:\n"
+"%%sPlease move or remove them before you %s."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені при виконанні "
+"%s:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед %s."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by %s:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені при виконанні "
+"%s:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by "
+"checkout:\n"
+"%%sPlease move or remove them before you switch branches."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані при "
+"переключенні стану\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед переходом до іншої гілки."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by "
+"checkout:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані при "
+"переключенні стану:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by merge:\n"
+"%%sPlease move or remove them before you merge."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані при "
+"злитті:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед злиттям."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by merge:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані при "
+"виконанні злиття:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by %s:\n"
+"%%sPlease move or remove them before you %s."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані %s:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед %s."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by %s:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані %s:\n"
+"%%s"
+
+#, c-format
+msgid "Entry '%s' overlaps with '%s'.  Cannot bind."
+msgstr "Запис \"%s\" перетинається з \"%s\".  Неможливо звʼязати."
+
+#, c-format
+msgid ""
+"Cannot update submodule:\n"
+"%s"
+msgstr ""
+"Не вдалося оновити підмодуль:\n"
+"%s"
+
+#, c-format
+msgid ""
+"The following paths are not up to date and were left despite sparse "
+"patterns:\n"
+"%s"
+msgstr ""
+"Наступні шляхи не є актуальними і були залишені, незважаючи на розріджені "
+"шаблони:\n"
+"%s"
+
+#, c-format
+msgid ""
+"The following paths are unmerged and were left despite sparse patterns:\n"
+"%s"
+msgstr ""
+"Наступні шляхи не злиті і були залишені, незважаючи на розріджені шаблони:\n"
+"%s"
+
+#, c-format
+msgid ""
+"The following paths were already present and thus not updated despite sparse "
+"patterns:\n"
+"%s"
+msgstr ""
+"Наступні шляхи вже існували і тому не були оновлені, незважаючи на "
+"розріджені шаблони:\n"
+"%s"
+
+#, c-format
+msgid "Aborting\n"
+msgstr "Переривання\n"
+
+#, c-format
+msgid ""
+"After fixing the above paths, you may want to run `git sparse-checkout "
+"reapply`.\n"
+msgstr ""
+"Після виправлення наведених вище шляхів можливо ви забажаєте виконати \"git "
+"sparse-checkout reapply\".\n"
+
+msgid "Updating files"
+msgstr "Оновлення файлів"
+
+msgid ""
+"the following paths have collided (e.g. case-sensitive paths\n"
+"on a case-insensitive filesystem) and only one from the same\n"
+"colliding group is in the working tree:\n"
+msgstr ""
+"зіткнулися наступні шляхи (наприклад, шляхи, чутливі до регістру\n"
+"у файловій системі, не чутливій до регістру) і лише один шлях з тієї \n"
+"групи, що зіткнулися, знаходиться у робочому дереві:\n"
+
+msgid "Updating index flags"
+msgstr "Оновлення прапорців індексу"
+
+#, c-format
+msgid "worktree and untracked commit have duplicate entries: %s"
+msgstr "робоче дерево та невідстежуваний коміт мають дубльовані записи: %s"
+
+msgid "expected flush after fetch arguments"
+msgstr "очікувалось flush після аргументів отримання"
+
+msgid "invalid URL scheme name or missing '://' suffix"
+msgstr "неприпустима назва схеми URL-адреси або відсутній \"://\" суфікс"
+
+#, c-format
+msgid "invalid %XX escape sequence"
+msgstr "неприпустима %XX екрануюча послідовність"
+
+msgid "missing host and scheme is not 'file:'"
+msgstr "відсутній хост і схема не \"file:\""
+
+msgid "a 'file:' URL may not have a port number"
+msgstr "\"file:\" URL-адреса не може мати номера порту"
+
+msgid "invalid characters in host name"
+msgstr "неприпустимі символи в назві хоста"
+
+msgid "invalid port number"
+msgstr "неприпустимий номер порту"
+
+msgid "invalid '..' path segment"
+msgstr "неприпустимий \"..\" сегмент шляху"
+
+msgid "usage: "
+msgstr "використання: "
+
+msgid "fatal: "
+msgstr "збій: "
+
+msgid "error: "
+msgstr "помилка: "
+
+msgid "warning: "
+msgstr "попередження: "
+
+msgid "Fetching objects"
+msgstr "Отримання обʼєктів"
+
+#, c-format
+msgid "'%s' at main working tree is not the repository directory"
+msgstr "\"%s\" у головному робочому дереві не є директорією сховища"
+
+#, c-format
+msgid "'%s' file does not contain absolute path to the working tree location"
+msgstr ""
+"\"%s\" файл не містить абсолютного шляху до розташування робочого дерева"
+
+#, c-format
+msgid "'%s' is not a .git file, error code %d"
+msgstr "\"%s\" не є .git файлом, код помилки %d"
+
+#, c-format
+msgid "'%s' does not point back to '%s'"
+msgstr "\"%s\" не вказує назад на \"%s\""
+
+msgid "not a directory"
+msgstr "не є директорією"
+
+msgid ".git is not a file"
+msgstr ".git не є файлом"
+
+msgid ".git file broken"
+msgstr ".git файл пошкоджено"
+
+msgid ".git file incorrect"
+msgstr ".git файл не є коректним"
+
+msgid "not a valid path"
+msgstr "неприпустимий шлях"
+
+msgid "unable to locate repository; .git is not a file"
+msgstr "не вдалося знайти сховище; .git не є файлом"
+
+msgid "unable to locate repository; .git file does not reference a repository"
+msgstr "не вдалося знайти сховище; .git-файл не посилається на сховище"
+
+msgid "unable to locate repository; .git file broken"
+msgstr "не вдалося знайти сховище; файл .git пошкоджено"
+
+msgid "gitdir unreadable"
+msgstr "нечитабельна git директорія"
+
+msgid "gitdir incorrect"
+msgstr "невірна git директорія"
+
+msgid "not a valid directory"
+msgstr "неприпустима директорія"
+
+msgid "gitdir file does not exist"
+msgstr "файл git директорії не існує"
+
+#, c-format
+msgid "unable to read gitdir file (%s)"
+msgstr "не вдалося прочитати файл git директорії (%s)"
+
+#, c-format
+msgid "short read (expected %<PRIuMAX> bytes, read %<PRIuMAX>)"
+msgstr ""
+"помилка при зчитуванні (очікувалось %<PRIuMAX> байтів, прочитано %<PRIuMAX>)"
+
+msgid "invalid gitdir file"
+msgstr "неприпустимий файл git директорії"
+
+msgid "gitdir file points to non-existent location"
+msgstr "файл git директорії вказує на неіснуюче розташування"
+
+#, c-format
+msgid "unable to set %s in '%s'"
+msgstr "не вдалося встановити %s в \"%s\""
+
+#, c-format
+msgid "unable to unset %s in '%s'"
+msgstr "не вдалося скинути %s в \"%s\""
+
+msgid "failed to set extensions.worktreeConfig setting"
+msgstr "не вдалося встановити extensions.worktreeConfig параметр"
+
+#, c-format
+msgid "could not setenv '%s'"
+msgstr "не вдалося встановити змінну оточення \"%s\""
+
+#, c-format
+msgid "unable to create '%s'"
+msgstr "не вдалося створити \"%s\""
+
+#, c-format
+msgid "could not open '%s' for reading and writing"
+msgstr "не вдалося відкрити \"%s\" для читання та запису"
+
+#, c-format
+msgid "unable to access '%s'"
+msgstr "не вдалося отримати доступ до \"%s\""
+
+msgid "unable to get current working directory"
+msgstr "не вдалося завантажити поточну робочу директорію"
+
+msgid "unable to get random bytes"
+msgstr "не вдалося отримати випадкові байти"
+
+msgid "Unmerged paths:"
+msgstr "не злиті шляхи:"
+
+msgid "  (use \"git restore --staged <file>...\" to unstage)"
+msgstr ""
+"  (використовуйте \"git restore --staged <файл>...\", щоб видалити з індексу)"
+
+#, c-format
+msgid "  (use \"git restore --source=%s --staged <file>...\" to unstage)"
+msgstr ""
+"  (використовуйте \"git restore --source=%s --staged <file>...\", щоб "
+"прибрати з індексу)"
+
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+"  (використовуйте \"git rm --cached <file>...\", щоб видалити з індексу)"
+
+msgid "  (use \"git add <file>...\" to mark resolution)"
+msgstr "  (використовуйте \"git add <файл>...\", щоб позначити як розвʼязане)"
+
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+"  (використовуйте \"git add/rm <файл>...\" за потребою, щоб позначити "
+"вирішення)"
+
+msgid "  (use \"git rm <file>...\" to mark resolution)"
+msgstr "  (використовуйте \"git rm <файл>...\", щоб позначити вирішення)"
+
+msgid "Changes to be committed:"
+msgstr "Зміни, додані до майбутнього коміту:"
+
+msgid "Changes not staged for commit:"
+msgstr "Зміни, не додані до майбутнього коміту:"
+
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr "  (скористайтесь \"git add <файл>...\", щоб оновити майбутній коміт)"
+
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+"  (скористайтесь \"git add/rm <файл>...\", щоб оновити майбутній коміт)"
+
+msgid ""
+"  (use \"git restore <file>...\" to discard changes in working directory)"
+msgstr ""
+"  (скористайтесь \"git restore <файл>...\", щоб скасувати зміни в робочій "
+"директорії)"
+
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+"  (зробіть коміт або відкиньте невідстежуваний або змінений вміст у "
+"підмодулях)"
+
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+"  (скористайтесь \"git %s <файл>...\", щоб додати до майбутнього коміту)"
+
+msgid "both deleted:"
+msgstr "видалено обома:"
+
+msgid "added by us:"
+msgstr "додано нами:"
+
+msgid "deleted by them:"
+msgstr "видалено ними:"
+
+msgid "added by them:"
+msgstr "додано ними:"
+
+msgid "deleted by us:"
+msgstr "видалено нами:"
+
+msgid "both added:"
+msgstr "додано обома:"
+
+msgid "both modified:"
+msgstr "змінено обома:"
+
+msgid "new file:"
+msgstr "новий файл:"
+
+msgid "copied:"
+msgstr "скопійовано:"
+
+msgid "deleted:"
+msgstr "видалено:"
+
+msgid "modified:"
+msgstr "змінено:"
+
+msgid "renamed:"
+msgstr "перейменовано:"
+
+msgid "typechange:"
+msgstr "змінено тип:"
+
+msgid "unknown:"
+msgstr "невідомо:"
+
+msgid "unmerged:"
+msgstr "не злито:"
+
+msgid "new commits, "
+msgstr "нові коміти, "
+
+msgid "modified content, "
+msgstr "змінений контент, "
+
+msgid "untracked content, "
+msgstr "невідстежуваний контент, "
+
+#, c-format
+msgid "Your stash currently has %d entry"
+msgid_plural "Your stash currently has %d entries"
+msgstr[0] "У вашій схованці наразі є %d запис"
+msgstr[1] "У вашій схованці наразі є %d записи"
+msgstr[2] "У вашій схованці наразі є %d записів"
+
+msgid "Submodules changed but not updated:"
+msgstr "Підмодулі змінено, але не оновлено:"
+
+msgid "Submodule changes to be committed:"
+msgstr "Зміни в підмодулі, які будуть закомічені:"
+
+msgid ""
+"Do not modify or remove the line above.\n"
+"Everything below it will be ignored."
+msgstr ""
+"Не змінюйте та не видаляйте рядок вище.\n"
+"Все, що нижче, буде проігноровано."
+
+#, c-format
+msgid ""
+"\n"
+"It took %.2f seconds to compute the branch ahead/behind values.\n"
+"You can use '--no-ahead-behind' to avoid this.\n"
+msgstr ""
+"\n"
+"Обчислення значень попереду/позаду для гілки зайняло %.2f секунд.\n"
+"Ви можете використати параметр '--no-ahead-behind', щоб уникнути цього.\n"
+
+msgid "You have unmerged paths."
+msgstr "У вас є незлиті шляхи."
+
+msgid "  (fix conflicts and run \"git commit\")"
+msgstr "  (виправте конфлікти та виконайте \"git commit\")"
+
+msgid "  (use \"git merge --abort\" to abort the merge)"
+msgstr ""
+"  (скористайтесь командою \"git merge --abort\", щоб перервати злиття)."
+
+msgid "All conflicts fixed but you are still merging."
+msgstr "Усі конфлікти виправлено, але ви все ще продовжуєте злиття."
+
+msgid "  (use \"git commit\" to conclude merge)"
+msgstr "  (скористайтесь \"git commit\", щоб завершити злиття)"
+
+msgid "You are in the middle of an am session."
+msgstr "Ви всередині am сеансу."
+
+msgid "The current patch is empty."
+msgstr "Поточна латка порожня."
+
+msgid "  (fix conflicts and then run \"git am --continue\")"
+msgstr "  (виправте конфлікти, а потім виконайте \"git am --continue\")"
+
+msgid "  (use \"git am --skip\" to skip this patch)"
+msgstr "  (скористайтесь командою \"git am --skip\", щоб пропустити цю латку)"
+
+msgid ""
+"  (use \"git am --allow-empty\" to record this patch as an empty commit)"
+msgstr ""
+"  (скористайтесь \"git am --allow-empty\", щоб записати цю латку як порожній "
+"коміт)"
+
+msgid "  (use \"git am --abort\" to restore the original branch)"
+msgstr "  (скористайтесь \"git am --abort\", щоб відновити початкову гілку)"
+
+msgid "git-rebase-todo is missing."
+msgstr "git-rebase-todo відсутній."
+
+msgid "No commands done."
+msgstr "Не виконано жодної команди."
+
+#, c-format
+msgid "Last command done (%<PRIuMAX> command done):"
+msgid_plural "Last commands done (%<PRIuMAX> commands done):"
+msgstr[0] "Останню команду виконано (%<PRIuMAX> команду виконано):"
+msgstr[1] "Останні команди виконано (%<PRIuMAX> команди виконано):"
+msgstr[2] "Останніх команд виконано (%<PRIuMAX> команд виконано):"
+
+#, c-format
+msgid "  (see more in file %s)"
+msgstr "  (дивіться більше у файлі %s)"
+
+msgid "No commands remaining."
+msgstr "Не залишилось команд."
+
+#, c-format
+msgid "Next command to do (%<PRIuMAX> remaining command):"
+msgid_plural "Next commands to do (%<PRIuMAX> remaining commands):"
+msgstr[0] "Наступна команда для виконання (залишилась %<PRIuMAX> команда):"
+msgstr[1] "Наступні команди для виконання (залишилось %<PRIuMAX> команди):"
+msgstr[2] "Наступні команди для виконання (залишилось %<PRIuMAX> команд):"
+
+msgid "  (use \"git rebase --edit-todo\" to view and edit)"
+msgstr ""
+"  (скористайтесь \"git rebase --edit-todo\", щоб переглянути та "
+"відредагувати)"
+
+#, c-format
+msgid "You are currently rebasing branch '%s' on '%s'."
+msgstr "Наразі ви перебазовуєте гілку \"%s\" на \"%s\"."
+
+msgid "You are currently rebasing."
+msgstr "Наразі йде перебазування."
+
+msgid "  (fix conflicts and then run \"git rebase --continue\")"
+msgstr "  (виправте конфлікти, а потім виконайте \"git rebase --continue\")"
+
+msgid "  (use \"git rebase --skip\" to skip this patch)"
+msgstr "  (скористайтесь \"git rebase --skip\", щоб пропустити цю латку)"
+
+msgid "  (use \"git rebase --abort\" to check out the original branch)"
+msgstr ""
+"  (скористайтесь \"git rebase --abort\", щоб перейти до початкової гілки)"
+
+msgid "  (all conflicts fixed: run \"git rebase --continue\")"
+msgstr "  (усі конфлікти виправлено: виконайте \"git rebase --continue\")"
+
+#, c-format
+msgid ""
+"You are currently splitting a commit while rebasing branch '%s' on '%s'."
+msgstr ""
+"Наразі виконується розщеплення коміту під час перебазування гілки \"%s\" на "
+"\"%s\"."
+
+msgid "You are currently splitting a commit during a rebase."
+msgstr "Ви розщеплюєте коміт під час перебазування."
+
+msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
+msgstr ""
+"  (Після очищення робочої директорії виконайте \"git rebase --continue\")"
+
+#, c-format
+msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
+msgstr "Наразі ви редагуєте коміт при перебазуванні гілки \"%s\" на \"%s\"."
+
+msgid "You are currently editing a commit during a rebase."
+msgstr "Наразі ви редагуєте коміт при перебазуванні."
+
+msgid "  (use \"git commit --amend\" to amend the current commit)"
+msgstr ""
+"  (скористайтесь \"git commit --amend\", щоб внести зміни до поточного "
+"коміту)"
+
+msgid ""
+"  (use \"git rebase --continue\" once you are satisfied with your changes)"
+msgstr ""
+"  (скористайтесь командою \"git rebase --continue\", коли ви будете "
+"задоволені своїми змінами)"
+
+msgid "Cherry-pick currently in progress."
+msgstr "Наразі триває висмикування."
+
+#, c-format
+msgid "You are currently cherry-picking commit %s."
+msgstr "Наразі ви висмикуєте коміт %s."
+
+msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
+msgstr "  (виправте конфлікти і виконайте \"git cherry-pick --continue\")"
+
+msgid "  (run \"git cherry-pick --continue\" to continue)"
+msgstr "  (виконайте \"git cherry-pick --continue\", щоб продовжити)"
+
+msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
+msgstr "  (усі конфлікти виправлено: виконайте \"git cherry-pick --continue\")"
+
+msgid "  (use \"git cherry-pick --skip\" to skip this patch)"
+msgstr ""
+"  (скористайтесь командою \"git cherry-pick --skip\", щоб пропустити цю "
+"латку)"
+
+msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
+msgstr ""
+" (скористайтесь командою \"git cherry-pick --abort\", щоб скасувати операцію "
+"висмикування)"
+
+msgid "Revert currently in progress."
+msgstr "Наразі триває вивертання."
+
+#, c-format
+msgid "You are currently reverting commit %s."
+msgstr "Наразі ви вивертаєте коміт %s."
+
+msgid "  (fix conflicts and run \"git revert --continue\")"
+msgstr "  (виправте конфлікти і виконайте \"git revert --continue\")"
+
+msgid "  (run \"git revert --continue\" to continue)"
+msgstr "  (виконайте \"git revert --continue\", щоб продовжити)"
+
+msgid "  (all conflicts fixed: run \"git revert --continue\")"
+msgstr "  (усі конфлікти виправлено: виконайте \"git revert --continue\")"
+
+msgid "  (use \"git revert --skip\" to skip this patch)"
+msgstr "  (скористайтесь \"git revert --skip\", щоб пропустити цю латку)"
+
+msgid "  (use \"git revert --abort\" to cancel the revert operation)"
+msgstr ""
+"  (скористайтесь \"git revert --abort\", щоб скасувати операцію повернення)"
+
+#, c-format
+msgid "You are currently bisecting, started from branch '%s'."
+msgstr "Наразі ви робите бісекцію, починаючи з гілки '%s'."
+
+msgid "You are currently bisecting."
+msgstr "Наразі ви робите бісекцію."
+
+msgid "  (use \"git bisect reset\" to get back to the original branch)"
+msgstr ""
+"  (скористайтесь \"git bisect reset\", щоб повернутись на початкову гілку)"
+
+msgid "You are in a sparse checkout."
+msgstr "Ви перебуваєте в розрідженому переході на гілку."
+
+#, c-format
+msgid "You are in a sparse checkout with %d%% of tracked files present."
+msgstr ""
+"Ви перебуваєте в розрідженому переході на гілку з %d%% наявних відстежуваних "
+"файлів."
+
+msgid "On branch "
+msgstr "На гілці "
+
+msgid "interactive rebase in progress; onto "
+msgstr "триває інтерактивне перебазування на "
+
+msgid "rebase in progress; onto "
+msgstr "триває перебазування на "
+
+msgid "HEAD detached at "
+msgstr "HEAD відʼєднано на "
+
+msgid "HEAD detached from "
+msgstr "HEAD відʼєднано від "
+
+msgid "Not currently on any branch."
+msgstr "Не на гілці."
+
+msgid "Initial commit"
+msgstr "Початковий коміт"
+
+msgid "No commits yet"
+msgstr "Поки що немає комітів"
+
+msgid "Untracked files"
+msgstr "Невідстежувані файли"
+
+msgid "Ignored files"
+msgstr "Ігноровані файли"
+
+#, c-format
+msgid ""
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
+msgstr ""
+"На перерахування невідстежуваних файлів пішло %.2f секунд,\n"
+"але результат було додано у кеш, тому наступні запуски можуть бути швидшими."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "На перерахування невідстежуваних файлів пішло %.2f секунд."
+
+msgid "See 'git help status' for information on how to improve this."
+msgstr "Дивіться \"git help status\" для інформації про те, як це покращити."
+
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr "Невідстежувані файли не показані%s"
+
+msgid " (use -u option to show untracked files)"
+msgstr " (використовуйте опцію -u, щоб показати невідстежувані файли)"
+
+msgid "No changes"
+msgstr "Немає змін"
+
+#, c-format
+msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
+msgstr ""
+"не додано жодних змін до коміту (скористайтесь \"git add\" та/або \"git "
+"commit -a\")\n"
+
+#, c-format
+msgid "no changes added to commit\n"
+msgstr "нічого не додано до коміту\n"
+
+#, c-format
+msgid ""
+"nothing added to commit but untracked files present (use \"git add\" to "
+"track)\n"
+msgstr ""
+"нічого не додано до коміту, але є невідстежувані файли (скористайтесь \"git "
+"add\" для відстежування)\n"
+
+#, c-format
+msgid "nothing added to commit but untracked files present\n"
+msgstr "нічого не додано до коміту, але присутні невідстежувані файли\n"
+
+#, c-format
+msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
+msgstr ""
+"нічого комітити (створіть/скопіюйте файли та скористайтесь \"git add\" для "
+"відстежування)\n"
+
+#, c-format
+msgid "nothing to commit\n"
+msgstr "нічого комітити\n"
+
+#, c-format
+msgid "nothing to commit (use -u to show untracked files)\n"
+msgstr "нічого комітити (скористайтесь -u щоб показати невідстежувані файли)\n"
+
+#, c-format
+msgid "nothing to commit, working tree clean\n"
+msgstr "нічого комітити, робоче дерево чисте\n"
+
+msgid "No commits yet on "
+msgstr "Поки що немає комітів у "
+
+msgid "HEAD (no branch)"
+msgstr "HEAD (немає гілки)"
+
+msgid "different"
+msgstr "відрізняється"
+
+msgid "behind "
+msgstr "позаду "
+
+msgid "ahead "
+msgstr "попереду "
+
+#. TRANSLATORS: the action is e.g. "pull with rebase"
+#, c-format
+msgid "cannot %s: You have unstaged changes."
+msgstr "неможливо %s: У вас є неіндексовані зміни."
+
+msgid "additionally, your index contains uncommitted changes."
+msgstr "крім того, ваш індекс містить незакомічені зміни."
+
+#, c-format
+msgid "cannot %s: Your index contains uncommitted changes."
+msgstr "неможливо виконати %s: Ваш індекс містить незакомічені зміни."
+
+msgid ""
+"Error: Your local changes to the following files would be overwritten by "
+"merge"
+msgstr ""
+"Помилка: Ваші локальні зміни в наступних файлах були б перезаписані під час "
+"злиття"
+
+msgid "Automated merge did not work."
+msgstr "Автоматичне злиття не спрацювало."
+
+msgid "Should not be doing an octopus."
+msgstr "Не слід робити octopus злиття."
+
+#, sh-format
+msgid "Unable to find common commit with $pretty_name"
+msgstr "Не вдалося знайти спільний коміт з $pretty_name"
+
+#, sh-format
+msgid "Already up to date with $pretty_name"
+msgstr "Вже в актуальному стані з $pretty_name"
+
+#, sh-format
+msgid "Fast-forwarding to: $pretty_name"
+msgstr "Перемотування вперед до: $pretty_name"
+
+#, sh-format
+msgid "Trying simple merge with $pretty_name"
+msgstr "Спроба простого злиття з $pretty_name"
+
+msgid "Simple merge did not work, trying automatic merge."
+msgstr "Просте злиття не спрацювало, спроба автоматичного злиття."
+
+#, sh-format
+msgid "usage: $dashless $USAGE"
+msgstr "використання: $dashless $USAGE"
+
+#, sh-format
+msgid "Cannot chdir to $cdup, the toplevel of the working tree"
+msgstr "Неможливо виконати chdir до $cdup, верхнього рівня робочого дерева"
+
+#, sh-format
+msgid "fatal: $program_name cannot be used without a working tree."
+msgstr "збій: $program_name неможливо використовувати без робочого дерева."
+
+msgid "Cannot rewrite branches: You have unstaged changes."
+msgstr "Неможливо переписати гілку: у вас є неіндексовані зміни."
+
+#, sh-format
+msgid "Cannot $action: You have unstaged changes."
+msgstr "Неможливо $action: у вас є неіндексовані зміни."
+
+#, sh-format
+msgid "Cannot $action: Your index contains uncommitted changes."
+msgstr "Неможливо $action: ваш індекс містить незакомічені зміни."
+
+msgid "Additionally, your index contains uncommitted changes."
+msgstr "Крім того, ваш індекс містить незакомічені зміни."
+
+msgid "You need to run this command from the toplevel of the working tree."
+msgstr "Цю команду потрібно запускати з верхнього рівня робочого дерева."
+
+msgid "Unable to determine absolute path of git directory"
+msgstr "Не вдалося визначити абсолютний шлях git директорії"
+
+msgid "local zone differs from GMT by a non-minute interval\n"
+msgstr "місцева зона відрізняється від GMT на нехвилинний інтервал\n"
+
+msgid "local time offset greater than or equal to 24 hours\n"
+msgstr "зміщення місцевого часу більше або дорівнює 24 годинам\n"
+
+#, perl-format
+msgid "fatal: command '%s' died with exit code %d"
+msgstr "збій: команда \"%s\" завершилася невдало з кодом виходу %d"
+
+msgid "the editor exited uncleanly, aborting everything"
+msgstr "редактор вийшов неналежним чином, перервано всі процеси"
+
+#, perl-format
+msgid ""
+"'%s' contains an intermediate version of the email you were composing.\n"
+msgstr "\"%s\" містить проміжну версію листа, який ви створювали.\n"
+
+#, perl-format
+msgid "'%s.final' contains the composed email.\n"
+msgstr "\"%s.final\" містить створений лист.\n"
+
+msgid "--dump-aliases incompatible with other options\n"
+msgstr "--dump-aliases несумісна з іншими опціями\n"
+
+msgid ""
+"fatal: found configuration options for 'sendmail'\n"
+"git-send-email is configured with the sendemail.* options - note the 'e'.\n"
+"Set sendemail.forbidSendmailVariables to false to disable this check.\n"
+msgstr ""
+"збій: знайдено опції конфігурації для \"sendmail\"\n"
+"git-send-email налаштовано з параметрами sendemail.* options - зверніть "
+"увагу на \"e\".\n"
+"Встановіть sendemail.forbidSendmailVariables у false, щоб вимкнути цю "
+"перевірку.\n"
+
+msgid "Cannot run git format-patch from outside a repository\n"
+msgstr "Неможливо запустити git format-patch поза сховищем\n"
+
+msgid ""
+"`batch-size` and `relogin` must be specified together (via command-line or "
+"configuration option)\n"
+msgstr ""
+"\"batch-size\" і \"relogin\" повинні бути вказані разом (через командний "
+"рядок або опції конфігурації)\n"
+
+#, perl-format
+msgid "Unknown --suppress-cc field: '%s'\n"
+msgstr "Невідоме --suppress-cc поле: \"%s\"\n"
+
+#, perl-format
+msgid "Unknown --confirm setting: '%s'\n"
+msgstr "Невідомий --confirm параметр: \"%s\"\n"
+
+#, perl-format
+msgid "warning: sendmail alias with quotes is not supported: %s\n"
+msgstr "попередження: sendmail псевдонім у лапках не підтримується: %s\n"
+
+#, perl-format
+msgid "warning: `:include:` not supported: %s\n"
+msgstr "попередження: \":include:\" не підтримується: %s\n"
+
+#, perl-format
+msgid "warning: `/file` or `|pipe` redirection not supported: %s\n"
+msgstr ""
+"попередження: \"/file\" або \"|pipe\" перенаправлення не підтримуються: %s\n"
+
+#, perl-format
+msgid "warning: sendmail line is not recognized: %s\n"
+msgstr "попередження: рядок sendmail не розпізнано: %s\n"
+
+#, perl-format
+msgid ""
+"File '%s' exists but it could also be the range of commits\n"
+"to produce patches for.  Please disambiguate by...\n"
+"\n"
+"    * Saying \"./%s\" if you mean a file; or\n"
+"    * Giving --format-patch option if you mean a range.\n"
+msgstr ""
+"Файл \"%s\" існує, але це також може бути діапазон комітів\n"
+"для яких створюються латки.  Будь ласка, розтлумачте...\n"
+"\n"
+"    * Вказавши \"./%s\", якщо на увазі мається файл\n"
+"    * Додавши --format-patch, якщо на увазі мається діапазон.\n"
+
+#, perl-format
+msgid "Failed to opendir %s: %s"
+msgstr "Не вдалося виконати opendir %s: %s"
+
+msgid ""
+"\n"
+"No patch files specified!\n"
+"\n"
+msgstr ""
+"\n"
+"Файли латок не вказано!\n"
+"\n"
+
+#, perl-format
+msgid "No subject line in %s?"
+msgstr "Немає рядка теми в %s?"
+
+#, perl-format
+msgid "Failed to open for writing %s: %s"
+msgstr "Не вдалося відкрити для запису %s: %s"
+
+msgid ""
+"Lines beginning in \"GIT:\" will be removed.\n"
+"Consider including an overall diffstat or table of contents\n"
+"for the patch you are writing.\n"
+"\n"
+"Clear the body content if you don't wish to send a summary.\n"
+msgstr ""
+"Рядки, що починаються з \"GIT:\", будуть вилучені.\n"
+"Розгляньте можливість включення загального diffstat або змісту\n"
+"для латки, яку ви пишете.\n"
+"\n"
+"Очистіть вміст тіла, якщо ви не бажаєте надсилати підсумок.\n"
+
+#, perl-format
+msgid "Failed to open %s.final: %s"
+msgstr "Не вдалося відкрити %s.final: %s"
+
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "Не вдалося відкрити %s: %s"
+
+msgid "Summary email is empty, skipping it\n"
+msgstr "Підсумковий лист порожній, пропущено\n"
+
+#. TRANSLATORS: please keep [y/N] as is.
+#, perl-format
+msgid "Are you sure you want to use <%s> [y/N]? "
+msgstr "Ви впевнені, що хочете використати <%s> [y/N]? "
+
+msgid ""
+"The following files are 8bit, but do not declare a Content-Transfer-"
+"Encoding.\n"
+msgstr ""
+"Наступні файли є 8-бітними, але не містять Content-Transfer-Encoding.\n"
+
+msgid "Which 8bit encoding should I declare [UTF-8]? "
+msgstr "Яке 8-бітне кодування слід оголосити [UTF-8]? "
+
+#, perl-format
+msgid ""
+"Refusing to send because the patch\n"
+"\t%s\n"
+"has the template subject '*** SUBJECT HERE ***'. Pass --force if you really "
+"want to send.\n"
+msgstr ""
+"Відмовлено в надсиланні, тому що латка\n"
+"\t%s\n"
+"має шаблонну тему \"*** SUBJECT HERE ***\". Додайте --force, якщо ви дійсно "
+"хочете відправити.\n"
+
+msgid "To whom should the emails be sent (if anyone)?"
+msgstr "Кому слід надсилати електронні листи (якщо комусь)?"
+
+#, perl-format
+msgid "fatal: alias '%s' expands to itself\n"
+msgstr "збій: псевдонім \"%s\" розгортається на самого себе\n"
+
+msgid "Message-ID to be used as In-Reply-To for the first email (if any)? "
+msgstr ""
+"Message-ID, який буде використовуватися як In-Reply-To для першого листа "
+"(якщо такий є)? "
+
+#, perl-format
+msgid "error: unable to extract a valid address from: %s\n"
+msgstr "помилка: не вдалося витягти дійсну адресу з: %s\n"
+
+#. TRANSLATORS: Make sure to include [q] [d] [e] in your
+#. translation. The program will only accept English input
+#. at this point.
+msgid "What to do with this address? ([q]uit|[d]rop|[e]dit): "
+msgstr "Що робити з цією адресою? ([q]uit|[d]rop|[e]dit): "
+
+#, perl-format
+msgid "CA path \"%s\" does not exist"
+msgstr "Шлях до ЦВС \"%s\" не існує"
+
+msgid ""
+"    The Cc list above has been expanded by additional\n"
+"    addresses found in the patch commit message. By default\n"
+"    send-email prompts before sending whenever this occurs.\n"
+"    This behavior is controlled by the sendemail.confirm\n"
+"    configuration setting.\n"
+"\n"
+"    For additional information, run 'git send-email --help'.\n"
+"    To retain the current behavior, but squelch this message,\n"
+"    run 'git config --global sendemail.confirm auto'.\n"
+"\n"
+msgstr ""
+"    Наведений вище список копій було розширено додатковими\n"
+"    адресами, знайденими у дописі до коміту латки. Зазвичай\n"
+"    send-email запитує перед надсиланням, коли це трапляється.\n"
+"    Цю поведінку можна контролювати за допомогою параметра sendemail."
+"confirm\n"
+"    налаштування конфігурації.\n"
+"\n"
+"    Для отримання додаткової інформації виконайте команду \"git send-email --"
+"help\".\n"
+"Для утримання поточної поведінки, але без цього повідомлення,\n"
+"    виконайте \"git config --global sendemail.confirm auto\".\n"
+
+#. TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your
+#. translation. The program will only accept English input
+#. at this point.
+msgid "Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "
+msgstr "Надіслати цей лист? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "
+
+msgid "Send this email reply required"
+msgstr "Відповідь на запитання \"Надіслати цей лист?\" є обовʼязковою"
+
+msgid "The required SMTP server is not properly defined."
+msgstr "Потрібний SMTP-сервер не визначено належним чином."
+
+#, perl-format
+msgid "Server does not support STARTTLS! %s"
+msgstr "Сервер не підтримує STARTTLS! %s"
+
+#, perl-format
+msgid "STARTTLS failed! %s"
+msgstr "STARTTLS завершився невдало! %s"
+
+msgid "Unable to initialize SMTP properly. Check config and use --smtp-debug."
+msgstr ""
+"Не вдалося правильно ініціалізувати SMTP. Перевірте конфігурацію і "
+"скористайтесь --smtp-debug."
+
+#, perl-format
+msgid "Failed to send %s\n"
+msgstr "Не вдалося надіслати %s\n"
+
+#, perl-format
+msgid "Dry-Sent %s\n"
+msgstr "Пробно відправлено %s\n"
+
+#, perl-format
+msgid "Sent %s\n"
+msgstr "Відправлено %s\n"
+
+msgid "Dry-OK. Log says:\n"
+msgstr "Пробно OK. Журнал каже:\n"
+
+msgid "OK. Log says:\n"
+msgstr "ОК. Журнал каже:\n"
+
+msgid "Result: "
+msgstr "Результат: "
+
+msgid "Result: OK\n"
+msgstr "Результат: OK\n"
+
+#, perl-format
+msgid "can't open file %s"
+msgstr "неможливо відкрити файл %s"
+
+#, perl-format
+msgid "(mbox) Adding cc: %s from line '%s'\n"
+msgstr "(mbox) Додавання cc: %s з рядка \"%s\"\n"
+
+#, perl-format
+msgid "(mbox) Adding to: %s from line '%s'\n"
+msgstr "(mbox) Додавання до: %s з рядка \"%s\"\n"
+
+#, perl-format
+msgid "(non-mbox) Adding cc: %s from line '%s'\n"
+msgstr "(non-mbox) Додавання cc: %s з рядка \"%s\"\n"
+
+#, perl-format
+msgid "(body) Adding cc: %s from line '%s'\n"
+msgstr "(тіло) Додавання cc: %s з рядка \"%s\"\n"
+
+#, perl-format
+msgid "(%s) Could not execute '%s'"
+msgstr "(%s) Не вдалося виконати \"%s\""
+
+#, perl-format
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Невірно сформований вивід з \"%s\""
+
+#, perl-format
+msgid "(%s) failed to close pipe to '%s'"
+msgstr "(%s) не вдалося закрити канал до \"%s\""
+
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Додавання %s: %s з: \"%s\"\n"
+
+msgid "cannot send message as 7bit"
+msgstr "неможливо надіслати повідомлення як 7bit"
+
+msgid "invalid transfer encoding"
+msgstr "неприпустиме кодування передачі"
+
+#, perl-format
+msgid ""
+"fatal: %s: rejected by %s hook\n"
+"%s\n"
+"warning: no patches were sent\n"
+msgstr ""
+"збій: %s: відхилено %s хуком\n"
+"%s\n"
+"попередження: не було надіслано жодних латок\n"
+
+#, perl-format
+msgid "unable to open %s: %s\n"
+msgstr "не вдалося відкрити %s: %s\n"
+
+#, perl-format
+msgid ""
+"fatal: %s:%d is longer than 998 characters\n"
+"warning: no patches were sent\n"
+msgstr ""
+"збій: %s:%d довше ніж 998 символів\n"
+"попередження: не було надіслано жодних латок\n"
+
+#, perl-format
+msgid "Skipping %s with backup suffix '%s'.\n"
+msgstr "Пропуск %s з суфіксом резервної копії \"%s\".\n"
+
+#. TRANSLATORS: please keep "[y|N]" as is.
+#, perl-format
+msgid "Do you really want to send %s? [y|N]: "
+msgstr "Ви дійсно хочете відправити %s? [y|N]: "
index b0832be6952b61c33f15d176985cf13ccb4fc195..86402725b28399d5abc0e083bf17ba9b4d78e9de 100644 (file)
@@ -14,6 +14,7 @@
 #   - 依云 <lilydjwg AT gmail.com>
 #   - Yichao Yu <yyc1992 AT gmail.com>
 #   - Zhuang Ya <zhuangya AT me.com>
+#   - Teng Long <dyroneteng AT gmail.com>
 #
 #  Git glossary for Chinese translators
 #
 #   3-way merge                      |  三方合并
 #   abbreviate                       |  简写(的 SHA-1 值)
 #   alternate object database        |  备用对象库
+#   attribute source                 |  属性来源
 #   amend                            |  修补
 #   ancestor                         |  祖先,祖先提交
 #   annotated tag                    |  附注标签
 #   bare repository                  |  纯仓库
 #   bisect                           |  二分查找
+#   bitmap                           |  位图
 #   blob object                      |  数据对象
 #   bloom filter                     |  布隆过滤器
 #   branch                           |  分支
@@ -74,6 +77,7 @@
 #   index entry                      |  索引条目
 #   loose object                     |  松散对象
 #   loose refs                       |  松散引用
+#   magic                            |  神奇前缀(路径规格支持的一种前缀表达式)
 #   master                           |  master(默认分支名)
 #   merge                            |  合并
 #   object                           |  对象
@@ -83,6 +87,7 @@
 #   object type                      |  对象类型
 #   octopus                          |  章鱼式合并(两分支以上的合并)
 #   origin                           |  origin(默认的远程名称)
+#   orphan                           |  孤立(一般指孤立分支,即没有任何提交的分支)
 #   pack                             |  包
 #   pack index                       |  包索引
 #   packfile                         |  包文件
 #   rebase                           |  变基
 #   ref                              |  引用
 #   reflog                           |  引用日志
+#   refmap                           |  引用映射
 #   refspec                          |  引用规格
 #   remote                           |  远程,远程仓库
 #   remote-tracking branch           |  远程跟踪分支
 #   smart HTTP protocol              |  智能 HTTP 协议
 #   squash                           |  挤压
 #   stage                            |  n. 暂存区(即索引); v. 暂存
+#   stale                            |  过期的
 #   stash                            |  n. 贮藏区; v. 贮藏
 #   submodule                        |  子模组
 #   symref                           |  符号引用
 #   upstream                         |  上游
 #   upstream branch                  |  上游分支
 #   working tree                     |  工作区
-# Fangyi Zhou <me@fangyi.io>, 2021-2022.
+# Fangyi Zhou <me@fangyi.io>, 2021-2023.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-09-28 15:50+0100\n"
-"PO-Revision-Date: 2022-09-23 14:53+0100\n"
-"Last-Translator: Fangyi Zhou <me@fangyi.io>\n"
-"Language-Team: GitHub <https://github.com/fangyi-zhou/git-po/>\n"
+"POT-Creation-Date: 2023-11-10 10:49+0800\n"
+"PO-Revision-Date: 2023-11-10 17:13+0800\n"
+"Last-Translator: Teng Long <dyroneteng@gmail.com>\n"
+"Language-Team: GitHub <https://github.com/dyrone/git/>\n"
 "Language: zh_CN\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -164,19 +171,19 @@ msgstr "嗯(%s)?"
 msgid "could not read index"
 msgstr "不能读取索引"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "binary"
 msgstr "二进制"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "nothing"
 msgstr "无"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "unchanged"
 msgstr "没有修改"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Update"
 msgstr "更新"
 
@@ -189,15 +196,15 @@ msgstr "不能暂存 '%s'"
 msgid "could not write index"
 msgstr "不能写入索引"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "更新了 %d 个路径\n"
 msgstr[1] "更新了 %d 个路径\n"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "说明:%s 现已成为未跟踪的。\n"
 
@@ -206,7 +213,7 @@ msgstr "说明:%s 现已成为未跟踪的。\n"
 msgid "make_cache_entry failed for path '%s'"
 msgstr "对路径 '%s' 的 make_cache_entry 操作失败"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Revert"
 msgstr "还原"
 
@@ -214,24 +221,24 @@ msgstr "还原"
 msgid "Could not parse HEAD^{tree}"
 msgstr "不能解析 HEAD^{tree}"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "还原了 %d 个路径\n"
 msgstr[1] "还原了 %d 个路径\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 #, c-format
 msgid "No untracked files.\n"
 msgstr "没有未跟踪的文件。\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Add untracked"
 msgstr "添加未跟踪的"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "增加了 %d 个路径\n"
@@ -242,21 +249,21 @@ msgstr[1] "增加了 %d 个路径\n"
 msgid "ignoring unmerged: %s"
 msgstr "忽略未合入的:%s"
 
-#: add-interactive.c add-patch.c git-add--interactive.perl
+#: add-interactive.c add-patch.c
 #, c-format
 msgid "Only binary files changed.\n"
 msgstr "只有二进制文件被修改。\n"
 
-#: add-interactive.c add-patch.c git-add--interactive.perl
+#: add-interactive.c add-patch.c
 #, c-format
 msgid "No changes.\n"
 msgstr "没有修改。\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Patch update"
 msgstr "补丁更新"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Review diff"
 msgstr "检视 diff"
 
@@ -324,25 +331,25 @@ msgstr "选择一个编号条目"
 msgid "(empty) select nothing"
 msgstr "(空)不选择任何内容"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 msgid "*** Commands ***"
 msgstr "*** 命令 ***"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 msgid "What now"
 msgstr "请选择"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "staged"
 msgstr "缓存"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "unstaged"
 msgstr "未缓存"
 
 #: add-interactive.c apply.c builtin/am.c builtin/bugreport.c builtin/clone.c
-#: builtin/diagnose.c builtin/fetch.c builtin/merge.c builtin/pull.c
-#: builtin/submodule--helper.c git-add--interactive.perl
+#: builtin/diagnose.c builtin/fetch.c builtin/hook.c builtin/merge.c
+#: builtin/pull.c builtin/submodule--helper.c
 msgid "path"
 msgstr "路径"
 
@@ -350,28 +357,28 @@ msgstr "路径"
 msgid "could not refresh index"
 msgstr "不能刷新索引"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 #, c-format
 msgid "Bye.\n"
 msgstr "再见。\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "暂存模式变更 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "暂存删除动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "暂存添加动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "暂存该块 [y,n,q,a,d%s,?]? "
 
@@ -395,23 +402,23 @@ msgstr ""
 "a - 暂存该块和本文件中后面的全部块\n"
 "d - 不暂存该块和本文件中后面的全部块\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "贮藏模式变更 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "贮藏删除动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "贮藏添加动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "贮藏该块 [y,n,q,a,d%s,?]? "
 
@@ -435,23 +442,23 @@ msgstr ""
 "a - 贮藏该块和本文件中后面的全部块\n"
 "d - 不贮藏该块和本文件中后面的全部块\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "取消暂存模式变更 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "取消暂存删除动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "取消暂存添加动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "取消暂存该块 [y,n,q,a,d%s,?]? "
 
@@ -475,23 +482,23 @@ msgstr ""
 "a - 取消暂存该块和本文件中后面的全部块\n"
 "d - 不要取消暂存该块和本文件中后面的全部块\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "将模式变更应用到索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "将删除操作应用到索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "将添加操作应用到索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "将该块应用到索引 [y,n,q,a,d%s,?]? "
 
@@ -515,23 +522,23 @@ msgstr ""
 "a - 应用该块和本文件中后面的全部块\n"
 "d - 不要应用该块和本文件中后面的全部块\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "从工作区中丢弃模式变更 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "从工作区中丢弃删除动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "从工作区中丢弃添加动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "从工作区中丢弃该块 [y,n,q,a,d%s,?]? "
 
@@ -555,23 +562,23 @@ msgstr ""
 "a - 丢弃该块和本文件中后面的全部块\n"
 "d - 不要丢弃该块和本文件中后面的全部块\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "从索引和工作区中丢弃模式变更 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "从索引和工作区中丢弃删除动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "从索引和工作区中丢弃添加动作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "从索引和工作区中丢弃该块 [y,n,q,a,d%s,?]? "
 
@@ -589,23 +596,23 @@ msgstr ""
 "a - 丢弃该块和本文件中后面的全部块\n"
 "d - 不要丢弃该块和本文件中后面的全部块\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "将模式变更应用到索引和工作区 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "将删除操作应用到索引和工作区 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "将添加操作应用到索引和工作区 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "将该块应用到索引和工作区 [y,n,q,a,d%s,?]? "
 
@@ -623,23 +630,23 @@ msgstr ""
 "a - 应用该块和本文件中后面的全部块\n"
 "d - 不要应用该块和本文件中后面的全部块\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr "将模式变更应用到工作区 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "将删除操作应用到工作区 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "将添加操作应用到工作区 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "将该块应用到工作区 [y,n,q,a,d%s,?]? "
 
@@ -707,7 +714,7 @@ msgstr ""
 "\t不是结尾于:\n"
 "%.*s"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Manual hunk edit mode -- see bottom for a quick guide.\n"
 msgstr "手动块编辑模式 -- 查看底部的快速指南。\n"
 
@@ -724,9 +731,7 @@ msgstr ""
 "要删除 '%c' 开始的行,删除它们。\n"
 "以 %c 开始的行将被删除。\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -743,21 +748,13 @@ msgstr "无法解析数据块头信息"
 msgid "'git apply --cached' failed"
 msgstr "'git apply --cached' 失败"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr "您的编辑块不能被应用。重新编辑(选择 \"no\" 丢弃!) [y/n]? "
@@ -766,11 +763,11 @@ msgstr "您的编辑块不能被应用。重新编辑(选择 \"no\" 丢弃!
 msgid "The selected hunks do not apply to the index!"
 msgstr "选中的块不能应用到索引!"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Apply them to the worktree anyway? "
 msgstr "无论如何都要应用到工作区么?"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Nothing was applied.\n"
 msgstr "未应用。\n"
 
@@ -808,11 +805,11 @@ msgstr "没有下一个块"
 msgid "No other hunks to goto"
 msgstr "没有其它可供跳转的块"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "go to which hunk (<ret> to see more)? "
 msgstr "跳转到哪个块(<回车> 查看更多)? "
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "go to which hunk? "
 msgstr "跳转到哪个块?"
 
@@ -832,7 +829,7 @@ msgstr[1] "对不起,只有 %d 个可用块。"
 msgid "No other hunks to search"
 msgstr "没有其它可供查找的块"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "search for regex? "
 msgstr "使用正则表达式搜索?"
 
@@ -897,9 +894,8 @@ msgid "Reverting is not possible because you have unmerged files."
 msgstr "无法回退,因为您有未合并的文件。"
 
 #: advice.c
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "无法 %s,因为您有未合并的文件。"
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "无法变基,因为您有未合并的文件。"
 
 #: advice.c
 msgid ""
@@ -925,6 +921,24 @@ msgstr "请在合并前先提交您的修改。"
 msgid "Exiting because of unfinished merge."
 msgstr "因为存在未完成的合并而退出。"
 
+#: advice.c
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"无法在偏离的分支上进行快进操作,您需要:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"或者:\n"
+"\n"
+"\tgit rebase\n"
+
 #: advice.c
 msgid "Not possible to fast-forward, aborting."
 msgstr "无法快进,终止。"
@@ -1016,6 +1030,11 @@ msgstr "命令行以 \\ 结尾"
 msgid "unclosed quote"
 msgstr "未关闭的引号"
 
+#: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
+#: builtin/receive-pack.c builtin/tag.c
+msgid "too many arguments"
+msgstr "太多参数"
+
 #: apply.c
 #, c-format
 msgid "unrecognized whitespace option '%s'"
@@ -1026,14 +1045,15 @@ msgstr "未能识别的空白字符选项 '%s'"
 msgid "unrecognized whitespace ignore option '%s'"
 msgstr "未能识别的空白字符忽略选项 '%s'"
 
-#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout.c
-#: builtin/clone.c builtin/commit.c builtin/describe.c builtin/diff-tree.c
-#: builtin/difftool.c builtin/fast-export.c builtin/fetch.c builtin/help.c
-#: builtin/index-pack.c builtin/init-db.c builtin/log.c builtin/ls-files.c
-#: builtin/merge-base.c builtin/merge.c builtin/pack-objects.c builtin/push.c
-#: builtin/rebase.c builtin/repack.c builtin/reset.c builtin/rev-list.c
-#: builtin/show-branch.c builtin/stash.c builtin/submodule--helper.c
-#: builtin/tag.c builtin/worktree.c parse-options.c range-diff.c revision.c
+#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
+#: builtin/checkout.c builtin/clone.c builtin/commit.c builtin/describe.c
+#: builtin/diff-tree.c builtin/difftool.c builtin/fast-export.c builtin/fetch.c
+#: builtin/help.c builtin/index-pack.c builtin/init-db.c builtin/log.c
+#: builtin/ls-files.c builtin/merge-base.c builtin/merge.c
+#: builtin/pack-objects.c builtin/push.c builtin/rebase.c builtin/repack.c
+#: builtin/reset.c builtin/rev-list.c builtin/show-branch.c builtin/stash.c
+#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c
+#: range-diff.c revision.c
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
 msgstr "选项 '%s' 和 '%s' 不能同时使用"
@@ -1043,6 +1063,14 @@ msgstr "选项 '%s' 和 '%s' 不能同时使用"
 msgid "'%s' outside a repository"
 msgstr "'%s' 在仓库之外"
 
+#: apply.c
+msgid "failed to read patch"
+msgstr "无法读取补丁"
+
+#: apply.c
+msgid "patch too large"
+msgstr "补丁过大"
+
 #: apply.c
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
@@ -1407,7 +1435,7 @@ msgstr "不能为新建文件 %s 创建后端存储"
 msgid "unable to add cache entry for %s"
 msgstr "无法为 %s 添加缓存条目"
 
-#: apply.c builtin/bisect--helper.c builtin/gc.c
+#: apply.c builtin/bisect.c builtin/gc.c
 #, c-format
 msgid "failed to write to '%s'"
 msgstr "无法写入 '%s'"
@@ -1448,6 +1476,11 @@ msgstr "截短 .rej 文件名为 %.*s.rej"
 msgid "cannot open %s"
 msgstr "不能打开 %s"
 
+#: apply.c rerere.c
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "不能删除 '%s'"
+
 #: apply.c
 #, c-format
 msgid "Hunk #%d applied cleanly."
@@ -1467,7 +1500,7 @@ msgstr "略过补丁 '%s'。"
 msgid "No valid patches in input (allow with \"--allow-empty\")"
 msgstr "输入中没有合法的补丁 (使用 \"--allow-empty\" 来允许)"
 
-#: apply.c
+#: apply.c t/helper/test-cache-tree.c
 msgid "unable to read index file"
 msgstr "无法读取索引文件"
 
@@ -1497,7 +1530,7 @@ msgid_plural "%d lines applied after fixing whitespace errors."
 msgstr[0] "修复空白错误后,应用了 %d 行。"
 msgstr[1] "修复空白错误后,应用了 %d 行。"
 
-#: apply.c builtin/add.c builtin/mv.c builtin/rm.c
+#: apply.c builtin/mv.c builtin/rm.c
 msgid "Unable to write new index file"
 msgstr "无法写入新索引文件"
 
@@ -1685,6 +1718,11 @@ msgstr "git archive --remote <仓库> [--exec <命令>] --list"
 msgid "cannot read '%s'"
 msgstr "不能读取 '%s'"
 
+#: archive.c
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "路径规格 '%s' 匹配了当前目录外的文件'"
+
 #: archive.c builtin/add.c builtin/rm.c
 #, c-format
 msgid "pathspec '%s' did not match any files"
@@ -1700,15 +1738,11 @@ msgstr "无此引用:%.*s"
 msgid "not a valid object name: %s"
 msgstr "不是一个有效的对象名:%s"
 
-#: archive.c
+#: archive.c t/helper/test-cache-tree.c
 #, c-format
 msgid "not a tree object: %s"
 msgstr "不是一个树对象:%s"
 
-#: archive.c
-msgid "current working directory is untracked"
-msgstr "当前工作目录未被跟踪"
-
 #: archive.c
 #, c-format
 msgid "File not found: %s"
@@ -1742,7 +1776,7 @@ msgstr "格式"
 msgid "archive format"
 msgstr "归档格式"
 
-#: archive.c builtin/log.c
+#: archive.c builtin/log.c parse-options.h
 msgid "prefix"
 msgstr "前缀"
 
@@ -1751,7 +1785,7 @@ msgid "prepend prefix to each pathname in the archive"
 msgstr "为归档中每个路径名加上前缀"
 
 #: archive.c builtin/blame.c builtin/commit-tree.c builtin/config.c
-#: builtin/fast-export.c builtin/grep.c builtin/hash-object.c
+#: builtin/fast-export.c builtin/gc.c builtin/grep.c builtin/hash-object.c
 #: builtin/ls-files.c builtin/notes.c builtin/read-tree.c parse-options.h
 msgid "file"
 msgstr "文件"
@@ -1776,6 +1810,15 @@ msgstr "读取工作区中的 .gitattributes"
 msgid "report archived files on stderr"
 msgstr "在标准错误上报告归档文件"
 
+#: archive.c builtin/clone.c builtin/fetch.c builtin/pack-objects.c
+#: builtin/pull.c
+msgid "time"
+msgstr "时间"
+
+#: archive.c
+msgid "set modification time of archive entries"
+msgstr "设置归档条目的修改时间"
+
 #: archive.c
 msgid "set compression level"
 msgstr "设置压缩级别"
@@ -1831,6 +1874,15 @@ msgstr "参数不支持此格式 '%s':-%d"
 msgid "%.*s is not a valid attribute name"
 msgstr "%.*s 不是一个有效的属性名"
 
+#: attr.c
+msgid "unable to add additional attribute"
+msgstr "不能添加额外属性"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "忽略过长的属性行 %d"
+
 #: attr.c
 #, c-format
 msgid "%s not allowed: %s:%d"
@@ -1844,6 +1896,25 @@ msgstr ""
 "负值模版在 git attributes 中被忽略\n"
 "当字符串确实要以感叹号开始时,使用 '\\!'。"
 
+#: attr.c
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "无法 fstat gitattributes 文件 '%s'"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "忽略过大的 gitattributes 文件 '%s'"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "忽略过大的 gitattributes 数据对象 '%s'"
+
+#: attr.c
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "错误的 --attr-source 或 GIT_ATTR_SOURCE"
+
 #: bisect.c
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
@@ -1967,17 +2038,13 @@ msgstr[1] "二分查找中:在此之后,还剩 %d 个版本待测试 %s\n"
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents 和 --reverse 不能混用。"
 
-#: blame.c
-msgid "cannot use --contents with final commit object name"
-msgstr "不能将 --contents 和最终的提交对象名共用"
-
 #: blame.c
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr "--reverse 和 --first-parent 共用,需要指定最新的提交"
 
 #: blame.c builtin/commit.c builtin/log.c builtin/merge.c
-#: builtin/pack-objects.c builtin/shortlog.c bundle.c midx.c pack-bitmap.c
-#: ref-filter.c remote.c sequencer.c submodule.c
+#: builtin/pack-objects.c builtin/shortlog.c midx.c pack-bitmap.c remote.c
+#: sequencer.c submodule.c
 msgid "revision walk setup failed"
 msgstr "版本遍历初始化失败"
 
@@ -2101,8 +2168,8 @@ msgstr "一个名为 '%s' 的分支已经存在"
 
 #: branch.c
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "无法强制更新检出于 '%2$s' 的分支 '%1$s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr "无法强制更新被工作区 '%2$s' 所使用的分支 '%1$s'"
 
 #: branch.c
 #, c-format
@@ -2155,10 +2222,11 @@ msgstr "子模组 '%s':无法找到子模组"
 #: branch.c
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
-"你可以用 'git checkout %s && git submodule update --init' 来尝试更新子模组"
+"你可以用 'git checkout --no-recurse-submodules %s && git submodule update --"
+"init' 来尝试更新子模组"
 
 #: branch.c
 #, c-format
@@ -2167,13 +2235,8 @@ msgstr "子模组 '%s':不能创建分支 '%s'"
 
 #: branch.c
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' 已经检出到 '%s'"
-
-#: branch.c
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "工作区 %s 的 HEAD 指向没有被更新"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s' 已经被工作区 '%s' 使用"
 
 #: builtin/add.c
 msgid "git add [<options>] [--] <pathspec>..."
@@ -2184,48 +2247,38 @@ msgstr "git add [<选项>] [--] <路径规格>..."
 msgid "cannot chmod %cx '%s'"
 msgstr "不能 chmod %cx '%s'"
 
-#: builtin/add.c
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "意外的差异状态 %c"
-
-#: builtin/add.c builtin/commit.c
-msgid "updating files failed"
-msgstr "更新文件失败"
-
-#: builtin/add.c
-#, c-format
-msgid "remove '%s'\n"
-msgstr "删除 '%s'\n"
-
 #: builtin/add.c
 msgid "Unstaged changes after refreshing the index:"
 msgstr "刷新索引之后尚未被暂存的变更:"
 
-#: builtin/add.c builtin/rev-parse.c
-msgid "Could not read the index"
-msgstr "不能读取索引"
+#: builtin/add.c
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"设置 add.interactive.useBuiltin 已经被移除!\n"
+"查看 'git help config' 中的相关条目以获取更多信息。"
 
 #: builtin/add.c
-msgid "Could not write patch"
-msgstr "不能生成补丁"
+msgid "could not read the index"
+msgstr "不能读取索引"
 
 #: builtin/add.c
 msgid "editing patch failed"
 msgstr "编辑补丁失败"
 
-#: builtin/add.c
+#: builtin/add.c read-cache.c
 #, c-format
-msgid "Could not stat '%s'"
+msgid "could not stat '%s'"
 msgstr "不能对 '%s' 调用 stat"
 
 #: builtin/add.c
-msgid "Empty patch. Aborted."
-msgstr "空补丁。异常终止"
+msgid "empty patch. aborted"
+msgstr "空补丁。异常终止"
 
 #: builtin/add.c
 #, c-format
-msgid "Could not apply '%s'"
+msgid "could not apply '%s'"
 msgstr "不能应用 '%s'"
 
 #: builtin/add.c
@@ -2384,6 +2437,11 @@ msgstr ""
 msgid "index file corrupt"
 msgstr "索引文件损坏"
 
+#: builtin/add.c builtin/am.c builtin/checkout.c builtin/clone.c
+#: builtin/commit.c builtin/stash.c merge.c rerere.c
+msgid "unable to write new index file"
+msgstr "无法写新的索引文件"
+
 #: builtin/am.c builtin/mailinfo.c mailinfo.c
 #, c-format
 msgid "bad action '%s' for '%s'"
@@ -2434,7 +2492,7 @@ msgstr "fseek 失败"
 msgid "could not open '%s' for reading"
 msgstr "无法打开 '%s' 进行读取"
 
-#: builtin/am.c builtin/rebase.c sequencer.c strbuf.c wrapper.c
+#: builtin/am.c builtin/rebase.c editor.c sequencer.c wrapper.c
 #, c-format
 msgid "could not open '%s' for writing"
 msgstr "无法打开 '%s' 进行写入"
@@ -2633,11 +2691,6 @@ msgstr ""
 "您应该对已经冲突解决的每一个文件执行 'git add' 来标记已经完成。 \n"
 "您可以对 \"由他们删除\" 的文件执行 `git rm` 命令。"
 
-#: builtin/am.c builtin/checkout.c builtin/clone.c builtin/stash.c merge.c
-#: rerere.c
-msgid "unable to write new index file"
-msgstr "无法写新的索引文件"
-
 #: builtin/am.c builtin/reset.c
 #, c-format
 msgid "Could not parse object '%s'."
@@ -2653,16 +2706,11 @@ msgid ""
 "Not rewinding to ORIG_HEAD"
 msgstr "您好像在上一次 'am' 失败后移动了 HEAD。未回退至 ORIG_HEAD"
 
-#: builtin/am.c builtin/bisect--helper.c worktree.c
+#: builtin/am.c builtin/bisect.c worktree.c
 #, c-format
 msgid "failed to read '%s'"
 msgstr "无法读取 '%s'"
 
-#: builtin/am.c
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "选项 '%s=%s' 和 '%s=%s' 不能同时使用"
-
 #: builtin/am.c
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<选项>] [(<mbox> | <Maildir>)...]"
@@ -2675,6 +2723,10 @@ msgstr "git am [<选项>] (--continue | --skip | --abort)"
 msgid "run interactively"
 msgstr "以交互式方式运行"
 
+#: builtin/am.c
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "绕过 pre-applypatch 和 applypatch-msg 钩子"
+
 #: builtin/am.c
 msgid "historical option -- no-op"
 msgstr "老的参数 —— 无作用"
@@ -2712,10 +2764,6 @@ msgstr "向 git-mailinfo 传递 -m 参数"
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "针对 mbox 格式,向 git-mailsplit 传递 --keep-cr 参数"
 
-#: builtin/am.c
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr "不向 git-mailsplit 传递 --keep-cr 参数,覆盖 am.keepcr 的设置"
-
 #: builtin/am.c
 msgid "strip everything before a scissors line"
 msgstr "丢弃裁切线前的所有内容"
@@ -2864,110 +2912,103 @@ msgstr "git archive:协议错误"
 msgid "git archive: expected a flush"
 msgstr "git archive:应有一个 flush 包"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<提交>]"
-
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
-msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<术语> --term-{old,good}"
-"=<术语>] [--no-checkout] [--first-parent] [<坏> [<好>...]] [--] [<路径>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
+msgstr "git bisect start [--term-{new|bad}=<术语> --term-{old|good}=<术语>]    [--no-checkout] [--first-parent] [<坏> [<好>...]] [--]    [<路径规格>...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<版本>]"
+#: builtin/bisect.c
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<版本>...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<版本>...]"
+#: builtin/bisect.c
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<版本>|<范围>)...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <文件>"
+#: builtin/bisect.c
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<提交>]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<版本>|<范围>)...]"
+#: builtin/bisect.c
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <日志文件>"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <命令>..."
+#: builtin/bisect.c
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <命令> [<参数>...]"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
 msgstr "不能以 '%2$s' 模式打开文件 '%1$s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "could not write to file '%s'"
 msgstr "不能写入文件 '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' for reading"
 msgstr "不能打开文件 '%s' 来读取"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' is not a valid term"
 msgstr "'%s' 不是一个有效的术语"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "can't use the builtin command '%s' as a term"
 msgstr "不能使用内置命令 '%s' 作为术语"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "can't change the meaning of the term '%s'"
 msgstr "不能修改术语 '%s' 的含义"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "please use two different terms"
 msgstr "请使用两个不同的术语"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "We are not bisecting.\n"
 msgstr "我们没有在二分查找。\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' is not a valid commit"
 msgstr "'%s' 不是一个有效的提交"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
 msgstr "不能检出原始 HEAD '%s'。尝试 'git bisect reset <提交>'。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad bisect_write argument: %s"
 msgstr "坏的 bisect_write 参数:%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "couldn't get the oid of the rev '%s'"
 msgstr "无法获取版本 '%s' 的对象 ID"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "couldn't open the file '%s'"
 msgstr "无法打开文件 '%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Invalid command: you're currently in a %s/%s bisect"
 msgstr "无效的命令:您当前正处于一个 %s/%s 二分查找中"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "You need to give me at least one %s and %s revision.\n"
@@ -2976,7 +3017,7 @@ msgstr ""
 "您需要给我至少一个 %s 和一个 %s 版本。\n"
 "为此您可以用 \"git bisect %s\" 和 \"git bisect %s\"。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "You need to start by \"git bisect start\".\n"
@@ -2987,7 +3028,7 @@ msgstr ""
 "然后需要提供我至少一个 %s 和一个 %s 版本。\n"
 "为此您可以用 \"git bisect %s\" 和 \"git bisect %s\" 命令。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "bisecting only with a %s commit"
 msgstr "在只有一个 %s 提交的情况下二分查找"
@@ -2996,37 +3037,37 @@ msgstr "在只有一个 %s 提交的情况下二分查找"
 #. translation. The program will only accept English input
 #. at this point.
 #.
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Are you sure [Y/n]? "
 msgstr "您确认么[Y/n]? "
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "status: waiting for both good and bad commits\n"
 msgstr "状态:正在等待好的和坏的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "status: waiting for bad commit, %d good commit known\n"
 msgid_plural "status: waiting for bad commit, %d good commits known\n"
 msgstr[0] "状态:正在等待坏的提交,已知 %d 个好的提交\n"
 msgstr[1] "状态:正在等待坏的提交,已知 %d 个好的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "status: waiting for good commit(s), bad commit known\n"
 msgstr "状态:正在等待好的提交,已知坏的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "no terms defined"
 msgstr "未定义术语"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "Your current terms are %s for the old state\n"
 "and %s for the new state.\n"
 msgstr "您当前针对旧状态的术语是 %s,对新状态的术语是 %s。\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "invalid argument %s for 'git bisect terms'.\n"
@@ -3035,52 +3076,48 @@ msgstr ""
 "命令 'git bisect terms' 的参数 %s 无效。\n"
 "支持的选项有:--term-good|--term-old 和 --term-bad|--term-new。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "revision walk setup failed\n"
 msgstr "版本遍历设置失败\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "could not open '%s' for appending"
 msgstr "无法打开 '%s' 进行追加"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "'' is not a valid term"
 msgstr "'' 不是一个有效的术语"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "unrecognized option: '%s'"
 msgstr "未识别的选项:'%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' does not appear to be a valid revision"
 msgstr "'%s' 看起来不是一个有效的版本"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bad HEAD - I need a HEAD"
 msgstr "坏的 HEAD - 我需要一个 HEAD"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
 msgstr "检出 '%s' 失败。尝试 'git bisect start <有效分支>'。"
 
-#: builtin/bisect--helper.c
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "不会在做了 cg-seek 的树上做二分查找"
-
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bad HEAD - strange symbolic ref"
 msgstr "坏的 HEAD - 奇怪的符号引用"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "invalid ref: '%s'"
 msgstr "无效的引用:'%s'"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "You need to start by \"git bisect start\"\n"
 msgstr "您需要执行 \"git bisect start\" 来开始\n"
 
@@ -3088,165 +3125,130 @@ msgstr "您需要执行 \"git bisect start\" 来开始\n"
 #. translation. The program will only accept English input
 #. at this point.
 #.
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Do you want me to do it for you [Y/n]? "
 msgstr "您想让我为您这样做么[Y/n]? "
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Please call `--bisect-state` with at least one argument"
 msgstr "请使用至少一个参数调用 `--bisect-state`"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'git bisect %s' can take only one argument."
 msgstr "'git bisect %s' 只能带一个参数。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad rev input: %s"
 msgstr "坏的版本输入:%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad rev input (not a commit): %s"
 msgstr "坏的版本输入(不是提交):%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "We are not bisecting."
 msgstr "我们没有在二分查找。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s'?? what are you talking about?"
 msgstr "'%s'?? 您在说什么?"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot read file '%s' for replaying"
 msgstr "不能读取文件 '%s' 来重放"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "running %s\n"
 msgstr "正在执行 %s\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bisect run failed: no command provided."
 msgstr "二分查找运行失败:没有提供命令。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid "unable to verify '%s' on good revision"
-msgstr "无法在好版本中验证 '%s'"
+msgid "unable to verify %s on good revision"
+msgstr "无法在好版本中验证 %s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "bogus exit code %d for good revision"
 msgstr "好版本返回错误的退出码 %d"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
-msgstr "二分查找运行失败:命令 '%2$s' 的退出码 %1$d < 0 或 >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "二分查找运行失败:命令 %2$s 的退出码 %1$d < 0 或 >= 128"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' for writing"
 msgstr "无法打开文件 '%s' 进行写入"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bisect run cannot continue any more"
 msgstr "二分查找不能继续运行"
 
-#: builtin/bisect--helper.c
-#, c-format
+#: builtin/bisect.c
 msgid "bisect run success"
 msgstr "二分查找运行成功"
 
-#: builtin/bisect--helper.c
-#, c-format
+#: builtin/bisect.c
 msgid "bisect found first bad commit"
 msgstr "二分查找找到了第一个坏的提交"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
-msgstr "二分查找运行失败:'git bisect--helper --bisect-state %s' 退出码为 %d"
-
-#: builtin/bisect--helper.c
-msgid "reset the bisection state"
-msgstr "清除二分查找状态"
-
-#: builtin/bisect--helper.c
-msgid "check whether bad or good terms exist"
-msgstr "检查坏的或好的术语是否存在"
-
-#: builtin/bisect--helper.c
-msgid "print out the bisect terms"
-msgstr "打印二分查找术语"
-
-#: builtin/bisect--helper.c
-msgid "start the bisect session"
-msgstr "启动二分查找过程"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr "二分查找运行失败:'git bisect %s' 退出码为 %d"
 
-#: builtin/bisect--helper.c
-msgid "find the next bisection commit"
-msgstr "查询下一个二分查找提交"
-
-#: builtin/bisect--helper.c
-msgid "mark the state of ref (or refs)"
-msgstr "标记引用的状态"
-
-#: builtin/bisect--helper.c
-msgid "list the bisection steps so far"
-msgstr "列出到目前为止的二分查找步骤"
-
-#: builtin/bisect--helper.c
-msgid "replay the bisection process from the given file"
-msgstr "从给定文件重放二分查找进程"
-
-#: builtin/bisect--helper.c
-msgid "skip some commits for checkout"
-msgstr "跳过要检出的一些提交"
-
-#: builtin/bisect--helper.c
-msgid "visualize the bisection"
-msgstr "可视化二分查找过程"
-
-#: builtin/bisect--helper.c
-msgid "use <cmd>... to automatically bisect"
-msgstr "使用 <命令>... 来自动二分查找"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "'%s' 无需参数或者需要一个提交"
 
-#: builtin/bisect--helper.c
-msgid "no log for BISECT_WRITE"
-msgstr "BISECT_WRITE 无日志"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "'%s' 无需参数或者需要一个参数"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset 无需参数或者需要一个提交"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "'%s' 无需参数"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms 需要 0 或 1 个参数"
+#: builtin/bisect.c
+msgid "no logfile given"
+msgstr "未提供日志文件"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next 需要 0 个参数"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "'%s' 运行失败:没有提供命令。"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log 需要 0 个参数"
+#: builtin/bisect.c
+msgid "need a command"
+msgstr "需要一个命令"
 
-#: builtin/bisect--helper.c
-msgid "no logfile given"
-msgstr "未提供日志文件"
+#: builtin/bisect.c builtin/cat-file.c
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "未知命令:'%s'"
 
 #: builtin/blame.c
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<选项>] [<版本选项>] [<版本>] [--] <文件>"
 
+#: builtin/blame.c
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<选项>] [<版本选项>] [<版本>] [--] <文件>"
+
 #: builtin/blame.c
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<版本选项> 的文档记录在 git-rev-list(1) 中"
@@ -3449,37 +3451,37 @@ msgstr "git branch [<选项>] [-r | -a] [--format]"
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "将要删除的分支 '%s' 已经被合并到\n"
-"         '%s',但未合并到 HEAD"
+"         '%s',但未合并到 HEAD"
 
 #  译者:保持原换行格式,在输出时 %s 的替代内容会让字符串变长
 #: builtin/branch.c
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"并未删除分支 '%s', 虽然它已经合并到 HEAD,\n"
-"         然而却尚未被合并到分支 '%s' 。"
+"并未删除分支 '%s',虽然它已经合并到 HEAD,\n"
+"         然而却尚未被合并到分支 '%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
+msgid "couldn't look up commit object for '%s'"
 msgstr "无法查询 '%s' 指向的提交对象"
 
 #: builtin/branch.c
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
 "分支 '%s' 没有完全合并。\n"
-"如果您确认要删除它,执行 'git branch -D %s'"
+"如果您确认要删除它,执行 'git branch -D %s'"
 
 #: builtin/branch.c
-msgid "Update of config-file failed"
+msgid "update of config-file failed"
 msgstr "更新配置文件失败"
 
 #: builtin/branch.c
@@ -3487,23 +3489,28 @@ msgid "cannot use -a with -d"
 msgstr "不能将 -a 和 -d 同时使用"
 
 #: builtin/branch.c
-msgid "Couldn't look up commit object for HEAD"
-msgstr "无法查询 HEAD 指向的提交对象"
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr "无法强制更新被工作区 '%2$s' 所使用的分支 '%1$s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "æ\97 æ³\95å\88 é\99¤æ£\80å\87ºäº\8e '%2$s' ç\9a\84å\88\86æ\94¯ '%1$s'ã\80\82"
+msgid "remote-tracking branch '%s' not found"
+msgstr "æ\9cªè\83½æ\89¾å\88°è¿\9cç¨\8bè·\9f踪å\88\86æ\94¯ '%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "未能找到远程跟踪分支 '%s'。"
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"分支 '%s' 未找到。\n"
+"是否忘记指定 --remote 选项?"
 
 #: builtin/branch.c
 #, c-format
-msgid "branch '%s' not found."
-msgstr "分支 '%s' 未发现"
+msgid "branch '%s' not found"
+msgstr "分支 '%s' 未发现"
 
 #: builtin/branch.c
 #, c-format
@@ -3530,56 +3537,63 @@ msgstr "HEAD (%s) 指向 refs/heads/ 之外"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being rebased at %s"
+msgid "branch %s is being rebased at %s"
 msgstr "分支 %s 正被变基到 %s"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being bisected at %s"
+msgid "branch %s is being bisected at %s"
 msgstr "分支 %s 正被二分查找于 %s"
 
 #: builtin/branch.c
-msgid "cannot copy the current branch while not on any."
-msgstr "无法拷贝当前分支因为不处于任何分支上。"
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "工作区 %s 的 HEAD 指向没有被更新"
+
+#: builtin/branch.c
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "无效的分支名:'%s'"
 
 #: builtin/branch.c
-msgid "cannot rename the current branch while not on any."
-msgstr "无法重命名当前分支因为不处于任何分支上。"
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "分支 '%s' 尚无提交"
 
 #: builtin/branch.c
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "æ\97 æ\95\88ç\9a\84å\88\86æ\94¯å\90\8dï¼\9a'%s'"
+msgid "no branch named '%s'"
+msgstr "没æ\9c\89å\88\86æ\94¯ '%s'"
 
 #: builtin/branch.c
-msgid "Branch rename failed"
+msgid "branch rename failed"
 msgstr "分支重命名失败"
 
 #: builtin/branch.c
-msgid "Branch copy failed"
+msgid "branch copy failed"
 msgstr "分支拷贝失败"
 
 #: builtin/branch.c
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
+msgid "created a copy of a misnamed branch '%s'"
 msgstr "已为错误命名的分支 '%s' 创建了一个副本"
 
 #: builtin/branch.c
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
+msgid "renamed a misnamed branch '%s' away"
 msgstr "已将错误命名的分支 '%s' 重命名"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "分支重命名为 %s,但 HEAD 没有更新"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "分支重命名为 %s,但 HEAD 没有更新"
 
 #: builtin/branch.c
-msgid "Branch is renamed, but update of config-file failed"
+msgid "branch is renamed, but update of config-file failed"
 msgstr "分支被重命名,但更新配置文件失败"
 
 #: builtin/branch.c
-msgid "Branch is copied, but update of config-file failed"
+msgid "branch is copied, but update of config-file failed"
 msgstr "分支已拷贝,但更新配置文件失败"
 
 #: builtin/branch.c
@@ -3665,6 +3679,10 @@ msgstr "移动/重命名一个分支,以及它的引用日志"
 msgid "move/rename a branch, even if target exists"
 msgstr "移动/重命名一个分支,即使目标已存在"
 
+#: builtin/branch.c builtin/for-each-ref.c builtin/tag.c
+msgid "do not output a newline after empty formatted refs"
+msgstr "在格式化引用结果为空之后,不输出换行符"
+
 #: builtin/branch.c
 msgid "copy a branch and its reflog"
 msgstr "拷贝一个分支和它的引用日志"
@@ -3726,9 +3744,9 @@ msgstr "在子模组中递归"
 msgid "format to use for the output"
 msgstr "输出格式"
 
-#: builtin/branch.c builtin/submodule--helper.c submodule.c
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "无法将 HEAD 解析为有效引用"
+#: builtin/branch.c
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "无法将 HEAD 解析为有效引用"
 
 #: builtin/branch.c builtin/clone.c
 msgid "HEAD not found below refs/heads!"
@@ -3750,7 +3768,7 @@ msgid "branch name required"
 msgstr "必须提供分支名"
 
 #: builtin/branch.c
-msgid "Cannot give description to detached HEAD"
+msgid "cannot give description to detached HEAD"
 msgstr "不能向分离头指针提供描述"
 
 #: builtin/branch.c
@@ -3758,14 +3776,12 @@ msgid "cannot edit description of more than one branch"
 msgstr "不能为一个以上的分支编辑描述"
 
 #: builtin/branch.c
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "分支 '%s' 尚无提交。"
+msgid "cannot copy the current branch while not on any"
+msgstr "不处于任何分支上,无法拷贝当前分支"
 
 #: builtin/branch.c
-#, c-format
-msgid "No branch named '%s'."
-msgstr "没有分支 '%s'。"
+msgid "cannot rename the current branch while not on any"
+msgstr "不处于任何分支上,无法重命名当前分支"
 
 #: builtin/branch.c
 msgid "too many branches for a copy operation"
@@ -3782,8 +3798,8 @@ msgstr "为设置新上游提供了太多的参数"
 #: builtin/branch.c
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
-msgstr "无法设置 HEAD 的上游为 %s,因为 HEAD 没有指向任何分支"
+"could not set upstream of HEAD to %s when it does not point to any branch"
+msgstr "无法设置 HEAD 的上游为 %s,因为 HEAD 没有指向任何分支"
 
 #: builtin/branch.c
 #, c-format
@@ -3800,17 +3816,17 @@ msgid "too many arguments to unset upstream"
 msgstr "为取消上游设置操作提供了太多的参数"
 
 #: builtin/branch.c
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr "无法取消 HEAD 的上游设置因为它没有指向一个分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch '%s' has no upstream information"
+msgid "branch '%s' has no upstream information"
 msgstr "分支 '%s' 没有上游信息"
 
 #: builtin/branch.c
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
 "'git branch' 的 -a 和 -r 选项不带一个分支名。\n"
@@ -3819,9 +3835,8 @@ msgstr ""
 #: builtin/branch.c
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
-msgstr ""
-"不再支持选项 '--set-upstream'。请使用 '--track' 或 '--set-upstream-to'。"
+"'--set-upstream-to' instead"
+msgstr "不再支持选项 '--set-upstream'。请使用 '--track' 或 '--set-upstream-to'"
 
 #: builtin/bugreport.c
 msgid "git version:\n"
@@ -3846,11 +3861,11 @@ msgstr "不是在 git 仓库中执行 - 没有可显示的钩子\n"
 
 #: builtin/bugreport.c
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory <文件>] [-s|--suffix <格式>] [--"
-"diagnose[=<模式>]"
+"git bugreport [-o|--output-directory <文件>] [(-s|--suffix) <格式>]\n"
+"              [--diagnose[=<模式>]"
 
 #: builtin/bugreport.c
 msgid ""
@@ -3904,6 +3919,11 @@ msgstr "指定错误报告文件的目标位置"
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "指定文件的 strftime 格式后缀"
 
+#: builtin/bugreport.c
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "未知参数 `%s'"
+
 #: builtin/bugreport.c builtin/diagnose.c
 #, c-format
 msgid "could not create leading directories for '%s'"
@@ -3933,20 +3953,28 @@ msgid "Created new report at '%s'.\n"
 msgstr "在 '%s' 创建了新报告。\n"
 
 #: builtin/bundle.c
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [<选项>] <文件> <git-rev-list 参数>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<版本>] <文件> <git-rev-list-参数>"
 
 #: builtin/bundle.c
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [<选项>] <文件>"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <文件>"
 
 #: builtin/bundle.c
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads <文件> [<引用名>...]"
 
 #: builtin/bundle.c
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle <文件> [<引用名>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <文件> [<引用名>...]"
+
+#: builtin/bundle.c
+msgid "need a <file> argument"
+msgstr "需要一个 <文件> 参数"
 
 #: builtin/bundle.c builtin/pack-objects.c
 msgid "do not show progress meter"
@@ -3956,13 +3984,13 @@ msgstr "不显示进度表"
 msgid "show progress meter"
 msgstr "显示进度表"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "show progress meter during object writing phase"
-msgstr "在对象写入阶段显示进度表"
+#: builtin/bundle.c
+msgid "historical; same as --progress"
+msgstr "老的参数;等同于 --progress"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "当进度表显示时类似于 --all-progress"
+#: builtin/bundle.c
+msgid "historical; does nothing"
+msgstr "老的参数;无作用"
 
 #: builtin/bundle.c
 msgid "specify bundle format version"
@@ -4017,11 +4045,6 @@ msgstr "%s 需要参数"
 msgid "%s takes no arguments"
 msgstr "%s 不需要参数"
 
-#: builtin/cat-file.c
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "未知命令:'%s'"
-
 #: builtin/cat-file.c
 msgid "only one batch option may be specified"
 msgstr "只能指定一个批处理选项"
@@ -4038,25 +4061,25 @@ msgstr "git cat-file (-e | -p) <对象>"
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <对象>"
 
+#: builtin/cat-file.c
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<版本>:<路径|树对象> | --path=<路径|树对象> <版本>]"
+
 #: builtin/cat-file.c
 msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-#: builtin/cat-file.c
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
-msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<版本>:<路径|树对象> | --path=<路径|树对象> <版本>]"
+"             [--textconv | --filters] [-Z]"
 
 #: builtin/cat-file.c
 msgid "Check object existence or emit object contents"
@@ -4106,6 +4129,10 @@ msgstr "类似于 --batch,但不输出 <内容>"
 msgid "stdin is NUL-terminated"
 msgstr "标准输入以 NUL 字符分隔"
 
+#: builtin/cat-file.c
+msgid "stdin and stdout is NUL-terminated"
+msgstr "标准输入和标准输出以 NUL 字符分隔"
+
 #: builtin/cat-file.c
 msgid "read commands from stdin"
 msgstr "从标准输入读取命令"
@@ -4185,23 +4212,23 @@ msgstr "'%s' 需要 <版本>"
 msgid "<object> required with '-%c'"
 msgstr "'-%c' 需要 <对象>"
 
-#: builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
-msgid "too many arguments"
-msgstr "太多参数"
-
 #: builtin/cat-file.c
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr "<类型> <对象> 模式只允许两个参数,而不是 %d 个"
 
 #: builtin/check-attr.c
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <属性>...] [--] <路径名>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <树对象>] [-a | --all | <属性>...] [--] <路径名>..."
 
 #: builtin/check-attr.c
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <属性>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <树对象>] [-a | --all | <属性>...]"
 
 #: builtin/check-attr.c
 msgid "report all attributes set on file"
@@ -4219,6 +4246,14 @@ msgstr "从标准输入读出文件名"
 msgid "terminate input and output records by a NUL character"
 msgstr "输入和输出的记录使用 NUL 字符终结"
 
+#: builtin/check-attr.c
+msgid "<tree-ish>"
+msgstr "<树对象>"
+
+#: builtin/check-attr.c
+msgid "which tree-ish to check attributes at"
+msgstr "要用哪一个树对象来检查属性"
+
 #: builtin/check-ignore.c builtin/checkout.c builtin/gc.c builtin/worktree.c
 msgid "suppress progress reporting"
 msgstr "不显示进度报告"
@@ -4421,6 +4456,11 @@ msgstr "未指定 '%2$s' 时,必须使用 '%1$s'"
 msgid "'%s' or '%s' cannot be used with %s"
 msgstr "'%s' 或 '%s' 不能和 %s 一起使用"
 
+#: builtin/checkout.c
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "'%s'、'%s' 或 '%s' 不能在检出一个树时使用"
+
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' is unmerged"
@@ -4847,9 +4887,10 @@ msgstr "使用叠加模式"
 
 #: builtin/clean.c
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <模式>] [-x | -X] [--] <路径>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <模式>] [-x | -X] [--] <路径规格>..."
 
 #: builtin/clean.c
 #, c-format
@@ -4889,7 +4930,7 @@ msgstr "拒绝删除当前工作目录\n"
 msgid "Would refuse to remove current working directory\n"
 msgstr "将拒绝删除当前工作目录\n"
 
-#: builtin/clean.c git-add--interactive.perl
+#: builtin/clean.c
 #, c-format
 msgid ""
 "Prompt help:\n"
@@ -4902,7 +4943,7 @@ msgstr ""
 "foo        - 通过唯一前缀选择一个选项\n"
 "           - (空)什么也不选择\n"
 
-#: builtin/clean.c git-add--interactive.perl
+#: builtin/clean.c
 #, c-format
 msgid ""
 "Prompt help:\n"
@@ -4923,8 +4964,8 @@ msgstr ""
 "*          - 选择所有选项\n"
 "           - (空)结束选择\n"
 
-#: builtin/clean.c git-add--interactive.perl
-#, c-format, perl-format
+#: builtin/clean.c
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "嗯(%s)?\n"
 
@@ -4993,7 +5034,8 @@ msgid "remove whole directories"
 msgstr "删除整个目录"
 
 #: builtin/clean.c builtin/describe.c builtin/grep.c builtin/log.c
-#: builtin/ls-files.c builtin/name-rev.c builtin/show-ref.c
+#: builtin/ls-files.c builtin/name-rev.c builtin/pack-refs.c builtin/show-ref.c
+#: ref-filter.h
 msgid "pattern"
 msgstr "模式"
 
@@ -5105,7 +5147,7 @@ msgstr "检出 <分支> 而不是远程 HEAD"
 msgid "path to git-upload-pack on the remote"
 msgstr "远程 git-upload-pack 路径"
 
-#: builtin/clone.c builtin/fetch.c builtin/grep.c builtin/pull.c
+#: builtin/clone.c builtin/fetch.c builtin/pull.c
 msgid "depth"
 msgstr "深度"
 
@@ -5113,10 +5155,6 @@ msgstr "深度"
 msgid "create a shallow clone of that depth"
 msgstr "创建一个指定深度的浅克隆"
 
-#: builtin/clone.c builtin/fetch.c builtin/pack-objects.c builtin/pull.c
-msgid "time"
-msgstr "时间"
-
 #: builtin/clone.c
 msgid "create a shallow clone since a specific time"
 msgstr "从一个特定时间创建一个浅克隆"
@@ -5167,14 +5205,6 @@ msgstr "server-specific"
 msgid "option to transmit"
 msgstr "传输选项"
 
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv4 addresses only"
-msgstr "只使用 IPv4 地址"
-
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv6 addresses only"
-msgstr "只使用 IPv6 地址"
-
 #: builtin/clone.c
 msgid "apply partial clone filters to submodules"
 msgstr "对子模组使用部分克隆过滤器"
@@ -5210,11 +5240,21 @@ msgstr "无法对 '%s' 调用 stat"
 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'"
@@ -5296,11 +5336,6 @@ msgstr "太多参数。"
 msgid "You must specify a repository to clone."
 msgstr "您必须指定一个仓库来克隆。"
 
-#: builtin/clone.c
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "选项 '%s' 和 '%s %s' 不能同时使用"
-
 #: builtin/clone.c
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
@@ -5404,6 +5439,10 @@ msgstr "无法初始化仓库,跳过归档包 URI"
 msgid "failed to fetch objects from bundle URI '%s'"
 msgstr "无法从归档包 URI '%s' 获取对象"
 
+#: builtin/clone.c
+msgid "failed to fetch advertised bundles"
+msgstr "无法获取公布的归档包"
+
 #: builtin/clone.c
 msgid "remote transport reported error"
 msgstr "远程传输报告错误"
@@ -5451,22 +5490,27 @@ msgstr "--command 必须是第一个参数"
 
 #: builtin/commit-graph.c
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir <对象目录>] [--shallow] [--"
-"[no-]progress]"
+"git commit-graph verify [--object-dir <目录>] [--shallow] [--[no-]progress]"
 
 #: builtin/commit-graph.c
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
-msgstr ""
-"git commit-graph write [--object-dir <对象目录>] [--append] [--split[=<策略"
-">]] [--reachable|--stdin-packs|--stdin-commits] [--changed-paths] [--"
-"[no-]max-new-filters <n>] [--[no-]progress] <切分选项>"
-
-#: builtin/commit-graph.c builtin/fetch.c builtin/log.c
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
+msgstr ""
+"git commit-graph write [--object-dir <目录>] [--append]\n"
+"                       [--split[=<策略>]] [--reachable | --stdin-packs | --"
+"stdin-commits] \n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <切分选项>"
+
+#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
 msgid "dir"
 msgstr "目录"
 
@@ -5483,6 +5527,11 @@ msgstr "如果提交图形被拆分,只验证头一个文件"
 msgid "Could not open commit-graph '%s'"
 msgstr "无法打开提交图形 '%s'"
 
+#: builtin/commit-graph.c
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "无法打开提交图形链 '%s'"
+
 #: builtin/commit-graph.c
 #, c-format
 msgid "unrecognized --split argument, %s"
@@ -5551,13 +5600,17 @@ msgstr "不能同时使用 --reachable、--stdin-commits 或 --stdin-packs"
 msgid "Collecting commits from input"
 msgstr "正从标准输入收集提交"
 
+#: builtin/commit-tree.c
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <树> [(-p <父提交>)...]"
+
 #: builtin/commit-tree.c
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p <父提交>)...] [-S[<keyid>]] [(-m <消息>)...] [(-F <文件"
-">)...] <树>"
+"git commit-tree [(-p <父提交>)...] [-S[<私钥 ID>]] [(-m <消息>)...]\n"
+"                [(-F <文件>)...] <树>"
 
 #: builtin/commit-tree.c
 #, c-format
@@ -5614,12 +5667,30 @@ msgid "git commit-tree: failed to read"
 msgstr "git commit-tree:无法读取"
 
 #: builtin/commit.c
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<选项>] [--] <路径规格>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<模式>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <提交> | --fixup [(amend|"
+"reword):]<提交>)]\n"
+"           [-F <文件> | -m <消息>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<作者>]\n"
+"           [--date=<日期>] [--cleanup=<模式>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<文件> [--pathspec-file-nul]]\n"
+"           [(--trailer <键>[(=|:)<值>])...] [-S[<私钥 ID>]]\n"
+"           [--] [<路径规格>...]"
 
 #: builtin/commit.c
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<选项>] [--] <路径规格>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<选项>] [--] [<路径规格>...]"
 
 #: builtin/commit.c
 msgid ""
@@ -5673,6 +5744,10 @@ msgstr ""
 "    git cherry-pick --skip\n"
 "\n"
 
+#: builtin/commit.c read-cache.c
+msgid "updating files failed"
+msgstr "更新文件失败"
+
 #: builtin/commit.c
 msgid "failed to unpack HEAD tree object"
 msgstr "无法解包 HEAD 树对象"
@@ -5697,10 +5772,6 @@ msgstr "无法更新临时索引"
 msgid "Failed to update main cache tree"
 msgstr "不能更新树的主缓存"
 
-#: builtin/commit.c
-msgid "unable to write new_index file"
-msgstr "无法写 new_index 文件"
-
 #: builtin/commit.c
 msgid "cannot do a partial commit during a merge."
 msgstr "在合并过程中不能做部分提交。"
@@ -5746,10 +5817,10 @@ msgid ""
 "in the current commit message"
 msgstr "无法选择一个未被当前提交说明使用的注释字符"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/merge-tree.c
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "不能查询提交 %s"
+msgid "could not lookup commit '%s'"
+msgstr "不能查询提交 '%s'"
 
 #: builtin/commit.c builtin/shortlog.c
 #, c-format
@@ -5966,7 +6037,7 @@ msgstr "计算完整的领先/落后值"
 msgid "version"
 msgstr "版本"
 
-#: builtin/commit.c builtin/push.c builtin/worktree.c
+#: builtin/commit.c builtin/fetch.c builtin/push.c builtin/worktree.c
 msgid "machine-readable output"
 msgstr "机器可读的输出"
 
@@ -6048,7 +6119,7 @@ msgstr "日期"
 msgid "override date for commit"
 msgstr "提交时覆盖日期"
 
-#: builtin/commit.c parse-options.h ref-filter.h
+#: builtin/commit.c builtin/merge-tree.c parse-options.h ref-filter.h
 msgid "commit"
 msgstr "提交"
 
@@ -6189,17 +6260,17 @@ msgstr "因提交说明的正文为空而终止提交。\n"
 #: builtin/commit.c
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"仓库已更新,但无法写 new_index 文件。检查是否磁盘已满或\n"
+"仓库已更新,但无法写入索引文件。检查是否磁盘已满或\n"
 "磁盘配额已耗尽,然后执行 \"git restore --staged :/\" 恢复。"
 
 #: builtin/config.c
 msgid "git config [<options>]"
 msgstr "git config [<选项>]"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 #, c-format
 msgid "unrecognized --type argument, %s"
 msgstr "未能识别的 --type 参数,%s"
@@ -6228,7 +6299,7 @@ msgstr "使用仓库级配置文件"
 msgid "use per-worktree config file"
 msgstr "使用工作区级别的配置文件"
 
-#: builtin/config.c
+#: builtin/config.c builtin/gc.c
 msgid "use given config file"
 msgstr "使用指定的配置文件"
 
@@ -6308,11 +6379,11 @@ msgstr "获得颜色设置:配置 [stdout-is-tty]"
 msgid "Type"
 msgstr "类型"
 
-#: builtin/config.c builtin/env--helper.c builtin/hash-object.c
+#: builtin/config.c builtin/hash-object.c
 msgid "type"
 msgstr "类型"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 msgid "value is given this type"
 msgstr "取值为该类型"
 
@@ -6364,7 +6435,7 @@ msgstr "显示配置的来源(文件、标准输入、数据对象,或命令
 msgid "show scope of config (worktree, local, global, system, command)"
 msgstr "显示配置的作用域(工作区、本地、全局、系统、命令)"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 msgid "value"
 msgstr "取值"
 
@@ -6449,7 +6520,7 @@ msgstr "--blob 只能在 git 仓库内使用"
 msgid "--worktree can only be used inside a git repository"
 msgstr "--worktree 只能在 git 仓库内使用"
 
-#: builtin/config.c
+#: builtin/config.c builtin/gc.c
 msgid "$HOME not set"
 msgstr "$HOME 未设置"
 
@@ -6559,12 +6630,20 @@ msgid "unable to get credential storage lock in %d ms"
 msgstr "无法在 %d ms 获得凭证存储锁"
 
 #: builtin/describe.c
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<选项>] [<提交号>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<提交号>...]"
+
+#: builtin/describe.c
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<标记>]"
 
 #: builtin/describe.c
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<选项>] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe <数据对象>"
 
 #: builtin/describe.c
 msgid "head"
@@ -6717,11 +6796,11 @@ msgstr "选项 '%s' 和提交号不能同时使用"
 
 #: builtin/diagnose.c
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory <文件>] [-s|--suffix <格式>] [--mode=<模"
-"式>]"
+"git diagnose [(-o | --output-directory) <路径>] [(-s | --suffix) <格式>]\n"
+"             [--mode=<模式>]"
 
 #: builtin/diagnose.c
 msgid "specify a destination for the diagnostics archive"
@@ -6744,6 +6823,10 @@ msgstr "--merge-base 仅适用于两个提交"
 msgid "'%s': not a regular file or symlink"
 msgstr "'%s':不是一个正规文件或符号链接"
 
+#: builtin/diff.c
+msgid "no merge given, only parents."
+msgstr "没有给出合并,只有父提交。"
+
 #: builtin/diff.c
 #, c-format
 msgid "invalid option: %s"
@@ -6887,30 +6970,6 @@ msgstr "没有为 --tool=<工具> 参数提供 <工具>"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "没有为 --extcmd=<命令> 参数提供 <命令>"
 
-#: builtin/env--helper.c
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <选项> <环境变量>"
-
-#: builtin/env--helper.c
-msgid "default for git_env_*(...) to fall back on"
-msgstr "git_env_*(...) 的默认值"
-
-#: builtin/env--helper.c
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr "安静模式,只使用 git_env_*() 的值作为退出码"
-
-#: builtin/env--helper.c
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr "选项 `--default' 和 `--type=bool` 期望一个布尔值,不是 `%s`"
-
-#: builtin/env--helper.c
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not "
-"`%s`"
-msgstr "选项 `--default' 和 `--type=ulong` 期望一个无符号长整型,不是 `%s`"
-
 #: builtin/fast-export.c
 msgid "git fast-export [<rev-list-opts>]"
 msgstr "git fast-export [<rev-list 选项>]"
@@ -7053,145 +7112,15 @@ msgstr "git fetch --all [<选项>]"
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel 不能为负数"
 
-#: builtin/fetch.c builtin/pull.c
-msgid "fetch from all remotes"
-msgstr "从所有的远程抓取"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "set upstream for git pull/fetch"
-msgstr "为 git pull/fetch 设置上游"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "追加到 .git/FETCH_HEAD 而不是覆盖它"
-
-#: builtin/fetch.c
-msgid "use atomic transaction to update references"
-msgstr "使用原子事务更新引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "path to upload pack on remote end"
-msgstr "上传包到远程的路径"
-
-#: builtin/fetch.c
-msgid "force overwrite of local reference"
-msgstr "强制覆盖本地引用"
-
-#: builtin/fetch.c
-msgid "fetch from multiple remotes"
-msgstr "从多个远程抓取"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "fetch all tags and associated objects"
-msgstr "抓取所有的标签和关联对象"
-
-#: builtin/fetch.c
-msgid "do not fetch all tags (--no-tags)"
-msgstr "不抓取任何标签(--no-tags)"
-
-#: builtin/fetch.c
-msgid "number of submodules fetched in parallel"
-msgstr "子模组获取的并发数"
-
-#: builtin/fetch.c
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr "修改引用规格以将所有引用放入 refs/prefetch/"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "prune remote-tracking branches no longer on remote"
-msgstr "清除远程已经不存在的分支的跟踪分支"
-
-#: builtin/fetch.c
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr "清除远程不存在的本地标签,并且替换变更标签"
-
-#  译者:可选值,不能翻译
-#: builtin/fetch.c builtin/pull.c
-msgid "on-demand"
-msgstr "on-demand"
-
-#: builtin/fetch.c
-msgid "control recursive fetching of submodules"
-msgstr "控制子模组的递归抓取"
-
-#: builtin/fetch.c
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "将获取到的引用写入 FETCH_HEAD 文件"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "keep downloaded pack"
-msgstr "保持下载包"
-
-#: builtin/fetch.c
-msgid "allow updating of HEAD ref"
-msgstr "允许更新 HEAD 引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "deepen history of shallow clone"
-msgstr "深化浅克隆的历史"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "deepen history of shallow repository based on time"
-msgstr "基于时间来深化浅克隆的历史"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "convert to a complete repository"
-msgstr "转换为一个完整的仓库"
-
-#: builtin/fetch.c
-msgid "re-fetch without negotiating common commits"
-msgstr "不协商共有提交重新获取"
-
-#: builtin/fetch.c
-msgid "prepend this to submodule path output"
-msgstr "在子模组路径输出的前面加上此目录"
-
-#: builtin/fetch.c
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr "递归获取子模组的缺省值(比配置文件优先级低)"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "accept refs that update .git/shallow"
-msgstr "接受更新 .git/shallow 的引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "refmap"
-msgstr "引用映射"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "specify fetch refmap"
-msgstr "指定获取操作的引用映射"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "report that we have only objects reachable from this object"
-msgstr "报告我们只拥有从该对象开始可达的对象"
-
-#: builtin/fetch.c
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr "不获取包文件;而是打印协商的祖先提交"
-
-#: builtin/fetch.c
-msgid "run 'maintenance --auto' after fetching"
-msgstr "获取后执行 'maintenance --auto'"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "check for forced-updates on all updated branches"
-msgstr "在所有更新分支上检查强制更新"
-
-#: builtin/fetch.c
-msgid "write the commit-graph after fetching"
-msgstr "抓取后写提交图"
-
-#: builtin/fetch.c
-msgid "accept refspecs from stdin"
-msgstr "从标准输入获取引用规格"
-
 #: builtin/fetch.c
 msgid "couldn't find remote ref HEAD"
 msgstr "无法发现远程 HEAD 引用"
 
+#: builtin/fetch.c
+#, c-format
+msgid "From %.*s\n"
+msgstr "来自 %.*s\n"
+
 #: builtin/fetch.c
 #, c-format
 msgid "object %s not found"
@@ -7277,11 +7206,6 @@ msgstr "%s 未发送所有必需的对象\n"
 msgid "rejected %s because shallow roots are not allowed to be updated"
 msgstr "拒绝 %s 因为浅克隆的根不允许被更新"
 
-#: builtin/fetch.c
-#, c-format
-msgid "From %.*s\n"
-msgstr "来自 %.*s\n"
-
 #: builtin/fetch.c
 #, c-format
 msgid ""
@@ -7392,21 +7316,161 @@ msgstr "未指定远程仓库;请指定一个用于获取新版本的 URL 或
 msgid "you need to specify a tag name"
 msgstr "您需要指定一个标签名称"
 
-#: builtin/fetch.c
-msgid "--negotiate-only needs one or more --negotiation-tip=*"
-msgstr "--negotiate-only 需要一个或多个 --negotiation-tip=*"
+#: builtin/fetch.c builtin/pull.c
+msgid "fetch from all remotes"
+msgstr "从所有远程抓取"
 
-#: builtin/fetch.c
-msgid "negative depth in --deepen is not supported"
-msgstr "--deepen 不支持负数深度"
+#: builtin/fetch.c builtin/pull.c
+msgid "set upstream for git pull/fetch"
+msgstr "为 git pull/fetch 设置上游"
 
-#: builtin/fetch.c
-msgid "--unshallow on a complete repository does not make sense"
-msgstr "对于一个完整的仓库,参数 --unshallow 没有意义"
+#: builtin/fetch.c builtin/pull.c
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "追加到 .git/FETCH_HEAD 而不是覆盖它"
 
 #: builtin/fetch.c
-msgid "fetch --all does not take a repository argument"
-msgstr "fetch --all 不能带一个仓库参数"
+msgid "use atomic transaction to update references"
+msgstr "使用原子事务更新引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "path to upload pack on remote end"
+msgstr "上传包到远程的路径"
+
+#: builtin/fetch.c
+msgid "force overwrite of local reference"
+msgstr "强制覆盖本地引用"
+
+#: builtin/fetch.c
+msgid "fetch from multiple remotes"
+msgstr "从多个远程抓取"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "fetch all tags and associated objects"
+msgstr "抓取所有的标签和关联对象"
+
+#: builtin/fetch.c
+msgid "do not fetch all tags (--no-tags)"
+msgstr "不抓取任何标签(--no-tags)"
+
+#: builtin/fetch.c
+msgid "number of submodules fetched in parallel"
+msgstr "获取子模组的并发数"
+
+#: builtin/fetch.c
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr "修改引用规格以将所有引用放入 refs/prefetch/"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "prune remote-tracking branches no longer on remote"
+msgstr "清除远程已经不存在的分支的跟踪分支"
+
+#: builtin/fetch.c
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr "清除远程不存在的本地标签,并且替换变更标签"
+
+#  译者:可选值,不能翻译
+#: builtin/fetch.c builtin/pull.c
+msgid "on-demand"
+msgstr "on-demand"
+
+#: builtin/fetch.c
+msgid "control recursive fetching of submodules"
+msgstr "控制子模组的递归抓取"
+
+#: builtin/fetch.c
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "将获取到的引用写入 FETCH_HEAD 文件"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "keep downloaded pack"
+msgstr "保持已下载的包"
+
+#: builtin/fetch.c
+msgid "allow updating of HEAD ref"
+msgstr "允许更新 HEAD 引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "deepen history of shallow clone"
+msgstr "深化浅克隆的历史"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "deepen history of shallow repository based on time"
+msgstr "基于时间来深化浅克隆的历史"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "convert to a complete repository"
+msgstr "转换为一个完整的仓库"
+
+#: builtin/fetch.c
+msgid "re-fetch without negotiating common commits"
+msgstr "重新获取而不协商共同提交"
+
+#: builtin/fetch.c
+msgid "prepend this to submodule path output"
+msgstr "在子模组路径输出的前面加上此目录"
+
+#: builtin/fetch.c
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr "递归获取子模组的缺省值(比配置文件优先级低)"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "accept refs that update .git/shallow"
+msgstr "接受更新 .git/shallow 的引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "refmap"
+msgstr "引用映射"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "specify fetch refmap"
+msgstr "指定获取操作的引用映射"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "report that we have only objects reachable from this object"
+msgstr "报告我们只拥有从该对象开始可达的对象"
+
+#: builtin/fetch.c
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr "不获取包文件;而是打印协商的祖先提交"
+
+#: builtin/fetch.c
+msgid "run 'maintenance --auto' after fetching"
+msgstr "获取后执行 'maintenance --auto'"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "check for forced-updates on all updated branches"
+msgstr "在所有更新分支上检查强制更新"
+
+#: builtin/fetch.c
+msgid "write the commit-graph after fetching"
+msgstr "抓取后写提交图"
+
+#: builtin/fetch.c
+msgid "accept refspecs from stdin"
+msgstr "从标准输入获取引用规格"
+
+#: builtin/fetch.c
+msgid "--negotiate-only needs one or more --negotiation-tip=*"
+msgstr "--negotiate-only 需要一个或多个 --negotiation-tip=*"
+
+#: builtin/fetch.c
+msgid "negative depth in --deepen is not supported"
+msgstr "--deepen 不支持负数深度"
+
+#: builtin/fetch.c
+msgid "--unshallow on a complete repository does not make sense"
+msgstr "对于一个完整的仓库,参数 --unshallow 没有意义"
+
+#: builtin/fetch.c
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "无法从 '%s' 获取归档包"
+
+#: builtin/fetch.c
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all 不能带一个仓库参数"
 
 #: builtin/fetch.c
 msgid "fetch --all does not make sense with refspecs"
@@ -7532,9 +7596,17 @@ msgstr "只打印包含该提交的引用"
 msgid "print only refs which don't contain the commit"
 msgstr "只打印不包含该提交的引用"
 
+#: builtin/for-each-ref.c
+msgid "read reference patterns from stdin"
+msgstr "从标准输入读取引用的模式"
+
+#: builtin/for-each-ref.c
+msgid "unknown arguments supplied with --stdin"
+msgstr "为 --stdin 提供了未知的命令参数"
+
 #: builtin/for-each-repo.c
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=<配置> <命令参数>"
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<配置> [--] <命令参数>"
 
 #: builtin/for-each-repo.c
 msgid "config"
@@ -7548,6 +7620,11 @@ msgstr "存储着仓库路径列表的配置项键名"
 msgid "missing --config=<config>"
 msgstr "缺少 --config=<配置>"
 
+#: builtin/for-each-repo.c
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "发现错误的配置行 --config=%s"
+
 #: builtin/fsck.c
 msgid "unknown"
 msgstr "未知"
@@ -7733,13 +7810,14 @@ msgid "notice: %s points to an unborn branch (%s)"
 msgstr "注意:%s 指向一个尚未诞生的分支(%s)"
 
 #: builtin/fsck.c
-msgid "Checking cache tree"
-msgstr "正在检查缓存树"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "正在检查缓存树 %s"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s:cache-tree 无效的 sha1 指针"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s:cache-tree %s 中存在无效的 sha1 指针"
 
 #: builtin/fsck.c
 msgid "non-tree in cache-tree"
@@ -7747,12 +7825,30 @@ msgstr "cache-tree 中非树对象"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s:resolve-undo 中无效的 sha1 指针"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s:resolve-undo %s 中存在无效的 sha1 指针"
+
+#: builtin/fsck.c
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "无法为包文件 %s 加载反向索引"
 
 #: builtin/fsck.c
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<选项>] [<对象>...]"
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "包文件 '%s' 的反向索引文件无效"
+
+#: builtin/fsck.c
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<对象>...]"
 
 #: builtin/fsck.c
 msgid "show unreachable objects"
@@ -7824,14 +7920,6 @@ msgstr "git fsmonitor--daemon start [<选项>]"
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [<选项>]"
 
-#: builtin/fsmonitor--daemon.c
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-#: builtin/fsmonitor--daemon.c
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
-
 #: builtin/fsmonitor--daemon.c
 #, c-format
 msgid "value of '%s' out of range: %d"
@@ -7932,7 +8020,7 @@ msgstr "等待守护进程启动的最大秒数"
 msgid "invalid 'ipc-threads' value (%d)"
 msgstr "无效的 'ipc-threads' 值(%d)"
 
-#: builtin/fsmonitor--daemon.c
+#: builtin/fsmonitor--daemon.c t/helper/test-cache-tree.c
 #, c-format
 msgid "Unhandled subcommand '%s'"
 msgstr "未处理的子命令 '%s'"
@@ -7955,7 +8043,7 @@ msgstr "对 %s 调用 fstat 失败:%s"
 msgid "failed to parse '%s' value '%s'"
 msgstr "无法解析 '%s' 值 '%s'"
 
-#: builtin/gc.c builtin/init-db.c
+#: builtin/gc.c setup.c
 #, c-format
 msgid "cannot stat '%s'"
 msgstr "不能对 '%s' 调用 stat"
@@ -7982,6 +8070,10 @@ msgstr "清除未引用的对象"
 msgid "pack unreferenced objects separately"
 msgstr "分开打包未引用的对象"
 
+#: builtin/gc.c builtin/repack.c
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "使用 --cruft,限制新 cruft 包的总大小"
+
 #: builtin/gc.c
 msgid "be more thorough (increased runtime)"
 msgstr "更彻底(增加运行时间)"
@@ -8132,8 +8224,23 @@ msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr "最多使用 --auto 和 --schedule=<频率> 其中之一"
 
 #: builtin/gc.c
-msgid "failed to run 'git config'"
-msgstr "无法运行 'git config'"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "无法添加 '%2$s' 的 '%1$s' 值"
+
+#: builtin/gc.c
+msgid "return success even if repository was not registered"
+msgstr "即便仓库非注册仍然返回成功"
+
+#: builtin/gc.c
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "无法取消设置 '%2$s' 的 '%1$s' 值"
+
+#: builtin/gc.c
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "仓库 '%s' 未注册"
 
 #: builtin/gc.c
 #, c-format
@@ -8182,14 +8289,6 @@ msgstr "无法运行 'crontab',您的系统可能不支持 'cron'"
 msgid "'crontab' died"
 msgstr "'crontab' 终止"
 
-#: builtin/gc.c
-msgid "failed to start systemctl"
-msgstr "无法启动 systemctl"
-
-#: builtin/gc.c
-msgid "failed to run systemctl"
-msgstr "无法运行 systemctl"
-
 #: builtin/gc.c builtin/worktree.c
 #, c-format
 msgid "failed to delete '%s'"
@@ -8200,6 +8299,14 @@ msgstr "无法删除 '%s'"
 msgid "failed to flush '%s'"
 msgstr "无法刷新 '%s'"
 
+#: builtin/gc.c
+msgid "failed to start systemctl"
+msgstr "无法启动 systemctl"
+
+#: builtin/gc.c
+msgid "failed to run systemctl"
+msgstr "无法运行 systemctl"
+
 #: builtin/gc.c
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
@@ -8230,6 +8337,10 @@ msgstr "调度器"
 msgid "scheduler to trigger git maintenance run"
 msgstr "触发 git maintenance 执行的调度器"
 
+#: builtin/gc.c
+msgid "failed to set up maintenance schedule"
+msgstr "无法设置维护计划"
+
 #: builtin/gc.c
 msgid "failed to add repo to global config"
 msgstr "无法将仓库添加到全局配置"
@@ -8326,8 +8437,8 @@ msgid "search in subdirectories (default)"
 msgstr "在子目录中寻找(默认)"
 
 #: builtin/grep.c
-msgid "descend at most <depth> levels"
-msgstr "最多以指定的深度向下寻找"
+msgid "descend at most <n> levels"
+msgstr "最多向下寻找 <n> 层"
 
 #: builtin/grep.c
 msgid "use extended POSIX regular expressions"
@@ -8509,11 +8620,15 @@ msgstr "同时给出了 --cached 和树对象"
 
 #: builtin/hash-object.c
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <类型>] [-w] [--path=<文件> | --no-filters] [--stdin] "
-"[--] <文件>..."
+"git hash-object [-t <类型>] [-w] [--path=<文件> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <文件>..."
+
+#: builtin/hash-object.c
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <类型>] [-w] --stdin-paths [--no-filters]"
 
 #: builtin/hash-object.c
 msgid "object type"
@@ -8676,13 +8791,21 @@ msgid "'git help config' for more information"
 msgstr "'git help config' 获取更多信息"
 
 #: builtin/hook.c
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] <钩子名称> [-- <钩子参数>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<路径>] <钩子名称> [-- <钩子参数"
+">]"
 
 #: builtin/hook.c
 msgid "silently ignore missing requested <hook-name>"
 msgstr "静默地忽略缺失的 <钩子名称>"
 
+#: builtin/hook.c
+msgid "file to read into hooks' stdin"
+msgstr "读取至钩子标准输入的文件"
+
 #: builtin/index-pack.c
 #, c-format
 msgid "object type mismatch at %s"
@@ -8939,7 +9062,7 @@ msgstr "无法返回当前工作目录"
 msgid "bad %s"
 msgstr "错误选项 %s"
 
-#: builtin/index-pack.c builtin/init-db.c
+#: builtin/index-pack.c builtin/init-db.c setup.c
 #, c-format
 msgid "unknown hash algorithm '%s'"
 msgstr "未知的哈希算法 '%s'"
@@ -8956,102 +9079,17 @@ msgstr "--verify 没有提供包文件名参数"
 msgid "fsck error in pack objects"
 msgstr "在打包对象中 fsck 检查出错"
 
-#: builtin/init-db.c
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "不能对模版 '%s' 调用 stat"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "不能打开目录 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "不能读取链接 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "不能自 '%s' 到 '%s' 创建符号链接"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "不能拷贝 '%s' 至 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "ignoring template %s"
-msgstr "忽略模版 %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "templates not found in %s"
-msgstr "没有在 %s 中找到模版"
-
-#: builtin/init-db.c
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "没有从 '%s' 复制模版:%s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "无效的初始分支名:'%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "不能处理 %d 类型的文件"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "不能移动 %s 至 %s"
-
-#: builtin/init-db.c
-msgid "attempt to reinitialize repository with different hash"
-msgstr "尝试用不同的哈希算法重新初始化仓库"
-
-#: builtin/init-db.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s 已经存在"
-
-#: builtin/init-db.c
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init:已忽略 --initial-branch=%s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "重新初始化已存在的共享 Git 仓库于 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "已重新初始化已存在的 Git 仓库于 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "已初始化空的共享 Git 仓库于 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "已初始化空的 Git 仓库于 %s%s\n"
-
 #: builtin/init-db.c
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<模板目录>] [--shared[=<权限>]] "
-"[<目录>]"
+"git init [-q | --quiet] [--bare] [--template=<模板目录>]\n"
+"         [--separate-git-dir <git 目录>] [--object-format=<格式>]\n"
+"         [-b <分支名> | --initial-branch=<分支名>]\n"
+"         [--shared[=<权限>]] [<目录>]"
 
 #: builtin/init-db.c
 msgid "permissions"
@@ -9101,11 +9139,13 @@ msgstr "--separate-git-dir 不能用于纯仓库"
 
 #: builtin/interpret-trailers.c
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer <键>[(=|:)<值"
-">])...] [<文件>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<键|键别名>)[(=|:)<值>])...]\n"
+"                       [--parse] [<文件>...]"
 
 #: builtin/interpret-trailers.c
 msgid "edit files in place"
@@ -9115,6 +9155,10 @@ msgstr "在原位编辑文件"
 msgid "trim empty trailers"
 msgstr "删除空的尾注"
 
+#: builtin/interpret-trailers.c
+msgid "placement"
+msgstr "安置"
+
 #: builtin/interpret-trailers.c
 msgid "where to place the new trailer"
 msgstr "在哪里放置新的尾注"
@@ -9132,20 +9176,20 @@ msgid "output only the trailers"
 msgstr "只输出尾注"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply config rules"
-msgstr "不要应用配置规则"
+msgid "do not apply trailer.* configuration variables"
+msgstr "不应用 trailer.* 配置变量"
 
 #: builtin/interpret-trailers.c
-msgid "join whitespace-continued values"
-msgstr "连接空白折行的值"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "将多行尾注值重新格式化为单行值"
 
 #: builtin/interpret-trailers.c
-msgid "set parsing options"
-msgstr "设置解析选项"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "--only-trailers --only-input --unfold 的别名"
 
 #: builtin/interpret-trailers.c
-msgid "do not treat --- specially"
-msgstr "ä¸\8dè¦\81对 --- ç\89¹æ®\8aå¤\84ç\90\86"
+msgid "do not treat \"---\" as the end of input"
+msgstr "ä¸\8dè¦\81å°\86 \"---\" è§\86为è¾\93å\85¥ç\9a\84ç»\93æ\9d\9f"
 
 #: builtin/interpret-trailers.c
 msgid "trailer(s) to add"
@@ -9257,6 +9301,11 @@ msgstr "只需要一个范围"
 msgid "not a range"
 msgstr "不是一个范围"
 
+#: builtin/log.c
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "无法读取分支描述文件 '%s'"
+
 #: builtin/log.c
 msgid "cover letter needs email format"
 msgstr "附函需要邮件地址格式"
@@ -9383,6 +9432,10 @@ msgstr "从分支描述获取附函的模式"
 msgid "generate parts of a cover letter based on a branch's description"
 msgstr "基于一个分支描述生成部分附函"
 
+#: builtin/log.c
+msgid "use branch description from file"
+msgstr "使用来自文件的分支描述"
+
 #: builtin/log.c
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "使用 [<前缀>] 代替 [PATCH]"
@@ -9586,6 +9639,11 @@ msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "不能找到跟踪的远程分支,请手工指定 <上游>。\n"
 
+#: builtin/ls-files.c builtin/ls-tree.c
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "无法获得关于 '%s' 的对象信息"
+
 #: builtin/ls-files.c
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
@@ -9724,12 +9782,12 @@ msgstr ""
 #: builtin/ls-remote.c
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
-"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<仓库> [<引用>...]]"
+"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<可执行文件>]\n"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<键>]\n"
+"              [--symref] [<仓库> [<模式>...]]"
 
 #: builtin/ls-remote.c
 msgid "do not print remote URL"
@@ -9771,11 +9829,6 @@ msgstr "除了显示指向的对象外,显示指向的引用名"
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
 msgstr "git ls-tree [<选项>] <树对象> [<路径>...]"
 
-#: builtin/ls-tree.c
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "无法获得关于 '%s' 的对象信息"
-
 #: builtin/ls-tree.c
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
@@ -9897,14 +9950,14 @@ msgstr "git merge-base [-a | --all] <提交> <提交>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <提交>..."
 
-#: builtin/merge-base.c
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <提交>..."
-
 #: builtin/merge-base.c
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <提交> <提交>"
 
+#: builtin/merge-base.c
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <提交>..."
+
 #: builtin/merge-base.c
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <引用> [<提交>]"
@@ -9941,6 +9994,10 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "将结果发送到标准输出"
 
+#: builtin/merge-file.c
+msgid "use object IDs instead of filenames"
+msgstr "使用对象 ID 替换文件名"
+
 #: builtin/merge-file.c
 msgid "use a diff3 based merge"
 msgstr "使用基于 diff3 的合并"
@@ -9973,6 +10030,15 @@ msgstr "不要警告冲突"
 msgid "set labels for file1/orig-file/file2"
 msgstr "为 文件1/初始文件/文件2 设置标签"
 
+#: builtin/merge-file.c
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "对象 '%s' 不存在"
+
+#: builtin/merge-file.c
+msgid "Could not write object file"
+msgstr "不能写入对象文件"
+
 #: builtin/merge-recursive.c
 #, c-format
 msgid "unknown option %s"
@@ -10044,10 +10110,45 @@ msgstr "列出没有模式/对象 ID/暂存的文件名"
 msgid "allow merging unrelated histories"
 msgstr "允许合并不相关的历史"
 
+#: builtin/merge-tree.c
+msgid "perform multiple merges, one per line of input"
+msgstr "实施多个合并,每输入行一个"
+
+#: builtin/merge-tree.c
+msgid "specify a merge-base for the merge"
+msgstr "指定用于合并的合并基线"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option=value"
+msgstr "option=value"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option for selected merge strategy"
+msgstr "所选的合并策略的选项"
+
 #: builtin/merge-tree.c
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge 与其他所有选项不兼容"
 
+#: builtin/merge-tree.c builtin/merge.c
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "未知的策略选项:-X%s"
+
+#: builtin/merge-tree.c
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base 与 --stdin 不兼容"
+
+#: builtin/merge-tree.c builtin/notes.c
+#, c-format
+msgid "malformed input line: '%s'."
+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 [<选项>] [<提交>...]"
@@ -10125,14 +10226,6 @@ msgstr "策略"
 msgid "merge strategy to use"
 msgstr "要使用的合并策略"
 
-#: builtin/merge.c builtin/pull.c
-msgid "option=value"
-msgstr "option=value"
-
-#: builtin/merge.c builtin/pull.c
-msgid "option for selected merge strategy"
-msgstr "所选的合并策略的选项"
-
 #: builtin/merge.c
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "合并的提交说明(针对非快进式合并)"
@@ -10211,11 +10304,6 @@ msgstr "不能写入索引。"
 msgid "Not handling anything other than two heads merge."
 msgstr "未处理两个头合并之外的任何操作。"
 
-#: builtin/merge.c
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "未知的策略选项:-X%s"
-
 #: builtin/merge.c t/helper/test-fast-rebase.c
 #, c-format
 msgid "unable to write %s"
@@ -10288,7 +10376,7 @@ msgstr "对于 %s 没有来自 %s 的远程跟踪分支"
 msgid "Bad value '%s' in environment '%s'"
 msgstr "环境 '%2$s' 中存在坏的取值 '%1$s'"
 
-#: builtin/merge.c read-cache.c strbuf.c wrapper.c
+#: builtin/merge.c editor.c read-cache.c wrapper.c
 #, c-format
 msgid "could not close '%s'"
 msgstr "不能关闭 '%s'"
@@ -10574,8 +10662,8 @@ msgid "can not move directory into itself"
 msgstr "不能将目录移动到自身"
 
 #: builtin/mv.c
-msgid "cannot move directory over file"
-msgstr "不能将目录移动到文件"
+msgid "destination already exists"
+msgstr "目标已存在"
 
 #: builtin/mv.c
 msgid "source directory is empty"
@@ -10620,7 +10708,7 @@ msgstr "%s,源=%s,目标=%s"
 msgid "Renaming %s to %s\n"
 msgstr "重命名 %s 至 %s\n"
 
-#: builtin/mv.c builtin/remote.c builtin/repack.c
+#: builtin/mv.c builtin/remote.c
 #, c-format
 msgid "renaming '%s' failed"
 msgstr "重命名 '%s' 失败"
@@ -10679,11 +10767,13 @@ msgstr "git notes [--ref <注解引用>] [list [<对象>]]"
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <注解引用>] add [-f] [--allow-empty] [-m <说明> | -F <文件> "
-"| (-c | -C) <对象>] [<对象>]"
+"git notes [--ref <注解引用>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<分段符>] [--[no-]stripspace] [-m <说明> | -F <文件> | (-c | -C) <"
+"对象>] [<对象>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
@@ -10691,11 +10781,13 @@ msgstr "git notes [--ref <注解引用>] copy [-f] <源对象> <目标对象>"
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <注解引用>] append [--allow-empty] [-m <说明> | -F <文件> | "
-"(-c | -C) <对象>] [<对象>]"
+"git notes [--ref <注解引用>] append [--allow-empty] [--[no-]separator|--"
+"separator=<分段符>] [--[no-]stripspace] [-m <说明> | -F <文件> | (-c | -C) <"
+"对象>] [<对象>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
@@ -10821,11 +10913,6 @@ msgstr "无法读取对象 '%s'。"
 msgid "cannot read note data from non-blob object '%s'."
 msgstr "不能从非数据对象 '%s' 中读取注解数据。"
 
-#: builtin/notes.c
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "格式错误的输入行:'%s'。"
-
 #: builtin/notes.c
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
@@ -10868,6 +10955,18 @@ msgstr "允许保存空白注释"
 msgid "replace existing notes"
 msgstr "替换已存在的注解"
 
+#: builtin/notes.c
+msgid "<paragraph-break>"
+msgstr "<分段符>"
+
+#: builtin/notes.c
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "在段落之间插入<分段符>"
+
+#: builtin/notes.c
+msgid "remove unnecessary whitespace"
+msgstr "删除不必要的空白字符"
+
 #: builtin/notes.c
 #, c-format
 msgid ""
@@ -11058,14 +11157,13 @@ msgid "unknown subcommand: `%s'"
 msgstr "未知子命令:`%s'"
 
 #: builtin/pack-objects.c
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
-msgstr "git pack-objects --stdout [<选项>...] [< <引用列表> | < <对象列表>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
+msgstr "git pack-objects --stdout [<选项>] [< <引用列表> | < <对象列表>]"
 
 #: builtin/pack-objects.c
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
-msgstr "git pack-objects [<选项>...] <前缀名称> [< <引用列表> | < <对象列表>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
+msgstr "git pack-objects [<选项>] <前缀名称> [< <引用列表> | < <对象列表>]"
 
 #: builtin/pack-objects.c
 #, c-format
@@ -11127,7 +11225,7 @@ msgstr "无法写入位图索引"
 msgid "wrote %<PRIu32> objects while expecting %<PRIu32>"
 msgstr "写入 %<PRIu32> 个对象而预期 %<PRIu32> 个"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/repack.c
 msgid "disabling bitmap writing, as some objects are not being packed"
 msgstr "禁用 bitmap 写入,因为一些对象将不会被打包"
 
@@ -11293,6 +11391,14 @@ msgstr "不支持的索引版本 %s"
 msgid "bad index version '%s'"
 msgstr "坏的索引版本 '%s'"
 
+#: builtin/pack-objects.c
+msgid "show progress meter during object writing phase"
+msgstr "在对象写入阶段显示进度表"
+
+#: builtin/pack-objects.c
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "当进度表显示时类似于 --all-progress"
+
 #: builtin/pack-objects.c
 msgid "<version>[,<offset>]"
 msgstr "<版本>[,<偏移>]"
@@ -11484,10 +11590,6 @@ msgstr "最小的包文件大小是 1 MiB"
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin 不能用于创建一个可索引包"
 
-#: builtin/pack-objects.c
-msgid "cannot use --filter without --stdout"
-msgstr "不能在没有 --stdout 的情况下使用 --filter"
-
 #: builtin/pack-objects.c
 msgid "cannot use --filter with --stdin-packs"
 msgstr "不能同时使用 --filter 和 --stdin-packs"
@@ -11504,10 +11606,6 @@ msgstr "不能同时使用内部版本列表和 --cruft"
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "不能将 --stdin-packs 和 --cruft 同时使用"
 
-#: builtin/pack-objects.c
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "不能将 --max-pack-size 和 --cruft 同时使用"
-
 #: builtin/pack-objects.c
 msgid "Enumerating objects"
 msgstr "枚举对象中"
@@ -11534,9 +11632,16 @@ msgstr ""
 "并通过发送邮件到 <git@vger.kernel.org> 让我们知道您仍旧\n"
 "使用它。 谢谢。\n"
 
+#: builtin/pack-redundant.c
+msgid "refusing to run without --i-still-use-this"
+msgstr "拒绝在未指定 --i-still-use-this 选项时运行"
+
 #: builtin/pack-refs.c
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<选项>]"
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <模式>] [--exclude <模式>]"
 
 #: builtin/pack-refs.c
 msgid "pack everything"
@@ -11546,6 +11651,30 @@ msgstr "打包一切"
 msgid "prune loose refs (default)"
 msgstr "清除松散的引用(默认)"
 
+#: builtin/pack-refs.c
+msgid "references to include"
+msgstr "需包含的引用"
+
+#: builtin/pack-refs.c
+msgid "references to exclude"
+msgstr "需排除的引用"
+
+#: builtin/patch-id.c
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+#: builtin/patch-id.c
+msgid "use the unstable patch-id algorithm"
+msgstr "使用不稳定的 patch-id 算法"
+
+#: builtin/patch-id.c
+msgid "use the stable patch-id algorithm"
+msgstr "使用稳定的 patch-id 算法"
+
+#: builtin/patch-id.c
+msgid "don't strip whitespace from the patch"
+msgstr "不要从补丁中删除空白字符"
+
 #: builtin/prune.c
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr "git prune [-n] [-v] [--progress] [--expire <时间>] [--] [<head>...]"
@@ -11606,6 +11735,14 @@ msgstr "强制覆盖本地分支"
 msgid "number of submodules pulled in parallel"
 msgstr "并发拉取的子模组的数量"
 
+#: builtin/pull.c parse-options.h
+msgid "use IPv4 addresses only"
+msgstr "只使用 IPv4 地址"
+
+#: builtin/pull.c parse-options.h
+msgid "use IPv6 addresses only"
+msgstr "只使用 IPv6 地址"
+
 #: builtin/pull.c
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
@@ -11719,9 +11856,9 @@ msgstr "更新尚未诞生的分支,变更添加至索引。"
 msgid "pull with rebase"
 msgstr "变基式拉取"
 
-#: builtin/pull.c
-msgid "please commit or stash them."
-msgstr "请提交或贮藏它们。"
+#: builtin/pull.c builtin/rebase.c
+msgid "Please commit or stash them."
+msgstr "请提交或贮藏修改。"
 
 #: builtin/pull.c
 #, c-format
@@ -11793,9 +11930,8 @@ msgstr ""
 #: builtin/push.c
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
@@ -11888,36 +12024,36 @@ msgstr ""
 #: builtin/push.c
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为您当前分支的最新提交落后于其对应的远程分支。\n"
-"å\86\8d次æ\8e¨é\80\81å\89\8dï¼\8cå\85\88ä¸\8eè¿\9cç¨\8bå\8f\98æ\9b´å\90\88并ï¼\88å¦\82 'git pull ...'ï¼\89ã\80\82详è§\81\n"
-"'git push --help' 中的 'Note about fast-forwards' 小节。"
+"å¦\82æ\9e\9cæ\82¨å¸\8cæ\9c\9bå\85\88ä¸\8eè¿\9cç¨\8bå\8f\98æ\9b´å\90\88并ï¼\8c请å\9c¨æ\8e¨é\80\81å\89\8dæ\89§è¡\8c 'git pull'ã\80\82\n"
+"详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
 #: builtin/push.c
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为推送的一个分支的最新提交落后于其对应的远程分支。\n"
-"检出该分支并整合远程变更(如 'git pull ...'),然后再推送。详见\n"
-"'git push --help' 中的 'Note about fast-forwards' 小节。"
+"如果您希望先与远程变更合并,请在推送前执行 'git pull'。\n"
+"详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为远程仓库包含您本地尚不存在的提交。这通常是因为另外\n"
-"ä¸\80个ä»\93åº\93å·²å\90\91该å¼\95ç\94¨è¿\9bè¡\8cäº\86æ\8e¨é\80\81ã\80\82å\86\8d次æ\8e¨é\80\81å\89\8dï¼\8cæ\82¨å\8f¯è\83½é\9c\80è¦\81å\85\88æ\95´å\90\88è¿\9cç¨\8bå\8f\98æ\9b´\n"
-"(如 'git pull ...')。\n"
+"ä¸\80个ä»\93åº\93å·²å\90\91该å¼\95ç\94¨è¿\9bè¡\8cäº\86æ\8e¨é\80\81ã\80\82å¦\82æ\9e\9cæ\82¨å¸\8cæ\9c\9bå\85\88ä¸\8eè¿\9cç¨\8bå\8f\98æ\9b´å\90\88并ï¼\8c请å\9c¨æ\8e¨é\80\81\n"
+"前执行 'git pull'。\n"
 "详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
 #: builtin/push.c
@@ -11935,13 +12071,14 @@ msgstr ""
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为远程跟踪分支的最新提交自从上次检出之后已被更新。\n"
-"在强制更新前,您可能想将这些变更整合到本地(如 'git pull ...')。\n"
+"如果您希望先与远程变更合并,请在推送前执行 'git pull'。\n"
+"详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
 #: builtin/push.c
 #, c-format
@@ -11953,6 +12090,13 @@ msgstr "推送到 %s\n"
 msgid "failed to push some refs to '%s'"
 msgstr "无法推送一些引用到 '%s'"
 
+#: builtin/push.c
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"在 push.recurseSubmodules=only 时递归进入子模组;取而代之使用 on-demand"
+
 #: builtin/push.c builtin/send-pack.c submodule-config.c
 #, c-format
 msgid "invalid value for '%s'"
@@ -11962,9 +12106,9 @@ msgstr "'%s' 的值无效"
 msgid "repository"
 msgstr "仓库"
 
-#: builtin/push.c builtin/send-pack.c
-msgid "push all refs"
-msgstr "æ\8e¨é\80\81æ\89\80æ\9c\89å¼\95ç\94¨"
+#: builtin/push.c
+msgid "push all branches"
+msgstr "æ\8e¨é\80\81æ\89\80æ\9c\89å\88\86æ\94¯"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "mirror all refs"
@@ -11975,8 +12119,8 @@ msgid "delete refs"
 msgstr "删除引用"
 
 #: builtin/push.c
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "推送标签(不能使用 --all or --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr "推送标签(不能使用 --all or --branches or --mirror)"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "force updates"
@@ -12034,7 +12178,7 @@ msgstr "需要远端支持原子事务"
 msgid "--delete doesn't make sense without any refs"
 msgstr "--delete 未接任何引用没有意义"
 
-#: builtin/push.c
+#: builtin/push.c t/helper/test-bundle-uri.c
 #, c-format
 msgid "bad repository '%s'"
 msgstr "坏的仓库 '%s'"
@@ -12125,13 +12269,14 @@ msgstr "需要两个提交范围"
 
 #: builtin/read-tree.c
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<前缀>) [-"
-"u | -i]] [--no-sparse-checkout] [--index-output=<文件>] (--empty | <树对象一"
-"> [<树对象二> [<树对象三>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<前缀>)\n"
+"              [-u | -i]] [--index-output=<文件>] [--no-sparse-checkout]\n"
+"              (--empty | <树对象一> [<树对象二> [<树对象三>]])"
 
 #: builtin/read-tree.c
 msgid "write resulting index to <file>"
@@ -12248,8 +12393,8 @@ msgstr "%s 需要合并后端"
 
 #: builtin/rebase.c
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "æ\97 æ³\95è\8e·å\8f\96 'onto':'%s'"
+msgid "invalid onto: '%s'"
+msgstr "æ\97 æ\95\88æ\96°å\9fºçº¿:'%s'"
 
 #: builtin/rebase.c
 #, c-format
@@ -12297,11 +12442,20 @@ msgstr ""
 "\n"
 "因此 git 无法对其变基。"
 
+#: builtin/rebase.c
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "未知的变基合并模式:%s"
+
 #: builtin/rebase.c
 #, c-format
 msgid "could not switch to %s"
 msgstr "无法切换到 %s"
 
+#: builtin/rebase.c
+msgid "apply options and merge options cannot be used together"
+msgstr "应用选项和合并选项不能同时使用"
+
 #: builtin/rebase.c
 #, c-format
 msgid ""
@@ -12309,6 +12463,16 @@ msgid ""
 "\"ask\"."
 msgstr "无法识别的空类型 '%s';有效值有 \"drop\"、\"keep\" 和 \"ask\"。"
 
+#: builtin/rebase.c
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"为 --rebase-merges 指定空值参数的方式已经被废弃并且在 Git 未来\n"
+"的版本中将不再支持。 支持使用无参数 --rebase-merges 的方式进行替\n"
+"代,它们具有相同的作用。"
+
 #: builtin/rebase.c
 #, c-format
 msgid ""
@@ -12572,17 +12736,22 @@ msgid "switch `C' expects a numerical value"
 msgstr "开关 `C' 期望一个数字值"
 
 #: builtin/rebase.c
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "未知模式:%s"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr "应用的选项与 rebase.autoSquash 不兼容。考虑加上 --no-autosquash"
 
 #: builtin/rebase.c
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy 需要 --merge 或 --interactive"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr "应用的选项与 rebase.rebaseMerges 不兼容。考虑加上 --no-rebase-merges"
 
 #: builtin/rebase.c
-msgid "apply options and merge options cannot be used together"
-msgstr "应用选项和合并选项不能同时使用"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr "应用的选项与 rebase.updateRefs 不兼容。考虑加上 --no-update-refs"
 
 #: builtin/rebase.c
 #, c-format
@@ -12613,8 +12782,8 @@ msgid "No such ref: %s"
 msgstr "没有这样的引用:%s"
 
 #: builtin/rebase.c
-msgid "Could not resolve HEAD to a revision"
-msgstr "无法将 HEAD 解析为一个版本"
+msgid "Could not resolve HEAD to a commit"
+msgstr "不能解析 HEAD 至一个提交"
 
 #: builtin/rebase.c
 #, c-format
@@ -12631,10 +12800,6 @@ msgstr "'%s':只需要一个合并基线"
 msgid "Does not point to a valid commit '%s'"
 msgstr "没有指向一个有效的提交 '%s'"
 
-#: builtin/rebase.c
-msgid "Please commit or stash them."
-msgstr "请提交或贮藏修改。"
-
 #: builtin/rebase.c
 msgid "HEAD is up to date."
 msgstr "HEAD 是最新的。"
@@ -12938,12 +13103,10 @@ msgid "fetch the remote branches"
 msgstr "抓取远程的分支"
 
 #: builtin/remote.c
-msgid "import all tags and associated objects when fetching"
-msgstr "抓取时导入所有的标签和关联对象"
-
-#: builtin/remote.c
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "或不抓取任何标签(--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr "抓取时导入所有的标签和关联对象,或不抓取任何标签(--no-tags)"
 
 #: builtin/remote.c
 msgid "branch(es) to track"
@@ -13084,7 +13247,7 @@ msgstr " 已跳过"
 
 #: builtin/remote.c
 msgid " stale (use 'git remote prune' to remove)"
-msgstr " 过时(使用 'git remote prune' 来移除)"
+msgstr " 已过期(使用 'git remote prune' 来移除)"
 
 #: builtin/remote.c
 msgid " ???"
@@ -13415,6 +13578,16 @@ msgstr "无法打开临时文件 %s 进行写入"
 msgid "could not close refs snapshot tempfile"
 msgstr "不能关闭引用快照临时文件"
 
+#: builtin/repack.c
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "无法删除过期的位图: %s"
+
+#: builtin/repack.c
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "包前缀 %s 没有以对象目录 %s 开始"
+
 #: builtin/repack.c
 msgid "pack everything in a single pack"
 msgstr "所有内容打包到一个包文件中"
@@ -13432,8 +13605,8 @@ msgid "approxidate"
 msgstr "近似日期"
 
 #: builtin/repack.c
-msgid "with -C, expire objects older than this"
-msgstr "使用 -C,使早于给定时间的对象过期"
+msgid "with --cruft, expire objects older than this"
+msgstr "使用 --cruft,使早于给定时间的对象过期"
 
 #: builtin/repack.c
 msgid "remove redundant packs, and run git-prune-packed"
@@ -13511,25 +13684,38 @@ msgstr "使用因子 <n> 查找几何级数"
 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 "储存被过滤的对象的包的前缀"
+
 #: builtin/repack.c
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "不能删除珍品仓库中的打包文件"
 
+#: builtin/repack.c
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "选项 '%s' 只能和 '%s' 搭配使用"
+
 #: builtin/repack.c
 msgid "Nothing new to pack."
 msgstr "没有新的要打包。"
 
 #: builtin/repack.c
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "包前缀 %s 没有以对象目录 .%s 开始"
+msgid "renaming pack to '%s' failed"
+msgstr "重命名包至 '%s' 失败"
 
 #: builtin/repack.c
 #, c-format
-msgid "missing required file: %s"
-msgstr "缺少需要的文件:%s"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects 未为包 %2$s-%3$s 写入 '%1$s' 文件"
 
-#: builtin/repack.c
+#: builtin/repack.c sequencer.c
 #, c-format
 msgid "could not unlink: %s"
 msgstr "不能删除:%s"
@@ -13771,8 +13957,10 @@ msgid "only one pattern can be given with -l"
 msgstr "只能为 -l 提供一个模式"
 
 #: builtin/rerere.c
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forget <路径>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <路径规格>... | diff | status | remaining | gc]"
 
 #: builtin/rerere.c
 msgid "register clean resolutions in index"
@@ -14027,30 +14215,53 @@ msgstr "--prefix 需要一个参数"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "未知的 --abbrev-ref 模式:%s"
 
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden 不能与 --branches 一起使用"
+
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden 不能与 --tags 一起使用"
+
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden 不能与 --remotes 一起使用"
+
 #: builtin/rev-parse.c setup.c
 msgid "this operation must be run in a work tree"
 msgstr "该操作必须在一个工作区中运行"
 
+#: builtin/rev-parse.c
+msgid "Could not read the index"
+msgstr "不能读取索引"
+
 #: builtin/rev-parse.c
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "未知的 --show-object-format 模式:%s"
 
 #: builtin/revert.c
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<选项>] <提交号>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <父编号>] [-s] [-S[<私钥 ID>]] <提交>..."
 
 #: builtin/revert.c
-msgid "git revert <subcommand>"
-msgstr "git revert <子命令>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
 
 #: builtin/revert.c
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<选项>] <提交号>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <父编号>] [-s] [-x] [--ff]\n"
+"                [-S[<私钥 ID>]] <提交>..."
 
 #: builtin/revert.c
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <子命令>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
 
 #: builtin/revert.c
 #, c-format
@@ -14131,8 +14342,14 @@ msgid "cherry-pick failed"
 msgstr "拣选失败"
 
 #: builtin/rm.c
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<选项>] [--] <文件>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<文件> [--pathspec-file-nul]]\n"
+"       [--] [<路径规格>...]"
 
 #: builtin/rm.c
 msgid ""
@@ -14215,17 +14432,23 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<主机>:]<目录> (--all | <引用>...)"
 
 #: builtin/send-pack.c
 msgid "remote name"
 msgstr "远程名称"
 
+#: builtin/send-pack.c
+msgid "push all refs"
+msgstr "推送所有引用"
+
 #: builtin/send-pack.c
 msgid "use stateless RPC protocol"
 msgstr "使用无状态的 RPC 协议"
@@ -14251,8 +14474,9 @@ msgid "using multiple --group options with stdin is not supported"
 msgstr "不支持和标准输入一起使用多个 --group 选项"
 
 #: builtin/shortlog.c
-msgid "using --group=trailer with stdin is not supported"
-msgstr "不支持和标准输入一起使用 --group=trailer"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "不支持对 %s 使用标准输入"
 
 #: builtin/shortlog.c
 #, c-format
@@ -14300,12 +14524,14 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<何时>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<版本> | <通配符>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<版本> | <通配符>)...]"
 
 #: builtin/show-branch.c
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
@@ -14434,16 +14660,40 @@ msgstr "未知的哈希算法"
 
 #: builtin/show-ref.c
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<模式>...]"
+
+#: builtin/show-ref.c
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<模式>...]"
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<引用>...]"
 
 #: builtin/show-ref.c
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<模式>]"
 
+#: builtin/show-ref.c
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <引用>"
+
+#: builtin/show-ref.c
+msgid "reference does not exist"
+msgstr "引用不存在"
+
+#: builtin/show-ref.c
+msgid "failed to look up reference"
+msgstr "无法找到引用"
+
 #: builtin/show-ref.c
 msgid "only show tags (can be combined with heads)"
 msgstr "只显示标签(可以和头共用)"
@@ -14452,6 +14702,10 @@ msgstr "只显示标签(可以和头共用)"
 msgid "only show heads (can be combined with tags)"
 msgstr "只显示头(可以和标签共用)"
 
+#: builtin/show-ref.c
+msgid "check for reference existence without resolving"
+msgstr "检查引用是否存在但不解析"
+
 #: builtin/show-ref.c
 msgid "stricter reference checking, requires exact ref path"
 msgstr "更严格的引用检测,需要精确的引用路径"
@@ -14476,9 +14730,18 @@ msgstr "不打印结果到标准输出(例如与 --verify 参数共用)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "显示从标准输入中读入的不在本地仓库中的引用"
 
+#: builtin/show-ref.c
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "选项 '%s'、'%s' 或 '%s' 只能使用其一"
+
 #: builtin/sparse-checkout.c
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) <选项>"
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check\n"
+"-rules) [<选项>]"
 
 #: builtin/sparse-checkout.c
 msgid "this worktree is not sparse"
@@ -14618,79 +14881,86 @@ msgstr "必须在稀疏检出中重应用稀疏模式"
 msgid "error while refreshing working directory"
 msgstr "刷新工作目录时出错"
 
-#: builtin/stash.c
-msgid "git stash list [<options>]"
-msgstr "git stash list [<选项>]"
+#: builtin/sparse-checkout.c
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <文件>]"
 
-#: builtin/stash.c
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<选项>] [<stash>]"
+#: builtin/sparse-checkout.c
+msgid "terminate input and output files by a NUL character"
+msgstr "输入和输出的文件使用 NUL 字符终结"
 
-#: builtin/stash.c
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<stash>]"
+#: builtin/sparse-checkout.c
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr "通过 --rules-file 选项传递的模型将被作为锥形(稀疏检出模型)进行解析"
 
-#: builtin/stash.c
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
+#: builtin/sparse-checkout.c
+msgid "use patterns in <file> instead of the current ones."
+msgstr "从 <文件> 参数中读取模式,而不是从标准输入"
 
 #: builtin/stash.c
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <分支名> [<stash>]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<日志选项>]"
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [[-S|--staged] -k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <消息>]\n"
-"          [--pathspec-from-file=<文件> [--pathspec-file-nul]]\n"
-"          [--] [<路径规格>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<差异选项>] [<"
+"贮存>]"
 
 #: builtin/stash.c
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<消息>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<贮存>]"
 
 #: builtin/stash.c
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<stash>]"
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<贮存>]"
 
 #: builtin/stash.c
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<stash>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<贮存>]"
+
+#: builtin/stash.c
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <分支名> [<stash>]"
 
 #: builtin/stash.c
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <消息>] [-q|--quiet] <提交>"
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr "git stash store [(-m | --message) <消息>] [-q | --quiet] <提交>"
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <消息>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message <消息>]\n"
+"          [--pathspec-from-file=<文件> [--pathspec-file-nul]]\n"
 "          [--] [<路径规格>...]]"
 
 #: builtin/stash.c
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<消息>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<消息>]"
+
+#: builtin/stash.c
+msgid "git stash create [<message>]"
+msgstr "git stash create [<消息>]"
 
 #: builtin/stash.c
 #, c-format
@@ -15264,6 +15534,11 @@ msgstr "略过未合并的子模组 %s"
 msgid "Skipping submodule '%s'"
 msgstr "略过子模组 '%s'"
 
+#: builtin/submodule--helper.c
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "无法在不提供 URL 时克隆子模组 '%s'"
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
@@ -15378,10 +15653,6 @@ msgstr "递归遍历子模组"
 msgid "don't fetch new objects from the remote site"
 msgstr "不要从远程地址获取新对象"
 
-#: builtin/submodule--helper.c
-msgid "path into the working tree"
-msgstr "到工作区的路径"
-
 #: builtin/submodule--helper.c
 msgid "use the 'checkout' update strategy (default)"
 msgstr "使用 'checkout' 更新策略(默认)"
@@ -15426,33 +15697,13 @@ msgstr ""
 "shallow] [--reference <仓库>] [--recursive] [--[no-]single-branch] [--] [<路"
 "径>...]"
 
-#: builtin/submodule--helper.c
-msgid "recurse into submodules"
-msgstr "在子模组中递归"
-
-#: builtin/submodule--helper.c
-msgid "git submodule absorbgitdirs [<options>] [<path>...]"
-msgstr "git submodule absorbgitdirs [<选项>] [<路径>...]"
-
-#: builtin/submodule--helper.c
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr "检查写入 .gitmodules 文件是否安全"
-
-#: builtin/submodule--helper.c
-msgid "unset the config in the .gitmodules file"
-msgstr "取消 .gitmodules 文件中的设置"
-
-#: builtin/submodule--helper.c
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <名称> [<值>]"
-
-#: builtin/submodule--helper.c
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <名称>"
+#: builtin/submodule--helper.c submodule.c
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "无法将 HEAD 解析为有效引用。"
 
 #: builtin/submodule--helper.c
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr "请确认 .gitmodules 文件在工作区里"
+msgid "git submodule absorbgitdirs [<options>] [<path>...]"
+msgstr "git submodule absorbgitdirs [<选项>] [<路径>...]"
 
 #: builtin/submodule--helper.c
 msgid "suppress output for setting url of a submodule"
@@ -15547,6 +15798,10 @@ msgstr "为子模组 '%s' 重新激活本地 git 目录\n"
 msgid "unable to checkout submodule '%s'"
 msgstr "无法检出子模组 '%s'"
 
+#: builtin/submodule--helper.c
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr "请确认 .gitmodules 文件在工作区里"
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "Failed to add submodule '%s'"
@@ -15608,23 +15863,21 @@ msgstr "仓库 URL:'%s' 必须是绝对路径或以 ./|../ 起始"
 msgid "'%s' is not a valid submodule name"
 msgstr "'%s' 不是一个有效的子模组名称"
 
-#: builtin/submodule--helper.c git.c
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s 不支持 --super-prefix"
-
 #: builtin/submodule--helper.c
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "'%s' 不是一个有效的 submodule--helper 子命令"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <命令>"
 
 #: builtin/symbolic-ref.c
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<选项>] <名称> [<引用>]"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <理由>] <名称> <引用>"
 
 #: builtin/symbolic-ref.c
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <名称>"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <名称>"
+
+#: builtin/symbolic-ref.c
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <名称>"
 
 #: builtin/symbolic-ref.c
 msgid "suppress error message for non-symbolic (detached) refs"
@@ -15638,6 +15891,10 @@ msgstr "删除符号引用"
 msgid "shorten ref output"
 msgstr "缩短引用输出"
 
+#: builtin/symbolic-ref.c
+msgid "recursively dereference (default)"
+msgstr "递归解引用(默认)"
+
 #: builtin/symbolic-ref.c builtin/update-ref.c
 msgid "reason"
 msgstr "原因"
@@ -15648,11 +15905,11 @@ msgstr "更新的原因"
 
 #: builtin/tag.c
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <消息> | -F <文件>]\n"
-"        <标签名> [<>]"
+"git tag [-a | -s | -u <私钥 ID>] [-f] [-m <消息> | -F <文件>] [-e]\n"
+"        <标签名> [<提交> | <对象>]"
 
 #: builtin/tag.c
 msgid "git tag -d <tagname>..."
@@ -15660,14 +15917,15 @@ msgstr "git tag -d <标签名>..."
 
 #: builtin/tag.c
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[<数字>]] [--contains <提交>] [--no-contains <提交>] [--points-"
-"at <对象>]\n"
-"        [--format=<格式>] [--merged <提交>] [--no-merged <提交>] [<模式>...]"
+"git tag [-n[<数字>]] -l [--contains <提交>] [--no-contains <提交>]\n"
+"        [--points-at <对象>] [--column[=<选项>] | --no-column]\n"
+"        [--create-reflog] [--sort=<键>] [--format=<格式>]\n"
+"        [--merged <提交>] [--no-merged <提交>] [<模式>...]"
 
 #: builtin/tag.c
 msgid "git tag -v [--format=<format>] <tagname>..."
@@ -16018,6 +16276,10 @@ msgstr "(for porcelains) 忘记保存的未解决的冲突"
 msgid "write index in this format"
 msgstr "以这种格式写入索引区"
 
+#: builtin/update-index.c
+msgid "report on-disk index format version"
+msgstr "报告磁盘索引格式的版本"
+
 #: builtin/update-index.c
 msgid "enable or disable split index"
 msgstr "启用或禁用索引拆分"
@@ -16050,6 +16312,16 @@ msgstr "标记文件为 fsmonitor 有效"
 msgid "clear fsmonitor valid bit"
 msgstr "清除 fsmonitor 有效位"
 
+#: builtin/update-index.c
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#: builtin/update-index.c
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "索引版本:从 %d 设置为 %d"
+
 #: builtin/update-index.c
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
@@ -16139,8 +16411,12 @@ msgid "update the info files from scratch"
 msgstr "从头开始更新文件信息"
 
 #: builtin/upload-pack.c
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<选项>] <目录>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <目录>"
 
 #: builtin/upload-pack.c t/helper/test-serve-v2.c
 msgid "quit after a single request/response exchange"
@@ -16159,8 +16435,8 @@ msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "不活动 <n> 秒钟后终止传输"
 
 #: builtin/verify-commit.c
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <提交>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | --verbose] [--raw] <提交>..."
 
 #: builtin/verify-commit.c
 msgid "print commit contents"
@@ -16171,8 +16447,8 @@ msgid "print raw gpg status output"
 msgstr "打印原始 gpg 状态输出"
 
 #: builtin/verify-pack.c
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <包>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <包>.idx..."
 
 #: builtin/verify-pack.c
 msgid "verbose"
@@ -16183,44 +16459,82 @@ msgid "show statistics only"
 msgstr "只显示统计"
 
 #: builtin/verify-tag.c
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<格式>] <标签>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<格式>] [--raw] <标签>..."
 
 #: builtin/verify-tag.c
 msgid "print tag contents"
 msgstr "打印标签内容"
 
 #: builtin/worktree.c
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<选项>] <路径> [<提交>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <字符串>]]\n"
+"                 [--orphan] [(-b | -B) <新分支>] <路径> [<提交号>]"
 
 #: builtin/worktree.c
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<选项>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
 
 #: builtin/worktree.c
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<选项>] <路径>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <字符串>] <工作区>"
 
 #: builtin/worktree.c
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <工作区> <新路径>"
 
 #: builtin/worktree.c
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<选项>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <过期>]"
 
 #: builtin/worktree.c
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<选项>] <工作区>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <工作区>"
 
 #: builtin/worktree.c
 msgid "git worktree repair [<path>...]"
 msgstr "git worktree repair [<路径>...]"
 
 #: builtin/worktree.c
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <路径>"
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <工作区>"
+
+#: builtin/worktree.c
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "没有可用的源分支,将基于 '--orphan' 选项进行推断"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"如果你打算为此仓库创建一个包含新的孤立分支\n"
+"(没有提交的分支)的工作区,你可以使用选项\n"
+"--orphan 来执行此操作:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"如果你打算为此仓库创建一个包含新的孤立分支\n"
+"(没有提交的分支)的工作区,你可以使用选项\n"
+"--orphan 来执行此操作:\n"
+"\n"
+"    git worktree add --orphan %s\n"
 
 #: builtin/worktree.c
 #, c-format
@@ -16302,11 +16616,40 @@ msgstr "准备工作区(重置分支 '%s',之前为 %s)"
 msgid "Preparing worktree (checking out '%s')"
 msgstr "准备工作区(检出 '%s')"
 
+#: builtin/worktree.c
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "不可达:无效引用:%s"
+
 #: builtin/worktree.c
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "准备工作区(分离头指针 %s)"
 
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD 指向了一个无用的(或孤立的)引用。\n"
+"HEAD 路径: '%s'\n"
+"HEAD 内容: '%s'"
+
+#: builtin/worktree.c
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"尽管已配置远程仓库,但不存在任何本地的或远程的引用,操作终止。\n"
+"请先使用 'add -f' 来覆盖或拉取一个远程仓库"
+
+#: builtin/worktree.c
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "'%s' 和 '%s' 不能同时使用"
+
 #: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "检出 <分支>,即使已经被检出到其它工作区"
@@ -16319,6 +16662,10 @@ msgstr "创建一个新分支"
 msgid "create or reset a branch"
 msgstr "创建或重置一个分支"
 
+#: builtin/worktree.c
+msgid "create unborn/orphaned branch"
+msgstr "创建一个尚未诞生的/孤立的分支"
+
 #: builtin/worktree.c
 msgid "populate the new working tree"
 msgstr "生成新的工作区"
@@ -16344,6 +16691,15 @@ msgstr "尝试为新分支名匹配一个远程跟踪分支"
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "选项 '%s'、'%s' 和 '%s' 不能同时使用"
 
+#: builtin/worktree.c
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "选项 '%s' 与 '%s' 不能同时使用"
+
+#: builtin/worktree.c
+msgid "<commit-ish>"
+msgstr "<提交号>"
+
 #: builtin/worktree.c
 msgid "added with --lock"
 msgstr "由 --lock 添加"
@@ -16504,6 +16860,16 @@ msgstr "只对调试有用"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "core.fsyncMethod = batch 不支持本平台"
 
+#: bundle-uri.c
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "不能解析归档包列表键 %s 的值 '%s'"
+
+#: bundle-uri.c
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "在 '%s' 的归档包列表没有模式"
+
 #: bundle-uri.c
 msgid "failed to create temporary file"
 msgstr "无法创建临时文件"
@@ -16512,6 +16878,25 @@ msgstr "无法创建临时文件"
 msgid "insufficient capabilities"
 msgstr "缺乏能力"
 
+#: bundle-uri.c
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "下载自 '%s' 的文件不是归档包"
+
+#: bundle-uri.c
+msgid "failed to store maximum creation token"
+msgstr "无法储存最大的创建令牌"
+
+#: bundle-uri.c
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "不可辨认的归档包模式来自 URI '%s'"
+
+#: bundle-uri.c
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "超过了 URI 递归限制 (%d)"
+
 #: bundle-uri.c
 #, c-format
 msgid "failed to download bundle from URI '%s'"
@@ -16519,13 +16904,29 @@ msgstr "无法从 URI '%s' 下载归档包"
 
 #: bundle-uri.c
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "位于 URI '%s' 的文件不是归档包"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "位于 URI '%s' 的文件不是归档包或归档包列表"
 
 #: bundle-uri.c
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "无法从 URI '%s' 解开归档包"
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: 意外的参数:'%s'"
+
+#: bundle-uri.c
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: 在参数之后应有一个 flush"
+
+#: bundle-uri.c
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: 获得了空行"
+
+#: bundle-uri.c
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: 行不是以 'key=value' 格式"
+
+#: bundle-uri.c
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: 行有空的键或值"
 
 #: bundle.c
 #, c-format
@@ -16555,6 +16956,12 @@ msgstr "仓库中缺少这些必备的提交:"
 msgid "need a repository to verify a bundle"
 msgstr "需要一个仓库来校验一个归档包"
 
+#: bundle.c
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr "一些前置提交存在于对象储存中,但未连接于本仓库的历史"
+
 #: bundle.c
 #, c-format
 msgid "The bundle contains this ref:"
@@ -16573,6 +16980,16 @@ msgid_plural "The bundle requires these %<PRIuMAX> refs:"
 msgstr[0] "这个归档包需要这个引用:"
 msgstr[1] "这个归档包需要 %<PRIuMAX> 个引用:"
 
+#: bundle.c
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "该归档包使用的哈希算法:%s"
+
+#: bundle.c
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "归档包使用了过滤器:%s"
+
 #: bundle.c
 msgid "unable to dup bundle descriptor"
 msgstr "无法复制归档包描述符"
@@ -16617,6 +17034,11 @@ msgstr "index-pack 终止"
 msgid "terminating chunk id appears earlier than expected"
 msgstr "终止块 ID 比预期更早出现"
 
+#: chunk-format.c
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "块 id %<PRIx32> 未 %d 字节对齐"
+
 #: chunk-format.c
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
@@ -16686,8 +17108,8 @@ msgid "Move objects and refs by archive"
 msgstr "通过归档移动对象和引用"
 
 #: command-list.h
-msgid "Provide content or type and size information for repository objects"
-msgstr "提供仓库对象的内容、类型或大小"
+msgid "Provide contents or details of repository objects"
+msgstr "提供仓库对象的内容或详情"
 
 #: command-list.h
 msgid "Display gitattributes information"
@@ -16870,8 +17292,8 @@ msgid "A portable graphical interface to Git"
 msgstr "一个便携的 Git 图形客户端"
 
 #: command-list.h
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "从一个文件计算对象 ID,并可以创建 blob 数据对象"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "从一个文件计算对象 ID,并支持可选地创建一个对象"
 
 #: command-list.h
 msgid "Display help information about Git"
@@ -17228,7 +17650,7 @@ msgid "Display version information about Git"
 msgstr "显示关于 Git 的版本信息"
 
 #: command-list.h
-msgid "Show logs with difference each commit introduces"
+msgid "Show logs with differences each commit introduces"
 msgstr "显示每一个提交引入的差异日志"
 
 #: command-list.h
@@ -17280,7 +17702,7 @@ msgid "Chunk-based file formats"
 msgstr "块式文件格式"
 
 #: command-list.h
-msgid "Git commit graph format"
+msgid "Git commit-graph format"
 msgstr "Git 提交图格式"
 
 #: command-list.h
@@ -17407,10 +17829,19 @@ msgstr "提交图形文件太小,容不下 %u 个块"
 msgid "commit-graph has no base graphs chunk"
 msgstr "提交图形没有基础图形块"
 
+#: commit-graph.c
+msgid "commit-graph base graphs chunk is too small"
+msgstr "提交图形的基础图形块过小"
+
 #: commit-graph.c
 msgid "commit-graph chain does not match"
 msgstr "提交图形链不匹配"
 
+#: commit-graph.c
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "基础图形中的提交数量过高:%<PRIuMAX>"
+
 #: commit-graph.c
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
@@ -17433,6 +17864,10 @@ msgstr "无法找到提交 %s"
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "提交图需要溢出世代数据,但是没有"
 
+#: commit-graph.c
+msgid "commit-graph overflow generation data is too small"
+msgstr "提交图溢出世代数据过小"
+
 #: commit-graph.c
 msgid "Loading known commits in commit graph"
 msgstr "正在加载提交图中的已知提交"
@@ -17518,6 +17953,17 @@ msgstr "无法重命名基础提交图形文件"
 msgid "failed to rename temporary commit-graph file"
 msgstr "无法重命名临时提交图形文件"
 
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr ""
+"无法合并提交图形,总共已累加提交数:%<PRIuMAX>,当前待累加提交数:%<PRIuMAX>"
+
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "无法合并提交图形 %s, 提交过多:%<PRIuMAX>"
+
 #: commit-graph.c
 msgid "Scanning merged commits"
 msgstr "正在扫描合并提交"
@@ -17553,10 +17999,6 @@ msgstr "提交图形有不正确的扇出值:fanout[%d] = %u != %u"
 msgid "failed to parse commit %s from commit-graph"
 msgstr "无法从提交图形中解析提交 %s"
 
-#: commit-graph.c
-msgid "Verifying commits in commit graph"
-msgstr "正在校验提交图中的提交"
-
 #: commit-graph.c
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
@@ -17584,25 +18026,24 @@ msgstr "提交 %s 的提交图形父提交列表过早终止"
 
 #: commit-graph.c
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr "提交图形中提交 %s 的世代号是零,但其它地方非零"
+msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
+msgstr "提交图形中的提交 %s 的世代号是 %<PRIuMAX> < %<PRIuMAX>"
 
 #: commit-graph.c
 #, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr "提交图形中提交 %s 的世代号非零,但其它地方是零"
+msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
+msgstr "提交图形中提交 %s 的提交日期是 %<PRIuMAX> != %<PRIuMAX>"
 
 #: commit-graph.c
 #, c-format
-msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
-msgstr "提交图形中的提交 %s 的世代号是 %<PRIuMAX> < %<PRIuMAX>"
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr "提交图形具有零和非零的世代(例如:提交 '%s' 和 '%s')"
 
 #: commit-graph.c
-#, c-format
-msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
-msgstr "提交图形中提交 %s 的提交日期是 %<PRIuMAX> != %<PRIuMAX>"
+msgid "Verifying commits in commit graph"
+msgstr "正在校验提交图中的提交"
 
 #: commit.c
 #, c-format
@@ -17629,6 +18070,11 @@ msgstr ""
 "设置 \"git config advice.graftFileDeprecated false\"\n"
 "可关闭本消息"
 
+#: commit.c
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr "提交 %s 存在于提交图形中,但不存在于对象数据库中"
+
 #: commit.c
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
@@ -17707,6 +18153,11 @@ msgstr "'has_worktree_moved' 中未处理的情况:%d"
 msgid "health thread wait failed [GLE %ld]"
 msgstr "健康监测线程等待失败 [GLE %ld]"
 
+#: compat/fsmonitor/fsm-ipc-darwin.c
+#, c-format
+msgid "Invalid path: %s"
+msgstr "无效路径: %s"
+
 #: compat/fsmonitor/fsm-listen-darwin.c
 msgid "Unable to create FSEventStream."
 msgstr "无法创建 FSEventStream。"
@@ -17745,12 +18196,32 @@ msgstr "GetOverlappedResult 失败于 '%s' [GLE %ld]"
 msgid "could not read directory changes [GLE %ld]"
 msgstr "无法获取目录变更 [GLE %ld]"
 
-#: compat/fsmonitor/fsm-settings-win32.c
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "opendir('%s') 失败"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "lstat('%s') 失败"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "strbuf_readlink('%s') 失败"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "closedir('%s') 失败"
+
+#: compat/fsmonitor/fsm-path-utils-win32.c
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
 msgstr "[GLE %ld] 无法打开要读取的 '%ls'"
 
-#: compat/fsmonitor/fsm-settings-win32.c
+#: compat/fsmonitor/fsm-path-utils-win32.c
 #, c-format
 msgid "[GLE %ld] unable to get protocol information for '%ls'"
 msgstr "[GLE %ld] 无法获取 '%ls' 的协议信息 "
@@ -18101,8 +18572,8 @@ msgid "bad zlib compression level %d"
 msgstr "错误的 zlib 压缩级别 %d"
 
 #: config.c
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar 应该是一个字符"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar 应该是一个 ASCII 编码的字符"
 
 #: config.c
 #, c-format
@@ -18147,11 +18618,6 @@ msgstr "引用 '%s' 没有指向一个数据对象"
 msgid "unable to resolve config blob '%s'"
 msgstr "不能解析配置对象 '%s'"
 
-#: config.c
-#, c-format
-msgid "failed to parse %s"
-msgstr "无法解析 %s"
-
 #: config.c
 msgid "unable to parse command-line config"
 msgstr "无法解析命令行中的配置"
@@ -18240,6 +18706,11 @@ msgstr "不能设置 '%s' 为 '%s'"
 msgid "invalid section name: %s"
 msgstr "无效的小节名称:%s"
 
+#: config.c
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "拒绝支持内容过长的行,位于文件 '%s' 中的第 %<PRIuMAX> 行"
+
 #: config.c
 #, c-format
 msgid "missing value for '%s'"
@@ -18308,17 +18779,26 @@ msgstr "服务器给出未知的对象格式 '%s'"
 
 #: connect.c
 #, c-format
-msgid "invalid ls-refs response: %s"
-msgstr "无效的 ls-refs 响应:%s"
+msgid "error on bundle-uri response line %d: %s"
+msgstr "于 bundle-uri 响应行 %d 发生错误:%s"
 
 #: connect.c
-msgid "expected flush after ref listing"
-msgstr "在引用列表之后应该有一个 flush 包"
+msgid "expected flush after bundle-uri listing"
+msgstr "在 bundle-uri 列表之后应该有一个 flush 包"
 
 #: connect.c
 msgid "expected response end packet after ref listing"
 msgstr "在引用列表之后应该有响应结束包"
 
+#: connect.c
+#, c-format
+msgid "invalid ls-refs response: %s"
+msgstr "无效的 ls-refs 响应:%s"
+
+#: connect.c
+msgid "expected flush after ref listing"
+msgstr "在引用列表之后应该有一个 flush 包"
+
 #: connect.c
 #, c-format
 msgid "protocol '%s' is not supported"
@@ -18705,10 +19185,6 @@ msgstr "无法写入归档"
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base 不适用于范围"
 
-#: diff-lib.c
-msgid "--merge-base only works with commits"
-msgstr "--merge-base 仅适用于提交"
-
 #: diff-lib.c
 msgid "unable to get HEAD"
 msgstr "不能解析 HEAD"
@@ -18721,6 +19197,14 @@ msgstr "未找到合并基线"
 msgid "multiple merge bases found"
 msgstr "找到了多条合并基线"
 
+#: diff-no-index.c
+msgid "cannot compare stdin to a directory"
+msgstr "无法将标准输入和目录进行比较"
+
+#: diff-no-index.c
+msgid "cannot compare a named pipe to a directory"
+msgstr "无法将命名管道和目录进行比较"
+
 #: diff-no-index.c
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<选项>] <路径> <路径>"
@@ -18785,6 +19269,15 @@ msgstr ""
 msgid "external diff died, stopping at %s"
 msgstr "外部 diff 退出,停止在 %s"
 
+#: diff.c
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow 明确要求只跟一个路径规格"
+
+#: diff.c
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "指定 --follow: %s 的同时,不支持在路径规格中使用神奇前缀"
+
 #: diff.c parse-options.c
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
@@ -18801,10 +19294,6 @@ msgid ""
 "options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"
 msgstr "选项 '%1$s'、'%2$s' 不能同时使用,与 '%4$s' 和 '%5$s' 一起使用 '%3$s'"
 
-#: diff.c
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow 明确要求只跟一个路径规格"
-
 #: diff.c
 #, c-format
 msgid "invalid --stat value: %s"
@@ -19041,6 +19530,10 @@ msgstr "输出的每一行附加前缀"
 msgid "do not show any source or destination prefix"
 msgstr "不显示任何源和目标前缀"
 
+#: diff.c
+msgid "use default prefixes a/ and b/"
+msgstr "使用 a/ 和 b/ 作为默认前缀"
+
 #: diff.c
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "显示指定行数的差异块间的上下文"
@@ -19410,6 +19903,16 @@ msgstr "不能从 '%s' 迁移 git 目录到 '%s'"
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "提示:等待您的编辑器关闭文件...%c"
 
+#: editor.c sequencer.c wrapper.c
+#, c-format
+msgid "could not write to '%s'"
+msgstr "不能写入 '%s'"
+
+#: editor.c
+#, c-format
+msgid "could not edit '%s'"
+msgstr "不能编辑 '%s'"
+
 #: entry.c
 msgid "Filtering content"
 msgstr "过滤内容"
@@ -19719,8 +20222,9 @@ msgstr "虚拟仓库 '%s' 与 fsmonitor 不兼容"
 #: fsmonitor-settings.c
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
-msgstr "因为缺少 Unix 套接字,仓库 '%s' 与 fsmonitor 不兼容"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
+msgstr "因为缺少 Unix 套接字支持,套接字目录 '%s' 与 fsmonitor 不兼容"
 
 #: git.c
 msgid ""
@@ -19729,16 +20233,14 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
-"git [-v | --version] [--help] [-C <路径>] [-c <名称>=<取值>]\n"
+"git [-v | --version] [-h | --help] [-C <路径>] [-c <名称>=<取值>]\n"
 "           [--exec-path[=<路径>]] [--html-path] [--man-path] [--info-path]\n"
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<路径>] [--work-tree=<路径>] [--namespace=<名称>]\n"
-"           [--super-prefix=<路径>] [--config-env=<名称>=<环境变量>]\n"
-"           <命令> [<参数>]"
+"           [--config-env=<名称>=<环境变量>] <命令> [<参数>]"
 
 #: git.c
 msgid ""
@@ -19767,11 +20269,6 @@ msgstr "没有为 '%s' 选项提供目录\n"
 msgid "no namespace given for --namespace\n"
 msgstr "没有为 --namespace 提供命名空间\n"
 
-#: git.c
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "没有为 --super-prefix 提供前缀\n"
-
 #: git.c
 #, c-format
 msgid "-c expects a configuration string\n"
@@ -19782,6 +20279,11 @@ msgstr "应为 -c 提供一个配置字符串\n"
 msgid "no config key given for --config-env\n"
 msgstr "没有为 --config-env 提供配置名称\n"
 
+#: git.c
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "没有为 --attr-source 提供属性来源\n"
+
 #: git.c
 #, c-format
 msgid "unknown option: %s\n"
@@ -19906,8 +20408,13 @@ msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "gpg.ssh.defaultKeyCommand 失败:%s %s"
 
 #: gpg-interface.c
-msgid "gpg failed to sign the data"
-msgstr "gpg 无法为数据签名"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg 无法为数据签名:\n"
+"%s"
 
 #: gpg-interface.c
 msgid "user.signingKey needs to be set for ssh signing"
@@ -20107,8 +20614,8 @@ msgstr[1] ""
 "最相似的命令是"
 
 #: help.c
-msgid "git version [<options>]"
-msgstr "git version [<选项>]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #: help.c
 #, c-format
@@ -20401,12 +20908,12 @@ msgstr ""
 "%s"
 
 #: merge-ort.c merge-recursive.c
-msgid "Failed to execute internal merge"
+msgid "failed to execute internal merge"
 msgstr "无法执行内部合并"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
-msgid "Unable to add %s to database"
+msgid "unable to add %s to database"
 msgstr "不能添加 %s 至对象库"
 
 #: merge-ort.c merge-recursive.c
@@ -20902,7 +21409,15 @@ msgstr "无法读取缓存"
 
 #: midx.c
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "多包索引的对象ID扇出表大小错误"
+msgstr "多包索引的对象 ID 扇出表大小错误"
+
+#: midx.c
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "多包索引的对象 ID 查询块大小错误"
+
+#: midx.c
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "多包索引的对象 ID 偏移块大小错误"
 
 #: midx.c
 #, c-format
@@ -20925,20 +21440,24 @@ msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "多包索引哈希版本 %u 和版本 %u 不匹配"
 
 #: midx.c
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "多包索引缺少必需的包名块"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "多包索引必需的包名块缺失或损坏"
 
 #: midx.c
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "多包索引缺少必需的对象 ID 扇出块"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr "多包索引必需的对象 ID 扇出块缺失或损坏"
 
 #: midx.c
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "多包索引缺少必需的对象 ID 查询块"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr "多包索引必需的对象 ID 查询块缺失或损坏"
 
 #: midx.c
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "多包索引缺少必需的对象偏移块"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr "多包索引必需的对象偏移块缺少或损坏"
+
+#: midx.c
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "多包索引包名块过短"
 
 #: midx.c
 #, c-format
@@ -20954,6 +21473,10 @@ msgstr "错的 pack-int-id:%u(共有 %u 个包)"
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr "多包索引存储一个64位偏移,但是 off_t 太小"
 
+#: midx.c
+msgid "multi-pack-index large offset out of bounds"
+msgstr "多包索引大偏移区越界"
+
 #: midx.c
 #, c-format
 msgid "failed to add packfile '%s'"
@@ -21180,11 +21703,6 @@ msgstr "无法规范化备用对象路径:%s"
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s:忽略备用对象库,嵌套太深"
 
-#: object-file.c
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "无法规范化对象目录: %s"
-
 #: object-file.c
 msgid "unable to fdopen alternates lockfile"
 msgstr "无法 fdopen 替换锁文件"
@@ -21257,6 +21775,11 @@ msgstr "损坏的松散对象 '%s'"
 msgid "garbage at end of loose object '%s'"
 msgstr "松散对象 '%s' 后面有垃圾数据"
 
+#: object-file.c
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "无法打开松散对象 %s"
+
 #: object-file.c
 #, c-format
 msgid "unable to parse %s header"
@@ -21278,19 +21801,14 @@ msgstr "%s 的头部太长,超出了 %d 字节"
 
 #: object-file.c
 #, c-format
-msgid "failed to read object %s"
-msgstr "æ\97 æ³\95读å\8f\96对象 %s"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "æ\9d¾æ\95£å¯¹è±¡ %sï¼\88ä¿\9då­\98å\9c¨ %sï¼\89å·²æ\8d\9få\9d\8f"
 
 #: object-file.c
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "找不到 %2$s 的替代 %1$s"
 
-#: object-file.c
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "松散对象 %s(保存在 %s)已损坏"
-
 #: object-file.c
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
@@ -21306,10 +21824,6 @@ msgstr "无法写文件 %s"
 msgid "unable to set permission to '%s'"
 msgstr "无法为 '%s' 设置权限"
 
-#: object-file.c
-msgid "file write error"
-msgstr "文件写错误"
-
 #: object-file.c
 msgid "error when closing loose object file"
 msgstr "关闭松散对象文件时出错"
@@ -21368,12 +21882,13 @@ msgid "cannot read object for %s"
 msgstr "不能读取对象 %s"
 
 #: object-file.c
-msgid "corrupt commit"
-msgstr "损坏的提交"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "对象 fsck 失败:%s"
 
 #: object-file.c
-msgid "corrupt tag"
-msgstr "æ\8d\9få\9d\8fç\9a\84æ \87ç­¾"
+msgid "refusing to create malformed object"
+msgstr "æ\8b\92ç»\9då\88\9b建格å¼\8fé\94\99误ç\9a\84对象"
 
 #: object-file.c
 #, c-format
@@ -21683,11 +22198,6 @@ msgstr "位图包索引中 XOR 偏移值无效"
 msgid "cannot fstat bitmap file"
 msgstr "不能对位图文件调用 fstat"
 
-#: pack-bitmap.c
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "忽略额外的位图文件:'%s'"
-
 #: pack-bitmap.c
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "MIDX 和位图中的校验码不匹配"
@@ -21771,6 +22281,11 @@ msgstr "无法在包 '%2$s' 偏移 %3$<PRIuMAX> 中找到 '%1$s'"
 msgid "unable to get disk usage of '%s'"
 msgstr "无法得到 '%s' 的磁盘使用量"
 
+#: pack-bitmap.c
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "位图文件 '%s' 有无效的校验码"
+
 #: pack-mtimes.c
 #, c-format
 msgid "mtimes file %s is too small"
@@ -21821,6 +22336,19 @@ msgstr "反向索引文件 %s 不支持的版本 %<PRIu32>"
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "反向索引文件 %s 有不支持的哈希 ID %<PRIu32>"
 
+#: pack-revindex.c
+msgid "invalid checksum"
+msgstr "无效的校验码 %s"
+
+#: pack-revindex.c
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "位于 %<PRIu64> 的无效的反向索引:%<PRIu32> != %<PRIu32>"
+
+#: pack-revindex.c
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "多包索引的反向索引块大小错误"
+
 #: pack-write.c
 msgid "cannot both write and verify reverse index"
 msgstr "无法同时写入和校验反向索引"
@@ -21874,25 +22402,15 @@ msgstr "选项 `%s' 期望 \"always\"、\"auto\" 或 \"never\""
 msgid "malformed object name '%s'"
 msgstr "格式错误的对象名 '%s'"
 
-#: parse-options-cb.c
-#, c-format
-msgid "option `%s' expects \"%s\" or \"%s\""
-msgstr "选项 `%s' 期望 \"%s\" 或 \"%s\""
-
-#: parse-options.c
-#, c-format
-msgid "%s requires a value"
-msgstr "%s 需要一个值"
-
-#: parse-options.c
+#: parse-options-cb.c
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s 与 %s 不兼容"
+msgid "option `%s' expects \"%s\" or \"%s\""
+msgstr "选项 `%s' 期望 \"%s\" 或 \"%s\""
 
 #: parse-options.c
 #, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s:和其它的不兼容"
+msgid "%s requires a value"
+msgstr "%s 需要一个值"
 
 #: parse-options.c
 #, c-format
@@ -21909,6 +22427,11 @@ msgstr "%s 不可用"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s 期望一个非负整数和一个可选的 k/m/g 后缀"
 
+#: parse-options.c
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s 与 %s 不兼容"
+
 #: parse-options.c
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
@@ -21994,6 +22517,11 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-数字"
 
+#: parse-options.c
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "与 --no-%s 相反"
+
 #: parse-options.h
 msgid "expiry-date"
 msgstr "到期时间"
@@ -22014,6 +22542,10 @@ msgstr "更加安静"
 msgid "use <n> digits to display object names"
 msgstr "用 <n> 位数字显示对象名"
 
+#: parse-options.h
+msgid "prefixed path to initial superproject"
+msgstr "用来初始化父项目的前缀路径"
+
 #: parse-options.h
 msgid "how to strip spaces and #comments from message"
 msgstr "设置如何删除提交说明里的空格和#注释"
@@ -22027,6 +22559,16 @@ msgid ""
 "with --pathspec-from-file, pathspec elements are separated with NUL character"
 msgstr "使用 --pathspec-from-file,路径表达式用空字符分隔"
 
+#: parse.c
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "对于 '%2$s' 的错误的布尔环境取值 '%1$s'"
+
+#: parse.c
+#, c-format
+msgid "failed to parse %s"
+msgstr "无法解析 %s"
+
 #: path.c
 #, c-format
 msgid "Could not make %s writable by group"
@@ -22083,6 +22625,11 @@ msgstr "路径规格 '%2$s' 中包含未实现的神奇前缀 '%1$c'"
 msgid "%s: 'literal' and 'glob' are incompatible"
 msgstr "%s:'literal' 和 'glob' 不兼容"
 
+#: pathspec.c
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s' 位于目录树之外"
+
 #: pathspec.c
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
@@ -22194,6 +22741,11 @@ msgstr "promisor-remote:无法关闭至 fetch 子进程的标准输入"
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr "promisor 远程名称不能以 '/' 开始:%s"
 
+#: promisor-remote.c
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "无法从承诺者远程获取 %s"
+
 #: protocol-caps.c
 msgid "object-info: expected flush after arguments"
 msgstr "object-info:在参数之后应有一个 flush"
@@ -22236,6 +22788,15 @@ msgstr "无法生成 diff"
 msgid "could not parse log for '%s'"
 msgstr "不能解析 '%s' 的日志"
 
+#: reachable.c
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "无效的额外废弃提交版本:'%s'"
+
+#: reachable.c
+msgid "unable to enumerate additional recent objects"
+msgstr "无法枚举额外的近期对象"
+
 #: read-cache.c
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
@@ -22398,11 +22959,6 @@ msgstr "无法为稀疏索引写入拆分索引"
 msgid "failed to convert to a sparse-index"
 msgstr "无法转换为稀疏索引"
 
-#: read-cache.c
-#, c-format
-msgid "could not stat '%s'"
-msgstr "不能对 '%s' 调用 stat"
-
 #: read-cache.c
 #, c-format
 msgid "unable to open git dir: %s"
@@ -22423,6 +22979,16 @@ msgstr "不能修复 '%s' 的权限位"
 msgid "%s: cannot drop to stage #0"
 msgstr "%s:不能落到暂存区 #0"
 
+#: read-cache.c
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "意外的差异状态 %c"
+
+#: read-cache.c
+#, c-format
+msgid "remove '%s'\n"
+msgstr "删除 '%s'\n"
+
 #: rebase-interactive.c
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
@@ -22591,6 +23157,16 @@ msgstr "落后 %d"
 msgid "ahead %d, behind %d"
 msgstr "领先 %d,落后 %d"
 
+#: ref-filter.c
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) 不带参数"
+
+#: ref-filter.c
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "未能识别的 %%(%.*s) 参数:%s"
+
 #: ref-filter.c
 #, c-format
 msgid "expected format: %%(color:<color>)"
@@ -22613,49 +23189,44 @@ msgstr "期望整数值 refname:rstrip=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "未能识别的 %%(%s) 参数:%s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "预期 %%(trailers:key=<值>)"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) 不带参数"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "未知的 %%(trailers) 参数:%s"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) 不带参数"
+msgid "positive value expected contents:lines=%s"
+msgstr "期望一个正数 contents:lines=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) 不带参数"
+msgid "argument expected for %s"
+msgstr "预期参数 %s"
 
 #: ref-filter.c
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "预期 %%(trailers:key=<值>)"
+msgid "positive value expected %s=%s"
+msgstr "预期正数参数值 %s=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "未知的 %%(trailers) 参数:%s"
+msgid "cannot fully parse %s=%s"
+msgstr "不能完整解析 %s=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "期望一个正数 contents:lines=%s"
+msgid "value expected %s="
+msgstr "预期值 %s="
 
 #: ref-filter.c
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "期望 %%(%2$s) 中的 '%1$s' 是一个正数"
 
-#: ref-filter.c
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "未识别的邮件选项:%s"
-
 #: ref-filter.c
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
@@ -22671,6 +23242,11 @@ msgstr "未能识别的位置:%s"
 msgid "unrecognized width:%s"
 msgstr "未能识别的宽度:%s"
 
+#: ref-filter.c
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "未能识别的 %%(%s) 参数:%s"
+
 #: ref-filter.c
 #, c-format
 msgid "positive width expected with the %%(align) atom"
@@ -22678,8 +23254,8 @@ msgstr "元素 %%(align) 需要一个正数的宽度"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) 不带参数"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "期望的格式:%%(ahead-behind:<提交号>)"
 
 #: ref-filter.c
 #, c-format
@@ -22737,6 +23313,10 @@ msgstr "这个命令拒绝元素 %%(%.*s)"
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s 不能和 --python、--shell、--tcl 同时使用"
 
+#: ref-filter.c
+msgid "failed to run 'describe'"
+msgstr "无法运行 'describe'"
+
 #: ref-filter.c
 #, c-format
 msgid "(no branch, rebasing %s)"
@@ -22814,6 +23394,10 @@ msgstr "key"
 msgid "field name to sort on"
 msgstr "排序的字段名"
 
+#: ref-filter.h
+msgid "exclude refs which match pattern"
+msgstr "排除与 <模式> 相匹配的引用"
+
 #: reflog.c
 #, c-format
 msgid "not a reflog: %s"
@@ -23343,8 +23927,9 @@ msgstr[1] ""
 
 #  译者:注意保持前导空格
 #: remote.c
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (使用 \"git pull\" 来合并远程分支)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr "  (如果您想将远程分支与您的更改合并,请使用 \"git pull\")\n"
 
 #: remote.c
 #, c-format
@@ -23434,11 +24019,6 @@ msgstr "无法更新 '%s' 中的冲突状态"
 msgid "no remembered resolution for '%s'"
 msgstr "没有为 '%s' 记忆的解决方案"
 
-#: rerere.c
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "不能删除 '%s'"
-
 #: rerere.c
 #, c-format
 msgid "Updated preimage for '%s'"
@@ -23466,6 +24046,15 @@ msgstr "不能确定 HEAD 版本"
 msgid "failed to find tree of %s"
 msgstr "无法找到 %s 指向的树"
 
+#: revision.c
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "不支持的隐藏引用片段: %s"
+
+#: revision.c
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= 传递了不止一次"
+
 #: revision.c
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
@@ -23480,6 +24069,11 @@ msgstr "无法获得 ancestry-path 参数 %s 的提交"
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "不再支持 --unpacked=<packfile>"
 
+#: revision.c
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "在 --stdin 模式下的无效选项:'%s'"
+
 #: revision.c
 msgid "your current branch appears to be broken"
 msgstr "您的当前分支好像被损坏"
@@ -23591,8 +24185,16 @@ msgid "only download metadata for the branch that will be checked out"
 msgstr "只下载要检出的分支的元信息"
 
 #: scalar.c
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<选项>] [--] <仓库> [<目录>]"
+msgid "create repository within 'src' directory"
+msgstr "在 'src' 目录中创建仓库"
+
+#: scalar.c
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<登记>]"
 
 #: scalar.c
 #, c-format
@@ -23653,8 +24255,37 @@ msgstr "--all 或者 <登记>,而不是两个一起"
 
 #: scalar.c
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "在 '%s' 的 git 仓库已消失"
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "无法删除过期的 scalar.repo '%s'"
+
+#: scalar.c
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "已删除过期的 scalar.repo '%s'"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "位于 '%s' 处的仓库有不同的所有者"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "位于 '%s' 处的仓库存在格式问题"
+
+#: scalar.c
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "在 '%s' 中找不到仓库"
+
+#: scalar.c
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"若希望从 Scalar 注销该仓库,执行\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 #: scalar.c
 msgid ""
@@ -23832,11 +24463,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "不能锁定 '%s'"
 
-#: sequencer.c strbuf.c wrapper.c
-#, c-format
-msgid "could not write to '%s'"
-msgstr "不能写入 '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not write eol to '%s'"
@@ -24134,11 +24760,6 @@ msgstr "不能得到 %s 的提交说明"
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s:不能解析父提交 %s"
 
-#: sequencer.c
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "不能将 '%s' 重命名为 '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not revert %s... %s"
@@ -24164,6 +24785,26 @@ msgstr "git %s:无法读取索引"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s:无法刷新索引"
 
+#: sequencer.c
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "'%s' 不是一个有效的标签"
+
+#: sequencer.c
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "'%s' 不是一个有效的引用名"
+
+#: sequencer.c
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr "update-ref 需要一个完整的引用名,例如:refs/heads/%s"
+
+#: sequencer.c
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "无效命令 '%.*s'"
+
 #: sequencer.c
 #, c-format
 msgid "%s does not accept arguments: '%s'"
@@ -24254,10 +24895,6 @@ msgstr "尝试 \"git cherry-pick (--continue | %s--abort | --quit)\""
 msgid "could not create sequencer directory '%s'"
 msgstr "不能创建序列目录 '%s'"
 
-#: sequencer.c
-msgid "could not lock HEAD"
-msgstr "不能锁定 HEAD"
-
 #: sequencer.c
 msgid "no cherry-pick or revert in progress"
 msgstr "拣选或还原操作并未进行"
@@ -24372,21 +25009,21 @@ msgstr ""
 "\n"
 
 #: sequencer.c
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "并且修改索引和/或工作区\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "并且修改索引和/或工作区\n"
 
 #: sequencer.c
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "执行成功:%s\n"
-"但是在索引和/或工作区中存在变更\n"
+"但是在索引和/或工作区中存在变更\n"
 "提交或贮藏修改,然后运行\n"
 "\n"
 "  git rebase --continue\n"
@@ -24397,6 +25034,11 @@ msgstr ""
 msgid "illegal label name: '%.*s'"
 msgstr "非法的标签名称:'%.*s'"
 
+#: sequencer.c
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "无法解析 '%s'"
+
 #: sequencer.c
 msgid "writing fake root commit"
 msgstr "写伪根提交"
@@ -24405,11 +25047,6 @@ msgstr "写伪根提交"
 msgid "writing squash-onto"
 msgstr "写入 squash-onto"
 
-#: sequencer.c
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "无法解析 '%s'"
-
 #: sequencer.c
 msgid "cannot merge without a current revision"
 msgstr "没有当前版本不能合并"
@@ -24554,13 +25191,13 @@ msgstr ""
 
 #: sequencer.c
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "正在变基(%d/%d)%s"
+msgid "Stopped at %s...  %.*s\n"
+msgstr "停止在 %s... %.*s\n"
 
 #: sequencer.c
 #, c-format
-msgid "Stopped at %s...  %.*s\n"
-msgstr "停止在 %s... %.*s\n"
+msgid "Rebasing (%d/%d)%s"
+msgstr "正在变基(%d/%d)%s"
 
 #: sequencer.c
 #, c-format
@@ -24777,75 +25414,164 @@ msgstr "不是 git 仓库:'%s'"
 
 #: setup.c
 #, c-format
-msgid "cannot chdir to '%s'"
-msgstr "不能切换目录到 '%s'"
+msgid "cannot chdir to '%s'"
+msgstr "不能切换目录到 '%s'"
+
+#: setup.c
+msgid "cannot come back to cwd"
+msgstr "无法返回当前工作目录"
+
+#: setup.c
+#, c-format
+msgid "failed to stat '%*s%s%s'"
+msgstr "无法获取 '%*s%s%s' 状态(stat)"
+
+#: setup.c
+msgid "Unable to read current working directory"
+msgstr "不能读取当前工作目录"
+
+#: setup.c
+#, c-format
+msgid "cannot change to '%s'"
+msgstr "不能切换到 '%s'"
+
+#: setup.c
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "不是 git 仓库(或者任何父目录):%s"
+
+#: setup.c
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"不是 git 仓库(或者直至挂载点 %s 的任何父目录)\n"
+"停止在文件系统边界(未设置 GIT_DISCOVERY_ACROSS_FILESYSTEM)。"
+
+#: setup.c
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"在 '%s' 检测到可疑的仓库所有权\n"
+"%s要为本仓库创建特例,请运行:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#: setup.c
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr "无法使用纯仓库 '%s' (safe.bareRepository 为 '%s')"
+
+#: setup.c
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"参数 core.sharedRepository 的文件属性值有问题(0%.3o)。\n"
+"文件属主必须始终拥有读写权限。"
+
+#: setup.c
+msgid "fork failed"
+msgstr "fork 失败"
+
+#: setup.c
+msgid "setsid failed"
+msgstr "setsid 失败"
+
+#: setup.c
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "不能对模版 '%s' 调用 stat"
+
+#: setup.c
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "不能打开目录 '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "不能读取链接 '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "不能自 '%s' 到 '%s' 创建符号链接"
+
+#: setup.c
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "不能拷贝 '%s' 至 '%s'"
+
+#: setup.c
+#, c-format
+msgid "ignoring template %s"
+msgstr "忽略模版 %s"
+
+#: setup.c
+#, c-format
+msgid "templates not found in %s"
+msgstr "没有在 %s 中找到模版"
 
 #: setup.c
-msgid "cannot come back to cwd"
-msgstr "无法返回当前工作目录"
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "没有从 '%s' 复制模版:%s"
 
 #: setup.c
 #, c-format
-msgid "failed to stat '%*s%s%s'"
-msgstr "æ\97 æ³\95è\8e·å\8f\96 '%*s%s%s' ç\8a¶æ\80\81ï¼\88statï¼\89"
+msgid "invalid initial branch name: '%s'"
+msgstr "æ\97 æ\95\88ç\9a\84å\88\9då§\8bå\88\86æ\94¯å\90\8dï¼\9a'%s'"
 
 #: setup.c
-msgid "Unable to read current working directory"
-msgstr "不能读取当前工作目录"
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "不能处理 %d 类型的文件"
 
 #: setup.c
 #, c-format
-msgid "cannot change to '%s'"
-msgstr "不能切换到 '%s'"
+msgid "unable to move %s to %s"
+msgstr "不能移动 %s 至 %s"
 
 #: setup.c
-#, c-format
-msgid "not a git repository (or any of the parent directories): %s"
-msgstr "不是 git 仓库(或者任何父目录):%s"
+msgid "attempt to reinitialize repository with different hash"
+msgstr "尝试用不同的哈希算法重新初始化仓库"
 
 #: setup.c
 #, c-format
-msgid ""
-"not a git repository (or any parent up to mount point %s)\n"
-"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
-msgstr ""
-"不是 git 仓库(或者直至挂载点 %s 的任何父目录)\n"
-"停止在文件系统边界(未设置 GIT_DISCOVERY_ACROSS_FILESYSTEM)。"
+msgid "%s already exists"
+msgstr "%s 已经存在"
 
 #: setup.c
 #, c-format
-msgid ""
-"detected dubious ownership in repository at '%s'\n"
-"%sTo add an exception for this directory, call:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
-msgstr ""
-"在 '%s' 检测到可疑的仓库所有权\n"
-"%s要为本仓库创建特例,请运行:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init:已忽略 --initial-branch=%s"
 
 #: setup.c
 #, c-format
-msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr "无法使用纯仓库 '%s' (safe.bareRepository 为 '%s')"
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "重新初始化已存在的共享 Git 仓库于 %s%s\n"
 
 #: setup.c
 #, c-format
-msgid ""
-"problem with core.sharedRepository filemode value (0%.3o).\n"
-"The owner of files must always have read and write permissions."
-msgstr ""
-"参数 core.sharedRepository 的文件属性值有问题(0%.3o)。\n"
-"文件属主必须始终拥有读写权限。"
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "已重新初始化已存在的 Git 仓库于 %s%s\n"
 
 #: setup.c
-msgid "fork failed"
-msgstr "fork 失败"
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "已初始化空的共享 Git 仓库于 %s%s\n"
 
 #: setup.c
-msgid "setsid failed"
-msgstr "setsid 失败"
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "已初始化空的 Git 仓库于 %s%s\n"
 
 #: sparse-index.c
 #, c-format
@@ -24908,11 +25634,6 @@ msgid_plural "%u bytes/s"
 msgstr[0] "%u 字节/秒"
 msgstr[1] "%u 字节/秒"
 
-#: strbuf.c
-#, c-format
-msgid "could not edit '%s'"
-msgstr "不能编辑 '%s'"
-
 #: submodule-config.c
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
@@ -25123,6 +25844,31 @@ msgstr "ls-tree 返回未知返回值 %d"
 msgid "failed to lstat '%s'"
 msgstr "无法执行 lstat '%s'"
 
+#: t/helper/test-bundle-uri.c
+msgid "no remote configured to get bundle URIs from"
+msgstr "没有远程被设置为可以获取归档包 URI"
+
+#: t/helper/test-bundle-uri.c
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "远程 '%s' 没有设置 URL"
+
+#: t/helper/test-bundle-uri.c
+msgid "could not get the bundle-uri list"
+msgstr "无法获取 bundle-uri 列表"
+
+#: t/helper/test-cache-tree.c
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <选项> (control|prime|update)"
+
+#: t/helper/test-cache-tree.c
+msgid "clear the cache tree before each iteration"
+msgstr "在每次迭代前清除缓存树"
+
+#: t/helper/test-cache-tree.c
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr "缓存树中无效化的条目数量(默认 0)"
+
 #: t/helper/test-fast-rebase.c
 msgid "unhandled options"
 msgstr "未处理的选项"
@@ -25547,6 +26293,14 @@ msgstr "正在终止。"
 msgid "failed to push all needed submodules"
 msgstr "不能推送全部需要的子模组"
 
+#: transport.c
+msgid "bundle-uri operation not supported by protocol"
+msgstr "协议不支持 bundle-uri 操作"
+
+#: transport.c
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "无法获取服务器公布的 bundle-uri 列表"
+
 #: tree-walk.c
 msgid "too-short tree object"
 msgstr "太短的树对象"
@@ -25999,6 +26753,10 @@ msgstr "不能访问 '%s'"
 msgid "unable to get current working directory"
 msgstr "不能获取当前工作目录"
 
+#: wrapper.c
+msgid "unable to get random bytes"
+msgstr "无法获取随机字节"
+
 #: wt-status.c
 msgid "Unmerged paths:"
 msgstr "未合并的路径:"
@@ -26464,12 +27222,20 @@ msgstr "忽略的文件"
 #: wt-status.c
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
 msgstr ""
-"耗费了 %.2f 秒以枚举未跟踪的文件。'status -uno' 也许能提高速度,\n"
-"但您需要小心不要忘了添加新文件(参见 'git help status')。"
+"枚举未追踪的文件花了 %.2f 秒,\n"
+"结果已被缓存,后续的执行会更快。"
+
+#: wt-status.c
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "枚举未追踪的文件花了 %.2f 秒。"
+
+#: wt-status.c
+msgid "See 'git help status' for information on how to improve this."
+msgstr "参见 'git help status' 来获取如何改善的信息。"
 
 #: wt-status.c
 #, c-format
@@ -26642,300 +27408,6 @@ msgstr "您需要在工作区的顶级目录中运行这个命令。"
 msgid "Unable to determine absolute path of git directory"
 msgstr "不能确定 git 目录的绝对路径"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#: git-add--interactive.perl
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %12s %s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "触碰了 %d 个路径\n"
-msgstr[1] "触碰了 %d 个路径\n"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr "如果补丁能干净地应用,编辑块将立即标记为暂存。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr "如果补丁能干净地应用,编辑块将立即标记为贮藏。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr "如果补丁能干净地应用,编辑块将立即标记为未暂存。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr "如果补丁能干净地应用,编辑块将立即标记为应用。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr "如果补丁能干净地应用,编辑块将立即标记为丢弃。"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "无法为写入打开块编辑文件:%s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"要删除 '%s' 开始的行,使其成为 ' ' 开始的行(上下文)。\n"
-"要删除 '%s' 开始的行,删除它们。\n"
-"以 %s 开始的行将被删除。\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "无法读取块编辑文件:%s"
-
-#: git-add--interactive.perl
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 暂存该块\n"
-"n - 不要暂存该块\n"
-"q - 退出。不暂存该块及后面的全部块\n"
-"a - 暂存该块和本文件中后面的全部块\n"
-"d - 不暂存该块和本文件中后面的全部块"
-
-#: git-add--interactive.perl
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 贮藏该块\n"
-"n - 不要贮藏该块\n"
-"q - 退出。不贮藏该块及后面的全部块\n"
-"a - 贮藏该块和本文件中后面的全部块\n"
-"d - 不贮藏该块和本文件中后面的全部块"
-
-#: git-add--interactive.perl
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 不暂存该块\n"
-"n - 不要不暂存该块\n"
-"q - 退出。不要不暂存该块及后面的全部块\n"
-"a - 不暂存该块和本文件中后面的全部块\n"
-"d - 不要不暂存该块和本文件中后面的全部块"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引中应用该块\n"
-"n - 不要在索引中应用该块\n"
-"q - 退出。不要应用该块及后面的全部块\n"
-"a - 应用该块和本文件中后面的全部块\n"
-"d - 不要应用该块和本文件中后面的全部块"
-
-#: git-add--interactive.perl
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在工作区中丢弃该块\n"
-"n - 不要在工作区中丢弃该块\n"
-"q - 退出。不要丢弃该块及后面的全部块\n"
-"a - 丢弃该块和本文件中后面的全部块\n"
-"d - 不要丢弃该块和本文件中后面的全部块"
-
-#: git-add--interactive.perl
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引和工作区中丢弃该块\n"
-"n - 不要在索引和工作区中丢弃该块\n"
-"q - 退出。不要丢弃该块及后面的全部块\n"
-"a - 丢弃该块和本文件中后面的全部块\n"
-"d - 不要丢弃该块和本文件中后面的全部块"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引和工作区中应用该块\n"
-"n - 不要在索引和工作区中应用该块\n"
-"q - 退出。不要应用该块及后面的全部块\n"
-"a - 应用该块和本文件中后面的全部块\n"
-"d - 不要应用该块和本文件中后面的全部块"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在工作区中应用该块\n"
-"n - 不要在工作区中应用该块\n"
-"q - 退出。不要应用该块及后面的全部块\n"
-"a - 应用该块和本文件中后面的全部块\n"
-"d - 不要应用该块和本文件中后面的全部块"
-
-#: git-add--interactive.perl
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - 选择跳转到一个块\n"
-"/ - 查找和给定正则表达式匹配的块\n"
-"j - 维持该块未决状态,查看下一个未决块\n"
-"J - 维持该块未决状态,查看下一个块\n"
-"k - 维持该块未决状态,查看上一个未决块\n"
-"K - 维持该块未决状态,查看上一个块\n"
-"s - 拆分当前块为更小的块\n"
-"e - 手动编辑当前块\n"
-"? - 显示帮助\n"
-
-#: git-add--interactive.perl
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "选中的块不能应用到索引!\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "忽略未合入的:%s\n"
-
-#: git-add--interactive.perl
-msgid "No other hunks to goto\n"
-msgstr "没有其它可供跳转的块\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "无效数字:'%s'\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "对不起,只有 %d 个可用块。\n"
-msgstr[1] "对不起,只有 %d 个可用块。\n"
-
-#: git-add--interactive.perl
-msgid "No other hunks to search\n"
-msgstr "没有其它可供查找的块\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "错误的正则表达式 %s:%s\n"
-
-#: git-add--interactive.perl
-msgid "No hunk matches the given pattern\n"
-msgstr "没有和给定模式相匹配的块\n"
-
-#: git-add--interactive.perl
-msgid "No previous hunk\n"
-msgstr "没有前一个块\n"
-
-#: git-add--interactive.perl
-msgid "No next hunk\n"
-msgstr "没有下一个块\n"
-
-#: git-add--interactive.perl
-msgid "Sorry, cannot split this hunk\n"
-msgstr "对不起,不能拆分这个块\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "拆分为 %d 块。\n"
-msgstr[1] "拆分为 %d 块。\n"
-
-#: git-add--interactive.perl
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "对不起,不能编辑这个块\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-#: git-add--interactive.perl
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - 显示含变更的路径\n"
-"update        - 添加工作区状态至暂存列表\n"
-"revert        - 还原修改的暂存集至 HEAD 版本\n"
-"patch         - 挑选块并且有选择地更新\n"
-"diff          - 显示 HEAD 和索引间差异\n"
-"add untracked - 添加未跟踪文件的内容至暂存列表\n"
-
-#: git-add--interactive.perl
-msgid "missing --"
-msgstr "缺失 --"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "未知的 --patch 模式:%s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "无效的参数 %s,期望是 --"
-
 #: git-send-email.perl
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "本地时间和 GMT 有不到一分钟间隔\n"
@@ -27073,13 +27545,13 @@ msgstr ""
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "无法打开 %s: %s"
+msgid "Failed to open %s.final: %s"
+msgstr "无法打开 %s.final: %s"
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s.final: %s"
-msgstr "无法打开 %s.final: %s"
+msgid "Failed to open %s: %s"
+msgstr "无法打开 %s: %s"
 
 #: git-send-email.perl
 msgid "Summary email is empty, skipping it\n"
@@ -27257,14 +27729,19 @@ msgstr "(%s) 不能执行 '%s'"
 
 #: git-send-email.perl
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) 添加 %s: %s 自:'%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) 非法的输出信息,来自于: '%s'"
 
 #: git-send-email.perl
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) 无法关闭管道至 '%s'"
 
+#: git-send-email.perl
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) 添加 %s: %s 自:'%s'\n"
+
 #: git-send-email.perl
 msgid "cannot send message as 7bit"
 msgstr "不能以 7bit 形式发送信息"
index 29b80891417a91156ca25b2dffd3453d83de5883..f777a0596f8caa04151ce928cf253090fcdd4557 100644 (file)
@@ -1,9 +1,11 @@
 # Chinese (traditional) translations for Git package
 # Git 套裝軟體的繁體中文翻譯。
 # Copyright (C) 2012-2021 Jiang Xin <worldhello.net AT gmail.com>
-# Copyright (C) 2019-2021 Yi-Jyun Pan <pan93412@gmail.com>
+# Copyright (C) 2019-2023 Yi-Jyun Pan <pan93412@gmail.com>
 # This file is distributed under the same license as the Git package.
 #
+# The glossary can be found on https://github.com/l10n-tw/git-glossary
+#
 # Contributors (CN):
 # - Fangyi Zhou <me AT fangyi.io>
 # - Jiang Xin <worldhello.net AT gmail.com>
 # - Yichao Yu <yyc1992 AT gmail.com>
 # - Zhuang Ya <zhuangya AT me.com>
 #
-# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022.
+# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022, 2023.
+# Kaiyang Wu <self@origincode.me>, 2022.
+# lumynou5 <lumynou5.tw@gmail.com>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-09-23 21:57+0000\n"
-"PO-Revision-Date: 2022-10-01 19:02+0800\n"
+"POT-Creation-Date: 2023-11-19 22:29+0800\n"
+"PO-Revision-Date: 2023-11-19 23:34+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"
@@ -32,7 +36,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 3.1.1\n"
+"X-Generator: Poedit 3.4.1\n"
 "X-ZhConverter: 繁化姬 dict-f4bc617e-r910 @ 2019/11/16 20:23:12 | https://"
 "zhconvert.org\n"
 
@@ -45,110 +49,110 @@ msgstr "嗯(%s)?"
 msgid "could not read index"
 msgstr "無法讀取索引"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "binary"
 msgstr "二進位"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "nothing"
 msgstr "無"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "unchanged"
 msgstr "未變更"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Update"
 msgstr "更新"
 
 #: add-interactive.c
 #, c-format
 msgid "could not stage '%s'"
-msgstr "無法暫存「%s」"
+msgstr "無法暫存 “%s”"
 
 #: add-interactive.c builtin/stash.c reset.c sequencer.c
 msgid "could not write index"
 msgstr "無法寫入索引"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "已更新 %d 個路徑\n"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "note: %s is untracked now.\n"
-msgstr "注意:%s 現已不再追蹤。\n"
+msgstr "註:現已不再追蹤 %s。\n"
 
 #: add-interactive.c apply.c builtin/checkout.c builtin/reset.c
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
-msgstr "「%s」路徑執行 make_cache_entry 失敗"
+msgstr "對 “%s” 路徑執行 make_cache_entry 失敗"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Revert"
 msgstr "還原"
 
 #: add-interactive.c
 msgid "Could not parse HEAD^{tree}"
-msgstr "不能解析 HEAD^{樹}"
+msgstr "無法解析 HEAD^{tree}"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
-msgstr[0] "還原了 %d 個路徑\n"
+msgstr[0] "已還原 %d 個路徑\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 #, c-format
 msgid "No untracked files.\n"
 msgstr "沒有未追蹤的檔案。\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Add untracked"
-msgstr "新增未追蹤的"
+msgstr "加入未追蹤項目"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
-msgstr[0] "å¢\9eå\8a äº\86 %d 個路徑\n"
+msgstr[0] "å·²å\8a å\85¥ %d 個路徑\n"
 
 #: add-interactive.c
 #, c-format
 msgid "ignoring unmerged: %s"
-msgstr "忽略未合併:%s"
+msgstr "忽略未合併項目:%s"
 
-#: add-interactive.c add-patch.c git-add--interactive.perl
+#: add-interactive.c add-patch.c
 #, c-format
 msgid "Only binary files changed.\n"
-msgstr "只有二進位檔案被修改。\n"
+msgstr "只有二進位檔案更動了。\n"
 
-#: add-interactive.c add-patch.c git-add--interactive.perl
+#: add-interactive.c add-patch.c
 #, c-format
 msgid "No changes.\n"
-msgstr "沒有修改。\n"
+msgstr "沒有更動。\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Patch update"
 msgstr "修補檔更新"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Review diff"
-msgstr "檢視 diff"
+msgstr "檢閱差異"
 
 #: add-interactive.c
 msgid "show paths with changes"
-msgstr "顯示有變更的路徑"
+msgstr "顯示有更動的路徑"
 
 #: add-interactive.c
 msgid "add working tree state to the staged set of changes"
-msgstr "å\8a å\85¥å·¥ä½\9cå\8d\80ç\8b\80æ\85\8bè\87³æ\9a«å­\98å\88\97表"
+msgstr "å°\87å·¥ä½\9cå\8d\80ç\8b\80æ\85\8bå\8a å\85¥è\87³æ\9a«å­\98æ\9b´å\8b\95é\9b\86"
 
 #: add-interactive.c
 msgid "revert staged set of changes back to the HEAD version"
-msgstr "還原修改的暫存集至 HEAD 版本"
+msgstr "將暫存的更動集還原回 HEAD 版本"
 
 #: add-interactive.c
 msgid "pick hunks and update selectively"
@@ -160,7 +164,7 @@ msgstr "檢視 HEAD 及索引之間的差異"
 
 #: add-interactive.c
 msgid "add contents of untracked files to the staged set of changes"
-msgstr "å\8a å\85¥æ\9cªè¿½è¹¤æª\94æ¡\88ç\9a\84å\85§å®¹è\87³æ\9a«å­\98å\88\97表"
+msgstr "å°\87æ\9cªè¿½è¹¤æª\94æ¡\88ç\9a\84å\85§å®¹å\8a å\85¥è\87³æ\9b´å\8b\95æ\9a«å­\98é\9b\86"
 
 #: add-interactive.c
 msgid "Prompt help:"
@@ -168,23 +172,23 @@ msgstr "提示說明:"
 
 #: add-interactive.c
 msgid "select a single item"
-msgstr "選擇單一項目"
+msgstr "選取一個項目"
 
 #: add-interactive.c
 msgid "select a range of items"
-msgstr "選擇項目範圍"
+msgstr "選取範圍中項目"
 
 #: add-interactive.c
 msgid "select multiple ranges"
-msgstr "選多個範圍"
+msgstr "選多個範圍"
 
 #: add-interactive.c
 msgid "select item based on unique prefix"
-msgstr "基於唯一前綴選擇項目"
+msgstr "根據獨特前綴選取項目"
 
 #: add-interactive.c
 msgid "unselect specified items"
-msgstr "取消選指定項目"
+msgstr "取消選指定項目"
 
 #: add-interactive.c
 msgid "choose all items"
@@ -196,31 +200,31 @@ msgstr "(空)完成選取"
 
 #: add-interactive.c
 msgid "select a numbered item"
-msgstr "選編號過的項目"
+msgstr "選編號過的項目"
 
 #: add-interactive.c
 msgid "(empty) select nothing"
-msgstr "(空)全不選取"
+msgstr "(空)全不選取"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 msgid "*** Commands ***"
-msgstr "*** 令 ***"
+msgstr "*** 令 ***"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 msgid "What now"
 msgstr "請選擇"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "staged"
-msgstr "å¿«å\8f\96"
+msgstr "å·²æ\9a«å­\98"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "unstaged"
-msgstr "未快取"
+msgstr "未暫存"
 
 #: add-interactive.c apply.c builtin/am.c builtin/bugreport.c builtin/clone.c
-#: builtin/diagnose.c builtin/fetch.c builtin/merge.c builtin/pull.c
-#: builtin/submodule--helper.c git-add--interactive.perl
+#: builtin/diagnose.c builtin/fetch.c builtin/hook.c builtin/merge.c
+#: builtin/pull.c builtin/submodule--helper.c
 msgid "path"
 msgstr "路徑"
 
@@ -228,28 +232,28 @@ msgstr "路徑"
 msgid "could not refresh index"
 msgstr "無法重新整理索引"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 #, c-format
 msgid "Bye.\n"
 msgstr "再見。\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
-msgstr "暫存模式變更 [y,n,q,a,d%s,?]? "
+msgstr "暫存模式更動 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
-msgstr "暫存刪除變更 [y,n,q,a,d%s,?]? "
+msgstr "暫存刪除動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
-msgstr "暫存新增變更 [y,n,q,a,d%s,?]? "
+msgstr "暫存加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "暫存此區塊 [y,n,q,a,d%s,?]? "
 
@@ -257,7 +261,7 @@ msgstr "暫存此區塊 [y,n,q,a,d%s,?]? "
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "staging."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為暫存。"
+msgstr "如果修補檔能完全套用,編輯區塊將立即標記為暫存。"
 
 #: add-patch.c
 msgid ""
@@ -273,31 +277,31 @@ msgstr ""
 "a - 暫存此區塊和本檔案中後面的全部區塊\n"
 "d - 不暫存此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
-msgstr "儲藏模式變更 [y,n,q,a,d%s,?]? "
+msgstr "貯存模式更動 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
-msgstr "儲藏刪除變更 [y,n,q,a,d%s,?]? "
+msgstr "貯存刪除動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
-msgstr "儲藏新增變更 [y,n,q,a,d%s,?]? "
+msgstr "貯存加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
-msgstr "儲藏此區塊 [y,n,q,a,d%s,?]? "
+msgstr "貯存此區塊 [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "stashing."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為儲藏。"
+msgstr "如果修補檔能完全套用,編輯區塊將立即標記為貯存。"
 
 #: add-patch.c
 msgid ""
@@ -307,29 +311,29 @@ msgid ""
 "a - stash this hunk and all later hunks in the file\n"
 "d - do not stash this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - 儲藏此區塊\n"
-"n - 不要儲藏此區塊\n"
-"q - 離開。不儲藏此區塊及後面的全部區塊\n"
-"a - 儲藏此區塊和本檔案中後面的全部區塊\n"
-"d - 不儲藏此區塊和本檔案中後面的全部區塊\n"
+"y - 貯存此區塊\n"
+"n - 不要貯存此區塊\n"
+"q - 離開。不貯存此區塊及後面的全部區塊\n"
+"a - 貯存此區塊和本檔案中後面的全部區塊\n"
+"d - 不貯存此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
-msgstr "取消暫存模式變更 [y,n,q,a,d%s,?]? "
+msgstr "取消暫存模式更動 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
-msgstr "取消暫存刪除變更 [y,n,q,a,d%s,?]? "
+msgstr "取消暫存刪除動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
-msgstr "取消暫存新增變更 [y,n,q,a,d%s,?]? "
+msgstr "取消暫存加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "取消暫存此區塊 [y,n,q,a,d%s,?]? "
 
@@ -337,7 +341,7 @@ msgstr "取消暫存此區塊 [y,n,q,a,d%s,?]? "
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "unstaging."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為未暫存。"
+msgstr "如果修補檔能完全套用,編輯區塊將立即標記為未暫存。"
 
 #: add-patch.c
 msgid ""
@@ -353,23 +357,23 @@ msgstr ""
 "a - 不暫存此區塊和本檔案中後面的全部區塊\n"
 "d - 不要不暫存此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
-msgstr "將模式變更套用到索引 [y,n,q,a,d%s,?]? "
+msgstr "將模式更動套用到索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
-msgstr "將刪除變更套用到索引 [y,n,q,a,d%s,?]? "
+msgstr "將刪除動作套用至索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
-msgstr "å¥\97ç\94¨æ\96°å¢\9eè®\8aæ\9b´至索引 [y,n,q,a,d%s,?]? "
+msgstr "å°\87å\8a å\85¥å\8b\95ä½\9cå¥\97ç\94¨至索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "將此區塊套用到索引 [y,n,q,a,d%s,?]? "
 
@@ -377,7 +381,7 @@ msgstr "將此區塊套用到索引 [y,n,q,a,d%s,?]? "
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "applying."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為套用。"
+msgstr "如果修補檔能完全套用,編輯區塊將立即標記為套用。"
 
 #: add-patch.c
 msgid ""
@@ -393,31 +397,31 @@ msgstr ""
 "a - 套用此區塊和本檔案中後面的全部區塊\n"
 "d - 不要套用此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
-msgstr "從工作區中捨棄模式變更 [y,n,q,a,d%s,?]? "
+msgstr "從工作區捨棄模式更動 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
-msgstr "從工作區中捨棄刪除變更 [y,n,q,a,d%s,?]? "
+msgstr "從工作區捨棄刪除動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
-msgstr "放棄工作目錄的新增變更 [y,n,q,a,d%s,?]? "
+msgstr "從工作區捨棄加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "從工作區捨棄此區塊 [y,n,q,a,d%s,?]? "
+msgstr "從工作區捨棄此區塊 [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "discarding."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為捨棄。"
+msgstr "如果修補檔能完全套用,編輯區塊將立即標記為捨棄。"
 
 #: add-patch.c
 msgid ""
@@ -433,25 +437,25 @@ msgstr ""
 "a - 捨棄此區塊和本檔案中後面的全部區塊\n"
 "d - 不要捨棄此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "從索引和工作區中捨棄模式變更 [y,n,q,a,d%s,?]? "
+msgstr "從索引和工作區捨棄模式更動 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "從索引和工作區捨棄刪除 [y,n,q,a,d%s,?]? "
+msgstr "從索引和工作區捨棄刪除 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "放棄索引及工作目錄的新增變更 [y,n,q,a,d%s,?]? "
+msgstr "從索引和工作區捨棄加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "從索引和工作區捨棄此區塊 [y,n,q,a,d%s,?]? "
+msgstr "從索引和工作區捨棄此區塊 [y,n,q,a,d%s,?]? "
 
 #: add-patch.c
 msgid ""
@@ -467,23 +471,23 @@ msgstr ""
 "a - 捨棄此區塊和本檔案中後面的全部區塊\n"
 "d - 不要捨棄此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "將模式變更套用到索引和工作區 [y,n,q,a,d%s,?]? "
+msgstr "將模式更動套用到索引和工作區 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "將刪除變更套用到索引和工作區 [y,n,q,a,d%s,?]? "
+msgstr "將刪除動作套用到索引和工作區 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
-msgstr "å¥\97ç\94¨ç´¢å¼\95å\8f\8aå·¥ä½\9cç\9b®é\8c\84ç\9a\84æ\96°å¢\9eè®\8aæ\9b´ [y,n,q,a,d%s,?]? "
+msgstr "å°\87å\8a å\85¥å\8b\95ä½\9cå¥\97ç\94¨å\88°ç´¢å¼\95å\92\8cå·¥ä½\9cå\8d\80 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "將此區塊套用到索引和工作區 [y,n,q,a,d%s,?]? "
 
@@ -501,23 +505,23 @@ msgstr ""
 "a - 套用此區塊和本檔案中後面的全部區塊\n"
 "d - 不要套用此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr "將模式變更套用到工作區 [y,n,q,a,d%s,?]? "
+msgstr "將模式更動套用到工作區 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr "將刪除變更套用到工作區 [y,n,q,a,d%s,?]? "
+msgstr "將刪除動作套用到工作區 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr "將新增變更套用到工作區 [y,n,q,a,d%s,?]? "
+msgstr "將加入動作套用到工作區 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "將此區塊套用到工作區 [y,n,q,a,d%s,?]? "
 
@@ -538,20 +542,20 @@ msgstr ""
 #: add-patch.c
 #, c-format
 msgid "could not parse hunk header '%.*s'"
-msgstr "無法解析區塊標頭 '%.*s'"
+msgstr "無法解析區塊標頭 “%.*s”"
 
 #: add-patch.c
 msgid "could not parse diff"
-msgstr "無法解析差異 (diff)"
+msgstr "無法解析差異"
 
 #: add-patch.c
 msgid "could not parse colored diff"
-msgstr "無法解析上色過的差異 (diff)"
+msgstr "無法解析上色過的差異"
 
 #: add-patch.c
 #, c-format
 msgid "failed to run '%s'"
-msgstr "無法執行 '%s'"
+msgstr "無法執行 “%s”"
 
 #: add-patch.c
 msgid "mismatched output from interactive.diffFilter"
@@ -562,7 +566,7 @@ msgid ""
 "Your filter must maintain a one-to-one correspondence\n"
 "between its input and output lines."
 msgstr ""
-"您的過濾器必須在其輸入及輸出\n"
+"您的過濾器必須在其輸入及輸出\n"
 "維持一對一的對應關係。"
 
 #: add-patch.c
@@ -571,7 +575,7 @@ msgid ""
 "expected context line #%d in\n"
 "%.*s"
 msgstr ""
-"應有上下文行 #%d 於\n"
+"預期在下述位置有上下文列 #%d:\n"
 "%.*s"
 
 #: add-patch.c
@@ -584,12 +588,12 @@ msgid ""
 msgstr ""
 "區塊未重疊:\n"
 "%.*s\n"
-"\t不以下述結尾:\n"
+"\t結尾不是:\n"
 "%.*s"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Manual hunk edit mode -- see bottom for a quick guide.\n"
-msgstr "手動區塊編輯模式 -- 檢視底部的快速指南。\n"
+msgstr "手動區塊編輯模式——檢視底部的快速指引。\n"
 
 #: add-patch.c
 #, c-format
@@ -600,21 +604,19 @@ msgid ""
 "Lines starting with %c will be removed.\n"
 msgstr ""
 "---\n"
-"要刪除 '%c' 開始的行,使其成為 ' ' 開始的行(上下文)。\n"
-"要刪除 '%c' 開始的行,刪除它們。\n"
-"以 %c 開始的行將被刪除。\n"
+"要刪除 “%c” 開頭的列,請將列首(上下文)改成空白。\n"
+"要刪除 “%c” 開頭的列,請直接刪除。\n"
+"開頭是 %c 的列將會被移除。\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
 "aborted and the hunk is left unchanged.\n"
 msgstr ""
-"å¦\82æ\9e\9cæ\9cªä¹¾æ·¨å¥\97ç\94¨ï¼\8cæ\82¨å°±æ\9c\89æ©\9fæ\9c\83é\87\8dæ\96°編輯。\n"
-"若刪掉此區塊的全部內容,則會中止\n"
-"本次編輯,區塊則不會被修改。\n"
+"å¦\82æ\9e\9cæ²\92æ\9c\89å®\8cå\85¨å¥\97ç\94¨ï¼\8cæ\82¨å\8f¯ä»¥å\86\8d次編輯。\n"
+"若刪掉此區塊的所有內容,則會中止編輯,\n"
+"區塊則不會變更。\n"
 
 #: add-patch.c
 msgid "could not parse hunk header"
@@ -622,36 +624,28 @@ msgstr "無法解析區塊標頭"
 
 #: add-patch.c
 msgid "'git apply --cached' failed"
-msgstr "「git apply --cached」失敗"
+msgstr "“git apply --cached” 失敗"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
-msgstr "未套用編輯區塊。是否重新編輯(輸入 “no” 捨棄!) [y/n]? "
+msgstr "未套用您編輯的區塊。是否重新編輯(輸入 “no” 捨棄!) [y/n]? "
 
 #: add-patch.c
 msgid "The selected hunks do not apply to the index!"
-msgstr "選取的區塊不會套用進索引!"
+msgstr "選取的區塊無法套用至索引!"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Apply them to the worktree anyway? "
-msgstr "無論如何都要套用到工作區嗎 "
+msgstr "無論如何都要套用到工作區嗎? "
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Nothing was applied.\n"
 msgstr "未套用。\n"
 
@@ -687,20 +681,20 @@ msgstr "沒有下一個區塊"
 
 #: add-patch.c
 msgid "No other hunks to goto"
-msgstr "æ²\92æ\9c\89å\85¶å®\83å\8f¯ä¾\9b跳轉的區塊"
+msgstr "æ²\92æ\9c\89å\85¶å®\83å\8f¯ä»¥跳轉的區塊"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "go to which hunk (<ret> to see more)? "
-msgstr "要跳轉到哪個區塊(<Enter> 檢視更多)? "
+msgstr "要跳轉到哪個區塊(<ret> 檢視更多)? "
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "go to which hunk? "
-msgstr "跳轉到哪個區塊 "
+msgstr "跳轉到哪個區塊? "
 
 #: add-patch.c
 #, c-format
 msgid "Invalid number: '%s'"
-msgstr "無效數字:'%s'"
+msgstr "無效數字:“%s”"
 
 #: add-patch.c
 #, c-format
@@ -710,20 +704,20 @@ msgstr[0] "對不起,只有 %d 個可用區塊。"
 
 #: add-patch.c
 msgid "No other hunks to search"
-msgstr "æ²\92æ\9c\89å\85¶å®\83å\8f¯ä¾\9b尋找的區塊"
+msgstr "æ²\92æ\9c\89å\85¶å®\83å\8f¯ä»¥尋找的區塊"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "search for regex? "
-msgstr "使用常規表示式搜尋 "
+msgstr "使用常規表示式搜尋? "
 
 #: add-patch.c
 #, c-format
 msgid "Malformed search regexp %s: %s"
-msgstr "錯誤的常規表示式 %s:%s"
+msgstr "格式錯誤的常規表示式 %s:%s"
 
 #: add-patch.c
 msgid "No hunk matches the given pattern"
-msgstr "沒有和提供模式相符合的區塊"
+msgstr "沒有符合提供模式的區塊"
 
 #: add-patch.c
 msgid "Sorry, cannot split this hunk"
@@ -736,11 +730,11 @@ msgstr "分割為 %d 個區塊。"
 
 #: add-patch.c
 msgid "Sorry, cannot edit this hunk"
-msgstr "對不起,不能編輯這個區塊"
+msgstr "對不起,無法編輯這個區塊"
 
 #: add-patch.c
 msgid "'git apply' failed"
-msgstr "'git apply' 失敗"
+msgstr "“git apply” 失敗"
 
 #: advice.c
 #, c-format
@@ -749,7 +743,7 @@ msgid ""
 "Disable this message with \"git config advice.%s false\""
 msgstr ""
 "\n"
-"請使用「git config advice.%s false」來停用此訊息"
+"請使用 “git config advice.%s false” 停用此訊息"
 
 #: advice.c
 #, c-format
@@ -758,56 +752,73 @@ msgstr "%s提示:%.*s%s\n"
 
 #: advice.c
 msgid "Cherry-picking is not possible because you have unmerged files."
-msgstr "無法揀選,因為您有未合併的檔案。"
+msgstr "無法揀選,有未合併的檔案。"
 
 #: advice.c
 msgid "Committing is not possible because you have unmerged files."
-msgstr "無法提交,因為您有未合併的檔案。"
+msgstr "無法提交,有未合併的檔案。"
 
 #: advice.c
 msgid "Merging is not possible because you have unmerged files."
-msgstr "無法合併,因為您有未合併的檔案。"
+msgstr "無法合併,有未合併的檔案。"
 
 #: advice.c
 msgid "Pulling is not possible because you have unmerged files."
-msgstr "無法拉取,因為您有未合併的檔案。"
+msgstr "無法拉取,有未合併的檔案。"
 
 #: advice.c
 msgid "Reverting is not possible because you have unmerged files."
-msgstr "無法還原提交,因為您有未合併的檔案。"
+msgstr "無法還原提交,有未合併的檔案。"
 
 #: advice.c
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "無法 %s,因為您有未合併的檔案。"
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "無法重定基底,有未合併的檔案。"
 
 #: advice.c
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
 "as appropriate to mark resolution and make a commit."
 msgstr ""
-"請在工作區改正檔案,然後酌情使用 'git add/rm <檔案>' 指令標記\n"
-"解決方案並提交。"
+"請在工作區修正檔案,然後視情況使用 “git add/rm <file>”\n"
+"命令標記解決方案並提交。"
 
 #: advice.c
 msgid "Exiting because of an unresolved conflict."
-msgstr "å\9b ç\82ºå­\98å\9c¨æ\9cªè§£æ±ºç\9a\84è¡\9dçª\81è\80\8cé\9b¢é\96\8bã\80\82"
+msgstr "å­\98å\9c¨æ\9cªè§£æ±ºç\9a\84è¡\9dçª\81ï¼\8cé\9b¢é\96\8bã\80\82"
 
 #: advice.c builtin/merge.c
 msgid "You have not concluded your merge (MERGE_HEAD exists)."
-msgstr "您尚未結束您的合併(存在 MERGE_HEAD)。"
+msgstr "合併尚未結束(有 MERGE_HEAD)。"
 
 #: advice.c
 msgid "Please, commit your changes before merging."
-msgstr "請在合併前先提交您的修改。"
+msgstr "請在合併前先提交您的更動。"
 
 #: advice.c
 msgid "Exiting because of unfinished merge."
-msgstr "因為存在未完成的合併而離開。"
+msgstr "存在未完成的合併,離開。"
+
+#: advice.c
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"岔開的分支不能快轉,您得執行:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"或者:\n"
+"\n"
+"\tgit rebase\n"
 
 #: advice.c
 msgid "Not possible to fast-forward, aborting."
-msgstr "無法快轉,止。"
+msgstr "無法快轉,止。"
 
 #: advice.c
 #, c-format
@@ -852,18 +863,18 @@ msgid ""
 "false\n"
 "\n"
 msgstr ""
-"注意:正在切換到 '%s'。\n"
+"註:切換至 “%s”。\n"
 "\n"
-"您正處於分離開頭指標狀態。您可以檢視、進行實驗性修改並提交,\n"
-"而且您可以在切換回一個分支時,\n"
-"捨棄在此狀態下所做的提交而不對分支造成影響。\n"
+"您正處於「分離 HEAD」狀態。您可以檢視、進行實驗性修改並提交,\n"
+"而且您可以在切回分支時,捨棄在此狀態下所做的提交\n"
+"而不對分支造成影響。\n"
 "\n"
 "如果您想要透過建立分支來保留在此狀態下所做的提交,\n"
 "您可以現在或稍後在 switch 指令使用 -c 選項。例如:\n"
 "\n"
 "  git switch -c <新分支名稱>\n"
 "\n"
-"或者是復原此動作:\n"
+"或者是使用下述命令復原此動作:\n"
 "\n"
 "  git switch -\n"
 "\n"
@@ -887,43 +898,57 @@ msgid ""
 "* Use \"git sparse-checkout reapply\" to apply the sparsity rules"
 msgstr ""
 "若要更正這些路徑的稀疏狀態,請:\n"
-"* 使用 `git add --sparse <路徑>` 更新索引\n"
-"* 使用 `git sparse-checkout reapply` 套用稀疏規則"
+"* 使用 “git add --sparse <路徑” 更新索引\n"
+"* 使用 “git sparse-checkout reapply” 套用稀疏規則"
 
 #: alias.c
 msgid "cmdline ends with \\"
-msgstr "令列以 \\ 結尾"
+msgstr "令列以 \\ 結尾"
 
 #: alias.c
 msgid "unclosed quote"
 msgstr "未閉合的引號"
 
+#: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
+#: builtin/receive-pack.c builtin/tag.c
+msgid "too many arguments"
+msgstr "引數過多"
+
 #: apply.c
 #, c-format
 msgid "unrecognized whitespace option '%s'"
-msgstr "ç\84¡æ³\95è­\98å\88¥ç\9a\84空ç\99½å­\97å\85\83é\81¸é \85 '%s'"
+msgstr "空ç\99½å­\97å\85\83é\81¸é \85 â\80\9c%sâ\80\9d ç\84¡æ³\95è­\98å\88¥"
 
 #: apply.c
 #, c-format
 msgid "unrecognized whitespace ignore option '%s'"
-msgstr "無法識別的空白字元忽略選項 '%s'"
-
-#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout.c
-#: builtin/clone.c builtin/commit.c builtin/describe.c builtin/diff-tree.c
-#: builtin/difftool.c builtin/fast-export.c builtin/fetch.c builtin/help.c
-#: builtin/index-pack.c builtin/init-db.c builtin/log.c builtin/ls-files.c
-#: builtin/merge-base.c builtin/merge.c builtin/pack-objects.c builtin/push.c
-#: builtin/rebase.c builtin/repack.c builtin/reset.c builtin/rev-list.c
-#: builtin/show-branch.c builtin/stash.c builtin/submodule--helper.c
-#: builtin/tag.c builtin/worktree.c parse-options.c range-diff.c revision.c
+msgstr "空白字元忽略選項 “%s” 無法識別"
+
+#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
+#: builtin/checkout.c builtin/clone.c builtin/commit.c builtin/describe.c
+#: builtin/diff-tree.c builtin/difftool.c builtin/fast-export.c builtin/fetch.c
+#: builtin/help.c builtin/index-pack.c builtin/init-db.c builtin/log.c
+#: builtin/ls-files.c builtin/merge-base.c builtin/merge.c
+#: builtin/pack-objects.c builtin/push.c builtin/rebase.c builtin/repack.c
+#: builtin/reset.c builtin/rev-list.c builtin/show-branch.c builtin/stash.c
+#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c
+#: range-diff.c revision.c
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
-msgstr "不能同時使用 '%s' 和 '%s' 選項"
+msgstr "無法同時使用 “%s” 和 “%s” 選項"
 
 #: apply.c
 #, c-format
 msgid "'%s' outside a repository"
-msgstr "'%s' 在版本庫之外"
+msgstr "“%s” 在版本庫之外"
+
+#: apply.c
+msgid "failed to read patch"
+msgstr "無法讀取修補檔"
+
+#: apply.c
+msgid "patch too large"
+msgstr "修補檔過大"
 
 #: apply.c
 #, c-format
@@ -933,42 +958,42 @@ msgstr "無法準備時間戳常規表示式 %s"
 #: apply.c
 #, c-format
 msgid "regexec returned %d for input: %s"
-msgstr "regexec 返回 %d,輸入為:%s"
+msgstr "regexec 回傳 %d,輸入為:%s"
 
 #: apply.c
 #, c-format
 msgid "unable to find filename in patch at line %d"
-msgstr "不能在修補檔的第 %d 行找到檔案名"
+msgstr "無法在修補檔的第 %d 列找到檔案名稱"
 
 #: apply.c
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
-msgstr "git apply:錯誤的 git-diff - 應為 /dev/null,但在第 %2$d 行得到 %1$s"
+msgstr "git apply:無效的 git-diff — 應為 /dev/null,但在第 %2$d 列得到 %1$s"
 
 #: apply.c
 #, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
-msgstr "git apply:錯誤的 git-diff - 第 %d 行上新檔案名不一致"
+msgstr "git apply:無效的 git-diff — 第 %d 列的新檔案名稱不一致"
 
 #: apply.c
 #, c-format
 msgid "git apply: bad git-diff - inconsistent old filename on line %d"
-msgstr "git apply:錯誤的 git-diff - 第 %d 行上舊檔案名不一致"
+msgstr "git apply:無效的 git-diff — 第 %d 列上舊檔案名稱不一致"
 
 #: apply.c
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
-msgstr "git apply:錯誤的 git-diff - 第 %d 行處應為 /dev/null"
+msgstr "git apply:無效的 git-diff — 第 %d 列處預期是 /dev/null"
 
 #: apply.c
 #, c-format
 msgid "invalid mode on line %d: %s"
-msgstr "第 %d 包含無效檔案模式:%s"
+msgstr "第 %d 包含無效檔案模式:%s"
 
 #: apply.c
 #, c-format
 msgid "inconsistent header lines %d and %d"
-msgstr "不一致的檔案頭,%d 行和 %d 行"
+msgstr "不一致的檔案標頭(%d 列和 %d 列)"
 
 #: apply.c
 #, c-format
@@ -978,22 +1003,22 @@ msgid ""
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
 "components (line %d)"
-msgstr[0] "ç\95¶ç§»é\99¤ %d å\80\8bå\89\8då°\8eè·¯å¾\91å¾\8c git diff é ­ç¼ºä¹\8fæª\94æ¡\88å\90\8dè¨\8aæ\81¯ï¼\88第 %d è¡\8c)"
+msgstr[0] "移é\99¤ %d å\80\8bå\89\8då°\8eè·¯å¾\91é\83¨å\88\86å¾\8cï¼\8cgit diff æ¨\99頭缺å°\91æª\94æ¡\88å\90\8d稱è³\87è¨\8aï¼\88第 %d å\88\97)"
 
 #: apply.c
 #, c-format
 msgid "git diff header lacks filename information (line %d)"
-msgstr "git diff 的標頭訊息中缺乏檔案名訊息(第 %d 行)"
+msgstr "git diff 標頭缺少檔案名稱資訊(第 %d 列)"
 
 #: apply.c
 #, c-format
 msgid "recount: unexpected line: %.*s"
-msgstr "recount:意外的行:%.*s"
+msgstr "recount:遇到非預期的列:%.*s"
 
 #: apply.c
 #, c-format
 msgid "patch fragment without header at line %d: %.*s"
-msgstr "第 %d 行的修補檔區塊沒有標頭訊息:%.*s"
+msgstr "第 %d 列的修補檔區段沒有標頭:%.*s"
 
 #: apply.c
 msgid "new file depends on old contents"
@@ -1006,7 +1031,7 @@ msgstr "刪除的檔案仍有內容"
 #: apply.c
 #, c-format
 msgid "corrupt patch at line %d"
-msgstr "修補檔在第 %d 發現損壞"
+msgstr "修補檔在第 %d 發現損壞"
 
 #: apply.c
 #, c-format
@@ -1026,17 +1051,17 @@ msgstr "** 警告:檔案 %s 成為空檔案但並未刪除"
 #: apply.c
 #, c-format
 msgid "corrupt binary patch at line %d: %.*s"
-msgstr "二進位修補檔在第 %d 損壞:%.*s"
+msgstr "二進位修補檔在第 %d 損壞:%.*s"
 
 #: apply.c
 #, c-format
 msgid "unrecognized binary patch at line %d"
-msgstr "ç\84¡æ³\95è­\98å\88¥ç\9a\84äº\8cé\80²ä½\8dä¿®è£\9cæª\94ä½\8dæ\96¼ç¬¬ %d è¡\8c"
+msgstr "第 %d å\88\97ç\9a\84äº\8cé\80²ä½\8dä¿®è£\9cæª\94ç\84¡æ³\95è­\98å\88¥"
 
 #: apply.c
 #, c-format
 msgid "patch with only garbage at line %d"
-msgstr "修補檔案的第 %d 只有垃圾資料"
+msgstr "修補檔案的第 %d 只有垃圾資料"
 
 #: apply.c
 #, c-format
@@ -1046,23 +1071,23 @@ msgstr "無法讀取符號連結 %s"
 #: apply.c
 #, c-format
 msgid "unable to open or read %s"
-msgstr "不能開啟或讀取 %s"
+msgstr "無法開啟或讀取 %s"
 
 #: apply.c
 #, c-format
 msgid "invalid start of line: '%c'"
-msgstr "無效的行首字元:'%c'"
+msgstr "無效的列首字元:“%c”"
 
 #: apply.c
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
 msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
-msgstr[0] "區塊 #%d 成功套用於 %d(位移 %d 行)。"
+msgstr[0] "區塊 #%d 成功套用於 %d(偏移 %d 列)。"
 
 #: apply.c
 #, c-format
 msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
-msgstr "上下文減少到(%ld/%ld)以在第 %d 套用修補檔區塊"
+msgstr "上下文減少到(%ld/%ld)以在第 %d 套用修補檔區塊"
 
 #: apply.c
 #, c-format
@@ -1076,53 +1101,53 @@ msgstr ""
 #: apply.c
 #, c-format
 msgid "missing binary patch data for '%s'"
-msgstr "缺少 '%s' 的二進位修補檔資料"
+msgstr "缺少 “%s” 的二進位修補檔資料"
 
 #: apply.c
 #, c-format
 msgid "cannot reverse-apply a binary patch without the reverse hunk to '%s'"
-msgstr "不能反向套用一個缺少到 '%s' 的反向資料區塊的二進位修補檔"
+msgstr "無法反向套用一個缺少至 “%s” 的反向資料區塊的二進位修補檔"
 
 #: apply.c
 #, c-format
 msgid "cannot apply binary patch to '%s' without full index line"
-msgstr "不能在 '%s' 上套用沒有完整索引行的二進位修補檔"
+msgstr "無法在 “%s” 上套用沒有完整索引列的二進位修補檔"
 
 #: apply.c
 #, c-format
 msgid ""
 "the patch applies to '%s' (%s), which does not match the current contents."
-msgstr "修補檔套用到 '%s'(%s),但是和目前內容不符合。"
+msgstr "修補檔要套用到 “%s”(%s),但與目前內容不符。"
 
 #: apply.c
 #, c-format
 msgid "the patch applies to an empty '%s' but it is not empty"
-msgstr "修補檔套用到空檔案 '%s',但其並非空檔案"
+msgstr "修補檔要套用至空檔案 “%s”,但其非空檔案"
 
 #: apply.c
 #, c-format
 msgid "the necessary postimage %s for '%s' cannot be read"
-msgstr "無法讀取 '%2$s' 必需的目標檔案 %1$s"
+msgstr "無法讀取 “%2$s” 必須的目標檔案 %1$s"
 
 #: apply.c
 #, c-format
 msgid "binary patch does not apply to '%s'"
-msgstr "二進位修補檔未套用到 '%s'"
+msgstr "二進位修補檔未套用到 “%s”"
 
 #: apply.c
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
-msgstr "到 '%s' 的二進位修補檔產生了不正確的結果(應為 %s,卻為 %s)"
+msgstr "修補 “%s” 的二進位修補檔,產生了不正確的結果(預期 %s,卻為 %s)"
 
 #: apply.c
 #, c-format
 msgid "patch failed: %s:%ld"
-msgstr "打修補檔失敗:%s:%ld"
+msgstr "修補失敗:%s:%ld"
 
 #: apply.c builtin/mv.c
 #, c-format
 msgid "cannot checkout %s"
-msgstr "不能簽出 %s"
+msgstr "無法簽出 %s"
 
 #: apply.c midx.c pack-mtimes.c pack-revindex.c setup.c
 #, c-format
@@ -1132,51 +1157,51 @@ msgstr "無法讀取 %s"
 #: apply.c
 #, c-format
 msgid "reading from '%s' beyond a symbolic link"
-msgstr "讀取位於符號連結中的 '%s'"
+msgstr "讀取符號連結背後的 “%s”"
 
 #: apply.c
 #, c-format
 msgid "path %s has been renamed/deleted"
-msgstr "路徑 %s 已經被重新命名/刪除"
+msgstr "路徑 %s 已被重新命名或刪除"
 
 #: apply.c
 #, c-format
 msgid "%s: does not exist in index"
-msgstr "%sï¼\9aä¸\8då­\98å\9c¨æ\96¼索引中"
+msgstr "%sï¼\9aä¸\8då\9c¨索引中"
 
 #: apply.c
 #, c-format
 msgid "%s: does not match index"
-msgstr "%s:和索引不符合"
+msgstr "%s:與索引不符"
 
 #: apply.c
 msgid "repository lacks the necessary blob to perform 3-way merge."
-msgstr "版本庫缺少用來進行三方合併所需要的資料物件。"
+msgstr "版本庫缺少用來進行三方合併所需要的資料物件。"
 
 #: apply.c
 #, c-format
 msgid "Performing three-way merge...\n"
-msgstr "æ­£å\9c¨é\80²è¡\8cä¸\89æ\96¹å\90\88ä½µâ\8b¯â\8b¯\n"
+msgstr "æ­£å\9c¨é\80²è¡\8cä¸\89æ\96¹å\90\88ä½µâ\80¦â\80¦\n"
 
 #: apply.c
 #, c-format
 msgid "cannot read the current contents of '%s'"
-msgstr "無法讀取 '%s' 的目前內容"
+msgstr "無法讀取 “%s” 目前的內容"
 
 #: apply.c
 #, c-format
 msgid "Failed to perform three-way merge...\n"
-msgstr "ç\84¡æ³\95é\80²è¡\8cä¸\89æ\96¹å\90\88ä½µâ\8b¯â\8b¯\n"
+msgstr "ç\84¡æ³\95é\80²è¡\8cä¸\89æ\96¹å\90\88ä½µâ\80¦â\80¦\n"
 
 #: apply.c
 #, c-format
 msgid "Applied patch to '%s' with conflicts.\n"
-msgstr "å¥\97ç\94¨ä¿®è£\9cæª\94å\88° '%s' å­\98å\9c¨衝突。\n"
+msgstr "å·²å¥\97ç\94¨å°\8d â\80\9c%sâ\80\9d ç\9a\84ä¿®è£\9cæª\94ï¼\8cä½\86æ\9c\89衝突。\n"
 
 #: apply.c
 #, c-format
 msgid "Applied patch to '%s' cleanly.\n"
-msgstr "成功套用修補檔到 '%s'。\n"
+msgstr "已完全套用對 “%s” 的修補檔。\n"
 
 #: apply.c
 #, c-format
@@ -1185,7 +1210,7 @@ msgstr "回復至直接套用模式⋯⋯\n"
 
 #: apply.c
 msgid "removal patch leaves file contents"
-msgstr "移除修補檔仍留下了檔案內容"
+msgstr "移除性的修補檔仍有留下檔案內容"
 
 #: apply.c
 #, c-format
@@ -1195,117 +1220,117 @@ msgstr "%s:錯誤類型"
 #: apply.c
 #, c-format
 msgid "%s has type %o, expected %o"
-msgstr "%s 的類型是 %o,應為 %o"
+msgstr "%s 的類型是 %o,預期是 %o"
 
 #: apply.c read-cache.c
 #, c-format
 msgid "invalid path '%s'"
-msgstr "無效路徑 '%s'"
+msgstr "路徑 “%s” 無效"
 
 #: apply.c
 #, c-format
 msgid "%s: already exists in index"
-msgstr "%s:已存在於索引中"
+msgstr "%s:已存在於索引中"
 
 #: apply.c
 #, c-format
 msgid "%s: already exists in working directory"
-msgstr "%s:已存在於工作區中"
+msgstr "%s:已存在於工作區中"
 
 #: apply.c
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o)"
-msgstr "%2$s 的新模式(%1$o)和舊模式(%3$o)不符合"
+msgstr "%2$s 的新模式 (%1$o) 和舊模式 (%3$o) 不符"
 
 #: apply.c
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
-msgstr "%2$s 的新模式(%1$o)和 %4$s 的舊模式(%3$o)不符合"
+msgstr "%2$s 的新模式 (%1$o) 和 %4$s 的舊模式 (%3$o) 不符"
 
 #: apply.c
 #, c-format
 msgid "affected file '%s' is beyond a symbolic link"
-msgstr "受影響的檔案 '%s' 位於符號連結中"
+msgstr "受影響的檔案 “%s” 在符號連結後"
 
 #: apply.c
 #, c-format
 msgid "%s: patch does not apply"
-msgstr "%s:修補檔未套用"
+msgstr "%s:未套用修補檔"
 
 #: apply.c
 #, c-format
 msgid "Checking patch %s..."
-msgstr "正在檢查修補檔 %s..."
+msgstr "正在檢查修補檔 %s……"
 
 #: apply.c
 #, c-format
 msgid "sha1 information is lacking or useless for submodule %s"
-msgstr "子模組 %s 的 sha1 訊息缺少或無效"
+msgstr "%s 子模組缺少 sha1 資訊或沒有幫助"
 
 #: apply.c
 #, c-format
 msgid "mode change for %s, which is not in current HEAD"
-msgstr "%s 的模式變更,但它不在目前 HEAD 中"
+msgstr "%s 的模式有更動,但其不在目前 HEAD 中"
 
 #: apply.c
 #, c-format
 msgid "sha1 information is lacking or useless (%s)."
-msgstr "sha1 訊息缺少或無效(%s)。"
+msgstr "缺少 sha1 資訊或沒有幫助 (%s)。"
 
 #: apply.c
 #, c-format
 msgid "could not add %s to temporary index"
-msgstr "不能在暫時索引中新增 %s"
+msgstr "無法將 %s 加進暫存索引"
 
 #: apply.c
 #, c-format
 msgid "could not write temporary index to %s"
-msgstr "不能把暫時索引寫入到 %s"
+msgstr "無法將暫存索引寫入 %s"
 
 #: apply.c
 #, c-format
 msgid "unable to remove %s from index"
-msgstr "不能從索引中移除 %s"
+msgstr "無法從索引移除 %s"
 
 #: apply.c
 #, c-format
 msgid "corrupt patch for submodule %s"
-msgstr "子模組 %s 損壞的修補檔"
+msgstr "修補 %s 子模組的修補檔損壞"
 
 #: apply.c
 #, c-format
 msgid "unable to stat newly created file '%s'"
-msgstr "不能對建立檔案 '%s' 呼叫 stat"
+msgstr "無法對剛建立的檔案 “%s” 執行 stat"
 
 #: apply.c
 #, c-format
 msgid "unable to create backing store for newly created file %s"
-msgstr "不能為建立檔案 %s 建立後端儲存"
+msgstr "無法對剛建立的檔案 %s 建立後端儲存"
 
 #: apply.c
 #, c-format
 msgid "unable to add cache entry for %s"
-msgstr "無法為 %s 新增快取條目"
+msgstr "無法為 %s 加入快取項目"
 
-#: apply.c builtin/bisect--helper.c builtin/gc.c
+#: apply.c builtin/bisect.c builtin/gc.c
 #, c-format
 msgid "failed to write to '%s'"
-msgstr "寫入 '%s' 失敗"
+msgstr "無法寫入 “%s”"
 
 #: apply.c
 #, c-format
 msgid "closing file '%s'"
-msgstr "關閉檔案 '%s'"
+msgstr "關閉檔案 “%s”"
 
 #: apply.c
 #, c-format
 msgid "unable to write file '%s' mode %o"
-msgstr "不能寫入檔案 '%s' 權限 %o"
+msgstr "無法以模式 %2$o 寫入 “%1$s” 檔案"
 
 #: apply.c
 #, c-format
 msgid "Applied patch %s cleanly."
-msgstr "成功套用修補檔 %s。"
+msgstr "已完全套用 %s 修補檔。"
 
 #: apply.c
 msgid "internal error"
@@ -1315,22 +1340,27 @@ msgstr "內部錯誤"
 #, c-format
 msgid "Applying patch %%s with %d reject..."
 msgid_plural "Applying patch %%s with %d rejects..."
-msgstr[0] "套用 %%s 個修補檔,其中 %d 個被拒絕..."
+msgstr[0] "套用 %%s 個修補檔,其中 %d 個被拒絕……"
 
 #: apply.c
 #, c-format
 msgid "truncating .rej filename to %.*s.rej"
-msgstr "æ\88ªç\9f­ .rej æª\94æ¡\88å\90\8d為 %.*s.rej"
+msgstr "æ­£å\9c¨å°\87 .rej æª\94æ¡\88å\90\8d稱æ\88ªç\9f­為 %.*s.rej"
 
 #: apply.c
 #, c-format
 msgid "cannot open %s"
-msgstr "不能開啟 %s"
+msgstr "無法開啟 %s"
+
+#: apply.c rerere.c
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "無法刪除 “%s”"
 
 #: apply.c
 #, c-format
 msgid "Hunk #%d applied cleanly."
-msgstr "成功套用第 #%d 個區塊。"
+msgstr "æ\88\90å\8a\9få®\8cå\85¨å¥\97ç\94¨ç¬¬ #%d å\80\8bå\8d\80å¡\8aã\80\82"
 
 #: apply.c
 #, c-format
@@ -1340,20 +1370,20 @@ msgstr "拒絕第 #%d 個區塊。"
 #: apply.c
 #, c-format
 msgid "Skipped patch '%s'."
-msgstr "略過修補檔 '%s'。"
+msgstr "略過修補檔 “%s”。"
 
 #: apply.c
 msgid "No valid patches in input (allow with \"--allow-empty\")"
-msgstr "輸入中沒有有效的修補檔內容(傳入「--allow-empty」允許)"
+msgstr "輸入沒有有效的修補檔內容(傳入 “--allow-empty” 允許此行為)"
 
-#: apply.c
+#: apply.c t/helper/test-cache-tree.c
 msgid "unable to read index file"
 msgstr "無法讀取索引檔案"
 
 #: apply.c
 #, c-format
 msgid "can't open patch '%s': %s"
-msgstr "不能開啟修補檔 '%s':%s"
+msgstr "無法開啟修補檔 “%s”:%s"
 
 #: apply.c
 #, c-format
@@ -1365,25 +1395,25 @@ msgstr[0] "抑制下仍有 %d 個空白字元誤用"
 #, c-format
 msgid "%d line adds whitespace errors."
 msgid_plural "%d lines add whitespace errors."
-msgstr[0] "%d 行新增了空白字元誤用。"
+msgstr[0] "%d 列加入了空白字元誤用。"
 
 #: apply.c
 #, c-format
 msgid "%d line applied after fixing whitespace errors."
 msgid_plural "%d lines applied after fixing whitespace errors."
-msgstr[0] "修復空白錯誤後,套用了 %d 行。"
+msgstr[0] "修正空白誤用後,套用了 %d 列。"
 
-#: apply.c builtin/add.c builtin/mv.c builtin/rm.c
+#: apply.c builtin/mv.c builtin/rm.c
 msgid "Unable to write new index file"
 msgstr "無法寫入新索引檔案"
 
 #: apply.c
 msgid "don't apply changes matching the given path"
-msgstr "不要套用符合提供路徑的變更"
+msgstr "不要套用符合提供路徑的更動"
 
 #: apply.c
 msgid "apply changes matching the given path"
-msgstr "套用符合提供路徑的變更"
+msgstr "套用符合提供路徑的更動"
 
 #: apply.c builtin/am.c
 msgid "num"
@@ -1391,19 +1421,19 @@ msgstr "數字"
 
 #: apply.c
 msgid "remove <num> leading slashes from traditional diff paths"
-msgstr "從傳統的 diff 路徑中移除指定數量的前導斜線"
+msgstr "從傳統的 diff 路徑中移除 <num> 個前導斜線"
 
 #: apply.c
 msgid "ignore additions made by the patch"
-msgstr "忽略修補檔中的新增的檔案"
+msgstr "忽略修補檔中的加入內容"
 
 #: apply.c
 msgid "instead of applying the patch, output diffstat for the input"
-msgstr "不套用修補檔,而是顯示輸入的差異統計(diffstat)"
+msgstr "不套用修補檔,而是顯示輸入的差異統計 (diffstat)"
 
 #: apply.c
 msgid "show number of added and deleted lines in decimal notation"
-msgstr "以十進位數顯示新增和刪除的行數"
+msgstr "以十進位數字,顯示加入和刪除的列數"
 
 #: apply.c
 msgid "instead of applying the patch, output a summary for the input"
@@ -1419,7 +1449,7 @@ msgstr "確認修補檔可以套用到目前索引"
 
 #: apply.c
 msgid "mark new files with `git add --intent-to-add`"
-msgstr "使用指令 `git add --intent-to-add` 標記新增檔案"
+msgstr "使用 `git add --intent-to-add` 命令標記新檔案"
 
 #: apply.c
 msgid "apply a patch without touching the working tree"
@@ -1431,15 +1461,15 @@ msgstr "接受修改工作區之外檔案的修補檔"
 
 #: apply.c
 msgid "also apply the patch (use with --stat/--summary/--check)"
-msgstr "還套用此修補檔(與 --stat/--summary/--check 選項同時使用)"
+msgstr "亦套用修補檔(與 --stat/--summary/--check 選項同時使用)"
 
 #: apply.c
 msgid "attempt three-way merge, fall back on normal patch if that fails"
-msgstr "嘗試三方合併。如果失敗,則回到正常修補檔 (patch) 模式"
+msgstr "嘗試三方合併,若失敗則回到正常修補模式"
 
 #: apply.c
 msgid "build a temporary index based on embedded index information"
-msgstr "建立一個暫時索引基於嵌入的索引訊息"
+msgstr "組建以嵌入索引資訊為基礎的暫存索引"
 
 #: apply.c builtin/checkout-index.c
 msgid "paths are separated with NUL character"
@@ -1447,7 +1477,7 @@ msgstr "路徑以 NUL 字元分隔"
 
 #: apply.c
 msgid "ensure at least <n> lines of context match"
-msgstr "確保至少符合 <n> 上下文"
+msgstr "確保至少符合 <n> 上下文"
 
 #: apply.c builtin/am.c builtin/interpret-trailers.c builtin/pack-objects.c
 #: builtin/rebase.c
@@ -1456,11 +1486,11 @@ msgstr "動作"
 
 #: apply.c
 msgid "detect new or modified lines that have whitespace errors"
-msgstr "檢查新增和修改的行中間的空白字元濫用"
+msgstr "檢查新增和修改的列中間,是否有空白字元誤用"
 
 #: apply.c
 msgid "ignore changes in whitespace when finding context"
-msgstr "尋找上下文時忽略空白字元的變更"
+msgstr "尋找上下文時忽略空白字元更動"
 
 #: apply.c
 msgid "apply the patch in reverse"
@@ -1468,23 +1498,23 @@ msgstr "反向套用修補檔"
 
 #: apply.c
 msgid "don't expect at least one line of context"
-msgstr "無需至少一上下文"
+msgstr "無需至少一上下文"
 
 #: apply.c
 msgid "leave the rejected hunks in corresponding *.rej files"
-msgstr "將拒絕的修補檔區塊儲存在對應的 *.rej 檔案中"
+msgstr "將拒絕的修補檔區塊,留在對應的 *.rej 檔案中"
 
 #: apply.c
 msgid "allow overlapping hunks"
-msgstr "允許重疊的修補檔區塊"
+msgstr "允許重疊區塊"
 
 #: apply.c
 msgid "tolerate incorrectly detected missing new-line at the end of file"
-msgstr "允許不正確的檔案末尾換符號"
+msgstr "允許不正確的檔案末尾換符號"
 
 #: apply.c
 msgid "do not trust the line counts in the hunk headers"
-msgstr "不信任修補檔區塊的標頭訊息中的行號"
+msgstr "不信任區塊標頭中的列號"
 
 #: apply.c builtin/am.c
 msgid "root"
@@ -1492,21 +1522,21 @@ msgstr "根目錄"
 
 #: apply.c
 msgid "prepend <root> to all filenames"
-msgstr "為所有檔案名前新增 <根目錄>"
+msgstr "在所有檔案名稱前加上 <root>"
 
 #: apply.c
 msgid "don't return error for empty patches"
-msgstr "遇到空白修補檔時不回傳錯誤"
+msgstr "遇到空白修補檔時不回傳錯誤"
 
 #: archive-tar.c archive-zip.c
 #, c-format
 msgid "cannot stream blob %s"
-msgstr "不能開啟資料物件 %s"
+msgstr "無法串流資料物件 %s"
 
 #: archive-tar.c archive-zip.c
 #, c-format
 msgid "unsupported file mode: 0%o (SHA1: %s)"
-msgstr "不支援的檔案模式:0%o (SHA1%s)"
+msgstr "不支援的檔案模式:0%o (SHA1%s)"
 
 #: archive-tar.c archive-zip.c builtin/pack-objects.c
 #, c-format
@@ -1516,16 +1546,16 @@ msgstr "壓縮錯誤 (%d)"
 #: archive-tar.c
 #, c-format
 msgid "unable to start '%s' filter"
-msgstr "無法啟動 '%s' 過濾器"
+msgstr "無法啟動 “%s” 過濾器"
 
 #: archive-tar.c
 msgid "unable to redirect descriptor"
-msgstr "無法重定向描述符"
+msgstr "無法重新導向描述元"
 
 #: archive-tar.c
 #, c-format
 msgid "'%s' filter reported error"
-msgstr "'%s' 過濾器報告了錯誤"
+msgstr "“%s” 過濾器回報錯誤"
 
 #: archive-zip.c
 #, c-format
@@ -1535,36 +1565,41 @@ msgstr "路徑不是有效的 UTF-8:%s"
 #: archive-zip.c
 #, c-format
 msgid "path too long (%d chars, SHA1: %s): %s"
-msgstr "路徑太長(%d 字元,SHA1:%s):%s"
+msgstr "è·¯å¾\91太é\95·ï¼\88%d å\80\8bå­\97å\85\83ï¼\8cSHA1ï¼\9a%sï¼\89ï¼\9a%s"
 
 #: archive-zip.c
 #, c-format
 msgid "timestamp too large for this system: %<PRIuMAX>"
-msgstr "å°\8dæ\96¼æ\9c¬ç³»çµ±時間戳太大:%<PRIuMAX>"
+msgstr "å°\8dæ\9c¬ç³»çµ±è\80\8cè¨\80時間戳太大:%<PRIuMAX>"
 
 #: archive.c
 msgid "git archive [<options>] <tree-ish> [<path>...]"
-msgstr "git archive [<選項>] <樹或提交> [<路徑>...]"
+msgstr "git archive [<options>] <tree-ish> [<path>...]"
 
 #: archive.c
 msgid ""
 "git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]"
 msgstr ""
-"git archive --remote <版本庫> [--exec <命令>] [<選項>] <樹或提交> [<路徑>...]"
+"git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]"
 
 #: archive.c
 msgid "git archive --remote <repo> [--exec <cmd>] --list"
-msgstr "git archive --remote <版本庫> [--exec <命令>] --list"
+msgstr "git archive --remote <repo> [--exec <cmd>] --list"
 
 #: archive.c builtin/gc.c builtin/notes.c builtin/tag.c
 #, c-format
 msgid "cannot read '%s'"
-msgstr "不能讀取 '%s'"
+msgstr "無法讀取 “%s”"
+
+#: archive.c
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "符合路徑規格 “%s” 的檔案在目前目錄之外"
 
 #: archive.c builtin/add.c builtin/rm.c
 #, c-format
 msgid "pathspec '%s' did not match any files"
-msgstr "路徑規格 '%s' 未符合任何檔案"
+msgstr "路徑規格 “%s” 未符合任何檔案"
 
 #: archive.c
 #, c-format
@@ -1574,16 +1609,12 @@ msgstr "無此引用:%.*s"
 #: archive.c
 #, c-format
 msgid "not a valid object name: %s"
-msgstr "不是一個有效的物件名:%s"
+msgstr "非有效物件名稱:%s"
 
-#: archive.c
+#: archive.c t/helper/test-cache-tree.c
 #, c-format
 msgid "not a tree object: %s"
-msgstr "不是一個樹狀物件:%s"
-
-#: archive.c
-msgid "current working directory is untracked"
-msgstr "目前工作目錄未被追蹤"
+msgstr "非樹狀物件:%s"
 
 #: archive.c
 #, c-format
@@ -1598,59 +1629,68 @@ msgstr "不是一般檔案:%s"
 #: archive.c
 #, c-format
 msgid "unclosed quote: '%s'"
-msgstr "未閉合的引號:「%s」"
+msgstr "未閉合的引號:“%s”"
 
 #: archive.c
 #, c-format
 msgid "missing colon: '%s'"
-msgstr "缺少冒號:「%s」"
+msgstr "缺少冒號:“%s”"
 
 #: archive.c
 #, c-format
 msgid "empty file name: '%s'"
-msgstr "檔案名稱空白:「%s」"
+msgstr "檔案名稱空白:“%s”"
 
 #: archive.c
 msgid "fmt"
-msgstr "格式"
+msgstr "fmt"
 
 #: archive.c
 msgid "archive format"
-msgstr "歸檔格式"
+msgstr "封存格式"
 
-#: archive.c builtin/log.c
+#: archive.c builtin/log.c parse-options.h
 msgid "prefix"
 msgstr "前綴"
 
 #: archive.c
 msgid "prepend prefix to each pathname in the archive"
-msgstr "為歸檔中每個路徑名加上前綴"
+msgstr "為封存中的每個路徑名稱加上前綴"
 
 #: archive.c builtin/blame.c builtin/commit-tree.c builtin/config.c
-#: builtin/fast-export.c builtin/grep.c builtin/hash-object.c
+#: builtin/fast-export.c builtin/gc.c builtin/grep.c builtin/hash-object.c
 #: builtin/ls-files.c builtin/notes.c builtin/read-tree.c parse-options.h
 msgid "file"
-msgstr "檔案"
+msgstr "file"
 
 #: archive.c
 msgid "add untracked file to archive"
-msgstr "將未追蹤檔案加入歸檔"
+msgstr "將未追蹤檔案加進封存"
 
 #: archive.c
 msgid "path:content"
-msgstr "路徑:內容"
+msgstr "path:content"
 
 #: archive.c builtin/archive.c
 msgid "write the archive to this file"
-msgstr "歸檔寫入此檔案"
+msgstr "將封存寫入此檔案"
 
 #: archive.c
 msgid "read .gitattributes in working directory"
-msgstr "讀取工作中的 .gitattributes"
+msgstr "讀取工作目錄中的 .gitattributes"
 
 #: archive.c
 msgid "report archived files on stderr"
-msgstr "在標準錯誤上報告歸檔檔案"
+msgstr "在 stderr 上回報封存的檔案"
+
+#: archive.c builtin/clone.c builtin/fetch.c builtin/pack-objects.c
+#: builtin/pull.c
+msgid "time"
+msgstr "time"
+
+#: archive.c
+msgid "set modification time of archive entries"
+msgstr "設定封存項目的修改時間"
 
 #: archive.c
 msgid "set compression level"
@@ -1658,27 +1698,27 @@ msgstr "設定壓縮級別"
 
 #: archive.c
 msgid "list supported archive formats"
-msgstr "列出支援的歸檔格式"
+msgstr "列出支援的封存格式"
 
 #: archive.c builtin/archive.c builtin/clone.c builtin/submodule--helper.c
 msgid "repo"
-msgstr "版本庫"
+msgstr "repo"
 
 #: archive.c builtin/archive.c
 msgid "retrieve the archive from remote repository <repo>"
-msgstr "從遠端版本庫(<版本庫>)擷取歸檔檔案"
+msgstr "從遠端版本庫 <repo> 擷取封存檔案"
 
 #: archive.c builtin/archive.c builtin/difftool.c builtin/notes.c
 msgid "command"
-msgstr "指令"
+msgstr "command"
 
 #: archive.c builtin/archive.c
 msgid "path to the remote git-upload-archive command"
-msgstr "遠端 git-upload-archive 令的路徑"
+msgstr "遠端 git-upload-archive 令的路徑"
 
 #: archive.c
 msgid "Unexpected option --remote"
-msgstr "未知參數 --remote"
+msgstr "非預期選項 --remote"
 
 #: archive.c builtin/add.c builtin/checkout.c builtin/clone.c builtin/commit.c
 #: builtin/fast-export.c builtin/index-pack.c builtin/log.c builtin/reset.c
@@ -1686,26 +1726,35 @@ msgstr "未知參數 --remote"
 #: revision.c
 #, c-format
 msgid "the option '%s' requires '%s'"
-msgstr "「%s」選項需要「%s」"
+msgstr "“%s” 選項需要 “%s”"
 
 #: archive.c
 msgid "Unexpected option --output"
-msgstr "未知參數 --output"
+msgstr "非預期選項 --output"
 
 #: archive.c
 #, c-format
 msgid "Unknown archive format '%s'"
-msgstr "未知歸檔格式 '%s'"
+msgstr "封存格式 “%s” 未知"
 
 #: archive.c
 #, c-format
 msgid "Argument not supported for format '%s': -%d"
-msgstr "å\8f\83æ\95¸ä¸\8dæ\94¯æ\8f´æ­¤æ ¼å¼\8f '%s':-%d"
+msgstr "å¼\95æ\95¸ä¸\8dæ\94¯æ\8f´ â\80\9c%sâ\80\9d æ ¼å¼\8f:-%d"
 
 #: attr.c
 #, c-format
 msgid "%.*s is not a valid attribute name"
-msgstr "%.*s 不是一個有效的屬性名"
+msgstr "%.*s 不是有效的屬性名稱"
+
+#: attr.c
+msgid "unable to add additional attribute"
+msgstr "無法加入其他屬性"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "忽略過長的屬性列 (第 %d 列)"
 
 #: attr.c
 #, c-format
@@ -1717,23 +1766,42 @@ msgid ""
 "Negative patterns are ignored in git attributes\n"
 "Use '\\!' for literal leading exclamation."
 msgstr ""
-"反向模式在 git attributes 中被忽略\n"
-"當字串確定要以驚嘆號開始時,使用 '\\!'。"
+"git attributes 會忽略反向模式\n"
+"當字串確定要以驚嘆號開始時,請使用 “\\!”。"
+
+#: attr.c
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "無法 fstat gitattributes 檔案 “%s”"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "忽略過大的 gitattributes 檔案 “%s”"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "忽略過大的 gitattributes 資料物件 “%s”"
+
+#: attr.c
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "無效的 --attr-source 或 GIT_ATTR_SOURCE"
 
 #: bisect.c
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
-msgstr "檔案 '%s' 包含錯誤的引用格式:%s"
+msgstr "檔案 “%s” 包含無效的引用內容:%s"
 
 #: bisect.c
 #, c-format
 msgid "We cannot bisect more!\n"
-msgstr "我們無法進行更多的二分搜尋!\n"
+msgstr "已經無法繼續二分搜尋!\n"
 
 #: bisect.c
 #, c-format
 msgid "Not a valid commit name %s"
-msgstr "不是一個有效的提交名 %s"
+msgstr "%s 不是有效的提交名稱"
 
 #: bisect.c
 #, c-format
@@ -1742,7 +1810,7 @@ msgid ""
 "This means the bug has been fixed between %s and [%s].\n"
 msgstr ""
 "合併基礎 %s 是壞的。\n"
-"這意味著介於 %s 和 [%s] 之間的 bug 已經被修復。\n"
+"這意味著 bug 已經在 %s 和 [%s] 之間被修正。\n"
 
 #: bisect.c
 #, c-format
@@ -1751,7 +1819,7 @@ msgid ""
 "The property has changed between %s and [%s].\n"
 msgstr ""
 "合併基礎 %s 是新的。\n"
-"介於 %s 和 [%s] 之間的屬性已被修改。\n"
+"介於 %s 和 [%s] 之間的屬性已被修改。\n"
 
 #: bisect.c
 #, c-format
@@ -1760,7 +1828,7 @@ msgid ""
 "This means the first '%s' commit is between %s and [%s].\n"
 msgstr ""
 "合併基礎 %s 是 %s。\n"
-"這意味著第一個 '%s' 提交位於 %s 和 [%s] 之間。\n"
+"這意味著第一個 “%s” 提交位於 %s 和 [%s] 之間。\n"
 
 #: bisect.c
 #, c-format
@@ -1769,9 +1837,9 @@ msgid ""
 "git bisect cannot work properly in this case.\n"
 "Maybe you mistook %s and %s revs?\n"
 msgstr ""
-"一些 %s 版本不是 %s 版本的祖先。\n"
-"這種情況下 git 二分搜尋無法正常工作。\n"
-"您可能弄錯了 %s 和 %s 版本?\n"
+"部分 %s 修訂版不是 %s 修訂版的祖先。\n"
+"這種情況下 git bisect 無法正常運作。\n"
+"您可能弄錯了 %s 和 %s 修訂版?\n"
 
 #: bisect.c
 #, c-format
@@ -1780,33 +1848,33 @@ msgid ""
 "So we cannot be sure the first %s commit is between %s and %s.\n"
 "We continue anyway."
 msgstr ""
-"介於 %s 和 [%s] 的合併基礎一定被忽略了。\n"
+"介於 %s 和 [%s] 的合併基礎一定被略過了。\n"
 "所以我們無法確認第一個 %s 提交是否介於 %s 和 %s 之間。\n"
 "我們仍舊繼續。"
 
 #: bisect.c
 #, c-format
 msgid "Bisecting: a merge base must be tested\n"
-msgstr "二分搜尋中:合併基礎必須是經過測試的\n"
+msgstr "二分搜尋中:合併基礎必須經過測試\n"
 
 #: bisect.c
 #, c-format
 msgid "a %s revision is needed"
-msgstr "需要一個 %s 版本"
+msgstr "需要一個 %s 修訂版"
 
 #: bisect.c
 #, c-format
 msgid "could not create file '%s'"
-msgstr "不能建立檔案 '%s'"
+msgstr "無法建立 “%s” 檔案"
 
 #: bisect.c builtin/merge.c
 #, c-format
 msgid "could not read file '%s'"
-msgstr "不能讀取檔案 '%s'"
+msgstr "無法讀取 “%s” 檔案"
 
 #: bisect.c
 msgid "reading bisect refs failed"
-msgstr "讀取二分搜尋引用失敗"
+msgstr "無法讀取二分搜尋的引用"
 
 #: bisect.c
 #, c-format
@@ -1820,13 +1888,13 @@ msgid ""
 "Maybe you started with bad path arguments?\n"
 msgstr ""
 "沒找到能夠測試的提交。\n"
-"å\8f¯è\83½æ\98¯å\9f·è¡\8cå\82³å\85¥ç\9a\84è·¯å¾\91å¼\95æ\95¸æ\98¯é\8c¯èª¤ç\9a\84?\n"
+"å\8f¯è\83½æ\98¯å\82³å\85¥ç\9a\84è·¯å¾\91å¼\95æ\95¸æ\9c\89誤?\n"
 
 #: bisect.c
 #, c-format
 msgid "(roughly %d step)"
 msgid_plural "(roughly %d steps)"
-msgstr[0] "(大 %d 步)"
+msgstr[0] "(大 %d 步)"
 
 #. TRANSLATORS: the last %s will be replaced with "(roughly %d
 #. steps)" translation.
@@ -1835,66 +1903,62 @@ msgstr[0] "(大概 %d 步)"
 #, c-format
 msgid "Bisecting: %d revision left to test after this %s\n"
 msgid_plural "Bisecting: %d revisions left to test after this %s\n"
-msgstr[0] "二分搜尋中:在此之後,還剩 %d 個版本待測試 %s\n"
+msgstr[0] "二分搜尋中:在此 (%2$s) 之後,尚餘 %1$d 個版本待測試\n"
 
 #: blame.c
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents 和 --reverse 不能混用。"
 
-#: blame.c
-msgid "cannot use --contents with final commit object name"
-msgstr "不能將 --contents 和最終的提交物件名共用"
-
 #: blame.c
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr "--reverse 和 --first-parent 共用,需要指定最新的提交"
 
 #: blame.c builtin/commit.c builtin/log.c builtin/merge.c
-#: builtin/pack-objects.c builtin/shortlog.c bundle.c midx.c pack-bitmap.c
-#: ref-filter.c remote.c sequencer.c submodule.c
+#: builtin/pack-objects.c builtin/shortlog.c midx.c pack-bitmap.c remote.c
+#: sequencer.c submodule.c
 msgid "revision walk setup failed"
-msgstr "版本遍歷設定失敗"
+msgstr "修訂版遍歷設定失敗"
 
 #: blame.c
 msgid ""
 "--reverse --first-parent together require range along first-parent chain"
-msgstr "--reverse 和 --first-parent 共用,需要第一祖先鏈上的提交範圍"
+msgstr "--reverse 和 --first-parent 共用,需要第一上級鏈的提交範圍"
 
 #: blame.c
 #, c-format
 msgid "no such path %s in %s"
-msgstr "在 %2$s 中無此路徑 %1$s"
+msgstr "在 %2$s 中,沒有路徑 %1$s"
 
 #: blame.c
 #, c-format
 msgid "cannot read blob %s for path %s"
-msgstr "不能為路徑 %2$s 讀取資料物件 %1$s"
+msgstr "無法為 %2$s 路徑讀取資料物件 %1$s"
 
 #: branch.c
 msgid ""
 "cannot inherit upstream tracking configuration of multiple refs when "
 "rebasing is requested"
-msgstr "è«\8bæ±\82é\87\8då®\9aå\9fºåº\95æ\99\82ï¼\8cç\84¡æ³\95ç¹¼æ\89¿å¤\9aå\80\8bå\8f\83ç\85§的上游追蹤設定"
+msgstr "è«\8bæ±\82é\87\8då®\9aå\9fºåº\95æ\99\82ï¼\8cç\84¡æ³\95ç¹¼æ\89¿å¤\9aå\80\8bå¼\95ç\94¨的上游追蹤設定"
 
 #: branch.c
 #, c-format
 msgid "not setting branch '%s' as its own upstream"
-msgstr "未將「%s」分支設定為其自己的上游"
+msgstr "未將 “%s” 分支設為其自己的上游"
 
 #: branch.c
 #, c-format
 msgid "branch '%s' set up to track '%s' by rebasing."
-msgstr "已將「%s」分支設定為透過重定基底追蹤「%s」。"
+msgstr "已藉由重訂基底,將 “%s” 分支設定為追蹤 “%s”。"
 
 #: branch.c
 #, c-format
 msgid "branch '%s' set up to track '%s'."
-msgstr "已將「%s」分支設定為追蹤「%s」。"
+msgstr "已將 “%s” 分支設定為追蹤 “%s”。"
 
 #: branch.c
 #, c-format
 msgid "branch '%s' set up to track:"
-msgstr "「%s」分支已設定追蹤:"
+msgstr "“%s” 分支已設定追蹤:"
 
 #: branch.c
 msgid "unable to write upstream branch configuration"
@@ -1907,31 +1971,29 @@ msgid ""
 "the remote tracking information by invoking:"
 msgstr ""
 "\n"
-"修正錯誤後,您可以執行下述命令\n"
-"命令修正遠端追蹤資訊:"
+"修正病灶後,您可以執行下述命令\n"
+"修正遠端追蹤資訊:"
 
 #: branch.c
 #, c-format
 msgid "asked to inherit tracking from '%s', but no remote is set"
-msgstr "請求繼承「%s」的追蹤設定,但未設定遠端"
+msgstr "請求繼承 “%s” 的追蹤設定,但未設定遠端"
 
 #: branch.c
 #, c-format
 msgid "asked to inherit tracking from '%s', but no merge configuration is set"
-msgstr "請求繼承「%s」的追蹤設定,但未設定合併設定"
+msgstr "請求繼承 “%s” 的追蹤設定,但未設定合併設定"
 
 #: branch.c
 #, c-format
 msgid "not tracking: ambiguous information for ref '%s'"
-msgstr "未追蹤:「%s」引用有歧義"
+msgstr "未追蹤:“%s” 引用有歧義"
 
 #  譯者:為保證在輸出中對齊,注意調整句中空格!
-#. #-#-#-#-#  branch.c.po  #-#-#-#-#
 #. TRANSLATORS: This is a line listing a remote with duplicate
 #. refspecs in the advice message below. For RTL languages you'll
 #. probably want to swap the "%s" and leading "  " space around.
 #.
-#. #-#-#-#-#  object-name.c.po  #-#-#-#-#
 #. TRANSLATORS: This is line item of ambiguous object output
 #. from describe_ambiguous_object() above. For RTL languages
 #. you'll probably want to swap the "%s" and leading " " space
@@ -1957,37 +2019,38 @@ msgid ""
 "different remotes' fetch refspecs map into different\n"
 "tracking namespaces."
 msgstr ""
-"有多個遠端的抓取引用規格映射到遠端追蹤引用「%s」的規格:\n"
+"有多個遠端的抓取引用規格,映射到\n"
+"遠端追蹤引用 “%s” 的規格:\n"
 "%s\n"
 "這通常是設定錯誤。\n"
 "\n"
-"若要支援設定追蹤分支,請確定不同遠端的抓取引用規格\n"
-"映射到不同的追蹤命名空間。"
+"若要支援設定追蹤分支,請確定不同遠端的\n"
+"æ\8a\93å\8f\96å¼\95ç\94¨è¦\8fæ ¼ï¼\8cæ\98 å°\84å\88°ä¸\8då\90\8cç\9a\84追蹤å\91½å\90\8d空é\96\93ã\80\82"
 
 #: branch.c
 #, c-format
 msgid "'%s' is not a valid branch name"
-msgstr "「%s」不是一個有效的分支名稱"
+msgstr "“%s” 不是有效的分支名稱"
 
 #: branch.c
 #, c-format
 msgid "a branch named '%s' already exists"
-msgstr "已有同名「%s」分支"
+msgstr "已有同名 “%s” 分支"
 
 #: branch.c
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "無法強制更新在「%2$s」簽出的「%1$s」分支"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr "無法強制更新被位於 “%2$s” 的工作區使用的 “%1$s” 分支"
 
 #: branch.c
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
-msgstr "無法設定追蹤資訊:起始點「%s」不是分支"
+msgstr "無法設定追蹤資訊:起始點 “%s” 不是分支"
 
 #: branch.c
 #, c-format
 msgid "the requested upstream branch '%s' does not exist"
-msgstr "請求的上游分支 '%s' 不存在"
+msgstr "請求的上游分支 “%s” 不存在"
 
 #: branch.c
 msgid ""
@@ -2002,112 +2065,97 @@ msgid ""
 msgstr ""
 "\n"
 "如果您打算以遠端現存的上游分支為基礎進行修改,\n"
-"您或許需要執行「git fetch」取得分支。\n"
+"您或許需要執行 `git fetch` 取得分支。\n"
 "\n"
-"如果您打算將新建立的本機分支推送至對應的遠端分支,\n"
-"ä¸\94建ç«\8bå\85©å\80\8bå\88\86æ\94¯é\96\93ç\9a\84追蹤é\97\9cä¿\82ï¼\8c\n"
-"æ\82¨å\8f¯è\83½é\9c\80è¦\81使ç\94¨ã\80\8cgit push -uã\80\8dæ\8e¨é\80\81å\88\86æ\94¯ä¸¦è¨­å®\9aå\92\8cä¸\8a游ç\9a\84é\97\9cè\81¯ã\80\82"
+"如果您打算將新建立的本機分支推送至對應的遠端分支,\n"
+"並建ç«\8bå\85©å\80\8bå\88\86æ\94¯é\96\93ç\9a\84追蹤é\97\9cä¿\82ï¼\8cæ\82¨å\8f¯è\83½é\9c\80è¦\81使ç\94¨ `git push -u`\n"
+"推送分支並設定和上游的關聯。"
 
 #: branch.c builtin/replace.c
 #, c-format
 msgid "not a valid object name: '%s'"
-msgstr "不是一個有效的物件名:'%s'"
+msgstr "物件名稱無效:“%s”"
 
 #: branch.c
 #, c-format
 msgid "ambiguous object name: '%s'"
-msgstr "物件名稱有歧義:「%s」"
+msgstr "物件名稱有歧義:“%s”"
 
 #: branch.c
 #, c-format
 msgid "not a valid branch point: '%s'"
-msgstr "無效的分支點:「%s」"
+msgstr "分支點無效:“%s”"
 
 #: branch.c
 #, c-format
 msgid "submodule '%s': unable to find submodule"
-msgstr "「%s」子模組:找不到子模組"
+msgstr "“%s” 子模組:找不到子模組"
 
 #: branch.c
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
-"您可以嘗試使用「git checkout %s && git submodule update --init」命令更新子模"
-"組"
+"您可以使用 “git checkout --no-recurse-submodules %s && git submodule update "
+"--init” 命令嘗試更新子模組"
 
 #: branch.c
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
-msgstr "「%s」子模組:無法建立「%s」分支"
-
-#: branch.c
-#, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' 已經簽出到 '%s'"
+msgstr "“%s” 子模組:無法建立 “%s” 分支"
 
 #: branch.c
 #, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "工作區 %s 的 HEAD 指向沒有被更新"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "“%s” 已被位於 “%s” 的工作區使用"
 
 #: builtin/add.c
 msgid "git add [<options>] [--] <pathspec>..."
-msgstr "git add [<選項>] [--] <路徑規格>..."
+msgstr "git add [<options>] [--] <pathspec>..."
 
 #: builtin/add.c
 #, c-format
 msgid "cannot chmod %cx '%s'"
-msgstr "無法 chmod %cx ‘%s’"
-
-#: builtin/add.c
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "意外的差異狀態 %c"
-
-#: builtin/add.c builtin/commit.c
-msgid "updating files failed"
-msgstr "更新檔案失敗"
-
-#: builtin/add.c
-#, c-format
-msgid "remove '%s'\n"
-msgstr "刪除 '%s'\n"
+msgstr "無法 chmod %cx '%s'"
 
 #: builtin/add.c
 msgid "Unstaged changes after refreshing the index:"
-msgstr "重新整理索引之後尚未被暫存的變更:"
+msgstr "重新整理索引之後,尚未被暫存的更動:"
 
-#: builtin/add.c builtin/rev-parse.c
-msgid "Could not read the index"
-msgstr "不能讀取索引"
+#: builtin/add.c
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"add.interactive.useBuiltin 設定已被移除!\n"
+"深入了解請參閱 “git help config” 中的對應條目。"
 
 #: builtin/add.c
-msgid "Could not write patch"
-msgstr "不能生成修補檔"
+msgid "could not read the index"
+msgstr "無法讀取索引"
 
 #: builtin/add.c
 msgid "editing patch failed"
 msgstr "編輯修補檔失敗"
 
-#: builtin/add.c
+#: builtin/add.c read-cache.c
 #, c-format
-msgid "Could not stat '%s'"
+msgid "could not stat '%s'"
 msgstr "不能對 '%s' 呼叫 stat"
 
 #: builtin/add.c
-msgid "Empty patch. Aborted."
-msgstr "空修補檔。異常終止。"
+msgid "empty patch. aborted"
+msgstr "修補檔空白。中止"
 
 #: builtin/add.c
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "不能套用 '%s'"
+msgid "could not apply '%s'"
+msgstr "無法套用 “%s”"
 
 #: builtin/add.c
 msgid "The following paths are ignored by one of your .gitignore files:\n"
-msgstr "下列路徑根據您的一個 .gitignore 檔案而被忽略:\n"
+msgstr "下列路徑根據其中一個 .gitignore 檔案而被忽略:\n"
 
 #: builtin/add.c builtin/clean.c builtin/fetch.c builtin/mv.c
 #: builtin/prune-packed.c builtin/pull.c builtin/push.c builtin/remote.c
@@ -2123,11 +2171,11 @@ msgstr "詳細輸出"
 
 #: builtin/add.c
 msgid "interactive picking"
-msgstr "äº\92å\8b\95å¼\8fæ\8f\80選"
+msgstr "äº\92å\8b\95å¼\8fæ\8c\91選"
 
 #: builtin/add.c builtin/checkout.c builtin/reset.c
 msgid "select hunks interactively"
-msgstr "互動式挑選資料區塊"
+msgstr "互動式選取區塊"
 
 #: builtin/add.c
 msgid "edit current diff and apply"
@@ -2135,7 +2183,7 @@ msgstr "編輯目前差異並套用"
 
 #: builtin/add.c
 msgid "allow adding otherwise ignored files"
-msgstr "允許新增忽略的檔案"
+msgstr "允許加入原先被忽略的檔案"
 
 #: builtin/add.c
 msgid "update tracked files"
@@ -2143,15 +2191,15 @@ msgstr "更新已追蹤的檔案"
 
 #: builtin/add.c
 msgid "renormalize EOL of tracked files (implies -u)"
-msgstr "å°\8d已追蹤æª\94æ¡\88ï¼\88é\9a±å\90« -uï¼\89é\87\8dæ\96°æ­¸ä¸\80æ\8f\9bè¡\8c符è\99\9f"
+msgstr "å°\8d已追蹤æª\94æ¡\88ï¼\88é\9a±å\90« -uï¼\89é\87\8dæ\96°æ¨\99æº\96å\8c\96 EOL"
 
 #: builtin/add.c
 msgid "record only the fact that the path will be added later"
-msgstr "只記錄,該路徑稍後再新增"
+msgstr "只記錄「路徑稍後會再加入」"
 
 #: builtin/add.c
 msgid "add changes from all tracked and untracked files"
-msgstr "新增所有改變的已追蹤檔案和未追蹤檔案"
+msgstr "從所有已追蹤和未追蹤檔案加入更動"
 
 #: builtin/add.c
 msgid "ignore paths removed in the working tree (same as --no-all)"
@@ -2167,7 +2215,7 @@ msgstr "略過因發生錯誤不能新增的檔案"
 
 #: builtin/add.c
 msgid "check if - even missing - files are ignored in dry run"
-msgstr "檢查在測試執行模式下檔案(即使不存在)是否被忽略"
+msgstr "檢查(即使不存在的)檔案是否在測試執行模式中被忽略"
 
 #: builtin/add.c builtin/mv.c builtin/rm.c
 msgid "allow updating entries outside of the sparse-checkout cone"
@@ -2175,11 +2223,11 @@ msgstr "允許更新稀疏簽出 cone 外的項目"
 
 #: builtin/add.c builtin/update-index.c
 msgid "override the executable bit of the listed files"
-msgstr "覆蓋列表裡檔案的可執行位"
+msgstr "覆寫列出檔案中的可執行位元"
 
 #: builtin/add.c
 msgid "warn when adding an embedded repository"
-msgstr "建ç«\8bä¸\80å\80\8båµ\8cå\85¥å¼\8fç\89\88æ\9c¬åº«æ\99\82給äº\88警告"
+msgstr "åµ\8cå\85¥ç\89\88æ\9c¬åº«æ\99\82ç\99¼å\87º警告"
 
 #: builtin/add.c
 #, c-format
@@ -2198,22 +2246,24 @@ msgid ""
 "\n"
 "See \"git help submodule\" for more information."
 msgstr ""
-"您在目前版本庫中新增了另外一個 Git 版本庫。複製外層的版本庫將不包含嵌入版本庫"
-"的內容,並且不知道該如何取得它。\n"
-"如果您要新增一個子模組,使用:\n"
+"您在目前的版本庫中加進另一個 Git 版本庫。\n"
+"複製外層的版本庫,將不包含嵌入版本庫的內容,\n"
+"並且不包含取得此版本庫的方式。\n"
+"如果您要加入子模組,請使用:\n"
 "\n"
 "\tgit submodule add <url> %s\n"
 "\n"
-"如果您不小心新增了這個路徑,可以用下面的指令將其從索引中刪除:\n"
+"如果您不小心加入了這個路徑,\n"
+"可以用下面的命令,將其從索引中移除:\n"
 "\n"
 "\tgit rm --cached %s\n"
 "\n"
-"參見 \"git help submodule\" 取得更多訊息。"
+"參見 “git help submodule” 深入了解。"
 
 #: builtin/add.c
 #, c-format
 msgid "adding embedded git repository: %s"
-msgstr "正在新增嵌入式 git 版本庫:%s"
+msgstr "正在加入嵌入式 git 版本庫:%s"
 
 #: builtin/add.c
 msgid ""
@@ -2221,29 +2271,29 @@ msgid ""
 "Turn this message off by running\n"
 "\"git config advice.addIgnoredFile false\""
 msgstr ""
-"如果您真的想加入,請使用 -f。\n"
+"若您真的想加入,請傳入 -f。\n"
 "如要關閉此訊息,請執行\n"
-"\"git config advice.addIgnoredFile false\""
+"“git config advice.addIgnoredFile false”"
 
 #: builtin/add.c
 msgid "adding files failed"
-msgstr "新增檔案失敗"
+msgstr "加入檔案失敗"
 
 #: builtin/add.c
 #, c-format
 msgid "--chmod param '%s' must be either -x or +x"
-msgstr "參數 --chmod 取值 '%s' 必須是 -x 或 +x"
+msgstr "--chmod 的參數 “%s” 必須是 -x 或 +x"
 
 #: builtin/add.c builtin/checkout.c builtin/commit.c builtin/reset.c
 #: builtin/rm.c builtin/stash.c
 #, c-format
 msgid "'%s' and pathspec arguments cannot be used together"
-msgstr "「%s」和路徑規格引數不得同時使用"
+msgstr "“%s” 和路徑規格引數不得同時使用"
 
 #: builtin/add.c
 #, c-format
 msgid "Nothing specified, nothing added.\n"
-msgstr "沒有指定檔案,也沒有檔案被新增。\n"
+msgstr "沒有指定,亦未加入檔案。\n"
 
 #: builtin/add.c
 msgid ""
@@ -2251,9 +2301,9 @@ msgid ""
 "Turn this message off by running\n"
 "\"git config advice.addEmptyPathspec false\""
 msgstr ""
-"可能你要做的是 'git add .'?\n"
+"可能您想做 “git add .”?\n"
 "如要關閉此訊息,請執行\n"
-"\"git config advice.addEmptyPathspec false\""
+"“git config advice.addEmptyPathspec false”"
 
 #: builtin/add.c builtin/check-ignore.c builtin/checkout.c builtin/clean.c
 #: builtin/commit.c builtin/diff-tree.c builtin/grep.c builtin/mv.c
@@ -2262,46 +2312,51 @@ msgstr ""
 msgid "index file corrupt"
 msgstr "索引檔案損壞"
 
+#: builtin/add.c builtin/am.c builtin/checkout.c builtin/clone.c
+#: builtin/commit.c builtin/stash.c merge.c rerere.c
+msgid "unable to write new index file"
+msgstr "無法寫入新的索引檔案"
+
 #: builtin/am.c builtin/mailinfo.c mailinfo.c
 #, c-format
 msgid "bad action '%s' for '%s'"
-msgstr "「%s」動作對「%s」無效"
+msgstr "“%s” 動作對 “%s” 無效"
 
 #: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c
 #: builtin/pull.c diff-merges.c gpg-interface.c ls-refs.c parallel-checkout.c
 #: sequencer.c setup.c
 #, c-format
 msgid "invalid value for '%s': '%s'"
-msgstr "「%s」的值無效:「%s」"
+msgstr "“%s” 的值無效:“%s”"
 
 #: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
 #, c-format
 msgid "could not read '%s'"
-msgstr "不能讀取 '%s'"
+msgstr "無法讀取 “%s”"
 
 #: builtin/am.c
 msgid "could not parse author script"
-msgstr "不能解析作者腳本"
+msgstr "無法解析作者文稿"
 
 #: builtin/am.c builtin/replace.c commit.c sequencer.c
 #, c-format
 msgid "could not parse %s"
-msgstr "不能解析 %s"
+msgstr "無法解析 %s"
 
 #: builtin/am.c
 #, c-format
 msgid "'%s' was deleted by the applypatch-msg hook"
-msgstr "'%s' 被 applypatch-msg 掛鉤刪除"
+msgstr "“%s” 被 applypatch-msg 掛鉤刪除"
 
 #: builtin/am.c
 #, c-format
 msgid "Malformed input line: '%s'."
-msgstr "非法的輸入行:'%s'。"
+msgstr "格式錯誤的輸入列:“%s”。"
 
 #: builtin/am.c
 #, c-format
 msgid "Failed to copy notes from '%s' to '%s'"
-msgstr "從 '%s' 複製註解到 '%s' 時失敗"
+msgstr "從 “%s” 拷貝註解到 “%s” 失敗"
 
 #: builtin/am.c
 msgid "fseek failed"
@@ -2310,21 +2365,21 @@ msgstr "fseek 失敗"
 #: builtin/am.c builtin/rebase.c sequencer.c wrapper.c
 #, c-format
 msgid "could not open '%s' for reading"
-msgstr "無法開啟 '%s' 進行讀取"
+msgstr "無法開啟 “%s” 進行讀取"
 
-#: builtin/am.c builtin/rebase.c sequencer.c strbuf.c wrapper.c
+#: builtin/am.c builtin/rebase.c editor.c sequencer.c wrapper.c
 #, c-format
 msgid "could not open '%s' for writing"
-msgstr "無法開啟 '%s' 進行寫入"
+msgstr "無法開啟 “%s” 進行寫入"
 
 #: builtin/am.c
 #, c-format
 msgid "could not parse patch '%s'"
-msgstr "無法解析修補檔 '%s'"
+msgstr "無法解析修補檔 “%s”"
 
 #: builtin/am.c
 msgid "Only one StGIT patch series can be applied at once"
-msgstr "一次只能有一個 StGIT 修補檔佇列被套用"
+msgstr "一次只能套用一個 StGIT 修補檔序列"
 
 #: builtin/am.c
 msgid "invalid timestamp"
@@ -2332,11 +2387,11 @@ msgstr "無效的時間戳"
 
 #: builtin/am.c
 msgid "invalid Date line"
-msgstr "無效的日期行"
+msgstr "無效的 Date 列"
 
 #: builtin/am.c
 msgid "invalid timezone offset"
-msgstr "無效的時區位移值"
+msgstr "無效的時區偏移"
 
 #: builtin/am.c
 msgid "Patch format detection failed."
@@ -2345,74 +2400,74 @@ msgstr "修補檔格式偵測失敗。"
 #: builtin/am.c builtin/clone.c
 #, c-format
 msgid "failed to create directory '%s'"
-msgstr "建立目錄 '%s' 失敗"
+msgstr "無法建立目錄 “%s”"
 
 #: builtin/am.c
 msgid "Failed to split patches."
-msgstr "分割修補檔失敗。"
+msgstr "無法切割修補檔。"
 
 #: builtin/am.c
 #, c-format
 msgid "When you have resolved this problem, run \"%s --continue\"."
-msgstr "當您解決這一問題,執行 \"%s --continue\"。"
+msgstr "解決此問題後,請執行 “%s --continue”。"
 
 #: builtin/am.c
 #, c-format
 msgid "If you prefer to skip this patch, run \"%s --skip\" instead."
-msgstr "如果您想要略過這一修補檔,則執行 \"%s --skip\"。"
+msgstr "若要略過此修補,請改執行 “%s --skip”。"
 
 #: builtin/am.c
 #, c-format
 msgid "To record the empty patch as an empty commit, run \"%s --allow-empty\"."
-msgstr "若要將空白修補檔錄製為空白提交,請執行「%s --allow-empty」。"
+msgstr "若要將空白修補檔錄入為空白提交,請執行 “%s --allow-empty”。"
 
 #: builtin/am.c
 #, c-format
 msgid "To restore the original branch and stop patching, run \"%s --abort\"."
-msgstr "若要復原至原始分支並停止修補動作,執行 \"%s --abort\"。"
+msgstr "若要還原至原始分支,並停止修補動作,請執行 “%s --abort”。"
 
 #: builtin/am.c
 msgid "Patch sent with format=flowed; space at the end of lines might be lost."
-msgstr "ä¿®è£\9cæª\94使ç\94¨ format=flowed æ ¼å¼\8få\82³é\80\81ï¼\8cè¡\8c尾的空格可能會遺失。"
+msgstr "ä¿®è£\9cæª\94使ç\94¨ format=flowed æ ¼å¼\8få\82³é\80\81ï¼\9aå\88\97尾的空格可能會遺失。"
 
 #: builtin/am.c
 #, c-format
 msgid "missing author line in commit %s"
-msgstr "在提交 %s 中缺少作者"
+msgstr "在提交 %s 中缺少作者"
 
 #: builtin/am.c
 #, c-format
 msgid "invalid ident line: %.*s"
-msgstr "無效的身份標記:%.*s"
+msgstr "ident 列無效:%.*s"
 
 #: builtin/am.c builtin/checkout.c builtin/clone.c commit-graph.c
 #, c-format
 msgid "unable to parse commit %s"
-msgstr "不能解析提交 %s"
+msgstr "無法解析 %s 提交"
 
 #: builtin/am.c
 msgid "Repository lacks necessary blobs to fall back on 3-way merge."
-msgstr "版本庫缺乏必要的資料物件以進行三方合併。"
+msgstr "版本庫缺少必要的資料物件,無法進行三方合併。"
 
 #: builtin/am.c
 msgid "Using index info to reconstruct a base tree..."
-msgstr "使用索引來重建一個(三方合併的)基礎目錄樹..."
+msgstr "正在使用索引資訊重建基礎樹……"
 
 #: builtin/am.c
 msgid ""
 "Did you hand edit your patch?\n"
 "It does not apply to blobs recorded in its index."
 msgstr ""
-"您是否曾手動編輯過您的修補檔?\n"
-"無法套用修補檔到索引中的資料物件上。"
+"您是否曾自行編輯過您的修補檔?\n"
+"未能套用至索引錄入的資料物件。"
 
 #: builtin/am.c
 msgid "Falling back to patching base and 3-way merge..."
-msgstr "回落到基礎版本上修補及進行三方合併..."
+msgstr "正在回復到修補基礎處,並進行三方合併……"
 
 #: builtin/am.c
 msgid "Failed to merge in the changes."
-msgstr "無法合併變更。"
+msgstr "無法合併更動。"
 
 #: builtin/am.c builtin/merge.c sequencer.c
 msgid "git write-tree failed to write a tree"
@@ -2420,12 +2475,12 @@ msgstr "git write-tree 無法寫入樹狀物件"
 
 #: builtin/am.c
 msgid "applying to an empty history"
-msgstr "æ­£å¥\97ç\94¨å\88°ä¸\80å\80\8b空歷å\8f²上"
+msgstr "æ­£å\9c¨å¥\97ç\94¨è\87³ç©ºç\99½æ­·å\8f²è¨\98é\8c\84上"
 
 #: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
 #: t/helper/test-fast-rebase.c
 msgid "failed to write commit object"
-msgstr "寫提交物件失敗"
+msgstr "無法寫入提交物件"
 
 #: builtin/am.c
 #, c-format
@@ -2434,7 +2489,7 @@ msgstr "無法繼續:%s 不存在。"
 
 #: builtin/am.c
 msgid "Commit Body is:"
-msgstr "提交內為:"
+msgstr "提交內為:"
 
 #  譯者:請維持句尾空格
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
@@ -2444,7 +2499,7 @@ msgstr "提交內容為:"
 #: builtin/am.c
 #, c-format
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "
-msgstr "套用?是[y]/否[n]/編輯[e]/檢視修補檔[v]/全部接受[a]: "
+msgstr "是否套用?是[y]/否[n]/編輯[e]/檢視修補檔[v]/全部接受[a]: "
 
 #: builtin/am.c builtin/commit.c
 msgid "unable to write index file"
@@ -2453,7 +2508,7 @@ msgstr "無法寫入索引檔案"
 #: builtin/am.c
 #, c-format
 msgid "Dirty index: cannot apply patches (dirty: %s)"
-msgstr "髒索引:不能套用修補檔(髒檔案:%s)"
+msgstr "有異動索引:無法套用修補檔(異動處:%s)"
 
 #: builtin/am.c
 #, c-format
@@ -2467,7 +2522,7 @@ msgstr "建立空白提交:%.*s"
 
 #: builtin/am.c
 msgid "Patch is empty."
-msgstr "ä¿®è£\9cæª\94ç\82ºç©º。"
+msgstr "ä¿®è£\9cæª\94空ç\99½。"
 
 #: builtin/am.c
 #, c-format
@@ -2476,20 +2531,20 @@ msgstr "套用:%.*s"
 
 #: builtin/am.c
 msgid "No changes -- Patch already applied."
-msgstr "沒有變更——修補檔已套用過。"
+msgstr "沒有變更——修補檔已套用過。"
 
 #: builtin/am.c
 #, c-format
 msgid "Patch failed at %s %.*s"
-msgstr "打修補檔失敗於 %s %.*s"
+msgstr "在 %s %.*s 處修補失敗"
 
 #: builtin/am.c
 msgid "Use 'git am --show-current-patch=diff' to see the failed patch"
-msgstr "用 'git am --show-current-patch=diff' 命令檢視失敗的修補檔"
+msgstr "使用 “git am --show-current-patch=diff” 命令檢視失敗的修補檔"
 
 #: builtin/am.c
 msgid "No changes - recorded it as an empty commit."
-msgstr "沒有變更 - 將其錄製為空白提交。"
+msgstr "沒有變更:將其錄入為空白提交。"
 
 #: builtin/am.c
 msgid ""
@@ -2497,9 +2552,9 @@ msgid ""
 "If there is nothing left to stage, chances are that something else\n"
 "already introduced the same changes; you might want to skip this patch."
 msgstr ""
-"沒有變更 —— 您是不是忘了執行 'git add'?\n"
-"如果沒有什麼要新增到暫存區的,則很可能是其它提交已經引入了相同的變更。\n"
-"您也許想要略過這個修補檔。"
+"沒有變更:是否忘記執行 “git add”?\n"
+"如果沒有其他要新增到暫存區的,則很可能是其它提交\n"
+"已經引入了相同的變更。您也許想要略過這個修補檔。"
 
 #: builtin/am.c
 msgid ""
@@ -2508,59 +2563,55 @@ msgid ""
 "such.\n"
 "You might run `git rm` on a file to accept \"deleted by them\" for it."
 msgstr ""
-"在您的索引中仍存在未合併的路徑。\n"
-"您應該對已經衝突解決的每一個檔案執行 'git add' 來標記已經完成。 \n"
-"你可以對 \"由他們刪除\" 的檔案執行 `git rm` 指令。"
-
-#: builtin/am.c builtin/checkout.c builtin/clone.c builtin/stash.c merge.c
-#: rerere.c
-msgid "unable to write new index file"
-msgstr "無法寫新的索引檔案"
+"索引中仍有未合併路徑。\n"
+"您應該對已經解決衝突的每一個檔案執行 `git add`,標記為已經完成。\n"
+"你可以對「由他們刪除」的檔案,執行 `git rm` 指令。"
 
 #: builtin/am.c builtin/reset.c
 #, c-format
 msgid "Could not parse object '%s'."
-msgstr "不能解析物件 '%s'。"
+msgstr "無法解析 “%s” 物件。"
 
 #: builtin/am.c
 msgid "failed to clean index"
-msgstr "清空索引失敗"
+msgstr "無法清除索引"
 
 #: builtin/am.c
 msgid ""
 "You seem to have moved HEAD since the last 'am' failure.\n"
 "Not rewinding to ORIG_HEAD"
-msgstr "您好像在上一次 'am' 失敗後移動了 HEAD。未還原至 ORIG_HEAD"
+msgstr ""
+"您似乎在上一次 “am” 失敗後移動了 HEAD。\n"
+"未倒轉回 ORIG_HEAD"
 
-#: builtin/am.c builtin/bisect--helper.c worktree.c
+#: builtin/am.c builtin/bisect.c worktree.c
 #, c-format
 msgid "failed to read '%s'"
-msgstr "讀取 '%s' 失敗"
-
-#: builtin/am.c
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "「%s=%s」和「%s=%s」選項不得同時使用"
+msgstr "無法讀取 “%s”"
 
 #: builtin/am.c
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
-msgstr "git am [<選項>] [(<mbox> | <Maildir>)...]"
+msgstr "git am [<options>] [(<mbox> | <Maildir>)...]"
 
 #: builtin/am.c
 msgid "git am [<options>] (--continue | --skip | --abort)"
-msgstr "git am [<選項>] (--continue | --skip | --abort)"
+msgstr "git am [<options>] (--continue | --skip | --abort)"
 
 #: builtin/am.c
 msgid "run interactively"
-msgstr "以互動式方式執行"
+msgstr "互動式執行"
+
+#: builtin/am.c
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "繞過 pre-applypatch 和 applypatch-msg 掛鉤"
 
 #: builtin/am.c
 msgid "historical option -- no-op"
-msgstr "老的參數 —— 無作用"
+msgstr "歷史遺留選項——無作用"
 
 #: builtin/am.c
 msgid "allow fall back on 3way merging if needed"
-msgstr "å¦\82æ\9e\9cå¿\85è¦\81ï¼\8cå\85\81許使ç\94¨三方合併"
+msgstr "å¿\85è¦\81æ\99\82å\85\81許å\9b\9e復è\87³三方合併"
 
 #: builtin/am.c builtin/init-db.c builtin/prune-packed.c builtin/repack.c
 #: builtin/stash.c
@@ -2569,43 +2620,39 @@ msgstr "靜默模式"
 
 #: builtin/am.c
 msgid "add a Signed-off-by trailer to the commit message"
-msgstr "在提交說明結尾加入 Signed-off-by"
+msgstr "在提交說明結尾加入 Signed-off-by"
 
 #: builtin/am.c
 msgid "recode into utf8 (default)"
-msgstr "使用 utf8 字元集(預設)"
+msgstr "使用 utf8 字元集(預設)"
 
 #: builtin/am.c
 msgid "pass -k flag to git-mailinfo"
-msgstr "向 git-mailinfo 傳 -k 參數"
+msgstr "向 git-mailinfo 傳 -k 參數"
 
 #: builtin/am.c
 msgid "pass -b flag to git-mailinfo"
-msgstr "向 git-mailinfo 傳 -b 參數"
+msgstr "向 git-mailinfo 傳 -b 參數"
 
 #: builtin/am.c
 msgid "pass -m flag to git-mailinfo"
-msgstr "向 git-mailinfo 傳 -m 參數"
+msgstr "向 git-mailinfo 傳 -m 參數"
 
 #: builtin/am.c
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
-msgstr "針對 mbox 格式,向 git-mailsplit 傳遞 --keep-cr 參數"
-
-#: builtin/am.c
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr "不向 git-mailsplit 傳遞 --keep-cr 參數,覆蓋 am.keepcr 的設定"
+msgstr "若為 mbox 格式,向 git-mailsplit 傳入 --keep-cr 標記"
 
 #: builtin/am.c
 msgid "strip everything before a scissors line"
-msgstr "æ\8d¨æ£\84裁切線前的所有內容"
+msgstr "æ\88ªæ\8e\89裁切線前的所有內容"
 
 #: builtin/am.c
 msgid "pass it through git-mailinfo"
-msgstr "透過 git-mailinfo 傳"
+msgstr "透過 git-mailinfo 傳"
 
 #: builtin/am.c
 msgid "pass it through git-apply"
-msgstr "傳遞給 git-apply"
+msgstr "透過 git-apply 傳入"
 
 #: builtin/am.c builtin/commit.c builtin/fmt-merge-msg.c builtin/grep.c
 #: builtin/merge.c builtin/pull.c builtin/rebase.c builtin/repack.c
@@ -2617,7 +2664,7 @@ msgstr "n"
 #: builtin/diagnose.c builtin/for-each-ref.c builtin/ls-files.c
 #: builtin/ls-tree.c builtin/replace.c builtin/tag.c builtin/verify-tag.c
 msgid "format"
-msgstr "格式"
+msgstr "format"
 
 #: builtin/am.c
 msgid "format the patch(es) are in"
@@ -2625,7 +2672,7 @@ msgstr "修補檔的格式"
 
 #: builtin/am.c
 msgid "override error message when patch failure occurs"
-msgstr "打修補檔失敗時顯示的錯誤訊息"
+msgstr "覆蓋修補失敗時顯示的錯誤訊息"
 
 #: builtin/am.c
 msgid "continue applying patches after resolving a conflict"
@@ -2645,7 +2692,7 @@ msgstr "還原原始分支並中止修補動作"
 
 #: builtin/am.c
 msgid "abort the patching operation but keep HEAD where it is"
-msgstr "止修補動作但保持 HEAD 不變"
+msgstr "止修補動作但保持 HEAD 不變"
 
 #: builtin/am.c
 msgid "show the patch being applied"
@@ -2653,15 +2700,15 @@ msgstr "顯示正在套用的修補檔"
 
 #: builtin/am.c
 msgid "record the empty patch as an empty commit"
-msgstr "將空白修補檔錄為空白提交"
+msgstr "將空白修補檔錄為空白提交"
 
 #: builtin/am.c
 msgid "lie about committer date"
-msgstr "將作者日期作為提交日期"
+msgstr "將作者日期當作提交日期"
 
 #: builtin/am.c
 msgid "use current timestamp for author date"
-msgstr "用目前時間作為作者日期"
+msgstr "把目前時間戳當作是作者日期"
 
 #: builtin/am.c builtin/commit-tree.c builtin/commit.c builtin/merge.c
 #: builtin/pull.c builtin/rebase.c builtin/revert.c builtin/tag.c
@@ -2685,8 +2732,8 @@ msgid ""
 "The -b/--binary option has been a no-op for long time, and\n"
 "it will be removed. Please do not use it anymore."
 msgstr ""
-"參數 -b/--binary 已經很長時間不做任何實質動作了,並且將被移除。\n"
-"請不要再使用它了。"
+"-b/--binary 選項已經有很長時間不做任何實質動作了,\n"
+"並且將被移除。請不要再使用它了。"
 
 #: builtin/am.c
 msgid "failed to read the index"
@@ -2703,170 +2750,165 @@ msgid ""
 "Stray %s directory found.\n"
 "Use \"git am --abort\" to remove it."
 msgstr ""
-"發現了錯誤的 %s 目錄。\n"
-"使用 \"git am --abort\" 刪除它。"
+"發現失散的 %s 目錄。\n"
+"使用 “git am --abort” 移除。"
 
 #: builtin/am.c
 msgid "Resolve operation not in progress, we are not resuming."
-msgstr "解決動作未進行,我們不會繼續。"
+msgstr "未在進行解決動作,不會繼續。"
 
 #: builtin/am.c
 msgid "interactive mode requires patches on the command line"
-msgstr "互動式模式需要指令列上提供修補檔"
+msgstr "互動模式需要命令列上提供修補檔"
 
 #: builtin/apply.c
 msgid "git apply [<options>] [<patch>...]"
-msgstr "git apply [<選項>] [<修補檔>...]"
+msgstr "git apply [<options>] [<patch>...]"
 
 #: builtin/archive.c diagnose.c
 msgid "could not redirect output"
-msgstr "不能重定向輸出"
+msgstr "無法重新導向輸出"
 
 #: builtin/archive.c
 msgid "git archive: Remote with no URL"
-msgstr "git archive未提供遠端 URL"
+msgstr "git archive未提供遠端 URL"
 
 #: builtin/archive.c
 msgid "git archive: expected ACK/NAK, got a flush packet"
-msgstr "git archive:期望是 ACK/NAK,卻得到 flush 包"
+msgstr "git archive:預期是 ACK/NAK,卻收到 flush 封包"
 
 #: builtin/archive.c
 #, c-format
 msgid "git archive: NACK %s"
-msgstr "git archiveNACK %s"
+msgstr "git archiveNACK %s"
 
 #: builtin/archive.c
 msgid "git archive: protocol error"
-msgstr "git archive:協定錯誤"
+msgstr "git archive:通訊協定錯誤"
 
 #: builtin/archive.c
 msgid "git archive: expected a flush"
-msgstr "git archive:應有一個 flush 包"
-
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<提交>]"
+msgstr "git archive:預期收到 flush 封包"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<術語> --term-{old,good}"
-"=<術語>] [--no-checkout] [--first-parent] [<壞> [<好>...]] [--] [<路徑>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<修訂版>]"
+#: builtin/bisect.c
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<rev>...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<修訂版>...]"
+#: builtin/bisect.c
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<rev>|<range>)...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <檔名>"
+#: builtin/bisect.c
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<commit>]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<修訂版>|<範圍>)...]"
+#: builtin/bisect.c
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <logfile>"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <cmd>..."
+#: builtin/bisect.c
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <cmd> [<arg>...]"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
-msgstr "無法以 '%2$s' 模式開啟 '%1$s' 檔案"
+msgstr "無法以 “%2$s” 模式開啟 “%1$s” 檔案"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "could not write to file '%s'"
-msgstr "無法寫入 '%s' 檔案"
+msgstr "無法寫入 “%s” 檔案"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' for reading"
-msgstr "無法開啟「%s」檔案進行讀取"
+msgstr "無法開啟 “%s” 檔案進行讀取"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' is not a valid term"
-msgstr "'%s' 不是一個有效的術語"
+msgstr "“%s” 不是有效術語"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "can't use the builtin command '%s' as a term"
-msgstr "不能使用內建指令 '%s' 作為術語"
+msgstr "不能將內建命令 “%s” 當作術語使用"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "can't change the meaning of the term '%s'"
-msgstr "不能修改術語 '%s' 的含義"
+msgstr "不能變更術語 “%s” 的含義"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "please use two different terms"
 msgstr "請使用兩個不同的術語"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "We are not bisecting.\n"
 msgstr "我們沒有在二分搜尋。\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' is not a valid commit"
-msgstr "'%s' 不是一個有效的提交"
+msgstr "“%s” 不是有效的提交"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
-msgstr "不能簽出原始 HEAD '%s'。嘗試 'git bisect reset <提交>'。"
+msgstr "不能簽出原始 HEAD “%s”。請嘗試 “git bisect reset <commit>”。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad bisect_write argument: %s"
-msgstr "壞的 bisect_write 參數:%s"
+msgstr "bisect_write 引數無效:%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "couldn't get the oid of the rev '%s'"
-msgstr "無法取得版本 '%s' 的物件 ID"
+msgstr "無法取得修訂版 “%s” 的物件 ID"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "couldn't open the file '%s'"
-msgstr "無法開啟檔案 '%s'"
+msgstr "無法開啟檔案 “%s”"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Invalid command: you're currently in a %s/%s bisect"
-msgstr "無效的指令:您目前正處於一個 %s/%s 二分搜尋中"
+msgstr "命令無效:您目前正處於二分搜尋 %s/%s 的狀態"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "You need to give me at least one %s and %s revision.\n"
 "You can use \"git bisect %s\" and \"git bisect %s\" for that."
 msgstr ""
-"您需要給我至少一個 %s 和一個 %s 版本。\n"
-"為此您可以用 \"git bisect %s\" 和 \"git bisect %s\"。"
+"需指定至少一個 %s 和一個 %s 修訂版。\n"
+"為此您可以用 “git bisect %s” 和 “git bisect %s”。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "You need to start by \"git bisect start\".\n"
 "You then need to give me at least one %s and %s revision.\n"
 "You can use \"git bisect %s\" and \"git bisect %s\" for that."
 msgstr ""
-"您需要執行 \"git bisect start\" 來開始。\n"
-"然後需要提供我至少一個 %s 和一個 %s 版本。\n"
-"為此您可以用 \"git bisect %s\" 和 \"git bisect %s\" 指令。"
+"要開始,請執行 “git bisect start”。\n"
+"接著提供至少一個 %s 和一個 %s 修訂版。\n"
+"為此您可以用 “git bisect %s” 和 “git bisect %s”。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "bisecting only with a %s commit"
 msgstr "在只有一個 %s 提交的情況下二分搜尋"
@@ -2876,287 +2918,249 @@ msgstr "在只有一個 %s 提交的情況下二分搜尋"
 #. translation. The program will only accept English input
 #. at this point.
 #.
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Are you sure [Y/n]? "
-msgstr "æ\82¨ç¢ºèª\8då\97\8e[Y/n]ï¼\9f "
+msgstr "æ\98¯å\90¦ç¢ºå®\9a [Y/n]? "
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "status: waiting for both good and bad commits\n"
 msgstr "狀態:正在等待好和壞的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "status: waiting for bad commit, %d good commit known\n"
 msgid_plural "status: waiting for bad commit, %d good commits known\n"
 msgstr[0] "狀態:正在等待壞的提交,已知有 %d 個好的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "status: waiting for good commit(s), bad commit known\n"
 msgstr "狀態:正在等待好的提交,已知有壞的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "no terms defined"
 msgstr "未定義術語"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "Your current terms are %s for the old state\n"
 "and %s for the new state.\n"
-msgstr "您目前針對舊狀態的術語是 %s,對新狀態的術語是 %s。\n"
+msgstr ""
+"您目前針對舊狀態的術語是 %s;\n"
+"對新狀態的術語是 %s。\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "invalid argument %s for 'git bisect terms'.\n"
 "Supported options are: --term-good|--term-old and --term-bad|--term-new."
 msgstr ""
-"指令 'git bisect terms' 的參數 %s 無效。\n"
+"傳入 “git bisect terms” 的 %s 引數無效。\n"
 "支援的選項有:--term-good|--term-old 和 --term-bad|--term-new。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "revision walk setup failed\n"
-msgstr "版本遍歷設定失敗\n"
+msgstr "修訂版遍歷設定失敗\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "could not open '%s' for appending"
-msgstr "無法開啟 '%s' 進行附加"
+msgstr "無法開啟 “%s” 進行附加"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "'' is not a valid term"
-msgstr "'' 不是一個有效的術語"
+msgstr "“ ” 不是有效術語"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "unrecognized option: '%s'"
-msgstr "未識別的選項:'%s'"
+msgstr "無法識別選項:“%s”"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' does not appear to be a valid revision"
-msgstr "'%s' 看起來不是一個有效的版本"
+msgstr "“%s” 似乎不是有效修訂版"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bad HEAD - I need a HEAD"
-msgstr "壞的 HEAD - 我需要一個 HEAD"
+msgstr "HEAD 無效 — 需要一個 HEAD"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
-msgstr "簽出 '%s' 失敗。嘗試 'git bisect start <有效分支>'。"
-
-#: builtin/bisect--helper.c
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "不會在做了 cg-seek 的樹上做二分搜尋"
+msgstr "簽出 “%s” 失敗。請嘗試 “git bisect start <valid-branch>”。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bad HEAD - strange symbolic ref"
-msgstr "壞的 HEAD - 奇怪的符號引用"
+msgstr "HEAD 無效 — 異常符號引用"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "invalid ref: '%s'"
-msgstr "無效的引用:'%s'"
+msgstr "引用無效:“%s”"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "You need to start by \"git bisect start\"\n"
-msgstr "您需要執行 \"git bisect start\" 來開始\n"
+msgstr "要開始,請執行 “git bisect start”\n"
 
 #  譯者:請維持句尾空格
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
 #.
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Do you want me to do it for you [Y/n]? "
-msgstr "æ\82¨æ\83³è®\93æ\88\91ç\82ºæ\82¨é\80\99樣å\81\9aå\97\8e[Y/n]ï¼\9f "
+msgstr "æ\98¯å\90¦è¦\81é\80\99麼å\81\9a [Y/n]? "
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Please call `--bisect-state` with at least one argument"
 msgstr "要呼叫 `--bisect-state`,請傳入一個以上的引數"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'git bisect %s' can take only one argument."
-msgstr "'git bisect %s' 只能有一個參數。"
+msgstr "“git bisect %s” 只取一個引數。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad rev input: %s"
-msgstr "<修訂版> 輸入格式錯誤:%s"
+msgstr "rev 輸入格式錯誤:%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad rev input (not a commit): %s"
-msgstr "修訂版輸入有誤(不是提交):%s"
+msgstr "rev 輸入有誤(不是提交):%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "We are not bisecting."
 msgstr "我們沒有在二分搜尋。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s'?? what are you talking about?"
 msgstr "「%s」??您在說什麼?"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot read file '%s' for replaying"
-msgstr "無法讀取「%s」檔案重放"
+msgstr "無法讀取「%s」檔案進行重放"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "running %s\n"
 msgstr "正在執行 %s\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bisect run failed: no command provided."
-msgstr "二分搜尋執行失敗:沒有提供令。"
+msgstr "二分搜尋執行失敗:沒有提供令。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid "unable to verify '%s' on good revision"
-msgstr "無法在正確修訂版上驗證「%s」"
+msgid "unable to verify %s on good revision"
+msgstr "無法在好的修訂版上驗證 %s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "bogus exit code %d for good revision"
-msgstr "正確修訂版回傳偽造的錯誤代碼 %d"
+msgstr "好的修訂版回傳偽造的錯誤碼 %d"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
-msgstr "二分搜尋執行失敗:「%2$s」返回的離開碼 %1$d 小於 0 或 >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "二分搜尋執行失敗:%2$s 回傳的結束代碼 %1$d 小於 0 或大於 128"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' for writing"
-msgstr "無法開啟「%s」檔案進行寫入"
+msgstr "無法開啟 “%s” 檔案進行寫入"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bisect run cannot continue any more"
 msgstr "二分搜尋不能繼續執行"
 
-#: builtin/bisect--helper.c
-#, c-format
+#: builtin/bisect.c
 msgid "bisect run success"
 msgstr "二分搜尋執行成功"
 
-#: builtin/bisect--helper.c
-#, c-format
+#: builtin/bisect.c
 msgid "bisect found first bad commit"
 msgstr "二分搜尋發現到第一個有問題的提交"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
-msgstr ""
-"二分搜尋執行失敗:'git bisect--helper --bisect-state %s' 以錯誤代碼 %d 離開"
-
-#: builtin/bisect--helper.c
-msgid "reset the bisection state"
-msgstr "清除二分搜尋狀態"
-
-#: builtin/bisect--helper.c
-msgid "check whether bad or good terms exist"
-msgstr "檢查壞的或好的術語是否存在"
-
-#: builtin/bisect--helper.c
-msgid "print out the bisect terms"
-msgstr "列印二分搜尋術語"
-
-#: builtin/bisect--helper.c
-msgid "start the bisect session"
-msgstr "啟動二分搜尋過程"
-
-#: builtin/bisect--helper.c
-msgid "find the next bisection commit"
-msgstr "尋找下一個二分搜尋提交"
-
-#: builtin/bisect--helper.c
-msgid "mark the state of ref (or refs)"
-msgstr "標記 ref (或 refs) 的狀態"
-
-#: builtin/bisect--helper.c
-msgid "list the bisection steps so far"
-msgstr "列出迄今的二分搜尋步驟"
-
-#: builtin/bisect--helper.c
-msgid "replay the bisection process from the given file"
-msgstr "從指定檔案重放二分搜尋過程"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr "二分搜尋執行失敗:“git bisect %s” 以錯誤碼 %d 離開"
 
-#: builtin/bisect--helper.c
-msgid "skip some commits for checkout"
-msgstr "略過要簽出的部分提交"
-
-#: builtin/bisect--helper.c
-msgid "visualize the bisection"
-msgstr "視覺化二分搜尋過程"
-
-#: builtin/bisect--helper.c
-msgid "use <cmd>... to automatically bisect"
-msgstr "使用 <cmd>... 自動進行二分搜尋"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "“%s” 不需要引數,或者得傳入一個提交"
 
-#: builtin/bisect--helper.c
-msgid "no log for BISECT_WRITE"
-msgstr "BISECT_WRITE 無日誌"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "“%s” 需要 0 或 1 個引數"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset 無需參數或者需要一個提交"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "“%s” 不需引數"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms 需要 0 或 1 個參數"
+#: builtin/bisect.c
+msgid "no logfile given"
+msgstr "未提供日誌檔案"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next 需要 0 個引數"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "“%s” 失敗:沒有提供命令。"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log 需要 0 個引數"
+#: builtin/bisect.c
+msgid "need a command"
+msgstr "需要提供命令"
 
-#: builtin/bisect--helper.c
-msgid "no logfile given"
-msgstr "未提供日誌檔案"
+#: builtin/bisect.c builtin/cat-file.c
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "未知命令:“%s”"
 
 #: builtin/blame.c
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
-msgstr "git blame [<選項>] [<版本選項>] [<版本>] [--] <檔案>"
+msgstr "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
+
+#: builtin/blame.c
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
 
 #: builtin/blame.c
 msgid "<rev-opts> are documented in git-rev-list(1)"
-msgstr "<版本選項> 的檔案記錄在 git-rev-list(1) 中"
+msgstr "<rev-opts> 的文件在 git-rev-list(1)"
 
 #: builtin/blame.c
 #, c-format
 msgid "expecting a color: %s"
-msgstr "期望一個顏色:%s"
+msgstr "預期是個顏色:%s"
 
 #: builtin/blame.c
 msgid "must end with a color"
-msgstr "必須以一個顏色結尾"
+msgstr "結尾必須是一個顏色"
 
 #: builtin/blame.c
 #, c-format
 msgid "cannot find revision %s to ignore"
-msgstr "不能找到要忽略的版本 %s"
+msgstr "不能找到要忽略的 %s 修訂版"
 
 #: builtin/blame.c
 msgid "show blame entries as we find them, incrementally"
-msgstr "增量式顯示發現的 blame 條目"
+msgstr "漸進式顯示發現的溯源項目"
 
 #: builtin/blame.c
 msgid "do not show object names of boundary commits (Default: off)"
-msgstr "不要顯示邊界提交的物件名稱(預設值off)"
+msgstr "不要顯示邊界提交的物件名稱(預設值off)"
 
 #: builtin/blame.c
 msgid "do not treat root commits as boundaries (Default: off)"
-msgstr "不將根提交看作邊界(預設值:off)"
+msgstr "不把根提交當作邊界(預設值:off)"
 
 #: builtin/blame.c
 msgid "show work cost statistics"
@@ -3170,7 +3174,7 @@ msgstr "強制顯示進度報告"
 
 #: builtin/blame.c
 msgid "show output score for blame entries"
-msgstr "顯示判斷 blame 條目位移的得分診斷訊息"
+msgstr "顯示溯源項目的輸出得分"
 
 #: builtin/blame.c
 msgid "show original filename (Default: auto)"
@@ -3178,7 +3182,7 @@ msgstr "顯示原始檔案名稱(預設值:auto)"
 
 #: builtin/blame.c
 msgid "show original linenumber (Default: off)"
-msgstr "顯示原始檔案名稱(預設值:off)"
+msgstr "顯示原始列碼(預設值:off)"
 
 #: builtin/blame.c
 msgid "show in a format designed for machine consumption"
@@ -3186,7 +3190,7 @@ msgstr "顯示成適合機器讀取的格式"
 
 #: builtin/blame.c
 msgid "show porcelain format with per-line commit information"
-msgstr "顯示每一列適合機器的提交說明"
+msgstr "顯示包含每一列提交資訊,適合機器讀取的格式"
 
 #: builtin/blame.c
 msgid "use the same output mode as git-annotate (Default: off)"
@@ -3214,15 +3218,15 @@ msgstr "忽略空白差異"
 
 #: builtin/blame.c builtin/log.c
 msgid "rev"
-msgstr "版本"
+msgstr "rev"
 
 #: builtin/blame.c
 msgid "ignore <rev> when blaming"
-msgstr "在執行 blame 動作時忽略 <修訂版>"
+msgstr "在執行溯源動作時忽略 <rev>"
 
 #: builtin/blame.c
 msgid "ignore revisions from <file>"
-msgstr "忽略 <檔案> 中的修訂版"
+msgstr "忽略 <file> 中的修訂版"
 
 #: builtin/blame.c
 msgid "color redundant metadata from previous line differently"
@@ -3230,31 +3234,31 @@ msgstr "使用顏色間隔輸出與前一行不同的重複中介資料"
 
 #: builtin/blame.c
 msgid "color lines by age"
-msgstr "據時間著色"
+msgstr "據時間著色"
 
 #: builtin/blame.c
 msgid "spend extra cycles to find better match"
-msgstr "循ç\92°æ\9b´å¤\9a次以æ\89¾å\88°æ\9b´ä½³ç¬¦å\90\88"
+msgstr "循ç\92°æ\9b´å¤\9a次ä¾\86æ\89¾å\88°æ\9b´ä½³ç¬¦å\90\88é \85ç\9b®"
 
 #: builtin/blame.c
 msgid "use revisions from <file> instead of calling git-rev-list"
-msgstr "使用來自 <檔案> 的修訂集而不是呼叫 git-rev-list"
+msgstr "使用來自 <file> 的修訂集而不是呼叫 git-rev-list"
 
 #: builtin/blame.c
 msgid "use <file>'s contents as the final image"
-msgstr "將 <檔案> 的內容當成是最終 image"
+msgstr "將 <file> 的內容當成是最終印象"
 
 #: builtin/blame.c
 msgid "score"
-msgstr "得分"
+msgstr "score"
 
 #: builtin/blame.c
 msgid "find line copies within and across files"
-msgstr "找到檔案內及跨檔案的複製列"
+msgstr "找到檔案內及跨檔案的列拷貝動作"
 
 #: builtin/blame.c
 msgid "find line movements within and across files"
-msgstr "找到檔案內及跨檔案的移動列"
+msgstr "找到檔案內及跨檔案的列移動動作"
 
 #: builtin/blame.c
 msgid "range"
@@ -3284,106 +3288,113 @@ msgstr "4 年 11 個月前"
 #, c-format
 msgid "file %s has only %lu line"
 msgid_plural "file %s has only %lu lines"
-msgstr[0] "檔案 %s 只有 %lu "
+msgstr[0] "檔案 %s 只有 %lu "
 
 #: builtin/blame.c
 msgid "Blaming lines"
-msgstr "追蹤程式碼行"
+msgstr "溯源文字列"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
-msgstr "git branch [<選項>] [-r | -a] [--merged] [--no-merged]"
+msgstr "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
 
 #: builtin/branch.c
 msgid ""
 "git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
 "point>]"
-msgstr "git branch [<選項>] [-f] [--recurse-submodules] <分支名> [<起始點>]"
+msgstr ""
+"git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
+"point>]"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-l] [<pattern>...]"
-msgstr "git branch [<選項>] [-l] [<模式>...]"
+msgstr "git branch [<options>] [-l] [<pattern>...]"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-r] (-d | -D) <branch-name>..."
-msgstr "git branch [<選項>] [-r] (-d | -D) <分支名>..."
+msgstr "git branch [<options>] [-r] (-d | -D) <branch-name>..."
 
 #: builtin/branch.c
 msgid "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"
-msgstr "git branch [<選項>] (-m | -M) [<舊分支>] <新分支>"
+msgstr "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"
 
 #: builtin/branch.c
 msgid "git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"
-msgstr "git branch [<選項>] (-c | -C) [<老分支>] <新分支>"
+msgstr "git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-r | -a] [--points-at]"
-msgstr "git branch [<選項>] [-r | -a] [--points-at]"
+msgstr "git branch [<options>] [-r | -a] [--points-at]"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-r | -a] [--format]"
-msgstr "git branch [<選項>] [-r | -a] [--format]"
+msgstr "git branch [<options>] [-r | -a] [--format]"
 
 #  譯者:保持原換行格式,在輸出時 %s 的替代內容會讓字串變長
 #: builtin/branch.c
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
-"將要刪除的分支 '%s' 已經被合併到\n"
-"         '%s',但未合併到 HEAD。"
+"將要刪除的 “%s” 分支已經被合併到\n"
+"         “%s”,但尚未合併到 HEAD"
 
 #  譯者:保持原換行格式,在輸出時 %s 的替代內容會讓字串變長
 #: builtin/branch.c
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"並未刪除分支 '%s', 雖然它已經合併到 HEAD,\n"
-"         然而卻尚未被合併到分支 '%s' 。"
+"並未刪除分支 “%s”, 雖然已經合併到 HEAD,\n"
+"         卻尚未被合併至 “%s” 分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "無法查詢 '%s' 指向的提交物件"
+msgid "couldn't look up commit object for '%s'"
+msgstr "無法查詢 “%s” 指向的提交物件"
 
 #: builtin/branch.c
 #, c-format
 msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+"the branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
-"分支 '%s' 沒有完全合併。\n"
-"如果您確認要刪除它,執行 'git branch -D %s'。"
+"分支 “%s” 沒有完全合併。\n"
+"如果確定要刪除它,請執行 “git branch -D %s”"
 
 #: builtin/branch.c
-msgid "Update of config-file failed"
-msgstr "更新設定檔案失敗"
+msgid "update of config-file failed"
+msgstr "更新組態檔案失敗"
 
 #: builtin/branch.c
 msgid "cannot use -a with -d"
 msgstr "不能將 -a 和 -d 同時使用"
 
 #: builtin/branch.c
-msgid "Couldn't look up commit object for HEAD"
-msgstr "無法查詢 HEAD 指向的提交物件"
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr "無法刪除被位於 “%2$s” 的工作區使用的 “%1$s” 分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "無法刪除在「%2$s」簽出的「%1$s」分支"
+msgid "remote-tracking branch '%s' not found"
+msgstr "找不到 “%s” 遠端追蹤分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "未能找到遠端追蹤分支 '%s'。"
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"找不到 “%s” 分支。\n"
+"您可能要加上 --remote?"
 
 #: builtin/branch.c
 #, c-format
-msgid "branch '%s' not found."
-msgstr "分支 '%s' 未發現。"
+msgid "branch '%s' not found"
+msgstr "找不到 “%s” 分支"
 
 #: builtin/branch.c
 #, c-format
@@ -3397,70 +3408,77 @@ msgstr "已刪除分支 %s(曾為 %s)。\n"
 
 #: builtin/branch.c builtin/tag.c
 msgid "unable to parse format string"
-msgstr "不能解析格式化字串"
+msgstr "無法解析格式化字串"
 
 #: builtin/branch.c
 msgid "could not resolve HEAD"
-msgstr "不能解析 HEAD 提交"
+msgstr "無法解析 HEAD 指針"
 
 #: builtin/branch.c
 #, c-format
 msgid "HEAD (%s) points outside of refs/heads/"
-msgstr "HEAD (%s) 指向 refs/heads/ 之外"
+msgstr "HEAD 指針 (%s) 指向 refs/heads/ 之外"
+
+#: builtin/branch.c
+#, c-format
+msgid "branch %s is being rebased at %s"
+msgstr "%s 分支正在重定基底至 %s"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "分支 %s 正被重定基底到 %s"
+msgid "branch %s is being bisected at %s"
+msgstr "%s 分支正於 %s 進行二分搜尋"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "分支 %s 正被二分搜尋於 %s"
+msgid "HEAD of working tree %s is not updated"
+msgstr "%s 工作區的 HEAD 指針未被更新"
 
 #: builtin/branch.c
-msgid "cannot copy the current branch while not on any."
-msgstr "無法複製目前分支因為不處於任何分支上。"
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "分支名稱無效:“%s”"
 
 #: builtin/branch.c
-msgid "cannot rename the current branch while not on any."
-msgstr "無法重新命名目前分支因為不處於任何分支上。"
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "分支 “%s” 尚無提交"
 
 #: builtin/branch.c
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "無效的分支名:'%s'"
+msgid "no branch named '%s'"
+msgstr "沒有名為 “%s” 的分支"
 
 #: builtin/branch.c
-msgid "Branch rename failed"
+msgid "branch rename failed"
 msgstr "分支重新命名失敗"
 
 #: builtin/branch.c
-msgid "Branch copy failed"
-msgstr "分支複製失敗"
+msgid "branch copy failed"
+msgstr "分支拷貝失敗"
 
 #: builtin/branch.c
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "已為錯誤命名的分支 '%s' 建立了一個副本"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "已為誤命名的 “%s” 分支建立拷貝"
 
 #: builtin/branch.c
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "已將錯誤命名的分支 '%s' 重新命名"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "已更改誤命名的 “%s” 分支的名稱"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "分支重新命名為 %s,但 HEAD 沒有更新!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "分支已重新命名為 %s,但 HEAD 指針尚未更新"
 
 #: builtin/branch.c
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "分支被重新命名,但更新設定檔案失敗"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "分支已重新命名,但無法更新組態檔案"
 
 #: builtin/branch.c
-msgid "Branch is copied, but update of config-file failed"
-msgstr "分支已複製,但更新設定檔案失敗"
+msgid "branch is copied, but update of config-file failed"
+msgstr "分支已拷貝,但無法更新組態檔案"
 
 #: builtin/branch.c
 #, c-format
@@ -3469,41 +3487,41 @@ msgid ""
 "  %s\n"
 "Lines starting with '%c' will be stripped.\n"
 msgstr ""
-"請編輯分支的描述\n"
+"請編輯下述分支的描述\n"
 "  %s\n"
-"以 '%c' 開頭的行將被過濾。\n"
+"開頭是 “%c” 的列將被刪除。\n"
 
 #: builtin/branch.c
 msgid "Generic options"
-msgstr "通用選項"
+msgstr "一般性選項"
 
 #: builtin/branch.c
 msgid "show hash and subject, give twice for upstream branch"
-msgstr "顯示雜湊值和主題,若參數出現兩次則顯示上游分支"
+msgstr "顯示雜湊值和主旨,若傳入兩次則顯示上游分支"
 
 #: builtin/branch.c
 msgid "suppress informational messages"
-msgstr "不顯示訊息"
+msgstr "ä¸\8d顯示è³\87è¨\8aè¨\8aæ\81¯"
 
 #: builtin/branch.c builtin/checkout.c builtin/submodule--helper.c
 msgid "set branch tracking configuration"
-msgstr "設定分支追蹤設定"
+msgstr "設定分支追蹤組態"
 
 #: builtin/branch.c
 msgid "do not use"
-msgstr "不要使用"
+msgstr "請勿使用"
 
 #: builtin/branch.c
 msgid "upstream"
-msgstr "上游"
+msgstr "upstream"
 
 #: builtin/branch.c
 msgid "change the upstream info"
-msgstr "改變上游訊息"
+msgstr "變更上游資訊"
 
 #: builtin/branch.c
 msgid "unset the upstream info"
-msgstr "取消上游資訊設定"
+msgstr "取消設定上游資訊"
 
 #: builtin/branch.c
 msgid "use colored output"
@@ -3515,19 +3533,19 @@ msgstr "作用於遠端追蹤分支"
 
 #: builtin/branch.c
 msgid "print only branches that contain the commit"
-msgstr "只列印包含該提交的分支"
+msgstr "只輸出包含此提交的分支"
 
 #: builtin/branch.c
 msgid "print only branches that don't contain the commit"
-msgstr "只列印不包含該提交的分支"
+msgstr "只輸出不包含此提交的分支"
 
 #: builtin/branch.c
 msgid "Specific git-branch actions:"
-msgstr "具體的 git-branch 動作:"
+msgstr "特定的 git-branch 動作:"
 
 #: builtin/branch.c
 msgid "list both remote-tracking and local branches"
-msgstr "å\88\97å\87ºé\81 ç«¯è¿½è¹¤å\8f\8a本機分支"
+msgstr "å\88\97å\87ºé\81 ç«¯è¿½è¹¤å\92\8c本機分支"
 
 #: builtin/branch.c
 msgid "delete fully merged branch"
@@ -3535,31 +3553,35 @@ msgstr "刪除完全合併的分支"
 
 #: builtin/branch.c
 msgid "delete branch (even if not merged)"
-msgstr "刪除分支(即使沒有合併)"
+msgstr "刪除分支(即使尚未合併)"
 
 #: builtin/branch.c
 msgid "move/rename a branch and its reflog"
-msgstr "移動/重新命名一個分支,以及它的引用日誌"
+msgstr "移動或重新命名分支及其引用日誌"
 
 #: builtin/branch.c
 msgid "move/rename a branch, even if target exists"
-msgstr "移動/重新命名一個分支,即使目標已存在"
+msgstr "即使目標已存在,仍移動或重新命名分支"
+
+#: builtin/branch.c builtin/for-each-ref.c builtin/tag.c
+msgid "do not output a newline after empty formatted refs"
+msgstr "在格式化引用結果為空之後,不要輸出換列符號"
 
 #: builtin/branch.c
 msgid "copy a branch and its reflog"
-msgstr "複製分支及其引用日誌"
+msgstr "拷貝分支及其引用日誌"
 
 #: builtin/branch.c
 msgid "copy a branch, even if target exists"
-msgstr "複製一個分支,即使目標已存在"
+msgstr "即使目標已存在仍拷貝分支"
 
 #: builtin/branch.c
 msgid "list branch names"
-msgstr "列出分支名"
+msgstr "列出分支名"
 
 #: builtin/branch.c
 msgid "show current branch name"
-msgstr "顯示目前分支名"
+msgstr "顯示目前分支名"
 
 #: builtin/branch.c builtin/submodule--helper.c
 msgid "create the branch's reflog"
@@ -3567,7 +3589,7 @@ msgstr "建立分支的引用日誌"
 
 #: builtin/branch.c
 msgid "edit the description for the branch"
-msgstr "標記分支的描述"
+msgstr "編輯分支的描述"
 
 #: builtin/branch.c
 msgid "force creation, move/rename, deletion"
@@ -3575,27 +3597,27 @@ msgstr "強制建立、移動/重新命名、刪除"
 
 #: builtin/branch.c
 msgid "print only branches that are merged"
-msgstr "只列印已經合併的分支"
+msgstr "只輸出已經合併的分支"
 
 #: builtin/branch.c
 msgid "print only branches that are not merged"
-msgstr "只列印尚未合併的分支"
+msgstr "只輸出尚未合併的分支"
 
 #: builtin/branch.c
 msgid "list branches in columns"
-msgstr "以å\88\97ç\9a\84æ\96¹å¼\8f顯示å\88\86æ\94¯"
+msgstr "以è¡\8cç\9a\84å½¢å¼\8få\88\97å\87ºå\88\86æ\94¯"
 
 #: builtin/branch.c builtin/for-each-ref.c builtin/notes.c builtin/tag.c
 msgid "object"
-msgstr "物件"
+msgstr "object"
 
 #: builtin/branch.c
 msgid "print only branches of the object"
-msgstr "只列印指向該物件的分支"
+msgstr "只輸出指向此物件的分支"
 
 #: builtin/branch.c builtin/for-each-ref.c builtin/tag.c
 msgid "sorting and filtering are case insensitive"
-msgstr "排序和過濾屬於大小寫不敏感"
+msgstr "排序和過濾不區分大小寫"
 
 #: builtin/branch.c builtin/ls-files.c
 msgid "recurse through submodules"
@@ -3606,20 +3628,20 @@ msgstr "在子模組中遞迴"
 msgid "format to use for the output"
 msgstr "輸出格式"
 
-#: builtin/branch.c builtin/submodule--helper.c submodule.c
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "無法將 HEAD 解析為有效引用"
+#: builtin/branch.c
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "無法將 HEAD 解析為有效引用"
 
 #: builtin/branch.c builtin/clone.c
 msgid "HEAD not found below refs/heads!"
-msgstr "HEAD æ²\92æ\9c\89ä½\8dæ\96¼ /refs/heads ä¹\8bä¸\8b!"
+msgstr "HEAD æ\8c\87é\87\9dä¸\8då\9c¨ /refs/heads ä¸­!"
 
 #: builtin/branch.c
 msgid ""
 "branch with --recurse-submodules can only be used if submodule."
 "propagateBranches is enabled"
 msgstr ""
-"有 --recurse-submodules 的分支只能在啟用 submodule.propagateBranches 時使用"
+"有 --recurse-submodules 的分支只能在啟用 submodule.propagateBranches 時使用"
 
 #: builtin/branch.c
 msgid "--recurse-submodules can only be used to create branches"
@@ -3627,81 +3649,78 @@ msgstr "--recurse-submodules 只能用來建立分支"
 
 #: builtin/branch.c
 msgid "branch name required"
-msgstr "必須提供分支名"
+msgstr "必須提供分支名"
 
 #: builtin/branch.c
-msgid "Cannot give description to detached HEAD"
-msgstr "不能向分離開頭指標提供描述"
+msgid "cannot give description to detached HEAD"
+msgstr "無法向分離 HEAD 指針提供描述"
 
 #: builtin/branch.c
 msgid "cannot edit description of more than one branch"
-msgstr "不能為一個以上的分支編輯描述"
+msgstr "無法編輯超過一個分支的描述"
 
 #: builtin/branch.c
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "分支 '%s' 尚無提交。"
+msgid "cannot copy the current branch while not on any"
+msgstr "不在任何分支上,無法拷貝目前分支"
 
 #: builtin/branch.c
-#, c-format
-msgid "No branch named '%s'."
-msgstr "沒有分支 '%s'。"
+msgid "cannot rename the current branch while not on any"
+msgstr "不在任何分支上,無法重新命名目前分支"
 
 #: builtin/branch.c
 msgid "too many branches for a copy operation"
-msgstr "為複製動作提供了太多的分支名"
+msgstr "要進行拷貝動作的分支太多"
 
 #: builtin/branch.c
 msgid "too many arguments for a rename operation"
-msgstr "為重新命名動作提供了太多的參數"
+msgstr "傳入重新命名動作的引數太多"
 
 #: builtin/branch.c
 msgid "too many arguments to set new upstream"
-msgstr "為設定新上游提供了太多的參數"
+msgstr "要設定新上游的引數太多"
 
 #: builtin/branch.c
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
-msgstr "無法設定 HEAD 的上游為 %s,因為 HEAD 沒有指向任何分支。"
+"could not set upstream of HEAD to %s when it does not point to any branch"
+msgstr "無法將 HEAD 的上游設為 %s:其未指向任何分支"
 
 #: builtin/branch.c
 #, c-format
 msgid "no such branch '%s'"
-msgstr "沒有此分支 '%s'"
+msgstr "無 “%s” 分支"
 
 #: builtin/branch.c
 #, c-format
 msgid "branch '%s' does not exist"
-msgstr "分支 '%s' 不存在"
+msgstr "沒有 “%s” 分支"
 
 #: builtin/branch.c
 msgid "too many arguments to unset upstream"
-msgstr "為取消上游設定動作提供了太多的參數"
+msgstr "要取消設定上游的引數太多"
 
 #: builtin/branch.c
-msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr "在 HEAD 的上游未指向任何分支時無法取消設定。"
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr "無法取消設定 HEAD 的上游:其未指向任何分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "分支 '%s' 沒有上游訊息"
+msgid "branch '%s' has no upstream information"
+msgstr "分支 “%s” 沒有上游資訊"
 
 #: builtin/branch.c
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"'git branch' 的 -a 和 -r 選項不帶一個分支名。\n"
-"您是否想要使用:-a|-r --list <模式>?"
+"“git branch” 的 -a 和 -r 選項不取分支名稱。\n"
+"您是想使用:-a|-r --list <pattern> 嗎?"
 
 #: builtin/branch.c
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
-msgstr ""
-"不再支援選項 '--set-upstream'。請使用 '--track' 或 '--set-upstream-to'。"
+"'--set-upstream-to' instead"
+msgstr "不再支援選項 “--set-upstream”。請改用 “--track” 或 “--set-upstream-to”"
 
 #: builtin/bugreport.c
 msgid "git version:\n"
@@ -3710,7 +3729,7 @@ msgstr "git 版本:\n"
 #: builtin/bugreport.c
 #, c-format
 msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() 失敗,錯誤:「%s」(%d)\n"
+msgstr "uname() 失敗,錯誤:“%s” (%d)\n"
 
 #: builtin/bugreport.c
 msgid "compiler info: "
@@ -3722,15 +3741,15 @@ msgstr "libc 資訊: "
 
 #: builtin/bugreport.c
 msgid "not run from a git repository - no hooks to show\n"
-msgstr "ä¸\8dæ\98¯å¾\9e git ç\89\88æ\9c¬åº«å\9f·è¡\8c - 沒有可顯示的掛鉤\n"
+msgstr "ä¸\8dæ\98¯å\9c¨ git ç\89\88æ\9c¬åº«å\9f·è¡\8c â\80\94 沒有可顯示的掛鉤\n"
 
 #: builtin/bugreport.c
 msgid ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [-o|--output-directory <file>] [-s|--suffix <format>] [--"
-"diagnose[=<mode>]"
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 
 #: builtin/bugreport.c
 msgid ""
@@ -3751,15 +3770,15 @@ msgid ""
 "You can delete any lines you don't wish to share.\n"
 msgstr ""
 "感謝您填寫 Git 臭蟲報告!\n"
-"請回答下列問題,以讓我們能夠了解您的問題。\n"
+"請回答下列詢問,協助我們了解您的問題。\n"
 "\n"
 "臭蟲發生前,您做了什麼?(重現問題的步驟)\n"
 "\n"
-"你原本期望發生什麼?(期望行為)\n"
+"原本您預期發生什麼?(預期行為)\n"
 "\n"
 "那實際上發生了什麼?(實際行為)\n"
 "\n"
-"期望行為跟實際發生的行為有什麼不同?\n"
+"預期發生的行為,跟實際發生的行為有什麼不同?\n"
 "\n"
 "其他您想加入的:\n"
 "\n"
@@ -3769,25 +3788,30 @@ msgstr ""
 #: builtin/bugreport.c builtin/commit.c builtin/fast-export.c builtin/rebase.c
 #: parse-options.h
 msgid "mode"
-msgstr "模式"
+msgstr "mode"
 
 #: builtin/bugreport.c
 msgid ""
 "create an additional zip archive of detailed diagnostics (default 'stats')"
-msgstr "建ç«\8b詳細診æ\96·è³\87è¨\8aç\9a\84é¡\8då¤\96 zip å°\81å­\98æª\94æ¡\88ï¼\88é \90設å\80¼æ\98¯ “stats”)"
+msgstr "å\8f¦å¤\96建ç«\8bæ\9c\89詳細診æ\96·è³\87è¨\8aç\9a\84 ZIP å°\81å­\98æª\94ï¼\88é \90設å\80¼ “stats”)"
 
 #: builtin/bugreport.c
 msgid "specify a destination for the bugreport file(s)"
-msgstr "指定錯誤報告檔案的目的地"
+msgstr "指定臭蟲報告檔案的目的地"
 
 #: builtin/bugreport.c
 msgid "specify a strftime format suffix for the filename(s)"
-msgstr "指定檔名,strftime 格式的後綴"
+msgstr "指定用於檔名的 strftime 格式後綴"
+
+#: builtin/bugreport.c
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "未知引數 “%s”"
 
 #: builtin/bugreport.c builtin/diagnose.c
 #, c-format
 msgid "could not create leading directories for '%s'"
-msgstr "無法建立 '%s' 的前置目錄"
+msgstr "無法建立 “%s” 的前置目錄"
 
 #: builtin/bugreport.c builtin/diagnose.c
 #, c-format
@@ -3810,51 +3834,59 @@ msgstr "無法寫入 %s"
 #: builtin/bugreport.c
 #, c-format
 msgid "Created new report at '%s'.\n"
-msgstr "已在「%s」建立新報告。\n"
+msgstr "已在 “%s” 建立新報告。\n"
 
 #: builtin/bundle.c
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
-msgstr "git bundle create [<選項>] <檔案> <git-rev-list 參數>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
 
 #: builtin/bundle.c
-msgid "git bundle verify [<options>] <file>"
-msgstr "git bundle verify [<選項>] <檔案>"
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <file>"
 
 #: builtin/bundle.c
 msgid "git bundle list-heads <file> [<refname>...]"
-msgstr "git bundle list-heads <檔案> [<引用名稱>...]"
+msgstr "git bundle list-heads <file> [<refname>...]"
+
+#: builtin/bundle.c
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <file> [<refname>...]"
 
 #: builtin/bundle.c
-msgid "git bundle unbundle <file> [<refname>...]"
-msgstr "git bundle unbundle <檔案> [<引用名稱>...]"
+msgid "need a <file> argument"
+msgstr "需要提供 <file> 引數"
 
 #: builtin/bundle.c builtin/pack-objects.c
 msgid "do not show progress meter"
-msgstr "不顯示進度"
+msgstr "不顯示進度"
 
 #: builtin/bundle.c builtin/pack-objects.c
 msgid "show progress meter"
-msgstr "顯示進度"
+msgstr "顯示進度"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "show progress meter during object writing phase"
-msgstr "在物件寫入階段顯示進度表"
+#: builtin/bundle.c
+msgid "historical; same as --progress"
+msgstr "歷史遺留選項;和 --progress 相同"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "當進度表顯示時類似於 --all-progress"
+#: builtin/bundle.c
+msgid "historical; does nothing"
+msgstr "歷史遺留選項;沒有作用"
 
 #: builtin/bundle.c
 msgid "specify bundle format version"
-msgstr "指定套件的格式版本"
+msgstr "指定套件的格式版本"
 
 #: builtin/bundle.c
 msgid "Need a repository to create a bundle."
-msgstr "需要版本庫來建立套件。"
+msgstr "需要版本庫來建立套件。"
 
 #: builtin/bundle.c
 msgid "do not show bundle details"
-msgstr "不顯示套件詳細資訊"
+msgstr "不顯示套件包的詳細資訊"
 
 #: builtin/bundle.c
 #, c-format
@@ -3863,16 +3895,16 @@ msgstr "%s 可以\n"
 
 #: builtin/bundle.c
 msgid "Need a repository to unbundle."
-msgstr "需要版本庫才能拆分套件。"
+msgstr "需要版本庫才能拆分套件。"
 
 #: builtin/bundle.c
 msgid "Unbundling objects"
-msgstr "正在解包物件"
+msgstr "正在拆分物件"
 
 #: builtin/cat-file.c merge-recursive.c
 #, c-format
 msgid "cannot read object %s '%s'"
-msgstr "不能讀取物件 %s '%s'"
+msgstr "無法讀取物件 %s “%s”"
 
 #: builtin/cat-file.c
 msgid "flush is only for --buffer mode"
@@ -3885,7 +3917,7 @@ msgstr "輸入中沒有命令"
 #: builtin/cat-file.c
 #, c-format
 msgid "whitespace before command: '%s'"
-msgstr "命令前空格:「%s」"
+msgstr "命令前有空格:“%s”"
 
 #: builtin/cat-file.c
 #, c-format
@@ -3897,11 +3929,6 @@ msgstr "%s 需要引數"
 msgid "%s takes no arguments"
 msgstr "%s 不取引數"
 
-#: builtin/cat-file.c
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "未知命令:「%s」"
-
 #: builtin/cat-file.c
 msgid "only one batch option may be specified"
 msgstr "只能指定一個批次處理選項"
@@ -3918,25 +3945,25 @@ msgstr "git cat-file (-e | -p) <object>"
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <object>"
 
+#: builtin/cat-file.c
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+
 #: builtin/cat-file.c
 msgid ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
-
-#: builtin/cat-file.c
-msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
-msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+"             [--textconv | --filters] [-Z]"
 
 #: builtin/cat-file.c
 msgid "Check object existence or emit object contents"
@@ -3952,7 +3979,7 @@ msgstr "美化輸出 <object> 的內容"
 
 #: builtin/cat-file.c
 msgid "Emit [broken] object attributes"
-msgstr "輸出 [損壞的] 物件屬性"
+msgstr "輸出 [損壞的] ([broken]) 物件屬性"
 
 #: builtin/cat-file.c
 msgid "show object type (one of 'blob', 'tree', 'commit', 'tag', ...)"
@@ -3984,15 +4011,19 @@ msgstr "類似 --batch 但不輸出 <contents>"
 
 #: builtin/cat-file.c
 msgid "stdin is NUL-terminated"
-msgstr "標準輸入的結尾是 NUL"
+msgstr "stdin 以 NUL 字元終止"
+
+#: builtin/cat-file.c
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin 和 stdout 以 NUL 字元終止"
 
 #: builtin/cat-file.c
 msgid "read commands from stdin"
-msgstr "從標準輸入讀取命令"
+msgstr "從 stdin 讀取命令"
 
 #: builtin/cat-file.c
 msgid "with --batch[-check]: ignores stdin, batches all known objects"
-msgstr "傳入 --batch[-check]:忽略標準輸入,批次處理所有已知物件"
+msgstr "傳入 --batch[-check]:忽略 stdin,批次處理所有已知物件"
 
 #: builtin/cat-file.c
 msgid "Change or optimize batch output"
@@ -4004,21 +4035,21 @@ msgstr "緩衝 --batch 的輸出"
 
 #: builtin/cat-file.c
 msgid "follow in-tree symlinks"
-msgstr "追蹤樹中的符號連結"
+msgstr "追蹤樹狀物件中的符號連結"
 
 #: builtin/cat-file.c
 msgid "do not order objects before emitting them"
-msgstr "不要在輸出物件前排序"
+msgstr "不要在輸出前排序物件"
 
 #: builtin/cat-file.c
 msgid ""
 "Emit object (blob or tree) with conversion or filter (stand-alone, or with "
 "batch)"
-msgstr "轉換或過濾後輸出物件(blob 或樹)(單獨或批次處理)"
+msgstr "轉換或過濾後輸出物件 (blob 或 tree)(單獨或批次處理)"
 
 #: builtin/cat-file.c
 msgid "run textconv on object's content"
-msgstr "å\9c¨ç\89©ä»¶å\85§å®¹å\9f·è¡\8c textconv"
+msgstr "å°\8dç\89©ä»¶å\85§å®¹é\80²è¡\8cæ\96\87å­\97è½\89æ\8f\9b (textconv)"
 
 #: builtin/cat-file.c
 msgid "run filters on object's content"
@@ -4035,7 +4066,7 @@ msgstr "請在 (--textconv | --filters) 使用 <path>,而非 “batch”"
 #: builtin/cat-file.c
 #, c-format
 msgid "'%s=<%s>' needs '%s' or '%s'"
-msgstr "「%s=<%s>」需要「%s」或「%s」"
+msgstr "“%s=<%s>” 需要 “%s” 或 “%s”"
 
 #: builtin/cat-file.c
 msgid "path|tree-ish"
@@ -4044,12 +4075,12 @@ msgstr "path|tree-ish"
 #: builtin/cat-file.c
 #, c-format
 msgid "'%s' requires a batch mode"
-msgstr "「%s」需要批次處理模式"
+msgstr "“%s” 需要批次處理模式"
 
 #: builtin/cat-file.c
 #, c-format
 msgid "'-%c' is incompatible with batch mode"
-msgstr "「-%c」與批次處理模式不相容"
+msgstr "“-%c” 與批次處理模式不相容"
 
 #: builtin/cat-file.c
 msgid "batch modes take no arguments"
@@ -4058,17 +4089,12 @@ msgstr "批次處理模式不取引數"
 #: builtin/cat-file.c
 #, c-format
 msgid "<rev> required with '%s'"
-msgstr "<rev> 需要搭配「%s」"
+msgstr "“%s” 需要 <rev>"
 
 #: builtin/cat-file.c
 #, c-format
 msgid "<object> required with '-%c'"
-msgstr "<object> 需要搭配「-%c」"
-
-#: builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
-msgid "too many arguments"
-msgstr "太多參數"
+msgstr "“-%c” 需要 <object>"
 
 #: builtin/cat-file.c
 #, c-format
@@ -4076,16 +4102,22 @@ msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr "只允許在 <type> <object> 模式傳入兩個引數,但傳了 %d 個"
 
 #: builtin/check-attr.c
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <屬性>...] [--] <路徑名>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
 
 #: builtin/check-attr.c
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <屬性>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
 
 #: builtin/check-attr.c
 msgid "report all attributes set on file"
-msgstr "å ±å\91\8a設定在檔案上的所有屬性"
+msgstr "å\9b\9eå ±設定在檔案上的所有屬性"
 
 #: builtin/check-attr.c
 msgid "use .gitattributes only from the index"
@@ -4093,11 +4125,19 @@ msgstr "只使用索引中的 .gitattributes"
 
 #: builtin/check-attr.c builtin/check-ignore.c builtin/hash-object.c
 msgid "read file names from stdin"
-msgstr "從標準輸入讀出檔案名"
+msgstr "從 stdin 讀出檔案名稱"
 
 #: builtin/check-attr.c builtin/check-ignore.c
 msgid "terminate input and output records by a NUL character"
-msgstr "輸入和輸出的紀錄使用 NUL 字元終結"
+msgstr "輸入和輸出的紀錄使用 NUL 字元終止"
+
+#: builtin/check-attr.c
+msgid "<tree-ish>"
+msgstr "<tree-ish>"
+
+#: builtin/check-attr.c
+msgid "which tree-ish to check attributes at"
+msgstr "要用哪一個樹狀物件檢查屬性"
 
 #: builtin/check-ignore.c builtin/checkout.c builtin/gc.c builtin/worktree.c
 msgid "suppress progress reporting"
@@ -4105,7 +4145,7 @@ msgstr "不顯示進度報告"
 
 #: builtin/check-ignore.c
 msgid "show non-matching input paths"
-msgstr "顯示符合的輸入路徑"
+msgstr "顯示符合的輸入路徑"
 
 #: builtin/check-ignore.c
 msgid "ignore index when checking"
@@ -4113,11 +4153,11 @@ msgstr "檢查時忽略索引"
 
 #: builtin/check-ignore.c
 msgid "cannot specify pathnames with --stdin"
-msgstr "不能同時指定路徑及 --stdin 參數"
+msgstr "無法同時指定路徑名稱及 --stdin"
 
 #: builtin/check-ignore.c
 msgid "-z only makes sense with --stdin"
-msgstr "-z é\9c\80è¦\81å\92\8c --stdin å\8f\83æ\95¸å\85±用才有意義"
+msgstr "-z é\9c\80è¦\81å\92\8c --stdin å\90\8cæ\99\82使用才有意義"
 
 #: builtin/check-ignore.c
 msgid "no path specified"
@@ -4125,11 +4165,11 @@ msgstr "未指定路徑"
 
 #: builtin/check-ignore.c
 msgid "--quiet is only valid with a single pathname"
-msgstr "å\8f\83æ\95¸ --quiet å\8fªå\9c¨æ\8f\90ä¾\9bä¸\80å\80\8bè·¯å¾\91å\90\8d時有效"
+msgstr "å¼\95æ\95¸ --quiet å\8fªå\9c¨æ\8f\90ä¾\9bä¸\80å\80\8bè·¯å¾\91å\90\8d稱時有效"
 
 #: builtin/check-ignore.c
 msgid "cannot have both --quiet and --verbose"
-msgstr "不能同時提供 --quiet 和 --verbose 參數"
+msgstr "無法同時傳入 --quiet 和 --verbose"
 
 #: builtin/check-ignore.c
 msgid "--non-matching is only valid with --verbose"
@@ -4137,41 +4177,41 @@ msgstr "--non-matching 選項只在使用 --verbose 時有效"
 
 #: builtin/check-mailmap.c
 msgid "git check-mailmap [<options>] <contact>..."
-msgstr "git check-mailmap [<選項>] <聯繫位址>..."
+msgstr "git check-mailmap [<options>] <contact>..."
 
 #: builtin/check-mailmap.c
 msgid "also read contacts from stdin"
-msgstr "還從標準輸入讀取聯繫位址"
+msgstr "亦從 stdin 讀取聯絡地址"
 
 #: builtin/check-mailmap.c
 #, c-format
 msgid "unable to parse contact: %s"
-msgstr "不能解析聯繫位址:%s"
+msgstr "無法解析聯絡地址:%s"
 
 #: builtin/check-mailmap.c
 msgid "no contacts specified"
-msgstr "æ\9cªæ\8c\87å®\9aè\81¯ç¹«ä½\8d址"
+msgstr "æ\9cªæ\8c\87å®\9aè\81¯çµ¡å\9c°址"
 
 #: builtin/checkout--worker.c
 msgid "git checkout--worker [<options>]"
-msgstr "git checkout--worker [<選項>]"
+msgstr "git checkout--worker [<options>]"
 
 #: builtin/checkout--worker.c builtin/checkout-index.c builtin/column.c
 #: builtin/submodule--helper.c builtin/worktree.c
 msgid "string"
-msgstr "字串"
+msgstr "string"
 
 #: builtin/checkout--worker.c builtin/checkout-index.c
 msgid "when creating files, prepend <string>"
-msgstr "å\9c¨å»ºç«\8bæª\94æ¡\88æ\99\82ï¼\8cå\9c¨å\89\8dé\9d¢å\8a ä¸\8a <å­\97串>"
+msgstr "建ç«\8bæª\94æ¡\88æ\99\82ï¼\8cå\9c¨å\89\8dé\9d¢å\8a ä¸\8a <string>"
 
 #: builtin/checkout-index.c
 msgid "git checkout-index [<options>] [--] [<file>...]"
-msgstr "git checkout-index [<選項>] [--] [<檔案>...]"
+msgstr "git checkout-index [<options>] [--] [<file>...]"
 
 #: builtin/checkout-index.c
 msgid "stage should be between 1 and 3 or all"
-msgstr "索引值應該取值 1 到 3 或者 all"
+msgstr "stage 應該是 1 到 3 的值,或者是 all"
 
 #: builtin/checkout-index.c
 msgid "check out all files in the index"
@@ -4195,11 +4235,11 @@ msgstr "不簽出新檔案"
 
 #: builtin/checkout-index.c
 msgid "update stat information in the index file"
-msgstr "更新索引檔案的狀態訊息"
+msgstr "更新索引檔案的狀態訊息"
 
 #: builtin/checkout-index.c
 msgid "read list of paths from the standard input"
-msgstr "從標準輸入讀取路徑列表"
+msgstr "從標準輸入讀取路徑清單"
 
 #: builtin/checkout-index.c
 msgid "write the content to temporary files"
@@ -4211,49 +4251,49 @@ msgstr "從指定暫存區中拷出檔案"
 
 #: builtin/checkout.c
 msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<選項>] <分支>"
+msgstr "git checkout [<options>] <branch>"
 
 #: builtin/checkout.c
 msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<選項>] [<分支>] -- <檔案>..."
+msgstr "git checkout [<options>] [<branch>] -- <file>..."
 
 #: builtin/checkout.c
 msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<選項>] [<分支>]"
+msgstr "git switch [<options>] [<branch>]"
 
 #: builtin/checkout.c
 msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<選項>] [--source=<分支>] <檔案>..."
+msgstr "git restore [<options>] [--source=<branch>] <file>..."
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have our version"
-msgstr "路徑 '%s' 沒有我們的版本"
+msgstr "“%s” 路徑沒有我們的版本"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have their version"
-msgstr "路徑 '%s' 沒有他們的版本"
+msgstr "“%s” 路徑沒有他們的版本"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have all necessary versions"
-msgstr "路徑 '%s' 沒有全部必需的版本"
+msgstr "“%s” 路徑沒有所有必須的版本"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have necessary versions"
-msgstr "路徑 '%s' 沒有必需的版本"
+msgstr "“%s” 路徑沒有必須的版本"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s': cannot merge"
-msgstr "path '%s':無法合併"
+msgstr "path “%s”:無法合併"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Unable to add merge result for '%s'"
-msgstr "無法為 '%s' 新增合併結果"
+msgstr "無法為 “%s” 加上合併結果"
 
 #: builtin/checkout.c
 #, c-format
@@ -4271,41 +4311,46 @@ msgstr[0] "從 %2$s 更新了 %1$d 個路徑"
 #, c-format
 msgid "Updated %d path from the index"
 msgid_plural "Updated %d paths from the index"
-msgstr[0] "å¾\9eç´¢å¼\95å\8d\80æ\9b´æ\96°äº\86 %d 個路徑"
+msgstr[0] "å·²å¾\9eç´¢å¼\95å\8d\80æ\9b´æ\96° %d 個路徑"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with updating paths"
-msgstr "'%s' 不能在更新路徑時使用"
+msgstr "無法在更新路徑時使用 “%s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
-msgstr "不能同時更新路徑並切換到分支'%s'。"
+msgstr "無法同時更新路徑和切換至 “%s” 分支。"
 
 #: builtin/checkout.c
 #, c-format
 msgid "neither '%s' or '%s' is specified"
-msgstr "'%s' 或 '%s' 都沒有指定"
+msgstr "“%s” 或 “%s” 皆未指定"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' must be used when '%s' is not specified"
-msgstr "未指定 '%2$s' 時,必須使用 '%1$s'"
+msgstr "未指定 “%2$s” 時,必須使用 “%1$s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' or '%s' cannot be used with %s"
-msgstr "'%s' 或 '%s' 不能和 %s 一起使用"
+msgstr "“%s” 或 “%s” 無法與 %s 一起使用"
+
+#: builtin/checkout.c
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "“%s”、“%s” 或 “%s” 無法在簽出樹狀物件時使用"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' is unmerged"
-msgstr "路徑 '%s' 未合併"
+msgstr "路徑 “%s” 未合併"
 
 #: builtin/checkout.c
 msgid "you need to resolve your current index first"
-msgstr "您需要先解決目前索引的衝突"
+msgstr "您需要先解決目前索引的衝突"
 
 #: builtin/checkout.c
 #, c-format
@@ -4313,13 +4358,13 @@ msgid ""
 "cannot continue with staged changes in the following files:\n"
 "%s"
 msgstr ""
-"不能繼續,下列檔案有暫存的修改:\n"
+"無法繼續,下列檔案有暫存的更動:\n"
 "%s"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Can not do reflog for '%s': %s\n"
-msgstr "不能對 '%s' 執行 reflog 動作:%s\n"
+msgstr "無法對 “%s” 執行 reflog 動作:%s\n"
 
 #: builtin/checkout.c
 msgid "HEAD is now at"
@@ -4327,38 +4372,38 @@ msgstr "HEAD 目前位於"
 
 #: builtin/checkout.c builtin/clone.c t/helper/test-fast-rebase.c
 msgid "unable to update HEAD"
-msgstr "不能更新 HEAD"
+msgstr "無法更新 HEAD"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Reset branch '%s'\n"
-msgstr "重設分支 '%s'\n"
+msgstr "重設分支 “%s”\n"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Already on '%s'\n"
-msgstr "已經位於 '%s'\n"
+msgstr "已經位於 “%s”\n"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
-msgstr "å\88\87æ\8f\9b並é\87\8d設å\88\86æ\94¯ '%s'\n"
+msgstr "å·²å\88\87æ\8f\9b並é\87\8d設å\88\86æ\94¯ â\80\9c%sâ\80\9d\n"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Switched to a new branch '%s'\n"
-msgstr "å\88\87æ\8f\9bå\88°ä¸\80å\80\8bæ\96°å\88\86æ\94¯ '%s'\n"
+msgstr "å·²å\88\87æ\8f\9bè\87³æ\96°å\88\86æ\94¯ â\80\9c%sâ\80\9d\n"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Switched to branch '%s'\n"
-msgstr "å\88\87æ\8f\9bå\88°å\88\86æ\94¯ '%s'\n"
+msgstr "å·²å\88\87æ\8f\9bè\87³å\88\86æ\94¯ â\80\9c%sâ\80\9d\n"
 
 #  譯者:請維持前導空格
 #: builtin/checkout.c
 #, c-format
 msgid " ... and %d more.\n"
-msgstr " ... 及其它 %d 個。\n"
+msgstr " …… 及其它 %d 個。\n"
 
 #: builtin/checkout.c
 #, c-format
@@ -4373,7 +4418,8 @@ msgid_plural ""
 "\n"
 "%s\n"
 msgstr[0] ""
-"警告:您正丟下 %d 個提交,未和任何分支關聯:\n"
+"警告:您正丟下 %d 個\n"
+"未和任何分支關聯的提交:\n"
 "\n"
 "%s\n"
 
@@ -4392,23 +4438,23 @@ msgid_plural ""
 " git branch <new-branch-name> %s\n"
 "\n"
 msgstr[0] ""
-"如果您想要透過建立新分支儲存它,這可能是一個好時候。\n"
-"如下動作:\n"
+"如果您想要透過建立新分支留下這些提交,\n"
+"現在很適合執行:\n"
 "\n"
-" git branch <新分支名> %s\n"
+" git branch <新分支名> %s\n"
 "\n"
 
 #: builtin/checkout.c
 msgid "internal error in revision walk"
-msgstr "在版本遍歷時遇到內部錯誤"
+msgstr "在修訂版遍歷時遇到內部錯誤"
 
 #: builtin/checkout.c
 msgid "Previous HEAD position was"
-msgstr "之前的 HEAD 位置是"
+msgstr "之前的 HEAD 指針位置在"
 
 #: builtin/checkout.c
 msgid "You are on a branch yet to be born"
-msgstr "您位於一個尚未初始化的分支"
+msgstr "您位於一個尚未初始化的分支"
 
 #: builtin/checkout.c
 #, c-format
@@ -4416,7 +4462,7 @@ msgid ""
 "'%s' could be both a local file and a tracking branch.\n"
 "Please use -- (and optionally --no-guess) to disambiguate"
 msgstr ""
-"'%s' 既可以是一個本機檔案,也可以是一個追蹤分支。\n"
+"“%s” 既可以是本機檔案,也可以是追蹤分支。\n"
 "請使用 --(和可選的 --no-guess)來消除歧義"
 
 #: builtin/checkout.c
@@ -4430,18 +4476,19 @@ msgid ""
 "one remote, e.g. the 'origin' remote, consider setting\n"
 "checkout.defaultRemote=origin in your config."
 msgstr ""
-"如果您想要簽出一個遠端追蹤分支,例如 'origin',您可以\n"
-"使用 --track 選項寫出全名:\n"
+"如果您想要簽出遠端追蹤分支,例如 “origin”,\n"
+"您可以使用 --track 選項寫出全名:\n"
 "\n"
 "    git checkout --track origin/<name>\n"
 "\n"
-"如果您總是喜歡使用模糊的簡短分支名 <name>,而不喜歡如 'origin' 的遠端\n"
-"版本庫名,可以在設定中設定 checkout.defaultRemote=origin。"
+"如果您平時比較想要使用模糊的簡短分支名稱 <name>,\n"
+"而不太想寫如 “origin” 的遠端版本庫名稱,\n"
+"可以在組態中設定 checkout.defaultRemote=origin。"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' matched multiple (%d) remote tracking branches"
-msgstr "'%s' 符合多個 (%d) 遠端追蹤分支"
+msgstr "“%s” 符合多個 (%d) 遠端追蹤分支"
 
 #: builtin/checkout.c
 msgid "only one reference expected"
@@ -4450,7 +4497,7 @@ msgstr "預期只有一個引用"
 #: builtin/checkout.c
 #, c-format
 msgid "only one reference expected, %d given."
-msgstr "只有一個引用,卻提供了 %d 個。"
+msgstr "預期只有一個引用,卻提供了 %d 個。"
 
 #: builtin/checkout.c builtin/worktree.c
 #, c-format
@@ -4460,112 +4507,112 @@ msgstr "無效引用:%s"
 #: builtin/checkout.c
 #, c-format
 msgid "reference is not a tree: %s"
-msgstr "引用不是一個樹:%s"
+msgstr "引用不是樹狀物件:%s"
 
 #: builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got tag '%s'"
-msgstr "期望一個分支,得到標籤 '%s'"
+msgstr "預期收到分支,卻收到標籤 “%s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got remote branch '%s'"
-msgstr "期望一個分支,得到遠端分支 '%s'"
+msgstr "預期收到分支,卻收到遠端分支 “%s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got '%s'"
-msgstr "期望一個分支,得到 '%s'"
+msgstr "預期收到分支,卻收到 “%s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got commit '%s'"
-msgstr "期望一個分支,得到提交 '%s'"
+msgstr "預期收到分支,卻收到提交 “%s”"
 
 #: builtin/checkout.c
 msgid ""
 "If you want to detach HEAD at the commit, try again with the --detach option."
-msgstr "è\8b¥æ\82¨æ\83³è¦\81å\88\86é\9b¢æ\8f\90交ç\9a\84 HEAD,請傳入 --detach 選項重試。"
+msgstr "è\8b¥æ\82¨æ\83³è¦\81å\9c¨æ­¤æ\8f\90交å\88\86é\9b¢ HEAD æ\8c\87é\87\9d,請傳入 --detach 選項重試。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch while merging\n"
 "Consider \"git merge --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在合併時切換分支\n"
-"è\80\83æ\85®ä½¿ç\94¨ \"git merge --quit\" æ\88\96 \"git worktree add\"。"
+"無法在合併時切換分支\n"
+"è«\8b試試 â\80\9cgit merge --quitâ\80\9d æ\88\96 â\80\9cgit worktree addâ\80\9d。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch in the middle of an am session\n"
 "Consider \"git am --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在 am 工作階段期間切換分支\n"
-"è\80\83æ\85®ä½¿ç\94¨ \"git am --quit\" æ\88\96 \"git worktree add\"。"
+"無法在 am 工作階段期間時切換分支\n"
+"è«\8b試試 â\80\9cgit am --quitâ\80\9d æ\88\96 â\80\9cgit worktree addâ\80\9d。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch while rebasing\n"
 "Consider \"git rebase --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在重定基底時切換分支\n"
-"è\80\83æ\85®ä½¿ç\94¨ \"git rebase --quit\" æ\88\96 \"git worktree add\"。"
+"無法在重定基底時切換分支\n"
+"è«\8b試試 â\80\9cgit rebase --quitâ\80\9d æ\88\96 â\80\9cgit worktree addâ\80\9d。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch while cherry-picking\n"
 "Consider \"git cherry-pick --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在揀選時切換分支\n"
-"è\80\83æ\85®ä½¿ç\94¨ \"git cherry-pick --quit\" æ\88\96 \"git worktree add\"。"
+"無法在揀選時切換分支\n"
+"è«\8b試試 â\80\9cgit cherry-pick --quitâ\80\9d æ\88\96 â\80\9cgit worktree addâ\80\9d。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch while reverting\n"
 "Consider \"git revert --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在還原時切換分支\n"
-"è\80\83æ\85®ä½¿ç\94¨ \"git revert --quit\" æ\88\96 \"git worktree add\"。"
+"無法在還原時切換分支\n"
+"è«\8b試試 â\80\9cgit revert --quitâ\80\9d æ\88\96 â\80\9cgit worktree addâ\80\9d。"
 
 #: builtin/checkout.c
 msgid "you are switching branch while bisecting"
-msgstr "您在行二分搜尋時切換分支"
+msgstr "您在行二分搜尋時切換分支"
 
 #: builtin/checkout.c
 msgid "paths cannot be used with switching branches"
-msgstr "路徑不能切換分支同時使用"
+msgstr "路徑不能切換分支同時使用"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with switching branches"
-msgstr "'%s' 不能和切換分支同時使用"
+msgstr "“%s” 不能與切換分支同時使用"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with '%s'"
-msgstr "'%s' 不能和 '%s' 同時使用"
+msgstr "“%s” 不能與 “%s” 同時使用"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot take <start-point>"
-msgstr "'%s' 不帶 <起始點>"
+msgstr "“%s” 不取 <start-point>"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
-msgstr "不能切換分支到一個非提交 '%s'"
+msgstr "無法將分支切換至非提交項目 “%s”"
 
 #: builtin/checkout.c
 msgid "missing branch or commit argument"
-msgstr "缺å°\91å\88\86æ\94¯æ\88\96æ\8f\90交å\8f\83數"
+msgstr "缺å°\91å\88\86æ\94¯æ\88\96æ\8f\90交å¼\95數"
 
 #: builtin/checkout.c
 msgid "perform a 3-way merge with the new branch"
-msgstr "和新的分支執行三方合併"
+msgstr "和新分支進行三方合併"
 
 #: builtin/checkout.c builtin/log.c parse-options.h
 msgid "style"
-msgstr "風格"
+msgstr "style"
 
 #: builtin/checkout.c
 msgid "conflict style (merge, diff3, or zdiff3)"
@@ -4573,7 +4620,7 @@ msgstr "衝突輸出風格(merge、diff3 或 zdiff3)"
 
 #: builtin/checkout.c builtin/worktree.c
 msgid "detach HEAD at named commit"
-msgstr "HEAD 從指定的提交分離"
+msgstr "自指定提交分離 HEAD 指針"
 
 #: builtin/checkout.c
 msgid "force checkout (throw away local modifications)"
@@ -4581,19 +4628,19 @@ msgstr "強制簽出(捨棄本機修改)"
 
 #: builtin/checkout.c
 msgid "new-branch"
-msgstr "新分支"
+msgstr "new-branch"
 
 #: builtin/checkout.c
 msgid "new unparented branch"
-msgstr "新的沒有父提交的分支"
+msgstr "新的沒有父提交的分支"
 
 #: builtin/checkout.c builtin/merge.c
 msgid "update ignored files (default)"
-msgstr "更新忽略的檔案(預設)"
+msgstr "更新忽略的檔案(預設)"
 
 #: builtin/checkout.c
 msgid "do not check if another worktree is holding the given ref"
-msgstr "不檢查指定的引用是否被其他工作區所占用"
+msgstr "不檢查其他工作區是否正在佔用指定的引用"
 
 #: builtin/checkout.c
 msgid "checkout our version for unmerged files"
@@ -4605,16 +4652,16 @@ msgstr "對尚未合併的檔案簽出他們的版本"
 
 #: builtin/checkout.c
 msgid "do not limit pathspecs to sparse entries only"
-msgstr "對路徑不做稀疏簽出的限制"
+msgstr "對路徑規格不做稀疏簽出的限制"
 
 #: builtin/checkout.c
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
-msgstr "「-%c」、「-%c」和「%s」選項不得同時使用"
+msgstr "“-%c”、“-%c” 和 “%s” 選項不得同時使用"
 
 #: builtin/checkout.c
 msgid "--track needs a branch name"
-msgstr "--track 需要一個分支名"
+msgstr "--track 需要分支名稱"
 
 #: builtin/checkout.c
 #, c-format
@@ -4633,60 +4680,61 @@ msgstr "無效的路徑規格"
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' is not a commit and a branch '%s' cannot be created from it"
-msgstr "'%s' 不是一個提交,不能基於它建立分支 '%s'"
+msgstr "“%s” 不是提交,因此不能以這為基礎建立 “%s” 分支"
 
 #: builtin/checkout.c
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
-msgstr "git checkout:--detach 不能接收路徑參數 '%s'"
+msgstr "git checkout:--detach 不取路徑引數 “%s”"
 
 #: builtin/checkout.c
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
 msgstr ""
-"git checkout:在從索引簽出時,--ours/--theirs、--force 和 --merge 不相容。"
+"git checkout:在從索引簽出時,--ours/--theirs、--force\n"
+"和 --merge 不相容。"
 
 #: builtin/checkout.c
 msgid "you must specify path(s) to restore"
-msgstr "您必須指定一個要復原的路徑"
+msgstr "您必須指定要還原的路徑"
 
 #: builtin/checkout.c builtin/clone.c builtin/remote.c
 #: builtin/submodule--helper.c builtin/worktree.c
 msgid "branch"
-msgstr "分支"
+msgstr "branch"
 
 #: builtin/checkout.c
 msgid "create and checkout a new branch"
-msgstr "建立並簽出一個新的分支"
+msgstr "建立並簽出分支"
 
 #: builtin/checkout.c
 msgid "create/reset and checkout a branch"
-msgstr "建立/重設並簽出一個分支"
+msgstr "建立/重設並簽出分支"
 
 #: builtin/checkout.c
 msgid "create reflog for new branch"
-msgstr "為新分支建立引用日誌"
+msgstr "為新分支建立引用日誌"
 
 #: builtin/checkout.c
 msgid "second guess 'git checkout <no-such-branch>' (default)"
-msgstr "二次猜測 'git checkout <無此分支>'(預設)"
+msgstr "二次猜測 “git checkout <無此分支>”(預設值)"
 
 #: builtin/checkout.c
 msgid "use overlay mode (default)"
-msgstr "使用疊加模式(預設)"
+msgstr "使用疊加模式(預設)"
 
 #: builtin/checkout.c
 msgid "create and switch to a new branch"
-msgstr "建立並切換一個新分支"
+msgstr "建立並切換新分支"
 
 #: builtin/checkout.c
 msgid "create/reset and switch to a branch"
-msgstr "建立/重設並切換一個分支"
+msgstr "建立/重設並切換至指定分支"
 
 #: builtin/checkout.c
 msgid "second guess 'git switch <no-such-branch>'"
-msgstr "二次猜測 'git switch <無此分支>'"
+msgstr "二次猜測 “git switch <無此分支>”"
 
 #: builtin/checkout.c
 msgid "throw away local modifications"
@@ -4694,19 +4742,19 @@ msgstr "捨棄本機修改"
 
 #: builtin/checkout.c
 msgid "which tree-ish to checkout from"
-msgstr "要簽出哪一個樹"
+msgstr "要自哪一個樹狀物件指示元簽出"
 
 #: builtin/checkout.c
 msgid "restore the index"
-msgstr "原索引"
+msgstr "原索引"
 
 #: builtin/checkout.c
 msgid "restore the working tree (default)"
-msgstr "復原工作區(預設)"
+msgstr "還原工作區(預設值)"
 
 #: builtin/checkout.c
 msgid "ignore unmerged entries"
-msgstr "忽略未合併目"
+msgstr "忽略未合併目"
 
 #: builtin/checkout.c
 msgid "use overlay mode"
@@ -4714,19 +4762,21 @@ msgstr "使用疊加模式"
 
 #: builtin/clean.c
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <模式>] [-x | -X] [--] <路徑>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 
 #: builtin/clean.c
 #, c-format
 msgid "Removing %s\n"
-msgstr "正刪除 %s\n"
+msgstr "æ­£å\9c¨å\88ªé\99¤ %s\n"
 
 #: builtin/clean.c
 #, c-format
 msgid "Would remove %s\n"
-msgstr "將刪除 %s\n"
+msgstr "將刪除 %s\n"
 
 #: builtin/clean.c
 #, c-format
@@ -4741,12 +4791,12 @@ msgstr "將忽略版本庫 %s\n"
 #: builtin/clean.c midx.c
 #, c-format
 msgid "failed to remove %s"
-msgstr "刪除 %s 失敗"
+msgstr "無法移除 %s"
 
 #: builtin/clean.c
 #, c-format
 msgid "could not lstat %s\n"
-msgstr "不能對 %s 呼叫 lstat\n"
+msgstr "不能對 %s 進行 lstat\n"
 
 #: builtin/clean.c
 msgid "Refusing to remove current working directory\n"
@@ -4756,7 +4806,7 @@ msgstr "拒絕移除目前工作目錄\n"
 msgid "Would refuse to remove current working directory\n"
 msgstr "會拒絕移除目前的工作目錄\n"
 
-#: builtin/clean.c git-add--interactive.perl
+#: builtin/clean.c
 #, c-format
 msgid ""
 "Prompt help:\n"
@@ -4764,12 +4814,12 @@ msgid ""
 "foo        - select item based on unique prefix\n"
 "           - (empty) select nothing\n"
 msgstr ""
-"協助:\n"
-"1          - 透過編號選擇一個選項\n"
-"foo        - 透過唯一前綴選擇一個選項\n"
+"提示說明:\n"
+"1          - 透過編號選擇項目\n"
+"foo        - 根據獨特前綴選擇項目\n"
 "           - (空)什麼也不選擇\n"
 
-#: builtin/clean.c git-add--interactive.perl
+#: builtin/clean.c
 #, c-format
 msgid ""
 "Prompt help:\n"
@@ -4781,39 +4831,39 @@ msgid ""
 "*          - choose all items\n"
 "           - (empty) finish selecting\n"
 msgstr ""
-"協助:\n"
-"1          - é\81¸æ\93\87ä¸\80å\80\8bé\81¸é \85\n"
-"3-5        - 選擇一個範圍內的所有選項\n"
-"2-3,6-9    - 選擇多個範圍內的所有選項\n"
-"foo        - 透過唯一前綴選擇一個選項\n"
-"-...       - å\8f\8dé\81¸ç\89¹å®\9aç\9a\84é\81¸é \85\n"
-"*          - é\81¸æ\93\87æ\89\80æ\9c\89é\81¸é \85\n"
-"           - (空)結束選擇\n"
-
-#: builtin/clean.c git-add--interactive.perl
-#, c-format, perl-format
+"提示說明:\n"
+"1          - é\81¸æ\93\87ä¸\80å\80\8bé \85ç\9b®\n"
+"3-5        - 選擇一個項目範圍\n"
+"2-3,6-9    - 選擇多個項目範圍\n"
+"foo        - 根據獨特前綴選擇項目\n"
+"-...       - å\8f\96æ¶\88é\81¸å\8f\96æ\8c\87å®\9aé \85ç\9b®\n"
+"*          - é\81¸æ\93\87æ\89\80æ\9c\89é \85ç\9b®\n"
+"           - (空)完成選擇\n"
+
+#: builtin/clean.c
+#, c-format
 msgid "Huh (%s)?\n"
-msgstr "嗯(%s)\n"
+msgstr "嗯(%s)?\n"
 
 #: builtin/clean.c
 #, c-format
 msgid "Input ignore patterns>> "
-msgstr "輸入範本以排除條目>> "
+msgstr "輸入要忽略的模式項目>> "
 
 #: builtin/clean.c
 #, c-format
 msgid "WARNING: Cannot find items matched by: %s"
-msgstr "警告:無法找到和 %s 符合的目"
+msgstr "警告:無法找到和 %s 符合的目"
 
 #: builtin/clean.c
 msgid "Select items to delete"
-msgstr "選擇要刪除的目"
+msgstr "選擇要刪除的目"
 
 #. TRANSLATORS: Make sure to keep [y/N] as is
 #: builtin/clean.c
 #, c-format
 msgid "Remove %s [y/N]? "
-msgstr "移除 %s [y/N] "
+msgstr "移除 %s [y/N]? "
 
 #: builtin/clean.c
 msgid ""
@@ -4828,15 +4878,15 @@ msgstr ""
 "clean               - 開始清理\n"
 "filter by pattern   - 透過範本排除要刪除的條目\n"
 "select by numbers   - 透過數字選擇要刪除的條目\n"
-"ask each            - 針對刪除逐一詢問(就像 \"rm -i\")\n"
+"ask each            - 針對刪除逐一詢問(就像 “rm -i”)\n"
 "quit                - 停止刪除並離開\n"
-"help                - 顯示本協助\n"
-"?                   - 顯示如何在提示下選擇的協助"
+"help                - 顯示本輔助說明\n"
+"?                   - 顯示如何在提示下選擇的協助"
 
 #: builtin/clean.c
 msgid "Would remove the following item:"
 msgid_plural "Would remove the following items:"
-msgstr[0] "將刪除如下條目:"
+msgstr[0] "將移除以下項目:"
 
 #: builtin/clean.c
 msgid "No more files to clean, exiting."
@@ -4844,11 +4894,11 @@ msgstr "沒有要清理的檔案,離開。"
 
 #: builtin/clean.c
 msgid "do not print names of files removed"
-msgstr "不列印刪除檔案的名稱"
+msgstr "不輸出移除的檔案的名稱"
 
 #: builtin/clean.c
 msgid "force"
-msgstr "強制"
+msgstr "force"
 
 #: builtin/clean.c
 msgid "interactive cleaning"
@@ -4856,24 +4906,25 @@ msgstr "互動式清除"
 
 #: builtin/clean.c
 msgid "remove whole directories"
-msgstr "除整個目錄"
+msgstr "除整個目錄"
 
 #: builtin/clean.c builtin/describe.c builtin/grep.c builtin/log.c
-#: builtin/ls-files.c builtin/name-rev.c builtin/show-ref.c
+#: builtin/ls-files.c builtin/name-rev.c builtin/pack-refs.c builtin/show-ref.c
+#: ref-filter.h
 msgid "pattern"
-msgstr "模式"
+msgstr "pattern"
 
 #: builtin/clean.c
 msgid "add <pattern> to ignore rules"
-msgstr "新增 <模式> 到忽略規則"
+msgstr "將 <pattern> 加到忽略規則"
 
 #: builtin/clean.c
 msgid "remove ignored files, too"
-msgstr "ä¹\9få\88ª除忽略的檔案"
+msgstr "亦移除忽略的檔案"
 
 #: builtin/clean.c
 msgid "remove only ignored files"
-msgstr "只除忽略的檔案"
+msgstr "只除忽略的檔案"
 
 #: builtin/clean.c
 msgid ""
@@ -4895,23 +4946,23 @@ msgstr "-x 和 -X 不能同時使用"
 
 #: builtin/clone.c
 msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<選項>] [--] <版本庫> [<路徑>]"
+msgstr "git clone [<options>] [--] <repo> [<dir>]"
 
 #: builtin/clone.c
 msgid "don't clone shallow repository"
-msgstr "不要複製淺版本庫"
+msgstr "不要複製淺版本庫"
 
 #: builtin/clone.c
 msgid "don't create a checkout"
-msgstr "不建ç«\8bä¸\80å\80\8bç°½å\87º"
+msgstr "不è¦\81建ç«\8bç°½å\87º"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "create a bare repository"
-msgstr "建立一個純版本庫"
+msgstr "建立版本庫"
 
 #: builtin/clone.c
 msgid "create a mirror repository (implies bare)"
-msgstr "建立一個鏡像版本庫(也是純版本庫)"
+msgstr "建立鏡像版本庫(亦即裸版本庫)"
 
 #: builtin/clone.c
 msgid "to clone from a local repository"
@@ -4919,7 +4970,7 @@ msgstr "從本機版本庫複製"
 
 #: builtin/clone.c
 msgid "don't use local hardlinks, always copy"
-msgstr "不使用本機硬連結,始終複製"
+msgstr "不使用本機硬連結,始終拷貝"
 
 #: builtin/clone.c
 msgid "setup as shared repository"
@@ -4935,15 +4986,15 @@ msgstr "在複製時初始化子模組"
 
 #: builtin/clone.c
 msgid "number of submodules cloned in parallel"
-msgstr "並行複製的子模組數"
+msgstr "平行複製的子模組數量"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "template-directory"
-msgstr "範本目錄"
+msgstr "模板目錄"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "directory from which templates will be used"
-msgstr "範本目錄將被使用"
+msgstr "將被使用的模板目錄"
 
 #: builtin/clone.c builtin/submodule--helper.c
 msgid "reference repository"
@@ -4951,49 +5002,45 @@ msgstr "引用版本庫"
 
 #: builtin/clone.c builtin/submodule--helper.c
 msgid "use --reference only while cloning"
-msgstr "僅在複製時參考 --reference 指向的本機版本庫"
+msgstr "僅在複製時使用 --reference"
 
 #: builtin/clone.c builtin/column.c builtin/fmt-merge-msg.c builtin/init-db.c
 #: builtin/merge-file.c builtin/merge.c builtin/pack-objects.c builtin/repack.c
 #: builtin/submodule--helper.c t/helper/test-simple-ipc.c
 msgid "name"
-msgstr "名稱"
+msgstr "name"
 
 #: builtin/clone.c
 msgid "use <name> instead of 'origin' to track upstream"
-msgstr "使用 <名稱> 而不是 'origin' 去追蹤上游"
+msgstr "使用 <name> 而不是 “origin” 追蹤上游"
 
 #: builtin/clone.c
 msgid "checkout <branch> instead of the remote's HEAD"
-msgstr "簽出 <分支> 而不是遠端 HEAD"
+msgstr "簽出 <branch> 而不是遠端 HEAD"
 
 #: builtin/clone.c
 msgid "path to git-upload-pack on the remote"
 msgstr "遠端 git-upload-pack 路徑"
 
-#: builtin/clone.c builtin/fetch.c builtin/grep.c builtin/pull.c
+#: builtin/clone.c builtin/fetch.c builtin/pull.c
 msgid "depth"
-msgstr "深度"
+msgstr "depth"
 
 #: builtin/clone.c
 msgid "create a shallow clone of that depth"
-msgstr "建立一個指定深度的淺複製"
-
-#: builtin/clone.c builtin/fetch.c builtin/pack-objects.c builtin/pull.c
-msgid "time"
-msgstr "時間"
+msgstr "建立指定深度的淺層複製"
 
 #: builtin/clone.c
 msgid "create a shallow clone since a specific time"
-msgstr "建立從指定時間到現在的淺複製"
+msgstr "建立從指定時間到現在的淺複製"
 
 #: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
 msgid "revision"
-msgstr "修訂版"
+msgstr "revision"
 
 #: builtin/clone.c builtin/fetch.c builtin/pull.c
 msgid "deepen history of shallow clone, excluding rev"
-msgstr "取得更多淺複製的過去歷史記錄,除了特定版本"
+msgstr "取得更多淺層複製的過去歷史記錄,除了特定修訂版"
 
 #: builtin/clone.c builtin/submodule--helper.c
 msgid "clone only one branch, HEAD or --branch"
@@ -5009,11 +5056,11 @@ msgstr "子模組將以淺下載模式複製"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "gitdir"
-msgstr "git目錄"
+msgstr "gitdir"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "separate git dir from working tree"
-msgstr "git目錄和工作區分離"
+msgstr "git 目錄和工作區分離"
 
 #: builtin/clone.c
 msgid "key=value"
@@ -5033,14 +5080,6 @@ msgstr "server-specific"
 msgid "option to transmit"
 msgstr "傳輸選項"
 
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv4 addresses only"
-msgstr "只使用 IPv4 位址"
-
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv6 addresses only"
-msgstr "只使用 IPv6 位址"
-
 #: builtin/clone.c
 msgid "apply partial clone filters to submodules"
 msgstr "將部分複製過濾器套用至子模組"
@@ -5059,7 +5098,7 @@ msgstr "uri"
 
 #: builtin/clone.c
 msgid "a URI for downloading bundles before fetching from origin remote"
-msgstr "在從 origin 遠端抓取前,用來下載套件的 URI"
+msgstr "在從 origin 遠端抓取前,用來下載套件的 URI"
 
 #: builtin/clone.c
 #, c-format
@@ -5076,15 +5115,25 @@ msgstr "對 '%s' 呼叫 stat 失敗"
 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' 失敗"
+msgstr "無法刪除 “%s”"
 
 #: builtin/clone.c
 #, c-format
@@ -5136,7 +5185,7 @@ msgstr "無法初始化稀疏簽出"
 
 #: builtin/clone.c
 msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr "遠端 HEAD 指向不存在的引用,無法簽出"
+msgstr "é\81 ç«¯ HEAD æ\8c\87å\90\91ä¸\80å\80\8bä¸\8då­\98å\9c¨ç\9a\84å¼\95ç\94¨ï¼\8cç\84¡æ³\95ç°½å\87º"
 
 #: builtin/clone.c
 msgid "unable to checkout working tree"
@@ -5152,7 +5201,7 @@ msgstr "無法執行 repack 來清理"
 
 #: builtin/clone.c
 msgid "cannot unlink temporary alternates file"
-msgstr "無法刪除暫時的 alternates 檔案"
+msgstr "無法刪除暫 alternates 檔案"
 
 #: builtin/clone.c
 msgid "Too many arguments."
@@ -5162,11 +5211,6 @@ msgstr "太多參數。"
 msgid "You must specify a repository to clone."
 msgstr "您必須指定要複製的版本庫。"
 
-#: builtin/clone.c
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
-msgstr "「%s」和「%s %s」選項不得同時使用"
-
 #: builtin/clone.c
 msgid ""
 "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
@@ -5259,16 +5303,20 @@ msgstr "--local 被忽略"
 
 #: builtin/clone.c
 msgid "cannot clone from filtered bundle"
-msgstr "無法從過濾後的套件複製"
+msgstr "無法從過濾後的套件複製"
 
 #: builtin/clone.c
 msgid "failed to initialize the repo, skipping bundle URI"
-msgstr "無法初始化版本庫,略過套件 URI"
+msgstr "無法初始化版本庫,略過套件 URI"
 
 #: builtin/clone.c
 #, c-format
 msgid "failed to fetch objects from bundle URI '%s'"
-msgstr "無法從套件 URL “%s” 抓取物件"
+msgstr "無法從套件包 URL “%s” 抓取物件"
+
+#: builtin/clone.c
+msgid "failed to fetch advertised bundles"
+msgstr "無法抓取公佈的套件包"
 
 #: builtin/clone.c
 msgid "remote transport reported error"
@@ -5317,22 +5365,27 @@ msgstr "--command 必須是第一個參數"
 
 #: builtin/commit-graph.c
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
-"git commit-graph verify [--object-dir <物件目錄>] [--shallow] [--"
-"[no-]progress]"
+"git commit-graph verify [--object-dir <目錄>] [--shallow] [--[no-]progress]"
 
 #: builtin/commit-graph.c
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
-msgstr ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
-
-#: builtin/commit-graph.c builtin/fetch.c builtin/log.c
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
+msgstr ""
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
+
+#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
 msgid "dir"
 msgstr "目錄"
 
@@ -5349,6 +5402,11 @@ msgstr "如果提交圖形被分割,只驗證頭一個檔案"
 msgid "Could not open commit-graph '%s'"
 msgstr "無法開啟提交圖形 '%s'"
 
+#: builtin/commit-graph.c
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "無法開啟提交圖鏈 “%s”"
+
 #: builtin/commit-graph.c
 #, c-format
 msgid "unrecognized --split argument, %s"
@@ -5417,13 +5475,17 @@ msgstr "不能同時使用 --reachable、--stdin-commits 或 --stdin-packs"
 msgid "Collecting commits from input"
 msgstr "正在從輸入收集提交"
 
+#: builtin/commit-tree.c
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <tree> [(-p <parent>)...]"
+
 #: builtin/commit-tree.c
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
-"git commit-tree [(-p <父提交>)...] [-S[<keyid>]] [(-m <消息>)...] [(-F <檔案"
-">)...] <樹>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 
 #: builtin/commit-tree.c
 #, c-format
@@ -5480,12 +5542,30 @@ msgid "git commit-tree: failed to read"
 msgstr "git commit-tree:讀取失敗"
 
 #: builtin/commit.c
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<選項>] [--] <路徑規格>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"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>...]"
 
 #: builtin/commit.c
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<選項>] [--] <路徑規格>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<選項>] [--] [<路徑規格>...]"
 
 #: builtin/commit.c
 msgid ""
@@ -5539,6 +5619,10 @@ msgstr ""
 "    git cherry-pick --skip\n"
 "\n"
 
+#: builtin/commit.c read-cache.c
+msgid "updating files failed"
+msgstr "更新檔案失敗"
+
 #: builtin/commit.c
 msgid "failed to unpack HEAD tree object"
 msgstr "解包 HEAD 樹狀物件失敗"
@@ -5563,10 +5647,6 @@ msgstr "無法更新暫時索引"
 msgid "Failed to update main cache tree"
 msgstr "不能更新樹的主快取"
 
-#: builtin/commit.c
-msgid "unable to write new_index file"
-msgstr "無法寫 new_index 檔案"
-
 #: builtin/commit.c
 msgid "cannot do a partial commit during a merge."
 msgstr "在合併過程中不能做部分提交。"
@@ -5612,10 +5692,10 @@ msgid ""
 "in the current commit message"
 msgstr "無法選擇一個未被目前提交說明使用的備註字元"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/merge-tree.c
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "不能查詢提交 %s"
+msgid "could not lookup commit '%s'"
+msgstr "無法查詢提交 “%s”"
 
 #: builtin/commit.c builtin/shortlog.c
 #, c-format
@@ -5827,7 +5907,7 @@ msgstr "顯示分支訊息"
 
 #: builtin/commit.c
 msgid "show stash information"
-msgstr "顯示儲藏區訊息"
+msgstr "顯示貯存區訊息"
 
 #: builtin/commit.c
 msgid "compute full ahead/behind values"
@@ -5837,13 +5917,13 @@ msgstr "計算完整的領先/落後值"
 msgid "version"
 msgstr "版本"
 
-#: builtin/commit.c builtin/push.c builtin/worktree.c
+#: builtin/commit.c builtin/fetch.c builtin/push.c builtin/worktree.c
 msgid "machine-readable output"
 msgstr "機器可讀的輸出"
 
 #: builtin/commit.c
 msgid "show status in long format (default)"
-msgstr "以長格式顯示狀態(預設)"
+msgstr "以長格式顯示狀態(預設)"
 
 #: builtin/commit.c
 msgid "terminate entries with NUL"
@@ -5920,7 +6000,7 @@ msgstr "日期"
 msgid "override date for commit"
 msgstr "提交時覆蓋日期"
 
-#: builtin/commit.c parse-options.h ref-filter.h
+#: builtin/commit.c builtin/merge-tree.c parse-options.h ref-filter.h
 msgid "commit"
 msgstr "提交"
 
@@ -6061,17 +6141,17 @@ msgstr "提交說明內文空白,中止提交作業。\n"
 #: builtin/commit.c
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"版本庫已更新,但無法寫 new_index 檔案。檢查是否磁碟已滿或\n"
-"磁碟配額已耗盡,然後執行 \"git restore --staged :/\" 復原。"
+"版本庫已更新,但無法寫入新的索引檔案。請檢查磁碟是否\n"
+"已滿或磁碟配額已耗盡,然後執行 “git restore --staged :/” 復原。"
 
 #: builtin/config.c
 msgid "git config [<options>]"
 msgstr "git config [<選項>]"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 #, c-format
 msgid "unrecognized --type argument, %s"
 msgstr "無法識別的 --type 參數,%s"
@@ -6100,7 +6180,7 @@ msgstr "使用版本庫級設定檔案"
 msgid "use per-worktree config file"
 msgstr "使用工作區級別的設定檔案"
 
-#: builtin/config.c
+#: builtin/config.c builtin/gc.c
 msgid "use given config file"
 msgstr "使用指定的設定檔案"
 
@@ -6170,7 +6250,7 @@ msgstr "開啟一個編輯器"
 
 #: builtin/config.c
 msgid "find the color configured: slot [default]"
-msgstr "獲得設定的顏色:設定 [預設]"
+msgstr "獲得設定的顏色:設定 [預設]"
 
 #: builtin/config.c
 msgid "find the color setting: slot [stdout-is-tty]"
@@ -6180,11 +6260,11 @@ msgstr "獲得顏色設定:設定 [stdout-is-tty]"
 msgid "Type"
 msgstr "類型"
 
-#: builtin/config.c builtin/env--helper.c builtin/hash-object.c
+#: builtin/config.c builtin/hash-object.c
 msgid "type"
 msgstr "類型"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 msgid "value is given this type"
 msgstr "取值為該類型"
 
@@ -6226,11 +6306,11 @@ msgstr "只顯示變數名"
 
 #: builtin/config.c
 msgid "respect include directives on lookup"
-msgstr "æ\9f¥è©¢æ\99\82å\8f\83ç\85§ include 指令遞迴尋找"
+msgstr "æ\9f¥è©¢æ\99\82å¼\95ç\94¨ include 指令遞迴尋找"
 
 #: builtin/config.c
 msgid "show origin of config (file, standard input, blob, command line)"
-msgstr "顯示設定的來源(檔案、標準輸入、資料物件,或令列)"
+msgstr "顯示設定的來源(檔案、標準輸入、資料物件,或令列)"
 
 #: builtin/config.c
 msgid "show scope of config (worktree, local, global, system, command)"
@@ -6238,7 +6318,7 @@ msgstr ""
 "顯示設定檔的作用域 (工作區 worktree、本機 local、全域 global、系統 system、指"
 "令 command)"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 msgid "value"
 msgstr "取值"
 
@@ -6323,7 +6403,7 @@ msgstr "--blob 只能在一個版本庫內使用"
 msgid "--worktree can only be used inside a git repository"
 msgstr "--worktree 只能在 git 版本庫中使用"
 
-#: builtin/config.c
+#: builtin/config.c builtin/gc.c
 msgid "$HOME not set"
 msgstr "$HOME 未設定"
 
@@ -6433,12 +6513,20 @@ msgid "unable to get credential storage lock in %d ms"
 msgstr "無法在 %d 毫秒內取得憑證儲存空間的鎖"
 
 #: builtin/describe.c
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<選項>] [<提交號>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+
+#: builtin/describe.c
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
 
 #: builtin/describe.c
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<選項>] --dirty"
+msgid "git describe <blob>"
+msgstr "git describe <blob>"
 
 #: builtin/describe.c
 msgid "head"
@@ -6591,15 +6679,15 @@ msgstr "「%s」選項和提交號不得同時使用"
 
 #: builtin/diagnose.c
 msgid ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 msgstr ""
-"git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--"
-"mode=<mode>]"
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
 
 #: builtin/diagnose.c
 msgid "specify a destination for the diagnostics archive"
-msgstr "指定診斷封存檔的目的地"
+msgstr "指定診斷封存檔的目的地"
 
 #: builtin/diagnose.c
 msgid "specify a strftime format suffix for the filename"
@@ -6618,6 +6706,10 @@ msgstr "--merge-base 只對 2 個以上的提交有用"
 msgid "'%s': not a regular file or symlink"
 msgstr "'%s':不是一個正規檔案或符號連結"
 
+#: builtin/diff.c
+msgid "no merge given, only parents."
+msgstr "沒有提供合併提交,只有提供父提交。"
+
 #: builtin/diff.c
 #, c-format
 msgid "invalid option: %s"
@@ -6761,30 +6853,6 @@ msgstr "沒有為 --tool=<工具> 參數提供 <工具>"
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "沒有為 --extcmd=<命令> 參數提供 <命令>"
 
-#: builtin/env--helper.c
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <選項> <環境變數>"
-
-#: builtin/env--helper.c
-msgid "default for git_env_*(...) to fall back on"
-msgstr "git_env_*(...) 的預設值"
-
-#: builtin/env--helper.c
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr "安靜模式,只使用 git_env_*() 的值作為離開碼"
-
-#: builtin/env--helper.c
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr "選項「--default」預期收到「--type=bool」的布林值,而非「%s」"
-
-#: builtin/env--helper.c
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not `"
-"%s`"
-msgstr "選項「--default」預期收到「--type=ulong」的無號 long 數值,而非「%s」"
-
 #: builtin/fast-export.c
 msgid "git fast-export [<rev-list-opts>]"
 msgstr "git fast-export [<rev-list-opts>]"
@@ -6927,145 +6995,15 @@ msgstr "git fetch --all [<選項>]"
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel 不能為負數"
 
-#: builtin/fetch.c builtin/pull.c
-msgid "fetch from all remotes"
-msgstr "從所有的遠端抓取"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "set upstream for git pull/fetch"
-msgstr "為 git pull/fetch 設定上游"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "追加到 .git/FETCH_HEAD 而不是覆蓋它"
-
-#: builtin/fetch.c
-msgid "use atomic transaction to update references"
-msgstr "使用 atomic 事務更新引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "path to upload pack on remote end"
-msgstr "上傳包到遠端的路徑"
-
-#: builtin/fetch.c
-msgid "force overwrite of local reference"
-msgstr "強制覆蓋本機引用"
-
-#: builtin/fetch.c
-msgid "fetch from multiple remotes"
-msgstr "從多個遠端抓取"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "fetch all tags and associated objects"
-msgstr "抓取所有的標籤和關聯物件"
-
-#: builtin/fetch.c
-msgid "do not fetch all tags (--no-tags)"
-msgstr "不抓取任何標籤(--no-tags)"
-
-#: builtin/fetch.c
-msgid "number of submodules fetched in parallel"
-msgstr "並行取得的子模組數量"
-
-#: builtin/fetch.c
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr "修改引用規格 (refspec) 以便將所有引用 (refs) 放置在 refs/prefetch/ 中"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "prune remote-tracking branches no longer on remote"
-msgstr "清除遠端已經不存在的分支的追蹤分支"
-
-#: builtin/fetch.c
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr "清除遠端不存在的本機標籤,並且取代變更標籤"
-
-#  譯者:可選值,不能翻譯
-#: builtin/fetch.c builtin/pull.c
-msgid "on-demand"
-msgstr "on-demand"
-
-#: builtin/fetch.c
-msgid "control recursive fetching of submodules"
-msgstr "控制子模組的遞迴抓取"
-
-#: builtin/fetch.c
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "將取得的引用寫入 FETCH_HEAD 檔案"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "keep downloaded pack"
-msgstr "保持下載包"
-
-#: builtin/fetch.c
-msgid "allow updating of HEAD ref"
-msgstr "允許更新 HEAD 引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "deepen history of shallow clone"
-msgstr "取得淺複製的更多過去歷史記錄"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "deepen history of shallow repository based on time"
-msgstr "基於時間來深化淺複製的歷史"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "convert to a complete repository"
-msgstr "轉換為一個完整的版本庫"
-
-#: builtin/fetch.c
-msgid "re-fetch without negotiating common commits"
-msgstr "重新抓取而不協商共同提交"
-
-#: builtin/fetch.c
-msgid "prepend this to submodule path output"
-msgstr "在子模組路徑輸出的前面加上此目錄"
-
-#: builtin/fetch.c
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr "遞迴取得子模組的預設值(比設定檔案優先度低)"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "accept refs that update .git/shallow"
-msgstr "接受更新 .git/shallow 的引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "refmap"
-msgstr "引用映射"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "specify fetch refmap"
-msgstr "指定取得動作的引用映射"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "report that we have only objects reachable from this object"
-msgstr "報告我們只擁有從該物件開始可以取得的物件"
-
-#: builtin/fetch.c
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr "不取得 packfile,而是輸出協商的祖先提交"
-
-#: builtin/fetch.c
-msgid "run 'maintenance --auto' after fetching"
-msgstr "取得 (fetch) 後執行 'maintenance --auto'"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "check for forced-updates on all updated branches"
-msgstr "在所有更新分支上檢查強制更新"
-
-#: builtin/fetch.c
-msgid "write the commit-graph after fetching"
-msgstr "抓取後寫入分支圖"
-
-#: builtin/fetch.c
-msgid "accept refspecs from stdin"
-msgstr "從標準輸入中接受引用規格"
-
 #: builtin/fetch.c
 msgid "couldn't find remote ref HEAD"
 msgstr "找不到遠端 HEAD 引用"
 
+#: builtin/fetch.c
+#, c-format
+msgid "From %.*s\n"
+msgstr "來自 %.*s\n"
+
 #: builtin/fetch.c
 #, c-format
 msgid "object %s not found"
@@ -7151,11 +7089,6 @@ msgstr "%s 未傳送所有必需的物件\n"
 msgid "rejected %s because shallow roots are not allowed to be updated"
 msgstr "已拒絕 %s,不允許更新淺複製"
 
-#: builtin/fetch.c
-#, c-format
-msgid "From %.*s\n"
-msgstr "來自 %.*s\n"
-
 #: builtin/fetch.c
 #, c-format
 msgid ""
@@ -7269,18 +7202,158 @@ msgstr ""
 msgid "you need to specify a tag name"
 msgstr "您需要指定標籤名稱"
 
-#: builtin/fetch.c
-msgid "--negotiate-only needs one or more --negotiation-tip=*"
-msgstr "--negotiate-only 需要一或多個 --negotiation-tip=*"
-
-#: builtin/fetch.c
-msgid "negative depth in --deepen is not supported"
-msgstr "--deepen 不支援負數深度"
+#: builtin/fetch.c builtin/pull.c
+msgid "fetch from all remotes"
+msgstr "從所有的遠端抓取"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "set upstream for git pull/fetch"
+msgstr "為 git pull/fetch 設定上游"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "追加到 .git/FETCH_HEAD 而不是覆蓋它"
+
+#: builtin/fetch.c
+msgid "use atomic transaction to update references"
+msgstr "使用 atomic 事務更新引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "path to upload pack on remote end"
+msgstr "上傳包到遠端的路徑"
+
+#: builtin/fetch.c
+msgid "force overwrite of local reference"
+msgstr "強制覆蓋本機引用"
+
+#: builtin/fetch.c
+msgid "fetch from multiple remotes"
+msgstr "從多個遠端抓取"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "fetch all tags and associated objects"
+msgstr "抓取所有的標籤和關聯物件"
+
+#: builtin/fetch.c
+msgid "do not fetch all tags (--no-tags)"
+msgstr "不抓取任何標籤(--no-tags)"
+
+#: builtin/fetch.c
+msgid "number of submodules fetched in parallel"
+msgstr "並行取得的子模組數量"
+
+#: builtin/fetch.c
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr "修改引用規格 (refspec) 以便將所有引用 (refs) 放置在 refs/prefetch/ 中"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "prune remote-tracking branches no longer on remote"
+msgstr "清除遠端已經不存在的分支的追蹤分支"
+
+#: builtin/fetch.c
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr "清除遠端不存在的本機標籤,並且取代變更標籤"
+
+#  譯者:可選值,不能翻譯
+#: builtin/fetch.c builtin/pull.c
+msgid "on-demand"
+msgstr "on-demand"
+
+#: builtin/fetch.c
+msgid "control recursive fetching of submodules"
+msgstr "控制子模組的遞迴抓取"
+
+#: builtin/fetch.c
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "將取得的引用寫入 FETCH_HEAD 檔案"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "keep downloaded pack"
+msgstr "保持下載包"
+
+#: builtin/fetch.c
+msgid "allow updating of HEAD ref"
+msgstr "允許更新 HEAD 引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "deepen history of shallow clone"
+msgstr "取得淺複製的更多過去歷史記錄"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "deepen history of shallow repository based on time"
+msgstr "基於時間來深化淺複製的歷史"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "convert to a complete repository"
+msgstr "轉換為一個完整的版本庫"
+
+#: builtin/fetch.c
+msgid "re-fetch without negotiating common commits"
+msgstr "重新抓取而不協商共同提交"
+
+#: builtin/fetch.c
+msgid "prepend this to submodule path output"
+msgstr "在子模組路徑輸出的前面加上此目錄"
+
+#: builtin/fetch.c
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr "遞迴取得子模組的預設值(比設定檔案優先度低)"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "accept refs that update .git/shallow"
+msgstr "接受更新 .git/shallow 的引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "refmap"
+msgstr "引用映射"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "specify fetch refmap"
+msgstr "指定取得動作的引用映射"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "report that we have only objects reachable from this object"
+msgstr "報告我們只擁有從該物件開始可以取得的物件"
+
+#: builtin/fetch.c
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr "不取得 packfile,而是輸出協商的祖先提交"
+
+#: builtin/fetch.c
+msgid "run 'maintenance --auto' after fetching"
+msgstr "取得 (fetch) 後執行 'maintenance --auto'"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "check for forced-updates on all updated branches"
+msgstr "在所有更新分支上檢查強制更新"
+
+#: builtin/fetch.c
+msgid "write the commit-graph after fetching"
+msgstr "抓取後寫入分支圖"
+
+#: builtin/fetch.c
+msgid "accept refspecs from stdin"
+msgstr "從標準輸入中接受引用規格"
+
+#: builtin/fetch.c
+msgid "--negotiate-only needs one or more --negotiation-tip=*"
+msgstr "--negotiate-only 需要一或多個 --negotiation-tip=*"
+
+#: builtin/fetch.c
+msgid "negative depth in --deepen is not supported"
+msgstr "--deepen 不支援負數深度"
 
 #: builtin/fetch.c
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "對於一個完整的版本庫,參數 --unshallow 沒有意義"
 
+#: builtin/fetch.c
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "無法自「%s」抓取套件包"
+
 #: builtin/fetch.c
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all 不能帶一個版本庫參數"
@@ -7409,9 +7482,17 @@ msgstr "只列印包含該提交的引用"
 msgid "print only refs which don't contain the commit"
 msgstr "只列印不包含該提交的引用"
 
+#: builtin/for-each-ref.c
+msgid "read reference patterns from stdin"
+msgstr "從 stdin 讀取引用模式"
+
+#: builtin/for-each-ref.c
+msgid "unknown arguments supplied with --stdin"
+msgstr "為 --stdin 提供的引數未知"
+
 #: builtin/for-each-repo.c
-msgid "git for-each-repo --config=<config> <command-args>"
-msgstr "git for-each-repo --config=<設定> <命令引數>"
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<config> [--] <arguments>"
 
 #: builtin/for-each-repo.c
 msgid "config"
@@ -7425,6 +7506,11 @@ msgstr "儲存版本庫路徑清單的設定鍵"
 msgid "missing --config=<config>"
 msgstr "缺少 --config=<設定>"
 
+#: builtin/for-each-repo.c
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "收到無效的組態 --config=%s"
+
 #: builtin/fsck.c
 msgid "unknown"
 msgstr "未知"
@@ -7610,13 +7696,14 @@ msgid "notice: %s points to an unborn branch (%s)"
 msgstr "注意:%s 指向一個尚未誕生的分支(%s)"
 
 #: builtin/fsck.c
-msgid "Checking cache tree"
-msgstr "正在檢查快取樹"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "正在檢查 %s 的快取樹"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s:cache-tree 中無效的 sha1 指標"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s:%s 的 cache-tree 中的 sha1 指標無效"
 
 #: builtin/fsck.c
 msgid "non-tree in cache-tree"
@@ -7624,12 +7711,30 @@ msgstr "cache-tree 中非樹狀物件"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s:resolve-undo 的 sha1 指針無效"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s:%s 的 resolve-undo 中的 sha1 指標無效"
+
+#: builtin/fsck.c
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "無法讀取 “%s” 封裝的修訂版索引 (rev-index)"
 
 #: builtin/fsck.c
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<選項>] [<物件>...]"
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "“%s” 封裝的修訂版索引 (rev-index) 無效"
+
+#: builtin/fsck.c
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
 
 #: builtin/fsck.c
 msgid "show unreachable objects"
@@ -7653,7 +7758,7 @@ msgstr "將索引亦作為檢查的頭節點"
 
 #: builtin/fsck.c
 msgid "make reflogs head nodes (default)"
-msgstr "將引用日誌作為檢查的 HEAD 節點(預設)"
+msgstr "將引用日誌作為檢查的 HEAD 節點(預設)"
 
 #: builtin/fsck.c
 msgid "also consider packs and alternate objects"
@@ -7701,14 +7806,6 @@ msgstr "git fsmonitor--daemon start [<options>]"
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr "git fsmonitor--daemon run [<options>]"
 
-#: builtin/fsmonitor--daemon.c
-msgid "git fsmonitor--daemon stop"
-msgstr "git fsmonitor--daemon stop"
-
-#: builtin/fsmonitor--daemon.c
-msgid "git fsmonitor--daemon status"
-msgstr "git fsmonitor--daemon status"
-
 #: builtin/fsmonitor--daemon.c
 #, c-format
 msgid "value of '%s' out of range: %d"
@@ -7809,7 +7906,7 @@ msgstr "要等待背景守護程式啟動的最長秒數"
 msgid "invalid 'ipc-threads' value (%d)"
 msgstr "無效的「ipc-threads」數值(%d)"
 
-#: builtin/fsmonitor--daemon.c
+#: builtin/fsmonitor--daemon.c t/helper/test-cache-tree.c
 #, c-format
 msgid "Unhandled subcommand '%s'"
 msgstr "未處理的子命令「%s」"
@@ -7832,7 +7929,7 @@ msgstr "對 %s 呼叫 fstat 失敗:%s"
 msgid "failed to parse '%s' value '%s'"
 msgstr "無法解析 '%s' 值 '%s'"
 
-#: builtin/gc.c builtin/init-db.c
+#: builtin/gc.c setup.c
 #, c-format
 msgid "cannot stat '%s'"
 msgstr "不能對 '%s' 呼叫 stat"
@@ -7857,7 +7954,11 @@ msgstr "清除未引用的物件"
 
 #: builtin/gc.c
 msgid "pack unreferenced objects separately"
-msgstr "獨立封裝無參照物件"
+msgstr "獨立封裝無引用物件"
+
+#: builtin/gc.c builtin/repack.c
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "搭配 --cruft,限制新廢棄封裝的大小"
 
 #: builtin/gc.c
 msgid "be more thorough (increased runtime)"
@@ -8008,8 +8109,23 @@ msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr "--auto 和 --schedule=<頻率> 請任選一"
 
 #: builtin/gc.c
-msgid "failed to run 'git config'"
-msgstr "無法執行 ‘git config’"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "無法為「%2$s」的值加上「%1$s」"
+
+#: builtin/gc.c
+msgid "return success even if repository was not registered"
+msgstr "即便版本庫未註冊仍回傳成功狀態"
+
+#: builtin/gc.c
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "無法取消設定「%2$s」的「%1$s」值"
+
+#: builtin/gc.c
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "版本庫「%s」未註冊"
 
 #: builtin/gc.c
 #, c-format
@@ -8058,14 +8174,6 @@ msgstr "無法執行 “crontab”;您的系統可能不支援 “cron”"
 msgid "'crontab' died"
 msgstr "“crontab” 結束運作"
 
-#: builtin/gc.c
-msgid "failed to start systemctl"
-msgstr "無法啟動 systemctl"
-
-#: builtin/gc.c
-msgid "failed to run systemctl"
-msgstr "無法執行 systemctl"
-
 #: builtin/gc.c builtin/worktree.c
 #, c-format
 msgid "failed to delete '%s'"
@@ -8076,6 +8184,14 @@ msgstr "刪除 '%s' 失敗"
 msgid "failed to flush '%s'"
 msgstr "排清 '%s' 失敗"
 
+#: builtin/gc.c
+msgid "failed to start systemctl"
+msgstr "無法啟動 systemctl"
+
+#: builtin/gc.c
+msgid "failed to run systemctl"
+msgstr "無法執行 systemctl"
+
 #: builtin/gc.c
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
@@ -8106,6 +8222,10 @@ msgstr "scheduler"
 msgid "scheduler to trigger git maintenance run"
 msgstr "要觸發 git maintenance run 的排程器"
 
+#: builtin/gc.c
+msgid "failed to set up maintenance schedule"
+msgstr "無法設定維護排程"
+
 #: builtin/gc.c
 msgid "failed to add repo to global config"
 msgstr "無法將版本庫加至全域設定"
@@ -8128,7 +8248,6 @@ msgstr "grep:無法建立執行緒:%s"
 msgid "invalid number of threads specified (%d) for %s"
 msgstr "為 %2$s 設定的執行緒數 (%1$d) 無效"
 
-#. #-#-#-#-#  grep.c.po  #-#-#-#-#
 #. TRANSLATORS: %s is the configuration
 #. variable for tweaking threads, currently
 #. grep.threads
@@ -8200,11 +8319,11 @@ msgstr "用 textconv 過濾器處理二進位檔案"
 
 #: builtin/grep.c
 msgid "search in subdirectories (default)"
-msgstr "在子目錄中尋找(預設)"
+msgstr "在子目錄中尋找(預設)"
 
 #: builtin/grep.c
-msgid "descend at most <depth> levels"
-msgstr "最多以指定的深度向下尋找"
+msgid "descend at most <n> levels"
+msgstr "最多向下尋找 <n> 層"
 
 #: builtin/grep.c
 msgid "use extended POSIX regular expressions"
@@ -8212,7 +8331,7 @@ msgstr "使用延伸的 POSIX 常規表示式"
 
 #: builtin/grep.c
 msgid "use basic POSIX regular expressions (default)"
-msgstr "使用基本的 POSIX 常規表示式(預設)"
+msgstr "使用基本的 POSIX 常規表示式(預設)"
 
 #: builtin/grep.c
 msgid "interpret patterns as fixed strings"
@@ -8386,11 +8505,15 @@ msgstr "同時給出了 --cached 和樹狀物件"
 
 #: builtin/hash-object.c
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <類型>] [-w] [--path=<檔案> | --no-filters] [--stdin] "
-"[--] <檔案>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
+
+#: builtin/hash-object.c
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
 
 #: builtin/hash-object.c
 msgid "object type"
@@ -8553,13 +8676,21 @@ msgid "'git help config' for more information"
 msgstr "'git help config' 取得更多訊息"
 
 #: builtin/hook.c
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
 
 #: builtin/hook.c
 msgid "silently ignore missing requested <hook-name>"
 msgstr "靜默忽略不存在而請求的 <hook-name>"
 
+#: builtin/hook.c
+msgid "file to read into hooks' stdin"
+msgstr "要讀進掛鉤 stdin 的檔案"
+
 #: builtin/index-pack.c
 #, c-format
 msgid "object type mismatch at %s"
@@ -8810,7 +8941,7 @@ msgstr "無法返回目前工作目錄"
 msgid "bad %s"
 msgstr "錯誤選項 %s"
 
-#: builtin/index-pack.c builtin/init-db.c
+#: builtin/index-pack.c builtin/init-db.c setup.c
 #, c-format
 msgid "unknown hash algorithm '%s'"
 msgstr "未知的「%s」雜湊算法"
@@ -8827,102 +8958,17 @@ msgstr "--verify 沒有提供 packfile 名稱參數"
 msgid "fsck error in pack objects"
 msgstr "在打包物件中 fsck 檢查發生錯誤"
 
-#: builtin/init-db.c
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "不能對範本 '%s' 呼叫 stat"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "不能開啟目錄 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "不能讀取連結 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "不能自 '%s' 到 '%s' 建立符號連結"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "不能複製 '%s' 至 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "ignoring template %s"
-msgstr "忽略範本 %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "templates not found in %s"
-msgstr "沒有在 %s 中找到範本"
-
-#: builtin/init-db.c
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "沒有從 '%s' 複製範本:%s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "無效的初始分支名稱:'%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "不能處理 %d 類型的檔案"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "不能移動 %s 至 %s"
-
-#: builtin/init-db.c
-msgid "attempt to reinitialize repository with different hash"
-msgstr "嘗試以不同的雜湊值重新初始化版本庫"
-
-#: builtin/init-db.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s 已經存在"
-
-#: builtin/init-db.c
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: 忽略 --initial-branch=%s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "重新初始化已存在的共享 Git 版本庫於 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "重新初始化已存在的 Git 版本庫於 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "已初始化空的共享 Git 版本庫於 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "已初始化空的 Git 版本庫於 %s%s\n"
-
 #: builtin/init-db.c
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<範本目錄>] [--shared[=<權限>]] "
-"[<目錄>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 
 #: builtin/init-db.c
 msgid "permissions"
@@ -8972,11 +9018,13 @@ msgstr "--separate-git-dir 與純版本庫不相容"
 
 #: builtin/interpret-trailers.c
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer <鍵>[(=|:)<值"
-">])...] [<檔案>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 
 #: builtin/interpret-trailers.c
 msgid "edit files in place"
@@ -8986,6 +9034,10 @@ msgstr "在原位編輯檔案"
 msgid "trim empty trailers"
 msgstr "刪除空的尾部署名"
 
+#: builtin/interpret-trailers.c
+msgid "placement"
+msgstr "placement"
+
 #: builtin/interpret-trailers.c
 msgid "where to place the new trailer"
 msgstr "在哪裡放置新的尾部署名"
@@ -9003,20 +9055,20 @@ msgid "output only the trailers"
 msgstr "只輸出尾部署名"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply config rules"
-msgstr "不要套用組態設定規則"
+msgid "do not apply trailer.* configuration variables"
+msgstr "不套用 trailer.* 組態變數"
 
 #: builtin/interpret-trailers.c
-msgid "join whitespace-continued values"
-msgstr "連線空白折行的值"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "將多列尾注值 (trailer values) 重新格式化為單列值"
 
 #: builtin/interpret-trailers.c
-msgid "set parsing options"
-msgstr "設定解析選項"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "--only-trailers --only-input --unfold 的別名"
 
 #: builtin/interpret-trailers.c
-msgid "do not treat --- specially"
-msgstr "不要對 --- 特殊處理"
+msgid "do not treat \"---\" as the end of input"
+msgstr "不要把 “---” 當作輸入結尾"
 
 #: builtin/interpret-trailers.c
 msgid "trailer(s) to add"
@@ -9128,6 +9180,11 @@ msgstr "只需要一個範圍"
 msgid "not a range"
 msgstr "不是一個範圍"
 
+#: builtin/log.c
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "無法讀取分支描述檔 “%s”"
+
 #: builtin/log.c
 msgid "cover letter needs email format"
 msgstr "附函需要信件位址格式"
@@ -9254,6 +9311,10 @@ msgstr "從描述產生附函的模式"
 msgid "generate parts of a cover letter based on a branch's description"
 msgstr "基於分支描述產生部分附函"
 
+#: builtin/log.c
+msgid "use branch description from file"
+msgstr "從檔案讀取分支描述並使用"
+
 #: builtin/log.c
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "使用 [<前綴>] 代替 [PATCH]"
@@ -9280,7 +9341,7 @@ msgstr "不包含已在上游提交中的修補檔"
 
 #: builtin/log.c
 msgid "show patch format instead of default (patch + stat)"
-msgstr "顯示純修補檔格式而非預設(修補檔+狀態)"
+msgstr "顯示純修補檔格式而非預設(修補檔+狀態)"
 
 #: builtin/log.c
 msgid "Messaging"
@@ -9296,7 +9357,7 @@ msgstr "新增信件頭"
 
 #: builtin/log.c
 msgid "email"
-msgstr "信件位址"
+msgstr "信"
 
 #: builtin/log.c
 msgid "add To: header"
@@ -9457,6 +9518,11 @@ msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "不能找到追蹤的遠端分支,請手動指定 <上游>。\n"
 
+#: builtin/ls-files.c builtin/ls-tree.c
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "無法取得「%s」相關的物件資訊"
+
 #: builtin/ls-files.c
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
@@ -9494,7 +9560,7 @@ msgstr "使用小寫字母表示 'fsmonitor clean' 檔案"
 
 #: builtin/ls-files.c
 msgid "show cached files in the output (default)"
-msgstr "顯示快取的檔案(預設)"
+msgstr "顯示快取的檔案(預設)"
 
 #: builtin/ls-files.c
 msgid "show deleted files in the output"
@@ -9594,12 +9660,12 @@ msgstr ""
 #: builtin/ls-remote.c
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<版本庫> [<引用>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 
 #: builtin/ls-remote.c
 msgid "do not print remote URL"
@@ -9641,11 +9707,6 @@ msgstr "除了顯示指向的物件外,顯示指向的引用名"
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
 msgstr "git ls-tree [<選項>] <樹或提交> [<路徑>...]"
 
-#: builtin/ls-tree.c
-#, c-format
-msgid "could not get object info about '%s'"
-msgstr "無法取得「%s」相關的物件資訊"
-
 #: builtin/ls-tree.c
 #, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
@@ -9752,7 +9813,7 @@ msgstr "在訊息內文中使用標頭"
 
 #: builtin/mailsplit.c
 msgid "reading patches from stdin/tty..."
-msgstr "æ­£å\9c¨å¾\9eæ¨\99æº\96輸å\85¥æ\88\96 tty è®\80å\8f\96ä¿®è£\9cæª\94å\85§å®¹â\8b¯â\8b¯"
+msgstr "æ­£å\9c¨å¾\9eæ¨\99æº\96輸å\85¥æ\88\96 tty è®\80å\8f\96ä¿®è£\9cæª\94å\85§å®¹â\80¦â\80¦"
 
 #: builtin/mailsplit.c
 #, c-format
@@ -9767,14 +9828,14 @@ msgstr "git merge-base [-a | --all] <提交> <提交>..."
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <提交>..."
 
-#: builtin/merge-base.c
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <提交>..."
-
 #: builtin/merge-base.c
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <提交> <提交>"
 
+#: builtin/merge-base.c
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <提交>..."
+
 #: builtin/merge-base.c
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <引用> [<提交>]"
@@ -9811,6 +9872,10 @@ msgstr ""
 msgid "send results to standard output"
 msgstr "將結果傳送到標準輸出"
 
+#: builtin/merge-file.c
+msgid "use object IDs instead of filenames"
+msgstr "使用物件 ID 取代檔名"
+
 #: builtin/merge-file.c
 msgid "use a diff3 based merge"
 msgstr "使用基於 diff3 的合併"
@@ -9843,6 +9908,15 @@ msgstr "不要警告衝突"
 msgid "set labels for file1/orig-file/file2"
 msgstr "為 檔案1/初始檔案/檔案2 設定標籤"
 
+#: builtin/merge-file.c
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "物件 “%s” 不存在"
+
+#: builtin/merge-file.c
+msgid "Could not write object file"
+msgstr "無法寫入物件檔案"
+
 #: builtin/merge-recursive.c
 #, c-format
 msgid "unknown option %s"
@@ -9913,10 +9987,45 @@ msgstr "列出檔名,但不附加 modes/oids/stages"
 msgid "allow merging unrelated histories"
 msgstr "允許合並不相關的歷史"
 
+#: builtin/merge-tree.c
+msgid "perform multiple merges, one per line of input"
+msgstr "執行多次合併,一次執行輸入一列"
+
+#: builtin/merge-tree.c
+msgid "specify a merge-base for the merge"
+msgstr "指定用來合併的合併基底"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option=value"
+msgstr "option=value"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option for selected merge strategy"
+msgstr "所選的合併策略的選項"
+
 #: builtin/merge-tree.c
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge 和其他所有選項都不相容"
 
+#: builtin/merge-tree.c builtin/merge.c
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "未知的策略選項:-X%s"
+
+#: builtin/merge-tree.c
+msgid "--merge-base is incompatible with --stdin"
+msgstr "--merge-base 與 --stdin 不相容"
+
+#: builtin/merge-tree.c builtin/notes.c
+#, c-format
+msgid "malformed input line: '%s'."
+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 [<選項>] [<提交>...]"
@@ -9967,7 +10076,7 @@ msgstr "建立一個單獨的提交而不是做一次合併"
 
 #: builtin/merge.c builtin/pull.c
 msgid "perform a commit if the merge succeeds (default)"
-msgstr "如果合併成功,執行一次提交(預設)"
+msgstr "如果合併成功,執行一次提交(預設)"
 
 #: builtin/merge.c builtin/pull.c
 msgid "edit message before committing"
@@ -9975,7 +10084,7 @@ msgstr "在提交前編輯提交說明"
 
 #: builtin/merge.c
 msgid "allow fast-forward (default)"
-msgstr "允許快轉(預設)"
+msgstr "允許快轉(預設)"
 
 #: builtin/merge.c builtin/pull.c
 msgid "abort if fast-forward is not possible"
@@ -9994,14 +10103,6 @@ msgstr "策略"
 msgid "merge strategy to use"
 msgstr "要使用的合併策略"
 
-#: builtin/merge.c builtin/pull.c
-msgid "option=value"
-msgstr "option=value"
-
-#: builtin/merge.c builtin/pull.c
-msgid "option for selected merge strategy"
-msgstr "所選的合併策略的選項"
-
 #: builtin/merge.c
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "合併的提交說明(針對非快轉式合併)"
@@ -10029,11 +10130,11 @@ msgstr "繞過 pre-merge-commit 和 commit-msg 掛鉤"
 
 #: builtin/merge.c
 msgid "could not run stash."
-msgstr "不能執行儲藏。"
+msgstr "不能執行貯存。"
 
 #: builtin/merge.c
 msgid "stash failed"
-msgstr "儲藏失敗"
+msgstr "貯存失敗"
 
 #: builtin/merge.c
 #, c-format
@@ -10081,11 +10182,6 @@ msgstr "不能寫入索引。"
 msgid "Not handling anything other than two heads merge."
 msgstr "未處理兩個頭合併之外的任何動作。"
 
-#: builtin/merge.c
-#, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "未知的策略選項:-X%s"
-
 #: builtin/merge.c t/helper/test-fast-rebase.c
 #, c-format
 msgid "unable to write %s"
@@ -10158,7 +10254,7 @@ msgstr "對於 %s 沒有來自 %s 的遠端追蹤分支"
 msgid "Bad value '%s' in environment '%s'"
 msgstr "環境 '%2$s' 中存在壞的取值 '%1$s'"
 
-#: builtin/merge.c read-cache.c strbuf.c wrapper.c
+#: builtin/merge.c editor.c read-cache.c wrapper.c
 #, c-format
 msgid "could not close '%s'"
 msgstr "不能關閉 '%s'"
@@ -10286,7 +10382,7 @@ msgstr "自動合併進展順利,按要求在提交前停止\n"
 #: builtin/merge.c
 #, c-format
 msgid "When finished, apply stashed changes with `git stash pop`\n"
-msgstr "完成時,使用 `git stash pop` 套用儲藏更改\n"
+msgstr "完成時,使用 `git stash pop` 套用貯存更改\n"
 
 #: builtin/mktag.c
 #, c-format
@@ -10406,7 +10502,7 @@ msgstr "目錄 %s 在索引中並且不是子模組?"
 
 #: builtin/mv.c
 msgid "Please stage your changes to .gitmodules or stash them to proceed"
-msgstr "請將您的修改暫存到 .gitmodules 中或儲藏後再繼續"
+msgstr "請將您的修改暫存到 .gitmodules 中或貯存後再繼續"
 
 #: builtin/mv.c
 #, c-format
@@ -10444,8 +10540,8 @@ msgid "can not move directory into itself"
 msgstr "不能將目錄移動到自身"
 
 #: builtin/mv.c
-msgid "cannot move directory over file"
-msgstr "不能將目錄移動到檔案"
+msgid "destination already exists"
+msgstr "目的地已存在"
 
 #: builtin/mv.c
 msgid "source directory is empty"
@@ -10490,7 +10586,7 @@ msgstr "%s,來源=%s,目的地=%s"
 msgid "Renaming %s to %s\n"
 msgstr "重新命名 %s 至 %s\n"
 
-#: builtin/mv.c builtin/remote.c builtin/repack.c
+#: builtin/mv.c builtin/remote.c
 #, c-format
 msgid "renaming '%s' failed"
 msgstr "重新命名 '%s' 失敗"
@@ -10537,7 +10633,7 @@ msgstr "註解從標準輸入收到的文字"
 
 #: builtin/name-rev.c
 msgid "allow to print `undefined` names (default)"
-msgstr "允許列印 `未定義` 的名稱(預設)"
+msgstr "允許列印 `未定義` 的名稱(預設)"
 
 #: builtin/name-rev.c
 msgid "dereference tags in the input (internal use)"
@@ -10549,11 +10645,13 @@ msgstr "git notes [--ref <註解引用>] [list [<物件>]]"
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <註解引用>] add [-f] [--allow-empty] [-m <說明> | -F <檔案> "
-"| (-c | -C) <物件>] [<物件>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
@@ -10561,11 +10659,13 @@ msgstr "git notes [--ref <註解引用>] copy [-f] <來源物件> <目標物件>
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <註解引用>] append [--allow-empty] [-m <說明> | -F <檔案> | "
-"(-c | -C) <物件>] [<物件>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
@@ -10691,11 +10791,6 @@ msgstr "讀取物件 '%s' 失敗。"
 msgid "cannot read note data from non-blob object '%s'."
 msgstr "不能從非資料物件 '%s' 中讀取註解資料。"
 
-#: builtin/notes.c
-#, c-format
-msgid "malformed input line: '%s'."
-msgstr "格式錯誤的輸入行:'%s'。"
-
 #: builtin/notes.c
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
@@ -10738,6 +10833,18 @@ msgstr "允許儲存空白備註"
 msgid "replace existing notes"
 msgstr "取代已存在的註解"
 
+#: builtin/notes.c
+msgid "<paragraph-break>"
+msgstr "<paragraph-break>"
+
+#: builtin/notes.c
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "在段落間插入 <paragraph-break>"
+
+#: builtin/notes.c
+msgid "remove unnecessary whitespace"
+msgstr "移除不必要的空白字元"
+
 #: builtin/notes.c
 #, c-format
 msgid ""
@@ -10928,14 +11035,13 @@ msgid "unknown subcommand: `%s'"
 msgstr "未知子指令:「%s」"
 
 #: builtin/pack-objects.c
-msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
-msgstr "git pack-objects --stdout [<選項>...] [< <引用列表> | < <物件列表>]"
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
+msgstr "git pack-objects --stdout [<選項>] [< <引用列表> | < <物件列表>]"
 
 #: builtin/pack-objects.c
 msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
-msgstr "git pack-objects [<選項>...] <前綴名稱> [< <引用列表> | < <物件列表>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
+msgstr "git pack-objects [<選項>] <前綴名稱> [< <引用列表> | < <物件列表>]"
 
 #: builtin/pack-objects.c
 #, c-format
@@ -10997,7 +11103,7 @@ msgstr "無法寫入位圖索引"
 msgid "wrote %<PRIu32> objects while expecting %<PRIu32>"
 msgstr "寫入 %<PRIu32> 個物件而預期 %<PRIu32> 個"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/repack.c
 msgid "disabling bitmap writing, as some objects are not being packed"
 msgstr "停用 bitmap 寫入,因為一些物件將不會被打包"
 
@@ -11163,6 +11269,14 @@ msgstr "不支援的索引版本 %s"
 msgid "bad index version '%s'"
 msgstr "壞的索引版本 '%s'"
 
+#: builtin/pack-objects.c
+msgid "show progress meter during object writing phase"
+msgstr "在物件寫入階段顯示進度列"
+
+#: builtin/pack-objects.c
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "顯示進度列時類似 --all-progress"
+
 #: builtin/pack-objects.c
 msgid "<version>[,<offset>]"
 msgstr "<版本>[,<位移>]"
@@ -11354,10 +11468,6 @@ msgstr "最小的包檔案大小是 1 MiB"
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin 不能用於建立一個可索引包"
 
-#: builtin/pack-objects.c
-msgid "cannot use --filter without --stdout"
-msgstr "不能在沒有 --stdout 的情況下使用 --filter"
-
 #: builtin/pack-objects.c
 msgid "cannot use --filter with --stdin-packs"
 msgstr "無法將 --filter 及 --stdin-packs 結合使用"
@@ -11374,10 +11484,6 @@ msgstr "無法透過 --cruft 使用內部修訂清單"
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "無法將 --stdin-packs 與 --cruft 組合使用"
 
-#: builtin/pack-objects.c
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "無法將 --max-pack-size 與 --cruft 組合使用"
-
 #: builtin/pack-objects.c
 msgid "Enumerating objects"
 msgstr "枚舉物件"
@@ -11405,9 +11511,17 @@ msgstr ""
 "<git@vger.kernel.org> 讓我們知道您還在使用。\n"
 "感謝。\n"
 
+#: builtin/pack-redundant.c
+msgid "refusing to run without --i-still-use-this"
+msgstr "傳入 --i-still-use-this 前拒絕執行"
+
 #: builtin/pack-refs.c
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<選項>]"
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
 
 #: builtin/pack-refs.c
 msgid "pack everything"
@@ -11415,7 +11529,31 @@ msgstr "打包一切"
 
 #: builtin/pack-refs.c
 msgid "prune loose refs (default)"
-msgstr "剪除鬆散引用(預設)"
+msgstr "剪除鬆散引用(預設值)"
+
+#: builtin/pack-refs.c
+msgid "references to include"
+msgstr "要包含的引用"
+
+#: builtin/pack-refs.c
+msgid "references to exclude"
+msgstr "要排除的引用"
+
+#: builtin/patch-id.c
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+#: builtin/patch-id.c
+msgid "use the unstable patch-id algorithm"
+msgstr "使用不穩定的 patch-id 演算法"
+
+#: builtin/patch-id.c
+msgid "use the stable patch-id algorithm"
+msgstr "使用穩定的 patch-id 演算法"
+
+#: builtin/patch-id.c
+msgid "don't strip whitespace from the patch"
+msgstr "不要去除修補檔的空白字元"
 
 #: builtin/prune.c
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
@@ -11463,7 +11601,7 @@ msgstr "控制 pre-merge-commit 和 commit-msg 掛鉤的使用"
 
 #: builtin/pull.c parse-options.h
 msgid "automatically stash/stash pop before and after"
-msgstr "在動作前後執行自動儲藏和彈出儲藏"
+msgstr "在動作前後執行自動貯存和彈出貯存"
 
 #: builtin/pull.c
 msgid "Options related to fetching"
@@ -11477,6 +11615,14 @@ msgstr "強制覆蓋本機分支"
 msgid "number of submodules pulled in parallel"
 msgstr "並行拉取的子模組數量"
 
+#: builtin/pull.c parse-options.h
+msgid "use IPv4 addresses only"
+msgstr "只使用 IPv4 位址"
+
+#: builtin/pull.c parse-options.h
+msgid "use IPv6 addresses only"
+msgstr "只使用 IPv6 位址"
+
 #: builtin/pull.c
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
@@ -11502,7 +11648,7 @@ msgid ""
 "for your current branch, you must specify a branch on the command line."
 msgstr ""
 "您要求從遠端 '%s' 拉取,但是未指定一個分支。因為這不是目前\n"
-"分支預設的遠端版本庫,您必須在令列中指定一個分支名。"
+"分支預設的遠端版本庫,您必須在令列中指定一個分支名。"
 
 #: builtin/pull.c builtin/rebase.c
 msgid "You are not currently on a branch."
@@ -11592,9 +11738,9 @@ msgstr "更新尚未誕生的分支,變更新增至索引。"
 msgid "pull with rebase"
 msgstr "重定基底式拉取"
 
-#: builtin/pull.c
-msgid "please commit or stash them."
-msgstr "請提交或儲藏它們。"
+#: builtin/pull.c builtin/rebase.c
+msgid "Please commit or stash them."
+msgstr "請提交或貯存修改。"
 
 #: builtin/pull.c
 #, c-format
@@ -11666,14 +11812,14 @@ msgstr ""
 #: builtin/push.c
 msgid ""
 "\n"
-"To avoid automatically configuring upstream branches when their name\n"
-"doesn't match the local branch, see option 'simple' of branch."
-"autoSetupMerge\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 "\n"
-"為了避免自動在上游分支的名稱與本機不符時,設定上游的分支,請參閱\n"
-"“git help config” 中 branch.autoSetupMerge 的 “simple” 選項。\n"
+"若要避免在名稱與本機分支不同時自動設定上游分支,\n"
+"請參閱 “git help config” 中 branch.autoSetupMerge 的\n"
+"“simple” 選項。\n"
 
 #: builtin/push.c
 #, c-format
@@ -11761,37 +11907,37 @@ msgstr ""
 #: builtin/push.c
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒絕,因為您目前分支的最新提交落後於其對應的遠端分支。\n"
-"å\86\8d次æ\8e¨é\80\81å\89\8dï¼\8cå\85\88è\88\87é\81 ç«¯è®\8aæ\9b´å\90\88ä½µï¼\88å¦\82 'git pull ...'ï¼\89。詳見\n"
-"'git push --help' 中的 'Note about fast-forwards' 小節。"
+"å¦\82æ\9e\9cæ\82¨æ\83³è¦\81æ\95´å\90\88é\81 ç«¯æ\9b´å\8b\95ï¼\8cè«\8bå\9c¨å\86\8d次æ\8e¨é\80\81å\89\8d使ç\94¨ â\80\9cgit pullâ\80\9d。詳見\n"
+"“git push --help” 中的〈Note about fast-forwards〉小節。"
 
 #: builtin/push.c
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"更新被拒絕,因為推送的一個分支的最新提交落後於其對應的遠端分支。\n"
-"簽出該分支並整合遠端變更(如 'git pull ...'),然後再推送。詳見\n"
-"'git push --help' 中的 'Note about fast-forwards' 小節。"
+"更新被拒絕,因為推送的分支的最新提交落後於其對應的遠端分支。\n"
+"如果您想要整合遠端更動,請在再次推送前使用 “git pull”。詳見\n"
+"“git push --help” 中的〈Note about fast-forwards〉小節。"
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"因為遠端版本庫包含您本機尚不存在的提交,而導致更新被拒絕。這通常是因為另外\n"
-"一個版本庫已向該引用進行了推送。再次推送前,您可能需要先整合遠端變更\n"
-"(如 'git pull ...')。\n"
-"詳見 'git push --help' 中的 'Note about fast-forwards' 小節。"
+"更新被拒絕,因為遠端包含您本機沒有的提交。這通常是因為\n"
+"另一個版本庫有推送更動到同個引用。如果您想要整合遠端更動,\n"
+"請在再次推送前使用 “git pull”。詳見 “git push --help” 中的\n"
+"〈Note about fast-forwards〉小節。"
 
 #: builtin/push.c
 msgid "Updates were rejected because the tag already exists in the remote."
@@ -11808,14 +11954,14 @@ msgstr ""
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"更新被拒,因為遠端追蹤分支的最新指針繼上次簽出後有更新。\n"
-"您可能會希望先將這些變更整合至本地(例如:‘git pull …’)\n"
-"最後才強制更新。\n"
+"更新被拒絕,因為遠端追蹤分支的最新提交自上次簽出後有改變。\n"
+"如果您想要整合遠端更動,請在再次推送前使用 “git pull”。\n"
+"詳見 “git push --help” 中的〈Note about fast-forwards〉小節。"
 
 #: builtin/push.c
 #, c-format
@@ -11827,6 +11973,12 @@ msgstr "推送到 %s\n"
 msgid "failed to push some refs to '%s'"
 msgstr "推送一些引用到 '%s' 失敗"
 
+#: builtin/push.c
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr "在有 push.recurseSubmodules=only; 的情況嘗試遞迴子模組;改用 on-demand"
+
 #: builtin/push.c builtin/send-pack.c submodule-config.c
 #, c-format
 msgid "invalid value for '%s'"
@@ -11836,9 +11988,9 @@ msgstr "「%s」的值無效"
 msgid "repository"
 msgstr "版本庫"
 
-#: builtin/push.c builtin/send-pack.c
-msgid "push all refs"
-msgstr "æ\8e¨é\80\81æ\89\80æ\9c\89å¼\95ç\94¨"
+#: builtin/push.c
+msgid "push all branches"
+msgstr "æ\8e¨é\80\81æ\89\80æ\9c\89å\88\86æ\94¯"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "mirror all refs"
@@ -11849,8 +12001,8 @@ msgid "delete refs"
 msgstr "刪除引用"
 
 #: builtin/push.c
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "推送標籤(不能使用 --all or --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr "推送標籤(不能和 --all、--branches 或 --mirror 一起使用)"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "force updates"
@@ -11908,7 +12060,7 @@ msgstr "需要遠端支援 atomic 事務"
 msgid "--delete doesn't make sense without any refs"
 msgstr "--delete 未接任何引用沒有意義"
 
-#: builtin/push.c
+#: builtin/push.c t/helper/test-bundle-uri.c
 #, c-format
 msgid "bad repository '%s'"
 msgstr "壞的版本庫 '%s'"
@@ -11999,13 +12151,15 @@ msgstr "需要兩個提交範圍"
 
 #: builtin/read-tree.c
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<前綴>) [-"
-"u | -i]] [--no-sparse-checkout] [--index-output=<檔案>] (--empty | <樹狀物件"
-"1> [<樹狀物件2> [<樹狀物件3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 
 #: builtin/read-tree.c
 msgid "write resulting index to <file>"
@@ -12122,8 +12276,8 @@ msgstr "%s 需要合併後端"
 
 #: builtin/rebase.c
 #, c-format
-msgid "could not get 'onto': '%s'"
-msgstr "ç\84¡æ³\95å\8f\96å¾\97 'onto'ï¼\9a'%s'"
+msgid "invalid onto: '%s'"
+msgstr "ç\84¡æ\95\88ç\9a\84 ontoï¼\9aã\80\8c%sã\80\8d"
 
 #: builtin/rebase.c
 #, c-format
@@ -12171,18 +12325,36 @@ msgstr ""
 "\n"
 "因此 git 無法對其重定基底。"
 
+#: builtin/rebase.c
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "未知的 rebase-merges 模式:%s"
+
 #: builtin/rebase.c
 #, c-format
 msgid "could not switch to %s"
 msgstr "無法切換到 %s"
 
+#: builtin/rebase.c
+msgid "apply options and merge options cannot be used together"
+msgstr "套用選項與合併選項不得同時使用"
+
 #: builtin/rebase.c
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
 msgstr "無法識別的 '%s' 空類型;有效的數值有 \"drop\"、\"keep\" 跟 \"ask\"。"
 
+#: builtin/rebase.c
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"有字串引數的 --rebase-merges 已被棄用,而且會在未來的 Git 版本停止運作。請改"
+"用無引數的 --rebase-merges,功能相同。"
+
 #: builtin/rebase.c
 #, c-format
 msgid ""
@@ -12447,17 +12619,22 @@ msgid "switch `C' expects a numerical value"
 msgstr "開關 `C' 期望一個數字值"
 
 #: builtin/rebase.c
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "未知模式:%s"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr "apply 選項與 rebase.autoSquash 不相容。請考慮加上 --no-autosquash"
 
 #: builtin/rebase.c
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy 需要 --merge 或 --interactive"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr "apply 選項與 rebase.rebaseMerges 不相容。請考慮加上 --no-rebase-merges"
 
 #: builtin/rebase.c
-msgid "apply options and merge options cannot be used together"
-msgstr "套用選項與合併選項不得同時使用"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr "apply 選項與 rebase.updateRefs 不相容。請考慮加上 --no-update-refs"
 
 #: builtin/rebase.c
 #, c-format
@@ -12488,8 +12665,8 @@ msgid "No such ref: %s"
 msgstr "沒有這樣的引用:%s"
 
 #: builtin/rebase.c
-msgid "Could not resolve HEAD to a revision"
-msgstr "無法將 HEAD 解析為一個版本"
+msgid "Could not resolve HEAD to a commit"
+msgstr "無法將 HEAD 解析成提交"
 
 #: builtin/rebase.c
 #, c-format
@@ -12506,10 +12683,6 @@ msgstr "'%s':只需要一個合併基礎"
 msgid "Does not point to a valid commit '%s'"
 msgstr "沒有指向一個有效的提交 '%s'"
 
-#: builtin/rebase.c
-msgid "Please commit or stash them."
-msgstr "請提交或儲藏修改。"
-
 #: builtin/rebase.c
 msgid "HEAD is up to date."
 msgstr "HEAD 是最新的。"
@@ -12813,12 +12986,12 @@ msgid "fetch the remote branches"
 msgstr "抓取遠端的分支"
 
 #: builtin/remote.c
-msgid "import all tags and associated objects when fetching"
-msgstr "抓取時匯入所有的標籤和關聯物件"
-
-#: builtin/remote.c
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "或不抓取任何標籤(--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"抓取時匯入所有的標籤和關聯物件\n"
+"或者是完全不抓取所有標籤 (--no-tags)"
 
 #: builtin/remote.c
 msgid "branch(es) to track"
@@ -13287,6 +13460,16 @@ msgstr "無法開啟 '%s' 暫存檔進行寫入"
 msgid "could not close refs snapshot tempfile"
 msgstr "無法關閉 refs 的快照暫存檔"
 
+#: builtin/repack.c
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "無法移除過時位圖:%s"
+
+#: builtin/repack.c
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "封包前綴 %s 不以 objdir %s 開頭"
+
 #: builtin/repack.c
 msgid "pack everything in a single pack"
 msgstr "所有內容打包到一個包檔案中"
@@ -13304,8 +13487,8 @@ msgid "approxidate"
 msgstr "近似日期"
 
 #: builtin/repack.c
-msgid "with -C, expire objects older than this"
-msgstr "搭配 -C 會將早於此的物件設為過期"
+msgid "with --cruft, expire objects older than this"
+msgstr "搭配 --cruft 會將早於此的物件標為過期"
 
 #: builtin/repack.c
 msgid "remove redundant packs, and run git-prune-packed"
@@ -13383,28 +13566,41 @@ msgstr "尋找因數是 <N> 的等比數列"
 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 "將前綴進行包裝,儲存為包含已過濾物件的封裝"
+
 #: builtin/repack.c
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "不能刪除珍品版本庫中的封包"
 
+#: builtin/repack.c
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "“%s” 選項只能與 “%s” 一起使用"
+
 #: builtin/repack.c
 msgid "Nothing new to pack."
 msgstr "沒有新的要打包。"
 
 #: builtin/repack.c
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "封包前綴 %s 不以 objdir %s 開頭"
+msgid "renaming pack to '%s' failed"
+msgstr "無法將包重新命名為「%s」"
 
 #: builtin/repack.c
 #, c-format
-msgid "missing required file: %s"
-msgstr "缺少必要檔案:%s"
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects 沒有為 %2$s-%3$s 套件包寫入 “%1$s” 檔案"
 
-#: builtin/repack.c
+#: builtin/repack.c sequencer.c
 #, c-format
 msgid "could not unlink: %s"
-msgstr "ç\84¡æ³\95å\8f\96æ¶\88é\80£çµ\90:%s"
+msgstr "ç\84¡æ³\95å\88ªé\99¤:%s"
 
 #: builtin/replace.c
 msgid "git replace [-f] <object> <replacement>"
@@ -13643,8 +13839,10 @@ msgid "only one pattern can be given with -l"
 msgstr "只能為 -l 提供一個模式"
 
 #: builtin/rerere.c
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forget <路徑>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 
 #: builtin/rerere.c
 msgid "register clean resolutions in index"
@@ -13902,30 +14100,54 @@ msgstr "--prefix 需要 1 個引數"
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "--abbrev-ref 的模式未知:%s"
 
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr "--exclude-hidden 無法與 --branches 同時使用"
+
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr "--exclude-hidden 無法與 --tags 同時使用"
+
+#: builtin/rev-parse.c revision.c
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr "--exclude-hidden 無法與 --remotes 同時使用"
+
 #: builtin/rev-parse.c setup.c
 msgid "this operation must be run in a work tree"
 msgstr "該動作必須在一個工作區中執行"
 
+#: builtin/rev-parse.c
+msgid "Could not read the index"
+msgstr "無法讀取索引"
+
 #: builtin/rev-parse.c
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "--show-object-format 的模式未知:%s"
 
 #: builtin/revert.c
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<選項>] <提交號>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
 
 #: builtin/revert.c
-msgid "git revert <subcommand>"
-msgstr "git revert <子指令>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
 
 #: builtin/revert.c
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<選項>] <提交號>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
 
 #: builtin/revert.c
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <子指令>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
 
 #: builtin/revert.c
 #, c-format
@@ -14006,8 +14228,14 @@ msgid "cherry-pick failed"
 msgstr "揀選失敗"
 
 #: builtin/rm.c
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<選項>] [--] <檔案>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
 
 #: builtin/rm.c
 msgid ""
@@ -14070,7 +14298,7 @@ msgstr "沒有提供路徑規格。我該移除哪個檔案?"
 
 #: builtin/rm.c
 msgid "please stage your changes to .gitmodules or stash them to proceed"
-msgstr "請將您的修改暫存到 .gitmodules 中或儲藏後再繼續"
+msgstr "請將您的修改暫存到 .gitmodules 中或貯存後再繼續"
 
 #: builtin/rm.c
 #, c-format
@@ -14087,17 +14315,23 @@ msgid ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
-"              [<主機>:]<目錄> (--all | <引用>...)"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
+"              [<host>:]<directory> (--all | <ref>...)"
 
 #: builtin/send-pack.c
 msgid "remote name"
 msgstr "遠端名稱"
 
+#: builtin/send-pack.c
+msgid "push all refs"
+msgstr "推送所有引用"
+
 #: builtin/send-pack.c
 msgid "use stateless RPC protocol"
 msgstr "使用無狀態的 RPC 協定"
@@ -14123,8 +14357,9 @@ msgid "using multiple --group options with stdin is not supported"
 msgstr "不支援在標準輸入使用多個 --group 選項"
 
 #: builtin/shortlog.c
-msgid "using --group=trailer with stdin is not supported"
-msgstr "不支援在標準輸入使用 --group=trailer"
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "不支援對 %s 使用 stdin"
 
 #: builtin/shortlog.c
 #, c-format
@@ -14172,13 +14407,14 @@ msgid ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
-"                [--current] [--color[=<何時>] | --no-color] [--sparse]\n"
+"                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<版本> | <萬用字元"
-">)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 
 #: builtin/show-branch.c
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
@@ -14304,16 +14540,40 @@ msgstr "未知的雜湊算法"
 
 #: builtin/show-ref.c
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+
+#: builtin/show-ref.c
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<模式>...]"
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 
 #: builtin/show-ref.c
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<模式>]"
 
+#: builtin/show-ref.c
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <ref>"
+
+#: builtin/show-ref.c
+msgid "reference does not exist"
+msgstr "引用不存在"
+
+#: builtin/show-ref.c
+msgid "failed to look up reference"
+msgstr "無法查詢引用"
+
 #: builtin/show-ref.c
 msgid "only show tags (can be combined with heads)"
 msgstr "只顯示標籤(可以和頭共用)"
@@ -14322,6 +14582,10 @@ msgstr "只顯示標籤(可以和頭共用)"
 msgid "only show heads (can be combined with tags)"
 msgstr "只顯示頭(可以和標籤共用)"
 
+#: builtin/show-ref.c
+msgid "check for reference existence without resolving"
+msgstr "檢查引用是否存在但不解析"
+
 #: builtin/show-ref.c
 msgid "stricter reference checking, requires exact ref path"
 msgstr "更嚴格的引用檢測,需要精確的引用路徑"
@@ -14346,9 +14610,18 @@ msgstr "不列印結果到標準輸出(例如與 --verify 參數共用)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "顯示從標準輸入中讀入的不在本機版本庫中的引用"
 
+#: builtin/show-ref.c
+#, c-format
+msgid "only one of '%s', '%s' or '%s' can be given"
+msgstr "只能傳入 “%s”、“%s” 或 “%s”"
+
 #: builtin/sparse-checkout.c
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
-msgstr "git sparse-checkout (init|list|set|add|reapply|disable) <選項>"
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 
 #: builtin/sparse-checkout.c
 msgid "this worktree is not sparse"
@@ -14489,84 +14762,93 @@ msgstr "必須在稀疏提交才能重新套用稀疏樣式 (sparsity pattern)"
 msgid "error while refreshing working directory"
 msgstr "重新整理工作目錄時發生錯誤"
 
-#: builtin/stash.c
-msgid "git stash list [<options>]"
-msgstr "git stash list [<選項>]"
+#: builtin/sparse-checkout.c
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
 
-#: builtin/stash.c
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<選項>] [<stash>]"
+#: builtin/sparse-checkout.c
+msgid "terminate input and output files by a NUL character"
+msgstr "使用 NUL 字元終止輸入和輸出檔案"
 
-#: builtin/stash.c
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<stash>]"
+#: builtin/sparse-checkout.c
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"與 --rules-file 搭配使用時,將 patterns (模式) 解釋為 cone 模式的 patterns"
 
-#: builtin/stash.c
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
+#: builtin/sparse-checkout.c
+msgid "use patterns in <file> instead of the current ones."
+msgstr "使用 <file> 中的(而非目前使用的)模式。"
 
 #: builtin/stash.c
-msgid "git stash branch <branchname> [<stash>]"
-msgstr "git stash branch <分支名> [<stash>]"
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<log-options>]"
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
-"          [--] [<pathspec>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 msgstr ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <訊息>]\n"
-"          [--pathspec-from-file=<檔案> [--pathspec-file-nul]]\n"
-"          [--] [<路徑規格>...]]"
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
 
 #: builtin/stash.c
-msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
-msgstr ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<訊息>]"
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<stash>]"
+
+#: builtin/stash.c
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<stash>]"
 
 #: builtin/stash.c
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<stash>]"
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<stash>]"
 
 #: builtin/stash.c
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<stash>]"
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <分支名> [<stash>]"
 
 #: builtin/stash.c
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <消息>] [-q|--quiet] <提交>"
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
 
 #: builtin/stash.c
 msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "          [--] [<pathspec>...]]"
 msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <消息>]\n"
-"          [--] [<路徑規格>...]]"
+"git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
+"| --quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [(-m | --message) "
+"<message>]\n"
+"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"          [--] [<pathspec>...]]"
 
 #: builtin/stash.c
 msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<消息>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
+
+#: builtin/stash.c
+msgid "git stash create [<message>]"
+msgstr "git stash create [<message>]"
 
 #: builtin/stash.c
 #, c-format
 msgid "'%s' is not a stash-like commit"
-msgstr "'%s' 不像是一個儲藏提交"
+msgstr "'%s' 不像是一個貯存提交"
 
 #: builtin/stash.c
 #, c-format
@@ -14575,7 +14857,7 @@ msgstr "指定了太多的版本:%s"
 
 #: builtin/stash.c
 msgid "No stash entries found."
-msgstr "未發現儲藏條目。"
+msgstr "未發現貯存條目。"
 
 #: builtin/stash.c
 #, c-format
@@ -14599,7 +14881,7 @@ msgstr ""
 
 #: builtin/stash.c
 msgid "cannot apply a stash in the middle of a merge"
-msgstr "無法在合併過程套用儲藏"
+msgstr "無法在合併過程套用貯存"
 
 #: builtin/stash.c
 #, c-format
@@ -14621,11 +14903,11 @@ msgstr "正在合併 %s 和 %s"
 
 #: builtin/stash.c
 msgid "Index was not unstashed."
-msgstr "索引未從儲藏中復原。"
+msgstr "索引未從貯存中復原。"
 
 #: builtin/stash.c
 msgid "could not restore untracked files from stash"
-msgstr "無法從儲藏條目中復原未追蹤檔案"
+msgstr "無法從貯存條目中復原未追蹤檔案"
 
 #: builtin/stash.c
 msgid "attempt to recreate the index"
@@ -14639,16 +14921,16 @@ msgstr "捨棄了 %s(%s)"
 #: builtin/stash.c
 #, c-format
 msgid "%s: Could not drop stash entry"
-msgstr "%s:無法捨棄儲藏條目"
+msgstr "%s:無法捨棄貯存條目"
 
 #: builtin/stash.c
 #, c-format
 msgid "'%s' is not a stash reference"
-msgstr "'%s' 不是一個儲藏引用"
+msgstr "'%s' 不是一個貯存引用"
 
 #: builtin/stash.c
 msgid "The stash entry is kept in case you need it again."
-msgstr "儲藏條目被保留以備您再次需要。"
+msgstr "貯存條目被保留以備您再次需要。"
 
 #: builtin/stash.c
 msgid "No branch name specified"
@@ -14664,11 +14946,11 @@ msgstr "無法解包樹"
 
 #: builtin/stash.c
 msgid "include untracked files in the stash"
-msgstr "在儲藏區包含未追蹤檔案"
+msgstr "在貯存區包含未追蹤檔案"
 
 #: builtin/stash.c
 msgid "only show untracked files in the stash"
-msgstr "只在儲藏區顯示未追蹤檔案"
+msgstr "只在貯存區顯示未追蹤檔案"
 
 #: builtin/stash.c
 #, c-format
@@ -14677,7 +14959,7 @@ msgstr "無法用 %2$s 更新 %1$s"
 
 #: builtin/stash.c
 msgid "stash message"
-msgstr "儲藏說明"
+msgstr "貯存說明"
 
 #: builtin/stash.c
 msgid "\"git stash store\" requires one <commit> argument"
@@ -14733,7 +15015,7 @@ msgstr "沒有要儲存的本機修改"
 
 #: builtin/stash.c
 msgid "Cannot initialize stash"
-msgstr "無法初始化儲藏"
+msgstr "無法初始化貯存"
 
 #: builtin/stash.c
 msgid "Cannot save the current status"
@@ -14754,11 +15036,11 @@ msgstr "保持索引"
 
 #: builtin/stash.c
 msgid "stash staged changes only"
-msgstr "只儲藏暫存變更"
+msgstr "只貯存暫存變更"
 
 #: builtin/stash.c
 msgid "stash in patch mode"
-msgstr "以修補檔模式儲藏"
+msgstr "以修補檔模式貯存"
 
 #: builtin/stash.c
 msgid "quiet mode"
@@ -14766,7 +15048,7 @@ msgstr "靜默模式"
 
 #: builtin/stash.c
 msgid "include untracked files in stash"
-msgstr "儲藏中包含未追蹤檔案"
+msgstr "貯存中包含未追蹤檔案"
 
 #: builtin/stash.c
 msgid "include ignore files"
@@ -15134,6 +15416,11 @@ msgstr "略過未合併的子模組 %s"
 msgid "Skipping submodule '%s'"
 msgstr "略過子模組 '%s'"
 
+#: builtin/submodule--helper.c
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "無法在沒有網址的情況下複製 “%s” 子模組"
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
@@ -15248,10 +15535,6 @@ msgstr "遞迴遍歷子模組"
 msgid "don't fetch new objects from the remote site"
 msgstr "不從遠端站台取得新物件"
 
-#: builtin/submodule--helper.c
-msgid "path into the working tree"
-msgstr "到工作區的路徑"
-
 #: builtin/submodule--helper.c
 msgid "use the 'checkout' update strategy (default)"
 msgstr "使用 “checkout” 更新策略(預設值)"
@@ -15291,38 +15574,18 @@ msgid ""
 "shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
 "[--] [<path>...]"
 msgstr ""
-"git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] "
-"[-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-"
-"shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
-"[--] [<path>...]"
-
-#: builtin/submodule--helper.c
-msgid "recurse into submodules"
-msgstr "在子模組中遞迴"
-
-#: builtin/submodule--helper.c
-msgid "git submodule absorbgitdirs [<options>] [<path>...]"
-msgstr "git submodule absorbgitdirs [<options>] [<path>...]"
-
-#: builtin/submodule--helper.c
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr "檢查寫入 .gitmodules 檔案是否安全"
-
-#: builtin/submodule--helper.c
-msgid "unset the config in the .gitmodules file"
-msgstr "取消 .gitmodules 檔案中的設定"
-
-#: builtin/submodule--helper.c
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <名稱> [<值>]"
+"git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] "
+"[-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-"
+"shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
+"[--] [<path>...]"
 
-#: builtin/submodule--helper.c
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <名稱>"
+#: builtin/submodule--helper.c submodule.c
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "無法將 HEAD 解析為有效引用。"
 
 #: builtin/submodule--helper.c
-msgid "please make sure that the .gitmodules file is in the working tree"
-msgstr "請確認 .gitmodules 檔案在工作區裡"
+msgid "git submodule absorbgitdirs [<options>] [<path>...]"
+msgstr "git submodule absorbgitdirs [<options>] [<path>...]"
 
 #: builtin/submodule--helper.c
 msgid "suppress output for setting url of a submodule"
@@ -15417,6 +15680,10 @@ msgstr "正在重新啟用「%s」子模組的本機 Git 目錄\n"
 msgid "unable to checkout submodule '%s'"
 msgstr "無法簽出「%s」子模組"
 
+#: builtin/submodule--helper.c
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr "請確認 .gitmodules 檔案在工作區裡"
+
 #: builtin/submodule--helper.c
 #, c-format
 msgid "Failed to add submodule '%s'"
@@ -15478,23 +15745,21 @@ msgstr "版本庫 URL:「%s」必須是絕對路徑,或開頭是 ./|../"
 msgid "'%s' is not a valid submodule name"
 msgstr "「%s」不是有效的子模組名稱"
 
-#: builtin/submodule--helper.c git.c
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s 不支援 --super-prefix"
-
 #: builtin/submodule--helper.c
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "'%s' 不是一個有效的 submodule--helper 子指令"
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <command>"
+
+#: builtin/symbolic-ref.c
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <reason>] <name> <ref>"
 
 #: builtin/symbolic-ref.c
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<選項>] <名稱> [<引用>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
 
 #: builtin/symbolic-ref.c
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <名稱>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <name>"
 
 #: builtin/symbolic-ref.c
 msgid "suppress error message for non-symbolic (detached) refs"
@@ -15508,6 +15773,10 @@ msgstr "刪除符號引用"
 msgid "shorten ref output"
 msgstr "縮短引用輸出"
 
+#: builtin/symbolic-ref.c
+msgid "recursively dereference (default)"
+msgstr "遞迴反解引用(預設)"
+
 #: builtin/symbolic-ref.c builtin/update-ref.c
 msgid "reason"
 msgstr "原因"
@@ -15518,11 +15787,11 @@ msgstr "更新的原因"
 
 #: builtin/tag.c
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <消息> | -F <檔案>]\n"
-"        <標籤名> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 
 #: builtin/tag.c
 msgid "git tag -d <tagname>..."
@@ -15530,14 +15799,15 @@ msgstr "git tag -d <標籤名>..."
 
 #: builtin/tag.c
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
-"git tag -l [-n[<數字>]] [--contains <提交>] [--no-contains <提交>] [--points-"
-"at <物件>]\n"
-"        [--format=<格式>] [--merged <提交>] [--no-merged <提交>] [<模式>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 
 #: builtin/tag.c
 msgid "git tag -v [--format=<format>] <tagname>..."
@@ -15888,6 +16158,10 @@ msgstr "(for porcelains) 忘記儲存的未解決的衝突"
 msgid "write index in this format"
 msgstr "以這種格式寫入索引區"
 
+#: builtin/update-index.c
+msgid "report on-disk index format version"
+msgstr "回報磁碟上索引格式的版本"
+
 #: builtin/update-index.c
 msgid "enable or disable split index"
 msgstr "啟用或停用索引分割"
@@ -15920,6 +16194,16 @@ msgstr "標記檔案為 fsmonitor 有效"
 msgid "clear fsmonitor valid bit"
 msgstr "清除 fsmonitor 有效位"
 
+#: builtin/update-index.c
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#: builtin/update-index.c
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version:曾是 %d,已設為 %d"
+
 #: builtin/update-index.c
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
@@ -16007,8 +16291,12 @@ msgid "update the info files from scratch"
 msgstr "從頭開始更新檔案訊息"
 
 #: builtin/upload-pack.c
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<選項>] <目錄>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
 
 #: builtin/upload-pack.c t/helper/test-serve-v2.c
 msgid "quit after a single request/response exchange"
@@ -16027,8 +16315,8 @@ msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "不活動 <n> 秒鐘後終止傳輸"
 
 #: builtin/verify-commit.c
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <提交>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | --verbose] [--raw] <commit>..."
 
 #: builtin/verify-commit.c
 msgid "print commit contents"
@@ -16039,8 +16327,8 @@ msgid "print raw gpg status output"
 msgstr "列印原始 gpg 狀態輸出"
 
 #: builtin/verify-pack.c
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <包>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
 
 #: builtin/verify-pack.c
 msgid "verbose"
@@ -16051,44 +16339,82 @@ msgid "show statistics only"
 msgstr "只顯示統計"
 
 #: builtin/verify-tag.c
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<格式>] <標籤>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
 
 #: builtin/verify-tag.c
 msgid "print tag contents"
 msgstr "列印標籤內容"
 
 #: builtin/worktree.c
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<選項>] <路徑> [<提交>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 
 #: builtin/worktree.c
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<選項>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
 
 #: builtin/worktree.c
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<選項>] <路徑>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <string>] <worktree>"
 
 #: builtin/worktree.c
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <工作區> <新路徑>"
 
 #: builtin/worktree.c
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<選項>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <expire>]"
 
 #: builtin/worktree.c
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<選項>] <工作區>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <worktree>"
 
 #: builtin/worktree.c
 msgid "git worktree repair [<path>...]"
 msgstr "git worktree repair [<路徑>...]"
 
 #: builtin/worktree.c
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <路徑>"
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <worktree>"
+
+#: builtin/worktree.c
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "沒有可能的來源分支,推測為 “--orphan”"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"如果您是想要在這個版本庫建立一個工作區,裡面包含一個\n"
+"孤立分支(即沒有提交的分支),可以使用 --orphan 達到\n"
+"這個效果:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new orphan branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"如果您是想要在這個版本庫建立一個工作區,裡面包含一個\n"
+"孤立分支(即沒有提交的分支),可以使用 --orphan 達到\n"
+"這個效果:\n"
+"\n"
+"    git worktree add --orphan %s\n"
 
 #: builtin/worktree.c
 #, c-format
@@ -16170,11 +16496,40 @@ msgstr "準備工作區(重設分支 '%s',之前為 %s)"
 msgid "Preparing worktree (checking out '%s')"
 msgstr "準備工作區(簽出 '%s')"
 
+#: builtin/worktree.c
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "不可達:無效引用:%s"
+
 #: builtin/worktree.c
 #, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "準備工作區(分離開頭指標 %s)"
 
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD 指向無效(或孤立)引用。\n"
+"HEAD 路徑:“%s”\n"
+"HEAD 內容:“%s”"
+
+#: builtin/worktree.c
+msgid ""
+"No local or remote refs exist despite at least one remote\n"
+"present, stopping; use 'add -f' to override or fetch a remote first"
+msgstr ""
+"即使有提供一個遠端,卻不存在本機或遠端引用,\n"
+"故停止。使用 “add -f” 先覆蓋或抓取遠端"
+
+#: builtin/worktree.c
+#, c-format
+msgid "'%s' and '%s' cannot be used together"
+msgstr "無法同時使用 “%s” 和 “%s”"
+
 #: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "簽出 <分支>,即使已經被簽出到其它工作區"
@@ -16187,6 +16542,10 @@ msgstr "建立一個新分支"
 msgid "create or reset a branch"
 msgstr "建立或重設一個分支"
 
+#: builtin/worktree.c
+msgid "create unborn/orphaned branch"
+msgstr "建立尚無內容(孤立)的分支"
+
 #: builtin/worktree.c
 msgid "populate the new working tree"
 msgstr "生成新的工作區"
@@ -16212,6 +16571,15 @@ msgstr "嘗試為新分支名符合一個遠端追蹤分支"
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "「%s」、「%s」和「%s」選項不得同時使用"
 
+#: builtin/worktree.c
+#, c-format
+msgid "options '%s', and '%s' cannot be used together"
+msgstr "無法同時使用 “%s” 和 “%s” 選項"
+
+#: builtin/worktree.c
+msgid "<commit-ish>"
+msgstr "<提交指示元>"
+
 #: builtin/worktree.c
 msgid "added with --lock"
 msgstr "已使用 --lock 加入"
@@ -16372,6 +16740,16 @@ msgstr "只對除錯有用"
 msgid "core.fsyncMethod = batch is unsupported on this platform"
 msgstr "core.fsyncMethod = batch 不支援本平台"
 
+#: bundle-uri.c
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "無法解析套件包清單鍵 %s 的值 “%s”"
+
+#: bundle-uri.c
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "位於 “%s” 的套件包清單沒有模式"
+
 #: bundle-uri.c
 msgid "failed to create temporary file"
 msgstr "無法建立暫存檔"
@@ -16380,25 +16758,60 @@ msgstr "無法建立暫存檔"
 msgid "insufficient capabilities"
 msgstr "功能不足"
 
+#: bundle-uri.c
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "從 “%s” 下載的檔案不是套件包"
+
+#: bundle-uri.c
+msgid "failed to store maximum creation token"
+msgstr "無法儲存最大的建立權杖"
+
+#: bundle-uri.c
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "無法識別從 URI “%s” 取回的套件包模式"
+
+#: bundle-uri.c
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "超出套件包 URI 遞迴限制 (%d)"
+
 #: bundle-uri.c
 #, c-format
 msgid "failed to download bundle from URI '%s'"
-msgstr "無法從 “%s” URI 下載套件"
+msgstr "無法從 “%s” URI 下載套件"
 
 #: bundle-uri.c
 #, c-format
-msgid "file at URI '%s' is not a bundle"
-msgstr "位於 URI “%s” 的檔案不是套件"
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "位於 URI “%s” 的檔案不是套件包或套件包清單"
 
 #: bundle-uri.c
 #, c-format
-msgid "failed to unbundle bundle from URI '%s'"
-msgstr "無法解開源自 URI “%s” 的套件"
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: 非預期的引數:“%s”"
+
+#: bundle-uri.c
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: 引數後應該有一個 flush 包"
+
+#: bundle-uri.c
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: 收到空白列"
+
+#: bundle-uri.c
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: 列的格式不是 “key=value”"
+
+#: bundle-uri.c
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: 列有空鍵或空值"
 
 #: bundle.c
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
-msgstr "無法識別的套件雜湊演算法:%s"
+msgstr "無法識別的套件雜湊演算法:%s"
 
 #: bundle.c
 #, c-format
@@ -16408,7 +16821,7 @@ msgstr "未知功能 '%s'"
 #: bundle.c
 #, c-format
 msgid "'%s' does not look like a v2 or v3 bundle file"
-msgstr "'%s' 不像是一個 v2 或 v3 版本的套件檔案"
+msgstr "“%s” 不像是一個 v2 或 v3 版本的套件包檔案"
 
 #: bundle.c
 #, c-format
@@ -16421,27 +16834,43 @@ msgstr "版本庫中缺少這些必備的提交:"
 
 #: bundle.c
 msgid "need a repository to verify a bundle"
-msgstr "需要版本庫驗證套件"
+msgstr "需要版本庫驗證套件包"
+
+#: bundle.c
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr "一些前提提交存在於物件儲存區,但未連接到版本庫的歷史記錄"
 
 #: bundle.c
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
-msgstr[0] "這個套件含有這 %<PRIuMAX> 個引用:"
+msgstr[0] "é\80\99å\80\8bå¥\97件å\8c\85ï¼\8cå\90«æ\9c\89é\80\99 %<PRIuMAX> å\80\8bå¼\95ç\94¨ï¼\9a"
 
 #: bundle.c
 msgid "The bundle records a complete history."
-msgstr "這個套件記錄完整歷史紀錄。"
+msgstr "這個套件包記下了完整歷史紀錄。"
 
 #: bundle.c
 #, c-format
 msgid "The bundle requires this ref:"
 msgid_plural "The bundle requires these %<PRIuMAX> refs:"
-msgstr[0] "這個套件需要這 %<PRIuMAX> 個引用:"
+msgstr[0] "這個套件包需要這 %<PRIuMAX> 個引用:"
+
+#: bundle.c
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "本套件包採用此雜湊演算法:%s"
+
+#: bundle.c
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "本套件包使用此過濾器:%s"
 
 #: bundle.c
 msgid "unable to dup bundle descriptor"
-msgstr "無法複製套件描述符"
+msgstr "無法複製套件包描述元"
 
 #: bundle.c
 msgid "Could not spawn pack-objects"
@@ -16459,16 +16888,16 @@ msgstr "引用 '%s' 被 rev-list 選項排除"
 #: bundle.c
 #, c-format
 msgid "unsupported bundle version %d"
-msgstr "不支援的套件版本 %d"
+msgstr "不支援的套件版本 %d"
 
 #: bundle.c
 #, c-format
 msgid "cannot write bundle version %d with algorithm %s"
-msgstr "無法寫入 %2$s 演算法的套件版本 %1$d"
+msgstr "無法寫入使用 %2$s 演算法的套件包版本 %1$d"
 
 #: bundle.c
 msgid "Refusing to create empty bundle."
-msgstr "不能建立空套件。"
+msgstr "不能建立空套件。"
 
 #: bundle.c
 #, c-format
@@ -16483,6 +16912,11 @@ msgstr "index-pack 終止"
 msgid "terminating chunk id appears earlier than expected"
 msgstr "終止區塊 id 出現的時間早於預期"
 
+#: chunk-format.c
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "區塊 ID %<PRIx32> 沒有以 %d 位元組為單位對齊"
+
 #: chunk-format.c
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
@@ -16552,8 +16986,8 @@ msgid "Move objects and refs by archive"
 msgstr "透過歸檔移動物件和引用"
 
 #: command-list.h
-msgid "Provide content or type and size information for repository objects"
-msgstr "提供版本庫物件的內容、類型或大小"
+msgid "Provide contents or details of repository objects"
+msgstr "提供版本庫物件的內容或詳細資訊"
 
 #: command-list.h
 msgid "Display gitattributes information"
@@ -16621,7 +17055,7 @@ msgstr "取得和設定版本庫或者全域選項"
 
 #: command-list.h
 msgid "Count unpacked number of objects and their disk consumption"
-msgstr "計算未打包物件的數量和磁碟空間用"
+msgstr "計算未打包物件的數量和磁碟空間用"
 
 #: command-list.h
 msgid "Retrieve and store user credentials"
@@ -16736,8 +17170,8 @@ msgid "A portable graphical interface to Git"
 msgstr "一個便攜的 Git 圖形用戶端"
 
 #: command-list.h
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "å¾\9eä¸\80å\80\8bæª\94æ¡\88è¨\88ç®\97ç\89©ä»¶ IDï¼\8c並å\8f¯ä»¥å»ºç«\8b blob è³\87æ\96\99物件"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "å¾\9eä¸\80å\80\8bæª\94æ¡\88è¨\88ç®\97ç\89©ä»¶ IDï¼\8cä¹\9fè\83½é \86帶建ç«\8bä¸\80å\80\8b物件"
 
 #: command-list.h
 msgid "Display help information about Git"
@@ -17015,7 +17449,7 @@ msgstr "將檔案內容新增到索引"
 
 #: command-list.h
 msgid "Stash the changes in a dirty working directory away"
-msgstr "儲藏髒工作區中的修改"
+msgstr "貯存髒工作區中的修改"
 
 #: command-list.h
 msgid "Show the working tree status"
@@ -17094,7 +17528,7 @@ msgid "Display version information about Git"
 msgstr "顯示 Git 的版本資訊"
 
 #: command-list.h
-msgid "Show logs with difference each commit introduces"
+msgid "Show logs with differences each commit introduces"
 msgstr "顯示每一個提交引入的差異日誌"
 
 #: command-list.h
@@ -17111,7 +17545,7 @@ msgstr "定義路徑的屬性"
 
 #: command-list.h
 msgid "Git command-line interface and conventions"
-msgstr "Git 令列介面和約定"
+msgstr "Git 令列介面和約定"
 
 #: command-list.h
 msgid "A Git core tutorial for developers"
@@ -17139,14 +17573,14 @@ msgstr "Git 使用的常見問題"
 
 #: command-list.h
 msgid "The bundle file format"
-msgstr "套件檔案格式"
+msgstr "套件檔案格式"
 
 #: command-list.h
 msgid "Chunk-based file formats"
 msgstr "以區塊為基礎的檔案格式"
 
 #: command-list.h
-msgid "Git commit graph format"
+msgid "Git commit-graph format"
 msgstr "Git 提交圖格式"
 
 #: command-list.h
@@ -17273,10 +17707,19 @@ msgstr "commit-graph 檔案不夠放置 %u 個區塊"
 msgid "commit-graph has no base graphs chunk"
 msgstr "提交圖形沒有基礎圖形區塊"
 
+#: commit-graph.c
+msgid "commit-graph base graphs chunk is too small"
+msgstr "提交圖形的基礎圖形區塊過小"
+
 #: commit-graph.c
 msgid "commit-graph chain does not match"
 msgstr "提交圖形鏈不符合"
 
+#: commit-graph.c
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "基礎圖 (base graph) 中的提交數過多:%<PRIuMAX>"
+
 #: commit-graph.c
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
@@ -17299,6 +17742,10 @@ msgstr "無法找到提交 %s"
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "提交圖需要比目前更多的世代資料,但沒有相關資料"
 
+#: commit-graph.c
+msgid "commit-graph overflow generation data is too small"
+msgstr "提交圖形的溢出世代資料過小"
+
 #: commit-graph.c
 msgid "Loading known commits in commit graph"
 msgstr "正在載入提交圖中的已知提交"
@@ -17382,6 +17829,16 @@ msgstr "無法重新命名基礎提交圖形檔案"
 msgid "failed to rename temporary commit-graph file"
 msgstr "無法重新命名暫時提交圖形檔案"
 
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "無法將圖與 %<PRIuMAX>, %<PRIuMAX> 個提交進行合併"
+
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "無法合併 %s 圖,太多提交:%<PRIuMAX>"
+
 #: commit-graph.c
 msgid "Scanning merged commits"
 msgstr "正在掃描合併提交"
@@ -17417,10 +17874,6 @@ msgstr "提交圖形有不正確的扇出值:fanout[%d] = %u != %u"
 msgid "failed to parse commit %s from commit-graph"
 msgstr "無法從提交圖形中解析提交 %s"
 
-#: commit-graph.c
-msgid "Verifying commits in commit graph"
-msgstr "正在驗證提交圖中的提交"
-
 #: commit-graph.c
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
@@ -17448,25 +17901,24 @@ msgstr "提交 %s 的提交圖形父提交列表過早終止"
 
 #: commit-graph.c
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr "提交圖形中提交 %s 的世代號是零,但其它地方非零"
+msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
+msgstr "提交 %s 的提交圖形處於 %<PRIuMAX> < %<PRIuMAX> 世代"
 
 #: commit-graph.c
 #, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr "提交圖形中提交 %s 的世代號非零,但其它地方是零"
+msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
+msgstr "提交圖形中提交 %s 的提交日期是 %<PRIuMAX> != %<PRIuMAX>"
 
 #: commit-graph.c
 #, c-format
-msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
-msgstr "提交 %s 的提交圖形處於 %<PRIuMAX> < %<PRIuMAX> 世代"
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr "提交圖形中包含 0 和非 0 兩個世代號(例如 “%s” 和 “%s” 提交)"
 
 #: commit-graph.c
-#, c-format
-msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
-msgstr "提交圖形中提交 %s 的提交日期是 %<PRIuMAX> != %<PRIuMAX>"
+msgid "Verifying commits in commit graph"
+msgstr "正在驗證提交圖中的提交"
 
 #: commit.c
 #, c-format
@@ -17493,6 +17945,11 @@ msgstr ""
 "設定 \"git config advice.graftFileDeprecated false\"\n"
 "可關閉本消息"
 
+#: commit.c
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr "%s 提交在提交圖形中,但不在物件資料庫中"
+
 #: commit.c
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
@@ -17571,6 +18028,11 @@ msgstr "“has_worktree_moved” 中有未處置的情況:%d"
 msgid "health thread wait failed [GLE %ld]"
 msgstr "健康監聽執行緒等待失敗 [GLE %ld]"
 
+#: compat/fsmonitor/fsm-ipc-darwin.c
+#, c-format
+msgid "Invalid path: %s"
+msgstr "無效路徑:%s"
+
 #: compat/fsmonitor/fsm-listen-darwin.c
 msgid "Unable to create FSEventStream."
 msgstr "無法建立 FSEventStream。"
@@ -17609,12 +18071,32 @@ msgstr "在 “%s” 上呼叫 GetOverlappedResult 失敗 [GLE %ld]"
 msgid "could not read directory changes [GLE %ld]"
 msgstr "無法讀取目錄變化 [GLE %ld]"
 
-#: compat/fsmonitor/fsm-settings-win32.c
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "opendir('%s') 失敗"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "lstat('%s') 失敗"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "strbuf_readlink('%s') 失敗"
+
+#: compat/fsmonitor/fsm-path-utils-darwin.c
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "closedir('%s') 失敗"
+
+#: compat/fsmonitor/fsm-path-utils-win32.c
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
 msgstr "[GLE %ld] 無法開啟以讀取「%ls」"
 
-#: compat/fsmonitor/fsm-settings-win32.c
+#: compat/fsmonitor/fsm-path-utils-win32.c
 #, c-format
 msgid "[GLE %ld] unable to get protocol information for '%ls'"
 msgstr "[GLE %ld] 無法取得「%ls」的通訊協定資訊"
@@ -17879,7 +18361,7 @@ msgstr "子模組資料物件 %2$s 中錯誤的設定行 %1$d"
 #: config.c
 #, c-format
 msgid "bad config line %d in command line %s"
-msgstr "令列 %2$s 中錯誤的設定行 %1$d"
+msgstr "令列 %2$s 中錯誤的設定行 %1$d"
 
 #: config.c
 #, c-format
@@ -17922,7 +18404,7 @@ msgstr "子模組資料 %3$s 中設定變數 '%2$s' 錯誤的取值 '%1$s':%4$
 #: config.c
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in command line %s: %s"
-msgstr "令列 %3$s 中設定變數 '%2$s' 錯誤的取值 '%1$s':%4$s"
+msgstr "令列 %3$s 中設定變數 '%2$s' 錯誤的取值 '%1$s':%4$s"
 
 #: config.c
 #, c-format
@@ -17965,8 +18447,8 @@ msgid "bad zlib compression level %d"
 msgstr "錯誤的 zlib 壓縮級別 %d"
 
 #: config.c
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar 應該是一個字元"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar 應該是一個 ASCII 字元"
 
 #: config.c
 #, c-format
@@ -18011,14 +18493,9 @@ msgstr "引用 '%s' 沒有指向一個資料物件"
 msgid "unable to resolve config blob '%s'"
 msgstr "不能解析設定物件 '%s'"
 
-#: config.c
-#, c-format
-msgid "failed to parse %s"
-msgstr "解析 %s 失敗"
-
 #: config.c
 msgid "unable to parse command-line config"
-msgstr "無法解析令列中的設定"
+msgstr "無法解析令列中的設定"
 
 #: config.c
 msgid "unknown error occurred while reading the configuration files"
@@ -18037,7 +18514,7 @@ msgstr "splitIndex.maxPercentChange 的取值 '%d' 應該介於 0 和 100 之間
 #: config.c
 #, c-format
 msgid "unable to parse '%s' from command-line config"
-msgstr "無法解析令列設定中的 '%s'"
+msgstr "無法解析令列設定中的 '%s'"
 
 #: config.c
 #, c-format
@@ -18104,6 +18581,11 @@ msgstr "不能設定 '%s' 為 '%s'"
 msgid "invalid section name: %s"
 msgstr "無效的小節名稱:%s"
 
+#: config.c
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "因為第 %2$<PRIuMAX> 列中 “%1$s” 的文字列太長,故拒絕運作"
+
 #: config.c
 #, c-format
 msgid "missing value for '%s'"
@@ -18172,17 +18654,26 @@ msgstr "伺服器指定的「%s」物件格式無效"
 
 #: connect.c
 #, c-format
-msgid "invalid ls-refs response: %s"
-msgstr "無效的 ls-refs 回應:%s"
+msgid "error on bundle-uri response line %d: %s"
+msgstr "在 bundle-uri 回應的第 %d 列發現錯誤:%s"
 
 #: connect.c
-msgid "expected flush after ref listing"
-msgstr "在引用列表之後應該有一個 flush 包"
+msgid "expected flush after bundle-uri listing"
+msgstr "在 bundle-uri 清單後應該有一個 flush 包"
 
 #: connect.c
 msgid "expected response end packet after ref listing"
 msgstr "在引用列表後預期要有回應結束封包"
 
+#: connect.c
+#, c-format
+msgid "invalid ls-refs response: %s"
+msgstr "無效的 ls-refs 回應:%s"
+
+#: connect.c
+msgid "expected flush after ref listing"
+msgstr "在引用列表之後應該有一個 flush 包"
+
 #: connect.c
 #, c-format
 msgid "protocol '%s' is not supported"
@@ -18559,10 +19050,6 @@ msgstr "無法寫入封存"
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base 跟範圍無法搭配運作"
 
-#: diff-lib.c
-msgid "--merge-base only works with commits"
-msgstr "--merge-base 只能跟提交搭配才能運作"
-
 #: diff-lib.c
 msgid "unable to get HEAD"
 msgstr "不能取得 HEAD"
@@ -18575,6 +19062,14 @@ msgstr "找不到合併基底"
 msgid "multiple merge bases found"
 msgstr "找到多個合併基底"
 
+#: diff-no-index.c
+msgid "cannot compare stdin to a directory"
+msgstr "無法比對 stdin 和目錄"
+
+#: diff-no-index.c
+msgid "cannot compare a named pipe to a directory"
+msgstr "無法比對命名管線 (pipe) 和目錄"
+
 #: diff-no-index.c
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<選項>] <路徑> <路徑>"
@@ -18639,6 +19134,15 @@ msgstr ""
 msgid "external diff died, stopping at %s"
 msgstr "外部 diff 離開,停止在 %s"
 
+#: diff.c
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow 明確要求只跟一個路徑規格"
+
+#: diff.c
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "--follow 不支援路徑規格魔法:%s"
+
 #: diff.c parse-options.c
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
@@ -18655,10 +19159,6 @@ msgid ""
 "options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"
 msgstr "「%s」和「%s」選項不得同時使用,請使用「%s」搭配「%s」、「%s」"
 
-#: diff.c
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow 明確要求只跟一個路徑規格"
-
 #: diff.c
 #, c-format
 msgid "invalid --stat value: %s"
@@ -18783,7 +19283,7 @@ msgstr "<參數1,參數2>..."
 #: diff.c
 msgid ""
 "output the distribution of relative amount of changes for each sub-directory"
-msgstr "輸出每個子目錄相對變更的分"
+msgstr "輸出每個子目錄相對變更的分"
 
 #: diff.c
 msgid "synonym for --dirstat=cumulative"
@@ -18895,6 +19395,10 @@ msgstr "輸出的每一行附加前綴"
 msgid "do not show any source or destination prefix"
 msgstr "不顯示任何來源和目的地前綴"
 
+#: diff.c
+msgid "use default prefixes a/ and b/"
+msgstr "使用預設的前置名稱 a/ 和 b/"
+
 #: diff.c
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "顯示指定行數的差異區塊間的上下文"
@@ -19242,7 +19746,7 @@ msgid ""
 "Please specify a directory on the command line"
 msgstr ""
 "無法猜到目錄名。\n"
-"請在令列指定一個目錄"
+"請在令列指定一個目錄"
 
 #: dir.c
 #, c-format
@@ -19264,6 +19768,16 @@ msgstr "不能從 '%s' 遷移 git 目錄到 '%s'"
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "提示:等待您的編輯器關閉檔案...%c"
 
+#: editor.c sequencer.c wrapper.c
+#, c-format
+msgid "could not write to '%s'"
+msgstr "不能寫入 '%s'"
+
+#: editor.c
+#, c-format
+msgid "could not edit '%s'"
+msgstr "無法編輯 '%s'"
+
 #: entry.c
 msgid "Filtering content"
 msgstr "過濾內容"
@@ -19573,8 +20087,9 @@ msgstr "虛擬版本庫 “%s” 與 fsmonitor 不相容"
 #: fsmonitor-settings.c
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
-msgstr "版本庫 “%s” 因缺少 Unix 通訊端而與 fsmonitor 不相容"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
+msgstr "通訊端 “%s” 因缺少 Unix 通訊端支援,而與 fsmonitor 不相容"
 
 #: git.c
 msgid ""
@@ -19583,16 +20098,14 @@ msgid ""
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
 "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 
 #: git.c
 msgid ""
@@ -19609,7 +20122,7 @@ msgstr ""
 #: git.c help.c
 #, c-format
 msgid "unsupported command listing type '%s'"
-msgstr "不支援的令列表類型 '%s'"
+msgstr "不支援的令列表類型 '%s'"
 
 #: git.c
 #, c-format
@@ -19621,11 +20134,6 @@ msgstr "未傳入目錄至「%s」選項\n"
 msgid "no namespace given for --namespace\n"
 msgstr "沒有為 --namespace 提供命名空間\n"
 
-#: git.c
-#, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "沒有為 --super-prefix 提供前綴\n"
-
 #: git.c
 #, c-format
 msgid "-c expects a configuration string\n"
@@ -19636,6 +20144,11 @@ msgstr "應為 -c 提供一個設定字串\n"
 msgid "no config key given for --config-env\n"
 msgstr "未傳入設定鍵至 --config-env\n"
 
+#: git.c
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "沒有為 --attr-source 提供屬性來源\n"
+
 #: git.c
 #, c-format
 msgid "unknown option: %s\n"
@@ -19760,8 +20273,13 @@ msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr "gpg.ssh.defaultKeyCommand 執行失敗:%s %s"
 
 #: gpg-interface.c
-msgid "gpg failed to sign the data"
-msgstr "gpg 無法為資料簽名"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg 無法簽名資料:\n"
+"%s"
 
 #: gpg-interface.c
 msgid "user.signingKey needs to be set for ssh signing"
@@ -19958,8 +20476,8 @@ msgstr[0] ""
 "最類似的指令有"
 
 #: help.c
-msgid "git version [<options>]"
-msgstr "git version [<選項>]"
+msgid "git version [--build-options]"
+msgstr "git version [--build-options]"
 
 #: help.c
 #, c-format
@@ -20251,13 +20769,13 @@ msgstr ""
 "%s"
 
 #: merge-ort.c merge-recursive.c
-msgid "Failed to execute internal merge"
+msgid "failed to execute internal merge"
 msgstr "無法執行內部合併"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "不能新增 %s 至物件庫"
+msgid "unable to add %s to database"
+msgstr "無法將 %s 加進資料庫"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
@@ -20422,7 +20940,7 @@ msgstr ""
 #. conflict in a submodule. The first argument is the submodule
 #. name, and the second argument is the abbreviated id of the
 #. commit that needs to be merged.  For example:
-#.  - go to submodule (mysubmodule), and either merge commit abc1234"
+#. - go to submodule (mysubmodule), and either merge commit abc1234"
 #.
 #: merge-ort.c
 #, c-format
@@ -20611,7 +21129,7 @@ msgstr "重新命名"
 
 #: merge-recursive.c
 msgid "renamed"
-msgstr "重新命名"
+msgstr "重新命名"
 
 #: merge-recursive.c
 #, c-format
@@ -20642,8 +21160,8 @@ msgstr "拒絕遺失未追蹤檔案 '%s',而是新增為 %s"
 #: merge-recursive.c
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
 "衝突(重新命名/重新命名):在分支 \"%3$s\" 中重新命名 \"%1$s\"->\"%2$s\",在"
 "分支 \"%6$s\" 中重新命名 \"%4$s\"->\"%5$s\"%7$s"
@@ -20756,6 +21274,14 @@ msgstr "讀取快取失敗"
 msgid "multi-pack-index OID fanout is of the wrong size"
 msgstr "多包索引的物件 ID fanout 大小錯誤"
 
+#: midx.c
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "多包索引 OID 查詢區塊的大小有誤"
+
+#: midx.c
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "多包索引的物件偏移區塊大小有誤"
+
 #: midx.c
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -20777,20 +21303,24 @@ msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "multi-pack-index 雜湊版本 %u 與版本 %u 不符合"
 
 #: midx.c
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "多包索引缺少必需的包名區塊"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "多包索引所需的封裝名稱區塊不存在或損壞"
+
+#: midx.c
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr "多包索引所需的 OID fanout 區塊不存在或損壞"
 
 #: midx.c
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "多包索引缺少必需的物件 ID fanout 區塊"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr "多包索引所需的 OID 查詢區塊不存在或損壞"
 
 #: midx.c
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "多包索引缺少必需的物件 ID 查詢區塊"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr "多包索引所需的物件偏移區塊不存在或損壞"
 
 #: midx.c
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "å¤\9aå\8c\85ç´¢å¼\95缺å°\91å¿\85é\9c\80ç\9a\84ç\89©ä»¶ä½\8d移å\8d\80å¡\8a"
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "å¤\9aå\8c\85ç´¢å¼\95ç\9a\84å°\81è£\9då\90\8d稱å\8d\80å¡\8aé\81\8eç\9f­"
 
 #: midx.c
 #, c-format
@@ -20806,6 +21336,10 @@ msgstr "錯的 pack-int-id:%u(共有 %u 個包)"
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr "多包索引儲存一個64位位移,但是 off_t 太小"
 
+#: midx.c
+msgid "multi-pack-index large offset out of bounds"
+msgstr "多包索引的最大偏移超出邊界"
+
 #: midx.c
 #, c-format
 msgid "failed to add packfile '%s'"
@@ -21032,11 +21566,6 @@ msgstr "無法規範化備用物件路徑:%s"
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr "%s:忽略備用物件庫,嵌套太深"
 
-#: object-file.c
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr "無法規範化物件目錄: %s"
-
 #: object-file.c
 msgid "unable to fdopen alternates lockfile"
 msgstr "無法 fdopen 取代鎖檔案"
@@ -21109,6 +21638,11 @@ msgstr "損壞的鬆散物件 '%s'"
 msgid "garbage at end of loose object '%s'"
 msgstr "鬆散物件 '%s' 後面有垃圾資料"
 
+#: object-file.c
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "無法打開鬆散物件 %s"
+
 #: object-file.c
 #, c-format
 msgid "unable to parse %s header"
@@ -21130,19 +21664,14 @@ msgstr "%s 的標頭過長,超出 %d 位元組"
 
 #: object-file.c
 #, c-format
-msgid "failed to read object %s"
-msgstr "讀取物件 %s 失敗"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "鬆散物件 %s(儲存在 %s)已損壞"
 
 #: object-file.c
 #, c-format
 msgid "replacement %s not found for %s"
 msgstr "找不到 %2$s 的替代 %1$s"
 
-#: object-file.c
-#, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "鬆散物件 %s(儲存在 %s)已損壞"
-
 #: object-file.c
 #, c-format
 msgid "packed object %s (stored in %s) is corrupt"
@@ -21158,10 +21687,6 @@ msgstr "無法寫檔案 %s"
 msgid "unable to set permission to '%s'"
 msgstr "無法為 '%s' 設定權限"
 
-#: object-file.c
-msgid "file write error"
-msgstr "檔案寫錯誤"
-
 #: object-file.c
 msgid "error when closing loose object file"
 msgstr "關閉鬆散物件檔案時發生錯誤"
@@ -21220,12 +21745,13 @@ msgid "cannot read object for %s"
 msgstr "不能讀取物件 %s"
 
 #: object-file.c
-msgid "corrupt commit"
-msgstr "損壞的提交"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "物件 fsck 失敗:%s"
 
 #: object-file.c
-msgid "corrupt tag"
-msgstr "æ\90\8då£\9eç\9a\84æ¨\99籤"
+msgid "refusing to create malformed object"
+msgstr "æ\8b\92çµ\95建ç«\8bæ ¼å¼\8fé\8c¯èª¤ç\9a\84ç\89©ä»¶"
 
 #: object-file.c
 #, c-format
@@ -21294,7 +21820,7 @@ msgstr "%s [無效物件]"
 #. TRANSLATORS: This is a line of ambiguous commit
 #. object output. E.g.:
 #. *
-#.    "deadbeef commit 2021-01-01 - Some Commit Message"
+#. "deadbeef commit 2021-01-01 - Some Commit Message"
 #.
 #: object-name.c
 #, c-format
@@ -21304,7 +21830,7 @@ msgstr "%s 提交 %s - %s"
 #. TRANSLATORS: This is a line of ambiguous
 #. tag object output. E.g.:
 #. *
-#.    "deadbeef tag 2022-01-01 - Some Tag Message"
+#. "deadbeef tag 2022-01-01 - Some Tag Message"
 #. *
 #. The second argument is the YYYY-MM-DD found
 #. in the tag.
@@ -21321,7 +21847,7 @@ msgstr "%s 標籤 %s - %s"
 #. tag object output where we couldn't parse
 #. the tag itself. E.g.:
 #. *
-#.    "deadbeef [bad tag, could not parse it]"
+#. "deadbeef [bad tag, could not parse it]"
 #.
 #: object-name.c
 #, c-format
@@ -21536,11 +22062,6 @@ msgstr "位圖包索引的 XOR 偏移無效"
 msgid "cannot fstat bitmap file"
 msgstr "無法 fstat 位圖檔案"
 
-#: pack-bitmap.c
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "忽略多出來的位圖檔案:「%s」"
-
 #: pack-bitmap.c
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "總和檢查碼在 MIDX 和位圖中無符合項目"
@@ -21624,6 +22145,11 @@ msgstr "在「%2$s」封包,位移 %3$<PRIuMAX> 的地方找不到「%1$s」"
 msgid "unable to get disk usage of '%s'"
 msgstr "無法取得「%s」的磁碟用量"
 
+#: pack-bitmap.c
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "“%s” 位圖檔案的總和檢查碼無效"
+
 #: pack-mtimes.c
 #, c-format
 msgid "mtimes file %s is too small"
@@ -21666,13 +22192,26 @@ msgstr "%s 倒排索引檔案有未知簽章"
 
 #: pack-revindex.c
 #, c-format
-msgid "reverse-index file %s has unsupported version %<PRIu32>"
-msgstr "倒排索引檔案 %s 有不支援的版本 %<PRIu32>"
+msgid "reverse-index file %s has unsupported version %<PRIu32>"
+msgstr "倒排索引檔案 %s 有不支援的版本 %<PRIu32>"
+
+#: pack-revindex.c
+#, c-format
+msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
+msgstr "倒排索引檔案 %s 有不支援的雜湊 ID %<PRIu32>"
+
+#: pack-revindex.c
+msgid "invalid checksum"
+msgstr "無效的總和檢查碼"
+
+#: pack-revindex.c
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "%<PRIu64> 位置的修訂版索引 (rev-index) 無效:%<PRIu32> != %<PRIu32>"
 
 #: pack-revindex.c
-#, c-format
-msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
-msgstr "倒排索引檔案 %s 有不支援的雜湊 ID %<PRIu32>"
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "多包索引的反向索引區塊大小有誤"
 
 #: pack-write.c
 msgid "cannot both write and verify reverse index"
@@ -21737,16 +22276,6 @@ msgstr "選項「%s」期望「%s」或「%s」"
 msgid "%s requires a value"
 msgstr "%s 需要一個值"
 
-#: parse-options.c
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s 與 %s 不相容"
-
-#: parse-options.c
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s:和其它的不相容"
-
 #: parse-options.c
 #, c-format
 msgid "%s takes no value"
@@ -21762,6 +22291,11 @@ msgstr "%s 不可用"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s 期望一個非負整數和一個可選的 k/m/g 後綴"
 
+#: parse-options.c
+#, c-format
+msgid "%s is incompatible with %s"
+msgstr "%s 與 %s 不相容"
+
 #: parse-options.c
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
@@ -21847,6 +22381,11 @@ msgstr "    %s"
 msgid "-NUM"
 msgstr "-數字"
 
+#: parse-options.c
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "--no-%s 的相反行為"
+
 #: parse-options.h
 msgid "expiry-date"
 msgstr "到期時間"
@@ -21867,6 +22406,10 @@ msgstr "更加安靜"
 msgid "use <n> digits to display object names"
 msgstr "用 <n> 位數字顯示物件名稱"
 
+#: parse-options.h
+msgid "prefixed path to initial superproject"
+msgstr "初始父專案的前綴路徑"
+
 #: parse-options.h
 msgid "how to strip spaces and #comments from message"
 msgstr "設定如何刪除提交說明裡的空格和 #備註"
@@ -21880,6 +22423,16 @@ msgid ""
 "with --pathspec-from-file, pathspec elements are separated with NUL character"
 msgstr "如使用 --pathspec-from-file,則 <路徑規格> 元件會使用 NUL 字元分隔"
 
+#: parse.c
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "「%2$s」的「%1$s」布林環境值無效"
+
+#: parse.c
+#, c-format
+msgid "failed to parse %s"
+msgstr "解析 %s 失敗"
+
 #: path.c
 #, c-format
 msgid "Could not make %s writable by group"
@@ -21936,6 +22489,11 @@ msgstr "路徑規格 '%2$s' 中包含未實現的神奇前綴 '%1$c'"
 msgid "%s: 'literal' and 'glob' are incompatible"
 msgstr "%s:'literal' 和 'glob' 不相容"
 
+#: pathspec.c
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "“%s” 在目錄樹之外"
+
 #: pathspec.c
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
@@ -22047,6 +22605,11 @@ msgstr "promisor-remote: 無法關閉 fetch 子處理程序的 stdin"
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr "promisor 遠端名稱不能以 '/' 開始:%s"
 
+#: promisor-remote.c
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "無法從承諾者遠端抓取 %s"
+
 #: protocol-caps.c
 msgid "object-info: expected flush after arguments"
 msgstr "object-info:引數後預期要有 flush"
@@ -22089,6 +22652,15 @@ msgstr "生成 diff 失敗"
 msgid "could not parse log for '%s'"
 msgstr "不能解析 '%s' 的日誌"
 
+#: reachable.c
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "無效的額外廢棄提交修訂版:“%s”"
+
+#: reachable.c
+msgid "unable to enumerate additional recent objects"
+msgstr "無法列舉多出來的近期物件"
+
 #: read-cache.c
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
@@ -22252,11 +22824,6 @@ msgstr "無法寫入稀疏索引的索引分割"
 msgid "failed to convert to a sparse-index"
 msgstr "無法轉換成稀疏索引"
 
-#: read-cache.c
-#, c-format
-msgid "could not stat '%s'"
-msgstr "不能對 '%s' 呼叫 stat"
-
 #: read-cache.c
 #, c-format
 msgid "unable to open git dir: %s"
@@ -22277,6 +22844,16 @@ msgstr "不能修復 '%s' 的權限位"
 msgid "%s: cannot drop to stage #0"
 msgstr "%s:不能落到暫存區 #0"
 
+#: read-cache.c
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "非預期的 diff 狀態 %c"
+
+#: read-cache.c
+#, c-format
+msgid "remove '%s'\n"
+msgstr "移除 “%s”\n"
+
 #: rebase-interactive.c
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
@@ -22443,6 +23020,16 @@ msgstr "落後 %d"
 msgid "ahead %d, behind %d"
 msgstr "領先 %d,落後 %d"
 
+#: ref-filter.c
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) 不取引數"
+
+#: ref-filter.c
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "無法識別的 %%(%.*s) 引數:%s"
+
 #: ref-filter.c
 #, c-format
 msgid "expected format: %%(color:<color>)"
@@ -22465,49 +23052,44 @@ msgstr "期望整數值 refname:rstrip=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "無法識別的 %%(%s) 參數:%s"
+msgid "expected %%(trailers:key=<value>)"
+msgstr "預期是 %%(trailers:key=<value>)"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) 不帶參數"
+msgid "unknown %%(trailers) argument: %s"
+msgstr "未知的 %%(trailers) 參數:%s"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) 不帶參數"
+msgid "positive value expected contents:lines=%s"
+msgstr "期望一個正數 contents:lines=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) 不帶參數"
+msgid "argument expected for %s"
+msgstr "引數預期 %s"
 
 #: ref-filter.c
 #, c-format
-msgid "expected %%(trailers:key=<value>)"
-msgstr "預期是 %%(trailers:key=<value>)"
+msgid "positive value expected %s=%s"
+msgstr "期望一個正數 %s=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "unknown %%(trailers) argument: %s"
-msgstr "未知的 %%(trailers) 參數:%s"
+msgid "cannot fully parse %s=%s"
+msgstr "無法完全解析 %s=%s"
 
 #: ref-filter.c
 #, c-format
-msgid "positive value expected contents:lines=%s"
-msgstr "æ\9c\9fæ\9c\9bä¸\80å\80\8bæ­£æ\95¸ contents:lines=%s"
+msgid "value expected %s="
+msgstr "æ\95¸å\80¼é \90æ\9c\9f %s="
 
 #: ref-filter.c
 #, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "%%(%2$s) 中的 '%1$s' 預期是正數值"
 
-#: ref-filter.c
-#, c-format
-msgid "unrecognized email option: %s"
-msgstr "無法識別的 email 選項:%s"
-
 #: ref-filter.c
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
@@ -22523,6 +23105,11 @@ msgstr "無法識別的位置:%s"
 msgid "unrecognized width:%s"
 msgstr "無法識別的寬度:%s"
 
+#: ref-filter.c
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "無法識別的 %%(%s) 參數:%s"
+
 #: ref-filter.c
 #, c-format
 msgid "positive width expected with the %%(align) atom"
@@ -22530,8 +23117,8 @@ msgstr "元素 %%(align) 需要一個正數的寬度"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) 未取引數"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "預期格式:%%(ahead-behind:<committish>)"
 
 #: ref-filter.c
 #, c-format
@@ -22589,6 +23176,10 @@ msgstr "本命令拒絕 atom %%(%.*s)"
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s 不能和 --python、--shell、--tcl 一起使用"
 
+#: ref-filter.c
+msgid "failed to run 'describe'"
+msgstr "無法執行 “describe”"
+
 #: ref-filter.c
 #, c-format
 msgid "(no branch, rebasing %s)"
@@ -22666,6 +23257,10 @@ msgstr "key"
 msgid "field name to sort on"
 msgstr "排序的欄位名"
 
+#: ref-filter.h
+msgid "exclude refs which match pattern"
+msgstr "排除符合模式的引用"
+
 #: reflog.c
 #, c-format
 msgid "not a reflog: %s"
@@ -23195,8 +23790,9 @@ msgstr[0] ""
 
 #  譯者:請維持前導空格
 #: remote.c
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (使用 \"git pull\" 來合併遠端分支)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr "  (使用 “git pull” 來將遠端分支整合進您的分支)\n"
 
 #: remote.c
 #, c-format
@@ -23269,7 +23865,7 @@ msgstr "使用之前的解決方案解決 '%s'。"
 #: rerere.c
 #, c-format
 msgid "cannot unlink stray '%s'"
-msgstr "不能刪除 stray '%s'"
+msgstr "無法刪除失散檔案 “%s”"
 
 #: rerere.c
 #, c-format
@@ -23286,11 +23882,6 @@ msgstr "更新 '%s' 中的衝突狀態失敗"
 msgid "no remembered resolution for '%s'"
 msgstr "沒有為 '%s' 記憶的解決方案"
 
-#: rerere.c
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "不能刪除 '%s'"
-
 #: rerere.c
 #, c-format
 msgid "Updated preimage for '%s'"
@@ -23318,6 +23909,15 @@ msgstr "不能確定 HEAD 版本"
 msgid "failed to find tree of %s"
 msgstr "無法找到 %s 指向的樹"
 
+#: revision.c
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "不支援的隱藏引用區塊:%s"
+
+#: revision.c
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= 傳入了不止一次"
+
 #: revision.c
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
@@ -23332,6 +23932,11 @@ msgstr "無法取得 ancestry-path 引數 %s 的提交"
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<packfile> 已不受支援"
 
+#: revision.c
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "在 --stdin 模式下,“%s” 選項無效"
+
 #: revision.c
 msgid "your current branch appears to be broken"
 msgstr "您的目前分支好像被損壞"
@@ -23443,8 +24048,16 @@ msgid "only download metadata for the branch that will be checked out"
 msgstr "只下載會簽出的分支中介資料"
 
 #: scalar.c
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<options>] [--] <repo> [<dir>]"
+msgid "create repository within 'src' directory"
+msgstr "在 “src” 目錄建立版本庫"
+
+#: scalar.c
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
 
 #: scalar.c
 #, c-format
@@ -23505,8 +24118,37 @@ msgstr "--all 或 <enlistment> 但不能傳入兩者"
 
 #: scalar.c
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "git 版本庫在「%s」遺失"
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "無法移除過時的 scalar.repo “%s”"
+
+#: scalar.c
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "已移除過時的 scalar.repo “%s”"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "位於 “%s” 的版本庫有不同的擁有者"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "位於 “%s” 的版本庫有格式問題"
+
+#: scalar.c
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "版本庫不在 “%s”"
+
+#: scalar.c
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"如果要從 Scalar 解除這個版本庫的註冊,請執行\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 #: scalar.c
 msgid ""
@@ -23636,7 +24278,7 @@ msgstr "摘取"
 
 #: sequencer.c
 msgid "rebase"
-msgstr "rebase"
+msgstr "重定基底"
 
 #: sequencer.c
 #, c-format
@@ -23688,11 +24330,6 @@ msgstr ""
 msgid "could not lock '%s'"
 msgstr "不能鎖定 '%s'"
 
-#: sequencer.c strbuf.c wrapper.c
-#, c-format
-msgid "could not write to '%s'"
-msgstr "不能寫入 '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not write eol to '%s'"
@@ -23710,7 +24347,7 @@ msgstr "您的本機修改將被%s覆蓋。"
 
 #: sequencer.c
 msgid "commit your changes or stash them to proceed."
-msgstr "提交您的修改或儲藏後再繼續。"
+msgstr "提交您的修改或貯存後再繼續。"
 
 #. TRANSLATORS: %s will be "revert", "cherry-pick" or
 #. "rebase".
@@ -23990,11 +24627,6 @@ msgstr "不能得到 %s 的提交說明"
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s:不能解析父提交 %s"
 
-#: sequencer.c
-#, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "不能將 '%s' 重新命名為 '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not revert %s... %s"
@@ -24020,6 +24652,26 @@ msgstr "git %s:無法讀取索引"
 msgid "git %s: failed to refresh the index"
 msgstr "git %s:無法重新整理索引"
 
+#: sequencer.c
+#, c-format
+msgid "'%s' is not a valid label"
+msgstr "“%s” 不是有效的標籤"
+
+#: sequencer.c
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr "“%s” 不是有效的引用名稱"
+
+#: sequencer.c
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr "update-ref 需要完全限定的引用名稱,比如:refs/heads/%s"
+
+#: sequencer.c
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "無效的命令 “%.*s”"
+
 #: sequencer.c
 #, c-format
 msgid "%s does not accept arguments: '%s'"
@@ -24110,10 +24762,6 @@ msgstr "嘗試 \"git cherry-pick (--continue | %s--abort | --quit)\""
 msgid "could not create sequencer directory '%s'"
 msgstr "不能建立序列目錄 '%s'"
 
-#: sequencer.c
-msgid "could not lock HEAD"
-msgstr "不能鎖定 HEAD"
-
 #: sequencer.c
 msgid "no cherry-pick or revert in progress"
 msgstr "揀選或還原動作並未進行"
@@ -24229,22 +24877,22 @@ msgstr ""
 "\n"
 
 #: sequencer.c
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "並且修改索引和/或工作區\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "並且更改索引和(或)工作區。\n"
 
 #: sequencer.c
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "執行成功:%s\n"
-"但是在索引和/或工作區中存在變更\n"
-"提交或儲藏修改,然後執行\n"
+"但是在索引和(或)工作區中存在變更。\n"
+"請提交或貯存修改,然後執行\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
@@ -24254,6 +24902,11 @@ msgstr ""
 msgid "illegal label name: '%.*s'"
 msgstr "非法的標籤名稱:'%.*s'"
 
+#: sequencer.c
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "無法解析 '%s'"
+
 #: sequencer.c
 msgid "writing fake root commit"
 msgstr "寫偽根提交"
@@ -24262,11 +24915,6 @@ msgstr "寫偽根提交"
 msgid "writing squash-onto"
 msgstr "寫入 squash-onto"
 
-#: sequencer.c
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "無法解析 '%s'"
-
 #: sequencer.c
 msgid "cannot merge without a current revision"
 msgstr "沒有目前版本不能合併"
@@ -24364,16 +25012,16 @@ msgid ""
 "You can run \"git stash pop\" or \"git stash drop\" at any time.\n"
 msgstr ""
 "%s\n"
-"您的修改安全地儲存在儲藏區中。\n"
+"您的修改安全地儲存在貯存區中。\n"
 "您可以在任何時候執行 \"git stash pop\" 或 \"git stash drop\"。\n"
 
 #: sequencer.c
 msgid "Applying autostash resulted in conflicts."
-msgstr "因套用自動儲藏而導致衝突。"
+msgstr "因套用自動貯存而導致衝突。"
 
 #: sequencer.c
 msgid "Autostash exists; creating a new stash entry."
-msgstr "已有自動儲藏;建立新儲藏項目。"
+msgstr "已有自動貯存;建立新貯存項目。"
 
 #: sequencer.c
 msgid "could not detach HEAD"
@@ -24411,13 +25059,13 @@ msgstr ""
 
 #: sequencer.c
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "正在重定基底 (%d/%d)%s"
+msgid "Stopped at %s...  %.*s\n"
+msgstr "停止在 %s... %.*s\n"
 
 #: sequencer.c
 #, c-format
-msgid "Stopped at %s...  %.*s\n"
-msgstr "停止在 %s... %.*s\n"
+msgid "Rebasing (%d/%d)%s"
+msgstr "正在重定基底 (%d/%d)%s"
 
 #: sequencer.c
 #, c-format
@@ -24632,75 +25280,164 @@ msgstr "不是一個 git 版本庫:'%s'"
 
 #: setup.c
 #, c-format
-msgid "cannot chdir to '%s'"
-msgstr "不能切換目錄到 '%s'"
+msgid "cannot chdir to '%s'"
+msgstr "不能切換目錄到 '%s'"
+
+#: setup.c
+msgid "cannot come back to cwd"
+msgstr "無法返回目前工作目錄"
+
+#: setup.c
+#, c-format
+msgid "failed to stat '%*s%s%s'"
+msgstr "取得 '%*s%s%s' 狀態(stat)失敗"
+
+#: setup.c
+msgid "Unable to read current working directory"
+msgstr "不能讀取目前工作目錄"
+
+#: setup.c
+#, c-format
+msgid "cannot change to '%s'"
+msgstr "不能切換到 '%s'"
+
+#: setup.c
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "不是一個 git 版本庫(或者任何父目錄):%s"
+
+#: setup.c
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"不是一個 git 版本庫(或者直至掛載點 %s 的任何父目錄)\n"
+"停止在檔案系統邊界(未設定 GIT_DISCOVERY_ACROSS_FILESYSTEM)。"
+
+#: setup.c
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"在位於「%s」的版本庫偵測到可疑所有權\n"
+"%s若要放行本目錄,請呼叫:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#: setup.c
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr "無法使用 “%s” 純版本庫(safe.bareRepository 是 “%s”)"
+
+#: setup.c
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"參數 core.sharedRepository 的檔案屬性值有問題(0%.3o)。\n"
+"檔案所有者必須始終擁有讀寫權限。"
+
+#: setup.c
+msgid "fork failed"
+msgstr "fork 失敗"
+
+#: setup.c
+msgid "setsid failed"
+msgstr "setsid 失敗"
+
+#: setup.c
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "不能對範本 '%s' 呼叫 stat"
+
+#: setup.c
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "不能開啟目錄 '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "不能讀取連結 '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "不能自 '%s' 到 '%s' 建立符號連結"
+
+#: setup.c
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "不能複製 '%s' 至 '%s'"
+
+#: setup.c
+#, c-format
+msgid "ignoring template %s"
+msgstr "忽略範本 %s"
+
+#: setup.c
+#, c-format
+msgid "templates not found in %s"
+msgstr "沒有在 %s 中找到範本"
 
 #: setup.c
-msgid "cannot come back to cwd"
-msgstr "無法返回目前工作目錄"
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "沒有從 '%s' 複製範本:%s"
 
 #: setup.c
 #, c-format
-msgid "failed to stat '%*s%s%s'"
-msgstr "取得 '%*s%s%s' 狀態(stat)失敗"
+msgid "invalid initial branch name: '%s'"
+msgstr "無效的初始分支名稱:'%s'"
 
 #: setup.c
-msgid "Unable to read current working directory"
-msgstr "不能讀取目前工作目錄"
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "不能處理 %d 類型的檔案"
 
 #: setup.c
 #, c-format
-msgid "cannot change to '%s'"
-msgstr "不能切換到 '%s'"
+msgid "unable to move %s to %s"
+msgstr "不能移動 %s 至 %s"
 
 #: setup.c
-#, c-format
-msgid "not a git repository (or any of the parent directories): %s"
-msgstr "不是一個 git 版本庫(或者任何父目錄):%s"
+msgid "attempt to reinitialize repository with different hash"
+msgstr "嘗試以不同的雜湊值重新初始化版本庫"
 
 #: setup.c
 #, c-format
-msgid ""
-"not a git repository (or any parent up to mount point %s)\n"
-"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
-msgstr ""
-"不是一個 git 版本庫(或者直至掛載點 %s 的任何父目錄)\n"
-"停止在檔案系統邊界(未設定 GIT_DISCOVERY_ACROSS_FILESYSTEM)。"
+msgid "%s already exists"
+msgstr "%s 已經存在"
 
 #: setup.c
 #, c-format
-msgid ""
-"detected dubious ownership in repository at '%s'\n"
-"%sTo add an exception for this directory, call:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
-msgstr ""
-"在位於「%s」的版本庫偵測到可疑所有權\n"
-"%s若要放行本目錄,請呼叫:\n"
-"\n"
-"\tgit config --global --add safe.directory %s"
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: 忽略 --initial-branch=%s"
 
 #: setup.c
 #, c-format
-msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr "無法使用 “%s” 純版本庫(safe.bareRepository 是 “%s”)"
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "重新初始化已存在的共享 Git 版本庫於 %s%s\n"
 
 #: setup.c
 #, c-format
-msgid ""
-"problem with core.sharedRepository filemode value (0%.3o).\n"
-"The owner of files must always have read and write permissions."
-msgstr ""
-"參數 core.sharedRepository 的檔案屬性值有問題(0%.3o)。\n"
-"檔案所有者必須始終擁有讀寫權限。"
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "重新初始化已存在的 Git 版本庫於 %s%s\n"
 
 #: setup.c
-msgid "fork failed"
-msgstr "fork 失敗"
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "已初始化空的共享 Git 版本庫於 %s%s\n"
 
 #: setup.c
-msgid "setsid failed"
-msgstr "setsid 失敗"
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "已初始化空的 Git 版本庫於 %s%s\n"
 
 #: sparse-index.c
 #, c-format
@@ -24761,11 +25498,6 @@ msgid "%u byte/s"
 msgid_plural "%u bytes/s"
 msgstr[0] "%u 位元組/秒"
 
-#: strbuf.c
-#, c-format
-msgid "could not edit '%s'"
-msgstr "無法編輯 '%s'"
-
 #: submodule-config.c
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
@@ -24778,7 +25510,7 @@ msgstr "submodule.fetchJobs 不允許為負值"
 #: submodule-config.c
 #, c-format
 msgid "ignoring '%s' which may be interpreted as a command-line option: %s"
-msgstr "忽略可能被解析為令列選項的 '%s':%s"
+msgstr "忽略可能被解析為令列選項的 '%s':%s"
 
 #: submodule-config.c
 #, c-format
@@ -24976,6 +25708,31 @@ msgstr "ls-tree 返回未知返回值 %d"
 msgid "failed to lstat '%s'"
 msgstr "無法 lstat “%s”"
 
+#: t/helper/test-bundle-uri.c
+msgid "no remote configured to get bundle URIs from"
+msgstr "沒有設定可以用來取得套件包 URIs 的遠端"
+
+#: t/helper/test-bundle-uri.c
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "“%s” 遠端未設定 URL"
+
+#: t/helper/test-bundle-uri.c
+msgid "could not get the bundle-uri list"
+msgstr "無法取得 bundle-uri 清單"
+
+#: t/helper/test-cache-tree.c
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <options> (control|prime|update)"
+
+#: t/helper/test-cache-tree.c
+msgid "clear the cache tree before each iteration"
+msgstr "每次迭代前清除快取樹狀物件"
+
+#: t/helper/test-cache-tree.c
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr "在快取樹狀物件中,要使失效的項目數量(預設值為 0)"
+
 #: t/helper/test-fast-rebase.c
 msgid "unhandled options"
 msgstr "未處理選項"
@@ -25317,7 +26074,7 @@ msgstr "將要設定 '%1$s' 的上游為 '%3$s' 的 '%2$s'\n"
 #: transport.c
 #, c-format
 msgid "could not read bundle '%s'"
-msgstr "無法讀取「%s」套件"
+msgstr "無法讀取「%s」套件"
 
 #: transport.c
 #, c-format
@@ -25400,6 +26157,14 @@ msgstr "正在終止。"
 msgid "failed to push all needed submodules"
 msgstr "不能推送全部需要的子模組"
 
+#: transport.c
+msgid "bundle-uri operation not supported by protocol"
+msgstr "通訊協定不支援 bundle-uri 動作"
+
+#: transport.c
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "無法取得伺服器公佈的 bundle-uri 清單"
+
 #: tree-walk.c
 msgid "too-short tree object"
 msgstr "太短的樹狀物件"
@@ -25423,7 +26188,7 @@ msgid ""
 "%%sPlease commit your changes or stash them before you switch branches."
 msgstr ""
 "您對下列檔案的本機修改將被簽出動作覆蓋:\n"
-"%%s請在切換分支前提交或儲藏您的修改。"
+"%%s請在切換分支前提交或貯存您的修改。"
 
 #: unpack-trees.c
 #, c-format
@@ -25441,7 +26206,7 @@ msgid ""
 "%%sPlease commit your changes or stash them before you merge."
 msgstr ""
 "您對下列檔案的本機修改將被合併動作覆蓋:\n"
-"%%s請在合併前提交或儲藏您的修改。"
+"%%s請在合併前提交或貯存您的修改。"
 
 #: unpack-trees.c
 #, c-format
@@ -25459,7 +26224,7 @@ msgid ""
 "%%sPlease commit your changes or stash them before you %s."
 msgstr ""
 "您對下列檔案的本機修改將被 %s 覆蓋:\n"
-"%%s請在 %s 之前提交或儲藏您的修改。"
+"%%s請在 %s 之前提交或貯存您的修改。"
 
 #: unpack-trees.c
 #, c-format
@@ -25710,19 +26475,19 @@ msgstr "無效的 '..' 路徑區塊"
 
 #: usage.c
 msgid "usage: "
-msgstr "用法"
+msgstr "用法"
 
 #: usage.c
 msgid "fatal: "
-msgstr "致命錯誤"
+msgstr "致命錯誤"
 
 #: usage.c
 msgid "error: "
-msgstr "錯誤"
+msgstr "錯誤"
 
 #: usage.c
 msgid "warning: "
-msgstr "警告"
+msgstr "警告"
 
 #: walker.c
 msgid "Fetching objects"
@@ -25852,6 +26617,10 @@ msgstr "不能存取 '%s'"
 msgid "unable to get current working directory"
 msgstr "不能取得目前工作目錄"
 
+#: wrapper.c
+msgid "unable to get random bytes"
+msgstr "無法取得隨機位元組"
+
 #: wt-status.c
 msgid "Unmerged paths:"
 msgstr "未合併的路徑:"
@@ -26001,7 +26770,7 @@ msgstr "未追蹤的內容, "
 #, c-format
 msgid "Your stash currently has %d entry"
 msgid_plural "Your stash currently has %d entries"
-msgstr[0] "您的儲藏區目前有 %d 條紀錄"
+msgstr[0] "您的貯存區目前有 %d 條紀錄"
 
 #: wt-status.c
 msgid "Submodules changed but not updated:"
@@ -26314,12 +27083,20 @@ msgstr "忽略的檔案"
 #: wt-status.c
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
 msgstr ""
-"耗費了 %.2f 秒以枚舉未追蹤的檔案。'status -uno' 也許能提高速度,\n"
-"但您需要小心不要忘了新增新檔案(參見 'git help status')。"
+"列舉未追蹤檔案花費 %.2f 秒,\n"
+"不過已經快取結果,之後執行的速度或許能比較快。"
+
+#: wt-status.c
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "列舉未追蹤檔案花費 %.2f 秒。"
+
+#: wt-status.c
+msgid "See 'git help status' for information on how to improve this."
+msgstr "請參閱 “git help status” 深入了解如何改善。"
 
 #: wt-status.c
 #, c-format
@@ -26495,297 +27272,6 @@ msgstr "您需要在工作區的頂級目錄中執行這個指令。"
 msgid "Unable to determine absolute path of git directory"
 msgstr "不能確定 git 目錄的絕對路徑"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#: git-add--interactive.perl
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %12s %s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "建立了 %d 個路徑\n"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為暫存。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為儲藏。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為未暫存。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr "如果修補檔能乾淨地套用,編輯區塊將立即標記為套用。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr "如果修補檔能乾淨地套用,編輯塊將立即標記為捨棄。"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "為寫入開啟區塊編輯檔案失敗:%s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"要刪除 '%s' 開始的行,使其成為 ' ' 開始的行(上下文)。\n"
-"要刪除 '%s' 開始的行,刪除它們。\n"
-"以 %s 開始的行將被刪除。\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "無法讀取區塊編輯檔案:%s"
-
-#: git-add--interactive.perl
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 暫存此區塊\n"
-"n - 不要暫存此區塊\n"
-"q - 離開。不暫存此區塊及後面的全部區塊\n"
-"a - 暫存此區塊和本檔案中後面的全部區塊\n"
-"d - 不暫存此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 儲藏此區塊\n"
-"n - 不要儲藏此區塊\n"
-"q - 離開。不儲藏此區塊及後面的全部區塊\n"
-"a - 儲藏此區塊和本檔案中後面的全部區塊\n"
-"d - 不儲藏此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 不暫存此區塊\n"
-"n - 不要不暫存此區塊\n"
-"q - 離開。不要不暫存此區塊及後面的全部區塊\n"
-"a - 不暫存此區塊和本檔案中後面的全部區塊\n"
-"d - 不要不暫存此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引中套用此區塊\n"
-"n - 不要在索引中套用此區塊\n"
-"q - 離開。不要套用此區塊及後面的全部區塊\n"
-"a - 套用此區塊和本檔案中後面的全部區塊\n"
-"d - 不要套用此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在工作區中捨棄此區塊\n"
-"n - 不要在工作區中捨棄此區塊\n"
-"q - 離開。不要捨棄此區塊及後面的全部區塊\n"
-"a - 捨棄此區塊和本檔案中後面的全部區塊\n"
-"d - 不要捨棄此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引和工作區中捨棄此區塊\n"
-"n - 不要在索引和工作區中捨棄此區塊\n"
-"q - 離開。不要捨棄此區塊及後面的全部區塊\n"
-"a - 捨棄此區塊和本檔案中後面的全部區塊\n"
-"d - 不要捨棄此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引和工作區中套用此區塊\n"
-"n - 不要在索引和工作區中套用此區塊\n"
-"q - 離開。不要套用此區塊及後面的全部區塊\n"
-"a - 套用此區塊和本檔案中後面的全部區塊\n"
-"d - 不要套用此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在工作區中套用此區塊\n"
-"n - 不要在工作區中套用此區塊\n"
-"q - 離開。不要套用此區塊及後面的全部區塊\n"
-"a - 套用此區塊和本檔案中後面的全部區塊\n"
-"d - 不要套用此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"j - leave this hunk undecided, see next undecided hunk\n"
-"J - leave this hunk undecided, see next hunk\n"
-"k - leave this hunk undecided, see previous undecided hunk\n"
-"K - leave this hunk undecided, see previous hunk\n"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - 選擇跳轉到一個區塊\n"
-"/ - 尋找和提供常規表示式符合的區塊\n"
-"j - 維持此區塊未決狀態,檢視下一個未決定區塊\n"
-"J - 維持此區塊未決狀態,檢視下一個區塊\n"
-"k - 維持此區塊未決狀態,檢視上一個未決定區塊\n"
-"K - 維持此區塊未決狀態,檢視上一個區塊\n"
-"s - 分割目前區塊為更小的區塊\n"
-"e - 手動編輯目前區塊\n"
-"? - 顯示說明\n"
-
-#: git-add--interactive.perl
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "選取區塊不能套用到索引!\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "忽略未套用的:%s\n"
-
-#: git-add--interactive.perl
-msgid "No other hunks to goto\n"
-msgstr "沒有其它可供跳轉的區塊\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "無效數字:'%s'\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "對不起,只有 %d 個可用區塊。\n"
-
-#: git-add--interactive.perl
-msgid "No other hunks to search\n"
-msgstr "沒有其它可供尋找的區塊\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "錯誤的常規表示式 %s:%s\n"
-
-#: git-add--interactive.perl
-msgid "No hunk matches the given pattern\n"
-msgstr "沒有和提供模式相符合的區塊\n"
-
-#: git-add--interactive.perl
-msgid "No previous hunk\n"
-msgstr "沒有上一個區塊\n"
-
-#: git-add--interactive.perl
-msgid "No next hunk\n"
-msgstr "沒有下一個區塊\n"
-
-#: git-add--interactive.perl
-msgid "Sorry, cannot split this hunk\n"
-msgstr "對不起,不能分割這個區塊\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "分割為 %d 塊。\n"
-
-#: git-add--interactive.perl
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "對不起,不能編輯這個區塊\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-#: git-add--interactive.perl
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - 顯示含變更的路徑\n"
-"update        - 新增工作區狀態至暫存列表\n"
-"revert        - 還原修改的暫存集至 HEAD 版本\n"
-"patch         - 挑選區塊並且有選擇地更新\n"
-"diff          - 顯示 HEAD 和索引間差異\n"
-"add untracked - 新增未追蹤檔案的內容至暫存列表\n"
-
-#: git-add--interactive.perl
-msgid "missing --"
-msgstr "缺少 --"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "未知的 --patch 模式:%s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "無效的參數 %s,期望是 --"
-
 #: git-send-email.perl
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "本機時間和 GMT 有不到一分鐘間隔\n"
@@ -26801,7 +27287,7 @@ msgstr "致命錯誤:命令「%s」中止,結束碼:%d"
 
 #: git-send-email.perl
 msgid "the editor exited uncleanly, aborting everything"
-msgstr "編輯器非正常離開,止所有動作"
+msgstr "編輯器非正常離開,止所有動作"
 
 #: git-send-email.perl
 #, perl-format
@@ -26836,7 +27322,7 @@ msgstr "不能在版本庫之外執行 git format-patch\n"
 msgid ""
 "`batch-size` and `relogin` must be specified together (via command-line or "
 "configuration option)\n"
-msgstr "`batch-size` 和 `relogin` 必須同時定義(透過令列或者設定選項)\n"
+msgstr "`batch-size` 和 `relogin` 必須同時定義(透過令列或者設定選項)\n"
 
 #: git-send-email.perl
 #, perl-format
@@ -26923,13 +27409,13 @@ msgstr ""
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "無法開啟 %s: %s"
+msgid "Failed to open %s.final: %s"
+msgstr "無法開啟 %s.final: %s"
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s.final: %s"
-msgstr "無法開啟 %s.final: %s"
+msgid "Failed to open %s: %s"
+msgstr "無法開啟 %s: %s"
 
 #: git-send-email.perl
 msgid "Summary email is empty, skipping it\n"
@@ -27107,14 +27593,19 @@ msgstr "(%s) 不能執行 '%s'"
 
 #: git-send-email.perl
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) 新增 %s: %s 自:'%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) 從 “%s” 讀到格式錯誤的輸出"
 
 #: git-send-email.perl
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) 無法關閉管道至 '%s'"
 
+#: git-send-email.perl
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) 新增 %s: %s 自:'%s'\n"
+
 #: git-send-email.perl
 msgid "cannot send message as 7bit"
 msgstr "不能以 7bit 形式傳送訊息"
index 100f7a374dca1b8ffaae1ae9a2c00011d05a208a..63fd35d64b141f6f1488c40c4fb34b1fe1e53ae0 100644 (file)
@@ -1,14 +1,20 @@
 /*
  * Copyright (C) 2008 Linus Torvalds
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "pathspec.h"
 #include "dir.h"
+#include "environment.h"
 #include "fsmonitor.h"
-#include "config.h"
+#include "gettext.h"
+#include "parse.h"
+#include "preload-index.h"
 #include "progress.h"
+#include "read-cache.h"
 #include "thread-utils.h"
 #include "repository.h"
+#include "symlinks.h"
+#include "trace2.h"
 
 /*
  * Mostly randomly chosen maximum thread counts: we
diff --git a/preload-index.h b/preload-index.h
new file mode 100644 (file)
index 0000000..251b1ed
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef PRELOAD_INDEX_H
+#define PRELOAD_INDEX_H
+
+struct index_state;
+struct pathspec;
+struct repository;
+
+void preload_index(struct index_state *index,
+                  const struct pathspec *pathspec,
+                  unsigned int refresh_flags);
+int repo_read_index_preload(struct repository *,
+                           const struct pathspec *pathspec,
+                           unsigned refresh_flags);
+
+#endif /* PRELOAD_INDEX_H */
index 1e1e21878c833d54b165b0aa6d9720eb16536a94..cf964b060cd128e2bc271c07ed8bb8b4c28bfe29 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1,8 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "commit.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "utf8.h"
 #include "diff.h"
+#include "pager.h"
 #include "revision.h"
 #include "string-list.h"
 #include "mailmap.h"
@@ -13,6 +18,7 @@
 #include "gpg-interface.h"
 #include "trailer.h"
 #include "run-command.h"
+#include "object-name.h"
 
 /*
  * The limit for formatting directives, which enable the caller to append
@@ -51,6 +57,7 @@ static void save_user_format(struct rev_info *rev, const char *cp, int is_tforma
 }
 
 static int git_pretty_formats_config(const char *var, const char *value,
+                                    const struct config_context *ctx UNUSED,
                                     void *cb UNUSED)
 {
        struct cmt_fmt_map *commit_format = NULL;
@@ -719,7 +726,7 @@ const char *repo_logmsg_reencode(struct repository *r,
                 * Otherwise, we still want to munge the encoding header in the
                 * result, which will be done by modifying the buffer. If we
                 * are using a fresh copy, we can reuse it. But if we are using
-                * the cached copy from get_commit_buffer, we need to duplicate it
+                * the cached copy from repo_get_commit_buffer, we need to duplicate it
                 * to avoid munging the cached copy.
                 */
                if (msg == get_cached_commit_buffer(r, commit, NULL))
@@ -1245,6 +1252,27 @@ static int format_trailer_match_cb(const struct strbuf *key, void *ud)
        return 0;
 }
 
+static struct strbuf *expand_string_arg(struct strbuf *sb,
+                                       const char *argval, size_t arglen)
+{
+       char *fmt = xstrndup(argval, arglen);
+       const char *format = fmt;
+
+       strbuf_reset(sb);
+       while (strbuf_expand_step(sb, &format)) {
+               size_t len;
+
+               if (skip_prefix(format, "%", &format))
+                       strbuf_addch(sb, '%');
+               else if ((len = strbuf_expand_literal(sb, format)))
+                       format += len;
+               else
+                       strbuf_addch(sb, '%');
+       }
+       free(fmt);
+       return sb;
+}
+
 int format_set_trailers_options(struct process_trailer_options *opts,
                                struct string_list *filter_list,
                                struct strbuf *sepbuf,
@@ -1273,21 +1301,9 @@ int format_set_trailers_options(struct process_trailer_options *opts,
                        opts->filter_data = filter_list;
                        opts->only_trailers = 1;
                } else if (match_placeholder_arg_value(*arg, "separator", arg, &argval, &arglen)) {
-                       char *fmt;
-
-                       strbuf_reset(sepbuf);
-                       fmt = xstrndup(argval, arglen);
-                       strbuf_expand(sepbuf, fmt, strbuf_expand_literal_cb, NULL);
-                       free(fmt);
-                       opts->separator = sepbuf;
+                       opts->separator = expand_string_arg(sepbuf, argval, arglen);
                } else if (match_placeholder_arg_value(*arg, "key_value_separator", arg, &argval, &arglen)) {
-                       char *fmt;
-
-                       strbuf_reset(kvsepbuf);
-                       fmt = xstrndup(argval, arglen);
-                       strbuf_expand(kvsepbuf, fmt, strbuf_expand_literal_cb, NULL);
-                       free(fmt);
-                       opts->key_value_separator = kvsepbuf;
+                       opts->key_value_separator = expand_string_arg(kvsepbuf, argval, arglen);
                } else if (!match_placeholder_bool_arg(*arg, "only", arg, &opts->only_trailers) &&
                           !match_placeholder_bool_arg(*arg, "unfold", arg, &opts->unfold) &&
                           !match_placeholder_bool_arg(*arg, "keyonly", arg, &opts->key_only) &&
@@ -1368,6 +1384,44 @@ static size_t parse_describe_args(const char *start, struct strvec *args)
        return arg - start;
 }
 
+
+static int parse_decoration_option(const char **arg,
+                                  const char *name,
+                                  char **opt)
+{
+       const char *argval;
+       size_t arglen;
+
+       if (match_placeholder_arg_value(*arg, name, arg, &argval, &arglen)) {
+               struct strbuf sb = STRBUF_INIT;
+
+               expand_string_arg(&sb, argval, arglen);
+               *opt = strbuf_detach(&sb, NULL);
+               return 1;
+       }
+       return 0;
+}
+
+static void parse_decoration_options(const char **arg,
+                                    struct decoration_options *opts)
+{
+       while (parse_decoration_option(arg, "prefix", &opts->prefix) ||
+              parse_decoration_option(arg, "suffix", &opts->suffix) ||
+              parse_decoration_option(arg, "separator", &opts->separator) ||
+              parse_decoration_option(arg, "pointer", &opts->pointer) ||
+              parse_decoration_option(arg, "tag", &opts->tag))
+               ;
+}
+
+static void free_decoration_options(const struct decoration_options *opts)
+{
+       free(opts->prefix);
+       free(opts->suffix);
+       free(opts->separator);
+       free(opts->pointer);
+       free(opts->tag);
+}
+
 static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                                const char *placeholder,
                                void *context)
@@ -1381,7 +1435,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        char **slot;
 
        /* these are independent of the commit */
-       res = strbuf_expand_literal_cb(sb, placeholder, NULL);
+       res = strbuf_expand_literal(sb, placeholder);
        if (res)
                return res;
 
@@ -1521,11 +1575,18 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                strbuf_addstr(sb, get_revision_mark(NULL, commit));
                return 1;
        case 'd':
-               format_decorations(sb, commit, c->auto_color);
+               format_decorations(sb, commit, c->auto_color, NULL);
                return 1;
        case 'D':
-               format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
-               return 1;
+               {
+                       const struct decoration_options opts = {
+                               .prefix = "",
+                               .suffix = ""
+                       };
+
+                       format_decorations(sb, commit, c->auto_color, &opts);
+                       return 1;
+               }
        case 'S':               /* tag/branch like --source */
                if (!(c->pretty_ctx->rev && c->pretty_ctx->rev->sources))
                        return 0;
@@ -1622,6 +1683,23 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                return 2;
        }
 
+       if (skip_prefix(placeholder, "(decorate", &arg)) {
+               struct decoration_options opts = { NULL };
+               size_t ret = 0;
+
+               if (*arg == ':') {
+                       arg++;
+                       parse_decoration_options(&arg, &opts);
+               }
+               if (*arg == ')') {
+                       format_decorations(sb, commit, c->auto_color, &opts);
+                       ret = arg - placeholder + 1;
+               }
+
+               free_decoration_options(&opts);
+               return ret;
+       }
+
        /* For the rest we have to parse the commit header. */
        if (!c->commit_header_parsed) {
                msg = c->message =
@@ -1799,7 +1877,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
 
 static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
                                 const char *placeholder,
-                                void *context)
+                                struct format_commit_context *context)
 {
        size_t consumed, orig_len;
        enum {
@@ -1838,10 +1916,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
        }
 
        orig_len = sb->len;
-       if (((struct format_commit_context *)context)->flush_type != no_flush)
-               consumed = format_and_pad_commit(sb, placeholder, context);
-       else
+       if (context->flush_type == no_flush)
                consumed = format_commit_one(sb, placeholder, context);
+       else
+               consumed = format_and_pad_commit(sb, placeholder, context);
        if (magic == NO_MAGIC)
                return consumed;
 
@@ -1857,40 +1935,38 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
        return consumed + 1;
 }
 
-static size_t userformat_want_item(struct strbuf *sb, const char *placeholder,
-                                  void *context)
-{
-       struct userformat_want *w = context;
-
-       if (*placeholder == '+' || *placeholder == '-' || *placeholder == ' ')
-               placeholder++;
-
-       switch (*placeholder) {
-       case 'N':
-               w->notes = 1;
-               break;
-       case 'S':
-               w->source = 1;
-               break;
-       case 'd':
-       case 'D':
-               w->decorate = 1;
-               break;
-       }
-       return 0;
-}
-
 void userformat_find_requirements(const char *fmt, struct userformat_want *w)
 {
-       struct strbuf dummy = STRBUF_INIT;
-
        if (!fmt) {
                if (!user_format)
                        return;
                fmt = user_format;
        }
-       strbuf_expand(&dummy, fmt, userformat_want_item, w);
-       strbuf_release(&dummy);
+       while ((fmt = strchr(fmt, '%'))) {
+               fmt++;
+               if (skip_prefix(fmt, "%", &fmt))
+                       continue;
+
+               if (*fmt == '+' || *fmt == '-' || *fmt == ' ')
+                       fmt++;
+
+               switch (*fmt) {
+               case 'N':
+                       w->notes = 1;
+                       break;
+               case 'S':
+                       w->source = 1;
+                       break;
+               case 'd':
+               case 'D':
+                       w->decorate = 1;
+                       break;
+               case '(':
+                       if (starts_with(fmt + 1, "decorate"))
+                               w->decorate = 1;
+                       break;
+               }
+       }
 }
 
 void repo_format_commit_message(struct repository *r,
@@ -1907,7 +1983,16 @@ void repo_format_commit_message(struct repository *r,
        const char *output_enc = pretty_ctx->output_encoding;
        const char *utf8 = "UTF-8";
 
-       strbuf_expand(sb, format, format_commit_item, &context);
+       while (strbuf_expand_step(sb, &format)) {
+               size_t len;
+
+               if (skip_prefix(format, "%", &format))
+                       strbuf_addch(sb, '%');
+               else if ((len = format_commit_item(sb, format, &context)))
+                       format += len;
+               else
+                       strbuf_addch(sb, '%');
+       }
        rewrap_message_tail(sb, &context, 0, 0, 0);
 
        /*
@@ -2199,12 +2284,14 @@ void pretty_print_commit(struct pretty_print_context *pp,
        int need_8bit_cte = pp->need_8bit_cte;
 
        if (pp->fmt == CMIT_FMT_USERFORMAT) {
-               format_commit_message(commit, user_format, sb, pp);
+               repo_format_commit_message(the_repository, commit,
+                                          user_format, sb, pp);
                return;
        }
 
        encoding = get_log_output_encoding();
-       msg = reencoded = logmsg_reencode(commit, NULL, encoding);
+       msg = reencoded = repo_logmsg_reencode(the_repository, commit, NULL,
+                                              encoding);
 
        if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                indent = 0;
@@ -2261,7 +2348,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
        if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
 
-       unuse_commit_buffer(commit, reencoded);
+       repo_unuse_commit_buffer(the_repository, commit, reencoded);
 }
 
 void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
index f34e24c53a4925aec51cd62117654fdd5f7f980e..421209e9ec2732e1a08b130a6895d2688a28f52e 100644 (file)
--- a/pretty.h
+++ b/pretty.h
@@ -1,11 +1,11 @@
 #ifndef PRETTY_H
 #define PRETTY_H
 
-#include "cache.h"
 #include "date.h"
 #include "string-list.h"
 
 struct commit;
+struct repository;
 struct strbuf;
 struct process_trailer_options;
 
@@ -120,10 +120,6 @@ void repo_format_commit_message(struct repository *r,
                        const struct commit *commit,
                        const char *format, struct strbuf *sb,
                        const struct pretty_print_context *context);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define format_commit_message(c, f, s, con) \
-       repo_format_commit_message(the_repository, c, f, s, con)
-#endif
 
 /*
  * Parse given arguments from "arg", check it for correctness and
@@ -153,6 +149,8 @@ int commit_format_is_empty(enum cmit_fmt);
 /* Make subject of commit message suitable for filename */
 void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);
 
+int has_non_ascii(const char *text);
+
 /*
  * Set values of fields in "struct process_trailer_options"
  * according to trailers arguments.
index d31b48e725083654222842049e121a35fe8d511b..450775a37492082130211431229ce5645ae17613 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "prio-queue.h"
 
 static inline int compare(struct prio_queue *queue, int i, int j)
index 0cdd875d37f166bedbbeb5f0e889046674ed58be..c83cb60bf17eb490ccc3abc97a1b6f6679196e07 100644 (file)
@@ -9,13 +9,15 @@
  */
 
 #define GIT_TEST_PROGRESS_ONLY
-#include "cache.h"
-#include "gettext.h"
+#include "git-compat-util.h"
+#include "pager.h"
 #include "progress.h"
+#include "repository.h"
 #include "strbuf.h"
 #include "trace.h"
+#include "trace2.h"
 #include "utf8.h"
-#include "config.h"
+#include "parse.h"
 
 #define TP_IDX_MAX      8
 
@@ -59,7 +61,7 @@ void progress_test_force_update(void)
 }
 
 
-static void progress_interval(int signum)
+static void progress_interval(int signum UNUSED)
 {
        progress_update = 1;
 }
index 68f46f5ec70b93b28fa7c75988d671a4046e8fee..ac3aa1e365760596990188548410a16900e005d7 100644 (file)
@@ -1,9 +1,13 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "promisor-remote.h"
 #include "config.h"
+#include "trace2.h"
 #include "transport.h"
 #include "strvec.h"
+#include "packfile.h"
 
 struct promisor_remote_config {
        struct promisor_remote *promisors;
@@ -96,7 +100,9 @@ static void promisor_remote_move_to_tail(struct promisor_remote_config *config,
        config->promisors_tail = &r->next;
 }
 
-static int promisor_remote_config(const char *var, const char *value, void *data)
+static int promisor_remote_config(const char *var, const char *value,
+                                 const struct config_context *ctx UNUSED,
+                                 void *data)
 {
        struct promisor_remote_config *config = data;
        const char *name;
@@ -230,18 +236,18 @@ static int remove_fetched_oids(struct repository *repo,
        return remaining_nr;
 }
 
-int promisor_remote_get_direct(struct repository *repo,
-                              const struct object_id *oids,
-                              int oid_nr)
+void promisor_remote_get_direct(struct repository *repo,
+                               const struct object_id *oids,
+                               int oid_nr)
 {
        struct promisor_remote *r;
        struct object_id *remaining_oids = (struct object_id *)oids;
        int remaining_nr = oid_nr;
        int to_free = 0;
-       int res = -1;
+       int i;
 
        if (oid_nr == 0)
-               return 0;
+               return;
 
        promisor_remote_init(repo);
 
@@ -256,12 +262,16 @@ int promisor_remote_get_direct(struct repository *repo,
                                continue;
                        }
                }
-               res = 0;
-               break;
+               goto all_fetched;
        }
 
+       for (i = 0; i < remaining_nr; i++) {
+               if (is_promisor_object(&remaining_oids[i]))
+                       die(_("could not fetch %s from promisor remote"),
+                           oid_to_hex(&remaining_oids[i]));
+       }
+
+all_fetched:
        if (to_free)
                free(remaining_oids);
-
-       return res;
 }
index edc45ab0f5fdcd8c4145aa1c81eb3346f2eeeda9..2cb9eda9ea46a9c004757693fc230dc90cebfc50 100644 (file)
@@ -18,34 +18,18 @@ struct promisor_remote {
 };
 
 void repo_promisor_remote_reinit(struct repository *r);
-static inline void promisor_remote_reinit(void)
-{
-       repo_promisor_remote_reinit(the_repository);
-}
-
 void promisor_remote_clear(struct promisor_remote_config *config);
-
 struct promisor_remote *repo_promisor_remote_find(struct repository *r, const char *remote_name);
-static inline struct promisor_remote *promisor_remote_find(const char *remote_name)
-{
-       return repo_promisor_remote_find(the_repository, remote_name);
-}
-
 int repo_has_promisor_remote(struct repository *r);
-static inline int has_promisor_remote(void)
-{
-       return repo_has_promisor_remote(the_repository);
-}
 
 /*
  * Fetches all requested objects from all promisor remotes, trying them one at
- * a time until all objects are fetched. Returns 0 upon success, and non-zero
- * otherwise.
+ * a time until all objects are fetched.
  *
- * If oid_nr is 0, this function returns 0 (success) immediately.
+ * If oid_nr is 0, this function returns immediately.
  */
-int promisor_remote_get_direct(struct repository *repo,
-                              const struct object_id *oids,
-                              int oid_nr);
+void promisor_remote_get_direct(struct repository *repo,
+                               const struct object_id *oids,
+                               int oid_nr);
 
 #endif /* PROMISOR_REMOTE_H */
index 50df17279d1d5b2615fb8dd5a13853a2ea87b8eb..8935fe4dfb9414f101bf603aaf866ac6a1f6e027 100644 (file)
--- a/prompt.c
+++ b/prompt.c
@@ -1,5 +1,6 @@
-#include "cache.h"
-#include "config.h"
+#include "git-compat-util.h"
+#include "parse.h"
+#include "environment.h"
 #include "run-command.h"
 #include "strbuf.h"
 #include "prompt.h"
index bbde91810ac6667a1ee624d112ad26cf23776b1e..75f4cbb0a70951a85c8e5a4dba853f027ee7cf4b 100644 (file)
@@ -1,11 +1,12 @@
 #include "git-compat-util.h"
 #include "protocol-caps.h"
 #include "gettext.h"
+#include "hex.h"
 #include "pkt-line.h"
-#include "strvec.h"
-#include "hash.h"
+#include "hash-ll.h"
+#include "hex.h"
 #include "object.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "string-list.h"
 #include "strbuf.h"
 
@@ -77,7 +78,7 @@ static void send_info(struct repository *r, struct packet_writer *writer,
 
 int cap_object_info(struct repository *r, struct packet_reader *request)
 {
-       struct requested_info info;
+       struct requested_info info = { 0 };
        struct packet_writer writer;
        struct string_list oid_str_list = STRING_LIST_INIT_DUP;
 
index c53f7df5be4abae7a4f5792f42e4c4ecac006e9a..079ba75acf4e84451a80aa764cad3ac77a7d667f 100644 (file)
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
 #include "protocol.h"
+#include "trace2.h"
 
 static enum protocol_version parse_protocol_version(const char *value)
 {
index cef1a4a01c7902b0b1181a43580639fcead442ff..1e574bbd80be1bf9d9366dfefd24e3016ec87cf0 100644 (file)
@@ -1,6 +1,27 @@
 #ifndef PROTOCOL_H
 #define PROTOCOL_H
 
+/*
+ * Intensive research over the course of many years has shown that
+ * port 9418 is totally unused by anything else. Or
+ *
+ *     Your search - "port 9418" - did not match any documents.
+ *
+ * as www.google.com puts it.
+ *
+ * This port has been properly assigned for git use by IANA:
+ * git (Assigned-9418) [I06-050728-0001].
+ *
+ *     git  9418/tcp   git pack transfer service
+ *     git  9418/udp   git pack transfer service
+ *
+ * with Linus Torvalds <torvalds@osdl.org> as the point of
+ * contact. September 2005.
+ *
+ * See https://www.iana.org/assignments/port-numbers
+ */
+#define DEFAULT_GIT_PORT 9418
+
 enum protocol_version {
        protocol_unknown_version = -1,
        protocol_v0 = 0,
index 261520b472c9c2bbac57ea94460dcabeefb3a91b..e54daf740a26345a182eb67d7ab2716916e41c37 100644 (file)
@@ -1,4 +1,7 @@
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-store-ll.h"
 #include "packfile.h"
 #include "progress.h"
 #include "prune-packed.h"
diff --git a/quote.c b/quote.c
index 26719d21d1e7555d92289b26402a73030e330606..3c05194496f65f879f437abd29823986ef118f47 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "path.h"
 #include "quote.h"
+#include "strbuf.h"
 #include "strvec.h"
 
 int quote_path_fully = 1;
diff --git a/quote.h b/quote.h
index 87ff458b06dd6430855b39f6cb4833664e630746..0300c291041b02464e55e36a6c820a9faecf260f 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -3,6 +3,8 @@
 
 struct strbuf;
 
+extern int quote_path_fully;
+
 /* Help to copy the thing properly quoted for the shell safety.
  * any single quote is replaced with '\'', any exclamation point
  * is replaced with '\!', and the whole thing is enclosed in a
index 8b7d81adc1be09d91a1551c370c08058766f76f6..c45b6d849cbf4f97ec3d6eb893259837d25d9052 100644 (file)
@@ -1,5 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
 #include "range-diff.h"
+#include "object-name.h"
 #include "string-list.h"
 #include "run-command.h"
 #include "strvec.h"
@@ -8,7 +11,9 @@
 #include "linear-assignment.h"
 #include "diffcore.h"
 #include "commit.h"
+#include "pager.h"
 #include "pretty.h"
+#include "repository.h"
 #include "userdiff.h"
 #include "apply.h"
 #include "revision.h"
@@ -55,7 +60,7 @@ static int read_patches(const char *range, struct string_list *list,
                     "--output-indicator-context=#",
                     "--no-abbrev-commit",
                     "--pretty=medium",
-                    "--notes",
+                    "--show-notes-by-default",
                     NULL);
        strvec_push(&cp.args, range);
        if (other_arg)
@@ -94,7 +99,7 @@ static int read_patches(const char *range, struct string_list *list,
                                strbuf_reset(&buf);
                        }
                        CALLOC_ARRAY(util, 1);
-                       if (get_oid(p, &util->oid)) {
+                       if (repo_get_oid(the_repository, p, &util->oid)) {
                                error(_("could not parse commit '%s'"), p);
                                FREE_AND_NULL(util);
                                string_list_clear(list, 1);
@@ -225,16 +230,19 @@ cleanup:
 }
 
 static int patch_util_cmp(const void *cmp_data UNUSED,
-                         const struct patch_util *a,
-                         const struct patch_util *b,
-                         const char *keydata)
+                         const struct hashmap_entry *ha,
+                         const struct hashmap_entry *hb,
+                         const void *keydata)
 {
+       const struct patch_util
+               *a = container_of(ha, const struct patch_util, e),
+               *b = container_of(hb, const struct patch_util, e);
        return strcmp(a->diff, keydata ? keydata : b->diff);
 }
 
 static void find_exact_matches(struct string_list *a, struct string_list *b)
 {
-       struct hashmap map = HASHMAP_INIT((hashmap_cmp_fn)patch_util_cmp, NULL);
+       struct hashmap map = HASHMAP_INIT(patch_util_cmp, NULL);
        int i;
 
        /* First, add the patches of a to a hash map */
@@ -269,14 +277,18 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)
        hashmap_clear(&map);
 }
 
-static int diffsize_consume(void *data, char *line, unsigned long len)
+static int diffsize_consume(void *data,
+                            char *line UNUSED,
+                            unsigned long len UNUSED)
 {
        (*(int *)data)++;
        return 0;
 }
 
-static void diffsize_hunk(void *data, long ob, long on, long nb, long nn,
-                         const char *funcline, long funclen)
+static void diffsize_hunk(void *data,
+                         long ob UNUSED, long on UNUSED,
+                         long nb UNUSED, long nn UNUSED,
+                         const char *func UNUSED, long funclen UNUSED)
 {
        diffsize_consume(data, NULL, 0);
 }
@@ -379,11 +391,14 @@ static void output_pair_header(struct diff_options *diffopt,
        const char *color_new = diff_get_color_opt(diffopt, DIFF_FILE_NEW);
        const char *color_commit = diff_get_color_opt(diffopt, DIFF_COMMIT);
        const char *color;
+       int abbrev = diffopt->abbrev;
+
+       if (abbrev < 0)
+               abbrev = DEFAULT_ABBREV;
 
        if (!dashes->len)
                strbuf_addchars(dashes, '-',
-                               strlen(find_unique_abbrev(oid,
-                                                         DEFAULT_ABBREV)));
+                               strlen(repo_find_unique_abbrev(the_repository, oid, abbrev)));
 
        if (!b_util) {
                color = color_old;
@@ -405,7 +420,7 @@ static void output_pair_header(struct diff_options *diffopt,
                strbuf_addf(buf, "%*s:  %s ", patch_no_width, "-", dashes->buf);
        else
                strbuf_addf(buf, "%*d:  %s ", patch_no_width, a_util->i + 1,
-                           find_unique_abbrev(&a_util->oid, DEFAULT_ABBREV));
+                           repo_find_unique_abbrev(the_repository, &a_util->oid, abbrev));
 
        if (status == '!')
                strbuf_addf(buf, "%s%s", color_reset, color);
@@ -417,7 +432,7 @@ static void output_pair_header(struct diff_options *diffopt,
                strbuf_addf(buf, " %*s:  %s", patch_no_width, "-", dashes->buf);
        else
                strbuf_addf(buf, " %*d:  %s", patch_no_width, b_util->i + 1,
-                           find_unique_abbrev(&b_util->oid, DEFAULT_ABBREV));
+                           repo_find_unique_abbrev(the_repository, &b_util->oid, abbrev));
 
        commit = lookup_commit_reference(the_repository, oid);
        if (commit) {
@@ -461,7 +476,7 @@ static void patch_diff(const char *a, const char *b,
        diff_flush(diffopt);
 }
 
-static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
+static struct strbuf *output_prefix_cb(struct diff_options *opt UNUSED, void *data)
 {
        return data;
 }
@@ -478,7 +493,7 @@ static void output(struct string_list *a, struct string_list *b,
        if (range_diff_opts->diffopt)
                memcpy(&opts, range_diff_opts->diffopt, sizeof(opts));
        else
-               diff_setup(&opts);
+               repo_diff_setup(the_repository, &opts);
 
        opts.no_free = 1;
        if (!opts.output_format)
@@ -581,7 +596,7 @@ int is_range_diff_range(const char *arg)
        int i, positive = 0, negative = 0;
        struct rev_info revs;
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        if (setup_revisions(3, argv, &revs, NULL) == 1) {
                for (i = 0; i < revs.pending.nr; i++)
                        if (revs.pending.objects[i].item->flags & UNINTERESTING)
index aba63ebeb3be784a1fba95e13070e8bd847315c1..f29b06a5d059e23030333f26d5ba0667872251ab 100644 (file)
@@ -1,6 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
-#include "tag.h"
 #include "commit.h"
 #include "blob.h"
 #include "diff.h"
 #include "list-objects.h"
 #include "packfile.h"
 #include "worktree.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "pack-bitmap.h"
 #include "pack-mtimes.h"
+#include "config.h"
+#include "run-command.h"
 
 struct connectivity_progress {
        struct progress *progress;
@@ -48,7 +51,9 @@ static int add_one_ref(const char *path, const struct object_id *oid,
  * The traversal will have already marked us as SEEN, so we
  * only need to handle any progress reporting here.
  */
-static void mark_object(struct object *obj, const char *name, void *data)
+static void mark_object(struct object *obj UNUSED,
+                       const char *name UNUSED,
+                       void *data)
 {
        update_progress(data);
 }
@@ -63,8 +68,77 @@ struct recent_data {
        timestamp_t timestamp;
        report_recent_object_fn *cb;
        int ignore_in_core_kept_packs;
+
+       struct oidset extra_recent_oids;
+       int extra_recent_oids_loaded;
 };
 
+static int run_one_gc_recent_objects_hook(struct oidset *set,
+                                           const char *args)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       FILE *out;
+       int ret = 0;
+
+       cmd.use_shell = 1;
+       cmd.out = -1;
+
+       strvec_push(&cmd.args, args);
+
+       if (start_command(&cmd))
+               return -1;
+
+       out = xfdopen(cmd.out, "r");
+       while (strbuf_getline(&buf, out) != EOF) {
+               struct object_id oid;
+               const char *rest;
+
+               if (parse_oid_hex(buf.buf, &oid, &rest) || *rest) {
+                       ret = error(_("invalid extra cruft tip: '%s'"), buf.buf);
+                       break;
+               }
+
+               oidset_insert(set, &oid);
+       }
+
+       fclose(out);
+       ret |= finish_command(&cmd);
+
+       strbuf_release(&buf);
+       return ret;
+}
+
+static void load_gc_recent_objects(struct recent_data *data)
+{
+       const struct string_list *programs;
+       int ret = 0;
+       size_t i;
+
+       data->extra_recent_oids_loaded = 1;
+
+       if (git_config_get_string_multi("gc.recentobjectshook", &programs))
+               return;
+
+       for (i = 0; i < programs->nr; i++) {
+               ret = run_one_gc_recent_objects_hook(&data->extra_recent_oids,
+                                                      programs->items[i].string);
+               if (ret)
+                       die(_("unable to enumerate additional recent objects"));
+       }
+}
+
+static int obj_is_recent(const struct object_id *oid, timestamp_t mtime,
+                        struct recent_data *data)
+{
+       if (mtime > data->timestamp)
+               return 1;
+
+       if (!data->extra_recent_oids_loaded)
+               load_gc_recent_objects(data);
+       return oidset_contains(&data->extra_recent_oids, oid);
+}
+
 static void add_recent_object(const struct object_id *oid,
                              struct packed_git *pack,
                              off_t offset,
@@ -74,7 +148,7 @@ static void add_recent_object(const struct object_id *oid,
        struct object *obj;
        enum object_type type;
 
-       if (mtime <= data->timestamp)
+       if (!obj_is_recent(oid, mtime, data))
                return;
 
        /*
@@ -152,7 +226,8 @@ static int add_recent_loose(const struct object_id *oid,
 }
 
 static int add_recent_packed(const struct object_id *oid,
-                            struct packed_git *p, uint32_t pos,
+                            struct packed_git *p,
+                            uint32_t pos,
                             void *data)
 {
        struct object *obj;
@@ -188,24 +263,32 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
        data.cb = cb;
        data.ignore_in_core_kept_packs = ignore_in_core_kept_packs;
 
+       oidset_init(&data.extra_recent_oids, 0);
+       data.extra_recent_oids_loaded = 0;
+
        r = for_each_loose_object(add_recent_loose, &data,
                                  FOR_EACH_OBJECT_LOCAL_ONLY);
        if (r)
-               return r;
+               goto done;
 
        flags = FOR_EACH_OBJECT_LOCAL_ONLY | FOR_EACH_OBJECT_PACK_ORDER;
        if (ignore_in_core_kept_packs)
                flags |= FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS;
 
-       return for_each_packed_object(add_recent_packed, &data, flags);
+       r = for_each_packed_object(add_recent_packed, &data, flags);
+
+done:
+       oidset_clear(&data.extra_recent_oids);
+
+       return r;
 }
 
 static int mark_object_seen(const struct object_id *oid,
                             enum object_type type,
-                            int exclude,
-                            uint32_t name_hash,
-                            struct packed_git *found_pack,
-                            off_t found_offset)
+                            int exclude UNUSED,
+                            uint32_t name_hash UNUSED,
+                            struct packed_git *found_pack UNUSED,
+                            off_t found_offset UNUSED)
 {
        struct object *obj = lookup_object_by_type(the_repository, oid, type);
        if (!obj)
diff --git a/read-cache-ll.h b/read-cache-ll.h
new file mode 100644 (file)
index 0000000..2a50a78
--- /dev/null
@@ -0,0 +1,489 @@
+#ifndef READ_CACHE_LL_H
+#define READ_CACHE_LL_H
+
+#include "hash-ll.h"
+#include "hashmap.h"
+#include "statinfo.h"
+
+/*
+ * Basic data structures for the directory cache
+ */
+
+#define CACHE_SIGNATURE 0x44495243     /* "DIRC" */
+struct cache_header {
+       uint32_t hdr_signature;
+       uint32_t hdr_version;
+       uint32_t hdr_entries;
+};
+
+#define INDEX_FORMAT_LB 2
+#define INDEX_FORMAT_UB 4
+
+struct cache_entry {
+       struct hashmap_entry ent;
+       struct stat_data ce_stat_data;
+       unsigned int ce_mode;
+       unsigned int ce_flags;
+       unsigned int mem_pool_allocated;
+       unsigned int ce_namelen;
+       unsigned int index;     /* for link extension */
+       struct object_id oid;
+       char name[FLEX_ARRAY]; /* more */
+};
+
+#define CE_STAGEMASK (0x3000)
+#define CE_EXTENDED  (0x4000)
+#define CE_VALID     (0x8000)
+#define CE_STAGESHIFT 12
+
+/*
+ * Range 0xFFFF0FFF in ce_flags is divided into
+ * two parts: in-memory flags and on-disk ones.
+ * Flags in CE_EXTENDED_FLAGS will get saved on-disk
+ * if you want to save a new flag, add it in
+ * CE_EXTENDED_FLAGS
+ *
+ * In-memory only flags
+ */
+#define CE_UPDATE            (1 << 16)
+#define CE_REMOVE            (1 << 17)
+#define CE_UPTODATE          (1 << 18)
+#define CE_ADDED             (1 << 19)
+
+#define CE_HASHED            (1 << 20)
+#define CE_FSMONITOR_VALID   (1 << 21)
+#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
+#define CE_CONFLICTED        (1 << 23)
+
+#define CE_UNPACKED          (1 << 24)
+#define CE_NEW_SKIP_WORKTREE (1 << 25)
+
+/* used to temporarily mark paths matched by pathspecs */
+#define CE_MATCHED           (1 << 26)
+
+#define CE_UPDATE_IN_BASE    (1 << 27)
+#define CE_STRIP_NAME        (1 << 28)
+
+/*
+ * Extended on-disk flags
+ */
+#define CE_INTENT_TO_ADD     (1 << 29)
+#define CE_SKIP_WORKTREE     (1 << 30)
+/* CE_EXTENDED2 is for future extension */
+#define CE_EXTENDED2         (1U << 31)
+
+#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
+
+/*
+ * Safeguard to avoid saving wrong flags:
+ *  - CE_EXTENDED2 won't get saved until its semantic is known
+ *  - Bits in 0x0000FFFF have been saved in ce_flags already
+ *  - Bits in 0x003F0000 are currently in-memory flags
+ */
+#if CE_EXTENDED_FLAGS & 0x803FFFFF
+#error "CE_EXTENDED_FLAGS out of range"
+#endif
+
+/* Forward structure decls */
+struct pathspec;
+struct tree;
+
+/*
+ * Copy the sha1 and stat state of a cache entry from one to
+ * another. But we never change the name, or the hash state!
+ */
+static inline void copy_cache_entry(struct cache_entry *dst,
+                                   const struct cache_entry *src)
+{
+       unsigned int state = dst->ce_flags & CE_HASHED;
+       int mem_pool_allocated = dst->mem_pool_allocated;
+
+       /* Don't copy hash chain and name */
+       memcpy(&dst->ce_stat_data, &src->ce_stat_data,
+                       offsetof(struct cache_entry, name) -
+                       offsetof(struct cache_entry, ce_stat_data));
+
+       /* Restore the hash state */
+       dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
+
+       /* Restore the mem_pool_allocated flag */
+       dst->mem_pool_allocated = mem_pool_allocated;
+}
+
+static inline unsigned create_ce_flags(unsigned stage)
+{
+       return (stage << CE_STAGESHIFT);
+}
+
+#define ce_namelen(ce) ((ce)->ce_namelen)
+#define ce_size(ce) cache_entry_size(ce_namelen(ce))
+#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
+#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
+#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
+#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
+#define ce_intent_to_add(ce) ((ce)->ce_flags & CE_INTENT_TO_ADD)
+
+#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
+
+#define SOMETHING_CHANGED      (1 << 0) /* unclassified changes go here */
+#define CE_ENTRY_CHANGED       (1 << 1)
+#define CE_ENTRY_REMOVED       (1 << 2)
+#define CE_ENTRY_ADDED         (1 << 3)
+#define RESOLVE_UNDO_CHANGED   (1 << 4)
+#define CACHE_TREE_CHANGED     (1 << 5)
+#define SPLIT_INDEX_ORDERED    (1 << 6)
+#define UNTRACKED_CHANGED      (1 << 7)
+#define FSMONITOR_CHANGED      (1 << 8)
+
+struct split_index;
+struct untracked_cache;
+struct progress;
+struct pattern_list;
+
+enum sparse_index_mode {
+       /*
+        * There are no sparse directories in the index at all.
+        *
+        * Repositories that don't use cone-mode sparse-checkout will
+        * always have their indexes in this mode.
+        */
+       INDEX_EXPANDED = 0,
+
+       /*
+        * The index has already been collapsed to sparse directories
+        * whereever possible.
+        */
+       INDEX_COLLAPSED,
+
+       /*
+        * The sparse directories that exist are outside the
+        * sparse-checkout boundary, but it is possible that some file
+        * entries could collapse to sparse directory entries.
+        */
+       INDEX_PARTIALLY_SPARSE,
+};
+
+struct index_state {
+       struct cache_entry **cache;
+       unsigned int version;
+       unsigned int cache_nr, cache_alloc, cache_changed;
+       struct string_list *resolve_undo;
+       struct cache_tree *cache_tree;
+       struct split_index *split_index;
+       struct cache_time timestamp;
+       unsigned name_hash_initialized : 1,
+                initialized : 1,
+                drop_cache_tree : 1,
+                updated_workdir : 1,
+                updated_skipworktree : 1,
+                fsmonitor_has_run_once : 1;
+       enum sparse_index_mode sparse_index;
+       struct hashmap name_hash;
+       struct hashmap dir_hash;
+       struct object_id oid;
+       struct untracked_cache *untracked;
+       char *fsmonitor_last_update;
+       struct ewah_bitmap *fsmonitor_dirty;
+       struct mem_pool *ce_mem_pool;
+       struct progress *progress;
+       struct repository *repo;
+       struct pattern_list *sparse_checkout_patterns;
+};
+
+/**
+ * A "struct index_state istate" must be initialized with
+ * INDEX_STATE_INIT or the corresponding index_state_init().
+ *
+ * If the variable won't be used again, use release_index() to free()
+ * its resources. If it needs to be used again use discard_index(),
+ * which does the same thing, but will use use index_state_init() at
+ * the end. The discard_index() will use its own "istate->repo" as the
+ * "r" argument to index_state_init() in that case.
+ */
+#define INDEX_STATE_INIT(r) { \
+       .repo = (r), \
+}
+void index_state_init(struct index_state *istate, struct repository *r);
+void release_index(struct index_state *istate);
+
+/* Cache entry creation and cleanup */
+
+/*
+ * Create cache_entry intended for use in the specified index. Caller
+ * is responsible for discarding the cache_entry with
+ * `discard_cache_entry`.
+ */
+struct cache_entry *make_cache_entry(struct index_state *istate,
+                                    unsigned int mode,
+                                    const struct object_id *oid,
+                                    const char *path,
+                                    int stage,
+                                    unsigned int refresh_options);
+
+struct cache_entry *make_empty_cache_entry(struct index_state *istate,
+                                          size_t name_len);
+
+/*
+ * Create a cache_entry that is not intended to be added to an index. If
+ * `ce_mem_pool` is not NULL, the entry is allocated within the given memory
+ * pool. Caller is responsible for discarding "loose" entries with
+ * `discard_cache_entry()` and the memory pool with
+ * `mem_pool_discard(ce_mem_pool, should_validate_cache_entries())`.
+ */
+struct cache_entry *make_transient_cache_entry(unsigned int mode,
+                                              const struct object_id *oid,
+                                              const char *path,
+                                              int stage,
+                                              struct mem_pool *ce_mem_pool);
+
+struct cache_entry *make_empty_transient_cache_entry(size_t len,
+                                                    struct mem_pool *ce_mem_pool);
+
+/*
+ * Discard cache entry.
+ */
+void discard_cache_entry(struct cache_entry *ce);
+
+/*
+ * Check configuration if we should perform extra validation on cache
+ * entries.
+ */
+int should_validate_cache_entries(void);
+
+/*
+ * Duplicate a cache_entry. Allocate memory for the new entry from a
+ * memory_pool. Takes into account cache_entry fields that are meant
+ * for managing the underlying memory allocation of the cache_entry.
+ */
+struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
+
+/*
+ * Validate the cache entries in the index.  This is an internal
+ * consistency check that the cache_entry structs are allocated from
+ * the expected memory pool.
+ */
+void validate_cache_entries(const struct index_state *istate);
+
+/*
+ * Bulk prefetch all missing cache entries that are not GITLINKs and that match
+ * the given predicate. This function should only be called if
+ * repo_has_promisor_remote() returns true.
+ */
+typedef int (*must_prefetch_predicate)(const struct cache_entry *);
+void prefetch_cache_entries(const struct index_state *istate,
+                           must_prefetch_predicate must_prefetch);
+
+/* Initialize and use the cache information */
+struct lock_file;
+int do_read_index(struct index_state *istate, const char *path,
+                 int must_exist); /* for testting only! */
+int read_index_from(struct index_state *, const char *path,
+                   const char *gitdir);
+int is_index_unborn(struct index_state *);
+
+/* For use with `write_locked_index()`. */
+#define COMMIT_LOCK            (1 << 0)
+#define SKIP_IF_UNCHANGED      (1 << 1)
+
+/*
+ * Write the index while holding an already-taken lock. Close the lock,
+ * and if `COMMIT_LOCK` is given, commit it.
+ *
+ * Unless a split index is in use, write the index into the lockfile.
+ *
+ * With a split index, write the shared index to a temporary file,
+ * adjust its permissions and rename it into place, then write the
+ * split index to the lockfile. If the temporary file for the shared
+ * index cannot be created, fall back to the behavior described in
+ * the previous paragraph.
+ *
+ * With `COMMIT_LOCK`, the lock is always committed or rolled back.
+ * Without it, the lock is closed, but neither committed nor rolled
+ * back.
+ *
+ * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
+ * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
+ */
+int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
+
+void discard_index(struct index_state *);
+void move_index_extensions(struct index_state *dst, struct index_state *src);
+int unmerged_index(const struct index_state *);
+
+/**
+ * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
+ * compares istate to HEAD.  If tree is NULL and on an unborn branch,
+ * returns 1 if there are entries in istate, 0 otherwise.  If an strbuf is
+ * provided, the space-separated list of files that differ will be appended
+ * to it.
+ */
+int repo_index_has_changes(struct repository *repo,
+                          struct tree *tree,
+                          struct strbuf *sb);
+
+int verify_path(const char *path, unsigned mode);
+int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
+
+/*
+ * Searches for an entry defined by name and namelen in the given index.
+ * If the return value is positive (including 0) it is the position of an
+ * exact match. If the return value is negative, the negated value minus 1
+ * is the position where the entry would be inserted.
+ * Example: The current index consists of these files and its stages:
+ *
+ *   b#0, d#0, f#1, f#3
+ *
+ * index_name_pos(&index, "a", 1) -> -1
+ * index_name_pos(&index, "b", 1) ->  0
+ * index_name_pos(&index, "c", 1) -> -2
+ * index_name_pos(&index, "d", 1) ->  1
+ * index_name_pos(&index, "e", 1) -> -3
+ * index_name_pos(&index, "f", 1) -> -3
+ * index_name_pos(&index, "g", 1) -> -5
+ */
+int index_name_pos(struct index_state *, const char *name, int namelen);
+
+/*
+ * Like index_name_pos, returns the position of an entry of the given name in
+ * the index if one exists, otherwise returns a negative value where the negated
+ * value minus 1 is the position where the index entry would be inserted. Unlike
+ * index_name_pos, however, a sparse index is not expanded to find an entry
+ * inside a sparse directory.
+ */
+int index_name_pos_sparse(struct index_state *, const char *name, int namelen);
+
+/*
+ * Determines whether an entry with the given name exists within the
+ * given index. The return value is 1 if an exact match is found, otherwise
+ * it is 0. Note that, unlike index_name_pos, this function does not expand
+ * the index if it is sparse. If an item exists within the full index but it
+ * is contained within a sparse directory (and not in the sparse index), 0 is
+ * returned.
+ */
+int index_entry_exists(struct index_state *, const char *name, int namelen);
+
+/*
+ * Some functions return the negative complement of an insert position when a
+ * precise match was not found but a position was found where the entry would
+ * need to be inserted. This helper protects that logic from any integer
+ * underflow.
+ */
+static inline int index_pos_to_insert_pos(uintmax_t pos)
+{
+       if (pos > INT_MAX)
+               die("overflow: -1 - %"PRIuMAX, pos);
+       return -1 - (int)pos;
+}
+
+#define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
+#define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
+#define ADD_CACHE_SKIP_DFCHECK 4       /* Ok to skip DF conflict checks */
+#define ADD_CACHE_JUST_APPEND 8                /* Append only */
+#define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
+#define ADD_CACHE_KEEP_CACHE_TREE 32   /* Do not invalidate cache-tree */
+#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
+int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
+void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
+
+/* Remove entry, return true if there are more entries to go. */
+int remove_index_entry_at(struct index_state *, int pos);
+
+void remove_marked_cache_entries(struct index_state *istate, int invalidate);
+int remove_file_from_index(struct index_state *, const char *path);
+#define ADD_CACHE_VERBOSE 1
+#define ADD_CACHE_PRETEND 2
+#define ADD_CACHE_IGNORE_ERRORS        4
+#define ADD_CACHE_IGNORE_REMOVAL 8
+#define ADD_CACHE_INTENT 16
+/*
+ * These two are used to add the contents of the file at path
+ * to the index, marking the working tree up-to-date by storing
+ * the cached stat info in the resulting cache entry.  A caller
+ * that has already run lstat(2) on the path can call
+ * add_to_index(), and all others can call add_file_to_index();
+ * the latter will do necessary lstat(2) internally before
+ * calling the former.
+ */
+int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
+int add_file_to_index(struct index_state *, const char *path, int flags);
+
+int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
+int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
+void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
+int index_name_is_other(struct index_state *, const char *, int);
+void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
+
+/* do stat comparison even if CE_VALID is true */
+#define CE_MATCH_IGNORE_VALID          01
+/* do not check the contents but report dirty on racily-clean entries */
+#define CE_MATCH_RACY_IS_DIRTY         02
+/* do stat comparison even if CE_SKIP_WORKTREE is true */
+#define CE_MATCH_IGNORE_SKIP_WORKTREE  04
+/* ignore non-existent files during stat update  */
+#define CE_MATCH_IGNORE_MISSING                0x08
+/* enable stat refresh */
+#define CE_MATCH_REFRESH               0x10
+/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
+#define CE_MATCH_IGNORE_FSMONITOR 0X20
+int is_racy_timestamp(const struct index_state *istate,
+                     const struct cache_entry *ce);
+int has_racy_timestamp(struct index_state *istate);
+int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+
+int match_stat_data_racy(const struct index_state *istate,
+                        const struct stat_data *sd, struct stat *st);
+
+void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st);
+
+/*
+ * Fill members of st by members of sd enough to convince match_stat()
+ * to consider that they match.  It should be usable as a replacement
+ * for lstat() for a tracked path that is known to be up-to-date via
+ * some out-of-line means (like fsmonitor).
+ */
+int fake_lstat(const struct cache_entry *ce, struct stat *st);
+
+#define REFRESH_REALLY                   (1 << 0) /* ignore_valid */
+#define REFRESH_UNMERGED                 (1 << 1) /* allow unmerged */
+#define REFRESH_QUIET                    (1 << 2) /* be quiet about it */
+#define REFRESH_IGNORE_MISSING           (1 << 3) /* ignore non-existent */
+#define REFRESH_IGNORE_SUBMODULES        (1 << 4) /* ignore submodules */
+#define REFRESH_IN_PORCELAIN             (1 << 5) /* user friendly output, not "needs update" */
+#define REFRESH_PROGRESS                 (1 << 6) /* show progress bar if stderr is tty */
+#define REFRESH_IGNORE_SKIP_WORKTREE     (1 << 7) /* ignore skip_worktree entries */
+int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
+/*
+ * Refresh the index and write it to disk.
+ *
+ * 'refresh_flags' is passed directly to 'refresh_index()', while
+ * 'COMMIT_LOCK | write_flags' is passed to 'write_locked_index()', so
+ * the lockfile is always either committed or rolled back.
+ *
+ * If 'gentle' is passed, errors locking the index are ignored.
+ *
+ * Return 1 if refreshing the index returns an error, -1 if writing
+ * the index to disk fails, 0 on success.
+ *
+ * Note that if refreshing the index returns an error, we still write
+ * out the index (unless locking fails).
+ */
+int repo_refresh_and_write_index(struct repository*, unsigned int refresh_flags, unsigned int write_flags, int gentle, const struct pathspec *, char *seen, const char *header_msg);
+
+struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
+
+void set_alternate_index_output(const char *);
+
+extern int verify_index_checksum;
+extern int verify_ce_order;
+
+int cmp_cache_name_compare(const void *a_, const void *b_);
+
+int add_files_to_cache(struct repository *repo, const char *prefix,
+                      const struct pathspec *pathspec, int include_sparse,
+                      int flags);
+
+void overlay_tree_on_index(struct index_state *istate,
+                          const char *tree_name, const char *prefix);
+
+#endif /* READ_CACHE_LL_H */
index 32024029274828c12654e3c256de3d8d08f37734..f546cf7875cbfefddbec2449e8303786e485a8dd 100644 (file)
@@ -3,24 +3,38 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "bulk-checkin.h"
 #include "config.h"
+#include "date.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "hex.h"
 #include "tempfile.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "refs.h"
 #include "dir.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
 #include "tree.h"
 #include "commit.h"
-#include "blob.h"
+#include "environment.h"
+#include "gettext.h"
+#include "mem-pool.h"
+#include "name-hash.h"
+#include "object-name.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
 #include "resolve-undo.h"
-#include "run-command.h"
+#include "revision.h"
 #include "strbuf.h"
+#include "trace2.h"
 #include "varint.h"
 #include "split-index.h"
+#include "symlinks.h"
 #include "utf8.h"
 #include "fsmonitor.h"
 #include "thread-utils.h"
@@ -163,61 +177,6 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
                add_index_entry(istate, new_entry, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
-void fill_stat_data(struct stat_data *sd, struct stat *st)
-{
-       sd->sd_ctime.sec = (unsigned int)st->st_ctime;
-       sd->sd_mtime.sec = (unsigned int)st->st_mtime;
-       sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
-       sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
-       sd->sd_dev = st->st_dev;
-       sd->sd_ino = st->st_ino;
-       sd->sd_uid = st->st_uid;
-       sd->sd_gid = st->st_gid;
-       sd->sd_size = st->st_size;
-}
-
-int match_stat_data(const struct stat_data *sd, struct stat *st)
-{
-       int changed = 0;
-
-       if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
-               changed |= MTIME_CHANGED;
-       if (trust_ctime && check_stat &&
-           sd->sd_ctime.sec != (unsigned int)st->st_ctime)
-               changed |= CTIME_CHANGED;
-
-#ifdef USE_NSEC
-       if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
-               changed |= MTIME_CHANGED;
-       if (trust_ctime && check_stat &&
-           sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
-               changed |= CTIME_CHANGED;
-#endif
-
-       if (check_stat) {
-               if (sd->sd_uid != (unsigned int) st->st_uid ||
-                       sd->sd_gid != (unsigned int) st->st_gid)
-                       changed |= OWNER_CHANGED;
-               if (sd->sd_ino != (unsigned int) st->st_ino)
-                       changed |= INODE_CHANGED;
-       }
-
-#ifdef USE_STDEV
-       /*
-        * st_dev breaks on network filesystems where different
-        * clients will have different views of what "device"
-        * the filesystem is on
-        */
-       if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
-                       changed |= INODE_CHANGED;
-#endif
-
-       if (sd->sd_size != (unsigned int) st->st_size)
-               changed |= DATA_CHANGED;
-
-       return changed;
-}
-
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -236,6 +195,33 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
        }
 }
 
+static unsigned int st_mode_from_ce(const struct cache_entry *ce)
+{
+       extern int trust_executable_bit, has_symlinks;
+
+       switch (ce->ce_mode & S_IFMT) {
+       case S_IFLNK:
+               return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
+       case S_IFREG:
+               return (ce->ce_mode & (trust_executable_bit ? 0755 : 0644)) | S_IFREG;
+       case S_IFGITLINK:
+               return S_IFDIR | 0755;
+       case S_IFDIR:
+               return ce->ce_mode;
+       default:
+               BUG("unsupported ce_mode: %o", ce->ce_mode);
+       }
+}
+
+int fake_lstat(const struct cache_entry *ce, struct stat *st)
+{
+       fake_lstat_data(&ce->ce_stat_data, st);
+       st->st_mode = st_mode_from_ce(ce);
+
+       /* always succeed as lstat() replacement */
+       return 0;
+}
+
 static int ce_compare_data(struct index_state *istate,
                           const struct cache_entry *ce,
                           struct stat *st)
@@ -263,7 +249,7 @@ static int ce_compare_link(const struct cache_entry *ce, size_t expected_size)
        if (strbuf_readlink(&sb, ce->name, expected_size))
                return -1;
 
-       buffer = read_object_file(&ce->oid, &type, &size);
+       buffer = repo_read_object_file(the_repository, &ce->oid, &type, &size);
        if (buffer) {
                if (size == sb.len)
                        match = memcmp(buffer, sb.buf, size);
@@ -488,86 +474,30 @@ int ie_modified(struct index_state *istate,
        return 0;
 }
 
-int base_name_compare(const char *name1, int len1, int mode1,
-                     const char *name2, int len2, int mode2)
+static int cache_name_stage_compare(const char *name1, int len1, int stage1,
+                                   const char *name2, int len2, int stage2)
 {
-       unsigned char c1, c2;
-       int len = len1 < len2 ? len1 : len2;
        int cmp;
 
-       cmp = memcmp(name1, name2, len);
-       if (cmp)
-               return cmp;
-       c1 = name1[len];
-       c2 = name2[len];
-       if (!c1 && S_ISDIR(mode1))
-               c1 = '/';
-       if (!c2 && S_ISDIR(mode2))
-               c2 = '/';
-       return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
-}
-
-/*
- * df_name_compare() is identical to base_name_compare(), except it
- * compares conflicting directory/file entries as equal. Note that
- * while a directory name compares as equal to a regular file, they
- * then individually compare _differently_ to a filename that has
- * a dot after the basename (because '\0' < '.' < '/').
- *
- * This is used by routines that want to traverse the git namespace
- * but then handle conflicting entries together when possible.
- */
-int df_name_compare(const char *name1, int len1, int mode1,
-                   const char *name2, int len2, int mode2)
-{
-       int len = len1 < len2 ? len1 : len2, cmp;
-       unsigned char c1, c2;
-
-       cmp = memcmp(name1, name2, len);
+       cmp = name_compare(name1, len1, name2, len2);
        if (cmp)
                return cmp;
-       /* Directories and files compare equal (same length, same name) */
-       if (len1 == len2)
-               return 0;
-       c1 = name1[len];
-       if (!c1 && S_ISDIR(mode1))
-               c1 = '/';
-       c2 = name2[len];
-       if (!c2 && S_ISDIR(mode2))
-               c2 = '/';
-       if (c1 == '/' && !c2)
-               return 0;
-       if (c2 == '/' && !c1)
-               return 0;
-       return c1 - c2;
-}
 
-int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)
-{
-       size_t min_len = (len1 < len2) ? len1 : len2;
-       int cmp = memcmp(name1, name2, min_len);
-       if (cmp)
-               return cmp;
-       if (len1 < len2)
+       if (stage1 < stage2)
                return -1;
-       if (len1 > len2)
+       if (stage1 > stage2)
                return 1;
        return 0;
 }
 
-int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
+int cmp_cache_name_compare(const void *a_, const void *b_)
 {
-       int cmp;
-
-       cmp = name_compare(name1, len1, name2, len2);
-       if (cmp)
-               return cmp;
+       const struct cache_entry *ce1, *ce2;
 
-       if (stage1 < stage2)
-               return -1;
-       if (stage1 > stage2)
-               return 1;
-       return 0;
+       ce1 = *((const struct cache_entry **)a_);
+       ce2 = *((const struct cache_entry **)b_);
+       return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),
+                                 ce2->name, ce2->ce_namelen, ce_stage(ce2));
 }
 
 static int index_name_stage_pos(struct index_state *istate,
@@ -1817,6 +1747,8 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
        git_hash_ctx c;
        unsigned char hash[GIT_MAX_RAWSZ];
        int hdr_version;
+       unsigned char *start, *end;
+       struct object_id oid;
 
        if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
                return error(_("bad signature 0x%08x"), hdr->hdr_signature);
@@ -1827,10 +1759,16 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
        if (!verify_index_checksum)
                return 0;
 
+       end = (unsigned char *)hdr + size;
+       start = end - the_hash_algo->rawsz;
+       oidread(&oid, start);
+       if (oideq(&oid, null_oid()))
+               return 0;
+
        the_hash_algo->init_fn(&c);
        the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
        the_hash_algo->final_fn(hash, &c);
-       if (!hasheq(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz))
+       if (!hasheq(hash, start))
                return error(_("bad index file sha1 signature"));
        return 0;
 }
@@ -2292,12 +2230,10 @@ static void set_new_index_sparsity(struct index_state *istate)
         * If the index's repo exists, mark it sparse according to
         * repo settings.
         */
-       if (istate->repo) {
-               prepare_repo_settings(istate->repo);
-               if (!istate->repo->settings.command_requires_full_index &&
-                   is_sparse_index_allowed(istate, 0))
-                       istate->sparse_index = 1;
-       }
+       prepare_repo_settings(istate->repo);
+       if (!istate->repo->settings.command_requires_full_index &&
+           is_sparse_index_allowed(istate, 0))
+               istate->sparse_index = 1;
 }
 
 /* remember to discard_cache() before reading a different cache! */
@@ -2322,9 +2258,8 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        fd = open(path, O_RDONLY);
        if (fd < 0) {
                if (!must_exist && errno == ENOENT) {
-                       if (!istate->repo)
-                               istate->repo = the_repository;
                        set_new_index_sparsity(istate);
+                       istate->initialized = 1;
                        return 0;
                }
                die_errno(_("%s: index file open failed"), path);
@@ -2425,9 +2360,6 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        trace2_data_intmax("index", the_repository, "read/cache_nr",
                           istate->cache_nr);
 
-       if (!istate->repo)
-               istate->repo = the_repository;
-
        /*
         * If the command explicitly requires a full index, force it
         * to be full. Otherwise, correct the sparsity based on repository
@@ -2490,18 +2422,21 @@ int read_index_from(struct index_state *istate, const char *path,
 
        trace_performance_enter();
        if (split_index->base)
-               discard_index(split_index->base);
+               release_index(split_index->base);
        else
-               CALLOC_ARRAY(split_index->base, 1);
+               ALLOC_ARRAY(split_index->base, 1);
+       index_state_init(split_index->base, istate->repo);
 
        base_oid_hex = oid_to_hex(&split_index->base_oid);
        base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
-       trace2_region_enter_printf("index", "shared/do_read_index",
-                                  the_repository, "%s", base_path);
-       ret = do_read_index(split_index->base, base_path, 0);
-       trace2_region_leave_printf("index", "shared/do_read_index",
-                                  the_repository, "%s", base_path);
-       if (!ret) {
+       if (file_exists(base_path)) {
+               trace2_region_enter_printf("index", "shared/do_read_index",
+                                       the_repository, "%s", base_path);
+
+               ret = do_read_index(split_index->base, base_path, 0);
+               trace2_region_leave_printf("index", "shared/do_read_index",
+                                       the_repository, "%s", base_path);
+       } else {
                char *path_copy = xstrdup(path);
                char *base_path2 = xstrfmt("%s/sharedindex.%s",
                                           dirname(path_copy), base_oid_hex);
@@ -2531,7 +2466,13 @@ int is_index_unborn(struct index_state *istate)
        return (!istate->cache_nr && !istate->timestamp.sec);
 }
 
-int discard_index(struct index_state *istate)
+void index_state_init(struct index_state *istate, struct repository *r)
+{
+       struct index_state blank = INDEX_STATE_INIT(r);
+       memcpy(istate, &blank, sizeof(*istate));
+}
+
+void release_index(struct index_state *istate)
 {
        /*
         * Cache entries in istate->cache[] should have been allocated
@@ -2543,27 +2484,28 @@ int discard_index(struct index_state *istate)
        validate_cache_entries(istate);
 
        resolve_undo_clear_index(istate);
-       istate->cache_nr = 0;
-       istate->cache_changed = 0;
-       istate->timestamp.sec = 0;
-       istate->timestamp.nsec = 0;
        free_name_hash(istate);
        cache_tree_free(&(istate->cache_tree));
-       istate->initialized = 0;
-       istate->fsmonitor_has_run_once = 0;
-       FREE_AND_NULL(istate->fsmonitor_last_update);
-       FREE_AND_NULL(istate->cache);
-       istate->cache_alloc = 0;
+       free(istate->fsmonitor_last_update);
+       free(istate->cache);
        discard_split_index(istate);
        free_untracked_cache(istate->untracked);
-       istate->untracked = NULL;
+
+       if (istate->sparse_checkout_patterns) {
+               clear_pattern_list(istate->sparse_checkout_patterns);
+               FREE_AND_NULL(istate->sparse_checkout_patterns);
+       }
 
        if (istate->ce_mem_pool) {
                mem_pool_discard(istate->ce_mem_pool, should_validate_cache_entries());
                FREE_AND_NULL(istate->ce_mem_pool);
        }
+}
 
-       return 0;
+void discard_index(struct index_state *istate)
+{
+       release_index(istate);
+       index_state_init(istate, istate->repo);
 }
 
 /*
@@ -2618,7 +2560,7 @@ int repo_index_has_changes(struct repository *repo,
 
        if (tree)
                cmp = tree->object.oid;
-       if (tree || !get_oid_tree("HEAD", &cmp)) {
+       if (tree || !repo_get_oid_tree(repo, "HEAD", &cmp)) {
                struct diff_options opt;
 
                repo_diff_setup(repo, &opt);
@@ -2891,6 +2833,16 @@ static int record_ieot(void)
        return !git_config_get_index_threads(&val) && val != 1;
 }
 
+enum write_extensions {
+       WRITE_NO_EXTENSION =              0,
+       WRITE_SPLIT_INDEX_EXTENSION =     1<<0,
+       WRITE_CACHE_TREE_EXTENSION =      1<<1,
+       WRITE_RESOLVE_UNDO_EXTENSION =    1<<2,
+       WRITE_UNTRACKED_CACHE_EXTENSION = 1<<3,
+       WRITE_FSMONITOR_EXTENSION =       1<<4,
+};
+#define WRITE_ALL_EXTENSIONS ((enum write_extensions)-1)
+
 /*
  * On success, `tempfile` is closed. If it is the temporary file
  * of a `struct lock_file`, we will therefore effectively perform
@@ -2899,7 +2851,7 @@ static int record_ieot(void)
  * rely on it.
  */
 static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
-                         int strip_extensions, unsigned flags)
+                         enum write_extensions write_extensions, unsigned flags)
 {
        uint64_t start = getnanotime();
        struct hashfile *f;
@@ -2917,9 +2869,13 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        int ieot_entries = 1;
        struct index_entry_offset_table *ieot = NULL;
        int nr, nr_threads;
+       struct repository *r = istate->repo;
 
        f = hashfd(tempfile->fd, tempfile->filename.buf);
 
+       prepare_repo_settings(r);
+       f->skip_hash = r->settings.index_skip_hash;
+
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
                        removed++;
@@ -2933,7 +2889,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        }
 
        if (!istate->version)
-               istate->version = get_index_format_default(the_repository);
+               istate->version = get_index_format_default(r);
 
        /* demote version 3 to version 2 when the latter suffices */
        if (istate->version == 3 || istate->version == 2)
@@ -3068,8 +3024,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                        return -1;
        }
 
-       if (!strip_extensions && istate->split_index &&
-           !is_null_oid(&istate->split_index->base_oid)) {
+       if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
+           istate->split_index) {
                struct strbuf sb = STRBUF_INIT;
 
                if (istate->sparse_index)
@@ -3083,7 +3039,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                if (err)
                        return -1;
        }
-       if (!strip_extensions && !drop_cache_tree && istate->cache_tree) {
+       if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
+           !drop_cache_tree && istate->cache_tree) {
                struct strbuf sb = STRBUF_INIT;
 
                cache_tree_write(&sb, istate->cache_tree);
@@ -3093,7 +3050,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                if (err)
                        return -1;
        }
-       if (!strip_extensions && istate->resolve_undo) {
+       if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
+           istate->resolve_undo) {
                struct strbuf sb = STRBUF_INIT;
 
                resolve_undo_write(&sb, istate->resolve_undo);
@@ -3104,7 +3062,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                if (err)
                        return -1;
        }
-       if (!strip_extensions && istate->untracked) {
+       if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
+           istate->untracked) {
                struct strbuf sb = STRBUF_INIT;
 
                write_untracked_extension(&sb, istate->untracked);
@@ -3115,7 +3074,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                if (err)
                        return -1;
        }
-       if (!strip_extensions && istate->fsmonitor_last_update) {
+       if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
+           istate->fsmonitor_last_update) {
                struct strbuf sb = STRBUF_INIT;
 
                write_fsmonitor_extension(&sb, istate);
@@ -3189,8 +3149,10 @@ static int commit_locked_index(struct lock_file *lk)
                return commit_lock_file(lk);
 }
 
-static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
-                                unsigned flags)
+static int do_write_locked_index(struct index_state *istate,
+                                struct lock_file *lock,
+                                unsigned flags,
+                                enum write_extensions write_extensions)
 {
        int ret;
        int was_full = istate->sparse_index == INDEX_EXPANDED;
@@ -3208,7 +3170,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
         */
        trace2_region_enter_printf("index", "do_write_index", the_repository,
                                   "%s", get_lock_file_path(lock));
-       ret = do_write_index(istate, lock->tempfile, 0, flags);
+       ret = do_write_index(istate, lock->tempfile, write_extensions, flags);
        trace2_region_leave_printf("index", "do_write_index", the_repository,
                                   "%s", get_lock_file_path(lock));
 
@@ -3237,7 +3199,7 @@ static int write_split_index(struct index_state *istate,
 {
        int ret;
        prepare_to_write_split_index(istate);
-       ret = do_write_locked_index(istate, lock, flags);
+       ret = do_write_locked_index(istate, lock, flags, WRITE_ALL_EXTENSIONS);
        finish_writing_split_index(istate);
        return ret;
 }
@@ -3312,7 +3274,7 @@ static int write_shared_index(struct index_state *istate,
 
        trace2_region_enter_printf("index", "shared/do_write_index",
                                   the_repository, "%s", get_tempfile_path(*temp));
-       ret = do_write_index(si->base, *temp, 1, flags);
+       ret = do_write_index(si->base, *temp, WRITE_NO_EXTENSION, flags);
        trace2_region_leave_printf("index", "shared/do_write_index",
                                   the_repository, "%s", get_tempfile_path(*temp));
 
@@ -3389,9 +3351,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
        if ((!si && !test_split_index_env) ||
            alternate_index_output ||
            (istate->cache_changed & ~EXTMASK)) {
-               if (si)
-                       oidclr(&si->base_oid);
-               ret = do_write_locked_index(istate, lock, flags);
+               ret = do_write_locked_index(istate, lock, flags,
+                                           ~WRITE_SPLIT_INDEX_EXTENSION);
                goto out;
        }
 
@@ -3417,8 +3378,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
                /* Same initial permissions as the main .git/index file */
                temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
                if (!temp) {
-                       oidclr(&si->base_oid);
-                       ret = do_write_locked_index(istate, lock, flags);
+                       ret = do_write_locked_index(istate, lock, flags,
+                                                   ~WRITE_SPLIT_INDEX_EXTENSION);
                        goto out;
                }
                ret = write_shared_index(istate, &temp, flags);
@@ -3539,7 +3500,8 @@ void *read_blob_data_from_index(struct index_state *istate,
        }
        if (pos < 0)
                return NULL;
-       data = read_object_file(&istate->cache[pos]->oid, &type, &sz);
+       data = repo_read_object_file(the_repository, &istate->cache[pos]->oid,
+                                    &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return NULL;
@@ -3549,35 +3511,6 @@ void *read_blob_data_from_index(struct index_state *istate,
        return data;
 }
 
-void stat_validity_clear(struct stat_validity *sv)
-{
-       FREE_AND_NULL(sv->sd);
-}
-
-int stat_validity_check(struct stat_validity *sv, const char *path)
-{
-       struct stat st;
-
-       if (stat(path, &st) < 0)
-               return sv->sd == NULL;
-       if (!sv->sd)
-               return 0;
-       return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
-}
-
-void stat_validity_update(struct stat_validity *sv, int fd)
-{
-       struct stat st;
-
-       if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
-               stat_validity_clear(sv);
-       else {
-               if (!sv->sd)
-                       CALLOC_ARRAY(sv->sd, 1);
-               fill_stat_data(sv->sd, &st);
-       }
-}
-
 void move_index_extensions(struct index_state *dst, struct index_state *src)
 {
        dst->untracked = src->untracked;
@@ -3821,3 +3754,240 @@ void prefetch_cache_entries(const struct index_state *istate,
                                   to_fetch.oid, to_fetch.nr);
        oid_array_clear(&to_fetch);
 }
+
+static int read_one_entry_opt(struct index_state *istate,
+                             const struct object_id *oid,
+                             struct strbuf *base,
+                             const char *pathname,
+                             unsigned mode, int opt)
+{
+       int len;
+       struct cache_entry *ce;
+
+       if (S_ISDIR(mode))
+               return READ_TREE_RECURSIVE;
+
+       len = strlen(pathname);
+       ce = make_empty_cache_entry(istate, base->len + len);
+
+       ce->ce_mode = create_ce_mode(mode);
+       ce->ce_flags = create_ce_flags(1);
+       ce->ce_namelen = base->len + len;
+       memcpy(ce->name, base->buf, base->len);
+       memcpy(ce->name + base->len, pathname, len+1);
+       oidcpy(&ce->oid, oid);
+       return add_index_entry(istate, ce, opt);
+}
+
+static int read_one_entry(const struct object_id *oid, struct strbuf *base,
+                         const char *pathname, unsigned mode,
+                         void *context)
+{
+       struct index_state *istate = context;
+       return read_one_entry_opt(istate, oid, base, pathname,
+                                 mode,
+                                 ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+}
+
+/*
+ * This is used when the caller knows there is no existing entries at
+ * the stage that will conflict with the entry being added.
+ */
+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
+                               const char *pathname, unsigned mode,
+                               void *context)
+{
+       struct index_state *istate = context;
+       return read_one_entry_opt(istate, oid, base, pathname,
+                                 mode, ADD_CACHE_JUST_APPEND);
+}
+
+/*
+ * Read the tree specified with --with-tree option
+ * (typically, HEAD) into stage #1 and then
+ * squash them down to stage #0.  This is used for
+ * --error-unmatch to list and check the path patterns
+ * that were given from the command line.  We are not
+ * going to write this index out.
+ */
+void overlay_tree_on_index(struct index_state *istate,
+                          const char *tree_name, const char *prefix)
+{
+       struct tree *tree;
+       struct object_id oid;
+       struct pathspec pathspec;
+       struct cache_entry *last_stage0 = NULL;
+       int i;
+       read_tree_fn_t fn = NULL;
+       int err;
+
+       if (repo_get_oid(the_repository, tree_name, &oid))
+               die("tree-ish %s not found.", tree_name);
+       tree = parse_tree_indirect(&oid);
+       if (!tree)
+               die("bad tree-ish %s", tree_name);
+
+       /* Hoist the unmerged entries up to stage #3 to make room */
+       /* TODO: audit for interaction with sparse-index. */
+       ensure_full_index(istate);
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
+               if (!ce_stage(ce))
+                       continue;
+               ce->ce_flags |= CE_STAGEMASK;
+       }
+
+       if (prefix) {
+               static const char *(matchbuf[1]);
+               matchbuf[0] = NULL;
+               parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
+                              PATHSPEC_PREFER_CWD, prefix, matchbuf);
+       } else
+               memset(&pathspec, 0, sizeof(pathspec));
+
+       /*
+        * See if we have cache entry at the stage.  If so,
+        * do it the original slow way, otherwise, append and then
+        * sort at the end.
+        */
+       for (i = 0; !fn && i < istate->cache_nr; i++) {
+               const struct cache_entry *ce = istate->cache[i];
+               if (ce_stage(ce) == 1)
+                       fn = read_one_entry;
+       }
+
+       if (!fn)
+               fn = read_one_entry_quick;
+       err = read_tree(the_repository, tree, &pathspec, fn, istate);
+       clear_pathspec(&pathspec);
+       if (err)
+               die("unable to read tree entries %s", tree_name);
+
+       /*
+        * Sort the cache entry -- we need to nuke the cache tree, though.
+        */
+       if (fn == read_one_entry_quick) {
+               cache_tree_free(&istate->cache_tree);
+               QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);
+       }
+
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
+               switch (ce_stage(ce)) {
+               case 0:
+                       last_stage0 = ce;
+                       /* fallthru */
+               default:
+                       continue;
+               case 1:
+                       /*
+                        * If there is stage #0 entry for this, we do not
+                        * need to show it.  We use CE_UPDATE bit to mark
+                        * such an entry.
+                        */
+                       if (last_stage0 &&
+                           !strcmp(last_stage0->name, ce->name))
+                               ce->ce_flags |= CE_UPDATE;
+               }
+       }
+}
+
+struct update_callback_data {
+       struct index_state *index;
+       int include_sparse;
+       int flags;
+       int add_errors;
+};
+
+static int fix_unmerged_status(struct diff_filepair *p,
+                              struct update_callback_data *data)
+{
+       if (p->status != DIFF_STATUS_UNMERGED)
+               return p->status;
+       if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
+               /*
+                * This is not an explicit add request, and the
+                * path is missing from the working tree (deleted)
+                */
+               return DIFF_STATUS_DELETED;
+       else
+               /*
+                * Either an explicit add request, or path exists
+                * in the working tree.  An attempt to explicitly
+                * add a path that does not exist in the working tree
+                * will be caught as an error by the caller immediately.
+                */
+               return DIFF_STATUS_MODIFIED;
+}
+
+static void update_callback(struct diff_queue_struct *q,
+                           struct diff_options *opt UNUSED, void *cbdata)
+{
+       int i;
+       struct update_callback_data *data = cbdata;
+
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               const char *path = p->one->path;
+
+               if (!data->include_sparse &&
+                   !path_in_sparse_checkout(path, data->index))
+                       continue;
+
+               switch (fix_unmerged_status(p, data)) {
+               default:
+                       die(_("unexpected diff status %c"), p->status);
+               case DIFF_STATUS_MODIFIED:
+               case DIFF_STATUS_TYPE_CHANGED:
+                       if (add_file_to_index(data->index, path, data->flags)) {
+                               if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+                                       die(_("updating files failed"));
+                               data->add_errors++;
+                       }
+                       break;
+               case DIFF_STATUS_DELETED:
+                       if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
+                               break;
+                       if (!(data->flags & ADD_CACHE_PRETEND))
+                               remove_file_from_index(data->index, path);
+                       if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
+                               printf(_("remove '%s'\n"), path);
+                       break;
+               }
+       }
+}
+
+int add_files_to_cache(struct repository *repo, const char *prefix,
+                      const struct pathspec *pathspec, int include_sparse,
+                      int flags)
+{
+       struct update_callback_data data;
+       struct rev_info rev;
+
+       memset(&data, 0, sizeof(data));
+       data.index = repo->index;
+       data.include_sparse = include_sparse;
+       data.flags = flags;
+
+       repo_init_revisions(repo, &rev, prefix);
+       setup_revisions(0, NULL, &rev, NULL);
+       if (pathspec)
+               copy_pathspec(&rev.prune_data, pathspec);
+       rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+       rev.diffopt.format_callback = update_callback;
+       rev.diffopt.format_callback_data = &data;
+       rev.diffopt.flags.override_submodule_config = 1;
+       rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
+
+       /*
+        * Use an ODB transaction to optimize adding multiple objects.
+        * This function is invoked from commands other than 'add', which
+        * may not have their own transaction active.
+        */
+       begin_odb_transaction();
+       run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+       end_odb_transaction();
+
+       release_revisions(&rev);
+       return !!data.add_errors;
+}
diff --git a/read-cache.h b/read-cache.h
new file mode 100644 (file)
index 0000000..043da1f
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef READ_CACHE_H
+#define READ_CACHE_H
+
+#include "read-cache-ll.h"
+#include "object.h"
+#include "pathspec.h"
+
+static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+                                            unsigned int mode)
+{
+       extern int trust_executable_bit, has_symlinks;
+       if (!has_symlinks && S_ISREG(mode) &&
+           ce && S_ISLNK(ce->ce_mode))
+               return ce->ce_mode;
+       if (!trust_executable_bit && S_ISREG(mode)) {
+               if (ce && S_ISREG(ce->ce_mode))
+                       return ce->ce_mode;
+               return create_ce_mode(0666);
+       }
+       return create_ce_mode(mode);
+}
+
+static inline int ce_to_dtype(const struct cache_entry *ce)
+{
+       unsigned ce_mode = ntohl(ce->ce_mode);
+       if (S_ISREG(ce_mode))
+               return DT_REG;
+       else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
+               return DT_DIR;
+       else if (S_ISLNK(ce_mode))
+               return DT_LNK;
+       else
+               return DT_UNKNOWN;
+}
+
+static inline int ce_path_match(struct index_state *istate,
+                               const struct cache_entry *ce,
+                               const struct pathspec *pathspec,
+                               char *seen)
+{
+       return match_pathspec(istate, pathspec, ce->name, ce_namelen(ce), 0, seen,
+                             S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
+}
+
+#endif /* READ_CACHE_H */
index 7407c593191679c757573e567a28f35c65c9cb33..d9718409b3d7862fee0eaa7e2255e83f50379656 100644 (file)
@@ -1,11 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
 #include "sequencer.h"
 #include "rebase-interactive.h"
+#include "repository.h"
 #include "strbuf.h"
 #include "commit-slab.h"
 #include "config.h"
 #include "dir.h"
+#include "object-name.h"
 
 static const char edit_todo_list_advice[] =
 N_("You can fix this with 'git rebase --edit-todo' "
@@ -66,13 +71,14 @@ void append_todo_help(int command_count,
 
        if (!edit_todo) {
                strbuf_addch(buf, '\n');
-               strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
-                                             "Rebase %s onto %s (%d commands)",
-                                             command_count),
+               strbuf_commented_addf(buf, comment_line_char,
+                                     Q_("Rebase %s onto %s (%d command)",
+                                        "Rebase %s onto %s (%d commands)",
+                                        command_count),
                                      shortrevisions, shortonto, command_count);
        }
 
-       strbuf_add_commented_lines(buf, msg, strlen(msg));
+       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
 
        if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
                msg = _("\nDo not remove any line. Use 'drop' "
@@ -81,7 +87,7 @@ void append_todo_help(int command_count,
                msg = _("\nIf you remove a line here "
                         "THAT COMMIT WILL BE LOST.\n");
 
-       strbuf_add_commented_lines(buf, msg, strlen(msg));
+       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
 
        if (edit_todo)
                msg = _("\nYou are editing the todo file "
@@ -92,7 +98,7 @@ void append_todo_help(int command_count,
                msg = _("\nHowever, if you remove everything, "
                        "the rebase will be aborted.\n\n");
 
-       strbuf_add_commented_lines(buf, msg, strlen(msg));
+       strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
 }
 
 int edit_todo_list(struct repository *r, struct todo_list *todo_list,
@@ -124,7 +130,7 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
        if (launch_sequence_editor(todo_file, &new_todo->buf, NULL))
                return -2;
 
-       strbuf_stripspace(&new_todo->buf, 1);
+       strbuf_stripspace(&new_todo->buf, comment_line_char);
        if (initial && new_todo->buf.len == 0)
                return -3;
 
@@ -187,7 +193,7 @@ int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)
                struct commit *commit = item->commit;
                if (commit && !*commit_seen_at(&commit_seen, commit)) {
                        strbuf_addf(&missing, " - %s %.*s\n",
-                                   find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),
+                                   repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
                                    item->arg_len,
                                    todo_item_get_arg(old_todo, item));
                        *commit_seen_at(&commit_seen, commit) = 1;
index 6775cddb28434d0780cb483357476f4783e1f596..69a1822da34ce7b5877c7aace0a770ef51c655fd 100644 (file)
--- a/rebase.c
+++ b/rebase.c
@@ -1,5 +1,6 @@
+#include "git-compat-util.h"
 #include "rebase.h"
-#include "config.h"
+#include "parse.h"
 #include "gettext.h"
 
 /*
index 914908fac524e788cb362511deb0592e4948f62e..35b989e1dfe59e9d274afead3d397f97ed624b26 100644 (file)
@@ -1,11 +1,20 @@
-#include "builtin.h"
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "config.h"
+#include "gpg-interface.h"
+#include "hex.h"
 #include "parse-options.h"
+#include "run-command.h"
 #include "refs.h"
 #include "wildmatch.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
 #include "repository.h"
 #include "commit.h"
+#include "mailmap.h"
+#include "ident.h"
 #include "remote.h"
 #include "color.h"
 #include "tag.h"
 #include "ref-filter.h"
 #include "revision.h"
 #include "utf8.h"
-#include "git-compat-util.h"
-#include "version.h"
+#include "versioncmp.h"
 #include "trailer.h"
 #include "wt-status.h"
 #include "commit-slab.h"
-#include "commit-graph.h"
 #include "commit-reach.h"
 #include "worktree.h"
 #include "hashmap.h"
-#include "strvec.h"
 
 static struct ref_msg {
        const char *gone;
@@ -140,10 +146,12 @@ enum atom_type {
        ATOM_TAGGERDATE,
        ATOM_CREATOR,
        ATOM_CREATORDATE,
+       ATOM_DESCRIBE,
        ATOM_SUBJECT,
        ATOM_BODY,
        ATOM_TRAILERS,
        ATOM_CONTENTS,
+       ATOM_SIGNATURE,
        ATOM_RAW,
        ATOM_UPSTREAM,
        ATOM_PUSH,
@@ -158,6 +166,7 @@ enum atom_type {
        ATOM_THEN,
        ATOM_ELSE,
        ATOM_REST,
+       ATOM_AHEADBEHIND,
 };
 
 /*
@@ -205,9 +214,22 @@ static struct used_atom {
                struct {
                        enum { O_SIZE, O_SIZE_DISK } option;
                } objectsize;
-               struct email_option {
-                       enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;
+               struct {
+                       enum { N_RAW, N_MAILMAP } option;
+               } name_option;
+               struct {
+                       enum {
+                               EO_RAW = 0,
+                               EO_TRIM = 1<<0,
+                               EO_LOCALPART = 1<<1,
+                               EO_MAILMAP = 1<<2,
+                       } option;
                } email_option;
+               struct {
+                       enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
+                              S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
+               } signature;
+               const char **describe_args;
                struct refname_atom refname;
                char *head;
        } u;
@@ -228,6 +250,126 @@ static int strbuf_addf_ret(struct strbuf *sb, int ret, const char *fmt, ...)
        return ret;
 }
 
+static int err_no_arg(struct strbuf *sb, const char *name)
+{
+       size_t namelen = strchrnul(name, ':') - name;
+       strbuf_addf(sb, _("%%(%.*s) does not take arguments"),
+                   (int)namelen, name);
+       return -1;
+}
+
+static int err_bad_arg(struct strbuf *sb, const char *name, const char *arg)
+{
+       size_t namelen = strchrnul(name, ':') - name;
+       strbuf_addf(sb, _("unrecognized %%(%.*s) argument: %s"),
+                   (int)namelen, name, arg);
+       return -1;
+}
+
+/*
+ * Parse option of name "candidate" in the option string "to_parse" of
+ * the form
+ *
+ *     "candidate1[=val1],candidate2[=val2],candidate3[=val3],..."
+ *
+ * The remaining part of "to_parse" is stored in "end" (if we are
+ * parsing the last candidate, then this is NULL) and the value of
+ * the candidate is stored in "valuestart" and its length in "valuelen",
+ * that is the portion after "=". Since it is possible for a "candidate"
+ * to not have a value, in such cases, "valuestart" is set to point to
+ * NULL and "valuelen" to 0.
+ *
+ * The function returns 1 on success. It returns 0 if we don't find
+ * "candidate" in "to_parse" or we find "candidate" but it is followed
+ * by more chars (for example, "candidatefoo"), that is, we don't find
+ * an exact match.
+ *
+ * This function only does the above for one "candidate" at a time. So
+ * it has to be called each time trying to parse a "candidate" in the
+ * option string "to_parse".
+ */
+static int match_atom_arg_value(const char *to_parse, const char *candidate,
+                               const char **end, const char **valuestart,
+                               size_t *valuelen)
+{
+       const char *atom;
+
+       if (!skip_prefix(to_parse, candidate, &atom))
+               return 0; /* definitely not "candidate" */
+
+       if (*atom == '=') {
+               /* we just saw "candidate=" */
+               *valuestart = atom + 1;
+               atom = strchrnul(*valuestart, ',');
+               *valuelen = atom - *valuestart;
+       } else if (*atom != ',' && *atom != '\0') {
+               /* key begins with "candidate" but has more chars */
+               return 0;
+       } else {
+               /* just "candidate" without "=val" */
+               *valuestart = NULL;
+               *valuelen = 0;
+       }
+
+       /* atom points at either the ',' or NUL after this key[=val] */
+       if (*atom == ',')
+               atom++;
+       else if (*atom)
+               BUG("Why is *atom not NULL yet?");
+
+       *end = atom;
+       return 1;
+}
+
+/*
+ * Parse boolean option of name "candidate" in the option list "to_parse"
+ * of the form
+ *
+ *     "candidate1[=bool1],candidate2[=bool2],candidate3[=bool3],..."
+ *
+ * The remaining part of "to_parse" is stored in "end" (if we are parsing
+ * the last candidate, then this is NULL) and the value (if given) is
+ * parsed and stored in "val", so "val" always points to either 0 or 1.
+ * If the value is not given, then "val" is set to point to 1.
+ *
+ * The boolean value is parsed using "git_parse_maybe_bool()", so the
+ * accepted values are
+ *
+ *     to set true  - "1", "yes", "true"
+ *     to set false - "0", "no", "false"
+ *
+ * This function returns 1 on success. It returns 0 when we don't find
+ * an exact match for "candidate" or when the boolean value given is
+ * not valid.
+ */
+static int match_atom_bool_arg(const char *to_parse, const char *candidate,
+                               const char **end, int *val)
+{
+       const char *argval;
+       char *strval;
+       size_t arglen;
+       int v;
+
+       if (!match_atom_arg_value(to_parse, candidate, end, &argval, &arglen))
+               return 0;
+
+       if (!argval) {
+               *val = 1;
+               return 1;
+       }
+
+       strval = xstrndup(argval, arglen);
+       v = git_parse_maybe_bool(strval);
+       free(strval);
+
+       if (v == -1)
+               return 0;
+
+       *val = v;
+
+       return 1;
+}
+
 static int color_atom_parser(struct ref_format *format, struct used_atom *atom,
                             const char *color_value, struct strbuf *err)
 {
@@ -262,11 +404,12 @@ static int refname_atom_parser_internal(struct refname_atom *atom, const char *a
                if (strtol_i(arg, 10, &atom->rstrip))
                        return strbuf_addf_ret(err, -1, _("Integer value expected refname:rstrip=%s"), arg);
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), name, arg);
+               return err_bad_arg(err, name, arg);
        return 0;
 }
 
-static int remote_ref_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int remote_ref_atom_parser(struct ref_format *format UNUSED,
+                                 struct used_atom *atom,
                                  const char *arg, struct strbuf *err)
 {
        struct string_list params = STRING_LIST_INIT_DUP;
@@ -313,11 +456,12 @@ static int remote_ref_atom_parser(struct ref_format *format, struct used_atom *a
        return 0;
 }
 
-static int objecttype_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int objecttype_atom_parser(struct ref_format *format UNUSED,
+                                 struct used_atom *atom,
                                  const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(objecttype) does not take arguments"));
+               return err_no_arg(err, "objecttype");
        if (*atom->name == '*')
                oi_deref.info.typep = &oi_deref.type;
        else
@@ -325,7 +469,8 @@ static int objecttype_atom_parser(struct ref_format *format, struct used_atom *a
        return 0;
 }
 
-static int objectsize_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int objectsize_atom_parser(struct ref_format *format UNUSED,
+                                 struct used_atom *atom,
                                  const char *arg, struct strbuf *err)
 {
        if (!arg) {
@@ -341,15 +486,16 @@ static int objectsize_atom_parser(struct ref_format *format, struct used_atom *a
                else
                        oi.info.disk_sizep = &oi.disk_size;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "objectsize", arg);
+               return err_bad_arg(err, "objectsize", arg);
        return 0;
 }
 
-static int deltabase_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int deltabase_atom_parser(struct ref_format *format UNUSED,
+                                struct used_atom *atom,
                                 const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(deltabase) does not take arguments"));
+               return err_no_arg(err, "deltabase");
        if (*atom->name == '*')
                oi_deref.info.delta_base_oid = &oi_deref.delta_base_oid;
        else
@@ -357,16 +503,18 @@ static int deltabase_atom_parser(struct ref_format *format, struct used_atom *at
        return 0;
 }
 
-static int body_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int body_atom_parser(struct ref_format *format UNUSED,
+                           struct used_atom *atom,
                            const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(body) does not take arguments"));
+               return err_no_arg(err, "body");
        atom->u.contents.option = C_BODY_DEP;
        return 0;
 }
 
-static int subject_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int subject_atom_parser(struct ref_format *format UNUSED,
+                              struct used_atom *atom,
                               const char *arg, struct strbuf *err)
 {
        if (!arg)
@@ -374,11 +522,42 @@ static int subject_atom_parser(struct ref_format *format, struct used_atom *atom
        else if (!strcmp(arg, "sanitize"))
                atom->u.contents.option = C_SUB_SANITIZE;
        else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "subject", arg);
+               return err_bad_arg(err, "subject", arg);
+       return 0;
+}
+
+static int parse_signature_option(const char *arg)
+{
+       if (!arg)
+               return S_BARE;
+       else if (!strcmp(arg, "signer"))
+               return S_SIGNER;
+       else if (!strcmp(arg, "grade"))
+               return S_GRADE;
+       else if (!strcmp(arg, "key"))
+               return S_KEY;
+       else if (!strcmp(arg, "fingerprint"))
+               return S_FINGERPRINT;
+       else if (!strcmp(arg, "primarykeyfingerprint"))
+               return S_PRI_KEY_FP;
+       else if (!strcmp(arg, "trustlevel"))
+               return S_TRUST_LEVEL;
+       return -1;
+}
+
+static int signature_atom_parser(struct ref_format *format UNUSED,
+                                struct used_atom *atom,
+                                const char *arg, struct strbuf *err)
+{
+       int opt = parse_signature_option(arg);
+       if (opt < 0)
+               return err_bad_arg(err, "signature", arg);
+       atom->u.signature.option = opt;
        return 0;
 }
 
-static int trailers_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int trailers_atom_parser(struct ref_format *format UNUSED,
+                               struct used_atom *atom,
                                const char *arg, struct strbuf *err)
 {
        atom->u.contents.trailer_opts.no_divider = 1;
@@ -411,9 +590,10 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato
                atom->u.contents.option = C_BARE;
        else if (!strcmp(arg, "body"))
                atom->u.contents.option = C_BODY;
-       else if (!strcmp(arg, "size"))
+       else if (!strcmp(arg, "size")) {
+               atom->type = FIELD_ULONG;
                atom->u.contents.option = C_LENGTH;
-       else if (!strcmp(arg, "signature"))
+       else if (!strcmp(arg, "signature"))
                atom->u.contents.option = C_SIG;
        else if (!strcmp(arg, "subject"))
                atom->u.contents.option = C_SUB;
@@ -428,23 +608,107 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato
                if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
                        return strbuf_addf_ret(err, -1, _("positive value expected contents:lines=%s"), arg);
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "contents", arg);
+               return err_bad_arg(err, "contents", arg);
        return 0;
 }
 
-static int raw_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int describe_atom_option_parser(struct strvec *args, const char **arg,
+                                      struct strbuf *err)
+{
+       const char *argval;
+       size_t arglen = 0;
+       int optval = 0;
+
+       if (match_atom_bool_arg(*arg, "tags", arg, &optval)) {
+               if (!optval)
+                       strvec_push(args, "--no-tags");
+               else
+                       strvec_push(args, "--tags");
+               return 1;
+       }
+
+       if (match_atom_arg_value(*arg, "abbrev", arg, &argval, &arglen)) {
+               char *endptr;
+
+               if (!arglen)
+                       return strbuf_addf_ret(err, -1,
+                                              _("argument expected for %s"),
+                                              "describe:abbrev");
+               if (strtol(argval, &endptr, 10) < 0)
+                       return strbuf_addf_ret(err, -1,
+                                              _("positive value expected %s=%s"),
+                                              "describe:abbrev", argval);
+               if (endptr - argval != arglen)
+                       return strbuf_addf_ret(err, -1,
+                                              _("cannot fully parse %s=%s"),
+                                              "describe:abbrev", argval);
+
+               strvec_pushf(args, "--abbrev=%.*s", (int)arglen, argval);
+               return 1;
+       }
+
+       if (match_atom_arg_value(*arg, "match", arg, &argval, &arglen)) {
+               if (!arglen)
+                       return strbuf_addf_ret(err, -1,
+                                              _("value expected %s="),
+                                              "describe:match");
+
+               strvec_pushf(args, "--match=%.*s", (int)arglen, argval);
+               return 1;
+       }
+
+       if (match_atom_arg_value(*arg, "exclude", arg, &argval, &arglen)) {
+               if (!arglen)
+                       return strbuf_addf_ret(err, -1,
+                                              _("value expected %s="),
+                                              "describe:exclude");
+
+               strvec_pushf(args, "--exclude=%.*s", (int)arglen, argval);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int describe_atom_parser(struct ref_format *format UNUSED,
+                               struct used_atom *atom,
                                const char *arg, struct strbuf *err)
+{
+       struct strvec args = STRVEC_INIT;
+
+       for (;;) {
+               int found = 0;
+               const char *bad_arg = arg;
+
+               if (!arg || !*arg)
+                       break;
+
+               found = describe_atom_option_parser(&args, &arg, err);
+               if (found < 0)
+                       return found;
+               if (!found)
+                       return err_bad_arg(err, "describe", bad_arg);
+       }
+       atom->u.describe_args = strvec_detach(&args);
+       return 0;
+}
+
+static int raw_atom_parser(struct ref_format *format UNUSED,
+                          struct used_atom *atom,
+                          const char *arg, struct strbuf *err)
 {
        if (!arg)
                atom->u.raw_data.option = RAW_BARE;
-       else if (!strcmp(arg, "size"))
+       else if (!strcmp(arg, "size")) {
+               atom->type = FIELD_ULONG;
                atom->u.raw_data.option = RAW_LENGTH;
-       else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "raw", arg);
+       else
+               return err_bad_arg(err, "raw", arg);
        return 0;
 }
 
-static int oid_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int oid_atom_parser(struct ref_format *format UNUSED,
+                          struct used_atom *atom,
                           const char *arg, struct strbuf *err)
 {
        if (!arg)
@@ -459,25 +723,61 @@ static int oid_atom_parser(struct ref_format *format, struct used_atom *atom,
                if (atom->u.oid.length < MINIMUM_ABBREV)
                        atom->u.oid.length = MINIMUM_ABBREV;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), atom->name, arg);
+               return err_bad_arg(err, atom->name, arg);
        return 0;
 }
 
-static int person_email_atom_parser(struct ref_format *format, struct used_atom *atom,
-                                   const char *arg, struct strbuf *err)
+static int person_name_atom_parser(struct ref_format *format UNUSED,
+                                  struct used_atom *atom,
+                                  const char *arg, struct strbuf *err)
 {
        if (!arg)
-               atom->u.email_option.option = EO_RAW;
-       else if (!strcmp(arg, "trim"))
-               atom->u.email_option.option = EO_TRIM;
-       else if (!strcmp(arg, "localpart"))
-               atom->u.email_option.option = EO_LOCALPART;
+               atom->u.name_option.option = N_RAW;
+       else if (!strcmp(arg, "mailmap"))
+               atom->u.name_option.option = N_MAILMAP;
        else
-               return strbuf_addf_ret(err, -1, _("unrecognized email option: %s"), arg);
+               return err_bad_arg(err, atom->name, arg);
+       return 0;
+}
+
+static int email_atom_option_parser(struct used_atom *atom,
+                                   const char **arg, struct strbuf *err)
+{
+       if (!*arg)
+               return EO_RAW;
+       if (skip_prefix(*arg, "trim", arg))
+               return EO_TRIM;
+       if (skip_prefix(*arg, "localpart", arg))
+               return EO_LOCALPART;
+       if (skip_prefix(*arg, "mailmap", arg))
+               return EO_MAILMAP;
+       return -1;
+}
+
+static int person_email_atom_parser(struct ref_format *format UNUSED,
+                                   struct used_atom *atom,
+                                   const char *arg, struct strbuf *err)
+{
+       for (;;) {
+               int opt = email_atom_option_parser(atom, &arg, err);
+               const char *bad_arg = arg;
+
+               if (opt < 0)
+                       return err_bad_arg(err, atom->name, bad_arg);
+               atom->u.email_option.option |= opt;
+
+               if (!arg || !*arg)
+                       break;
+               if (*arg == ',')
+                       arg++;
+               else
+                       return err_bad_arg(err, atom->name, bad_arg);
+       }
        return 0;
 }
 
-static int refname_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int refname_atom_parser(struct ref_format *format UNUSED,
+                              struct used_atom *atom,
                               const char *arg, struct strbuf *err)
 {
        return refname_atom_parser_internal(&atom->u.refname, arg, atom->name, err);
@@ -494,7 +794,8 @@ static align_type parse_align_position(const char *s)
        return -1;
 }
 
-static int align_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int align_atom_parser(struct ref_format *format UNUSED,
+                            struct used_atom *atom,
                             const char *arg, struct strbuf *err)
 {
        struct align *align = &atom->u.align;
@@ -546,7 +847,8 @@ static int align_atom_parser(struct ref_format *format, struct used_atom *atom,
        return 0;
 }
 
-static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int if_atom_parser(struct ref_format *format UNUSED,
+                         struct used_atom *atom,
                          const char *arg, struct strbuf *err)
 {
        if (!arg) {
@@ -557,22 +859,42 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
        } else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) {
                atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "if", arg);
+               return err_bad_arg(err, "if", arg);
        return 0;
 }
 
-static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int rest_atom_parser(struct ref_format *format UNUSED,
+                           struct used_atom *atom UNUSED,
                            const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
-       format->use_rest = 1;
+               return err_no_arg(err, "rest");
        return 0;
 }
 
-static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
-                           const char *arg, struct strbuf *unused_err)
+static int ahead_behind_atom_parser(struct ref_format *format,
+                                   struct used_atom *atom UNUSED,
+                                   const char *arg, struct strbuf *err)
 {
+       struct string_list_item *item;
+
+       if (!arg)
+               return strbuf_addf_ret(err, -1, _("expected format: %%(ahead-behind:<committish>)"));
+
+       item = string_list_append(&format->bases, arg);
+       item->util = lookup_commit_reference_by_name(arg);
+       if (!item->util)
+               die("failed to find '%s'", arg);
+
+       return 0;
+}
+
+static int head_atom_parser(struct ref_format *format UNUSED,
+                           struct used_atom *atom,
+                           const char *arg, struct strbuf *err)
+{
+       if (arg)
+               return err_no_arg(err, "HEAD");
        atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
        return 0;
 }
@@ -596,23 +918,25 @@ static struct {
        [ATOM_TYPE] = { "type", SOURCE_OBJ },
        [ATOM_TAG] = { "tag", SOURCE_OBJ },
        [ATOM_AUTHOR] = { "author", SOURCE_OBJ },
-       [ATOM_AUTHORNAME] = { "authorname", SOURCE_OBJ },
+       [ATOM_AUTHORNAME] = { "authorname", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
        [ATOM_AUTHOREMAIL] = { "authoremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
        [ATOM_AUTHORDATE] = { "authordate", SOURCE_OBJ, FIELD_TIME },
        [ATOM_COMMITTER] = { "committer", SOURCE_OBJ },
-       [ATOM_COMMITTERNAME] = { "committername", SOURCE_OBJ },
+       [ATOM_COMMITTERNAME] = { "committername", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
        [ATOM_COMMITTEREMAIL] = { "committeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
        [ATOM_COMMITTERDATE] = { "committerdate", SOURCE_OBJ, FIELD_TIME },
        [ATOM_TAGGER] = { "tagger", SOURCE_OBJ },
-       [ATOM_TAGGERNAME] = { "taggername", SOURCE_OBJ },
+       [ATOM_TAGGERNAME] = { "taggername", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
        [ATOM_TAGGEREMAIL] = { "taggeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
        [ATOM_TAGGERDATE] = { "taggerdate", SOURCE_OBJ, FIELD_TIME },
        [ATOM_CREATOR] = { "creator", SOURCE_OBJ },
        [ATOM_CREATORDATE] = { "creatordate", SOURCE_OBJ, FIELD_TIME },
+       [ATOM_DESCRIBE] = { "describe", SOURCE_OBJ, FIELD_STR, describe_atom_parser },
        [ATOM_SUBJECT] = { "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser },
        [ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
        [ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
        [ATOM_CONTENTS] = { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser },
+       [ATOM_SIGNATURE] = { "signature", SOURCE_OBJ, FIELD_STR, signature_atom_parser },
        [ATOM_RAW] = { "raw", SOURCE_OBJ, FIELD_STR, raw_atom_parser },
        [ATOM_UPSTREAM] = { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
        [ATOM_PUSH] = { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
@@ -627,6 +951,7 @@ static struct {
        [ATOM_THEN] = { "then", SOURCE_NONE },
        [ATOM_ELSE] = { "else", SOURCE_NONE },
        [ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
+       [ATOM_AHEADBEHIND] = { "ahead-behind", SOURCE_OTHER, FIELD_STR, ahead_behind_atom_parser },
        /*
         * Please update $__git_ref_fieldlist in git-completion.bash
         * when you add new atoms
@@ -773,7 +1098,7 @@ static void quote_formatting(struct strbuf *s, const char *str, ssize_t len, int
 }
 
 static int append_atom(struct atom_value *v, struct ref_formatting_state *state,
-                      struct strbuf *unused_err)
+                      struct strbuf *err UNUSED)
 {
        /*
         * Quote formatting is only done when the stack has a single
@@ -823,7 +1148,7 @@ static void end_align_handler(struct ref_formatting_stack **stack)
 }
 
 static int align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
-                             struct strbuf *unused_err)
+                             struct strbuf *err UNUSED)
 {
        struct ref_formatting_stack *new_stack;
 
@@ -870,7 +1195,7 @@ static void if_then_else_handler(struct ref_formatting_stack **stack)
 }
 
 static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
-                          struct strbuf *unused_err)
+                          struct strbuf *err UNUSED)
 {
        struct ref_formatting_stack *new_stack;
        struct if_then_else *if_then_else = xcalloc(1,
@@ -897,7 +1222,8 @@ static int is_empty(struct strbuf *buf)
        return cur == end;
  }
 
-static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+static int then_atom_handler(struct atom_value *atomv UNUSED,
+                            struct ref_formatting_state *state,
                             struct strbuf *err)
 {
        struct ref_formatting_stack *cur = state->stack;
@@ -934,7 +1260,8 @@ static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
        return 0;
 }
 
-static int else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+static int else_atom_handler(struct atom_value *atomv UNUSED,
+                            struct ref_formatting_state *state,
                             struct strbuf *err)
 {
        struct ref_formatting_stack *prev = state->stack;
@@ -955,7 +1282,8 @@ static int else_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
        return 0;
 }
 
-static int end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+static int end_atom_handler(struct atom_value *atomv UNUSED,
+                           struct ref_formatting_state *state,
                            struct strbuf *err)
 {
        struct ref_formatting_stack *current = state->stack;
@@ -1057,9 +1385,11 @@ static const char *do_grab_oid(const char *field, const struct object_id *oid,
        case O_FULL:
                return oid_to_hex(oid);
        case O_LENGTH:
-               return find_unique_abbrev(oid, atom->u.oid.length);
+               return repo_find_unique_abbrev(the_repository, oid,
+                                              atom->u.oid.length);
        case O_SHORT:
-               return find_unique_abbrev(oid, DEFAULT_ABBREV);
+               return repo_find_unique_abbrev(the_repository, oid,
+                                              DEFAULT_ABBREV);
        default:
                BUG("unknown %%(%s) option", field);
        }
@@ -1191,38 +1521,55 @@ static const char *copy_name(const char *buf)
 {
        const char *cp;
        for (cp = buf; *cp && *cp != '\n'; cp++) {
-               if (!strncmp(cp, " <", 2))
+               if (starts_with(cp, " <"))
                        return xmemdupz(buf, cp - buf);
        }
        return xstrdup("");
 }
 
+static const char *find_end_of_email(const char *email, int opt)
+{
+       const char *eoemail;
+
+       if (opt & EO_LOCALPART) {
+               eoemail = strchr(email, '@');
+               if (eoemail)
+                       return eoemail;
+               return strchr(email, '>');
+       }
+
+       if (opt & EO_TRIM)
+               return strchr(email, '>');
+
+       /*
+        * The option here is either the raw email option or the raw
+        * mailmap option (that is EO_RAW or EO_MAILMAP). In such cases,
+        * we directly grab the whole email including the closing
+        * angle brackets.
+        *
+        * If EO_MAILMAP was set with any other option (that is either
+        * EO_TRIM or EO_LOCALPART), we already grab the end of email
+        * above.
+        */
+       eoemail = strchr(email, '>');
+       if (eoemail)
+               eoemail++;
+       return eoemail;
+}
+
 static const char *copy_email(const char *buf, struct used_atom *atom)
 {
        const char *email = strchr(buf, '<');
        const char *eoemail;
+       int opt = atom->u.email_option.option;
+
        if (!email)
                return xstrdup("");
-       switch (atom->u.email_option.option) {
-       case EO_RAW:
-               eoemail = strchr(email, '>');
-               if (eoemail)
-                       eoemail++;
-               break;
-       case EO_TRIM:
-               email++;
-               eoemail = strchr(email, '>');
-               break;
-       case EO_LOCALPART:
+
+       if (opt & (EO_LOCALPART | EO_TRIM))
                email++;
-               eoemail = strchr(email, '@');
-               if (!eoemail)
-                       eoemail = strchr(email, '>');
-               break;
-       default:
-               BUG("unknown email option");
-       }
 
+       eoemail = find_end_of_email(email, opt);
        if (!eoemail)
                return xstrdup("");
        return xmemdupz(email, eoemail - email);
@@ -1283,16 +1630,23 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
        v->value = 0;
 }
 
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
+
 /* See grab_values */
 static void grab_person(const char *who, struct atom_value *val, int deref, void *buf)
 {
        int i;
        int wholen = strlen(who);
        const char *wholine = NULL;
+       const char *headers[] = { "author ", "committer ",
+                                 "tagger ", NULL };
 
        for (i = 0; i < used_atom_cnt; i++) {
-               const char *name = used_atom[i].name;
+               struct used_atom *atom = &used_atom[i];
+               const char *name = atom->name;
                struct atom_value *v = &val[i];
+               struct strbuf mailmap_buf = STRBUF_INIT;
+
                if (!!deref != (*name == '*'))
                        continue;
                if (deref)
@@ -1300,22 +1654,36 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
                if (strncmp(who, name, wholen))
                        continue;
                if (name[wholen] != 0 &&
-                   strcmp(name + wholen, "name") &&
+                   !starts_with(name + wholen, "name") &&
                    !starts_with(name + wholen, "email") &&
                    !starts_with(name + wholen, "date"))
                        continue;
-               if (!wholine)
+
+               if ((starts_with(name + wholen, "name") &&
+                   (atom->u.name_option.option == N_MAILMAP)) ||
+                   (starts_with(name + wholen, "email") &&
+                   (atom->u.email_option.option & EO_MAILMAP))) {
+                       if (!mailmap.items)
+                               read_mailmap(&mailmap);
+                       strbuf_addstr(&mailmap_buf, buf);
+                       apply_mailmap_to_header(&mailmap_buf, headers, &mailmap);
+                       wholine = find_wholine(who, wholen, mailmap_buf.buf);
+               } else {
                        wholine = find_wholine(who, wholen, buf);
+               }
+
                if (!wholine)
                        return; /* no point looking for it */
                if (name[wholen] == 0)
                        v->s = copy_line(wholine);
-               else if (!strcmp(name + wholen, "name"))
+               else if (starts_with(name + wholen, "name"))
                        v->s = copy_name(wholine);
                else if (starts_with(name + wholen, "email"))
                        v->s = copy_email(wholine, &used_atom[i]);
                else if (starts_with(name + wholen, "date"))
                        grab_date(wholine, v, name);
+
+               strbuf_release(&mailmap_buf);
        }
 
        /*
@@ -1344,6 +1712,92 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
        }
 }
 
+static void grab_signature(struct atom_value *val, int deref, struct object *obj)
+{
+       int i;
+       struct commit *commit = (struct commit *) obj;
+       struct signature_check sigc = { 0 };
+       int signature_checked = 0;
+
+       for (i = 0; i < used_atom_cnt; i++) {
+               struct used_atom *atom = &used_atom[i];
+               const char *name = atom->name;
+               struct atom_value *v = &val[i];
+               int opt;
+
+               if (!!deref != (*name == '*'))
+                       continue;
+               if (deref)
+                       name++;
+
+               if (!skip_prefix(name, "signature", &name) ||
+                   (*name && *name != ':'))
+                       continue;
+               if (!*name)
+                       name = NULL;
+               else
+                       name++;
+
+               opt = parse_signature_option(name);
+               if (opt < 0)
+                       continue;
+
+               if (!signature_checked) {
+                       check_commit_signature(commit, &sigc);
+                       signature_checked = 1;
+               }
+
+               switch (opt) {
+               case S_BARE:
+                       v->s = xstrdup(sigc.output ? sigc.output: "");
+                       break;
+               case S_SIGNER:
+                       v->s = xstrdup(sigc.signer ? sigc.signer : "");
+                       break;
+               case S_GRADE:
+                       switch (sigc.result) {
+                       case 'G':
+                               switch (sigc.trust_level) {
+                               case TRUST_UNDEFINED:
+                               case TRUST_NEVER:
+                                       v->s = xstrfmt("%c", (char)'U');
+                                       break;
+                               default:
+                                       v->s = xstrfmt("%c", (char)'G');
+                                       break;
+                               }
+                               break;
+                       case 'B':
+                       case 'E':
+                       case 'N':
+                       case 'X':
+                       case 'Y':
+                       case 'R':
+                               v->s = xstrfmt("%c", (char)sigc.result);
+                               break;
+                       }
+                       break;
+               case S_KEY:
+                       v->s = xstrdup(sigc.key ? sigc.key : "");
+                       break;
+               case S_FINGERPRINT:
+                       v->s = xstrdup(sigc.fingerprint ?
+                                      sigc.fingerprint : "");
+                       break;
+               case S_PRI_KEY_FP:
+                       v->s = xstrdup(sigc.primary_key_fingerprint ?
+                                      sigc.primary_key_fingerprint : "");
+                       break;
+               case S_TRUST_LEVEL:
+                       v->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level));
+                       break;
+               }
+       }
+
+       if (signature_checked)
+               signature_check_clear(&sigc);
+}
+
 static void find_subpos(const char *buf,
                        const char **sub, size_t *sublen,
                        const char **body, size_t *bodylen,
@@ -1358,6 +1812,7 @@ static void find_subpos(const char *buf,
 
        /* parse signature first; we might not even have a subject line */
        parse_signature(buf, end - buf, &payload, &signature);
+       strbuf_release(&payload);
 
        /* skip past header until we hit empty line */
        while (*buf && *buf != '\n') {
@@ -1375,12 +1830,12 @@ static void find_subpos(const char *buf,
        /* subject is first non-empty line */
        *sub = buf;
        /* subject goes to first empty line before signature begins */
-       if ((eol = strstr(*sub, "\n\n"))) {
+       if ((eol = strstr(*sub, "\n\n")) ||
+           (eol = strstr(*sub, "\r\n\r\n"))) {
                eol = eol < sigstart ? eol : sigstart;
-       /* check if message uses CRLF */
-       } else if (! (eol = strstr(*sub, "\r\n\r\n"))) {
+       } else {
                /* treat whole message as subject */
-               eol = strrchr(*sub, '\0');
+               eol = sigstart;
        }
        buf = eol;
        *sublen = buf - *sub;
@@ -1421,6 +1876,44 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
        }
 }
 
+static void grab_describe_values(struct atom_value *val, int deref,
+                                struct object *obj)
+{
+       struct commit *commit = (struct commit *)obj;
+       int i;
+
+       for (i = 0; i < used_atom_cnt; i++) {
+               struct used_atom *atom = &used_atom[i];
+               enum atom_type type = atom->atom_type;
+               const char *name = atom->name;
+               struct atom_value *v = &val[i];
+
+               struct child_process cmd = CHILD_PROCESS_INIT;
+               struct strbuf out = STRBUF_INIT;
+               struct strbuf err = STRBUF_INIT;
+
+               if (type != ATOM_DESCRIBE)
+                       continue;
+
+               if (!!deref != (*name == '*'))
+                       continue;
+
+               cmd.git_cmd = 1;
+               strvec_push(&cmd.args, "describe");
+               strvec_pushv(&cmd.args, atom->u.describe_args);
+               strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
+               if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
+                       error(_("failed to run 'describe'"));
+                       v->s = xstrdup("");
+                       continue;
+               }
+               strbuf_rtrim(&out);
+               v->s = strbuf_detach(&out, NULL);
+
+               strbuf_release(&err);
+       }
+}
+
 /* See grab_values */
 static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
@@ -1447,7 +1940,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp
                                v->s = xmemdupz(buf, buf_size);
                                v->s_size = buf_size;
                        } else if (atom->u.raw_data.option == RAW_LENGTH) {
-                               v->s = xstrfmt("%"PRIuMAX, (uintmax_t)buf_size);
+                               v->value = buf_size;
+                               v->s = xstrfmt("%"PRIuMAX, v->value);
                        }
                        continue;
                }
@@ -1473,9 +1967,10 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp
                        v->s = strbuf_detach(&sb, NULL);
                } else if (atom->u.contents.option == C_BODY_DEP)
                        v->s = xmemdupz(bodypos, bodylen);
-               else if (atom->u.contents.option == C_LENGTH)
-                       v->s = xstrfmt("%"PRIuMAX, (uintmax_t)strlen(subpos));
-               else if (atom->u.contents.option == C_BODY)
+               else if (atom->u.contents.option == C_LENGTH) {
+                       v->value = strlen(subpos);
+                       v->s = xstrfmt("%"PRIuMAX, v->value);
+               } else if (atom->u.contents.option == C_BODY)
                        v->s = xmemdupz(bodypos, nonsiglen);
                else if (atom->u.contents.option == C_SIG)
                        v->s = xmemdupz(sigpos, siglen);
@@ -1530,12 +2025,15 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
                grab_tag_values(val, deref, obj);
                grab_sub_body_contents(val, deref, data);
                grab_person("tagger", val, deref, buf);
+               grab_describe_values(val, deref, obj);
                break;
        case OBJ_COMMIT:
                grab_commit_values(val, deref, obj);
                grab_sub_body_contents(val, deref, data);
                grab_person("author", val, deref, buf);
                grab_person("committer", val, deref, buf);
+               grab_signature(val, deref, obj);
+               grab_describe_values(val, deref, obj);
                break;
        case OBJ_TREE:
                /* grab_tree_values(val, deref, obj, buf, sz); */
@@ -1711,7 +2209,7 @@ char *get_head_description(void)
                                    state.detached_from);
        } else if (state.bisect_in_progress)
                strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
-                           state.branch);
+                           state.bisecting_from);
        else if (state.detached_from) {
                if (state.detached_at)
                        strbuf_addf(&desc, _("(HEAD detached at %s)"),
@@ -1803,7 +2301,7 @@ static void lazy_init_worktree_map(void)
        populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
 }
 
-static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
+static char *get_worktree_path(const struct ref_array_item *ref)
 {
        struct hashmap_entry entry, *e;
        struct ref_to_worktree_entry *lookup_result;
@@ -1829,6 +2327,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
        struct object *obj;
        int i;
        struct object_info empty = OBJECT_INFO_INIT;
+       int ahead_behind_atoms = 0;
 
        CALLOC_ARRAY(ref->value, used_atom_cnt);
 
@@ -1851,6 +2350,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
                v->s_size = ATOM_SIZE_UNSPECIFIED;
                v->handler = append_atom;
+               v->value = 0;
                v->atom = atom;
 
                if (*name == '*') {
@@ -1862,7 +2362,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        refname = get_refname(atom, ref);
                else if (atom_type == ATOM_WORKTREEPATH) {
                        if (ref->kind == FILTER_REFS_BRANCHES)
-                               v->s = get_worktree_path(atom, ref);
+                               v->s = get_worktree_path(ref);
                        else
                                v->s = xstrdup("");
                        continue;
@@ -1959,6 +2459,16 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        else
                                v->s = xstrdup("");
                        continue;
+               } else if (atom_type == ATOM_AHEADBEHIND) {
+                       if (ref->counts) {
+                               const struct ahead_behind_count *count;
+                               count = ref->counts[ahead_behind_atoms++];
+                               v->s = xstrfmt("%d %d", count->ahead, count->behind);
+                       } else {
+                               /* Not a commit. */
+                               v->s = xstrdup("");
+                       }
+                       continue;
                } else
                        continue;
 
@@ -1995,17 +2505,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                return 0;
 
        /*
-        * If it is a tag object, see if we use a value that derefs
-        * the object, and if we do grab the object it refers to.
+        * If it is a tag object, see if we use the peeled value. If we do,
+        * grab the peeled OID.
         */
-       oi_deref.oid = *get_tagged_oid((struct tag *)obj);
+       if (need_tagged && peel_iterated_oid(&obj->oid, &oi_deref.oid))
+               die("bad tag");
 
-       /*
-        * NEEDSWORK: This derefs tag only once, which
-        * is good to deal with chains of trust, but
-        * is not consistent with what deref_tag() does
-        * which peels the onion to the core.
-        */
        return get_object(ref, 1, &obj, &oi_deref, err);
 }
 
@@ -2031,12 +2536,12 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom,
  * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
  * matches "refs/heads/mas*", too).
  */
-static int match_pattern(const struct ref_filter *filter, const char *refname)
+static int match_pattern(const char **patterns, const char *refname,
+                        int ignore_case)
 {
-       const char **patterns = filter->name_patterns;
        unsigned flags = 0;
 
-       if (filter->ignore_case)
+       if (ignore_case)
                flags |= WM_CASEFOLD;
 
        /*
@@ -2061,13 +2566,13 @@ static int match_pattern(const struct ref_filter *filter, const char *refname)
  * matches a pattern "refs/heads/" but not "refs/heads/m") or a
  * wildcard (e.g. the same ref matches "refs/heads/m*", too).
  */
-static int match_name_as_path(const struct ref_filter *filter, const char *refname)
+static int match_name_as_path(const char **pattern, const char *refname,
+                             int ignore_case)
 {
-       const char **pattern = filter->name_patterns;
        int namelen = strlen(refname);
        unsigned flags = WM_PATHNAME;
 
-       if (filter->ignore_case)
+       if (ignore_case)
                flags |= WM_CASEFOLD;
 
        for (; *pattern; pattern++) {
@@ -2092,8 +2597,20 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
        if (!*filter->name_patterns)
                return 1; /* No pattern always matches */
        if (filter->match_as_path)
-               return match_name_as_path(filter, refname);
-       return match_pattern(filter, refname);
+               return match_name_as_path(filter->name_patterns, refname,
+                                         filter->ignore_case);
+       return match_pattern(filter->name_patterns, refname,
+                            filter->ignore_case);
+}
+
+static int filter_exclude_match(struct ref_filter *filter, const char *refname)
+{
+       if (!filter->exclude.nr)
+               return 0;
+       if (filter->match_as_path)
+               return match_name_as_path(filter->exclude.v, refname,
+                                         filter->ignore_case);
+       return match_pattern(filter->exclude.v, refname, filter->ignore_case);
 }
 
 /*
@@ -2125,42 +2642,53 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
 
        if (!filter->name_patterns[0]) {
                /* no patterns; we have to look at everything */
-               return for_each_fullref_in("", cb, cb_data);
+               return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+                                                "", filter->exclude.v, cb, cb_data);
        }
 
-       return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
-                                           cb, cb_data);
+       return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
+                                                NULL, filter->name_patterns,
+                                                filter->exclude.v,
+                                                cb, cb_data);
 }
 
 /*
  * Given a ref (oid, refname), check if the ref belongs to the array
  * of oids. If the given ref is a tag, check if the given tag points
- * at one of the oids in the given oid array.
+ * at one of the oids in the given oid array. Returns non-zero if a
+ * match is found.
+ *
  * NEEDSWORK:
- * 1. Only a single level of indirection is obtained, we might want to
- * change this to account for multiple levels (e.g. annotated tags
- * pointing to annotated tags pointing to a commit.)
- * 2. As the refs are cached we might know what refname peels to without
+ * As the refs are cached we might know what refname peels to without
  * the need to parse the object via parse_object(). peel_ref() might be a
  * more efficient alternative to obtain the pointee.
  */
-static const struct object_id *match_points_at(struct oid_array *points_at,
-                                              const struct object_id *oid,
-                                              const char *refname)
+static int match_points_at(struct oid_array *points_at,
+                          const struct object_id *oid,
+                          const char *refname)
 {
-       const struct object_id *tagged_oid = NULL;
        struct object *obj;
 
        if (oid_array_lookup(points_at, oid) >= 0)
-               return oid;
-       obj = parse_object(the_repository, oid);
+               return 1;
+       obj = parse_object_with_flags(the_repository, oid,
+                                     PARSE_OBJECT_SKIP_HASH_CHECK);
+       while (obj && obj->type == OBJ_TAG) {
+               struct tag *tag = (struct tag *)obj;
+
+               if (parse_tag(tag) < 0) {
+                       obj = NULL;
+                       break;
+               }
+
+               if (oid_array_lookup(points_at, get_tagged_oid(tag)) >= 0)
+                       return 1;
+
+               obj = tag->tagged;
+       }
        if (!obj)
                die(_("malformed object at '%s'"), refname);
-       if (obj->type == OBJ_TAG)
-               tagged_oid = get_tagged_oid((struct tag *)obj);
-       if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0)
-               return tagged_oid;
-       return NULL;
+       return 0;
 }
 
 /*
@@ -2180,15 +2708,18 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
        return ref;
 }
 
+static void ref_array_append(struct ref_array *array, struct ref_array_item *ref)
+{
+       ALLOC_GROW(array->items, array->nr + 1, array->alloc);
+       array->items[array->nr++] = ref;
+}
+
 struct ref_array_item *ref_array_push(struct ref_array *array,
                                      const char *refname,
                                      const struct object_id *oid)
 {
        struct ref_array_item *ref = new_ref_array_item(refname, oid);
-
-       ALLOC_GROW(array->items, array->nr + 1, array->alloc);
-       array->items[array->nr++] = ref;
-
+       ref_array_append(array, ref);
        return ref;
 }
 
@@ -2225,45 +2756,36 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
        return ref_kind_from_refname(refname);
 }
 
-struct ref_filter_cbdata {
-       struct ref_array *array;
-       struct ref_filter *filter;
-       struct contains_cache contains_cache;
-       struct contains_cache no_contains_cache;
-};
-
-/*
- * A call-back given to for_each_ref().  Filter refs and keep them for
- * later object processing.
- */
-static int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,
+                           int flag, struct ref_filter *filter)
 {
-       struct ref_filter_cbdata *ref_cbdata = cb_data;
-       struct ref_filter *filter = ref_cbdata->filter;
        struct ref_array_item *ref;
        struct commit *commit = NULL;
        unsigned int kind;
 
        if (flag & REF_BAD_NAME) {
                warning(_("ignoring ref with broken name %s"), refname);
-               return 0;
+               return NULL;
        }
 
        if (flag & REF_ISBROKEN) {
                warning(_("ignoring broken ref %s"), refname);
-               return 0;
+               return NULL;
        }
 
        /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
        kind = filter_ref_kind(filter, refname);
        if (!(kind & filter->kind))
-               return 0;
+               return NULL;
 
        if (!filter_pattern_match(filter, refname))
-               return 0;
+               return NULL;
+
+       if (filter_exclude_match(filter, refname))
+               return NULL;
 
        if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
-               return 0;
+               return NULL;
 
        /*
         * A merge filter is applied on refs pointing to commits. Hence
@@ -2274,15 +2796,15 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
            filter->with_commit || filter->no_commit || filter->verbose) {
                commit = lookup_commit_reference_gently(the_repository, oid, 1);
                if (!commit)
-                       return 0;
+                       return NULL;
                /* We perform the filtering for the '--contains' option... */
                if (filter->with_commit &&
-                   !commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache))
-                       return 0;
+                   !commit_contains(filter, commit, filter->with_commit, &filter->internal.contains_cache))
+                       return NULL;
                /* ...or for the `--no-contains' option */
                if (filter->no_commit &&
-                   commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache))
-                       return 0;
+                   commit_contains(filter, commit, filter->no_commit, &filter->internal.no_contains_cache))
+                       return NULL;
        }
 
        /*
@@ -2290,11 +2812,32 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
         * to do its job and the resulting list may yet to be pruned
         * by maxcount logic.
         */
-       ref = ref_array_push(ref_cbdata->array, refname, oid);
+       ref = new_ref_array_item(refname, oid);
        ref->commit = commit;
        ref->flag = flag;
        ref->kind = kind;
 
+       return ref;
+}
+
+struct ref_filter_cbdata {
+       struct ref_array *array;
+       struct ref_filter *filter;
+};
+
+/*
+ * A call-back given to for_each_ref().  Filter refs and keep them for
+ * later object processing.
+ */
+static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+       struct ref_filter_cbdata *ref_cbdata = cb_data;
+       struct ref_array_item *ref;
+
+       ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);
+       if (ref)
+               ref_array_append(ref_cbdata->array, ref);
+
        return 0;
 }
 
@@ -2308,9 +2851,53 @@ static void free_array_item(struct ref_array_item *item)
                        free((char *)item->value[i].s);
                free(item->value);
        }
+       free(item->counts);
        free(item);
 }
 
+struct ref_filter_and_format_cbdata {
+       struct ref_filter *filter;
+       struct ref_format *format;
+
+       struct ref_filter_and_format_internal {
+               int count;
+       } internal;
+};
+
+static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+       struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
+       struct ref_array_item *ref;
+       struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
+
+       ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);
+       if (!ref)
+               return 0;
+
+       if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
+               die("%s", err.buf);
+
+       if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
+               fwrite(output.buf, 1, output.len, stdout);
+               putchar('\n');
+       }
+
+       strbuf_release(&output);
+       strbuf_release(&err);
+       free_array_item(ref);
+
+       /*
+        * Increment the running count of refs that match the filter. If
+        * max_count is set and we've reached the max, stop the ref
+        * iteration by returning a nonzero value.
+        */
+       if (ref_cbdata->format->array_opts.max_count &&
+           ++ref_cbdata->internal.count >= ref_cbdata->format->array_opts.max_count)
+               return 1;
+
+       return 0;
+}
+
 /* Free all memory allocated for ref_array */
 void ref_array_clear(struct ref_array *array)
 {
@@ -2336,41 +2923,32 @@ void ref_array_clear(struct ref_array *array)
                free_worktrees(ref_to_worktree_map.worktrees);
                ref_to_worktree_map.worktrees = NULL;
        }
+
+       FREE_AND_NULL(array->counts);
 }
 
 #define EXCLUDE_REACHED 0
 #define INCLUDE_REACHED 1
 static void reach_filter(struct ref_array *array,
-                        struct commit_list *check_reachable,
+                        struct commit_list **check_reachable,
                         int include_reached)
 {
-       struct rev_info revs;
        int i, old_nr;
        struct commit **to_clear;
-       struct commit_list *cr;
 
-       if (!check_reachable)
+       if (!*check_reachable)
                return;
 
        CALLOC_ARRAY(to_clear, array->nr);
-
-       repo_init_revisions(the_repository, &revs, NULL);
-
        for (i = 0; i < array->nr; i++) {
                struct ref_array_item *item = array->items[i];
-               add_pending_object(&revs, &item->commit->object, item->refname);
                to_clear[i] = item->commit;
        }
 
-       for (cr = check_reachable; cr; cr = cr->next) {
-               struct commit *merge_commit = cr->item;
-               merge_commit->object.flags |= UNINTERESTING;
-               add_pending_object(&revs, &merge_commit->object, "");
-       }
-
-       revs.limited = 1;
-       if (prepare_revision_walk(&revs))
-               die(_("revision walk setup failed"));
+       tips_reachable_from_bases(the_repository,
+                                 *check_reachable,
+                                 to_clear, array->nr,
+                                 UNINTERESTING);
 
        old_nr = array->nr;
        array->nr = 0;
@@ -2389,37 +2967,63 @@ static void reach_filter(struct ref_array *array,
 
        clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
 
-       while (check_reachable) {
-               struct commit *merge_commit = pop_commit(&check_reachable);
+       while (*check_reachable) {
+               struct commit *merge_commit = pop_commit(check_reachable);
                clear_commit_marks(merge_commit, ALL_REV_FLAGS);
        }
 
-       release_revisions(&revs);
        free(to_clear);
 }
 
-/*
- * API for filtering a set of refs. Based on the type of refs the user
- * has requested, we iterate through those refs and apply filters
- * as per the given ref_filter structure and finally store the
- * filtered refs in the ref_array structure.
- */
-int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type)
+void filter_ahead_behind(struct repository *r,
+                        struct ref_format *format,
+                        struct ref_array *array)
 {
-       struct ref_filter_cbdata ref_cbdata;
-       int save_commit_buffer_orig;
-       int ret = 0;
+       struct commit **commits;
+       size_t commits_nr = format->bases.nr + array->nr;
 
-       ref_cbdata.array = array;
-       ref_cbdata.filter = filter;
+       if (!format->bases.nr || !array->nr)
+               return;
 
-       filter->kind = type & FILTER_REFS_KIND_MASK;
+       ALLOC_ARRAY(commits, commits_nr);
+       for (size_t i = 0; i < format->bases.nr; i++)
+               commits[i] = format->bases.items[i].util;
 
-       save_commit_buffer_orig = save_commit_buffer;
-       save_commit_buffer = 0;
+       ALLOC_ARRAY(array->counts, st_mult(format->bases.nr, array->nr));
+
+       commits_nr = format->bases.nr;
+       array->counts_nr = 0;
+       for (size_t i = 0; i < array->nr; i++) {
+               const char *name = array->items[i]->refname;
+               commits[commits_nr] = lookup_commit_reference_by_name(name);
+
+               if (!commits[commits_nr])
+                       continue;
+
+               CALLOC_ARRAY(array->items[i]->counts, format->bases.nr);
+               for (size_t j = 0; j < format->bases.nr; j++) {
+                       struct ahead_behind_count *count;
+                       count = &array->counts[array->counts_nr++];
+                       count->tip_index = commits_nr;
+                       count->base_index = j;
+
+                       array->items[i]->counts[j] = count;
+               }
+               commits_nr++;
+       }
+
+       ahead_behind(r, commits, commits_nr, array->counts, array->counts_nr);
+       free(commits);
+}
+
+static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data)
+{
+       int ret = 0;
+
+       filter->kind = type & FILTER_REFS_KIND_MASK;
 
-       init_contains_cache(&ref_cbdata.contains_cache);
-       init_contains_cache(&ref_cbdata.no_contains_cache);
+       init_contains_cache(&filter->internal.contains_cache);
+       init_contains_cache(&filter->internal.no_contains_cache);
 
        /*  Simple per-ref filtering */
        if (!filter->kind)
@@ -2432,28 +3036,96 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
                 * of filter_ref_kind().
                 */
                if (filter->kind == FILTER_REFS_BRANCHES)
-                       ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata);
+                       ret = for_each_fullref_in("refs/heads/", fn, cb_data);
                else if (filter->kind == FILTER_REFS_REMOTES)
-                       ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata);
+                       ret = for_each_fullref_in("refs/remotes/", fn, cb_data);
                else if (filter->kind == FILTER_REFS_TAGS)
-                       ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata);
+                       ret = for_each_fullref_in("refs/tags/", fn, cb_data);
                else if (filter->kind & FILTER_REFS_ALL)
-                       ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata);
+                       ret = for_each_fullref_in_pattern(filter, fn, cb_data);
                if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
-                       head_ref(ref_filter_handler, &ref_cbdata);
+                       head_ref(fn, cb_data);
        }
 
-       clear_contains_cache(&ref_cbdata.contains_cache);
-       clear_contains_cache(&ref_cbdata.no_contains_cache);
+       clear_contains_cache(&filter->internal.contains_cache);
+       clear_contains_cache(&filter->internal.no_contains_cache);
+
+       return ret;
+}
+
+/*
+ * API for filtering a set of refs. Based on the type of refs the user
+ * has requested, we iterate through those refs and apply filters
+ * as per the given ref_filter structure and finally store the
+ * filtered refs in the ref_array structure.
+ */
+int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type)
+{
+       struct ref_filter_cbdata ref_cbdata;
+       int save_commit_buffer_orig;
+       int ret = 0;
+
+       ref_cbdata.array = array;
+       ref_cbdata.filter = filter;
+
+       save_commit_buffer_orig = save_commit_buffer;
+       save_commit_buffer = 0;
+
+       ret = do_filter_refs(filter, type, filter_one, &ref_cbdata);
 
        /*  Filters that need revision walking */
-       reach_filter(array, filter->reachable_from, INCLUDE_REACHED);
-       reach_filter(array, filter->unreachable_from, EXCLUDE_REACHED);
+       reach_filter(array, &filter->reachable_from, INCLUDE_REACHED);
+       reach_filter(array, &filter->unreachable_from, EXCLUDE_REACHED);
 
        save_commit_buffer = save_commit_buffer_orig;
        return ret;
 }
 
+static inline int can_do_iterative_format(struct ref_filter *filter,
+                                         struct ref_sorting *sorting,
+                                         struct ref_format *format)
+{
+       /*
+        * Filtering & formatting results within a single ref iteration
+        * callback is not compatible with options that require
+        * post-processing a filtered ref_array. These include:
+        * - filtering on reachability
+        * - sorting the filtered results
+        * - including ahead-behind information in the formatted output
+        */
+       return !(filter->reachable_from ||
+                filter->unreachable_from ||
+                sorting ||
+                format->bases.nr);
+}
+
+void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
+                           struct ref_sorting *sorting,
+                           struct ref_format *format)
+{
+       if (can_do_iterative_format(filter, sorting, format)) {
+               int save_commit_buffer_orig;
+               struct ref_filter_and_format_cbdata ref_cbdata = {
+                       .filter = filter,
+                       .format = format,
+               };
+
+               save_commit_buffer_orig = save_commit_buffer;
+               save_commit_buffer = 0;
+
+               do_filter_refs(filter, type, filter_and_format_one, &ref_cbdata);
+
+               save_commit_buffer = save_commit_buffer_orig;
+       } else {
+               struct ref_array array = { 0 };
+               filter_refs(&array, filter, type);
+               filter_ahead_behind(the_repository, format, &array);
+               ref_array_sort(sorting, &array);
+               print_formatted_ref_array(&array, format);
+               ref_array_clear(&array);
+       }
+}
+
 static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b)
 {
        if (!(a->kind ^ b->kind))
@@ -2571,7 +3243,8 @@ void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting,
 
 void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
 {
-       QSORT_S(array->items, array->nr, compare_refs, sorting);
+       if (sorting)
+               QSORT_S(array->items, array->nr, compare_refs, sorting);
 }
 
 static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state)
@@ -2642,6 +3315,29 @@ int format_ref_array_item(struct ref_array_item *info,
        return 0;
 }
 
+void print_formatted_ref_array(struct ref_array *array, struct ref_format *format)
+{
+       int total;
+       struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
+
+       total = format->array_opts.max_count;
+       if (!total || array->nr < total)
+               total = array->nr;
+       for (int i = 0; i < total; i++) {
+               strbuf_reset(&err);
+               strbuf_reset(&output);
+               if (format_ref_array_item(array->items[i], format, &output, &err))
+                       die("%s", err.buf);
+               if (output.len || !format->array_opts.omit_empty) {
+                       fwrite(output.buf, 1, output.len, stdout);
+                       putchar('\n');
+               }
+       }
+
+       strbuf_release(&err);
+       strbuf_release(&output);
+}
+
 void pretty_print_ref(const char *name, const struct object_id *oid,
                      struct ref_format *format)
 {
@@ -2677,18 +3373,6 @@ static int parse_sorting_atom(const char *atom)
        return res;
 }
 
-/*  If no sorting option is given, use refname to sort as default */
-static struct ref_sorting *ref_default_sorting(void)
-{
-       static const char cstr_name[] = "refname";
-
-       struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));
-
-       sorting->next = NULL;
-       sorting->atom = parse_sorting_atom(cstr_name);
-       return sorting;
-}
-
 static void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
 {
        struct ref_sorting *s;
@@ -2712,9 +3396,7 @@ struct ref_sorting *ref_sorting_options(struct string_list *options)
        struct string_list_item *item;
        struct ref_sorting *sorting = NULL, **tail = &sorting;
 
-       if (!options->nr) {
-               sorting = ref_default_sorting();
-       } else {
+       if (options->nr) {
                for_each_string_list_item(item, options)
                        parse_ref_sorting(tail, item->string);
        }
@@ -2745,7 +3427,7 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
 
        BUG_ON_OPT_NEG(unset);
 
-       if (get_oid(arg, &oid))
+       if (repo_get_oid(the_repository, arg, &oid))
                die(_("malformed object name %s"), arg);
 
        merge_commit = lookup_commit_reference_gently(the_repository, &oid, 0);
@@ -2760,3 +3442,20 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
 
        return 0;
 }
+
+void ref_filter_init(struct ref_filter *filter)
+{
+       struct ref_filter blank = REF_FILTER_INIT;
+       memcpy(filter, &blank, sizeof(blank));
+}
+
+void ref_filter_clear(struct ref_filter *filter)
+{
+       strvec_clear(&filter->exclude);
+       oid_array_clear(&filter->points_at);
+       free_commit_list(filter->with_commit);
+       free_commit_list(filter->no_commit);
+       free_commit_list(filter->reachable_from);
+       free_commit_list(filter->unreachable_from);
+       ref_filter_init(filter);
+}
index aa0eea4ecf591edd0620dc47db0e0bfc3e5c4208..07cd6f6da3da7e3950dc77538baf0f71950630e1 100644 (file)
@@ -1,10 +1,12 @@
 #ifndef REF_FILTER_H
 #define REF_FILTER_H
 
+#include "gettext.h"
 #include "oid-array.h"
-#include "refs.h"
 #include "commit.h"
-#include "parse-options.h"
+#include "string-list.h"
+#include "strvec.h"
+#include "commit-reach.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -24,6 +26,8 @@
 
 struct atom_value;
 struct ref_sorting;
+struct ahead_behind_count;
+struct option;
 
 enum ref_sorting_order {
        REF_SORTING_REVERSE = 1<<0,
@@ -40,6 +44,8 @@ struct ref_array_item {
        const char *symref;
        struct commit *commit;
        struct atom_value *value;
+       struct ahead_behind_count **counts;
+
        char refname[FLEX_ARRAY];
 };
 
@@ -47,10 +53,14 @@ struct ref_array {
        int nr, alloc;
        struct ref_array_item **items;
        struct rev_info *revs;
+
+       struct ahead_behind_count *counts;
+       size_t counts_nr;
 };
 
 struct ref_filter {
        const char **name_patterns;
+       struct strvec exclude;
        struct oid_array points_at;
        struct commit_list *with_commit;
        struct commit_list *no_commit;
@@ -65,6 +75,11 @@ struct ref_filter {
                lines;
        int abbrev,
                verbose;
+
+       struct {
+               struct contains_cache contains_cache;
+               struct contains_cache no_contains_cache;
+       } internal;
 };
 
 struct ref_format {
@@ -75,14 +90,28 @@ struct ref_format {
        const char *format;
        const char *rest;
        int quote_style;
-       int use_rest;
        int use_color;
 
        /* Internal state to ref-filter */
        int need_color_reset_at_eol;
+
+       /* List of bases for ahead-behind counts. */
+       struct string_list bases;
+
+       struct {
+               int max_count;
+               int omit_empty;
+       } array_opts;
 };
 
-#define REF_FORMAT_INIT { .use_color = -1 }
+#define REF_FILTER_INIT { \
+       .points_at = OID_ARRAY_INIT, \
+       .exclude = STRVEC_INIT, \
+}
+#define REF_FORMAT_INIT {             \
+       .use_color = -1,              \
+       .bases = STRING_LIST_INIT_DUP, \
+}
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
@@ -96,6 +125,9 @@ struct ref_format {
 #define OPT_REF_SORT(var) \
        OPT_STRING_LIST(0, "sort", (var), \
                        N_("key"), N_("field name to sort on"))
+#define OPT_REF_FILTER_EXCLUDE(var) \
+       OPT_STRVEC(0, "exclude", &(var)->exclude, \
+                  N_("pattern"), N_("exclude refs which match pattern"))
 
 /*
  * API for filtering a set of refs. Based on the type of refs the user
@@ -104,6 +136,14 @@ struct ref_format {
  * filtered refs in the ref_array structure.
  */
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
+/*
+ * Filter refs using the given ref_filter and type, sort the contents
+ * according to the given ref_sorting, format the filtered refs with the
+ * given ref_format, and print them to stdout.
+ */
+void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
+                           struct ref_sorting *sorting,
+                           struct ref_format *format);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
@@ -128,6 +168,12 @@ char *get_head_description(void);
 /*  Set up translated strings in the output. */
 void setup_ref_filter_porcelain_msg(void);
 
+/*
+ * Print up to maxcount ref_array elements to stdout using the given
+ * ref_format.
+ */
+void print_formatted_ref_array(struct ref_array *array, struct ref_format *format);
+
 /*
  * Print a single ref, outside of any ref-filter. Note that the
  * name must be a fully qualified refname.
@@ -143,4 +189,18 @@ struct ref_array_item *ref_array_push(struct ref_array *array,
                                      const char *refname,
                                      const struct object_id *oid);
 
+/*
+ * If the provided format includes ahead-behind atoms, then compute the
+ * ahead-behind values for the array of filtered references. Must be
+ * called after filter_refs() but before outputting the formatted refs.
+ *
+ * If this is not called, then any ahead-behind atoms will be blank.
+ */
+void filter_ahead_behind(struct repository *r,
+                        struct ref_format *format,
+                        struct ref_array *array);
+
+void ref_filter_init(struct ref_filter *filter);
+void ref_filter_clear(struct ref_filter *filter);
+
 #endif /*  REF_FILTER_H  */
index 7aa6595a51f757ec2d25ed5d96c805ff1dbf13d2..d216f6f966da91459ea946790be56ba011760170 100644 (file)
@@ -1,7 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
 #include "refs.h"
 #include "diff.h"
+#include "repository.h"
 #include "revision.h"
 #include "string-list.h"
 #include "reflog-walk.h"
@@ -55,7 +56,7 @@ static void free_complete_reflog(struct complete_reflogs *array)
        free(array);
 }
 
-static void complete_reflogs_clear(void *util, const char *str)
+static void complete_reflogs_clear(void *util, const char *str UNUSED)
 {
        struct complete_reflogs *array = util;
        free_complete_reflog(array);
index 8076f10d9fbc0dd5588582f5bfec105416037233..4d93a269571980772c49cb25d37749a033482b4f 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef REFLOG_WALK_H
 #define REFLOG_WALK_H
 
-#include "cache.h"
-
 struct commit;
 struct reflog_walk_info;
 struct date_mode;
index d258fd31995fbb4f8a198ba0bd275564182fe43c..0a1bc35e8cd2c82771c0272dcdc08f8b99ba73e7 100644 (file)
--- a/reflog.c
+++ b/reflog.c
@@ -1,9 +1,11 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "object-store-ll.h"
 #include "reflog.h"
 #include "refs.h"
 #include "revision.h"
-#include "worktree.h"
+#include "tree.h"
+#include "tree-walk.h"
 
 /* Remember to update object flag allocation in object.h */
 #define INCOMPLETE     (1u<<10)
@@ -28,7 +30,8 @@ static int tree_is_complete(const struct object_id *oid)
        if (!tree->buffer) {
                enum object_type type;
                unsigned long size;
-               void *data = read_object_file(oid, &type, &size);
+               void *data = repo_read_object_file(the_repository, oid, &type,
+                                                  &size);
                if (!data) {
                        tree->object.flags |= INCOMPLETE;
                        return 0;
@@ -39,7 +42,7 @@ static int tree_is_complete(const struct object_id *oid)
        init_tree_desc(&desc, tree->buffer, tree->size);
        complete = 1;
        while (tree_entry(&desc, &entry)) {
-               if (!has_object_file(&entry.oid) ||
+               if (!repo_has_object_file(the_repository, &entry.oid) ||
                    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
                        tree->object.flags |= INCOMPLETE;
                        complete = 0;
@@ -186,14 +189,13 @@ static void mark_reachable(struct expire_reflog_policy_cb *cb)
                struct commit *commit = pop_commit(&pending);
                if (commit->object.flags & REACHABLE)
                        continue;
-               if (parse_commit(commit))
+               if (repo_parse_commit(the_repository, commit))
                        continue;
                commit->object.flags |= REACHABLE;
                if (commit->date < expire_limit) {
                        commit_list_insert(commit, &leftover);
                        continue;
                }
-               commit->object.flags |= REACHABLE;
                parent = commit->parents;
                while (parent) {
                        commit = parent->item;
@@ -312,16 +314,9 @@ static int push_tip_to_list(const char *refname UNUSED,
 
 static int is_head(const char *refname)
 {
-       switch (ref_type(refname)) {
-       case REF_TYPE_OTHER_PSEUDOREF:
-       case REF_TYPE_MAIN_PSEUDOREF:
-               if (parse_worktree_ref(refname, NULL, NULL, &refname))
-                       BUG("not a worktree ref: %s", refname);
-               break;
-       default:
-               break;
-       }
-       return !strcmp(refname, "HEAD");
+       const char *stripped_refname;
+       parse_worktree_ref(refname, NULL, NULL, &stripped_refname);
+       return !strcmp(stripped_refname, "HEAD");
 }
 
 void reflog_expiry_prepare(const char *refname,
@@ -378,6 +373,9 @@ void reflog_expiry_cleanup(void *cb_data)
                clear_commit_marks(cb->tip_commit, REACHABLE);
                break;
        }
+       for (elem = cb->mark_list; elem; elem = elem->next)
+               clear_commit_marks(elem->item, REACHABLE);
+       free_commit_list(cb->mark_list);
 }
 
 int count_reflog_ent(struct object_id *ooid UNUSED,
diff --git a/refs.c b/refs.c
index c89d558892569b6326d092eb61a7bba28ef4b3d4..20e8f1ff1f114ab0dff0927a4456fbca639c6fb3 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -2,40 +2,64 @@
  * The backend-independent part of the reference module.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
 #include "hashmap.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "iterator.h"
 #include "refs.h"
 #include "refs/refs-internal.h"
 #include "run-command.h"
 #include "hook.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "object.h"
+#include "path.h"
 #include "tag.h"
 #include "submodule.h"
 #include "worktree.h"
 #include "strvec.h"
 #include "repository.h"
+#include "setup.h"
 #include "sigchain.h"
 #include "date.h"
 #include "commit.h"
+#include "wildmatch.h"
 
 /*
  * List of all available backends
  */
-static struct ref_storage_be *refs_backends = &refs_be_files;
+static const struct ref_storage_be *refs_backends[] = {
+       [REF_STORAGE_FORMAT_FILES] = &refs_be_files,
+};
 
-static struct ref_storage_be *find_ref_storage_backend(const char *name)
+static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
 {
-       struct ref_storage_be *be;
-       for (be = refs_backends; be; be = be->next)
-               if (!strcmp(be->name, name))
-                       return be;
+       if (ref_storage_format < ARRAY_SIZE(refs_backends))
+               return refs_backends[ref_storage_format];
        return NULL;
 }
 
+unsigned int ref_storage_format_by_name(const char *name)
+{
+       for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
+               if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
+                       return i;
+       return REF_STORAGE_FORMAT_UNKNOWN;
+}
+
+const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+{
+       const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
+       if (!be)
+               return "unknown";
+       return be->name;
+}
+
 /*
  * How to handle various characters in refnames:
  * 0: An acceptable character for refs
@@ -367,8 +391,8 @@ char *resolve_refdup(const char *refname, int resolve_flags,
                                   oid, flags);
 }
 
-/* The argument to filter_refs */
-struct ref_filter {
+/* The argument to for_each_filter_refs */
+struct for_each_ref_filter {
        const char *pattern;
        const char *prefix;
        each_ref_fn *fn;
@@ -401,10 +425,11 @@ int ref_exists(const char *refname)
        return refs_ref_exists(get_main_ref_store(the_repository), refname);
 }
 
-static int filter_refs(const char *refname, const struct object_id *oid,
-                          int flags, void *data)
+static int for_each_filter_refs(const char *refname,
+                               const struct object_id *oid,
+                               int flags, void *data)
 {
-       struct ref_filter *filter = (struct ref_filter *)data;
+       struct for_each_ref_filter *filter = data;
 
        if (wildmatch(filter->pattern, refname, 0))
                return 0;
@@ -561,7 +586,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        const char *prefix, void *cb_data)
 {
        struct strbuf real_pattern = STRBUF_INIT;
-       struct ref_filter filter;
+       struct for_each_ref_filter filter;
        int ret;
 
        if (!prefix && !starts_with(pattern, "refs/"))
@@ -581,7 +606,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        filter.prefix = prefix;
        filter.fn = fn;
        filter.cb_data = cb_data;
-       ret = for_each_ref(filter_refs, &filter);
+       ret = for_each_ref(for_each_filter_refs, &filter);
 
        strbuf_release(&real_pattern);
        return ret;
@@ -811,7 +836,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)
        return repo_dwim_log(the_repository, str, len, oid, log);
 }
 
-static int is_per_worktree_ref(const char *refname)
+int is_per_worktree_ref(const char *refname)
 {
        return starts_with(refname, "refs/worktree/") ||
               starts_with(refname, "refs/bisect/") ||
@@ -827,37 +852,63 @@ static int is_pseudoref_syntax(const char *refname)
                        return 0;
        }
 
+       /*
+        * HEAD is not a pseudoref, but it certainly uses the
+        * pseudoref syntax.
+        */
        return 1;
 }
 
-static int is_main_pseudoref_syntax(const char *refname)
-{
-       return skip_prefix(refname, "main-worktree/", &refname) &&
-               *refname &&
-               is_pseudoref_syntax(refname);
+static int is_current_worktree_ref(const char *ref) {
+       return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
 }
 
-static int is_other_pseudoref_syntax(const char *refname)
+enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
+                                         const char **worktree_name, int *worktree_name_length,
+                                         const char **bare_refname)
 {
-       if (!skip_prefix(refname, "worktrees/", &refname))
-               return 0;
-       refname = strchr(refname, '/');
-       if (!refname || !refname[1])
-               return 0;
-       return is_pseudoref_syntax(refname + 1);
-}
+       const char *name_dummy;
+       int name_length_dummy;
+       const char *ref_dummy;
 
-enum ref_type ref_type(const char *refname)
-{
-       if (is_per_worktree_ref(refname))
-               return REF_TYPE_PER_WORKTREE;
-       if (is_pseudoref_syntax(refname))
-               return REF_TYPE_PSEUDOREF;
-       if (is_main_pseudoref_syntax(refname))
-               return REF_TYPE_MAIN_PSEUDOREF;
-       if (is_other_pseudoref_syntax(refname))
-               return REF_TYPE_OTHER_PSEUDOREF;
-       return REF_TYPE_NORMAL;
+       if (!worktree_name)
+               worktree_name = &name_dummy;
+       if (!worktree_name_length)
+               worktree_name_length = &name_length_dummy;
+       if (!bare_refname)
+               bare_refname = &ref_dummy;
+
+       if (skip_prefix(maybe_worktree_ref, "worktrees/", bare_refname)) {
+               const char *slash = strchr(*bare_refname, '/');
+
+               *worktree_name = *bare_refname;
+               if (!slash) {
+                       *worktree_name_length = strlen(*worktree_name);
+
+                       /* This is an error condition, and the caller tell because the bare_refname is "" */
+                       *bare_refname = *worktree_name + *worktree_name_length;
+                       return REF_WORKTREE_OTHER;
+               }
+
+               *worktree_name_length = slash - *bare_refname;
+               *bare_refname = slash + 1;
+
+               if (is_current_worktree_ref(*bare_refname))
+                       return REF_WORKTREE_OTHER;
+       }
+
+       *worktree_name = NULL;
+       *worktree_name_length = 0;
+
+       if (skip_prefix(maybe_worktree_ref, "main-worktree/", bare_refname)
+           && is_current_worktree_ref(*bare_refname))
+               return REF_WORKTREE_MAIN;
+
+       *bare_refname = maybe_worktree_ref;
+       if (is_current_worktree_ref(maybe_worktree_ref))
+               return REF_WORKTREE_CURRENT;
+
+       return REF_WORKTREE_SHARED;
 }
 
 long get_files_ref_lock_timeout_ms(void)
@@ -1284,65 +1335,68 @@ int update_ref(const char *msg, const char *refname,
                               old_oid, flags, onerr);
 }
 
+/*
+ * Check that the string refname matches a rule of the form
+ * "{prefix}%.*s{suffix}". So "foo/bar/baz" would match the rule
+ * "foo/%.*s/baz", and return the string "bar".
+ */
+static const char *match_parse_rule(const char *refname, const char *rule,
+                                   size_t *len)
+{
+       /*
+        * Check that rule matches refname up to the first percent in the rule.
+        * We can bail immediately if not, but otherwise we leave "rule" at the
+        * %-placeholder, and "refname" at the start of the potential matched
+        * name.
+        */
+       while (*rule != '%') {
+               if (!*rule)
+                       BUG("rev-parse rule did not have percent");
+               if (*refname++ != *rule++)
+                       return NULL;
+       }
+
+       /*
+        * Check that our "%" is the expected placeholder. This assumes there
+        * are no other percents (placeholder or quoted) in the string, but
+        * that is sufficient for our rev-parse rules.
+        */
+       if (!skip_prefix(rule, "%.*s", &rule))
+               return NULL;
+
+       /*
+        * And now check that our suffix (if any) matches.
+        */
+       if (!strip_suffix(refname, rule, len))
+               return NULL;
+
+       return refname; /* len set by strip_suffix() */
+}
+
 char *refs_shorten_unambiguous_ref(struct ref_store *refs,
                                   const char *refname, int strict)
 {
        int i;
-       static char **scanf_fmts;
-       static int nr_rules;
-       char *short_name;
        struct strbuf resolved_buf = STRBUF_INIT;
 
-       if (!nr_rules) {
-               /*
-                * Pre-generate scanf formats from ref_rev_parse_rules[].
-                * Generate a format suitable for scanf from a
-                * ref_rev_parse_rules rule by interpolating "%s" at the
-                * location of the "%.*s".
-                */
-               size_t total_len = 0;
-               size_t offset = 0;
-
-               /* the rule list is NULL terminated, count them first */
-               for (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)
-                       /* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
-                       total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
-
-               scanf_fmts = xmalloc(st_add(st_mult(sizeof(char *), nr_rules), total_len));
-
-               offset = 0;
-               for (i = 0; i < nr_rules; i++) {
-                       assert(offset < total_len);
-                       scanf_fmts[i] = (char *)&scanf_fmts[nr_rules] + offset;
-                       offset += xsnprintf(scanf_fmts[i], total_len - offset,
-                                           ref_rev_parse_rules[i], 2, "%s") + 1;
-               }
-       }
-
-       /* bail out if there are no rules */
-       if (!nr_rules)
-               return xstrdup(refname);
-
-       /* buffer for scanf result, at most refname must fit */
-       short_name = xstrdup(refname);
-
        /* skip first rule, it will always match */
-       for (i = nr_rules - 1; i > 0 ; --i) {
+       for (i = NUM_REV_PARSE_RULES - 1; i > 0 ; --i) {
                int j;
                int rules_to_fail = i;
-               int short_name_len;
+               const char *short_name;
+               size_t short_name_len;
 
-               if (1 != sscanf(refname, scanf_fmts[i], short_name))
+               short_name = match_parse_rule(refname, ref_rev_parse_rules[i],
+                                             &short_name_len);
+               if (!short_name)
                        continue;
 
-               short_name_len = strlen(short_name);
-
                /*
                 * in strict mode, all (except the matched one) rules
                 * must fail to resolve to a valid non-ambiguous ref
                 */
                if (strict)
-                       rules_to_fail = nr_rules;
+                       rules_to_fail = NUM_REV_PARSE_RULES;
 
                /*
                 * check if the short name resolves to a valid ref,
@@ -1362,7 +1416,8 @@ char *refs_shorten_unambiguous_ref(struct ref_store *refs,
                         */
                        strbuf_reset(&resolved_buf);
                        strbuf_addf(&resolved_buf, rule,
-                                   short_name_len, short_name);
+                                   cast_size_t_to_int(short_name_len),
+                                   short_name);
                        if (refs_ref_exists(refs, resolved_buf.buf))
                                break;
                }
@@ -1373,12 +1428,11 @@ char *refs_shorten_unambiguous_ref(struct ref_store *refs,
                 */
                if (j == rules_to_fail) {
                        strbuf_release(&resolved_buf);
-                       return short_name;
+                       return xmemdupz(short_name, short_name_len);
                }
        }
 
        strbuf_release(&resolved_buf);
-       free(short_name);
        return xstrdup(refname);
 }
 
@@ -1388,9 +1442,8 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                                            refname, strict);
 }
 
-static struct string_list *hide_refs;
-
-int parse_hide_refs_config(const char *var, const char *value, const char *section)
+int parse_hide_refs_config(const char *var, const char *value, const char *section,
+                          struct strvec *hide_refs)
 {
        const char *key;
        if (!strcmp("transfer.hiderefs", var) ||
@@ -1401,27 +1454,23 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
 
                if (!value)
                        return config_error_nonbool(var);
-               ref = xstrdup(value);
+
+               /* drop const to remove trailing '/' characters */
+               ref = (char *)strvec_push(hide_refs, value);
                len = strlen(ref);
                while (len && ref[len - 1] == '/')
                        ref[--len] = '\0';
-               if (!hide_refs) {
-                       CALLOC_ARRAY(hide_refs, 1);
-                       hide_refs->strdup_strings = 1;
-               }
-               string_list_append(hide_refs, ref);
        }
        return 0;
 }
 
-int ref_is_hidden(const char *refname, const char *refname_full)
+int ref_is_hidden(const char *refname, const char *refname_full,
+                 const struct strvec *hide_refs)
 {
        int i;
 
-       if (!hide_refs)
-               return 0;
        for (i = hide_refs->nr - 1; i >= 0; i--) {
-               const char *match = hide_refs->items[i].string;
+               const char *match = hide_refs->v[i];
                const char *subject;
                int neg = 0;
                const char *p;
@@ -1447,6 +1496,30 @@ int ref_is_hidden(const char *refname, const char *refname_full)
        return 0;
 }
 
+const char **hidden_refs_to_excludes(const struct strvec *hide_refs)
+{
+       const char **pattern;
+       for (pattern = hide_refs->v; *pattern; pattern++) {
+               /*
+                * We can't feed any excludes from hidden refs config
+                * sections, since later rules may override previous
+                * ones. For example, with rules "refs/foo" and
+                * "!refs/foo/bar", we should show "refs/foo/bar" (and
+                * everything underneath it), but the earlier exclusion
+                * would cause us to skip all of "refs/foo".  We
+                * likewise don't implement the namespace stripping
+                * required for '^' rules.
+                *
+                * Both are possible to do, but complicated, so avoid
+                * populating the jump list at all if we see either of
+                * these patterns.
+                */
+               if (**pattern == '!' || **pattern == '^')
+                       return NULL;
+       }
+       return hide_refs->v;
+}
+
 const char *find_descendant_ref(const char *dirname,
                                const struct string_list *extras,
                                const struct string_list *skip)
@@ -1494,7 +1567,9 @@ int head_ref(each_ref_fn fn, void *cb_data)
 
 struct ref_iterator *refs_ref_iterator_begin(
                struct ref_store *refs,
-               const char *prefix, int trim,
+               const char *prefix,
+               const char **exclude_patterns,
+               int trim,
                enum do_for_each_ref_flags flags)
 {
        struct ref_iterator *iter;
@@ -1510,8 +1585,7 @@ struct ref_iterator *refs_ref_iterator_begin(
                }
        }
 
-       iter = refs->be->iterator_begin(refs, prefix, flags);
-
+       iter = refs->be->iterator_begin(refs, prefix, exclude_patterns, flags);
        /*
         * `iterator_begin()` already takes care of prefix, but we
         * might need to do some trimming:
@@ -1545,7 +1619,7 @@ static int do_for_each_repo_ref(struct repository *r, const char *prefix,
        if (!refs)
                return 0;
 
-       iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+       iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags);
 
        return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
 }
@@ -1555,7 +1629,7 @@ struct do_for_each_ref_help {
        void *cb_data;
 };
 
-static int do_for_each_ref_helper(struct repository *r,
+static int do_for_each_ref_helper(struct repository *r UNUSED,
                                  const char *refname,
                                  const struct object_id *oid,
                                  int flags,
@@ -1567,6 +1641,7 @@ static int do_for_each_ref_helper(struct repository *r,
 }
 
 static int do_for_each_ref(struct ref_store *refs, const char *prefix,
+                          const char **exclude_patterns,
                           each_ref_fn fn, int trim,
                           enum do_for_each_ref_flags flags, void *cb_data)
 {
@@ -1576,7 +1651,8 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
        if (!refs)
                return 0;
 
-       iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+       iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim,
+                                      flags);
 
        return do_for_each_repo_ref_iterator(the_repository, iter,
                                        do_for_each_ref_helper, &hp);
@@ -1584,7 +1660,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
 
 int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
+       return do_for_each_ref(refs, "", NULL, fn, 0, 0, cb_data);
 }
 
 int for_each_ref(each_ref_fn fn, void *cb_data)
@@ -1595,7 +1671,7 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
 int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
                         each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);
+       return do_for_each_ref(refs, prefix, NULL, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
@@ -1606,13 +1682,14 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
        return do_for_each_ref(get_main_ref_store(the_repository),
-                              prefix, fn, 0, 0, cb_data);
+                              prefix, NULL, fn, 0, 0, cb_data);
 }
 
 int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+                            const char **exclude_patterns,
                             each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data);
+       return do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);
 }
 
 int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
@@ -1623,20 +1700,21 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat
                                    DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+int for_each_namespaced_ref(const char **exclude_patterns,
+                           each_ref_fn fn, void *cb_data)
 {
        struct strbuf buf = STRBUF_INIT;
        int ret;
        strbuf_addf(&buf, "%srefs/", get_git_namespace());
        ret = do_for_each_ref(get_main_ref_store(the_repository),
-                             buf.buf, fn, 0, 0, cb_data);
+                             buf.buf, exclude_patterns, fn, 0, 0, cb_data);
        strbuf_release(&buf);
        return ret;
 }
 
 int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(refs, "", fn, 0,
+       return do_for_each_ref(refs, "", NULL, fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
@@ -1703,9 +1781,11 @@ static void find_longest_prefixes(struct string_list *out,
        strbuf_release(&prefix);
 }
 
-int for_each_fullref_in_prefixes(const char *namespace,
-                                const char **patterns,
-                                each_ref_fn fn, void *cb_data)
+int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
+                                     const char *namespace,
+                                     const char **patterns,
+                                     const char **exclude_patterns,
+                                     each_ref_fn fn, void *cb_data)
 {
        struct string_list prefixes = STRING_LIST_INIT_DUP;
        struct string_list_item *prefix;
@@ -1720,7 +1800,8 @@ int for_each_fullref_in_prefixes(const char *namespace,
 
        for_each_string_list_item(prefix, &prefixes) {
                strbuf_addstr(&buf, prefix->string);
-               ret = for_each_fullref_in(buf.buf, fn, cb_data);
+               ret = refs_for_each_fullref_in(ref_store, buf.buf,
+                                              exclude_patterns, fn, cb_data);
                if (ret)
                        break;
                strbuf_setlen(&buf, namespace_len);
@@ -1741,8 +1822,10 @@ static int refs_read_special_head(struct ref_store *ref_store,
        int result = -1;
        strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname);
 
-       if (strbuf_read_file(&content, full_path.buf, 0) < 0)
+       if (strbuf_read_file(&content, full_path.buf, 0) < 0) {
+               *failure_errno = errno;
                goto done;
+       }
 
        result = parse_loose_ref_contents(content.buf, oid, referent, type,
                                          failure_errno);
@@ -1753,15 +1836,66 @@ done:
        return result;
 }
 
+static int is_special_ref(const char *refname)
+{
+       /*
+        * Special references get written and read directly via the filesystem
+        * by the subsystems that create them. Thus, they must not go through
+        * the reference backend but must instead be read directly. It is
+        * arguable whether this behaviour is sensible, or whether it's simply
+        * a leaky abstraction enabled by us only having a single reference
+        * backend implementation. But at least for a subset of references it
+        * indeed does make sense to treat them specially:
+        *
+        * - FETCH_HEAD may contain multiple object IDs, and each one of them
+        *   carries additional metadata like where it came from.
+        *
+        * - MERGE_HEAD may contain multiple object IDs when merging multiple
+        *   heads.
+        *
+        * There are some exceptions that you might expect to see on this list
+        * but which are handled exclusively via the reference backend:
+        *
+        * - BISECT_EXPECTED_REV
+        *
+        * - CHERRY_PICK_HEAD
+        *
+        * - HEAD
+        *
+        * - ORIG_HEAD
+        *
+        * - "rebase-apply/" and "rebase-merge/" contain all of the state for
+        *   rebases, including some reference-like files. These are
+        *   exclusively read and written via the filesystem and never go
+        *   through the refdb.
+        *
+        * Writing or deleting references must consistently go either through
+        * the filesystem (special refs) or through the reference backend
+        * (normal ones).
+        */
+       static const char * const special_refs[] = {
+               "AUTO_MERGE",
+               "FETCH_HEAD",
+               "MERGE_AUTOSTASH",
+               "MERGE_HEAD",
+       };
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(special_refs); i++)
+               if (!strcmp(refname, special_refs[i]))
+                       return 1;
+
+       return 0;
+}
+
 int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
                      struct object_id *oid, struct strbuf *referent,
                      unsigned int *type, int *failure_errno)
 {
        assert(failure_errno);
-       if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) {
+       if (is_special_ref(refname))
                return refs_read_special_head(ref_store, refname, oid, referent,
                                              type, failure_errno);
-       }
 
        return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
                                           type, failure_errno);
@@ -1797,7 +1931,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                        return NULL;
 
                /*
-                * dwim_ref() uses REF_ISBROKEN to distinguish between
+                * repo_dwim_ref() uses REF_ISBROKEN to distinguish between
                 * missing refs and refs that were present but invalid,
                 * to complain about the latter to stderr.
                 *
@@ -1964,12 +2098,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
                                        const char *gitdir,
                                        unsigned int flags)
 {
-       const char *be_name = "files";
-       struct ref_storage_be *be = find_ref_storage_backend(be_name);
+       const struct ref_storage_be *be;
        struct ref_store *refs;
 
+       be = find_ref_storage_backend(repo->ref_storage_format);
        if (!be)
-               BUG("reference backend %s is unknown", be_name);
+               BUG("reference backend is unknown");
 
        refs = be->init(repo, gitdir, flags);
        return refs;
@@ -2100,9 +2234,9 @@ void base_ref_store_init(struct ref_store *refs, struct repository *repo,
 }
 
 /* backend functions */
-int refs_pack_refs(struct ref_store *refs, unsigned int flags)
+int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts)
 {
-       return refs->be->pack_refs(refs, flags);
+       return refs->be->pack_refs(refs, opts);
 }
 
 int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
@@ -2375,7 +2509,7 @@ int refs_verify_refname_available(struct ref_store *refs,
        strbuf_addstr(&dirname, refname + dirname.len);
        strbuf_addch(&dirname, '/');
 
-       iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
+       iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
                                       DO_FOR_EACH_INCLUDE_BROKEN);
        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
                if (skip &&
@@ -2534,13 +2668,55 @@ void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
 int refs_delete_refs(struct ref_store *refs, const char *logmsg,
                     struct string_list *refnames, unsigned int flags)
 {
+       struct ref_transaction *transaction;
+       struct strbuf err = STRBUF_INIT;
+       struct string_list_item *item;
+       int ret = 0, failures = 0;
        char *msg;
-       int retval;
+
+       if (!refnames->nr)
+               return 0;
 
        msg = normalize_reflog_message(logmsg);
-       retval = refs->be->delete_refs(refs, msg, refnames, flags);
+
+       /*
+        * Since we don't check the references' old_oids, the
+        * individual updates can't fail, so we can pack all of the
+        * updates into a single transaction.
+        */
+       transaction = ref_store_transaction_begin(refs, &err);
+       if (!transaction) {
+               ret = error("%s", err.buf);
+               goto out;
+       }
+
+       for_each_string_list_item(item, refnames) {
+               ret = ref_transaction_delete(transaction, item->string,
+                                            NULL, flags, msg, &err);
+               if (ret) {
+                       warning(_("could not delete reference %s: %s"),
+                               item->string, err.buf);
+                       strbuf_reset(&err);
+                       failures = 1;
+               }
+       }
+
+       ret = ref_transaction_commit(transaction, &err);
+       if (ret) {
+               if (refnames->nr == 1)
+                       error(_("could not delete reference %s: %s"),
+                             refnames->items[0].string, err.buf);
+               else
+                       error(_("could not delete references: %s"), err.buf);
+       }
+
+out:
+       if (!ret && failures)
+               ret = -1;
+       ref_transaction_free(transaction);
+       strbuf_release(&err);
        free(msg);
-       return retval;
+       return ret;
 }
 
 int delete_refs(const char *msg, struct string_list *refnames,
diff --git a/refs.h b/refs.h
index d6575b8c2bdf0d3e9021f7000b42308ff33203af..11b3b6cceafc1898eaf14cf80782d32061292ae9 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -1,7 +1,6 @@
 #ifndef REFS_H
 #define REFS_H
 
-#include "cache.h"
 #include "commit.h"
 
 struct object_id;
@@ -12,6 +11,9 @@ struct string_list;
 struct string_list_item;
 struct worktree;
 
+unsigned int ref_storage_format_by_name(const char *name);
+const char *ref_storage_format_to_name(unsigned int ref_storage_format);
+
 /*
  * Resolve a reference, recursively following symbolic refererences.
  *
@@ -57,13 +59,19 @@ struct worktree;
  * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
  * directory and do not consist of all caps and underscores cannot be
  * resolved. The function returns NULL for such ref names.
- * Caps and underscores refers to the special refs, such as HEAD,
+ * Caps and underscores refers to the pseudorefs, such as HEAD,
  * FETCH_HEAD and friends, that all live outside of the refs/ directory.
  */
 #define RESOLVE_REF_READING 0x01
 #define RESOLVE_REF_NO_RECURSE 0x02
 #define RESOLVE_REF_ALLOW_BAD_NAME 0x04
 
+struct pack_refs_opts {
+       unsigned int flags;
+       struct ref_exclusions *exclusions;
+       struct string_list *includes;
+};
+
 const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
                                    int resolve_flags,
@@ -159,12 +167,6 @@ int expand_ref(struct repository *r, const char *str, int len, struct object_id
 int repo_dwim_ref(struct repository *r, const char *str, int len,
                  struct object_id *oid, char **ref, int nonfatal_dangling_mark);
 int repo_dwim_log(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
-static inline int dwim_ref(const char *str, int len, struct object_id *oid,
-                          char **ref, int nonfatal_dangling_mark)
-{
-       return repo_dwim_ref(the_repository, str, len, oid, ref,
-                            nonfatal_dangling_mark);
-}
 int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
 
 /*
@@ -344,7 +346,12 @@ int for_each_ref(each_ref_fn fn, void *cb_data);
  */
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
 
+/*
+ * references matching any pattern in "exclude_patterns" are omitted from the
+ * result set on a best-effort basis.
+ */
 int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+                            const char **exclude_patterns,
                             each_ref_fn fn, void *cb_data);
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
 
@@ -352,10 +359,17 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
  * iterate all refs in "patterns" by partitioning patterns into disjoint sets
  * and iterating the longest-common prefix of each set.
  *
+ * references matching any pattern in "exclude_patterns" are omitted from the
+ * result set on a best-effort basis.
+ *
  * callers should be prepared to ignore references that they did not ask for.
  */
-int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
-                                each_ref_fn fn, void *cb_data);
+int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
+                                     const char *namespace,
+                                     const char **patterns,
+                                     const char **exclude_patterns,
+                                     each_ref_fn fn, void *cb_data);
+
 /**
  * iterate refs from the respective area.
  */
@@ -371,7 +385,12 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
                         const char *prefix, void *cb_data);
 
 int head_ref_namespaced(each_ref_fn fn, void *cb_data);
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
+/*
+ * references matching any pattern in "exclude_patterns" are omitted from the
+ * result set on a best-effort basis.
+ */
+int for_each_namespaced_ref(const char **exclude_patterns,
+                           each_ref_fn fn, void *cb_data);
 
 /* can be used to learn about broken ref and symref */
 int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
@@ -410,7 +429,7 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
  * Write a packed-refs file for the current repository.
  * flags: Combination of the above PACK_REFS_* flags.
  */
-int refs_pack_refs(struct ref_store *refs, unsigned int flags);
+int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts);
 
 /*
  * Setup reflog before using. Fill in err and return -1 on failure.
@@ -808,7 +827,8 @@ int update_ref(const char *msg, const char *refname,
               const struct object_id *new_oid, const struct object_id *old_oid,
               unsigned int flags, enum action_on_err onerr);
 
-int parse_hide_refs_config(const char *var, const char *value, const char *);
+int parse_hide_refs_config(const char *var, const char *value, const char *,
+                          struct strvec *);
 
 /*
  * Check whether a ref is hidden. If no namespace is set, both the first and
@@ -818,17 +838,42 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);
  * the ref is outside that namespace, the first parameter is NULL. The second
  * parameter always points to the full ref name.
  */
-int ref_is_hidden(const char *, const char *);
+int ref_is_hidden(const char *, const char *, const struct strvec *);
 
-enum ref_type {
-       REF_TYPE_PER_WORKTREE,    /* refs inside refs/ but not shared       */
-       REF_TYPE_PSEUDOREF,       /* refs outside refs/ in current worktree */
-       REF_TYPE_MAIN_PSEUDOREF,  /* pseudo refs from the main worktree     */
-       REF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees       */
-       REF_TYPE_NORMAL,          /* normal/shared refs inside refs/        */
+/*
+ * Returns an array of patterns to use as excluded_patterns, if none of the
+ * hidden references use the token '!' or '^'.
+ */
+const char **hidden_refs_to_excludes(const struct strvec *hide_refs);
+
+/* Is this a per-worktree ref living in the refs/ namespace? */
+int is_per_worktree_ref(const char *refname);
+
+/* Describes how a refname relates to worktrees */
+enum ref_worktree_type {
+       REF_WORKTREE_CURRENT, /* implicitly per worktree, eg. HEAD or
+                                refs/bisect/SOMETHING */
+       REF_WORKTREE_MAIN, /* explicitly in main worktree, eg.
+                             main-worktree/HEAD */
+       REF_WORKTREE_OTHER, /* explicitly in named worktree, eg.
+                              worktrees/bla/HEAD */
+       REF_WORKTREE_SHARED, /* the default, eg. refs/heads/main */
 };
 
-enum ref_type ref_type(const char *refname);
+/*
+ * Parse a `maybe_worktree_ref` as a ref that possibly refers to a worktree ref
+ * (ie. either REFNAME, main-worktree/REFNAME or worktree/WORKTREE/REFNAME). It
+ * returns what kind of ref was found, and in case of REF_WORKTREE_OTHER, the
+ * worktree name is returned in `worktree_name` (pointing into
+ * `maybe_worktree_ref`) and `worktree_name_length`. The bare refname (the
+ * refname stripped of prefixes) is returned in `bare_refname`. The
+ * `worktree_name`, `worktree_name_length` and `bare_refname` arguments may be
+ * NULL.
+ */
+enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
+                                         const char **worktree_name,
+                                         int *worktree_name_length,
+                                         const char **bare_refname);
 
 enum expire_reflog_flags {
        EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
index eed8bc94b04f68125bb84eddfc26381ad86d3157..b9775f2c37c7eb5e9de275fc3d6ad2125802eeef 100644 (file)
@@ -1,5 +1,7 @@
-
+#include "git-compat-util.h"
+#include "hex.h"
 #include "refs-internal.h"
+#include "string-list.h"
 #include "trace.h"
 
 static struct trace_key trace_refs = TRACE_KEY_INIT(REFS);
@@ -121,10 +123,10 @@ static int debug_initial_transaction_commit(struct ref_store *refs,
        return res;
 }
 
-static int debug_pack_refs(struct ref_store *ref_store, unsigned int flags)
+static int debug_pack_refs(struct ref_store *ref_store, struct pack_refs_opts *opts)
 {
        struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
-       int res = drefs->refs->be->pack_refs(drefs->refs, flags);
+       int res = drefs->refs->be->pack_refs(drefs->refs, opts);
        trace_printf_key(&trace_refs, "pack_refs: %d\n", res);
        return res;
 }
@@ -141,20 +143,6 @@ static int debug_create_symref(struct ref_store *ref_store,
        return res;
 }
 
-static int debug_delete_refs(struct ref_store *ref_store, const char *msg,
-                            struct string_list *refnames, unsigned int flags)
-{
-       struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
-       int res =
-               drefs->refs->be->delete_refs(drefs->refs, msg, refnames, flags);
-       int i;
-       trace_printf_key(&trace_refs, "delete_refs {\n");
-       for (i = 0; i < refnames->nr; i++)
-               trace_printf_key(&trace_refs, "%s\n", refnames->items[i].string);
-       trace_printf_key(&trace_refs, "}: %d\n", res);
-       return res;
-}
-
 static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
                            const char *newref, const char *logmsg)
 {
@@ -227,11 +215,12 @@ static struct ref_iterator_vtable debug_ref_iterator_vtable = {
 
 static struct ref_iterator *
 debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
-                        unsigned int flags)
+                        const char **exclude_patterns, unsigned int flags)
 {
        struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
        struct ref_iterator *res =
-               drefs->refs->be->iterator_begin(drefs->refs, prefix, flags);
+               drefs->refs->be->iterator_begin(drefs->refs, prefix,
+                                               exclude_patterns, flags);
        struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
        base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
        diter->iter = res;
@@ -437,7 +426,6 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
 }
 
 struct ref_storage_be refs_be_debug = {
-       .next = NULL,
        .name = "debug",
        .init = NULL,
        .init_db = debug_init_db,
@@ -455,7 +443,6 @@ struct ref_storage_be refs_be_debug = {
 
        .pack_refs = debug_pack_refs,
        .create_symref = debug_create_symref,
-       .delete_refs = debug_delete_refs,
        .rename_ref = debug_rename_ref,
        .copy_ref = debug_copy_ref,
 
index e4009b3c421f5bddf9b3480a9fc881cee0d2307b..b288fc97dba7b4986ccef25cdd1608eb39e349b9 100644 (file)
@@ -1,16 +1,27 @@
-#include "../cache.h"
-#include "../config.h"
+#include "../git-compat-util.h"
+#include "../copy.h"
+#include "../environment.h"
+#include "../gettext.h"
+#include "../hash.h"
+#include "../hex.h"
 #include "../refs.h"
 #include "refs-internal.h"
 #include "ref-cache.h"
 #include "packed-backend.h"
+#include "../ident.h"
 #include "../iterator.h"
 #include "../dir-iterator.h"
 #include "../lockfile.h"
 #include "../object.h"
+#include "../object-file.h"
+#include "../path.h"
 #include "../dir.h"
 #include "../chdir-notify.h"
-#include "worktree.h"
+#include "../setup.h"
+#include "../wrapper.h"
+#include "../write-or-die.h"
+#include "../revision.h"
+#include <wildmatch.h>
 
 /*
  * This backend uses the following flags in `ref_update::flags` for
@@ -138,44 +149,30 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
        return refs;
 }
 
-static void files_reflog_path_other_worktrees(struct files_ref_store *refs,
-                                             struct strbuf *sb,
-                                             const char *refname)
-{
-       const char *real_ref;
-       const char *worktree_name;
-       int length;
-
-       if (parse_worktree_ref(refname, &worktree_name, &length, &real_ref))
-               BUG("refname %s is not a other-worktree ref", refname);
-
-       if (worktree_name)
-               strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir,
-                           length, worktree_name, real_ref);
-       else
-               strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir,
-                           real_ref);
-}
-
 static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
 {
-       switch (ref_type(refname)) {
-       case REF_TYPE_PER_WORKTREE:
-       case REF_TYPE_PSEUDOREF:
+       const char *bare_refname;
+       const char *wtname;
+       int wtname_len;
+       enum ref_worktree_type wt_type = parse_worktree_ref(
+               refname, &wtname, &wtname_len, &bare_refname);
+
+       switch (wt_type) {
+       case REF_WORKTREE_CURRENT:
                strbuf_addf(sb, "%s/logs/%s", refs->base.gitdir, refname);
                break;
-       case REF_TYPE_OTHER_PSEUDOREF:
-       case REF_TYPE_MAIN_PSEUDOREF:
-               files_reflog_path_other_worktrees(refs, sb, refname);
+       case REF_WORKTREE_SHARED:
+       case REF_WORKTREE_MAIN:
+               strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, bare_refname);
                break;
-       case REF_TYPE_NORMAL:
-               strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
+       case REF_WORKTREE_OTHER:
+               strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir,
+                           wtname_len, wtname, bare_refname);
                break;
        default:
-               BUG("unknown ref type %d of ref %s",
-                   ref_type(refname), refname);
+               BUG("unknown ref type %d of ref %s", wt_type, refname);
        }
 }
 
@@ -183,22 +180,25 @@ static void files_ref_path(struct files_ref_store *refs,
                           struct strbuf *sb,
                           const char *refname)
 {
-       switch (ref_type(refname)) {
-       case REF_TYPE_PER_WORKTREE:
-       case REF_TYPE_PSEUDOREF:
+       const char *bare_refname;
+       const char *wtname;
+       int wtname_len;
+       enum ref_worktree_type wt_type = parse_worktree_ref(
+               refname, &wtname, &wtname_len, &bare_refname);
+       switch (wt_type) {
+       case REF_WORKTREE_CURRENT:
                strbuf_addf(sb, "%s/%s", refs->base.gitdir, refname);
                break;
-       case REF_TYPE_MAIN_PSEUDOREF:
-               if (!skip_prefix(refname, "main-worktree/", &refname))
-                       BUG("ref %s is not a main pseudoref", refname);
-               /* fallthrough */
-       case REF_TYPE_OTHER_PSEUDOREF:
-       case REF_TYPE_NORMAL:
-               strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
+       case REF_WORKTREE_OTHER:
+               strbuf_addf(sb, "%s/worktrees/%.*s/%s", refs->gitcommondir,
+                           wtname_len, wtname, bare_refname);
+               break;
+       case REF_WORKTREE_SHARED:
+       case REF_WORKTREE_MAIN:
+               strbuf_addf(sb, "%s/%s", refs->gitcommondir, bare_refname);
                break;
        default:
-               BUG("unknown ref type %d of ref %s",
-                   ref_type(refname), refname);
+               BUG("unknown ref type %d of ref %s", wt_type, refname);
        }
 }
 
@@ -244,10 +244,8 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        int dirnamelen = strlen(dirname);
        struct strbuf refname;
        struct strbuf path = STRBUF_INIT;
-       size_t path_baselen;
 
        files_ref_path(refs, &path, dirname);
-       path_baselen = path.len;
 
        d = opendir(path.buf);
        if (!d) {
@@ -260,23 +258,22 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
 
        while ((de = readdir(d)) != NULL) {
                struct object_id oid;
-               struct stat st;
                int flag;
+               unsigned char dtype;
 
                if (de->d_name[0] == '.')
                        continue;
                if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
-               strbuf_addstr(&path, de->d_name);
-               if (stat(path.buf, &st) < 0) {
-                       ; /* silently ignore */
-               } else if (S_ISDIR(st.st_mode)) {
+
+               dtype = get_dtype(de, &path, 1);
+               if (dtype == DT_DIR) {
                        strbuf_addch(&refname, '/');
                        add_entry_to_dir(dir,
                                         create_dir_entry(dir->cache, refname.buf,
                                                          refname.len));
-               } else {
+               } else if (dtype == DT_REG) {
                        if (!refs_resolve_ref_unsafe(&refs->base,
                                                     refname.buf,
                                                     RESOLVE_REF_READING,
@@ -306,7 +303,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
                                         create_ref_entry(refname.buf, &oid, flag));
                }
                strbuf_setlen(&refname, dirnamelen);
-               strbuf_setlen(&path, path_baselen);
        }
        strbuf_release(&refname);
        strbuf_release(&path);
@@ -771,7 +767,8 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
        while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
                if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
-                   ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
+                   parse_worktree_ref(iter->iter0->refname, NULL, NULL,
+                                      NULL) != REF_WORKTREE_CURRENT)
                        continue;
 
                if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
@@ -829,7 +826,8 @@ static struct ref_iterator_vtable files_ref_iterator_vtable = {
 
 static struct ref_iterator *files_ref_iterator_begin(
                struct ref_store *ref_store,
-               const char *prefix, unsigned int flags)
+               const char *prefix, const char **exclude_patterns,
+               unsigned int flags)
 {
        struct files_ref_store *refs;
        struct ref_iterator *loose_iter, *packed_iter, *overlay_iter;
@@ -874,7 +872,7 @@ static struct ref_iterator *files_ref_iterator_begin(
         * the packed and loose references.
         */
        packed_iter = refs_ref_iterator_begin(
-                       refs->packed_ref_store, prefix, 0,
+                       refs->packed_ref_store, prefix, exclude_patterns, 0,
                        DO_FOR_EACH_INCLUDE_BROKEN);
 
        overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);
@@ -1175,14 +1173,13 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
  */
 static int should_pack_ref(const char *refname,
                           const struct object_id *oid, unsigned int ref_flags,
-                          unsigned int pack_flags)
+                          struct pack_refs_opts *opts)
 {
-       /* Do not pack per-worktree refs: */
-       if (ref_type(refname) != REF_TYPE_NORMAL)
-               return 0;
+       struct string_list_item *item;
 
-       /* Do not pack non-tags unless PACK_REFS_ALL is set: */
-       if (!(pack_flags & PACK_REFS_ALL) && !starts_with(refname, "refs/tags/"))
+       /* Do not pack per-worktree refs: */
+       if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
+           REF_WORKTREE_SHARED)
                return 0;
 
        /* Do not pack symbolic refs: */
@@ -1193,10 +1190,18 @@ static int should_pack_ref(const char *refname,
        if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags))
                return 0;
 
-       return 1;
+       if (ref_excluded(opts->exclusions, refname))
+               return 0;
+
+       for_each_string_list_item(item, opts->includes)
+               if (!wildmatch(item->string, refname, 0))
+                       return 1;
+
+       return 0;
 }
 
-static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
+static int files_pack_refs(struct ref_store *ref_store,
+                          struct pack_refs_opts *opts)
 {
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
@@ -1221,8 +1226,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
                 * in the packed ref cache. If the reference should be
                 * pruned, also add it to refs_to_prune.
                 */
-               if (!should_pack_ref(iter->refname, iter->oid, iter->flags,
-                                    flags))
+               if (!should_pack_ref(iter->refname, iter->oid, iter->flags, opts))
                        continue;
 
                /*
@@ -1236,7 +1240,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
                            iter->refname, err.buf);
 
                /* Schedule the loose reference for pruning if requested. */
-               if ((flags & PACK_REFS_PRUNE)) {
+               if ((opts->flags & PACK_REFS_PRUNE)) {
                        struct ref_to_prune *n;
                        FLEX_ALLOC_STR(n, name, iter->refname);
                        oidcpy(&n->oid, iter->oid);
@@ -1259,54 +1263,6 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
        return 0;
 }
 
-static int files_delete_refs(struct ref_store *ref_store, const char *msg,
-                            struct string_list *refnames, unsigned int flags)
-{
-       struct files_ref_store *refs =
-               files_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
-       struct strbuf err = STRBUF_INIT;
-       int i, result = 0;
-
-       if (!refnames->nr)
-               return 0;
-
-       if (packed_refs_lock(refs->packed_ref_store, 0, &err))
-               goto error;
-
-       if (refs_delete_refs(refs->packed_ref_store, msg, refnames, flags)) {
-               packed_refs_unlock(refs->packed_ref_store);
-               goto error;
-       }
-
-       packed_refs_unlock(refs->packed_ref_store);
-
-       for (i = 0; i < refnames->nr; i++) {
-               const char *refname = refnames->items[i].string;
-
-               if (refs_delete_ref(&refs->base, msg, refname, NULL, flags))
-                       result |= error(_("could not remove reference %s"), refname);
-       }
-
-       strbuf_release(&err);
-       return result;
-
-error:
-       /*
-        * If we failed to rewrite the packed-refs file, then it is
-        * unsafe to try to remove loose refs, because doing so might
-        * expose an obsolete packed value for a reference that might
-        * even point at an object that has been garbage collected.
-        */
-       if (refnames->nr == 1)
-               error(_("could not delete reference %s: %s"),
-                     refnames->items[0].string, err.buf);
-       else
-               error(_("could not delete references: %s"), err.buf);
-
-       strbuf_release(&err);
-       return -1;
-}
-
 /*
  * People using contrib's git-new-workdir have .git/logs/refs ->
  * /some/other/path/.git/logs/refs, and that may live on another device.
@@ -2267,7 +2223,8 @@ static enum iterator_selection reflog_iterator_select(
                 */
                return ITER_SELECT_0;
        } else if (iter_common) {
-               if (ref_type(iter_common->refname) == REF_TYPE_NORMAL)
+               if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+                                      NULL) == REF_WORKTREE_SHARED)
                        return ITER_SELECT_1;
 
                /*
@@ -3282,7 +3239,6 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED)
 }
 
 struct ref_storage_be refs_be_files = {
-       .next = NULL,
        .name = "files",
        .init = files_ref_store_create,
        .init_db = files_init_db,
@@ -3293,7 +3249,6 @@ struct ref_storage_be refs_be_files = {
 
        .pack_refs = files_pack_refs,
        .create_symref = files_create_symref,
-       .delete_refs = files_delete_refs,
        .rename_ref = files_rename_ref,
        .copy_ref = files_copy_ref,
 
index c9fd0bcaf90c753031a1624f593c607a4549bf5a..6b680f610efbee4e144cfa41763f45834b39d210 100644 (file)
@@ -3,7 +3,7 @@
  * documentation about the design and use of reference iterators.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "refs.h"
 #include "refs/refs-internal.h"
 #include "iterator.h"
index 43cdb97f8b37756ee2f62fcf2dc285ec3116f5b4..bf04b933815955bc13109faf3e7916fc769fff15 100644 (file)
@@ -1,11 +1,18 @@
-#include "../cache.h"
+#include "../git-compat-util.h"
 #include "../config.h"
+#include "../gettext.h"
+#include "../hash.h"
+#include "../hex.h"
 #include "../refs.h"
 #include "refs-internal.h"
 #include "packed-backend.h"
 #include "../iterator.h"
 #include "../lockfile.h"
 #include "../chdir-notify.h"
+#include "../statinfo.h"
+#include "../wrapper.h"
+#include "../write-or-die.h"
+#include "../trace2.h"
 
 enum mmap_strategy {
        /*
@@ -297,7 +304,8 @@ static int cmp_packed_ref_records(const void *v1, const void *v2)
  * Compare a snapshot record at `rec` to the specified NUL-terminated
  * refname.
  */
-static int cmp_record_to_refname(const char *rec, const char *refname)
+static int cmp_record_to_refname(const char *rec, const char *refname,
+                                int start)
 {
        const char *r1 = rec + the_hash_algo->hexsz + 1;
        const char *r2 = refname;
@@ -306,7 +314,7 @@ static int cmp_record_to_refname(const char *rec, const char *refname)
                if (*r1 == '\n')
                        return *r2 ? -1 : 0;
                if (!*r2)
-                       return 1;
+                       return start ? 1 : -1;
                if (*r1 != *r2)
                        return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1;
                r1++;
@@ -521,22 +529,9 @@ static int load_contents(struct snapshot *snapshot)
        return 1;
 }
 
-/*
- * Find the place in `snapshot->buf` where the start of the record for
- * `refname` starts. If `mustexist` is true and the reference doesn't
- * exist, then return NULL. If `mustexist` is false and the reference
- * doesn't exist, then return the point where that reference would be
- * inserted, or `snapshot->eof` (which might be NULL) if it would be
- * inserted at the end of the file. In the latter mode, `refname`
- * doesn't have to be a proper reference name; for example, one could
- * search for "refs/replace/" to find the start of any replace
- * references.
- *
- * The record is sought using a binary search, so `snapshot->buf` must
- * be sorted.
- */
-static const char *find_reference_location(struct snapshot *snapshot,
-                                          const char *refname, int mustexist)
+static const char *find_reference_location_1(struct snapshot *snapshot,
+                                            const char *refname, int mustexist,
+                                            int start)
 {
        /*
         * This is not *quite* a garden-variety binary search, because
@@ -566,7 +561,7 @@ static const char *find_reference_location(struct snapshot *snapshot,
 
                mid = lo + (hi - lo) / 2;
                rec = find_start_of_record(lo, mid);
-               cmp = cmp_record_to_refname(rec, refname);
+               cmp = cmp_record_to_refname(rec, refname, start);
                if (cmp < 0) {
                        lo = find_end_of_record(mid, hi);
                } else if (cmp > 0) {
@@ -582,6 +577,41 @@ static const char *find_reference_location(struct snapshot *snapshot,
                return lo;
 }
 
+/*
+ * Find the place in `snapshot->buf` where the start of the record for
+ * `refname` starts. If `mustexist` is true and the reference doesn't
+ * exist, then return NULL. If `mustexist` is false and the reference
+ * doesn't exist, then return the point where that reference would be
+ * inserted, or `snapshot->eof` (which might be NULL) if it would be
+ * inserted at the end of the file. In the latter mode, `refname`
+ * doesn't have to be a proper reference name; for example, one could
+ * search for "refs/replace/" to find the start of any replace
+ * references.
+ *
+ * The record is sought using a binary search, so `snapshot->buf` must
+ * be sorted.
+ */
+static const char *find_reference_location(struct snapshot *snapshot,
+                                          const char *refname, int mustexist)
+{
+       return find_reference_location_1(snapshot, refname, mustexist, 1);
+}
+
+/*
+ * Find the place in `snapshot->buf` after the end of the record for
+ * `refname`. In other words, find the location of first thing *after*
+ * `refname`.
+ *
+ * Other semantics are identical to the ones in
+ * `find_reference_location()`.
+ */
+static const char *find_reference_location_end(struct snapshot *snapshot,
+                                              const char *refname,
+                                              int mustexist)
+{
+       return find_reference_location_1(snapshot, refname, mustexist, 0);
+}
+
 /*
  * Create a newly-allocated `snapshot` of the `packed-refs` file in
  * its current state and return it. The return value will already have
@@ -646,7 +676,7 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
                                         snapshot->buf,
                                         snapshot->eof - snapshot->buf);
 
-               string_list_split_in_place(&traits, p, ' ', -1);
+               string_list_split_in_place(&traits, p, " ", -1);
 
                if (unsorted_string_list_has_string(&traits, "fully-peeled"))
                        snapshot->peeled = PEELED_FULLY;
@@ -773,6 +803,13 @@ struct packed_ref_iterator {
        /* The end of the part of the buffer that will be iterated over: */
        const char *eof;
 
+       struct jump_list_entry {
+               const char *start;
+               const char *end;
+       } *jump;
+       size_t jump_nr, jump_alloc;
+       size_t jump_cur;
+
        /* Scratch space for current values: */
        struct object_id oid, peeled;
        struct strbuf refname_buf;
@@ -790,14 +827,36 @@ struct packed_ref_iterator {
  */
 static int next_record(struct packed_ref_iterator *iter)
 {
-       const char *p = iter->pos, *eol;
+       const char *p, *eol;
 
        strbuf_reset(&iter->refname_buf);
 
+       /*
+        * If iter->pos is contained within a skipped region, jump past
+        * it.
+        *
+        * Note that each skipped region is considered at most once,
+        * since they are ordered based on their starting position.
+        */
+       while (iter->jump_cur < iter->jump_nr) {
+               struct jump_list_entry *curr = &iter->jump[iter->jump_cur];
+               if (iter->pos < curr->start)
+                       break; /* not to the next jump yet */
+
+               iter->jump_cur++;
+               if (iter->pos < curr->end) {
+                       iter->pos = curr->end;
+                       trace2_counter_add(TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, 1);
+                       /* jumps are coalesced, so only one jump is necessary */
+                       break;
+               }
+       }
+
        if (iter->pos == iter->eof)
                return ITER_DONE;
 
        iter->base.flags = REF_ISPACKED;
+       p = iter->pos;
 
        if (iter->eof - p < the_hash_algo->hexsz + 2 ||
            parse_oid_hex(p, &iter->oid, &p) ||
@@ -862,7 +921,7 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
        while ((ok = next_record(iter)) == ITER_OK) {
                if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
-                   ref_type(iter->base.refname) != REF_TYPE_PER_WORKTREE)
+                   !is_per_worktree_ref(iter->base.refname))
                        continue;
 
                if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
@@ -905,6 +964,7 @@ static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator)
        int ok = ITER_DONE;
 
        strbuf_release(&iter->refname_buf);
+       free(iter->jump);
        release_snapshot(iter->snapshot);
        base_ref_iterator_free(ref_iterator);
        return ok;
@@ -916,9 +976,112 @@ static struct ref_iterator_vtable packed_ref_iterator_vtable = {
        .abort = packed_ref_iterator_abort
 };
 
+static int jump_list_entry_cmp(const void *va, const void *vb)
+{
+       const struct jump_list_entry *a = va;
+       const struct jump_list_entry *b = vb;
+
+       if (a->start < b->start)
+               return -1;
+       if (a->start > b->start)
+               return 1;
+       return 0;
+}
+
+static int has_glob_special(const char *str)
+{
+       const char *p;
+       for (p = str; *p; p++) {
+               if (is_glob_special(*p))
+                       return 1;
+       }
+       return 0;
+}
+
+static void populate_excluded_jump_list(struct packed_ref_iterator *iter,
+                                       struct snapshot *snapshot,
+                                       const char **excluded_patterns)
+{
+       size_t i, j;
+       const char **pattern;
+       struct jump_list_entry *last_disjoint;
+
+       if (!excluded_patterns)
+               return;
+
+       for (pattern = excluded_patterns; *pattern; pattern++) {
+               struct jump_list_entry *e;
+               const char *start, *end;
+
+               /*
+                * We can't feed any excludes with globs in them to the
+                * refs machinery.  It only understands prefix matching.
+                * We likewise can't even feed the string leading up to
+                * the first meta-character, as something like "foo[a]"
+                * should not exclude "foobar" (but the prefix "foo"
+                * would match that and mark it for exclusion).
+                */
+               if (has_glob_special(*pattern))
+                       continue;
+
+               start = find_reference_location(snapshot, *pattern, 0);
+               end = find_reference_location_end(snapshot, *pattern, 0);
+
+               if (start == end)
+                       continue; /* nothing to jump over */
+
+               ALLOC_GROW(iter->jump, iter->jump_nr + 1, iter->jump_alloc);
+
+               e = &iter->jump[iter->jump_nr++];
+               e->start = start;
+               e->end = end;
+       }
+
+       if (!iter->jump_nr) {
+               /*
+                * Every entry in exclude_patterns has a meta-character,
+                * nothing to do here.
+                */
+               return;
+       }
+
+       QSORT(iter->jump, iter->jump_nr, jump_list_entry_cmp);
+
+       /*
+        * As an optimization, merge adjacent entries in the jump list
+        * to jump forwards as far as possible when entering a skipped
+        * region.
+        *
+        * For example, if we have two skipped regions:
+        *
+        *      [[A, B], [B, C]]
+        *
+        * we want to combine that into a single entry jumping from A to
+        * C.
+        */
+       last_disjoint = iter->jump;
+
+       for (i = 1, j = 1; i < iter->jump_nr; i++) {
+               struct jump_list_entry *ours = &iter->jump[i];
+               if (ours->start <= last_disjoint->end) {
+                       /* overlapping regions extend the previous one */
+                       last_disjoint->end = last_disjoint->end > ours->end
+                               ? last_disjoint->end : ours->end;
+               } else {
+                       /* otherwise, insert a new region */
+                       iter->jump[j++] = *ours;
+                       last_disjoint = ours;
+               }
+       }
+
+       iter->jump_nr = j;
+       iter->jump_cur = 0;
+}
+
 static struct ref_iterator *packed_ref_iterator_begin(
                struct ref_store *ref_store,
-               const char *prefix, unsigned int flags)
+               const char *prefix, const char **exclude_patterns,
+               unsigned int flags)
 {
        struct packed_ref_store *refs;
        struct snapshot *snapshot;
@@ -950,6 +1113,9 @@ static struct ref_iterator *packed_ref_iterator_begin(
        ref_iterator = &iter->base;
        base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1);
 
+       if (exclude_patterns)
+               populate_excluded_jump_list(iter, snapshot, exclude_patterns);
+
        iter->snapshot = snapshot;
        acquire_snapshot(snapshot);
 
@@ -1143,7 +1309,7 @@ static int write_with_updates(struct packed_ref_store *refs,
         * list of refs is exhausted, set iter to NULL. When the list
         * of updates is exhausted, leave i set to updates->nr.
         */
-       iter = packed_ref_iterator_begin(&refs->base, "",
+       iter = packed_ref_iterator_begin(&refs->base, "", NULL,
                                         DO_FOR_EACH_INCLUDE_BROKEN);
        if ((ok = ref_iterator_advance(iter)) != ITER_OK)
                iter = NULL;
@@ -1263,7 +1429,8 @@ static int write_with_updates(struct packed_ref_store *refs,
                goto error;
        }
 
-       if (fsync_component(FSYNC_COMPONENT_REFERENCE, get_tempfile_fd(refs->tempfile)) ||
+       if (fflush(out) ||
+           fsync_component(FSYNC_COMPONENT_REFERENCE, get_tempfile_fd(refs->tempfile)) ||
            close_tempfile_gently(refs->tempfile)) {
                strbuf_addf(err, "error closing file %s: %s",
                            get_tempfile_path(refs->tempfile),
@@ -1520,57 +1687,8 @@ static int packed_initial_transaction_commit(struct ref_store *ref_store UNUSED,
        return ref_transaction_commit(transaction, err);
 }
 
-static int packed_delete_refs(struct ref_store *ref_store, const char *msg,
-                            struct string_list *refnames, unsigned int flags)
-{
-       struct packed_ref_store *refs =
-               packed_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
-       struct strbuf err = STRBUF_INIT;
-       struct ref_transaction *transaction;
-       struct string_list_item *item;
-       int ret;
-
-       (void)refs; /* We need the check above, but don't use the variable */
-
-       if (!refnames->nr)
-               return 0;
-
-       /*
-        * Since we don't check the references' old_oids, the
-        * individual updates can't fail, so we can pack all of the
-        * updates into a single transaction.
-        */
-
-       transaction = ref_store_transaction_begin(ref_store, &err);
-       if (!transaction)
-               return -1;
-
-       for_each_string_list_item(item, refnames) {
-               if (ref_transaction_delete(transaction, item->string, NULL,
-                                          flags, msg, &err)) {
-                       warning(_("could not delete reference %s: %s"),
-                               item->string, err.buf);
-                       strbuf_reset(&err);
-               }
-       }
-
-       ret = ref_transaction_commit(transaction, &err);
-
-       if (ret) {
-               if (refnames->nr == 1)
-                       error(_("could not delete reference %s: %s"),
-                             refnames->items[0].string, err.buf);
-               else
-                       error(_("could not delete references: %s"), err.buf);
-       }
-
-       ref_transaction_free(transaction);
-       strbuf_release(&err);
-       return ret;
-}
-
 static int packed_pack_refs(struct ref_store *ref_store UNUSED,
-                           unsigned int flags UNUSED)
+                           struct pack_refs_opts *pack_opts UNUSED)
 {
        /*
         * Packed refs are already packed. It might be that loose refs
@@ -1586,7 +1704,6 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
 }
 
 struct ref_storage_be refs_be_packed = {
-       .next = NULL,
        .name = "packed",
        .init = packed_ref_store_create,
        .init_db = packed_init_db,
@@ -1597,7 +1714,6 @@ struct ref_storage_be refs_be_packed = {
 
        .pack_refs = packed_pack_refs,
        .create_symref = NULL,
-       .delete_refs = packed_delete_refs,
        .rename_ref = NULL,
        .copy_ref = NULL,
 
index 32afd8a40b0faf22d1ef2de5bb2a2990fa6eebb3..a372a00941fda659cc60722452c8766f9c7de455 100644 (file)
@@ -1,5 +1,7 @@
-#include "../cache.h"
+#include "../git-compat-util.h"
+#include "../hash.h"
 #include "../refs.h"
+#include "../repository.h"
 #include "refs-internal.h"
 #include "ref-cache.h"
 #include "../iterator.h"
@@ -409,7 +411,8 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
                if (level->prefix_state == PREFIX_WITHIN_DIR) {
                        entry_prefix_state = overlaps_prefix(entry->name, iter->prefix);
-                       if (entry_prefix_state == PREFIX_EXCLUDES_DIR)
+                       if (entry_prefix_state == PREFIX_EXCLUDES_DIR ||
+                           (entry_prefix_state == PREFIX_WITHIN_DIR && !(entry->flag & REF_DIR)))
                                continue;
                } else {
                        entry_prefix_state = level->prefix_state;
index 850d9d3744e94270fb5cd445b4f6b7b18aac8128..95c76e27c833112743b82a0b62f9a5423fb52259 100644 (file)
@@ -1,10 +1,11 @@
 #ifndef REFS_REF_CACHE_H
 #define REFS_REF_CACHE_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 struct ref_dir;
 struct ref_store;
+struct repository;
 
 /*
  * If this ref_cache is filled lazily, this function is used to load
index 69f93b0e2ac9fa9ec2778b783fa98792030c327e..8e9f04cc670baeddd68b933ee41d11062b2fbe32 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef REFS_REFS_INTERNAL_H
 #define REFS_REFS_INTERNAL_H
 
-#include "cache.h"
 #include "refs.h"
 #include "iterator.h"
 
@@ -368,8 +367,8 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator);
  */
 struct ref_iterator *refs_ref_iterator_begin(
                struct ref_store *refs,
-               const char *prefix, int trim,
-               enum do_for_each_ref_flags flags);
+               const char *prefix, const char **exclude_patterns,
+               int trim, enum do_for_each_ref_flags flags);
 
 /*
  * A callback function used to instruct merge_ref_iterator how to
@@ -548,13 +547,12 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
                                      struct ref_transaction *transaction,
                                      struct strbuf *err);
 
-typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags);
+typedef int pack_refs_fn(struct ref_store *ref_store,
+                        struct pack_refs_opts *opts);
 typedef int create_symref_fn(struct ref_store *ref_store,
                             const char *ref_target,
                             const char *refs_heads_master,
                             const char *logmsg);
-typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg,
-                          struct string_list *refnames, unsigned int flags);
 typedef int rename_ref_fn(struct ref_store *ref_store,
                          const char *oldref, const char *newref,
                          const char *logmsg);
@@ -571,7 +569,8 @@ typedef int copy_ref_fn(struct ref_store *ref_store,
  */
 typedef struct ref_iterator *ref_iterator_begin_fn(
                struct ref_store *ref_store,
-               const char *prefix, unsigned int flags);
+               const char *prefix, const char **exclude_patterns,
+               unsigned int flags);
 
 /* reflog functions */
 
@@ -664,7 +663,6 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam
                                 struct strbuf *referent);
 
 struct ref_storage_be {
-       struct ref_storage_be *next;
        const char *name;
        ref_store_init_fn *init;
        ref_init_db_fn *init_db;
@@ -676,7 +674,6 @@ struct ref_storage_be {
 
        pack_refs_fn *pack_refs;
        create_symref_fn *create_symref;
-       delete_refs_fn *delete_refs;
        rename_ref_fn *rename_ref;
        copy_ref_fn *copy_ref;
 
index 63e3112104a949e28e0cae1c8beaee0c27c099f1..d60932f4ded876476b7dec21456b9088260d0d1f 100644 (file)
--- a/refspec.c
+++ b/refspec.c
@@ -1,7 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "strvec.h"
 #include "refs.h"
 #include "refspec.h"
+#include "strbuf.h"
 
 static struct refspec_item s_tag_refspec = {
        .force = 0,
index 34d4d073692f9e913d84715d987c8e1c35a7fe8a..1df3d8a0f09671c74143e655cb2963adda872545 100644 (file)
@@ -323,30 +323,28 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
                .len = it->br->block_len - it->next_off,
        };
        struct string_view start = in;
-       struct strbuf key = STRBUF_INIT;
        uint8_t extra = 0;
        int n = 0;
 
        if (it->next_off >= it->br->block_len)
                return 1;
 
-       n = reftable_decode_key(&key, &extra, it->last_key, in);
+       n = reftable_decode_key(&it->key, &extra, it->last_key, in);
        if (n < 0)
                return -1;
 
-       if (!key.len)
+       if (!it->key.len)
                return REFTABLE_FORMAT_ERROR;
 
        string_view_consume(&in, n);
-       n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
+       n = reftable_record_decode(rec, it->key, extra, in, it->br->hash_size);
        if (n < 0)
                return -1;
        string_view_consume(&in, n);
 
        strbuf_reset(&it->last_key);
-       strbuf_addbuf(&it->last_key, &key);
+       strbuf_addbuf(&it->last_key, &it->key);
        it->next_off += start.len - in.len;
-       strbuf_release(&key);
        return 0;
 }
 
@@ -377,6 +375,7 @@ int block_iter_seek(struct block_iter *it, struct strbuf *want)
 void block_iter_close(struct block_iter *it)
 {
        strbuf_release(&it->last_key);
+       strbuf_release(&it->key);
 }
 
 int block_reader_seek(struct block_reader *br, struct block_iter *it,
@@ -387,11 +386,8 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
                .r = br,
        };
        struct reftable_record rec = reftable_new_record(block_reader_type(br));
-       struct strbuf key = STRBUF_INIT;
        int err = 0;
-       struct block_iter next = {
-               .last_key = STRBUF_INIT,
-       };
+       struct block_iter next = BLOCK_ITER_INIT;
 
        int i = binsearch(br->restart_count, &restart_key_less, &args);
        if (args.error) {
@@ -416,8 +412,8 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
                if (err < 0)
                        goto done;
 
-               reftable_record_key(&rec, &key);
-               if (err > 0 || strbuf_cmp(&key, want) >= 0) {
+               reftable_record_key(&rec, &it->key);
+               if (err > 0 || strbuf_cmp(&it->key, want) >= 0) {
                        err = 0;
                        goto done;
                }
@@ -426,8 +422,7 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
        }
 
 done:
-       strbuf_release(&key);
-       strbuf_release(&next.last_key);
+       block_iter_close(&next);
        reftable_record_release(&rec);
 
        return err;
index 87c77539b5bd6ab0b9909c51801719258507b424..17481e6331979cc31972ee3dad576e9c594a1769 100644 (file)
@@ -84,8 +84,14 @@ struct block_iter {
 
        /* key for last entry we read. */
        struct strbuf last_key;
+       struct strbuf key;
 };
 
+#define BLOCK_ITER_INIT { \
+       .last_key = STRBUF_INIT, \
+       .key = STRBUF_INIT, \
+}
+
 /* initializes a block reader. */
 int block_reader_init(struct block_reader *br, struct reftable_block *bl,
                      uint32_t header_off, uint32_t table_block_size,
index cb88af4a5639258945f569ede758e31809b62ead..dedb05c7d8c0560898b03aba93043c7efa46aeea 100644 (file)
@@ -32,7 +32,7 @@ static void test_block_read_write(void)
        int i = 0;
        int n;
        struct block_reader br = { 0 };
-       struct block_iter it = { .last_key = STRBUF_INIT };
+       struct block_iter it = BLOCK_ITER_INIT;
        int j = 0;
        struct strbuf want = STRBUF_INIT;
 
@@ -49,13 +49,11 @@ static void test_block_read_write(void)
 
        for (i = 0; i < N; i++) {
                char name[100];
-               uint8_t hash[GIT_SHA1_RAWSZ];
                snprintf(name, sizeof(name), "branch%02d", i);
-               memset(hash, i, sizeof(hash));
 
                rec.u.ref.refname = name;
                rec.u.ref.value_type = REFTABLE_REF_VAL1;
-               rec.u.ref.value.val1 = hash;
+               memset(rec.u.ref.value.val1, i, GIT_SHA1_RAWSZ);
 
                names[i] = xstrdup(name);
                n = block_writer_add(&bw, &rec);
@@ -87,7 +85,7 @@ static void test_block_read_write(void)
        block_iter_close(&it);
 
        for (i = 0; i < N; i++) {
-               struct block_iter it = { .last_key = STRBUF_INIT };
+               struct block_iter it = BLOCK_ITER_INIT;
                strbuf_reset(&want);
                strbuf_addstr(&want, names[i]);
 
index 8331b34e82300630b68c574e4f68071f3ebf261a..a1ea3044292ef2032485bad261ffa5ed178421bc 100644 (file)
@@ -109,7 +109,7 @@ static int file_read_block(void *v, struct reftable_block *dest, uint64_t off,
        struct file_block_source *b = v;
        assert(off + size <= b->size);
        dest->data = reftable_malloc(size);
-       if (pread(b->fd, dest->data, size, off) != size)
+       if (pread_in_full(b->fd, dest->data, size, off) != size)
                return -1;
        dest->len = size;
        return size;
index 155953d1b82d4751dc80b1d486661e54d2ba16ba..26e0393c7db7a4b6677bb3c5a73ce08559956dd7 100644 (file)
@@ -7,18 +7,16 @@ https://developers.google.com/open-source/licenses/bsd
 */
 
 #include "git-compat-util.h"
-#include "hash.h"
+#include "hash-ll.h"
 
 #include "reftable-blocksource.h"
 #include "reftable-error.h"
-#include "reftable-merged.h"
 #include "reftable-record.h"
 #include "reftable-tests.h"
 #include "reftable-writer.h"
 #include "reftable-iterator.h"
 #include "reftable-reader.h"
 #include "reftable-stack.h"
-#include "reftable-generic.h"
 
 #include <stddef.h>
 #include <stdio.h>
index 93941f214570ace6e82e3c0a8b3c83d8b945405f..0d1766735e8ff0f1c5c057d1882728aa4292d1ee 100644 (file)
@@ -6,6 +6,7 @@ license that can be found in the LICENSE file or at
 https://developers.google.com/open-source/licenses/bsd
 */
 
+#include "system.h"
 #include "reftable-error.h"
 
 #include <stdio.h>
index 57f8032db941ca77f5c61d0e6d38268c56958be9..b9f1c7c18a2efc48e91c2526a42bb9433783f8dd 100644 (file)
@@ -6,7 +6,6 @@ license that can be found in the LICENSE file or at
 https://developers.google.com/open-source/licenses/bsd
 */
 
-#include "basics.h"
 #include "constants.h"
 #include "record.h"
 #include "generic.h"
index 09eb0cbfa5997e0eca4f378a8d45025c4af8732d..47d67d84df679c522ce50d1d7aade2d1be683b5a 100644 (file)
@@ -53,10 +53,10 @@ struct indexed_table_ref_iter {
        int is_finished;
 };
 
-#define INDEXED_TABLE_REF_ITER_INIT                                     \
-       {                                                               \
-               .cur = { .last_key = STRBUF_INIT }, .oid = STRBUF_INIT, \
-       }
+#define INDEXED_TABLE_REF_ITER_INIT { \
+       .cur = BLOCK_ITER_INIT, \
+       .oid = STRBUF_INIT, \
+}
 
 void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
                                          struct indexed_table_ref_iter *itr);
index 5ded470c086c92760083dc16c08e7bdff821e9bf..c258ce953e81d7df5774a03250b056280beccbf3 100644 (file)
@@ -11,7 +11,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "constants.h"
 #include "iter.h"
 #include "pq.h"
-#include "reader.h"
 #include "record.h"
 #include "generic.h"
 #include "reftable-merged.h"
@@ -52,6 +51,8 @@ static void merged_iter_close(void *p)
                reftable_iterator_destroy(&mi->stack[i]);
        }
        reftable_free(mi->stack);
+       strbuf_release(&mi->key);
+       strbuf_release(&mi->entry_key);
 }
 
 static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi,
@@ -85,7 +86,6 @@ static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
 static int merged_iter_next_entry(struct merged_iter *mi,
                                  struct reftable_record *rec)
 {
-       struct strbuf entry_key = STRBUF_INIT;
        struct pq_entry entry = { 0 };
        int err = 0;
 
@@ -105,33 +105,31 @@ static int merged_iter_next_entry(struct merged_iter *mi,
          such a deployment, the loop below must be changed to collect all
          entries for the same key, and return new the newest one.
        */
-       reftable_record_key(&entry.rec, &entry_key);
+       reftable_record_key(&entry.rec, &mi->entry_key);
        while (!merged_iter_pqueue_is_empty(mi->pq)) {
                struct pq_entry top = merged_iter_pqueue_top(mi->pq);
-               struct strbuf k = STRBUF_INIT;
-               int err = 0, cmp = 0;
+               int cmp = 0;
 
-               reftable_record_key(&top.rec, &k);
+               reftable_record_key(&top.rec, &mi->key);
 
-               cmp = strbuf_cmp(&k, &entry_key);
-               strbuf_release(&k);
-
-               if (cmp > 0) {
+               cmp = strbuf_cmp(&mi->key, &mi->entry_key);
+               if (cmp > 0)
                        break;
-               }
 
                merged_iter_pqueue_remove(&mi->pq);
                err = merged_iter_advance_subiter(mi, top.index);
-               if (err < 0) {
-                       return err;
-               }
+               if (err < 0)
+                       goto done;
                reftable_record_release(&top.rec);
        }
 
-       reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id));
-       reftable_record_release(&entry.rec);
-       strbuf_release(&entry_key);
-       return 0;
+       reftable_record_release(rec);
+       *rec = entry.rec;
+
+done:
+       if (err)
+               reftable_record_release(&entry.rec);
+       return err;
 }
 
 static int merged_iter_next(struct merged_iter *mi, struct reftable_record *rec)
@@ -248,6 +246,8 @@ static int merged_table_seek_record(struct reftable_merged_table *mt,
                .typ = reftable_record_type(rec),
                .hash_id = mt->hash_id,
                .suppress_deletions = mt->suppress_deletions,
+               .key = STRBUF_INIT,
+               .entry_key = STRBUF_INIT,
        };
        int n = 0;
        int err = 0;
index 7d9f95d27ed0a44c4208e743b6b83ba4cdc5536c..d5b39dfe7f1e3b54b5dc5e7ae46068155794986f 100644 (file)
@@ -31,6 +31,8 @@ struct merged_iter {
        uint8_t typ;
        int suppress_deletions;
        struct merged_iter_pqueue pq;
+       struct strbuf key;
+       struct strbuf entry_key;
 };
 
 void merged_table_release(struct reftable_merged_table *mt);
index d08c16abefbc3d8562c29cf1703d1a6908f71dcd..46908f738f770f4be920a65df85b96178ddbc70d 100644 (file)
@@ -12,7 +12,6 @@ https://developers.google.com/open-source/licenses/bsd
 
 #include "basics.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "test_framework.h"
@@ -123,13 +122,11 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 
 static void test_merged_between(void)
 {
-       uint8_t hash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 0 };
-
        struct reftable_ref_record r1[] = { {
                .refname = "b",
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash1,
+               .value.val1 = { 1, 2, 3, 0 },
        } };
        struct reftable_ref_record r2[] = { {
                .refname = "a",
@@ -165,26 +162,24 @@ static void test_merged_between(void)
 
 static void test_merged(void)
 {
-       uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
-       uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
        struct reftable_ref_record r1[] = {
                {
                        .refname = "a",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
                {
                        .refname = "b",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
                {
                        .refname = "c",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                }
        };
        struct reftable_ref_record r2[] = { {
@@ -197,13 +192,13 @@ static void test_merged(void)
                        .refname = "c",
                        .update_index = 3,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash2,
+                       .value.val1 = { 2 },
                },
                {
                        .refname = "d",
                        .update_index = 3,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
        };
 
index 0ad7d5c0ff225cf0904e5dc2ddfe09c0486710d8..bcb82530d6ce35e8a0a582d9718090067b238899 100644 (file)
@@ -6,10 +6,10 @@ license that can be found in the LICENSE file or at
 https://developers.google.com/open-source/licenses/bsd
 */
 
+#include "system.h"
 #include "reftable-malloc.h"
 
 #include "basics.h"
-#include "system.h"
 
 static void *(*reftable_malloc_ptr)(size_t sz);
 static void *(*reftable_realloc_ptr)(void *, size_t);
index b4db23ce1880794a937b841e28ffa27e8869d2c7..64dc366fb15935c55c5377ec9d4f1b74640cc3f6 100644 (file)
@@ -16,7 +16,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "record.h"
 #include "reftable-error.h"
 #include "reftable-generic.h"
-#include "tree.h"
 
 uint64_t block_source_size(struct reftable_block_source *source)
 {
@@ -224,10 +223,9 @@ struct table_iter {
        struct block_iter bi;
        int is_finished;
 };
-#define TABLE_ITER_INIT                          \
-       {                                        \
-               .bi = {.last_key = STRBUF_INIT } \
-       }
+#define TABLE_ITER_INIT { \
+       .bi = BLOCK_ITER_INIT \
+}
 
 static void table_iter_copy_from(struct table_iter *dest,
                                 struct table_iter *src)
index 469ab79a5adf3dfaba0160e523bdcece5b9875e0..b8a32240164d6a5c29789fb1730a630127f085d5 100644 (file)
@@ -11,7 +11,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "basics.h"
 #include "block.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "test_framework.h"
@@ -60,18 +59,15 @@ static void write_table(char ***names, struct strbuf *buf, int N,
        *names = reftable_calloc(sizeof(char *) * (N + 1));
        reftable_writer_set_limits(w, update_index, update_index);
        for (i = 0; i < N; i++) {
-               uint8_t hash[GIT_SHA256_RAWSZ] = { 0 };
                char name[100];
                int n;
 
-               set_test_hash(hash, i);
-
                snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
 
                ref.refname = name;
                ref.update_index = update_index;
                ref.value_type = REFTABLE_REF_VAL1;
-               ref.value.val1 = hash;
+               set_test_hash(ref.value.val1, i);
                (*names)[i] = xstrdup(name);
 
                n = reftable_writer_add_ref(w, &ref);
@@ -141,8 +137,8 @@ static void test_log_buffer_size(void)
        */
        uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
        for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
-               hash1[i] = (uint8_t)(rand() % 256);
-               hash2[i] = (uint8_t)(rand() % 256);
+               hash1[i] = (uint8_t)(git_rand() % 256);
+               hash2[i] = (uint8_t)(git_rand() % 256);
        }
        log.value.update.old_hash = hash1;
        log.value.update.new_hash = hash2;
@@ -320,7 +316,7 @@ static void test_log_zlib_corruption(void)
        };
 
        for (i = 0; i < sizeof(message) - 1; i++)
-               message[i] = (uint8_t)(rand() % 64 + ' ');
+               message[i] = (uint8_t)(git_rand() % 64 + ' ');
 
        reftable_writer_set_limits(w, 1, 1);
 
@@ -550,8 +546,6 @@ static void test_table_refs_for(int indexed)
                uint8_t hash[GIT_SHA1_RAWSZ];
                char fill[51] = { 0 };
                char name[100];
-               uint8_t hash1[GIT_SHA1_RAWSZ];
-               uint8_t hash2[GIT_SHA1_RAWSZ];
                struct reftable_ref_record ref = { NULL };
 
                memset(hash, i, sizeof(hash));
@@ -561,11 +555,9 @@ static void test_table_refs_for(int indexed)
                name[40] = 0;
                ref.refname = name;
 
-               set_test_hash(hash1, i / 4);
-               set_test_hash(hash2, 3 + i / 4);
                ref.value_type = REFTABLE_REF_VAL2;
-               ref.value.val2.value = hash1;
-               ref.value.val2.target_value = hash2;
+               set_test_hash(ref.value.val2.value, i / 4);
+               set_test_hash(ref.value.val2.target_value, 3 + i / 4);
 
                /* 80 bytes / entry, so 3 entries per block. Yields 17
                 */
@@ -573,8 +565,8 @@ static void test_table_refs_for(int indexed)
                n = reftable_writer_add_ref(w, &ref);
                EXPECT(n == 0);
 
-               if (!memcmp(hash1, want_hash, GIT_SHA1_RAWSZ) ||
-                   !memcmp(hash2, want_hash, GIT_SHA1_RAWSZ)) {
+               if (!memcmp(ref.value.val2.value, want_hash, GIT_SHA1_RAWSZ) ||
+                   !memcmp(ref.value.val2.target_value, want_hash, GIT_SHA1_RAWSZ)) {
                        want_names[want_names_len++] = xstrdup(name);
                }
        }
@@ -675,11 +667,10 @@ static void test_write_object_id_min_length(void)
        struct strbuf buf = STRBUF_INIT;
        struct reftable_writer *w =
                reftable_new_writer(&strbuf_add_void, &buf, &opts);
-       uint8_t hash[GIT_SHA1_RAWSZ] = {42};
        struct reftable_ref_record ref = {
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash,
+               .value.val1 = {42},
        };
        int err;
        int i;
@@ -711,11 +702,10 @@ static void test_write_object_id_length(void)
        struct strbuf buf = STRBUF_INIT;
        struct reftable_writer *w =
                reftable_new_writer(&strbuf_add_void, &buf, &opts);
-       uint8_t hash[GIT_SHA1_RAWSZ] = {42};
        struct reftable_ref_record ref = {
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash,
+               .value.val1 = {42},
        };
        int err;
        int i;
@@ -798,6 +788,84 @@ static void test_write_key_order(void)
        strbuf_release(&buf);
 }
 
+static void test_write_multiple_indices(void)
+{
+       struct reftable_write_options opts = {
+               .block_size = 100,
+       };
+       struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
+       struct reftable_block_source source = { 0 };
+       struct reftable_iterator it = { 0 };
+       const struct reftable_stats *stats;
+       struct reftable_writer *writer;
+       struct reftable_reader *reader;
+       int err, i;
+
+       writer = reftable_new_writer(&strbuf_add_void, &writer_buf, &opts);
+       reftable_writer_set_limits(writer, 1, 1);
+       for (i = 0; i < 100; i++) {
+               struct reftable_ref_record ref = {
+                       .update_index = 1,
+                       .value_type = REFTABLE_REF_VAL1,
+                       .value.val1 = {i},
+               };
+
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "refs/heads/%04d", i);
+               ref.refname = buf.buf,
+
+               err = reftable_writer_add_ref(writer, &ref);
+               EXPECT_ERR(err);
+       }
+
+       for (i = 0; i < 100; i++) {
+               unsigned char hash[GIT_SHA1_RAWSZ] = {i};
+               struct reftable_log_record log = {
+                       .update_index = 1,
+                       .value_type = REFTABLE_LOG_UPDATE,
+                       .value.update = {
+                               .old_hash = hash,
+                               .new_hash = hash,
+                       },
+               };
+
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "refs/heads/%04d", i);
+               log.refname = buf.buf,
+
+               err = reftable_writer_add_log(writer, &log);
+               EXPECT_ERR(err);
+       }
+
+       reftable_writer_close(writer);
+
+       /*
+        * The written data should be sufficiently large to result in indices
+        * for each of the block types.
+        */
+       stats = reftable_writer_stats(writer);
+       EXPECT(stats->ref_stats.index_offset > 0);
+       EXPECT(stats->obj_stats.index_offset > 0);
+       EXPECT(stats->log_stats.index_offset > 0);
+
+       block_source_from_strbuf(&source, &writer_buf);
+       err = reftable_new_reader(&reader, &source, "filename");
+       EXPECT_ERR(err);
+
+       /*
+        * Seeking the log uses the log index now. In case there is any
+        * confusion regarding indices we would notice here.
+        */
+       err = reftable_reader_seek_log(reader, &it, "");
+       EXPECT_ERR(err);
+
+       reftable_iterator_destroy(&it);
+       reftable_writer_free(writer);
+       reftable_reader_free(reader);
+       strbuf_release(&writer_buf);
+       strbuf_release(&buf);
+}
+
 static void test_corrupt_table_empty(void)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -847,5 +915,6 @@ int readwrite_test_main(int argc, const char *argv[])
        RUN_TEST(test_log_overflow);
        RUN_TEST(test_write_object_id_length);
        RUN_TEST(test_write_object_id_min_length);
+       RUN_TEST(test_write_multiple_indices);
        return 0;
 }
index fbaa1fbef56c52fed8391c7bcef2efd035eb588b..5c3fbb7b2a1e95ad418066df0aa96235116f5329 100644 (file)
@@ -76,7 +76,7 @@ int reftable_is_block_type(uint8_t typ)
        return 0;
 }
 
-uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec)
+const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec)
 {
        switch (rec->value_type) {
        case REFTABLE_REF_VAL1:
@@ -88,7 +88,7 @@ uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec)
        }
 }
 
-uint8_t *reftable_ref_record_val2(const struct reftable_ref_record *rec)
+const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec)
 {
        switch (rec->value_type) {
        case REFTABLE_REF_VAL2:
@@ -219,13 +219,10 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
        case REFTABLE_REF_DELETION:
                break;
        case REFTABLE_REF_VAL1:
-               ref->value.val1 = reftable_malloc(hash_size);
                memcpy(ref->value.val1, src->value.val1, hash_size);
                break;
        case REFTABLE_REF_VAL2:
-               ref->value.val2.value = reftable_malloc(hash_size);
                memcpy(ref->value.val2.value, src->value.val2.value, hash_size);
-               ref->value.val2.target_value = reftable_malloc(hash_size);
                memcpy(ref->value.val2.target_value,
                       src->value.val2.target_value, hash_size);
                break;
@@ -242,7 +239,7 @@ static char hexdigit(int c)
        return 'a' + (c - 10);
 }
 
-static void hex_format(char *dest, uint8_t *src, int hash_size)
+static void hex_format(char *dest, const unsigned char *src, int hash_size)
 {
        assert(hash_size > 0);
        if (src) {
@@ -299,11 +296,8 @@ void reftable_ref_record_release(struct reftable_ref_record *ref)
                reftable_free(ref->value.symref);
                break;
        case REFTABLE_REF_VAL2:
-               reftable_free(ref->value.val2.target_value);
-               reftable_free(ref->value.val2.value);
                break;
        case REFTABLE_REF_VAL1:
-               reftable_free(ref->value.val1);
                break;
        case REFTABLE_REF_DELETION:
                break;
@@ -394,7 +388,6 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
                        return -1;
                }
 
-               r->value.val1 = reftable_malloc(hash_size);
                memcpy(r->value.val1, in.buf, hash_size);
                string_view_consume(&in, hash_size);
                break;
@@ -404,11 +397,9 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
                        return -1;
                }
 
-               r->value.val2.value = reftable_malloc(hash_size);
                memcpy(r->value.val2.value, in.buf, hash_size);
                string_view_consume(&in, hash_size);
 
-               r->value.val2.target_value = reftable_malloc(hash_size);
                memcpy(r->value.val2.target_value, in.buf, hash_size);
                string_view_consume(&in, hash_size);
                break;
@@ -1164,7 +1155,7 @@ int reftable_record_equal(struct reftable_record *a, struct reftable_record *b,
                reftable_record_data(a), reftable_record_data(b), hash_size);
 }
 
-static int hash_equal(uint8_t *a, uint8_t *b, int hash_size)
+static int hash_equal(const unsigned char *a, const unsigned char *b, int hash_size)
 {
        if (a && b)
                return !memcmp(a, b, hash_size);
index 70ae78feca4f11cdb50f4fdcb08d90118bf487df..2876db7d2708aa36d861181775de74c966862c2d 100644 (file)
@@ -119,15 +119,10 @@ static void test_reftable_ref_record_roundtrip(void)
                case REFTABLE_REF_DELETION:
                        break;
                case REFTABLE_REF_VAL1:
-                       in.u.ref.value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val1, 1);
                        break;
                case REFTABLE_REF_VAL2:
-                       in.u.ref.value.val2.value =
-                               reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val2.value, 1);
-                       in.u.ref.value.val2.target_value =
-                               reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val2.target_value, 2);
                        break;
                case REFTABLE_REF_SYMREF:
index 8645cd93bbd8f9b51e05eb9143ed99217bfa1035..699e1aea4122d66a8a00451521f7d902858474c2 100644 (file)
@@ -9,7 +9,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "basics.h"
 #include "block.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "refname.h"
index 67104f8fbfecd7984127d29dd1ae3fa9bb263394..bb6e99acd3151ec75a7a2dc60e930064b7322e60 100644 (file)
@@ -9,6 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
 #ifndef REFTABLE_RECORD_H
 #define REFTABLE_RECORD_H
 
+#include "hash-ll.h"
 #include <stdint.h>
 
 /*
@@ -38,10 +39,10 @@ struct reftable_ref_record {
 #define REFTABLE_NR_REF_VALUETYPES 4
        } value_type;
        union {
-               uint8_t *val1; /* malloced hash. */
+               unsigned char val1[GIT_MAX_RAWSZ];
                struct {
-                       uint8_t *value; /* first value, malloced hash  */
-                       uint8_t *target_value; /* second value, malloced hash */
+                       unsigned char value[GIT_MAX_RAWSZ]; /* first hash  */
+                       unsigned char target_value[GIT_MAX_RAWSZ]; /* second hash */
                } val2;
                char *symref; /* referent, malloced 0-terminated string */
        } value;
@@ -49,11 +50,11 @@ struct reftable_ref_record {
 
 /* Returns the first hash, or NULL if `rec` is not of type
  * REFTABLE_REF_VAL1 or REFTABLE_REF_VAL2. */
-uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec);
+const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec);
 
 /* Returns the second hash, or NULL if `rec` is not of type
  * REFTABLE_REF_VAL2. */
-uint8_t *reftable_ref_record_val2(const struct reftable_ref_record *rec);
+const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec);
 
 /* returns whether 'ref' represents a deletion */
 int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
index ddbdf1b9c8bf4668dfe4f9b7c03cabd43effb660..7ffeb3ee1074bc59aa094fabbbce24d1f3cfaf9a 100644 (file)
@@ -17,6 +17,8 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reftable-merged.h"
 #include "writer.h"
 
+#include "tempfile.h"
+
 static int stack_try_add(struct reftable_stack *st,
                         int (*write_table)(struct reftable_writer *wr,
                                            void *arg),
@@ -42,7 +44,7 @@ static void stack_filename(struct strbuf *dest, struct reftable_stack *st,
 static ssize_t reftable_fd_write(void *arg, const void *data, size_t sz)
 {
        int *fdp = (int *)arg;
-       return write(*fdp, data, sz);
+       return write_in_full(*fdp, data, sz);
 }
 
 int reftable_new_stack(struct reftable_stack **dest, const char *dir,
@@ -92,7 +94,7 @@ static int fd_read_lines(int fd, char ***namesp)
        }
 
        buf = reftable_malloc(size + 1);
-       if (read(fd, buf, size) != size) {
+       if (read_in_full(fd, buf, size) != size) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
@@ -204,6 +206,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
                reftable_calloc(sizeof(struct reftable_table) * names_len);
        int new_readers_len = 0;
        struct reftable_merged_table *new_merged = NULL;
+       struct strbuf table_path = STRBUF_INIT;
        int i;
 
        while (*names) {
@@ -223,13 +226,10 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 
                if (!rd) {
                        struct reftable_block_source src = { NULL };
-                       struct strbuf table_path = STRBUF_INIT;
                        stack_filename(&table_path, st, name);
 
                        err = reftable_block_source_from_file(&src,
                                                              table_path.buf);
-                       strbuf_release(&table_path);
-
                        if (err < 0)
                                goto done;
 
@@ -267,16 +267,13 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
        for (i = 0; i < cur_len; i++) {
                if (cur[i]) {
                        const char *name = reader_name(cur[i]);
-                       struct strbuf filename = STRBUF_INIT;
-                       stack_filename(&filename, st, name);
+                       stack_filename(&table_path, st, name);
 
                        reader_close(cur[i]);
                        reftable_reader_free(cur[i]);
 
                        /* On Windows, can only unlink after closing. */
-                       unlink(filename.buf);
-
-                       strbuf_release(&filename);
+                       unlink(table_path.buf);
                }
        }
 
@@ -288,6 +285,7 @@ done:
        reftable_free(new_readers);
        reftable_free(new_tables);
        reftable_free(cur);
+       strbuf_release(&table_path);
        return err;
 }
 
@@ -427,16 +425,13 @@ int reftable_stack_add(struct reftable_stack *st,
                return err;
        }
 
-       if (!st->disable_auto_compact)
-               return reftable_stack_auto_compact(st);
-
        return 0;
 }
 
 static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
 {
        char buf[100];
-       uint32_t rnd = (uint32_t)rand();
+       uint32_t rnd = (uint32_t)git_rand();
        snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
                 min, max, rnd);
        strbuf_reset(dest);
@@ -444,8 +439,7 @@ static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
 }
 
 struct reftable_addition {
-       int lock_file_fd;
-       struct strbuf lock_file_name;
+       struct tempfile *lock_file;
        struct reftable_stack *stack;
 
        char **new_tables;
@@ -453,24 +447,19 @@ struct reftable_addition {
        uint64_t next_update_index;
 };
 
-#define REFTABLE_ADDITION_INIT                \
-       {                                     \
-               .lock_file_name = STRBUF_INIT \
-       }
+#define REFTABLE_ADDITION_INIT {0}
 
 static int reftable_stack_init_addition(struct reftable_addition *add,
                                        struct reftable_stack *st)
 {
+       struct strbuf lock_file_name = STRBUF_INIT;
        int err = 0;
        add->stack = st;
 
-       strbuf_reset(&add->lock_file_name);
-       strbuf_addstr(&add->lock_file_name, st->list_file);
-       strbuf_addstr(&add->lock_file_name, ".lock");
+       strbuf_addf(&lock_file_name, "%s.lock", st->list_file);
 
-       add->lock_file_fd = open(add->lock_file_name.buf,
-                                O_EXCL | O_CREAT | O_WRONLY, 0666);
-       if (add->lock_file_fd < 0) {
+       add->lock_file = create_tempfile(lock_file_name.buf);
+       if (!add->lock_file) {
                if (errno == EEXIST) {
                        err = REFTABLE_LOCK_ERROR;
                } else {
@@ -479,7 +468,7 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
                goto done;
        }
        if (st->config.default_permissions) {
-               if (chmod(add->lock_file_name.buf, st->config.default_permissions) < 0) {
+               if (chmod(add->lock_file->filename.buf, st->config.default_permissions) < 0) {
                        err = REFTABLE_IO_ERROR;
                        goto done;
                }
@@ -499,6 +488,7 @@ done:
        if (err) {
                reftable_addition_close(add);
        }
+       strbuf_release(&lock_file_name);
        return err;
 }
 
@@ -516,15 +506,7 @@ static void reftable_addition_close(struct reftable_addition *add)
        add->new_tables = NULL;
        add->new_tables_len = 0;
 
-       if (add->lock_file_fd > 0) {
-               close(add->lock_file_fd);
-               add->lock_file_fd = 0;
-       }
-       if (add->lock_file_name.len > 0) {
-               unlink(add->lock_file_name.buf);
-               strbuf_release(&add->lock_file_name);
-       }
-
+       delete_tempfile(&add->lock_file);
        strbuf_release(&nm);
 }
 
@@ -540,8 +522,10 @@ void reftable_addition_destroy(struct reftable_addition *add)
 int reftable_addition_commit(struct reftable_addition *add)
 {
        struct strbuf table_list = STRBUF_INIT;
+       int lock_file_fd = get_tempfile_fd(add->lock_file);
        int i = 0;
        int err = 0;
+
        if (add->new_tables_len == 0)
                goto done;
 
@@ -554,28 +538,20 @@ int reftable_addition_commit(struct reftable_addition *add)
                strbuf_addstr(&table_list, "\n");
        }
 
-       err = write(add->lock_file_fd, table_list.buf, table_list.len);
+       err = write_in_full(lock_file_fd, table_list.buf, table_list.len);
        strbuf_release(&table_list);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
 
-       err = close(add->lock_file_fd);
-       add->lock_file_fd = 0;
-       if (err < 0) {
-               err = REFTABLE_IO_ERROR;
-               goto done;
-       }
-
-       err = rename(add->lock_file_name.buf, add->stack->list_file);
+       err = rename_tempfile(&add->lock_file, add->stack->list_file);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
 
        /* success, no more state to clean up. */
-       strbuf_release(&add->lock_file_name);
        for (i = 0; i < add->new_tables_len; i++) {
                reftable_free(add->new_tables[i]);
        }
@@ -584,6 +560,12 @@ int reftable_addition_commit(struct reftable_addition *add)
        add->new_tables_len = 0;
 
        err = reftable_stack_reload(add->stack);
+       if (err)
+               goto done;
+
+       if (!add->stack->disable_auto_compact)
+               err = reftable_stack_auto_compact(add->stack);
+
 done:
        reftable_addition_close(add);
        return err;
@@ -816,18 +798,16 @@ static int stack_write_compact(struct reftable_stack *st,
                        err = 0;
                        break;
                }
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
 
                if (first == 0 && reftable_ref_record_is_deletion(&ref)) {
                        continue;
                }
 
                err = reftable_writer_add_ref(wr, &ref);
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                entries++;
        }
        reftable_iterator_destroy(&it);
@@ -842,9 +822,8 @@ static int stack_write_compact(struct reftable_stack *st,
                        err = 0;
                        break;
                }
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                if (first == 0 && reftable_log_record_is_deletion(&log)) {
                        continue;
                }
@@ -860,9 +839,8 @@ static int stack_write_compact(struct reftable_stack *st,
                }
 
                err = reftable_writer_add_log(wr, &log);
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                entries++;
        }
 
@@ -1024,7 +1002,7 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
                strbuf_addstr(&ref_list_contents, "\n");
        }
 
-       err = write(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
+       err = write_in_full(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                unlink(new_table_path.buf);
index d0b717510fa7d6dd8832bf77490630964e33b23b..289e902146470035e4dd0109af6b8436e97d40d4 100644 (file)
@@ -13,7 +13,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reftable-reader.h"
 #include "merged.h"
 #include "basics.h"
-#include "constants.h"
 #include "record.h"
 #include "test_framework.h"
 #include "reftable-tests.h"
@@ -78,7 +77,7 @@ static void test_read_file(void)
        int i = 0;
 
        EXPECT(fd > 0);
-       n = write(fd, out, strlen(out));
+       n = write_in_full(fd, out, strlen(out));
        EXPECT(n == strlen(out));
        err = close(fd);
        EXPECT(err >= 0);
@@ -289,6 +288,61 @@ static void test_reftable_stack_transaction_api(void)
        clear_dir(dir);
 }
 
+static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
+{
+       char *dir = get_tmp_dir(__LINE__);
+       struct reftable_write_options cfg = {0};
+       struct reftable_addition *add = NULL;
+       struct reftable_stack *st = NULL;
+       int i, n = 20, err;
+
+       err = reftable_new_stack(&st, dir, cfg);
+       EXPECT_ERR(err);
+
+       for (i = 0; i <= n; i++) {
+               struct reftable_ref_record ref = {
+                       .update_index = reftable_stack_next_update_index(st),
+                       .value_type = REFTABLE_REF_SYMREF,
+                       .value.symref = "master",
+               };
+               char name[100];
+
+               snprintf(name, sizeof(name), "branch%04d", i);
+               ref.refname = name;
+
+               /*
+                * Disable auto-compaction for all but the last runs. Like this
+                * we can ensure that we indeed honor this setting and have
+                * better control over when exactly auto compaction runs.
+                */
+               st->disable_auto_compact = i != n;
+
+               err = reftable_stack_new_addition(&add, st);
+               EXPECT_ERR(err);
+
+               err = reftable_addition_add(add, &write_test_ref, &ref);
+               EXPECT_ERR(err);
+
+               err = reftable_addition_commit(add);
+               EXPECT_ERR(err);
+
+               reftable_addition_destroy(add);
+
+               /*
+                * The stack length should grow continuously for all runs where
+                * auto compaction is disabled. When enabled, we should merge
+                * all tables in the stack.
+                */
+               if (i != n)
+                       EXPECT(st->merged->stack_len == i + 1);
+               else
+                       EXPECT(st->merged->stack_len == 1);
+       }
+
+       reftable_stack_destroy(st);
+       clear_dir(dir);
+}
+
 static void test_reftable_stack_validate_refname(void)
 {
        struct reftable_write_options cfg = { 0 };
@@ -408,7 +462,6 @@ static void test_reftable_stack_add(void)
                refs[i].refname = xstrdup(buf);
                refs[i].update_index = i + 1;
                refs[i].value_type = REFTABLE_REF_VAL1;
-               refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                set_test_hash(refs[i].value.val1, i);
 
                logs[i].refname = xstrdup(buf);
@@ -545,7 +598,6 @@ static void test_reftable_stack_tombstone(void)
                refs[i].update_index = i + 1;
                if (i % 2 == 0) {
                        refs[i].value_type = REFTABLE_REF_VAL1;
-                       refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                        set_test_hash(refs[i].value.val1, i);
                }
 
@@ -850,6 +902,54 @@ static void test_reftable_stack_auto_compaction(void)
        clear_dir(dir);
 }
 
+static void test_reftable_stack_add_performs_auto_compaction(void)
+{
+       struct reftable_write_options cfg = { 0 };
+       struct reftable_stack *st = NULL;
+       struct strbuf refname = STRBUF_INIT;
+       char *dir = get_tmp_dir(__LINE__);
+       int err, i, n = 20;
+
+       err = reftable_new_stack(&st, dir, cfg);
+       EXPECT_ERR(err);
+
+       for (i = 0; i <= n; i++) {
+               struct reftable_ref_record ref = {
+                       .update_index = reftable_stack_next_update_index(st),
+                       .value_type = REFTABLE_REF_SYMREF,
+                       .value.symref = "master",
+               };
+
+               /*
+                * Disable auto-compaction for all but the last runs. Like this
+                * we can ensure that we indeed honor this setting and have
+                * better control over when exactly auto compaction runs.
+                */
+               st->disable_auto_compact = i != n;
+
+               strbuf_reset(&refname);
+               strbuf_addf(&refname, "branch-%04d", i);
+               ref.refname = refname.buf;
+
+               err = reftable_stack_add(st, &write_test_ref, &ref);
+               EXPECT_ERR(err);
+
+               /*
+                * The stack length should grow continuously for all runs where
+                * auto compaction is disabled. When enabled, we should merge
+                * all tables in the stack.
+                */
+               if (i != n)
+                       EXPECT(st->merged->stack_len == i + 1);
+               else
+                       EXPECT(st->merged->stack_len == 1);
+       }
+
+       reftable_stack_destroy(st);
+       strbuf_release(&refname);
+       clear_dir(dir);
+}
+
 static void test_reftable_stack_compaction_concurrent(void)
 {
        struct reftable_write_options cfg = { 0 };
@@ -960,6 +1060,7 @@ int stack_test_main(int argc, const char *argv[])
        RUN_TEST(test_reftable_stack_add);
        RUN_TEST(test_reftable_stack_add_one);
        RUN_TEST(test_reftable_stack_auto_compaction);
+       RUN_TEST(test_reftable_stack_add_performs_auto_compaction);
        RUN_TEST(test_reftable_stack_compaction_concurrent);
        RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
        RUN_TEST(test_reftable_stack_hash_id);
@@ -967,6 +1068,7 @@ int stack_test_main(int argc, const char *argv[])
        RUN_TEST(test_reftable_stack_log_normalize);
        RUN_TEST(test_reftable_stack_tombstone);
        RUN_TEST(test_reftable_stack_transaction_api);
+       RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
        RUN_TEST(test_reftable_stack_update_index_check);
        RUN_TEST(test_reftable_stack_uptodate);
        RUN_TEST(test_reftable_stack_validate_refname);
index 18f9207dfee16accdaac46a77d13b9960aad3ca2..6b74a8151436144225c2e579340dfc3426a26470 100644 (file)
@@ -13,7 +13,7 @@ https://developers.google.com/open-source/licenses/bsd
 
 #include "git-compat-util.h"
 #include "strbuf.h"
-#include "hash.h" /* hash ID, sizes.*/
+#include "hash-ll.h" /* hash ID, sizes.*/
 #include "dir.h" /* remove_dir_recursively, for tests.*/
 
 int hash_size(uint32_t id);
index 84ac972cad063a3402731c704d9c7bfde4d24d6e..04044fc1a0fb5ffa143264527b882afb752fc2c6 100644 (file)
@@ -9,7 +9,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "system.h"
 #include "test_framework.h"
 
-#include "basics.h"
 
 void set_test_hash(uint8_t *p, int i)
 {
index 774cb275bf679440b804d86370c8858c9e0b1bd2..ee44f735aea51d03fc3400a9340ccbd7af881f9e 100644 (file)
@@ -12,32 +12,38 @@ https://developers.google.com/open-source/licenses/bsd
 #include "system.h"
 #include "reftable-error.h"
 
-#define EXPECT_ERR(c)                                                  \
-       if (c != 0) {                                                  \
-               fflush(stderr);                                        \
-               fflush(stdout);                                        \
-               fprintf(stderr, "%s: %d: error == %d (%s), want 0\n",  \
-                       __FILE__, __LINE__, c, reftable_error_str(c)); \
-               abort();                                               \
-       }
-
-#define EXPECT_STREQ(a, b)                                               \
-       if (strcmp(a, b)) {                                              \
-               fflush(stderr);                                          \
-               fflush(stdout);                                          \
-               fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
-                       __LINE__, #a, a, #b, b);                         \
-               abort();                                                 \
-       }
-
-#define EXPECT(c)                                                          \
-       if (!(c)) {                                                        \
-               fflush(stderr);                                            \
-               fflush(stdout);                                            \
-               fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
-                       __LINE__, #c);                                     \
-               abort();                                                   \
-       }
+#define EXPECT_ERR(c)                                                          \
+       do {                                                                   \
+               if (c != 0) {                                                  \
+                       fflush(stderr);                                        \
+                       fflush(stdout);                                        \
+                       fprintf(stderr, "%s: %d: error == %d (%s), want 0\n",  \
+                               __FILE__, __LINE__, c, reftable_error_str(c)); \
+                       abort();                                               \
+               }                                                              \
+       } while (0)
+
+#define EXPECT_STREQ(a, b)                                                       \
+       do {                                                                     \
+               if (strcmp(a, b)) {                                              \
+                       fflush(stderr);                                          \
+                       fflush(stdout);                                          \
+                       fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
+                               __LINE__, #a, a, #b, b);                         \
+                       abort();                                                 \
+               }                                                                \
+       } while (0)
+
+#define EXPECT(c)                                                                  \
+       do {                                                                       \
+               if (!(c)) {                                                        \
+                       fflush(stderr);                                            \
+                       fflush(stdout);                                            \
+                       fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
+                               __LINE__, #c);                                     \
+                       abort();                                                   \
+               }                                                                  \
+       } while (0)
 
 #define RUN_TEST(f)                          \
        fprintf(stderr, "running %s\n", #f); \
index b8899e060abdd6629d73510a8b126b0c00c90b8d..a5bf880985472dee7cd7c9169047752245599488 100644 (file)
@@ -6,10 +6,10 @@ license that can be found in the LICENSE file or at
 https://developers.google.com/open-source/licenses/bsd
 */
 
+#include "system.h"
 #include "tree.h"
 
 #include "basics.h"
-#include "system.h"
 
 struct tree_node *tree_search(void *key, struct tree_node **rootp,
                              int (*compare)(const void *, const void *),
index cbff12558868a2ad29337da146ed7c34d063634c..6961a657adb80497676895eedca696d142e5b57c 100644 (file)
@@ -6,10 +6,9 @@ license that can be found in the LICENSE file or at
 https://developers.google.com/open-source/licenses/bsd
 */
 
+#include "system.h"
 #include "tree.h"
 
-#include "basics.h"
-#include "record.h"
 #include "test_framework.h"
 #include "reftable-tests.h"
 
index 2e322a5683d081eea60fc85dfc4d792ce89b7a93..ee4590e20f84dd442fde4f7506e27e568f9ad509 100644 (file)
@@ -432,12 +432,12 @@ static int writer_finish_section(struct reftable_writer *w)
                reftable_free(idx);
        }
 
-       writer_clear_index(w);
-
        err = writer_flush_block(w);
        if (err < 0)
                return err;
 
+       writer_clear_index(w);
+
        bstats = writer_reftable_block_stats(w, typ);
        bstats->index_blocks = w->stats.idx_stats.blocks - before_blocks;
        bstats->index_offset = index_start;
index a76b6405eb2fc443ff22b93b1598892d02508e20..cb0182b582a5cb4d13718d4a47cfb07c078464a0 100644 (file)
@@ -1,22 +1,27 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "remote.h"
 #include "connect.h"
 #include "strbuf.h"
 #include "walker.h"
 #include "http.h"
-#include "exec-cmd.h"
 #include "run-command.h"
 #include "pkt-line.h"
 #include "string-list.h"
-#include "sideband.h"
 #include "strvec.h"
 #include "credential.h"
 #include "oid-array.h"
 #include "send-pack.h"
+#include "setup.h"
 #include "protocol.h"
 #include "quote.h"
+#include "trace2.h"
 #include "transport.h"
+#include "url.h"
+#include "write-or-die.h"
 
 static struct remote *remote;
 /* always ends with a trailing slash */
@@ -472,10 +477,11 @@ static struct discovery *discover_refs(const char *service, int for_push)
 
        /*
         * NEEDSWORK: If we are trying to use protocol v2 and we are planning
-        * to perform a push, then fallback to v0 since the client doesn't know
-        * how to push yet using v2.
+        * to perform any operation that doesn't involve upload-pack (i.e., a
+        * fetch, ls-remote, etc), then fallback to v0 since we don't know how
+        * to do anything else (like push or remote archive) via v2.
         */
-       if (version == protocol_v2 && !strcmp("git-receive-pack", service))
+       if (version == protocol_v2 && strcmp("git-upload-pack", service))
                version = protocol_v0;
 
        /* Add the extra Git-Protocol header */
@@ -755,7 +761,8 @@ static void check_pktline(struct check_pktline_state *state, const char *ptr, si
                        size -= digits_remaining;
 
                        if (state->len_filled == 4) {
-                               state->remaining = packet_length(state->len_buf);
+                               state->remaining = packet_length(state->len_buf,
+                                                                sizeof(state->len_buf));
                                if (state->remaining < 0) {
                                        die(_("remote-curl: bad line length character: %.4s"), state->len_buf);
                                } else if (state->remaining == 2) {
@@ -1556,8 +1563,11 @@ int cmd_main(int argc, const char **argv)
                if (buf.len == 0)
                        break;
                if (starts_with(buf.buf, "fetch ")) {
-                       if (nongit)
-                               die(_("remote-curl: fetch attempted without a local repo"));
+                       if (nongit) {
+                               setup_git_directory_gently(&nongit);
+                               if (nongit)
+                                       die(_("remote-curl: fetch attempted without a local repo"));
+                       }
                        parse_fetch(&buf);
 
                } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
index 60869beebe7364a594cd45938d4ed97dcdd28840..e07b316eac3f5242f59e8d586f8f8e52711be494 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1,20 +1,27 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "remote.h"
 #include "urlmatch.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
 #include "dir.h"
-#include "tag.h"
+#include "setup.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "commit-reach.h"
 #include "advice.h"
 #include "connect.h"
+#include "parse-options.h"
 
 enum map_direction { FROM_SRC, FROM_DST };
 
@@ -341,7 +348,8 @@ static void read_branches_file(struct remote_state *remote_state,
        remote->fetch_tags = 1; /* always auto-follow */
 }
 
-static int handle_config(const char *key, const char *value, void *cb)
+static int handle_config(const char *key, const char *value,
+                        const struct config_context *ctx, void *cb)
 {
        const char *name;
        size_t namelen;
@@ -349,6 +357,7 @@ static int handle_config(const char *key, const char *value, void *cb)
        struct remote *remote;
        struct branch *branch;
        struct remote_state *remote_state = cb;
+       const struct key_value_info *kvi = ctx->kvi;
 
        if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
                /* There is no subsection. */
@@ -406,8 +415,8 @@ static int handle_config(const char *key, const char *value, void *cb)
        }
        remote = make_remote(remote_state, name, namelen);
        remote->origin = REMOTE_CONFIG;
-       if (current_config_scope() == CONFIG_SCOPE_LOCAL ||
-           current_config_scope() == CONFIG_SCOPE_WORKTREE)
+       if (kvi->scope == CONFIG_SCOPE_LOCAL ||
+           kvi->scope == CONFIG_SCOPE_WORKTREE)
                remote->configured_in_repo = 1;
        if (!strcmp(subkey, "mirror"))
                remote->mirror = git_config_bool(key, value);
@@ -499,7 +508,7 @@ static void alias_all_urls(struct remote_state *remote_state)
        }
 }
 
-static void read_config(struct repository *repo)
+static void read_config(struct repository *repo, int early)
 {
        int flag;
 
@@ -508,7 +517,7 @@ static void read_config(struct repository *repo)
        repo->remote_state->initialized = 1;
 
        repo->remote_state->current_branch = NULL;
-       if (startup_info->have_repository) {
+       if (startup_info->have_repository && !early) {
                const char *head_ref = refs_resolve_ref_unsafe(
                        get_main_ref_store(repo), "HEAD", 0, NULL, &flag);
                if (head_ref && (flag & REF_ISSYMREF) &&
@@ -551,7 +560,7 @@ static const char *remotes_remote_for_branch(struct remote_state *remote_state,
 
 const char *remote_for_branch(struct branch *branch, int *explicit)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        die_on_missing_branch(the_repository, branch);
 
        return remotes_remote_for_branch(the_repository->remote_state, branch,
@@ -577,7 +586,7 @@ remotes_pushremote_for_branch(struct remote_state *remote_state,
 
 const char *pushremote_for_branch(struct branch *branch, int *explicit)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        die_on_missing_branch(the_repository, branch);
 
        return remotes_pushremote_for_branch(the_repository->remote_state,
@@ -589,7 +598,7 @@ static struct remote *remotes_remote_get(struct remote_state *remote_state,
 
 const char *remote_ref_for_branch(struct branch *branch, int for_push)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        die_on_missing_branch(the_repository, branch);
 
        if (branch) {
@@ -699,7 +708,13 @@ remotes_remote_get(struct remote_state *remote_state, const char *name)
 
 struct remote *remote_get(const char *name)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
+       return remotes_remote_get(the_repository->remote_state, name);
+}
+
+struct remote *remote_get_early(const char *name)
+{
+       read_config(the_repository, 1);
        return remotes_remote_get(the_repository->remote_state, name);
 }
 
@@ -712,7 +727,7 @@ remotes_pushremote_get(struct remote_state *remote_state, const char *name)
 
 struct remote *pushremote_get(const char *name)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        return remotes_pushremote_get(the_repository->remote_state, name);
 }
 
@@ -728,7 +743,7 @@ int remote_is_configured(struct remote *remote, int in_repo)
 int for_each_remote(each_remote_fn fn, void *priv)
 {
        int i, result = 0;
-       read_config(the_repository);
+       read_config(the_repository, 0);
        for (i = 0; i < the_repository->remote_state->remotes_nr && !result;
             i++) {
                struct remote *remote =
@@ -882,7 +897,7 @@ static int query_matches_negative_refspec(struct refspec *rs, struct refspec_ite
 {
        int i, matched_negative = 0;
        int find_src = !query->src;
-       struct string_list reversed = STRING_LIST_INIT_NODUP;
+       struct string_list reversed = STRING_LIST_INIT_DUP;
        const char *needle = find_src ? query->dst : query->src;
 
        /*
@@ -1163,7 +1178,7 @@ static int try_explicit_object_name(const char *name,
                return 0;
        }
 
-       if (get_oid(name, &oid))
+       if (repo_get_oid(the_repository, name, &oid))
                return -1;
 
        if (match) {
@@ -1251,7 +1266,7 @@ static void show_push_unqualified_ref_name_error(const char *dst_value,
        if (!advice_enabled(ADVICE_PUSH_UNQUALIFIED_REF_NAME))
                return;
 
-       if (get_oid(matched_src_name, &oid))
+       if (repo_get_oid(the_repository, matched_src_name, &oid))
                BUG("'%s' is not a valid object, "
                    "match_explicit_lhs() should catch this!",
                    matched_src_name);
@@ -1759,7 +1774,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
                        if (starts_with(ref->name, "refs/tags/"))
                                reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
-                       else if (!has_object_file(&ref->old_oid))
+                       else if (!repo_has_object_file(the_repository, &ref->old_oid))
                                reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
                        else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) ||
                                 !lookup_commit_reference_gently(the_repository, &ref->new_oid, 1))
@@ -1808,8 +1823,9 @@ static void set_merge(struct remote_state *remote_state, struct branch *ret)
                if (!remote_find_tracking(remote, ret->merge[i]) ||
                    strcmp(ret->remote_name, "."))
                        continue;
-               if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
-                            &oid, &ref, 0) == 1)
+               if (repo_dwim_ref(the_repository, ret->merge_name[i],
+                                 strlen(ret->merge_name[i]), &oid, &ref,
+                                 0) == 1)
                        ret->merge[i]->dst = ref;
                else
                        ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
@@ -1820,7 +1836,7 @@ struct branch *branch_get(const char *name)
 {
        struct branch *ret;
 
-       read_config(the_repository);
+       read_config(the_repository, 0);
        if (!name || !*name || !strcmp(name, "HEAD"))
                ret = the_repository->remote_state->current_branch;
        else
@@ -1962,7 +1978,7 @@ static const char *branch_get_push_1(struct remote_state *remote_state,
 
 const char *branch_get_push(struct branch *branch, struct strbuf *err)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        die_on_missing_branch(the_repository, branch);
 
        if (!branch)
@@ -2248,7 +2264,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
  * Return true when there is anything to report, otherwise false.
  */
 int format_tracking_info(struct branch *branch, struct strbuf *sb,
-                        enum ahead_behind_flags abf)
+                        enum ahead_behind_flags abf,
+                        int show_divergence_advice)
 {
        int ours, theirs, sti;
        const char *full_base;
@@ -2311,9 +2328,10 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
                               "respectively.\n",
                           ours + theirs),
                        base, ours, theirs);
-               if (advice_enabled(ADVICE_STATUS_HINTS))
+               if (show_divergence_advice &&
+                   advice_enabled(ADVICE_STATUS_HINTS))
                        strbuf_addstr(sb,
-                               _("  (use \"git pull\" to merge the remote branch into yours)\n"));
+                               _("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
        }
        free(base);
        return 1;
@@ -2505,7 +2523,7 @@ static int parse_push_cas_option(struct push_cas_option *cas, const char *arg, i
                entry->use_tracking = 1;
        else if (!colon[1])
                oidclr(&entry->expect);
-       else if (get_oid(colon + 1, &entry->expect))
+       else if (repo_get_oid(the_repository, colon + 1, &entry->expect))
                return error(_("cannot parse expected object name '%s'"),
                             colon + 1);
        return 0;
@@ -2662,7 +2680,7 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
                if (MERGE_BASES_BATCH_SIZE < size)
                        size = MERGE_BASES_BATCH_SIZE;
 
-               if ((ret = in_merge_bases_many(commit, size, chunk)))
+               if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk)))
                        break;
        }
 
index 1c4621b414bdc0372613e58c3b2133b84004d1e7..dfd4837e602755806bcfc73a725c04394ef81c6c 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -1,11 +1,11 @@
 #ifndef REMOTE_H
 #define REMOTE_H
 
-#include "cache.h"
-#include "parse-options.h"
+#include "hash-ll.h"
 #include "hashmap.h"
 #include "refspec.h"
 
+struct option;
 struct transport_ls_refs_options;
 
 /**
@@ -118,6 +118,7 @@ struct remote {
  * and configuration.
  */
 struct remote *remote_get(const char *name);
+struct remote *remote_get_early(const char *name);
 
 struct remote *pushremote_get(const char *name);
 int remote_is_configured(struct remote *remote, int in_repo);
@@ -234,6 +235,11 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
                             const struct string_list *server_options,
                             int stateless_rpc);
 
+/* Used for protocol v2 in order to retrieve refs from a remote */
+struct bundle_list;
+int get_remote_bundle_uri(int fd_out, struct packet_reader *reader,
+                         struct bundle_list *bundles, int stateless_rpc);
+
 int resolve_remote_symref(struct ref *ref, struct ref *list);
 
 /*
@@ -375,7 +381,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
                       const char **upstream_name, int for_push,
                       enum ahead_behind_flags abf);
 int format_tracking_info(struct branch *branch, struct strbuf *sb,
-                        enum ahead_behind_flags abf);
+                        enum ahead_behind_flags abf,
+                        int show_divergence_advice);
 
 struct ref *get_local_heads(void);
 /*
@@ -394,8 +401,6 @@ struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map);
 /*
  * Compare-and-swap
  */
-#define CAS_OPT_NAME "force-with-lease"
-
 struct push_cas_option {
        unsigned use_tracking_for_rest:1;
        unsigned use_force_if_includes:1;
index 320be2522d80a87fdb1569a97c5e24b77fd4a525..523215589de9221b0823aab9edbb06389ca52825 100644 (file)
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
 #include "oidmap.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "replace-object.h"
 #include "refs.h"
 #include "repository.h"
@@ -62,7 +64,7 @@ void prepare_replace_object(struct repository *r)
  * replacement object's name (replaced recursively, if necessary).
  * The return value is either oid or a pointer to a
  * permanently-allocated value.  This function always respects replace
- * references, regardless of the value of read_replace_refs.
+ * references, regardless of the value of r->settings.read_replace_refs.
  */
 const struct object_id *do_lookup_replace_object(struct repository *r,
                                                 const struct object_id *oid)
@@ -82,3 +84,29 @@ const struct object_id *do_lookup_replace_object(struct repository *r,
        }
        die(_("replace depth too high for object %s"), oid_to_hex(oid));
 }
+
+/*
+ * This indicator determines whether replace references should be
+ * respected process-wide, regardless of which repository is being
+ * using at the time.
+ */
+static int read_replace_refs = 1;
+
+void disable_replace_refs(void)
+{
+       read_replace_refs = 0;
+}
+
+int replace_refs_enabled(struct repository *r)
+{
+       if (!read_replace_refs)
+               return 0;
+
+       if (r->gitdir) {
+               prepare_repo_settings(r);
+               return r->settings.read_replace_refs;
+       }
+
+       /* repository has no objects or refs. */
+       return 0;
+}
index 3fbc32eb7b7ef7bf9119f83aa7e3a4b6c20104b3..66c41b938b44f4ba0a94521c4344aca0ca19087e 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "oidmap.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 struct replace_object {
        struct oidmap_entry original;
@@ -19,6 +19,18 @@ void prepare_replace_object(struct repository *r);
 const struct object_id *do_lookup_replace_object(struct repository *r,
                                                 const struct object_id *oid);
 
+/*
+ * Some commands disable replace-refs unconditionally, and otherwise each
+ * repository could alter the core.useReplaceRefs config value.
+ *
+ * Return 1 if and only if all of the following are true:
+ *
+ *  a. disable_replace_refs() has not been called.
+ *  b. GIT_NO_REPLACE_OBJECTS is unset or zero.
+ *  c. the given repository does not have core.useReplaceRefs=false.
+ */
+int replace_refs_enabled(struct repository *r);
+
 /*
  * If object sha1 should be replaced, return the replacement object's
  * name (replaced recursively, if necessary).  The return value is
@@ -33,11 +45,19 @@ const struct object_id *do_lookup_replace_object(struct repository *r,
 static inline const struct object_id *lookup_replace_object(struct repository *r,
                                                            const struct object_id *oid)
 {
-       if (!read_replace_refs ||
+       if (!replace_refs_enabled(r) ||
            (r->objects->replace_map_initialized &&
             r->objects->replace_map->map.tablesize == 0))
                return oid;
        return do_lookup_replace_object(r, oid);
 }
 
+/*
+ * Some commands override config and environment settings for using
+ * replace references. Use this method to disable the setting and ensure
+ * those other settings will not override this choice. This applies
+ * globally to all in-process repositories.
+ */
+void disable_replace_refs(void);
+
 #endif /* REPLACE_OBJECT_H */
index e8b58151bc4a01d0a90ba49d89ddf52d3d780995..30cd4787627b7cacbc2dbceddc0babee1b72cb3e 100644 (file)
@@ -1,8 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "repository.h"
 #include "midx.h"
-#include "compat/fsmonitor/fsm-listen.h"
 
 static void repo_cfg_bool(struct repository *r, const char *key, int *dest,
                          int def)
@@ -43,9 +42,11 @@ void prepare_repo_settings(struct repository *r)
        /* Defaults modified by feature.* */
        if (experimental) {
                r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
+               r->settings.pack_use_bitmap_boundary_traversal = 1;
        }
        if (manyfiles) {
                r->settings.index_version = 4;
+               r->settings.index_skip_hash = 1;
                r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE;
        }
 
@@ -60,6 +61,12 @@ void prepare_repo_settings(struct repository *r)
        repo_cfg_bool(r, "pack.usesparse", &r->settings.pack_use_sparse, 1);
        repo_cfg_bool(r, "core.multipackindex", &r->settings.core_multi_pack_index, 1);
        repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 0);
+       repo_cfg_bool(r, "index.skiphash", &r->settings.index_skip_hash, r->settings.index_skip_hash);
+       repo_cfg_bool(r, "pack.readreverseindex", &r->settings.pack_read_reverse_index, 1);
+       repo_cfg_bool(r, "pack.usebitmapboundarytraversal",
+                     &r->settings.pack_use_bitmap_boundary_traversal,
+                     r->settings.pack_use_bitmap_boundary_traversal);
+       repo_cfg_bool(r, "core.usereplacerefs", &r->settings.read_replace_refs, 1);
 
        /*
         * The GIT_TEST_MULTI_PACK_INDEX variable is special in that
index 5d166b692c8aa8ec24bbbde90cab1279ed2bdf53..d7d24d416ae4373c9283ec6bd5171b7cfdd1e759 100644 (file)
@@ -2,16 +2,21 @@
  * not really _using_ the compat macros, just make sure the_index
  * declaration matches the definition in this file.
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "cache.h"
+#define USE_THE_INDEX_VARIABLE
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "config.h"
 #include "object.h"
 #include "lockfile.h"
+#include "path.h"
+#include "read-cache-ll.h"
 #include "remote.h"
+#include "setup.h"
 #include "submodule-config.h"
 #include "sparse-index.h"
+#include "trace2.h"
 #include "promisor-remote.h"
 
 /* The main repository */
@@ -28,6 +33,8 @@ void initialize_the_repository(void)
        the_repo.remote_state = remote_state_new();
        the_repo.parsed_objects = parsed_object_pool_new();
 
+       index_state_init(&the_index, the_repository);
+
        repo_set_hash_algo(&the_repo, GIT_HASH_SHA1);
 }
 
@@ -97,6 +104,11 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)
        repo->hash_algo = &hash_algos[hash_algo];
 }
 
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+{
+       repo->ref_storage_format = format;
+}
+
 /*
  * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
  * Return 0 upon success and a non-zero value upon failure.
@@ -177,6 +189,8 @@ int repo_init(struct repository *repo,
                goto error;
 
        repo_set_hash_algo(repo, format.hash_algo);
+       repo_set_ref_storage_format(repo, format.ref_storage_format);
+       repo->repository_format_worktree_config = format.worktree_config;
 
        /* take ownership of format.partial_clone */
        repo->repository_format_partial_clone = format.partial_clone;
@@ -302,14 +316,13 @@ int repo_read_index(struct repository *repo)
 {
        int res;
 
-       if (!repo->index)
-               CALLOC_ARRAY(repo->index, 1);
-
        /* Complete the double-reference */
-       if (!repo->index->repo)
-               repo->index->repo = repo;
-       else if (repo->index->repo != repo)
+       if (!repo->index) {
+               ALLOC_ARRAY(repo->index, 1);
+               index_state_init(repo->index, repo);
+       } else if (repo->index->repo != repo) {
                BUG("repo's index should point back at itself");
+       }
 
        res = read_index_from(repo->index, repo->index_file, repo->gitdir);
 
index 24316ac944edcd5827de279d14fef7dea9334c74..f5269b3730c602a79adbde479f4622f124156b60 100644 (file)
@@ -1,9 +1,6 @@
 #ifndef REPOSITORY_H
 #define REPOSITORY_H
 
-#include "git-compat-util.h"
-#include "path.h"
-
 struct config_set;
 struct fsmonitor_settings;
 struct git_hash_algo;
@@ -27,6 +24,9 @@ enum fetch_negotiation_setting {
        FETCH_NEGOTIATION_NOOP,
 };
 
+#define REF_STORAGE_FORMAT_UNKNOWN 0
+#define REF_STORAGE_FORMAT_FILES   1
+
 struct repo_settings {
        int initialized;
 
@@ -37,10 +37,22 @@ struct repo_settings {
        int fetch_write_commit_graph;
        int command_requires_full_index;
        int sparse_index;
+       int pack_read_reverse_index;
+       int pack_use_bitmap_boundary_traversal;
+
+       /*
+        * Does this repository have core.useReplaceRefs=true (on by
+        * default)? This provides a repository-scoped version of this
+        * config, though it could be disabled process-wide via some Git
+        * builtins or the --no-replace-objects option. See
+        * replace_refs_enabled() for more details.
+        */
+       int read_replace_refs;
 
        struct fsmonitor_settings *fsmonitor; /* lazily loaded */
 
        int index_version;
+       int index_skip_hash;
        enum untracked_cache_setting core_untracked_cache;
 
        int pack_use_sparse;
@@ -151,6 +163,9 @@ struct repository {
        /* Repository's current hash algorithm, as serialized on disk. */
        const struct git_hash_algo *hash_algo;
 
+       /* Repository's reference storage format, as serialized on disk. */
+       unsigned int ref_storage_format;
+
        /* A unique-id for tracing purposes. */
        int trace2_repo_id;
 
@@ -162,12 +177,16 @@ struct repository {
        struct promisor_remote_config *promisor_remote_config;
 
        /* Configurations */
+       int repository_format_worktree_config;
 
        /* Indicate if a repository has a different 'commondir' from 'gitdir' */
        unsigned different_commondir:1;
 };
 
 extern struct repository *the_repository;
+#ifdef USE_THE_INDEX_VARIABLE
+extern struct index_state the_index;
+#endif
 
 /*
  * Define a custom repository layout. Any field can be NULL, which
@@ -186,6 +205,7 @@ void repo_set_gitdir(struct repository *repo, const char *root,
                     const struct set_gitdir_args *extra_args);
 void repo_set_worktree(struct repository *repo, const char *path);
 void repo_set_hash_algo(struct repository *repo, int algo);
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
 void initialize_the_repository(void);
 RESULT_MUST_BE_USED
 int repo_init(struct repository *r, const char *gitdir, const char *worktree);
@@ -219,9 +239,6 @@ int repo_hold_locked_index(struct repository *repo,
                           struct lock_file *lf,
                           int flags);
 
-int repo_read_index_preload(struct repository *,
-                           const struct pathspec *pathspec,
-                           unsigned refresh_flags);
 int repo_read_index_unmerged(struct repository *);
 /*
  * Opportunistically update the index but do not complain if we can't.
index 876ab435da949c8359c315636225d9fd7fd962ab..ca7e77ba68c1ad4aa49f032906ba419098be0d04 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -1,16 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "copy.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "string-list.h"
+#include "read-cache-ll.h"
 #include "rerere.h"
 #include "xdiff-interface.h"
 #include "dir.h"
 #include "resolve-undo.h"
-#include "ll-merge.h"
-#include "attr.h"
+#include "merge-ll.h"
+#include "path.h"
 #include "pathspec.h"
-#include "object-store.h"
-#include "hash-lookup.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "strmap.h"
 
 #define RESOLVED 0
@@ -197,7 +202,7 @@ static void read_rr(struct repository *r, struct string_list *rr)
                const unsigned hexsz = the_hash_algo->hexsz;
 
                /* There has to be the hash, tab, path and then NUL */
-               if (buf.len < hexsz + 2 || get_sha1_hex(buf.buf, hash))
+               if (buf.len < hexsz + 2 || get_hash_hex(buf.buf, hash))
                        die(_("corrupt MERGE_RR"));
 
                if (buf.buf[hexsz] != '.') {
@@ -965,8 +970,9 @@ static int handle_cache(struct index_state *istate,
                        break;
                i = ce_stage(ce) - 1;
                if (!mmfile[i].ptr) {
-                       mmfile[i].ptr = read_object_file(&ce->oid, &type,
-                                                        &size);
+                       mmfile[i].ptr = repo_read_object_file(the_repository,
+                                                             &ce->oid, &type,
+                                                             &size);
                        mmfile[i].size = size;
                }
        }
@@ -1104,7 +1110,7 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
         * recover the original conflicted state and then
         * find the conflicted paths.
         */
-       unmerge_index(r->index, pathspec);
+       unmerge_index(r->index, pathspec, 0);
        find_conflict(r, &conflict);
        for (i = 0; i < conflict.nr; i++) {
                struct string_list_item *it = &conflict.items[i];
index c32d79c3bd81f1ff6e9247b77a050784b917ecfd..5d6cb638793a4982e6b1e2cd1960dbb6ce0e6dd6 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -1,6 +1,7 @@
 #ifndef RERERE_H
 #define RERERE_H
 
+#include "gettext.h"
 #include "string-list.h"
 
 struct pathspec;
@@ -24,9 +25,6 @@ struct rerere_id {
 };
 
 int setup_rerere(struct repository *,struct string_list *, int);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define rerere(flags) repo_rerere(the_repository, flags)
-#endif
 int repo_rerere(struct repository *, int);
 /*
  * Given the conflict ID and the name of a "file" used for replaying
diff --git a/reset.c b/reset.c
index e3383a93343e3df16ee9eda6ee7886a49e980a1a..0f2ff0fe31531fc5602025cfcd866a9348cfcf63 100644 (file)
--- a/reset.c
+++ b/reset.c
@@ -1,9 +1,11 @@
 #include "git-compat-util.h"
 #include "cache-tree.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
+#include "object-name.h"
 #include "refs.h"
 #include "reset.h"
-#include "run-command.h"
 #include "tree-walk.h"
 #include "tree.h"
 #include "unpack-trees.h"
@@ -38,7 +40,7 @@ static int update_refs(const struct reset_head_opts *opts,
        prefix_len = msg.len;
 
        if (update_orig_head) {
-               if (!get_oid("ORIG_HEAD", &oid_old_orig))
+               if (!repo_get_oid(the_repository, "ORIG_HEAD", &oid_old_orig))
                        old_orig = &oid_old_orig;
                if (head) {
                        if (!reflog_orig_head) {
@@ -106,7 +108,7 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts)
                goto leave_reset_head;
        }
 
-       if (!get_oid("HEAD", &head_oid)) {
+       if (!repo_get_oid(r, "HEAD", &head_oid)) {
                head = &head_oid;
        } else if (!oid || !reset_hard) {
                ret = error(_("could not determine HEAD revision"));
@@ -128,6 +130,7 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts)
        unpack_tree_opts.update = 1;
        unpack_tree_opts.merge = 1;
        unpack_tree_opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
+       unpack_tree_opts.skip_cache_tree_update = 1;
        init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL);
        if (reset_hard)
                unpack_tree_opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
diff --git a/reset.h b/reset.h
index a28f81829d859dc3dfd3817d6449ab7eb195b0d3..10708d8ddc0040f86b5b1f400bfb14d932dffe8e 100644 (file)
--- a/reset.h
+++ b/reset.h
@@ -1,7 +1,7 @@
 #ifndef RESET_H
 #define RESET_H
 
-#include "hash.h"
+#include "hash-ll.h"
 #include "repository.h"
 
 #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
index e81096e2d459bd9391a5a8572fef2aae23d717ab..cd02dc99289997e5bf3ac57da3a0c5d482a8301d 100644 (file)
@@ -1,6 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
+#include "hash.h"
+#include "read-cache.h"
 #include "resolve-undo.h"
+#include "sparse-index.h"
 #include "string-list.h"
 
 /* The only error case is to run out of memory in string-list */
@@ -114,86 +117,59 @@ void resolve_undo_clear_index(struct index_state *istate)
        istate->cache_changed |= RESOLVE_UNDO_CHANGED;
 }
 
-int unmerge_index_entry_at(struct index_state *istate, int pos)
+int unmerge_index_entry(struct index_state *istate, const char *path,
+                       struct resolve_undo_info *ru, unsigned ce_flags)
 {
-       const struct cache_entry *ce;
-       struct string_list_item *item;
-       struct resolve_undo_info *ru;
-       int i, err = 0, matched;
-       char *name;
-
-       if (!istate->resolve_undo)
-               return pos;
-
-       ce = istate->cache[pos];
-       if (ce_stage(ce)) {
-               /* already unmerged */
-               while ((pos < istate->cache_nr) &&
-                      ! strcmp(istate->cache[pos]->name, ce->name))
-                       pos++;
-               return pos - 1; /* return the last entry processed */
+       int i = index_name_pos(istate, path, strlen(path));
+
+       if (i < 0) {
+               /* unmerged? */
+               i = -i - 1;
+               if (i < istate->cache_nr &&
+                   !strcmp(istate->cache[i]->name, path))
+                       /* yes, it is already unmerged */
+                       return 0;
+               /* fallthru: resolved to removal */
+       } else {
+               /* merged - remove it to replace it with unmerged entries */
+               remove_index_entry_at(istate, i);
        }
-       item = string_list_lookup(istate->resolve_undo, ce->name);
-       if (!item)
-               return pos;
-       ru = item->util;
-       if (!ru)
-               return pos;
-       matched = ce->ce_flags & CE_MATCHED;
-       name = xstrdup(ce->name);
-       remove_index_entry_at(istate, pos);
+
        for (i = 0; i < 3; i++) {
-               struct cache_entry *nce;
+               struct cache_entry *ce;
                if (!ru->mode[i])
                        continue;
-               nce = make_cache_entry(istate,
-                                      ru->mode[i],
-                                      &ru->oid[i],
-                                      name, i + 1, 0);
-               if (matched)
-                       nce->ce_flags |= CE_MATCHED;
-               if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
-                       err = 1;
-                       error("cannot unmerge '%s'", name);
-               }
+               ce = make_cache_entry(istate, ru->mode[i], &ru->oid[i],
+                                     path, i + 1, 0);
+               ce->ce_flags |= ce_flags;
+               if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD))
+                       return error("cannot unmerge '%s'", path);
        }
-       free(name);
-       if (err)
-               return pos;
-       free(ru);
-       item->util = NULL;
-       return unmerge_index_entry_at(istate, pos);
+       return 0;
 }
 
-void unmerge_marked_index(struct index_state *istate)
+void unmerge_index(struct index_state *istate, const struct pathspec *pathspec,
+                  unsigned ce_flags)
 {
-       int i;
+       struct string_list_item *item;
 
        if (!istate->resolve_undo)
                return;
 
        /* TODO: audit for interaction with sparse-index. */
        ensure_full_index(istate);
-       for (i = 0; i < istate->cache_nr; i++) {
-               const struct cache_entry *ce = istate->cache[i];
-               if (ce->ce_flags & CE_MATCHED)
-                       i = unmerge_index_entry_at(istate, i);
-       }
-}
 
-void unmerge_index(struct index_state *istate, const struct pathspec *pathspec)
-{
-       int i;
-
-       if (!istate->resolve_undo)
-               return;
-
-       /* TODO: audit for interaction with sparse-index. */
-       ensure_full_index(istate);
-       for (i = 0; i < istate->cache_nr; i++) {
-               const struct cache_entry *ce = istate->cache[i];
-               if (!ce_path_match(istate, ce, pathspec, NULL))
+       for_each_string_list_item(item, istate->resolve_undo) {
+               const char *path = item->string;
+               struct resolve_undo_info *ru = item->util;
+               if (!item->util)
+                       continue;
+               if (!match_pathspec(istate, pathspec,
+                                   item->string, strlen(item->string),
+                                   0, NULL, 0))
                        continue;
-               i = unmerge_index_entry_at(istate, i);
+               unmerge_index_entry(istate, path, ru, ce_flags);
+               free(ru);
+               item->util = NULL;
        }
 }
index 2b3f0f901e63aeab70dfca86d3067d169dc315e2..f3f8462751bfd2e7fb2ab38b2632f059b8f07013 100644 (file)
@@ -1,7 +1,12 @@
 #ifndef RESOLVE_UNDO_H
 #define RESOLVE_UNDO_H
 
-#include "cache.h"
+struct cache_entry;
+struct index_state;
+struct pathspec;
+struct string_list;
+
+#include "hash-ll.h"
 
 struct resolve_undo_info {
        unsigned int mode[3];
@@ -12,8 +17,7 @@ void record_resolve_undo(struct index_state *, struct cache_entry *);
 void resolve_undo_write(struct strbuf *, struct string_list *);
 struct string_list *resolve_undo_read(const char *, unsigned long);
 void resolve_undo_clear_index(struct index_state *);
-int unmerge_index_entry_at(struct index_state *, int);
-void unmerge_index(struct index_state *, const struct pathspec *);
-void unmerge_marked_index(struct index_state *);
+int unmerge_index_entry(struct index_state *, const char *, struct resolve_undo_info *, unsigned);
+void unmerge_index(struct index_state *, const struct pathspec *, unsigned);
 
 #endif
index 8f2623b3b5aab6d3723ecc7fb636bc8ece407b5b..2424c9bd674e534909df89e25c21b5eb119fda05 100644 (file)
@@ -1,5 +1,12 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oidset.h"
 #include "tag.h"
 #include "blob.h"
 #include "tree.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
 #include "decorate.h"
-#include "log-tree.h"
 #include "string-list.h"
 #include "line-log.h"
 #include "mailmap.h"
 #include "commit-slab.h"
-#include "dir.h"
 #include "cache-tree.h"
 #include "bisect.h"
 #include "packfile.h"
 #include "worktree.h"
+#include "read-cache.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "strvec.h"
+#include "trace2.h"
 #include "commit-reach.h"
 #include "commit-graph.h"
 #include "prio-queue.h"
@@ -34,6 +43,8 @@
 #include "json-writer.h"
 #include "list-objects-filter-options.h"
 #include "resolve-undo.h"
+#include "parse-options.h"
+#include "wildmatch.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -323,7 +334,8 @@ static void add_pending_object_with_path(struct rev_info *revs,
        if (revs->reflog_info && obj->type == OBJ_COMMIT) {
                struct strbuf buf = STRBUF_INIT;
                size_t namelen = strlen(name);
-               int len = interpret_branch_name(name, namelen, &buf, &options);
+               int len = repo_interpret_branch_name(the_repository, name,
+                                                    namelen, &buf, &options);
 
                if (0 < len && len < namelen && buf.len)
                        strbuf_addstr(&buf, name + len);
@@ -353,7 +365,7 @@ void add_head_to_pending(struct rev_info *revs)
 {
        struct object_id oid;
        struct object *obj;
-       if (get_oid("HEAD", &oid))
+       if (repo_get_oid(the_repository, "HEAD", &oid))
                return;
        obj = parse_object(revs->repo, &oid);
        if (!obj)
@@ -599,10 +611,12 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,
 static int tree_difference = REV_TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
-                   int addremove, unsigned mode,
-                   const struct object_id *oid,
-                   int oid_valid,
-                   const char *fullpath, unsigned dirty_submodule)
+                   int addremove,
+                   unsigned mode UNUSED,
+                   const struct object_id *oid UNUSED,
+                   int oid_valid UNUSED,
+                   const char *fullpath UNUSED,
+                   unsigned dirty_submodule UNUSED)
 {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
        struct rev_info *revs = options->change_fn_data;
@@ -613,12 +627,15 @@ static void file_add_remove(struct diff_options *options,
 }
 
 static void file_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 *fullpath,
-                unsigned old_dirty_submodule, unsigned new_dirty_submodule)
+                unsigned old_mode UNUSED,
+                unsigned new_mode UNUSED,
+                const struct object_id *old_oid UNUSED,
+                const struct object_id *new_oid UNUSED,
+                int old_oid_valid UNUSED,
+                int new_oid_valid UNUSED,
+                const char *fullpath UNUSED,
+                unsigned old_dirty_submodule UNUSED,
+                unsigned new_dirty_submodule UNUSED)
 {
        tree_difference = REV_TREE_DIFFERENT;
        options->flags.has_changes = 1;
@@ -771,8 +788,8 @@ static int check_maybe_different_in_bloom_filter(struct rev_info *revs,
 static int rev_compare_tree(struct rev_info *revs,
                            struct commit *parent, struct commit *commit, int nth_parent)
 {
-       struct tree *t1 = get_commit_tree(parent);
-       struct tree *t2 = get_commit_tree(commit);
+       struct tree *t1 = repo_get_commit_tree(the_repository, parent);
+       struct tree *t2 = repo_get_commit_tree(the_repository, commit);
        int bloom_ret = 1;
 
        if (!t1)
@@ -818,7 +835,7 @@ static int rev_compare_tree(struct rev_info *revs,
 
 static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
 {
-       struct tree *t1 = get_commit_tree(commit);
+       struct tree *t1 = repo_get_commit_tree(the_repository, commit);
 
        if (!t1)
                return 0;
@@ -956,7 +973,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
        if (!revs->prune)
                return;
 
-       if (!get_commit_tree(commit))
+       if (!repo_get_commit_tree(the_repository, commit))
                return;
 
        if (!commit->parents) {
@@ -1094,6 +1111,9 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
 
        if (commit->object.flags & ADDED)
                return 0;
+       if (revs->do_not_die_on_missing_objects &&
+           oidset_contains(&revs->missing_commits, &commit->object.oid))
+               return 0;
        commit->object.flags |= ADDED;
 
        if (revs->include_check &&
@@ -1150,7 +1170,8 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
        for (parent = commit->parents; parent; parent = parent->next) {
                struct commit *p = parent->item;
                int gently = revs->ignore_missing_links ||
-                            revs->exclude_promisor_objects;
+                            revs->exclude_promisor_objects ||
+                            revs->do_not_die_on_missing_objects;
                if (repo_parse_commit_gently(revs->repo, p, gently) < 0) {
                        if (revs->exclude_promisor_objects &&
                            is_promisor_object(&p->object.oid)) {
@@ -1158,7 +1179,11 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
                                        break;
                                continue;
                        }
-                       return -1;
+
+                       if (revs->do_not_die_on_missing_objects)
+                               oidset_insert(&revs->missing_commits, &p->object.oid);
+                       else
+                               return -1; /* corrupt repository */
                }
                if (revs->sources) {
                        char **slot = revision_sources_at(revs->sources, p);
@@ -1517,27 +1542,80 @@ static void add_rev_cmdline_list(struct rev_info *revs,
        }
 }
 
-struct all_refs_cb {
-       int all_flags;
-       int warned_bad_reflog;
-       struct rev_info *all_revs;
-       const char *name_for_errormsg;
-       struct worktree *wt;
-};
-
-int ref_excluded(struct string_list *ref_excludes, const char *path)
+int ref_excluded(const struct ref_exclusions *exclusions, const char *path)
 {
+       const char *stripped_path = strip_namespace(path);
        struct string_list_item *item;
 
-       if (!ref_excludes)
-               return 0;
-       for_each_string_list_item(item, ref_excludes) {
+       for_each_string_list_item(item, &exclusions->excluded_refs) {
                if (!wildmatch(item->string, path, 0))
                        return 1;
        }
+
+       if (ref_is_hidden(stripped_path, path, &exclusions->hidden_refs))
+               return 1;
+
        return 0;
 }
 
+void init_ref_exclusions(struct ref_exclusions *exclusions)
+{
+       struct ref_exclusions blank = REF_EXCLUSIONS_INIT;
+       memcpy(exclusions, &blank, sizeof(*exclusions));
+}
+
+void clear_ref_exclusions(struct ref_exclusions *exclusions)
+{
+       string_list_clear(&exclusions->excluded_refs, 0);
+       strvec_clear(&exclusions->hidden_refs);
+       exclusions->hidden_refs_configured = 0;
+}
+
+void add_ref_exclusion(struct ref_exclusions *exclusions, const char *exclude)
+{
+       string_list_append(&exclusions->excluded_refs, exclude);
+}
+
+struct exclude_hidden_refs_cb {
+       struct ref_exclusions *exclusions;
+       const char *section;
+};
+
+static int hide_refs_config(const char *var, const char *value,
+                           const struct config_context *ctx UNUSED,
+                           void *cb_data)
+{
+       struct exclude_hidden_refs_cb *cb = cb_data;
+       cb->exclusions->hidden_refs_configured = 1;
+       return parse_hide_refs_config(var, value, cb->section,
+                                     &cb->exclusions->hidden_refs);
+}
+
+void exclude_hidden_refs(struct ref_exclusions *exclusions, const char *section)
+{
+       struct exclude_hidden_refs_cb cb;
+
+       if (strcmp(section, "fetch") && strcmp(section, "receive") &&
+                       strcmp(section, "uploadpack"))
+               die(_("unsupported section for hidden refs: %s"), section);
+
+       if (exclusions->hidden_refs_configured)
+               die(_("--exclude-hidden= passed more than once"));
+
+       cb.exclusions = exclusions;
+       cb.section = section;
+
+       git_config(hide_refs_config, &cb);
+}
+
+struct all_refs_cb {
+       int all_flags;
+       int warned_bad_reflog;
+       struct rev_info *all_revs;
+       const char *name_for_errormsg;
+       struct worktree *wt;
+};
+
 static int handle_one_ref(const char *path, const struct object_id *oid,
                          int flag UNUSED,
                          void *cb_data)
@@ -1545,7 +1623,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
        struct all_refs_cb *cb = cb_data;
        struct object *object;
 
-       if (ref_excluded(cb->all_revs->ref_excludes, path))
+       if (ref_excluded(&cb->all_revs->ref_excludes, path))
            return 0;
 
        object = get_reference(cb->all_revs, path, oid, cb->all_flags);
@@ -1563,24 +1641,6 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->wt = NULL;
 }
 
-void clear_ref_exclusion(struct string_list **ref_excludes_p)
-{
-       if (*ref_excludes_p) {
-               string_list_clear(*ref_excludes_p, 0);
-               free(*ref_excludes_p);
-       }
-       *ref_excludes_p = NULL;
-}
-
-void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
-{
-       if (!*ref_excludes_p) {
-               CALLOC_ARRAY(*ref_excludes_p, 1);
-               (*ref_excludes_p)->strdup_strings = 1;
-       }
-       string_list_append(*ref_excludes_p, exclude);
-}
-
 static void handle_refs(struct ref_store *refs,
                        struct rev_info *revs, unsigned flags,
                        int (*for_each)(struct ref_store *, each_ref_fn, void *))
@@ -1775,7 +1835,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
        worktrees = get_worktrees();
        for (p = worktrees; *p; p++) {
                struct worktree *wt = *p;
-               struct index_state istate = { NULL };
+               struct index_state istate = INDEX_STATE_INIT(revs->repo);
 
                if (wt->is_current)
                        continue; /* current index already taken care of */
@@ -1829,7 +1889,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
                flags ^= UNINTERESTING | BOTTOM;
                arg++;
        }
-       if (get_oid_committish(arg, &oid))
+       if (repo_get_oid_committish(the_repository, arg, &oid))
                return 0;
        while (1) {
                it = get_reference(revs, arg, &oid, 0);
@@ -1865,30 +1925,15 @@ void repo_init_revisions(struct repository *r,
                         struct rev_info *revs,
                         const char *prefix)
 {
-       memset(revs, 0, sizeof(*revs));
+       struct rev_info blank = REV_INFO_INIT;
+       memcpy(revs, &blank, sizeof(*revs));
 
        revs->repo = r;
-       revs->abbrev = DEFAULT_ABBREV;
-       revs->simplify_history = 1;
        revs->pruning.repo = r;
-       revs->pruning.flags.recursive = 1;
-       revs->pruning.flags.quick = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
        revs->pruning.change_fn_data = revs;
-       revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
-       revs->dense = 1;
        revs->prefix = prefix;
-       revs->max_age = -1;
-       revs->max_age_as_filter = -1;
-       revs->min_age = -1;
-       revs->skip_count = -1;
-       revs->max_count = -1;
-       revs->max_parents = -1;
-       revs->expand_tabs_in_log = -1;
-
-       revs->commit_format = CMIT_FMT_DEFAULT;
-       revs->expand_tabs_in_log_default = 8;
 
        grep_init(&revs->grep_filter, revs->repo);
        revs->grep_filter.status_only = 1;
@@ -1901,6 +1946,7 @@ void repo_init_revisions(struct repository *r,
 
        init_display_notes(&revs->notes_opt);
        list_objects_filter_init(&revs->filter);
+       init_ref_exclusions(&revs->ref_excludes);
 }
 
 static void add_pending_commit_list(struct rev_info *revs,
@@ -1924,15 +1970,15 @@ static void prepare_show_merge(struct rev_info *revs)
        int i, prune_num = 1; /* counting terminating NULL */
        struct index_state *istate = revs->repo->index;
 
-       if (get_oid("HEAD", &oid))
+       if (repo_get_oid(the_repository, "HEAD", &oid))
                die("--merge without HEAD?");
        head = lookup_commit_or_die(&oid, "HEAD");
-       if (get_oid("MERGE_HEAD", &oid))
+       if (repo_get_oid(the_repository, "MERGE_HEAD", &oid))
                die("--merge without MERGE_HEAD?");
        other = lookup_commit_or_die(&oid, "MERGE_HEAD");
        add_pending_object(revs, &head->object, "HEAD");
        add_pending_object(revs, &other->object, "MERGE_HEAD");
-       bases = get_merge_bases(head, other);
+       bases = repo_get_merge_bases(the_repository, head, other);
        add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
        add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
        free_commit_list(bases);
@@ -2027,7 +2073,7 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
                if (!a || !b)
                        return dotdot_missing(arg, dotdot, revs, symmetric);
 
-               exclude = get_merge_bases(a, b);
+               exclude = repo_get_merge_bases(the_repository, a, b);
                add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
                                     flags_exclude);
                add_pending_commit_list(revs, exclude, flags_exclude);
@@ -2113,9 +2159,8 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
                int exclude_parent = 1;
 
                if (mark[2]) {
-                       char *end;
-                       exclude_parent = strtoul(mark + 2, &end, 10);
-                       if (*end != '\0' || !exclude_parent)
+                       if (strtol_i(mark + 2, 10, &exclude_parent) ||
+                           exclude_parent < 1)
                                return -1;
                }
 
@@ -2161,39 +2206,6 @@ static void read_pathspec_from_stdin(struct strbuf *sb,
                strvec_push(prune, sb->buf);
 }
 
-static void read_revisions_from_stdin(struct rev_info *revs,
-                                     struct strvec *prune)
-{
-       struct strbuf sb;
-       int seen_dashdash = 0;
-       int save_warning;
-
-       save_warning = warn_on_object_refname_ambiguity;
-       warn_on_object_refname_ambiguity = 0;
-
-       strbuf_init(&sb, 1000);
-       while (strbuf_getline(&sb, stdin) != EOF) {
-               int len = sb.len;
-               if (!len)
-                       break;
-               if (sb.buf[0] == '-') {
-                       if (len == 2 && sb.buf[1] == '-') {
-                               seen_dashdash = 1;
-                               break;
-                       }
-                       die("options not supported in --stdin mode");
-               }
-               if (handle_revision_arg(sb.buf, revs, 0,
-                                       REVARG_CANNOT_BE_FILENAME))
-                       die("bad revision '%s'", sb.buf);
-       }
-       if (seen_dashdash)
-               read_pathspec_from_stdin(&sb, prune);
-
-       strbuf_release(&sb);
-       warn_on_object_refname_ambiguity = save_warning;
-}
-
 static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
 {
        append_grep_pattern(&revs->grep_filter, ptn, "command line", 0, what);
@@ -2209,6 +2221,27 @@ static void add_message_grep(struct rev_info *revs, const char *pattern)
        add_grep(revs, pattern, GREP_PATTERN_BODY);
 }
 
+static int parse_count(const char *arg)
+{
+       int count;
+
+       if (strtol_i(arg, 10, &count) < 0)
+               die("'%s': not an integer", arg);
+       return count;
+}
+
+static timestamp_t parse_age(const char *arg)
+{
+       timestamp_t num;
+       char *p;
+
+       errno = 0;
+       num = parse_timestamp(arg, &p, 10);
+       if (errno || *p || p == arg)
+               die("'%s': not a number of seconds since epoch", arg);
+       return num;
+}
+
 static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
                               int *unkc, const char **unkv,
                               const struct setup_revision_opt* opt)
@@ -2226,7 +2259,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
            !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
            !strcmp(arg, "--indexed-objects") ||
            !strcmp(arg, "--alternate-refs") ||
-           starts_with(arg, "--exclude=") ||
+           starts_with(arg, "--exclude=") || starts_with(arg, "--exclude-hidden=") ||
            starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
            starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
        {
@@ -2235,29 +2268,27 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        }
 
        if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
-               revs->max_count = atoi(optarg);
+               revs->max_count = parse_count(optarg);
                revs->no_walk = 0;
                return argcount;
        } else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
-               revs->skip_count = atoi(optarg);
+               revs->skip_count = parse_count(optarg);
                return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
                /* accept -<digit>, like traditional "head" */
-               if (strtol_i(arg + 1, 10, &revs->max_count) < 0 ||
-                   revs->max_count < 0)
-                       die("'%s': not a non-negative integer", arg + 1);
+               revs->max_count = parse_count(arg + 1);
                revs->no_walk = 0;
        } else if (!strcmp(arg, "-n")) {
                if (argc <= 1)
                        return error("-n requires an argument");
-               revs->max_count = atoi(argv[1]);
+               revs->max_count = parse_count(argv[1]);
                revs->no_walk = 0;
                return 2;
        } else if (skip_prefix(arg, "-n", &optarg)) {
-               revs->max_count = atoi(optarg);
+               revs->max_count = parse_count(optarg);
                revs->no_walk = 0;
        } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
-               revs->max_age = atoi(optarg);
+               revs->max_age = parse_age(optarg);
                return argcount;
        } else if ((argcount = parse_long_opt("since", argv, &optarg))) {
                revs->max_age = approxidate(optarg);
@@ -2269,7 +2300,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->max_age = approxidate(optarg);
                return argcount;
        } else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
-               revs->min_age = atoi(optarg);
+               revs->min_age = parse_age(optarg);
                return argcount;
        } else if ((argcount = parse_long_opt("before", argv, &optarg))) {
                revs->min_age = approxidate(optarg);
@@ -2357,11 +2388,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--no-merges")) {
                revs->max_parents = 1;
        } else if (skip_prefix(arg, "--min-parents=", &optarg)) {
-               revs->min_parents = atoi(optarg);
+               revs->min_parents = parse_count(optarg);
        } else if (!strcmp(arg, "--no-min-parents")) {
                revs->min_parents = 0;
        } else if (skip_prefix(arg, "--max-parents=", &optarg)) {
-               revs->max_parents = atoi(optarg);
+               revs->max_parents = parse_count(optarg);
        } else if (!strcmp(arg, "--no-max-parents")) {
                revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
@@ -2370,8 +2401,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->left_right = 1;
        } else if (!strcmp(arg, "--left-only")) {
                if (revs->right_only)
-                       die("--left-only is incompatible with --right-only"
-                           " or --cherry");
+                       die(_("options '%s' and '%s' cannot be used together"),
+                           "--left-only", "--right-only/--cherry");
                revs->left_only = 1;
        } else if (!strcmp(arg, "--right-only")) {
                if (revs->left_only)
@@ -2479,6 +2510,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->break_bar = xstrdup(optarg);
                revs->track_linear = 1;
                revs->track_first_time = 1;
+       } else if (!strcmp(arg, "--show-notes-by-default")) {
+               revs->show_notes_by_default = 1;
        } else if (skip_prefix(arg, "--show-notes=", &optarg) ||
                   skip_prefix(arg, "--notes=", &optarg)) {
                if (starts_with(arg, "--show-notes=") &&
@@ -2636,7 +2669,7 @@ static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
        struct strbuf bisect_refs = STRBUF_INIT;
        int status;
        strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
-       status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data);
+       status = refs_for_each_fullref_in(refs, bisect_refs.buf, NULL, fn, cb_data);
        strbuf_release(&bisect_refs);
        return status;
 }
@@ -2690,10 +2723,13 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
                        init_all_refs_cb(&cb, revs, *flags);
                        other_head_refs(handle_one_ref, &cb);
                }
-               clear_ref_exclusion(&revs->ref_excludes);
+               clear_ref_exclusions(&revs->ref_excludes);
        } else if (!strcmp(arg, "--branches")) {
+               if (revs->ref_excludes.hidden_refs_configured)
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--branches");
                handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
-               clear_ref_exclusion(&revs->ref_excludes);
+               clear_ref_exclusions(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
                read_bisect_terms(&term_bad, &term_good);
                handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
@@ -2701,35 +2737,53 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
                            for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
+               if (revs->ref_excludes.hidden_refs_configured)
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--tags");
                handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
-               clear_ref_exclusion(&revs->ref_excludes);
+               clear_ref_exclusions(&revs->ref_excludes);
        } else if (!strcmp(arg, "--remotes")) {
+               if (revs->ref_excludes.hidden_refs_configured)
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--remotes");
                handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
-               clear_ref_exclusion(&revs->ref_excludes);
+               clear_ref_exclusions(&revs->ref_excludes);
        } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref(handle_one_ref, optarg, &cb);
-               clear_ref_exclusion(&revs->ref_excludes);
+               clear_ref_exclusions(&revs->ref_excludes);
                return argcount;
        } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
                add_ref_exclusion(&revs->ref_excludes, optarg);
                return argcount;
+       } else if ((argcount = parse_long_opt("exclude-hidden", argv, &optarg))) {
+               exclude_hidden_refs(&revs->ref_excludes, optarg);
+               return argcount;
        } else if (skip_prefix(arg, "--branches=", &optarg)) {
                struct all_refs_cb cb;
+               if (revs->ref_excludes.hidden_refs_configured)
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--branches");
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
-               clear_ref_exclusion(&revs->ref_excludes);
+               clear_ref_exclusions(&revs->ref_excludes);
        } else if (skip_prefix(arg, "--tags=", &optarg)) {
                struct all_refs_cb cb;
+               if (revs->ref_excludes.hidden_refs_configured)
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--tags");
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
-               clear_ref_exclusion(&revs->ref_excludes);
+               clear_ref_exclusions(&revs->ref_excludes);
        } else if (skip_prefix(arg, "--remotes=", &optarg)) {
                struct all_refs_cb cb;
+               if (revs->ref_excludes.hidden_refs_configured)
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--remotes");
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
-               clear_ref_exclusion(&revs->ref_excludes);
+               clear_ref_exclusions(&revs->ref_excludes);
        } else if (!strcmp(arg, "--reflog")) {
                add_reflogs_to_pending(revs, *flags);
        } else if (!strcmp(arg, "--indexed-objects")) {
@@ -2767,6 +2821,53 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
        return 1;
 }
 
+static void read_revisions_from_stdin(struct rev_info *revs,
+                                     struct strvec *prune)
+{
+       struct strbuf sb;
+       int seen_dashdash = 0;
+       int seen_end_of_options = 0;
+       int save_warning;
+       int flags = 0;
+
+       save_warning = warn_on_object_refname_ambiguity;
+       warn_on_object_refname_ambiguity = 0;
+
+       strbuf_init(&sb, 1000);
+       while (strbuf_getline(&sb, stdin) != EOF) {
+               if (!sb.len)
+                       break;
+
+               if (!strcmp(sb.buf, "--")) {
+                       seen_dashdash = 1;
+                       break;
+               }
+
+               if (!seen_end_of_options && sb.buf[0] == '-') {
+                       const char *argv[] = { sb.buf, NULL };
+
+                       if (!strcmp(sb.buf, "--end-of-options")) {
+                               seen_end_of_options = 1;
+                               continue;
+                       }
+
+                       if (handle_revision_pseudo_opt(revs, argv, &flags) > 0)
+                               continue;
+
+                       die(_("invalid option '%s' in --stdin mode"), sb.buf);
+               }
+
+               if (handle_revision_arg(sb.buf, revs, flags,
+                                       REVARG_CANNOT_BE_FILENAME))
+                       die("bad revision '%s'", sb.buf);
+       }
+       if (seen_dashdash)
+               read_pathspec_from_stdin(&sb, prune);
+
+       strbuf_release(&sb);
+       warn_on_object_refname_ambiguity = save_warning;
+}
+
 static void NORETURN diagnose_missing_default(const char *def)
 {
        int flags;
@@ -2903,7 +3004,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (!revs->def)
                revs->def = opt ? opt->def : NULL;
        if (opt && opt->tweak)
-               opt->tweak(revs, opt);
+               opt->tweak(revs);
        if (revs->show_merge)
                prepare_show_merge(revs);
        if (revs->def && !revs->pending.nr && !revs->rev_input_given) {
@@ -2958,8 +3059,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                revs->grep_filter.ignore_locale = 1;
        compile_grep_patterns(&revs->grep_filter);
 
-       if (revs->reverse && revs->reflog_info)
-               die(_("options '%s' and '%s' cannot be used together"), "--reverse", "--walk-reflogs");
        if (revs->reflog_info && revs->limited)
                die("cannot combine --walk-reflogs with history-limiting options");
        if (revs->rewrite_parents && revs->children.name)
@@ -2970,11 +3069,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        /*
         * Limitations on the graph functionality
         */
-       if (revs->reverse && revs->graph)
-               die(_("options '%s' and '%s' cannot be used together"), "--reverse", "--graph");
+       die_for_incompatible_opt3(!!revs->graph, "--graph",
+                                 !!revs->reverse, "--reverse",
+                                 !!revs->reflog_info, "--walk-reflogs");
 
-       if (revs->reflog_info && revs->graph)
-               die(_("options '%s' and '%s' cannot be used together"), "--walk-reflogs", "--graph");
        if (revs->no_walk && revs->graph)
                die(_("options '%s' and '%s' cannot be used together"), "--no-walk", "--graph");
        if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
@@ -2987,6 +3085,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->expand_tabs_in_log < 0)
                revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
 
+       if (!revs->show_notes_given && revs->show_notes_by_default) {
+               enable_default_display_notes(&revs->notes_opt, &revs->show_notes);
+               revs->show_notes_given = 1;
+       }
+
        return left;
 }
 
@@ -3009,6 +3112,11 @@ static void release_revisions_mailmap(struct string_list *mailmap)
 
 static void release_revisions_topo_walk_info(struct topo_walk_info *info);
 
+static void free_void_commit_list(void *list)
+{
+       free_commit_list(list);
+}
+
 void release_revisions(struct rev_info *revs)
 {
        free_commit_list(revs->commits);
@@ -3021,10 +3129,16 @@ void release_revisions(struct rev_info *revs)
        date_mode_release(&revs->date_mode);
        release_revisions_mailmap(revs->mailmap);
        free_grep_patterns(&revs->grep_filter);
+       graph_clear(revs->graph);
        /* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
        diff_free(&revs->pruning);
        reflog_walk_info_release(revs->reflog_info);
        release_revisions_topo_walk_info(revs->topo_walk_info);
+       clear_decoration(&revs->children, free_void_commit_list);
+       clear_decoration(&revs->merge_simplification, free);
+       clear_decoration(&revs->treesame, free);
+       line_log_free(revs);
+       oidset_clear(&revs->missing_commits);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
@@ -3401,8 +3515,8 @@ void reset_revision_walk(void)
 }
 
 static int mark_uninteresting(const struct object_id *oid,
-                             struct packed_git *pack,
-                             uint32_t pos,
+                             struct packed_git *pack UNUSED,
+                             uint32_t pos UNUSED,
                              void *cb)
 {
        struct rev_info *revs = cb;
@@ -3716,6 +3830,8 @@ int prepare_revision_walk(struct rev_info *revs)
                                       FOR_EACH_OBJECT_PROMISOR_ONLY);
        }
 
+       oidset_init(&revs->missing_commits, 0);
+
        if (!revs->reflog_info)
                prepare_to_use_bloom_filter(revs);
        if (!revs->unsorted_input)
@@ -3838,7 +3954,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
         * in it.
         */
        encoding = get_log_output_encoding();
-       message = logmsg_reencode(commit, NULL, encoding);
+       message = repo_logmsg_reencode(the_repository, commit, NULL, encoding);
 
        /* Copy the commit to temporary if we are using "fake" headers */
        if (buf.len)
@@ -3874,7 +3990,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
                retval = grep_buffer(&opt->grep_filter,
                                     (char *)message, strlen(message));
        strbuf_release(&buf);
-       unuse_commit_buffer(commit, message);
+       repo_unuse_commit_buffer(the_repository, commit, message);
        return retval;
 }
 
@@ -4120,7 +4236,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
  * Return true for entries that have not yet been shown.  (This is an
  * object_array_each_func_t.)
  */
-static int entry_unshown(struct object_array_entry *entry, void *cb_data_unused)
+static int entry_unshown(struct object_array_entry *entry, void *cb_data UNUSED)
 {
        return !(entry->item->flags & SHOWN);
 }
index afe1b77985faf5caeea507dad6639441013587c2..94c43138bc3e68651accecf79cdf4c28ba98582f 100644 (file)
@@ -2,13 +2,16 @@
 #define REVISION_H
 
 #include "commit.h"
-#include "parse-options.h"
 #include "grep.h"
 #include "notes.h"
+#include "oidset.h"
 #include "pretty.h"
 #include "diff.h"
 #include "commit-slab-decl.h"
+#include "decorate.h"
+#include "ident.h"
 #include "list-objects-filter-options.h"
+#include "strvec.h"
 
 /**
  * The revision walking API offers functions to build a list of revisions
@@ -61,6 +64,8 @@ struct string_list;
 struct saved_parents;
 struct bloom_key;
 struct bloom_filter_settings;
+struct option;
+struct parse_opt_ctx_t;
 define_shared_commit_slab(revision_sources, char *);
 
 struct rev_cmdline_info {
@@ -81,6 +86,35 @@ struct rev_cmdline_info {
        } *rev;
 };
 
+struct ref_exclusions {
+       /*
+        * Excluded refs is a list of wildmatch patterns. If any of the
+        * patterns match, the reference will be excluded.
+        */
+       struct string_list excluded_refs;
+
+       /*
+        * Hidden refs is a list of patterns that is to be hidden via
+        * `ref_is_hidden()`.
+        */
+       struct strvec hidden_refs;
+
+       /*
+        * Indicates whether hidden refs have been configured. This is to
+        * distinguish between no hidden refs existing and hidden refs not
+        * being parsed.
+        */
+       char hidden_refs_configured;
+};
+
+/**
+ * Initialize a `struct ref_exclusions` with a macro.
+ */
+#define REF_EXCLUSIONS_INIT { \
+       .excluded_refs = STRING_LIST_INIT_DUP, \
+       .hidden_refs = STRVEC_INIT, \
+}
+
 struct oidset;
 struct topo_walk_info;
 
@@ -103,7 +137,7 @@ struct rev_info {
        struct list_objects_filter_options filter;
 
        /* excluding from --branches, --refs, etc. expansion */
-       struct string_list *ref_excludes;
+       struct ref_exclusions ref_excludes;
 
        /* Basic information */
        const char *prefix;
@@ -179,18 +213,19 @@ struct rev_info {
 
                        /*
                         * Blobs are shown without regard for their existence.
-                        * But not so for trees: unless exclude_promisor_objects
+                        * But not so for trees/commits: unless exclude_promisor_objects
                         * is set and the tree in question is a promisor object;
                         * OR ignore_missing_links is set, the revision walker
-                        * dies with a "bad tree object HASH" message when
-                        * encountering a missing tree. For callers that can
-                        * handle missing trees and want them to be filterable
+                        * dies with a "bad <type> object HASH" message when
+                        * encountering a missing object. For callers that can
+                        * handle missing trees/commits and want them to be filterable
                         * and showable, set this to true. The revision walker
-                        * will filter and show such a missing tree as usual,
-                        * but will not attempt to recurse into this tree
-                        * object.
+                        * will filter and show such a missing object as usual,
+                        * but will not attempt to recurse into this tree/commit
+                        * object. The revision walker will also set the MISSING
+                        * flag for such objects.
                         */
-                       do_not_die_on_missing_tree:1,
+                       do_not_die_on_missing_objects:1,
 
                        /* for internal use only */
                        exclude_promisor_objects:1;
@@ -220,6 +255,7 @@ struct rev_info {
                        shown_dashes:1,
                        show_merge:1,
                        show_notes_given:1,
+                       show_notes_by_default:1,
                        show_signature:1,
                        pretty_given:1,
                        abbrev_commit:1,
@@ -338,6 +374,9 @@ struct rev_info {
 
        /* Location where temporary objects for remerge-diff are written. */
        struct tmp_objdir *remerge_objdir;
+
+       /* Missing commits to be tracked without failing traversal. */
+       struct oidset missing_commits;
 };
 
 /**
@@ -357,7 +396,23 @@ struct rev_info {
  * called before release_revisions() the "struct rev_info" can be left
  * uninitialized.
  */
-#define REV_INFO_INIT { 0 }
+#define REV_INFO_INIT { \
+       .abbrev = DEFAULT_ABBREV, \
+       .simplify_history = 1, \
+       .pruning.flags.recursive = 1, \
+       .pruning.flags.quick = 1, \
+       .sort_order = REV_SORT_IN_GRAPH_ORDER, \
+       .dense = 1, \
+       .max_age = -1, \
+       .max_age_as_filter = -1, \
+       .min_age = -1, \
+       .skip_count = -1, \
+       .max_count = -1, \
+       .max_parents = -1, \
+       .expand_tabs_in_log = -1, \
+       .commit_format = CMIT_FMT_DEFAULT, \
+       .expand_tabs_in_log_default = 8, \
+}
 
 /**
  * Initialize a rev_info structure with default values. The third parameter may
@@ -370,9 +425,6 @@ struct rev_info {
 void repo_init_revisions(struct repository *r,
                         struct rev_info *revs,
                         const char *prefix);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
-#endif
 
 /**
  * Parse revision information, filling in the `rev_info` structure, and
@@ -383,7 +435,7 @@ void repo_init_revisions(struct repository *r,
  */
 struct setup_revision_opt {
        const char *def;
-       void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+       void (*tweak)(struct rev_info *);
        unsigned int    assume_dashdash:1,
                        allow_exclude_promisor_objects:1,
                        free_removed_argv_elements:1;
@@ -439,12 +491,14 @@ void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees)
 void show_object_with_name(FILE *, struct object *, const char *);
 
 /**
- * Helpers to check if a "struct string_list" item matches with
- * wildmatch().
+ * Helpers to check if a reference should be excluded.
  */
-int ref_excluded(struct string_list *, const char *path);
-void clear_ref_exclusion(struct string_list **);
-void add_ref_exclusion(struct string_list **, const char *exclude);
+
+int ref_excluded(const struct ref_exclusions *exclusions, const char *path);
+void init_ref_exclusions(struct ref_exclusions *);
+void clear_ref_exclusions(struct ref_exclusions *);
+void add_ref_exclusion(struct ref_exclusions *, const char *exclude);
+void exclude_hidden_refs(struct ref_exclusions *, const char *section);
 
 /**
  * This function can be used if you want to add commit objects as revision
index 5ec3a46dccf959bd54af42fbeaaf4027dc64996a..0e7435718a52267a2421a27320b783ee9ce0c1a4 100644 (file)
@@ -1,15 +1,19 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "run-command.h"
+#include "environment.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "sigchain.h"
 #include "strvec.h"
+#include "symlinks.h"
 #include "thread-utils.h"
 #include "strbuf.h"
 #include "string-list.h"
+#include "trace.h"
+#include "trace2.h"
 #include "quote.h"
 #include "config.h"
 #include "packfile.h"
-#include "hook.h"
 #include "compat/nonblock.h"
 
 void child_process_init(struct child_process *child)
@@ -164,6 +168,7 @@ int is_executable(const char *name)
        return st.st_mode & S_IXUSR;
 }
 
+#ifndef locate_in_PATH
 /*
  * Search $PATH for a command.  This emulates the path search that
  * execvp would perform, without actually executing the command so it
@@ -212,6 +217,7 @@ static char *locate_in_PATH(const char *file)
        strbuf_release(&buf);
        return NULL;
 }
+#endif
 
 int exists_in_PATH(const char *command)
 {
@@ -301,7 +307,6 @@ enum child_errcode {
        CHILD_ERR_DUP2,
        CHILD_ERR_CLOSE,
        CHILD_ERR_SIGPROCMASK,
-       CHILD_ERR_ENOENT,
        CHILD_ERR_SILENT,
        CHILD_ERR_ERRNO
 };
@@ -341,19 +346,19 @@ static void child_close_pair(int fd[2])
        child_close(fd[1]);
 }
 
-static void child_error_fn(const char *err, va_list params)
+static void child_error_fn(const char *err UNUSED, va_list params UNUSED)
 {
        const char msg[] = "error() should not be called in child\n";
        xwrite(2, msg, sizeof(msg) - 1);
 }
 
-static void child_warn_fn(const char *err, va_list params)
+static void child_warn_fn(const char *err UNUSED, va_list params UNUSED)
 {
        const char msg[] = "warn() should not be called in child\n";
        xwrite(2, msg, sizeof(msg) - 1);
 }
 
-static void NORETURN child_die_fn(const char *err, va_list params)
+static void NORETURN child_die_fn(const char *err UNUSED, va_list params UNUSED)
 {
        const char msg[] = "die() should not be called in child\n";
        xwrite(2, msg, sizeof(msg) - 1);
@@ -384,9 +389,6 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
        case CHILD_ERR_SIGPROCMASK:
                error_errno("sigprocmask failed restoring signals");
                break;
-       case CHILD_ERR_ENOENT:
-               error_errno("cannot run %s", cmd->args.v[0]);
-               break;
        case CHILD_ERR_SILENT:
                break;
        case CHILD_ERR_ERRNO:
@@ -840,13 +842,9 @@ fail_pipe:
                        execve(argv.v[0], (char *const *) argv.v,
                               (char *const *) childenv);
 
-               if (errno == ENOENT) {
-                       if (cmd->silent_exec_failure)
-                               child_die(CHILD_ERR_SILENT);
-                       child_die(CHILD_ERR_ENOENT);
-               } else {
-                       child_die(CHILD_ERR_ERRNO);
-               }
+               if (cmd->silent_exec_failure && errno == ENOENT)
+                       child_die(CHILD_ERR_SILENT);
+               child_die(CHILD_ERR_ERRNO);
        }
        atfork_parent(&as);
        if (cmd->pid < 0)
@@ -1004,41 +1002,6 @@ int run_command(struct child_process *cmd)
        return finish_command(cmd);
 }
 
-int run_command_v_opt(const char **argv, int opt)
-{
-       return run_command_v_opt_cd_env(argv, opt, NULL, NULL);
-}
-
-int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)
-{
-       return run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, tr2_class);
-}
-
-int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
-{
-       return run_command_v_opt_cd_env_tr2(argv, opt, dir, env, NULL);
-}
-
-int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,
-                                const char *const *env, const char *tr2_class)
-{
-       struct child_process cmd = CHILD_PROCESS_INIT;
-       strvec_pushv(&cmd.args, argv);
-       cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
-       cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
-       cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
-       cmd.silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
-       cmd.use_shell = opt & RUN_USING_SHELL ? 1 : 0;
-       cmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;
-       cmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0;
-       cmd.close_object_store = opt & RUN_CLOSE_OBJECT_STORE ? 1 : 0;
-       cmd.dir = dir;
-       if (env)
-               strvec_pushv(&cmd.env, (const char **)env);
-       cmd.trace2_child_class = tr2_class;
-       return run_command(&cmd);
-}
-
 #ifndef NO_PTHREADS
 static pthread_t main_thread;
 static int main_thread_set;
@@ -1054,7 +1017,7 @@ static void *run_thread(void *data)
                sigset_t mask;
                sigemptyset(&mask);
                sigaddset(&mask, SIGPIPE);
-               if (pthread_sigmask(SIG_BLOCK, &mask, NULL) < 0) {
+               if (pthread_sigmask(SIG_BLOCK, &mask, NULL)) {
                        ret = error("unable to block SIGPIPE in async thread");
                        return (void *)ret;
                }
@@ -1496,16 +1459,8 @@ enum child_state {
        GIT_CP_WAIT_CLEANUP,
 };
 
-int run_processes_parallel_ungroup;
 struct parallel_processes {
-       void *data;
-
-       int max_processes;
-       int nr_processes;
-
-       get_next_task_fn get_next_task;
-       start_failure_fn start_failure;
-       task_finished_fn task_finished;
+       size_t nr_processes;
 
        struct {
                enum child_state state;
@@ -1520,81 +1475,60 @@ struct parallel_processes {
        struct pollfd *pfd;
 
        unsigned shutdown : 1;
-       unsigned ungroup : 1;
 
-       int output_owner;
+       size_t output_owner;
        struct strbuf buffered_output; /* of finished children */
 };
 
-static int default_start_failure(struct strbuf *out,
-                                void *pp_cb,
-                                void *pp_task_cb)
-{
-       return 0;
-}
+struct parallel_processes_for_signal {
+       const struct run_process_parallel_opts *opts;
+       const struct parallel_processes *pp;
+};
 
-static int default_task_finished(int result,
-                                struct strbuf *out,
-                                void *pp_cb,
-                                void *pp_task_cb)
+static void kill_children(const struct parallel_processes *pp,
+                         const struct run_process_parallel_opts *opts,
+                         int signo)
 {
-       return 0;
+       for (size_t i = 0; i < opts->processes; i++)
+               if (pp->children[i].state == GIT_CP_WORKING)
+                       kill(pp->children[i].process.pid, signo);
 }
 
-static void kill_children(struct parallel_processes *pp, int signo)
+static void kill_children_signal(const struct parallel_processes_for_signal *pp_sig,
+                                int signo)
 {
-       int i, n = pp->max_processes;
-
-       for (i = 0; i < n; i++)
-               if (pp->children[i].state == GIT_CP_WORKING)
-                       kill(pp->children[i].process.pid, signo);
+       kill_children(pp_sig->pp, pp_sig->opts, signo);
 }
 
-static struct parallel_processes *pp_for_signal;
+static struct parallel_processes_for_signal *pp_for_signal;
 
 static void handle_children_on_signal(int signo)
 {
-       kill_children(pp_for_signal, signo);
+       kill_children_signal(pp_for_signal, signo);
        sigchain_pop(signo);
        raise(signo);
 }
 
 static void pp_init(struct parallel_processes *pp,
-                   int n,
-                   get_next_task_fn get_next_task,
-                   start_failure_fn start_failure,
-                   task_finished_fn task_finished,
-                   void *data, int ungroup)
+                   const struct run_process_parallel_opts *opts,
+                   struct parallel_processes_for_signal *pp_sig)
 {
-       int i;
-
-       if (n < 1)
-               n = online_cpus();
+       const size_t n = opts->processes;
 
-       pp->max_processes = n;
+       if (!n)
+               BUG("you must provide a non-zero number of processes!");
 
-       trace_printf("run_processes_parallel: preparing to run up to %d tasks", n);
+       trace_printf("run_processes_parallel: preparing to run up to %"PRIuMAX" tasks",
+                    (uintmax_t)n);
 
-       pp->data = data;
-       if (!get_next_task)
+       if (!opts->get_next_task)
                BUG("you need to specify a get_next_task function");
-       pp->get_next_task = get_next_task;
-
-       pp->start_failure = start_failure ? start_failure : default_start_failure;
-       pp->task_finished = task_finished ? task_finished : default_task_finished;
 
-       pp->nr_processes = 0;
-       pp->output_owner = 0;
-       pp->shutdown = 0;
-       pp->ungroup = ungroup;
        CALLOC_ARRAY(pp->children, n);
-       if (pp->ungroup)
-               pp->pfd = NULL;
-       else
+       if (!opts->ungroup)
                CALLOC_ARRAY(pp->pfd, n);
-       strbuf_init(&pp->buffered_output, 0);
 
-       for (i = 0; i < n; i++) {
+       for (size_t i = 0; i < n; i++) {
                strbuf_init(&pp->children[i].err, 0);
                child_process_init(&pp->children[i].process);
                if (pp->pfd) {
@@ -1603,16 +1537,17 @@ static void pp_init(struct parallel_processes *pp,
                }
        }
 
-       pp_for_signal = pp;
+       pp_sig->pp = pp;
+       pp_sig->opts = opts;
+       pp_for_signal = pp_sig;
        sigchain_push_common(handle_children_on_signal);
 }
 
-static void pp_cleanup(struct parallel_processes *pp)
+static void pp_cleanup(struct parallel_processes *pp,
+                      const struct run_process_parallel_opts *opts)
 {
-       int i;
-
        trace_printf("run_processes_parallel: done");
-       for (i = 0; i < pp->max_processes; i++) {
+       for (size_t i = 0; i < opts->processes; i++) {
                strbuf_release(&pp->children[i].err);
                child_process_clear(&pp->children[i].process);
        }
@@ -1637,39 +1572,52 @@ static void pp_cleanup(struct parallel_processes *pp)
  * <0 no new job was started, user wishes to shutdown early. Use negative code
  *    to signal the children.
  */
-static int pp_start_one(struct parallel_processes *pp)
+static int pp_start_one(struct parallel_processes *pp,
+                       const struct run_process_parallel_opts *opts)
 {
-       int i, code;
+       size_t i;
+       int code;
 
-       for (i = 0; i < pp->max_processes; i++)
+       for (i = 0; i < opts->processes; i++)
                if (pp->children[i].state == GIT_CP_FREE)
                        break;
-       if (i == pp->max_processes)
+       if (i == opts->processes)
                BUG("bookkeeping is hard");
 
-       code = pp->get_next_task(&pp->children[i].process,
-                                pp->ungroup ? NULL : &pp->children[i].err,
-                                pp->data,
-                                &pp->children[i].data);
+       /*
+        * By default, do not inherit stdin from the parent process - otherwise,
+        * all children would share stdin! Users may overwrite this to provide
+        * something to the child's stdin by having their 'get_next_task'
+        * callback assign 0 to .no_stdin and an appropriate integer to .in.
+        */
+       pp->children[i].process.no_stdin = 1;
+
+       code = opts->get_next_task(&pp->children[i].process,
+                                  opts->ungroup ? NULL : &pp->children[i].err,
+                                  opts->data,
+                                  &pp->children[i].data);
        if (!code) {
-               if (!pp->ungroup) {
+               if (!opts->ungroup) {
                        strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
                        strbuf_reset(&pp->children[i].err);
                }
                return 1;
        }
-       if (!pp->ungroup) {
+       if (!opts->ungroup) {
                pp->children[i].process.err = -1;
                pp->children[i].process.stdout_to_stderr = 1;
        }
-       pp->children[i].process.no_stdin = 1;
 
        if (start_command(&pp->children[i].process)) {
-               code = pp->start_failure(pp->ungroup ? NULL :
-                                        &pp->children[i].err,
-                                        pp->data,
-                                        pp->children[i].data);
-               if (!pp->ungroup) {
+               if (opts->start_failure)
+                       code = opts->start_failure(opts->ungroup ? NULL :
+                                                  &pp->children[i].err,
+                                                  opts->data,
+                                                  pp->children[i].data);
+               else
+                       code = 0;
+
+               if (!opts->ungroup) {
                        strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
                        strbuf_reset(&pp->children[i].err);
                }
@@ -1685,19 +1633,19 @@ static int pp_start_one(struct parallel_processes *pp)
        return 0;
 }
 
-static void pp_buffer_stderr(struct parallel_processes *pp, int output_timeout)
+static void pp_buffer_stderr(struct parallel_processes *pp,
+                            const struct run_process_parallel_opts *opts,
+                            int output_timeout)
 {
-       int i;
-
-       while ((i = poll(pp->pfd, pp->max_processes, output_timeout)) < 0) {
+       while (poll(pp->pfd, opts->processes, output_timeout) < 0) {
                if (errno == EINTR)
                        continue;
-               pp_cleanup(pp);
+               pp_cleanup(pp, opts);
                die_errno("poll");
        }
 
        /* Buffer output from all pipes. */
-       for (i = 0; i < pp->max_processes; i++) {
+       for (size_t i = 0; i < opts->processes; i++) {
                if (pp->children[i].state == GIT_CP_WORKING &&
                    pp->pfd[i].revents & (POLLIN | POLLHUP)) {
                        int n = strbuf_read_once(&pp->children[i].err,
@@ -1712,9 +1660,9 @@ static void pp_buffer_stderr(struct parallel_processes *pp, int output_timeout)
        }
 }
 
-static void pp_output(struct parallel_processes *pp)
+static void pp_output(const struct parallel_processes *pp)
 {
-       int i = pp->output_owner;
+       size_t i = pp->output_owner;
 
        if (pp->children[i].state == GIT_CP_WORKING &&
            pp->children[i].err.len) {
@@ -1723,24 +1671,28 @@ static void pp_output(struct parallel_processes *pp)
        }
 }
 
-static int pp_collect_finished(struct parallel_processes *pp)
+static int pp_collect_finished(struct parallel_processes *pp,
+                              const struct run_process_parallel_opts *opts)
 {
-       int i, code;
-       int n = pp->max_processes;
+       int code;
+       size_t i;
        int result = 0;
 
        while (pp->nr_processes > 0) {
-               for (i = 0; i < pp->max_processes; i++)
+               for (i = 0; i < opts->processes; i++)
                        if (pp->children[i].state == GIT_CP_WAIT_CLEANUP)
                                break;
-               if (i == pp->max_processes)
+               if (i == opts->processes)
                        break;
 
                code = finish_command(&pp->children[i].process);
 
-               code = pp->task_finished(code, pp->ungroup ? NULL :
-                                        &pp->children[i].err, pp->data,
-                                        pp->children[i].data);
+               if (opts->task_finished)
+                       code = opts->task_finished(code, opts->ungroup ? NULL :
+                                                  &pp->children[i].err, opts->data,
+                                                  pp->children[i].data);
+               else
+                       code = 0;
 
                if (code)
                        result = code;
@@ -1753,12 +1705,14 @@ static int pp_collect_finished(struct parallel_processes *pp)
                        pp->pfd[i].fd = -1;
                child_process_init(&pp->children[i].process);
 
-               if (pp->ungroup) {
+               if (opts->ungroup) {
                        ; /* no strbuf_*() work to do here */
                } else if (i != pp->output_owner) {
                        strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
                        strbuf_reset(&pp->children[i].err);
                } else {
+                       const size_t n = opts->processes;
+
                        strbuf_write(&pp->children[i].err, stderr);
                        strbuf_reset(&pp->children[i].err);
 
@@ -1783,76 +1737,60 @@ static int pp_collect_finished(struct parallel_processes *pp)
        return result;
 }
 
-int run_processes_parallel(int n,
-                          get_next_task_fn get_next_task,
-                          start_failure_fn start_failure,
-                          task_finished_fn task_finished,
-                          void *pp_cb)
+void run_processes_parallel(const struct run_process_parallel_opts *opts)
 {
        int i, code;
        int output_timeout = 100;
        int spawn_cap = 4;
-       int ungroup = run_processes_parallel_ungroup;
-       struct parallel_processes pp;
-
-       /* unset for the next API user */
-       run_processes_parallel_ungroup = 0;
-
-       pp_init(&pp, n, get_next_task, start_failure, task_finished, pp_cb,
-               ungroup);
+       struct parallel_processes_for_signal pp_sig;
+       struct parallel_processes pp = {
+               .buffered_output = STRBUF_INIT,
+       };
+       /* options */
+       const char *tr2_category = opts->tr2_category;
+       const char *tr2_label = opts->tr2_label;
+       const int do_trace2 = tr2_category && tr2_label;
+
+       if (do_trace2)
+               trace2_region_enter_printf(tr2_category, tr2_label, NULL,
+                                          "max:%d", opts->processes);
+
+       pp_init(&pp, opts, &pp_sig);
        while (1) {
                for (i = 0;
                    i < spawn_cap && !pp.shutdown &&
-                   pp.nr_processes < pp.max_processes;
+                   pp.nr_processes < opts->processes;
                    i++) {
-                       code = pp_start_one(&pp);
+                       code = pp_start_one(&pp, opts);
                        if (!code)
                                continue;
                        if (code < 0) {
                                pp.shutdown = 1;
-                               kill_children(&pp, -code);
+                               kill_children(&pp, opts, -code);
                        }
                        break;
                }
                if (!pp.nr_processes)
                        break;
-               if (ungroup) {
-                       int i;
-
-                       for (i = 0; i < pp.max_processes; i++)
+               if (opts->ungroup) {
+                       for (size_t i = 0; i < opts->processes; i++)
                                pp.children[i].state = GIT_CP_WAIT_CLEANUP;
                } else {
-                       pp_buffer_stderr(&pp, output_timeout);
+                       pp_buffer_stderr(&pp, opts, output_timeout);
                        pp_output(&pp);
                }
-               code = pp_collect_finished(&pp);
+               code = pp_collect_finished(&pp, opts);
                if (code) {
                        pp.shutdown = 1;
                        if (code < 0)
-                               kill_children(&pp, -code);
+                               kill_children(&pp, opts,-code);
                }
        }
 
-       pp_cleanup(&pp);
-       return 0;
-}
-
-int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task,
-                              start_failure_fn start_failure,
-                              task_finished_fn task_finished, void *pp_cb,
-                              const char *tr2_category, const char *tr2_label)
-{
-       int result;
-
-       trace2_region_enter_printf(tr2_category, tr2_label, NULL, "max:%d",
-                                  ((n < 1) ? online_cpus() : n));
+       pp_cleanup(&pp, opts);
 
-       result = run_processes_parallel(n, get_next_task, start_failure,
-                                       task_finished, pp_cb);
-
-       trace2_region_leave(tr2_category, tr2_label, NULL);
-
-       return result;
+       if (do_trace2)
+               trace2_region_leave(tr2_category, tr2_label, NULL);
 }
 
 int run_auto_maintenance(int quiet)
@@ -1918,7 +1856,7 @@ enum start_bg_result start_bg_command(struct child_process *cmd,
                 *
                 * We also assume that `start_command()` does not add
                 * us to the cleanup list.  And that it calls
-                * calls `child_process_clear()`.
+                * `child_process_clear()`.
                 */
                sbgr = SBGR_ERROR;
                goto done;
index 0e85e5846a568d12472e24958860b11598d93599..1f22cc3827d7d0c53119d085c3ef53a6ab3c36fd 100644 (file)
@@ -150,9 +150,7 @@ struct child_process {
 }
 
 /**
- * The functions: child_process_init, start_command, finish_command,
- * run_command, run_command_v_opt, run_command_v_opt_cd_env, child_process_clear
- * do the following:
+ * The functions: start_command, finish_command, run_command do the following:
  *
  * - If a system call failed, errno is set and -1 is returned. A diagnostic
  *   is printed.
@@ -224,36 +222,6 @@ int run_command(struct child_process *);
  */
 int run_auto_maintenance(int quiet);
 
-#define RUN_COMMAND_NO_STDIN           (1<<0)
-#define RUN_GIT_CMD                    (1<<1)
-#define RUN_COMMAND_STDOUT_TO_STDERR   (1<<2)
-#define RUN_SILENT_EXEC_FAILURE                (1<<3)
-#define RUN_USING_SHELL                        (1<<4)
-#define RUN_CLEAN_ON_EXIT              (1<<5)
-#define RUN_WAIT_AFTER_CLEAN           (1<<6)
-#define RUN_CLOSE_OBJECT_STORE         (1<<7)
-
-/**
- * Convenience functions that encapsulate a sequence of
- * start_command() followed by finish_command(). The argument argv
- * specifies the program and its arguments. The argument opt is zero
- * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,
- * `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`
- * that correspond to the members .no_stdin, .git_cmd,
- * .stdout_to_stderr, .silent_exec_failure of `struct child_process`.
- * The argument dir corresponds the member .dir. The argument env
- * corresponds to the member .env.
- */
-int run_command_v_opt(const char **argv, int opt);
-int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class);
-/*
- * env (the environment) is to be formatted like environ: "VAR=VALUE".
- * To unset an environment variable use just "VAR".
- */
-int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
-int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,
-                                const char *const *env, const char *tr2_class);
-
 /**
  * Execute the given command, sending "in" to its stdin, and capturing its
  * stdout and stderr in the "out" and "err" strbufs. Any of the three may
@@ -459,17 +427,64 @@ typedef int (*task_finished_fn)(int result,
                                void *pp_task_cb);
 
 /**
- * Runs up to n processes at the same time. Whenever a process can be
- * started, the callback get_next_task_fn is called to obtain the data
+ * Option used by run_processes_parallel(), { 0 }-initialized means no
+ * options.
+ */
+struct run_process_parallel_opts
+{
+       /**
+        * tr2_category & tr2_label: sets the trace2 category and label for
+        * logging. These must either be unset, or both of them must be set.
+        */
+       const char *tr2_category;
+       const char *tr2_label;
+
+       /**
+        * processes: see 'processes' in run_processes_parallel() below.
+        */
+       size_t processes;
+
+       /**
+        * ungroup: see 'ungroup' in run_processes_parallel() below.
+        */
+       unsigned int ungroup:1;
+
+       /**
+        * get_next_task: See get_next_task_fn() above. This must be
+        * specified.
+        */
+       get_next_task_fn get_next_task;
+
+       /**
+        * start_failure: See start_failure_fn() above. This can be
+        * NULL to omit any special handling.
+        */
+       start_failure_fn start_failure;
+
+       /**
+        * task_finished: See task_finished_fn() above. This can be
+        * NULL to omit any special handling.
+        */
+       task_finished_fn task_finished;
+
+       /**
+        * data: user data, will be passed as "pp_cb" to the callback
+        * parameters.
+        */
+       void *data;
+};
+
+/**
+ * Options are passed via the "struct run_process_parallel_opts" above.
+ *
+ * Runs N 'processes' at the same time. Whenever a process can be
+ * started, the callback opts.get_next_task is called to obtain the data
  * required to start another child process.
  *
  * The children started via this function run in parallel. Their output
  * (both stdout and stderr) is routed to stderr in a manner that output
  * from different tasks does not interleave (but see "ungroup" below).
  *
- * start_failure_fn and task_finished_fn can be NULL to omit any
- * special handling.
- *
  * If the "ungroup" option isn't specified, the API will set the
  * "stdout_to_stderr" parameter in "struct child_process" and provide
  * the callbacks with a "struct strbuf *out" parameter to write output
@@ -479,20 +494,8 @@ typedef int (*task_finished_fn)(int result,
  * NULL "struct strbuf *out" parameter, and are responsible for
  * emitting their own output, including dealing with any race
  * conditions due to writing in parallel to stdout and stderr.
- * The "ungroup" option can be enabled by setting the global
- * "run_processes_parallel_ungroup" to "1" before invoking
- * run_processes_parallel(), it will be set back to "0" as soon as the
- * API reads that setting.
  */
-extern int run_processes_parallel_ungroup;
-int run_processes_parallel(int n,
-                          get_next_task_fn,
-                          start_failure_fn,
-                          task_finished_fn,
-                          void *pp_cb);
-int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn,
-                              task_finished_fn, void *pp_cb,
-                              const char *tr2_category, const char *tr2_label);
+void run_processes_parallel(const struct run_process_parallel_opts *opts);
 
 /**
  * Convenience function which prepares env for a command to be run in a
@@ -500,7 +503,7 @@ int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn,
  * exception of GIT_CONFIG_PARAMETERS and GIT_CONFIG_COUNT (which cause the
  * corresponding environment variables to be unset in the subprocess) and adds
  * an environment variable pointing to new_git_dir. See local_repo_env in
- * cache.h for more information.
+ * environment.h for more information.
  */
 void prepare_other_repo_env(struct strvec *env, const char *new_git_dir);
 
@@ -561,4 +564,6 @@ enum start_bg_result start_bg_command(struct child_process *cmd,
                                      void *cb_data,
                                      unsigned int timeout_sec);
 
+int sane_execvp(const char *file, char *const argv[]);
+
 #endif
diff --git a/sane-ctype.h b/sane-ctype.h
new file mode 100644 (file)
index 0000000..cbea1b2
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef SANE_CTYPE_H
+#define SANE_CTYPE_H
+
+/* Sane ctype - no locale, and works with signed chars */
+#undef isascii
+#undef isspace
+#undef isdigit
+#undef isalpha
+#undef isalnum
+#undef isprint
+#undef islower
+#undef isupper
+#undef tolower
+#undef toupper
+#undef iscntrl
+#undef ispunct
+#undef isxdigit
+
+extern const unsigned char sane_ctype[256];
+extern const signed char hexval_table[256];
+#define GIT_SPACE 0x01
+#define GIT_DIGIT 0x02
+#define GIT_ALPHA 0x04
+#define GIT_GLOB_SPECIAL 0x08
+#define GIT_REGEX_SPECIAL 0x10
+#define GIT_PATHSPEC_MAGIC 0x20
+#define GIT_CNTRL 0x40
+#define GIT_PUNCT 0x80
+#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define isascii(x) (((x) & ~0x7f) == 0)
+#define isspace(x) sane_istest(x,GIT_SPACE)
+#define isdigit(x) sane_istest(x,GIT_DIGIT)
+#define isalpha(x) sane_istest(x,GIT_ALPHA)
+#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define isprint(x) ((x) >= 0x20 && (x) <= 0x7e)
+#define islower(x) sane_iscase(x, 1)
+#define isupper(x) sane_iscase(x, 0)
+#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
+#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
+#define iscntrl(x) (sane_istest(x,GIT_CNTRL))
+#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \
+               GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)
+#define isxdigit(x) (hexval_table[(unsigned char)(x)] != -1)
+#define tolower(x) sane_case((unsigned char)(x), 0x20)
+#define toupper(x) sane_case((unsigned char)(x), 0)
+#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
+
+static inline int sane_case(int x, int high)
+{
+       if (sane_istest(x, GIT_ALPHA))
+               x = (x & ~0x20) | high;
+       return x;
+}
+
+static inline int sane_iscase(int x, int is_lower)
+{
+       if (!sane_istest(x, GIT_ALPHA))
+               return 0;
+
+       if (is_lower)
+               return (x & 0x20) != 0;
+       else
+               return (x & 0x20) == 0;
+}
+
+#endif
index c5c1ce689199083a9eb47ff6161ce6b1bcd7be64..fb2940c2a00c94d806319dc8d45e8ffed1231c17 100644 (file)
--- a/scalar.c
+++ b/scalar.c
@@ -2,7 +2,8 @@
  * The Scalar command-line interface.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "gettext.h"
 #include "parse-options.h"
 #include "config.h"
@@ -14,6 +15,8 @@
 #include "dir.h"
 #include "packfile.h"
 #include "help.h"
+#include "setup.h"
+#include "trace2.h"
 
 static void setup_enlistment_directory(int argc, const char **argv,
                                       const char * const *usagestr,
@@ -69,21 +72,18 @@ static void setup_enlistment_directory(int argc, const char **argv,
 
 static int run_git(const char *arg, ...)
 {
-       struct strvec argv = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        va_list args;
        const char *p;
-       int res;
 
        va_start(args, arg);
-       strvec_push(&argv, arg);
+       strvec_push(&cmd.args, arg);
        while ((p = va_arg(args, const char *)))
-               strvec_push(&argv, p);
+               strvec_push(&cmd.args, p);
        va_end(args);
 
-       res = run_command_v_opt(argv.v, RUN_GIT_CMD);
-
-       strvec_clear(&argv);
-       return res;
+       cmd.git_cmd = 1;
+       return run_command(&cmd);
 }
 
 struct scalar_config {
@@ -146,6 +146,7 @@ static int set_recommended_config(int reconfigure)
                { "credential.validate", "false", 1 }, /* GCM4W-only */
                { "gc.auto", "0", 1 },
                { "gui.GCWarning", "false", 1 },
+               { "index.skipHash", "false", 1 },
                { "index.threads", "true", 1 },
                { "index.version", "4", 1 },
                { "merge.stat", "false", 1 },
@@ -207,7 +208,10 @@ static int set_recommended_config(int reconfigure)
 
 static int toggle_maintenance(int enable)
 {
-       return run_git("maintenance", enable ? "start" : "unregister", NULL);
+       return run_git("maintenance",
+                      enable ? "start" : "unregister",
+                      enable ? NULL : "--force",
+                      NULL);
 }
 
 static int add_or_remove_enlistment(int add)
@@ -261,7 +265,7 @@ static int register_dir(void)
                return error(_("could not set recommended config"));
 
        if (toggle_maintenance(1))
-               return error(_("could not turn on maintenance"));
+               warning(_("could not turn on maintenance"));
 
        if (have_fsmonitor_support() && start_fsmonitor_daemon()) {
                return error(_("could not start the FSMonitor daemon"));
@@ -404,7 +408,8 @@ void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
 static int cmd_clone(int argc, const char **argv)
 {
        const char *branch = NULL;
-       int full_clone = 0, single_branch = 0;
+       int full_clone = 0, single_branch = 0, show_progress = isatty(2);
+       int src = 1;
        struct option clone_options[] = {
                OPT_STRING('b', "branch", &branch, N_("<branch>"),
                           N_("branch to checkout after clone")),
@@ -413,10 +418,13 @@ static int cmd_clone(int argc, const char **argv)
                OPT_BOOL(0, "single-branch", &single_branch,
                         N_("only download metadata for the branch that will "
                            "be checked out")),
+               OPT_BOOL(0, "src", &src,
+                        N_("create repository within 'src' directory")),
                OPT_END(),
        };
        const char * const clone_usage[] = {
-               N_("scalar clone [<options>] [--] <repo> [<dir>]"),
+               N_("scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+                  "\t[--[no-]src] <url> [<enlistment>]"),
                NULL
        };
        const char *url;
@@ -452,7 +460,10 @@ static int cmd_clone(int argc, const char **argv)
        if (is_directory(enlistment))
                die(_("directory '%s' exists already"), enlistment);
 
-       dir = xstrfmt("%s/src", enlistment);
+       if (src)
+               dir = xstrfmt("%s/src", enlistment);
+       else
+               dir = xstrdup(enlistment);
 
        strbuf_reset(&buf);
        if (branch)
@@ -499,7 +510,9 @@ static int cmd_clone(int argc, const char **argv)
        if (set_recommended_config(0))
                return error(_("could not configure '%s'"), dir);
 
-       if ((res = run_git("fetch", "--quiet", "origin", NULL))) {
+       if ((res = run_git("fetch", "--quiet",
+                               show_progress ? "--progress" : "--no-progress",
+                               "origin", NULL))) {
                warning(_("partial clone failed; attempting full clone"));
 
                if (set_config("remote.origin.promisor") ||
@@ -508,7 +521,9 @@ static int cmd_clone(int argc, const char **argv)
                        goto cleanup;
                }
 
-               if ((res = run_git("fetch", "--quiet", "origin", NULL)))
+               if ((res = run_git("fetch", "--quiet",
+                                       show_progress ? "--progress" : "--no-progress",
+                                       "origin", NULL)))
                        goto cleanup;
        }
 
@@ -558,7 +573,7 @@ static int cmd_diagnose(int argc, const char **argv)
        return res;
 }
 
-static int cmd_list(int argc, const char **argv)
+static int cmd_list(int argc, const char **argv UNUSED)
 {
        if (argc != 1)
                die(_("`scalar list` does not take arguments"));
@@ -586,7 +601,9 @@ static int cmd_register(int argc, const char **argv)
        return register_dir();
 }
 
-static int get_scalar_repos(const char *key, const char *value, void *data)
+static int get_scalar_repos(const char *key, const char *value,
+                           const struct config_context *ctx UNUSED,
+                           void *data)
 {
        struct string_list *list = data;
 
@@ -596,6 +613,24 @@ static int get_scalar_repos(const char *key, const char *value, void *data)
        return 0;
 }
 
+static int remove_deleted_enlistment(struct strbuf *path)
+{
+       int res = 0;
+       strbuf_realpath_forgiving(path, path->buf, 1);
+
+       if (run_git("config", "--global",
+                   "--unset", "--fixed-value",
+                   "scalar.repo", path->buf, NULL) < 0)
+               res = -1;
+
+       if (run_git("config", "--global",
+                   "--unset", "--fixed-value",
+                   "maintenance.repo", path->buf, NULL) < 0)
+               res = -1;
+
+       return res;
+}
+
 static int cmd_reconfigure(int argc, const char **argv)
 {
        int all = 0;
@@ -629,26 +664,67 @@ static int cmd_reconfigure(int argc, const char **argv)
        git_config(get_scalar_repos, &scalar_repos);
 
        for (i = 0; i < scalar_repos.nr; i++) {
+               int succeeded = 0;
                const char *dir = scalar_repos.items[i].string;
 
                strbuf_reset(&commondir);
                strbuf_reset(&gitdir);
 
                if (chdir(dir) < 0) {
-                       warning_errno(_("could not switch to '%s'"), dir);
-                       res = -1;
-               } else if (discover_git_directory(&commondir, &gitdir) < 0) {
-                       warning_errno(_("git repository gone in '%s'"), dir);
-                       res = -1;
-               } else {
-                       git_config_clear();
+                       struct strbuf buf = STRBUF_INIT;
+
+                       if (errno != ENOENT) {
+                               warning_errno(_("could not switch to '%s'"), dir);
+                               goto loop_end;
+                       }
+
+                       strbuf_addstr(&buf, dir);
+                       if (remove_deleted_enlistment(&buf))
+                               error(_("could not remove stale "
+                                       "scalar.repo '%s'"), dir);
+                       else {
+                               warning(_("removed stale scalar.repo '%s'"),
+                                       dir);
+                               succeeded = 1;
+                       }
+                       strbuf_release(&buf);
+                       goto loop_end;
+               }
 
-                       the_repository = &r;
-                       r.commondir = commondir.buf;
-                       r.gitdir = gitdir.buf;
+               switch (discover_git_directory_reason(&commondir, &gitdir)) {
+               case GIT_DIR_INVALID_OWNERSHIP:
+                       warning(_("repository at '%s' has different owner"), dir);
+                       goto loop_end;
 
-                       if (set_recommended_config(1) < 0)
-                               res = -1;
+               case GIT_DIR_INVALID_GITFILE:
+               case GIT_DIR_INVALID_FORMAT:
+                       warning(_("repository at '%s' has a format issue"), dir);
+                       goto loop_end;
+
+               case GIT_DIR_DISCOVERED:
+                       succeeded = 1;
+                       break;
+
+               default:
+                       warning(_("repository not found in '%s'"), dir);
+                       break;
+               }
+
+               git_config_clear();
+
+               the_repository = &r;
+               r.commondir = commondir.buf;
+               r.gitdir = gitdir.buf;
+
+               if (set_recommended_config(1) >= 0)
+                       succeeded = 1;
+
+loop_end:
+               if (!succeeded) {
+                       res = -1;
+                       warning(_("to unregister this repository from Scalar, run\n"
+                                 "\tgit config --global --unset --fixed-value scalar.repo \"%s\""),
+                               dir);
                }
        }
 
@@ -722,24 +798,6 @@ static int cmd_run(int argc, const char **argv)
        return 0;
 }
 
-static int remove_deleted_enlistment(struct strbuf *path)
-{
-       int res = 0;
-       strbuf_realpath_forgiving(path, path->buf, 1);
-
-       if (run_git("config", "--global",
-                   "--unset", "--fixed-value",
-                   "scalar.repo", path->buf, NULL) < 0)
-               res = -1;
-
-       if (run_git("config", "--global",
-                   "--unset", "--fixed-value",
-                   "maintenance.repo", path->buf, NULL) < 0)
-               res = -1;
-
-       return res;
-}
-
 static int cmd_unregister(int argc, const char **argv)
 {
        struct option options[] = {
index f2e19838c9c342b3b9d6df5e9bf24d0add0d47d1..37f59d4f66bbc2b21bf08288a75de4a159b24625 100644 (file)
@@ -1,21 +1,24 @@
-#include "builtin.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "commit.h"
-#include "refs.h"
-#include "object-store.h"
+#include "date.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "pkt-line.h"
 #include "sideband.h"
 #include "run-command.h"
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
-#include "quote.h"
 #include "transport.h"
 #include "version.h"
 #include "oid-array.h"
 #include "gpg-interface.h"
-#include "cache.h"
 #include "shallow.h"
+#include "parse-options.h"
+#include "trace2.h"
+#include "write-or-die.h"
 
 int option_parse_push_signed(const struct option *opt,
                             const char *arg, int unset)
@@ -42,9 +45,9 @@ int option_parse_push_signed(const struct option *opt,
 static void feed_object(const struct object_id *oid, FILE *fh, int negative)
 {
        if (negative &&
-           !has_object_file_with_flags(oid,
-                                       OBJECT_INFO_SKIP_FETCH_OBJECT |
-                                       OBJECT_INFO_QUICK))
+           !repo_has_object_file_with_flags(the_repository, oid,
+                                            OBJECT_INFO_SKIP_FETCH_OBJECT |
+                                            OBJECT_INFO_QUICK))
                return;
 
        if (negative)
@@ -534,7 +537,7 @@ int send_pack(struct send_pack_args *args,
                die(_("the receiving end does not support this repository's hash algorithm"));
 
        if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
-               int len;
+               size_t len;
                push_cert_nonce = server_feature_value("push-cert", &len);
                if (push_cert_nonce) {
                        reject_invalid_nonce(push_cert_nonce, len);
index 0cf3842201a85d05bf77ba2cac4bbef57d681c9a..3cc88d8a8004d91eb2a404214f4ac136a845d61f 100644 (file)
@@ -1,23 +1,33 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "dir.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "object.h"
+#include "pager.h"
 #include "commit.h"
 #include "sequencer.h"
-#include "tag.h"
 #include "run-command.h"
 #include "hook.h"
-#include "exec-cmd.h"
 #include "utf8.h"
 #include "cache-tree.h"
 #include "diff.h"
+#include "path.h"
 #include "revision.h"
 #include "rerere.h"
+#include "merge.h"
 #include "merge-ort.h"
 #include "merge-ort-wrappers.h"
 #include "refs.h"
+#include "sparse-index.h"
 #include "strvec.h"
 #include "quote.h"
 #include "trailer.h"
@@ -27,7 +37,6 @@
 #include "notes-utils.h"
 #include "sigchain.h"
 #include "unpack-trees.h"
-#include "worktree.h"
 #include "oidmap.h"
 #include "oidset.h"
 #include "commit-slab.h"
 #include "rebase-interactive.h"
 #include "reset.h"
 #include "branch.h"
-#include "log-tree.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
+/*
+ * To accommodate common filesystem limitations, where the loose refs' file
+ * names must not exceed `NAME_MAX`, the labels generated by `git rebase
+ * --rebase-merges` need to be truncated if the corresponding commit subjects
+ * are too long.
+ * Add some margin to stay clear from reaching `NAME_MAX`.
+ */
+#define GIT_MAX_LABEL_LENGTH ((NAME_MAX) - (LOCK_SUFFIX_LEN) - 16)
+
 static const char sign_off_header[] = "Signed-off-by: ";
 static const char cherry_picked_prefix[] = "(cherry picked from commit ";
 
@@ -127,6 +144,11 @@ static GIT_PATH_FUNC(rebase_path_amend, "rebase-merge/amend")
  * the commit object name of the corresponding patch.
  */
 static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha")
+/*
+ * When we stop for the user to resolve conflicts this file contains
+ * the patch of the commit that is being picked.
+ */
+static GIT_PATH_FUNC(rebase_path_patch, "rebase-merge/patch")
 /*
  * For the post-rewrite hook, we make a list of rewritten commits and
  * their new sha1s.  The rewritten-pending list keeps the sha1s of
@@ -209,37 +231,33 @@ static struct update_ref_record *init_update_ref_record(const char *ref)
        return rec;
 }
 
-static int git_sequencer_config(const char *k, const char *v, void *cb)
+static int git_sequencer_config(const char *k, const char *v,
+                               const struct config_context *ctx, void *cb)
 {
        struct replay_opts *opts = cb;
-       int status;
 
        if (!strcmp(k, "commit.cleanup")) {
-               const char *s;
+               if (!v)
+                       return config_error_nonbool(k);
 
-               status = git_config_string(&s, k, v);
-               if (status)
-                       return status;
-
-               if (!strcmp(s, "verbatim")) {
+               if (!strcmp(v, "verbatim")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
                        opts->explicit_cleanup = 1;
-               } else if (!strcmp(s, "whitespace")) {
+               } else if (!strcmp(v, "whitespace")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
                        opts->explicit_cleanup = 1;
-               } else if (!strcmp(s, "strip")) {
+               } else if (!strcmp(v, "strip")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
                        opts->explicit_cleanup = 1;
-               } else if (!strcmp(s, "scissors")) {
+               } else if (!strcmp(v, "scissors")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
                        opts->explicit_cleanup = 1;
                } else {
                        warning(_("invalid commit message cleanup mode '%s'"),
-                                 s);
+                                 v);
                }
 
-               free((char *)s);
-               return status;
+               return 0;
        }
 
        if (!strcmp(k, "commit.gpgsign")) {
@@ -264,11 +282,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)
        if (opts->action == REPLAY_REVERT && !strcmp(k, "revert.reference"))
                opts->commit_use_reference = git_config_bool(k, v);
 
-       status = git_gpg_config(k, v, NULL);
-       if (status)
-               return status;
-
-       return git_diff_basic_config(k, v, NULL);
+       return git_diff_basic_config(k, v, ctx, NULL);
 }
 
 void sequencer_init_config(struct replay_opts *opts)
@@ -323,7 +337,7 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
        if (ignore_footer)
                sb->buf[sb->len - ignore_footer] = saved_char;
 
-       if (info.trailer_start == info.trailer_end)
+       if (info.trailer_block_start == info.trailer_block_end)
                return 0;
 
        for (i = 0; i < info.trailer_nr; i++)
@@ -352,10 +366,23 @@ static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
        return buf.buf;
 }
 
+void replay_opts_release(struct replay_opts *opts)
+{
+       free(opts->gpg_sign);
+       free(opts->reflog_action);
+       free(opts->default_strategy);
+       free(opts->strategy);
+       strvec_clear (&opts->xopts);
+       strbuf_release(&opts->current_fixups);
+       if (opts->revs)
+               release_revisions(opts->revs);
+       free(opts->revs);
+}
+
 int sequencer_remove_state(struct replay_opts *opts)
 {
        struct strbuf buf = STRBUF_INIT;
-       int i, ret = 0;
+       int ret = 0;
 
        if (is_rebase_i(opts) &&
            strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
@@ -374,14 +401,6 @@ int sequencer_remove_state(struct replay_opts *opts)
                }
        }
 
-       free(opts->gpg_sign);
-       free(opts->default_strategy);
-       free(opts->strategy);
-       for (i = 0; i < opts->xopts_nr; i++)
-               free(opts->xopts[i]);
-       free(opts->xopts);
-       strbuf_release(&opts->current_fixups);
-
        strbuf_reset(&buf);
        strbuf_addstr(&buf, get_dir(opts));
        if (remove_dir_recursively(&buf, 0))
@@ -411,9 +430,9 @@ struct commit_message {
        const char *message;
 };
 
-static const char *short_commit_name(struct commit *commit)
+static const char *short_commit_name(struct repository *r, struct commit *commit)
 {
-       return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV);
+       return repo_find_unique_abbrev(r, &commit->object.oid, DEFAULT_ABBREV);
 }
 
 static int get_message(struct commit *commit, struct commit_message *out)
@@ -421,8 +440,9 @@ static int get_message(struct commit *commit, struct commit_message *out)
        const char *abbrev, *subject;
        int subject_len;
 
-       out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding());
-       abbrev = short_commit_name(commit);
+       out->message = repo_logmsg_reencode(the_repository, commit, NULL,
+                                           get_commit_output_encoding());
+       abbrev = short_commit_name(the_repository, commit);
 
        subject_len = find_commit_subject(out->message, &subject);
 
@@ -438,7 +458,7 @@ static void free_message(struct commit *commit, struct commit_message *msg)
        free(msg->parent_label);
        free(msg->label);
        free(msg->subject);
-       unuse_commit_buffer(commit, msg->message);
+       repo_unuse_commit_buffer(the_repository, commit, msg->message);
 }
 
 static void print_advice(struct repository *r, int show_hint,
@@ -555,7 +575,7 @@ static void update_abort_safety_file(void)
        if (!file_exists(git_path_seq_dir()))
                return;
 
-       if (!get_oid("HEAD", &head))
+       if (!repo_get_oid(the_repository, "HEAD", &head))
                write_file(git_path_abort_safety_file(), "%s", oid_to_hex(&head));
        else
                write_file(git_path_abort_safety_file(), "%s", "");
@@ -647,11 +667,12 @@ void append_conflicts_hint(struct index_state *istate,
        }
 
        strbuf_addch(msgbuf, '\n');
-       strbuf_commented_addf(msgbuf, "Conflicts:\n");
+       strbuf_commented_addf(msgbuf, comment_line_char, "Conflicts:\n");
        for (i = 0; i < istate->cache_nr;) {
                const struct cache_entry *ce = istate->cache[i++];
                if (ce_stage(ce)) {
-                       strbuf_commented_addf(msgbuf, "\t%s\n", ce->name);
+                       strbuf_commented_addf(msgbuf, comment_line_char,
+                                             "\t%s\n", ce->name);
                        while (i < istate->cache_nr &&
                               !strcmp(ce->name, istate->cache[i]->name))
                                i++;
@@ -686,11 +707,11 @@ static int do_recursive_merge(struct repository *r,
        o.show_rename_progress = 1;
 
        head_tree = parse_tree_indirect(head);
-       next_tree = next ? get_commit_tree(next) : empty_tree(r);
-       base_tree = base ? get_commit_tree(base) : empty_tree(r);
+       next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r);
+       base_tree = base ? repo_get_commit_tree(r, base) : empty_tree(r);
 
-       for (i = 0; i < opts->xopts_nr; i++)
-               parse_merge_opt(&o, opts->xopts[i]);
+       for (i = 0; i < opts->xopts.nr; i++)
+               parse_merge_opt(&o, opts->xopts.v[i]);
 
        if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
                memset(&result, 0, sizeof(result));
@@ -760,12 +781,12 @@ static int is_index_unchanged(struct repository *r)
        /*
         * If head_commit is NULL, check_commit, called from
         * lookup_commit, would have indicated that head_commit is not
-        * a commit object already.  parse_commit() will return failure
+        * a commit object already.  repo_parse_commit() will return failure
         * without further complaints in such a case.  Otherwise, if
-        * the commit is invalid, parse_commit() will complain.  So
+        * the commit is invalid, repo_parse_commit() will complain.  So
         * there is nothing for us to say here.  Just return failure.
         */
-       if (parse_commit(head_commit))
+       if (repo_parse_commit(r, head_commit))
                return -1;
 
        if (!(cache_tree_oid = get_cache_tree_oid(istate)))
@@ -1050,6 +1071,8 @@ static int run_git_commit(const char *defmsg,
                             gpg_opt, gpg_opt);
        }
 
+       strvec_pushf(&cmd.env, GIT_REFLOG_ACTION "=%s", opts->reflog_message);
+
        if (opts->committer_date_is_author_date)
                strvec_pushf(&cmd.env, "GIT_COMMITTER_DATE=%s",
                             opts->ignore_date ?
@@ -1128,7 +1151,8 @@ void cleanup_message(struct strbuf *msgbuf,
            cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
                strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
        if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
-               strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+               strbuf_stripspace(msgbuf,
+                 cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
 }
 
 /*
@@ -1159,7 +1183,8 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
        if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
                return 0;
 
-       strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+       strbuf_stripspace(&tmpl,
+         cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
        if (!skip_prefix(sb->buf, tmpl.buf, &start))
                start = sb->buf;
        strbuf_release(&tmpl);
@@ -1328,13 +1353,15 @@ void print_commit_summary(struct repository *r,
        commit = lookup_commit(r, oid);
        if (!commit)
                die(_("couldn't look up newly created commit"));
-       if (parse_commit(commit))
+       if (repo_parse_commit(r, commit))
                die(_("could not parse newly created commit"));
 
        strbuf_addstr(&format, "format:%h] %s");
 
-       format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-       format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+       repo_format_commit_message(r, commit, "%an <%ae>", &author_ident,
+                                  &pctx);
+       repo_format_commit_message(r, commit, "%cn <%ce>", &committer_ident,
+                                  &pctx);
        if (strbuf_cmp(&author_ident, &committer_ident)) {
                strbuf_addstr(&format, "\n Author: ");
                strbuf_addbuf_percentquote(&format, &author_ident);
@@ -1342,7 +1369,7 @@ void print_commit_summary(struct repository *r,
        if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
                struct strbuf date = STRBUF_INIT;
 
-               format_commit_message(commit, "%ad", &date, &pctx);
+               repo_format_commit_message(r, commit, "%ad", &date, &pctx);
                strbuf_addstr(&format, "\n Date: ");
                strbuf_addbuf_percentquote(&format, &date);
                strbuf_release(&date);
@@ -1372,7 +1399,7 @@ void print_commit_summary(struct repository *r,
        rev.diffopt.detect_rename = DIFF_DETECT_RENAME;
        diff_setup_done(&rev.diffopt);
 
-       refs = get_main_ref_store(the_repository);
+       refs = get_main_ref_store(r);
        head = refs_resolve_ref_unsafe(refs, "HEAD", 0, NULL, NULL);
        if (!head)
                die(_("unable to resolve HEAD after creating commit"));
@@ -1398,7 +1425,7 @@ static int parse_head(struct repository *r, struct commit **head)
        struct commit *current_head;
        struct object_id oid;
 
-       if (get_oid("HEAD", &oid)) {
+       if (repo_get_oid(r, "HEAD", &oid)) {
                current_head = NULL;
        } else {
                current_head = lookup_commit_reference(r, &oid);
@@ -1408,7 +1435,7 @@ static int parse_head(struct repository *r, struct commit **head)
                        warning(_("HEAD %s is not a commit!"),
                                oid_to_hex(&oid));
                }
-               if (parse_commit(current_head))
+               if (repo_parse_commit(r, current_head))
                        return error(_("could not parse HEAD commit"));
        }
        *head = current_head;
@@ -1451,8 +1478,8 @@ static int try_to_commit(struct repository *r,
        if (flags & AMEND_MSG) {
                const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL };
                const char *out_enc = get_commit_output_encoding();
-               const char *message = logmsg_reencode(current_head, NULL,
-                                                     out_enc);
+               const char *message = repo_logmsg_reencode(r, current_head,
+                                                          NULL, out_enc);
 
                if (!msg) {
                        const char *orig_message = NULL;
@@ -1463,7 +1490,8 @@ static int try_to_commit(struct repository *r,
                        hook_commit = "HEAD";
                }
                author = amend_author = get_author(message);
-               unuse_commit_buffer(current_head, message);
+               repo_unuse_commit_buffer(r, current_head,
+                                        message);
                if (!author) {
                        res = error(_("unable to parse commit author"));
                        goto out;
@@ -1528,7 +1556,8 @@ static int try_to_commit(struct repository *r,
                cleanup = opts->default_msg_cleanup;
 
        if (cleanup != COMMIT_MSG_CLEANUP_NONE)
-               strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
+               strbuf_stripspace(msg,
+                 cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
        if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
                res = 1; /* run 'git commit' to display error message */
                goto out;
@@ -1589,8 +1618,8 @@ static int try_to_commit(struct repository *r,
                goto out;
        }
 
-       if (update_head_with_reflog(current_head, oid,
-                                   getenv("GIT_REFLOG_ACTION"), msg, &err)) {
+       if (update_head_with_reflog(current_head, oid, opts->reflog_message,
+                                   msg, &err)) {
                res = error("%s", err.buf);
                goto out;
        }
@@ -1660,12 +1689,12 @@ static int is_original_commit_empty(struct commit *commit)
 {
        const struct object_id *ptree_oid;
 
-       if (parse_commit(commit))
+       if (repo_parse_commit(the_repository, commit))
                return error(_("could not parse commit %s"),
                             oid_to_hex(&commit->object.oid));
        if (commit->parents) {
                struct commit *parent = commit->parents->item;
-               if (parse_commit(parent))
+               if (repo_parse_commit(the_repository, parent))
                        return error(_("could not parse parent commit %s"),
                                oid_to_hex(&parent->object.oid));
                ptree_oid = get_commit_tree_oid(parent);
@@ -1822,7 +1851,7 @@ static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
                s += count;
                len -= count;
        }
-       strbuf_add_commented_lines(buf, s, len);
+       strbuf_add_commented_lines(buf, s, len, comment_line_char);
 }
 
 /* Does the current fixup chain contain a squash command? */
@@ -1921,7 +1950,7 @@ static int append_squash_message(struct strbuf *buf, const char *body,
        strbuf_addf(buf, _(nth_commit_msg_fmt),
                    ++opts->current_fixup_count + 1);
        strbuf_addstr(buf, "\n\n");
-       strbuf_add_commented_lines(buf, body, commented_len);
+       strbuf_add_commented_lines(buf, body, commented_len, comment_line_char);
        /* buf->buf may be reallocated so store an offset into the buffer */
        fixup_off = buf->len;
        strbuf_addstr(buf, body + commented_len);
@@ -1989,17 +2018,18 @@ static int update_squash_messages(struct repository *r,
                struct commit *head_commit;
                const char *head_message, *body;
 
-               if (get_oid("HEAD", &head))
+               if (repo_get_oid(r, "HEAD", &head))
                        return error(_("need a HEAD to fixup"));
                if (!(head_commit = lookup_commit_reference(r, &head)))
                        return error(_("could not read HEAD"));
-               if (!(head_message = logmsg_reencode(head_commit, NULL, encoding)))
+               if (!(head_message = repo_logmsg_reencode(r, head_commit, NULL,
+                                                         encoding)))
                        return error(_("could not read HEAD's commit message"));
 
                find_commit_subject(head_message, &body);
                if (command == TODO_FIXUP && !flag && write_message(body, strlen(body),
                                                        rebase_path_fixup_msg(), 0) < 0) {
-                       unuse_commit_buffer(head_commit, head_message);
+                       repo_unuse_commit_buffer(r, head_commit, head_message);
                        return error(_("cannot write '%s'"), rebase_path_fixup_msg());
                }
                strbuf_addf(&buf, "%c ", comment_line_char);
@@ -2010,14 +2040,15 @@ static int update_squash_messages(struct repository *r,
                              _(first_commit_msg_str));
                strbuf_addstr(&buf, "\n\n");
                if (is_fixup_flag(command, flag))
-                       strbuf_add_commented_lines(&buf, body, strlen(body));
+                       strbuf_add_commented_lines(&buf, body, strlen(body),
+                                                  comment_line_char);
                else
                        strbuf_addstr(&buf, body);
 
-               unuse_commit_buffer(head_commit, head_message);
+               repo_unuse_commit_buffer(r, head_commit, head_message);
        }
 
-       if (!(message = logmsg_reencode(commit, NULL, encoding)))
+       if (!(message = repo_logmsg_reencode(r, commit, NULL, encoding)))
                return error(_("could not read commit message of %s"),
                             oid_to_hex(&commit->object.oid));
        find_commit_subject(message, &body);
@@ -2029,10 +2060,11 @@ static int update_squash_messages(struct repository *r,
                strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
                            ++opts->current_fixup_count + 1);
                strbuf_addstr(&buf, "\n\n");
-               strbuf_add_commented_lines(&buf, body, strlen(body));
+               strbuf_add_commented_lines(&buf, body, strlen(body),
+                                          comment_line_char);
        } else
                return error(_("unknown command: %d"), command);
-       unuse_commit_buffer(commit, message);
+       repo_unuse_commit_buffer(r, commit, message);
 
        if (!res)
                res = write_message(buf.buf, buf.len, rebase_path_squash_msg(),
@@ -2059,7 +2091,7 @@ static void flush_rewritten_pending(void)
        FILE *out;
 
        if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), (GIT_MAX_HEXSZ + 1) * 2) > 0 &&
-           !get_oid("HEAD", &newoid) &&
+           !repo_get_oid(the_repository, "HEAD", &newoid) &&
            (out = fopen_or_warn(rebase_path_rewritten_list(), "a"))) {
                char *bol = buf.buf, *eol;
 
@@ -2111,7 +2143,8 @@ static void refer_to_commit(struct replay_opts *opts,
                        .abbrev = DEFAULT_ABBREV,
                        .date_mode.type = DATE_SHORT,
                };
-               format_commit_message(commit, "%h (%s, %ad)", msgbuf, &ctx);
+               repo_format_commit_message(the_repository, commit,
+                                          "%h (%s, %ad)", msgbuf, &ctx);
        } else {
                strbuf_addstr(msgbuf, oid_to_hex(&commit->object.oid));
        }
@@ -2144,7 +2177,7 @@ static int do_pick_commit(struct repository *r,
                if (write_index_as_tree(&head, r->index, r->index_file, 0, NULL))
                        return error(_("your index file is unmerged."));
        } else {
-               unborn = get_oid("HEAD", &head);
+               unborn = repo_get_oid(r, "HEAD", &head);
                /* Do we want to generate a root commit? */
                if (is_pick_or_similar(command) && opts->have_squash_onto &&
                    oideq(&head, &opts->squash_onto)) {
@@ -2206,7 +2239,7 @@ static int do_pick_commit(struct repository *r,
                msg_file = NULL;
                goto fast_forward_edit;
        }
-       if (parent && parse_commit(parent) < 0)
+       if (parent && repo_parse_commit(r, parent) < 0)
                /* TRANSLATORS: The first %s will be a "todo" command like
                   "revert" or "pick", the second %s a SHA1. */
                return error(_("%s: cannot parse parent commit %s"),
@@ -2221,6 +2254,8 @@ static int do_pick_commit(struct repository *r,
         */
 
        if (command == TODO_REVERT) {
+               const char *orig_subject;
+
                base = commit;
                base_label = msg.label;
                next = parent;
@@ -2228,6 +2263,15 @@ static int do_pick_commit(struct repository *r,
                if (opts->commit_use_reference) {
                        strbuf_addstr(&msgbuf,
                                "# *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***");
+               } else if (skip_prefix(msg.subject, "Revert \"", &orig_subject) &&
+                          /*
+                           * We don't touch pre-existing repeated reverts, because
+                           * theoretically these can be nested arbitrarily deeply,
+                           * thus requiring excessive complexity to deal with.
+                           */
+                          !starts_with(orig_subject, "Revert \"")) {
+                       strbuf_addstr(&msgbuf, "Reapply \"");
+                       strbuf_addstr(&msgbuf, orig_subject);
                } else {
                        strbuf_addstr(&msgbuf, "Revert \"");
                        strbuf_addstr(&msgbuf, msg.subject);
@@ -2269,8 +2313,10 @@ static int do_pick_commit(struct repository *r,
                reword = 1;
        else if (is_fixup(command)) {
                if (update_squash_messages(r, command, commit,
-                                          opts, item->flags))
-                       return -1;
+                                          opts, item->flags)) {
+                       res = -1;
+                       goto leave;
+               }
                flags |= AMEND_MSG;
                if (!final_fixup)
                        msg_file = rebase_path_squash_msg();
@@ -2280,9 +2326,11 @@ static int do_pick_commit(struct repository *r,
                } else {
                        const char *dest = git_path_squash_msg(r);
                        unlink(dest);
-                       if (copy_file(dest, rebase_path_squash_msg(), 0666))
-                               return error(_("could not rename '%s' to '%s'"),
-                                            rebase_path_squash_msg(), dest);
+                       if (copy_file(dest, rebase_path_squash_msg(), 0666)) {
+                               res = error(_("could not copy '%s' to '%s'"),
+                                           rebase_path_squash_msg(), dest);
+                               goto leave;
+                       }
                        unlink(git_path_merge_msg(r));
                        msg_file = dest;
                        flags |= EDIT_MSG;
@@ -2315,12 +2363,11 @@ static int do_pick_commit(struct repository *r,
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
                res |= try_merge_command(r, opts->strategy,
-                                        opts->xopts_nr, (const char **)opts->xopts,
+                                        opts->xopts.nr, opts->xopts.v,
                                        common, oid_to_hex(&head), remotes);
                free_commit_list(common);
                free_commit_list(remotes);
        }
-       strbuf_release(&msgbuf);
 
        /*
         * If the merge was clean or if it failed due to conflict, we write
@@ -2343,7 +2390,7 @@ static int do_pick_commit(struct repository *r,
                error(command == TODO_REVERT
                      ? _("could not revert %s... %s")
                      : _("could not apply %s... %s"),
-                     short_commit_name(commit), msg.subject);
+                     short_commit_name(r, commit), msg.subject);
                print_advice(r, res == 1, opts);
                repo_rerere(r, opts->allow_rerere_auto);
                goto leave;
@@ -2394,6 +2441,7 @@ fast_forward_edit:
 leave:
        free_message(commit, &msg);
        free(author);
+       strbuf_release(&msgbuf);
        update_abort_safety_file();
 
        return res;
@@ -2453,7 +2501,6 @@ void todo_list_release(struct todo_list *todo_list)
 static struct todo_item *append_new_todo(struct todo_list *todo_list)
 {
        ALLOC_GROW(todo_list->items, todo_list->nr + 1, todo_list->alloc);
-       todo_list->total_nr++;
        return todo_list->items + todo_list->nr++;
 }
 
@@ -2467,12 +2514,39 @@ static int is_command(enum todo_command command, const char **bol)
 {
        const char *str = todo_command_info[command].str;
        const char nick = todo_command_info[command].c;
-       const char *p = *bol + 1;
+       const char *p = *bol;
 
-       return skip_prefix(*bol, str, bol) ||
-               ((nick && **bol == nick) &&
-                (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || !*p) &&
-                (*bol = p));
+       return (skip_prefix(p, str, &p) || (nick && *p++ == nick)) &&
+               (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || !*p) &&
+               (*bol = p);
+}
+
+static int check_label_or_ref_arg(enum todo_command command, const char *arg)
+{
+       switch (command) {
+       case TODO_LABEL:
+               /*
+                * '#' is not a valid label as the merge command uses it to
+                * separate merge parents from the commit subject.
+                */
+               if (!strcmp(arg, "#") ||
+                   check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
+                       return error(_("'%s' is not a valid label"), arg);
+               break;
+
+       case TODO_UPDATE_REF:
+               if (check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
+                       return error(_("'%s' is not a valid refname"), arg);
+               if (check_refname_format(arg, 0))
+                       return error(_("update-ref requires a fully qualified "
+                                      "refname e.g. refs/heads/%s"), arg);
+               break;
+
+       default:
+               BUG("unexpected todo_command");
+       }
+
+       return 0;
 }
 
 static int parse_insn_line(struct repository *r, struct todo_item *item,
@@ -2501,7 +2575,8 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
                        break;
                }
        if (i >= TODO_COMMENT)
-               return -1;
+               return error(_("invalid command '%.*s'"),
+                            (int)strcspn(bol, " \t\r\n"), bol);
 
        /* Eat up extra spaces/ tabs before object name */
        padding = strspn(bol, " \t");
@@ -2523,19 +2598,26 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
 
        if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
            item->command == TODO_RESET || item->command == TODO_UPDATE_REF) {
+               int ret = 0;
+
                item->commit = NULL;
                item->arg_offset = bol - buf;
                item->arg_len = (int)(eol - bol);
-               return 0;
+               if (item->command == TODO_LABEL ||
+                   item->command == TODO_UPDATE_REF) {
+                       saved = *eol;
+                       *eol = '\0';
+                       ret = check_label_or_ref_arg(item->command, bol);
+                       *eol = saved;
+               }
+               return ret;
        }
 
        if (item->command == TODO_FIXUP) {
-               if (skip_prefix(bol, "-C", &bol) &&
-                  (*bol == ' ' || *bol == '\t')) {
+               if (skip_prefix(bol, "-C", &bol)) {
                        bol += strspn(bol, " \t");
                        item->flags |= TODO_REPLACE_FIXUP_MSG;
-               } else if (skip_prefix(bol, "-c", &bol) &&
-                                 (*bol == ' ' || *bol == '\t')) {
+               } else if (skip_prefix(bol, "-c", &bol)) {
                        bol += strspn(bol, " \t");
                        item->flags |= TODO_EDIT_FIXUP_MSG;
                }
@@ -2559,7 +2641,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        end_of_object_name = (char *) bol + strcspn(bol, " \t\n");
        saved = *end_of_object_name;
        *end_of_object_name = '\0';
-       status = get_oid(bol, &commit_oid);
+       status = repo_get_oid(r, bol, &commit_oid);
        if (status < 0)
                error(_("could not parse '%s'"), bol); /* return later */
        *end_of_object_name = saved;
@@ -2575,7 +2657,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        return item->commit ? 0 : -1;
 }
 
-int sequencer_get_last_command(struct repository *r, enum replay_action *action)
+int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action)
 {
        const char *todo_file, *bol;
        struct strbuf buf = STRBUF_INIT;
@@ -2609,7 +2691,7 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,
        char *p = buf, *next_p;
        int i, res = 0, fixup_okay = file_exists(rebase_path_done());
 
-       todo_list->current = todo_list->nr = 0;
+       todo_list->current = todo_list->nr = todo_list->total_nr = 0;
 
        for (i = 1; *p; i++, p = next_p) {
                char *eol = strchrnul(p, '\n');
@@ -2630,10 +2712,13 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,
                        item->commit = NULL;
                }
 
+               if (item->command != TODO_COMMENT)
+                       todo_list->total_nr++;
+
                if (fixup_okay)
                        ; /* do nothing */
                else if (is_fixup(item->command))
-                       return error(_("cannot '%s' without a previous commit"),
+                       res = error(_("cannot '%s' without a previous commit"),
                                command_to_string(item->command));
                else if (!is_noop(item->command))
                        fixup_okay = 1;
@@ -2820,7 +2905,9 @@ static int git_config_string_dup(char **dest,
        return 0;
 }
 
-static int populate_opts_cb(const char *key, const char *value, void *data)
+static int populate_opts_cb(const char *key, const char *value,
+                           const struct config_context *ctx,
+                           void *data)
 {
        struct replay_opts *opts = data;
        int error_flag = 1;
@@ -2828,36 +2915,35 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
        if (!value)
                error_flag = 0;
        else if (!strcmp(key, "options.no-commit"))
-               opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+               opts->no_commit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.edit"))
-               opts->edit = git_config_bool_or_int(key, value, &error_flag);
+               opts->edit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.allow-empty"))
                opts->allow_empty =
-                       git_config_bool_or_int(key, value, &error_flag);
+                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.allow-empty-message"))
                opts->allow_empty_message =
-                       git_config_bool_or_int(key, value, &error_flag);
+                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.keep-redundant-commits"))
                opts->keep_redundant_commits =
-                       git_config_bool_or_int(key, value, &error_flag);
+                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.signoff"))
-               opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+               opts->signoff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.record-origin"))
-               opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+               opts->record_origin = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.allow-ff"))
-               opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+               opts->allow_ff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
        else if (!strcmp(key, "options.mainline"))
-               opts->mainline = git_config_int(key, value);
+               opts->mainline = git_config_int(key, value, ctx->kvi);
        else if (!strcmp(key, "options.strategy"))
                git_config_string_dup(&opts->strategy, key, value);
        else if (!strcmp(key, "options.gpg-sign"))
                git_config_string_dup(&opts->gpg_sign, key, value);
        else if (!strcmp(key, "options.strategy-option")) {
-               ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
-               opts->xopts[opts->xopts_nr++] = xstrdup(value);
+               strvec_push(&opts->xopts, value);
        } else if (!strcmp(key, "options.allow-rerere-auto"))
                opts->allow_rerere_auto =
-                       git_config_bool_or_int(key, value, &error_flag) ?
+                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag) ?
                                RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
        else if (!strcmp(key, "options.default-msg-cleanup")) {
                opts->explicit_cleanup = 1;
@@ -2871,22 +2957,27 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
        return 0;
 }
 
-void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
+static void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
 {
        int i;
+       int count;
+       const char **argv;
        char *strategy_opts_string = raw_opts;
 
        if (*strategy_opts_string == ' ')
                strategy_opts_string++;
 
-       opts->xopts_nr = split_cmdline(strategy_opts_string,
-                                      (const char ***)&opts->xopts);
-       for (i = 0; i < opts->xopts_nr; i++) {
-               const char *arg = opts->xopts[i];
+       count = split_cmdline(strategy_opts_string, &argv);
+       if (count < 0)
+               BUG("could not split '%s': %s", strategy_opts_string,
+                           split_cmdline_strerror(count));
+       for (i = 0; i < count; i++) {
+               const char *arg = argv[i];
 
                skip_prefix(arg, "--", &arg);
-               opts->xopts[i] = xstrdup(arg);
+               strvec_push(&opts->xopts, arg);
        }
+       free(argv);
 }
 
 static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
@@ -2974,7 +3065,7 @@ static int read_populate_opts(struct replay_opts *opts)
                }
 
                if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
-                       if (get_oid_committish(buf.buf, &opts->squash_onto) < 0) {
+                       if (repo_get_oid_committish(the_repository, buf.buf, &opts->squash_onto) < 0) {
                                ret = error(_("unusable squash-onto"));
                                goto done_rebase_i;
                        }
@@ -3002,12 +3093,13 @@ done_rebase_i:
 
 static void write_strategy_opts(struct replay_opts *opts)
 {
-       int i;
        struct strbuf buf = STRBUF_INIT;
 
-       for (i = 0; i < opts->xopts_nr; ++i)
-               strbuf_addf(&buf, " --%s", opts->xopts[i]);
-
+       /*
+        * Quote strategy options so that they can be read correctly
+        * by split_cmdline().
+        */
+       quote_cmdline(&buf, opts->xopts.v);
        write_file(rebase_path_strategy_opts(), "%s\n", buf.buf);
        strbuf_release(&buf);
 }
@@ -3030,7 +3122,7 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
                write_file(rebase_path_verbose(), "%s", "");
        if (opts->strategy)
                write_file(rebase_path_strategy(), "%s\n", opts->strategy);
-       if (opts->xopts_nr > 0)
+       if (opts->xopts.nr > 0)
                write_strategy_opts(opts);
 
        if (opts->allow_rerere_auto == RERERE_AUTOUPDATE)
@@ -3074,7 +3166,9 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
 
        while ((commit = get_revision(opts->revs))) {
                struct todo_item *item = append_new_todo(todo_list);
-               const char *commit_buffer = logmsg_reencode(commit, NULL, encoding);
+               const char *commit_buffer = repo_logmsg_reencode(the_repository,
+                                                                commit, NULL,
+                                                                encoding);
                const char *subject;
                int subject_len;
 
@@ -3085,8 +3179,10 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
                item->offset_in_buf = todo_list->buf.len;
                subject_len = find_commit_subject(commit_buffer, &subject);
                strbuf_addf(&todo_list->buf, "%s %s %.*s\n", command_string,
-                       short_commit_name(commit), subject_len, subject);
-               unuse_commit_buffer(commit, commit_buffer);
+                       short_commit_name(the_repository, commit),
+                       subject_len, subject);
+               repo_unuse_commit_buffer(the_repository, commit,
+                                        commit_buffer);
        }
 
        if (!todo_list->nr)
@@ -3136,25 +3232,7 @@ static int create_seq_dir(struct repository *r)
 
 static int save_head(const char *head)
 {
-       struct lock_file head_lock = LOCK_INIT;
-       struct strbuf buf = STRBUF_INIT;
-       int fd;
-       ssize_t written;
-
-       fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
-       if (fd < 0)
-               return error_errno(_("could not lock HEAD"));
-       strbuf_addf(&buf, "%s\n", head);
-       written = write_in_full(fd, buf.buf, buf.len);
-       strbuf_release(&buf);
-       if (written < 0) {
-               error_errno(_("could not write to '%s'"), git_path_head_file());
-               rollback_lock_file(&head_lock);
-               return -1;
-       }
-       if (commit_lock_file(&head_lock) < 0)
-               return error(_("failed to finalize '%s'"), git_path_head_file());
-       return 0;
+       return write_message(head, strlen(head), git_path_head_file(), 1);
 }
 
 static int rollback_is_safe(void)
@@ -3175,7 +3253,7 @@ static int rollback_is_safe(void)
        else
                die_errno(_("could not read '%s'"), git_path_abort_safety_file());
 
-       if (get_oid("HEAD", &actual_head))
+       if (repo_get_oid(the_repository, "HEAD", &actual_head))
                oidclr(&actual_head);
 
        return oideq(&actual_head, &expected_head);
@@ -3183,18 +3261,15 @@ static int rollback_is_safe(void)
 
 static int reset_merge(const struct object_id *oid)
 {
-       int ret;
-       struct strvec argv = STRVEC_INIT;
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
-       strvec_pushl(&argv, "reset", "--merge", NULL);
+       cmd.git_cmd = 1;
+       strvec_pushl(&cmd.args, "reset", "--merge", NULL);
 
        if (!is_null_oid(oid))
-               strvec_push(&argv, oid_to_hex(oid));
-
-       ret = run_command_v_opt(argv.v, RUN_GIT_CMD);
-       strvec_clear(&argv);
+               strvec_push(&cmd.args, oid_to_hex(oid));
 
-       return ret;
+       return run_command(&cmd);
 }
 
 static int rollback_single_pick(struct repository *r)
@@ -3334,7 +3409,8 @@ give_advice:
        return -1;
 }
 
-static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
+static int save_todo(struct todo_list *todo_list, struct replay_opts *opts,
+                    int reschedule)
 {
        struct lock_file todo_lock = LOCK_INIT;
        const char *todo_path = get_todo_path(opts);
@@ -3344,7 +3420,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
         * rebase -i writes "git-rebase-todo" without the currently executing
         * command, appending it to "done" instead.
         */
-       if (is_rebase_i(opts))
+       if (is_rebase_i(opts) && !reschedule)
                next++;
 
        fd = hold_lock_file_for_update(&todo_lock, todo_path, 0);
@@ -3357,7 +3433,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
        if (commit_lock_file(&todo_lock) < 0)
                return error(_("failed to finalize '%s'"), todo_path);
 
-       if (is_rebase_i(opts) && next > 0) {
+       if (is_rebase_i(opts) && !reschedule && next > 0) {
                const char *done = rebase_path_done();
                int fd = open(done, O_CREAT | O_WRONLY | O_APPEND, 0666);
                int ret = 0;
@@ -3417,13 +3493,10 @@ static int save_opts(struct replay_opts *opts)
        if (opts->gpg_sign)
                res |= git_config_set_in_file_gently(opts_file,
                                        "options.gpg-sign", opts->gpg_sign);
-       if (opts->xopts) {
-               int i;
-               for (i = 0; i < opts->xopts_nr; i++)
-                       res |= git_config_set_multivar_in_file_gently(opts_file,
-                                       "options.strategy-option",
-                                       opts->xopts[i], "^$", 0);
-       }
+       for (size_t i = 0; i < opts->xopts.nr; i++)
+               res |= git_config_set_multivar_in_file_gently(opts_file,
+                               "options.strategy-option",
+                               opts->xopts.v[i], "^$", 0);
        if (opts->allow_rerere_auto)
                res |= git_config_set_in_file_gently(opts_file,
                                "options.allow-rerere-auto",
@@ -3441,18 +3514,19 @@ static int make_patch(struct repository *r,
                      struct commit *commit,
                      struct replay_opts *opts)
 {
-       struct strbuf buf = STRBUF_INIT;
        struct rev_info log_tree_opt;
        const char *subject;
        char hex[GIT_MAX_HEXSZ + 1];
        int res = 0;
 
+       if (!is_rebase_i(opts))
+               BUG("make_patch should only be called when rebasing");
+
        oid_to_hex_r(hex, &commit->object.oid);
        if (write_message(hex, strlen(hex), rebase_path_stopped_sha(), 1) < 0)
                return -1;
        res |= write_rebase_head(&commit->object.oid);
 
-       strbuf_addf(&buf, "%s/patch", get_dir(opts));
        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
        repo_init_revisions(r, &log_tree_opt, NULL);
        log_tree_opt.abbrev = 0;
@@ -3460,25 +3534,26 @@ static int make_patch(struct repository *r,
        log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
        log_tree_opt.disable_stdin = 1;
        log_tree_opt.no_commit_id = 1;
-       log_tree_opt.diffopt.file = fopen(buf.buf, "w");
+       log_tree_opt.diffopt.file = fopen(rebase_path_patch(), "w");
        log_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;
        if (!log_tree_opt.diffopt.file)
-               res |= error_errno(_("could not open '%s'"), buf.buf);
+               res |= error_errno(_("could not open '%s'"),
+                                  rebase_path_patch());
        else {
                res |= log_tree_commit(&log_tree_opt, commit);
                fclose(log_tree_opt.diffopt.file);
        }
-       strbuf_reset(&buf);
 
-       strbuf_addf(&buf, "%s/message", get_dir(opts));
-       if (!file_exists(buf.buf)) {
+       if (!file_exists(rebase_path_message())) {
                const char *encoding = get_commit_output_encoding();
-               const char *commit_buffer = logmsg_reencode(commit, NULL, encoding);
+               const char *commit_buffer = repo_logmsg_reencode(r,
+                                                                commit, NULL,
+                                                                encoding);
                find_commit_subject(commit_buffer, &subject);
-               res |= write_message(subject, strlen(subject), buf.buf, 1);
-               unuse_commit_buffer(commit, commit_buffer);
+               res |= write_message(subject, strlen(subject), rebase_path_message(), 1);
+               repo_unuse_commit_buffer(r, commit,
+                                        commit_buffer);
        }
-       strbuf_release(&buf);
        release_revisions(&log_tree_opt);
 
        return res;
@@ -3489,7 +3564,7 @@ static int intend_to_amend(void)
        struct object_id head;
        char *p;
 
-       if (get_oid("HEAD", &head))
+       if (repo_get_oid(the_repository, "HEAD", &head))
                return error(_("cannot read HEAD"));
 
        p = oid_to_hex(&head);
@@ -3526,7 +3601,7 @@ static int error_with_patch(struct repository *r,
        } else if (exit_code) {
                if (commit)
                        fprintf_ln(stderr, _("Could not apply %s... %.*s"),
-                                  short_commit_name(commit), subject_len, subject);
+                                  short_commit_name(r, commit), subject_len, subject);
                else
                        /*
                         * We don't have the hash of the parent so
@@ -3558,15 +3633,17 @@ static int error_failed_squash(struct repository *r,
 
 static int do_exec(struct repository *r, const char *command_line)
 {
-       const char *child_argv[] = { NULL, NULL };
+       struct child_process cmd = CHILD_PROCESS_INIT;
        int dirty, status;
 
        fprintf(stderr, _("Executing: %s\n"), command_line);
-       child_argv[0] = command_line;
-       status = run_command_v_opt(child_argv, RUN_USING_SHELL);
+       cmd.use_shell = 1;
+       strvec_push(&cmd.args, command_line);
+       status = run_command(&cmd);
 
        /* force re-reading of the cache */
-       if (discard_index(r->index) < 0 || repo_read_index(r) < 0)
+       discard_index(r->index);
+       if (repo_read_index(r) < 0)
                return error(_("could not read index"));
 
        dirty = require_clean_work_tree(r, "rebase", NULL, 1, 1);
@@ -3578,14 +3655,14 @@ static int do_exec(struct repository *r, const char *command_line)
                          "  git rebase --continue\n"
                          "\n"),
                        command_line,
-                       dirty ? N_("and made changes to the index and/or the "
-                               "working tree\n") : "");
+                       dirty ? _("and made changes to the index and/or the "
+                               "working tree.\n") : "");
                if (status == 127)
                        /* command not found */
                        status = 1;
        } else if (dirty) {
                warning(_("execution succeeded: %s\nbut "
-                         "left changes to the index and/or the working tree\n"
+                         "left changes to the index and/or the working tree.\n"
                          "Commit or stash your changes, and then run\n"
                          "\n"
                          "  git rebase --continue\n"
@@ -3626,7 +3703,6 @@ static int safe_append(const char *filename, const char *fmt, ...)
        }
        if (commit_lock_file(&lock) < 0) {
                strbuf_release(&buf);
-               rollback_lock_file(&lock);
                return error(_("failed to finalize '%s'"), filename);
        }
 
@@ -3653,7 +3729,7 @@ static int do_label(struct repository *r, const char *name, int len)
        if (!transaction) {
                error("%s", err.buf);
                ret = -1;
-       } else if (get_oid("HEAD", &head_oid)) {
+       } else if (repo_get_oid(r, "HEAD", &head_oid)) {
                error(_("could not read HEAD"));
                ret = -1;
        } else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
@@ -3674,17 +3750,28 @@ static int do_label(struct repository *r, const char *name, int len)
        return ret;
 }
 
+static const char *sequencer_reflog_action(struct replay_opts *opts)
+{
+       if (!opts->reflog_action) {
+               opts->reflog_action = getenv(GIT_REFLOG_ACTION);
+               opts->reflog_action =
+                       xstrdup(opts->reflog_action ? opts->reflog_action
+                                                   : action_name(opts));
+       }
+
+       return opts->reflog_action;
+}
+
 __attribute__((format (printf, 3, 4)))
 static const char *reflog_message(struct replay_opts *opts,
        const char *sub_action, const char *fmt, ...)
 {
        va_list ap;
        static struct strbuf buf = STRBUF_INIT;
-       char *reflog_action = getenv(GIT_REFLOG_ACTION);
 
        va_start(ap, fmt);
        strbuf_reset(&buf);
-       strbuf_addstr(&buf, reflog_action ? reflog_action : action_name(opts));
+       strbuf_addstr(&buf, sequencer_reflog_action(opts));
        if (sub_action)
                strbuf_addf(&buf, " (%s)", sub_action);
        if (fmt) {
@@ -3696,6 +3783,28 @@ static const char *reflog_message(struct replay_opts *opts,
        return buf.buf;
 }
 
+static struct commit *lookup_label(struct repository *r, const char *label,
+                                  int len, struct strbuf *buf)
+{
+       struct commit *commit;
+       struct object_id oid;
+
+       strbuf_reset(buf);
+       strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
+       if (!read_ref(buf->buf, &oid)) {
+               commit = lookup_commit_object(r, &oid);
+       } else {
+               /* fall back to non-rewritten ref or commit */
+               strbuf_splice(buf, 0, strlen("refs/rewritten/"), "", 0);
+               commit = lookup_commit_reference_by_name(buf->buf);
+       }
+
+       if (!commit)
+               error(_("could not resolve '%s'"), buf->buf);
+
+       return commit;
+}
+
 static int do_reset(struct repository *r,
                    const char *name, int len,
                    struct replay_opts *opts)
@@ -3727,6 +3836,7 @@ static int do_reset(struct repository *r,
                oidcpy(&oid, &opts->squash_onto);
        } else {
                int i;
+               struct commit *commit;
 
                /* Determine the length of the label */
                for (i = 0; i < len; i++)
@@ -3734,12 +3844,12 @@ static int do_reset(struct repository *r,
                                break;
                len = i;
 
-               strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
-               if (get_oid(ref_name.buf, &oid) &&
-                   get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) {
-                       ret = error(_("could not read '%s'"), ref_name.buf);
+               commit = lookup_label(r, name, len, &ref_name);
+               if (!commit) {
+                       ret = -1;
                        goto cleanup;
                }
+               oid = commit->object.oid;
        }
 
        setup_unpack_trees_porcelain(&unpack_tree_opts, "reset");
@@ -3750,6 +3860,7 @@ static int do_reset(struct repository *r,
        unpack_tree_opts.merge = 1;
        unpack_tree_opts.update = 1;
        unpack_tree_opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
+       unpack_tree_opts.skip_cache_tree_update = 1;
        init_checkout_metadata(&unpack_tree_opts.meta, name, &oid, NULL);
 
        if (repo_read_index_unmerged(r)) {
@@ -3786,26 +3897,6 @@ cleanup:
        return ret;
 }
 
-static struct commit *lookup_label(const char *label, int len,
-                                  struct strbuf *buf)
-{
-       struct commit *commit;
-
-       strbuf_reset(buf);
-       strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
-       commit = lookup_commit_reference_by_name(buf->buf);
-       if (!commit) {
-               /* fall back to non-rewritten ref or commit */
-               strbuf_splice(buf, 0, strlen("refs/rewritten/"), "", 0);
-               commit = lookup_commit_reference_by_name(buf->buf);
-       }
-
-       if (!commit)
-               error(_("could not resolve '%s'"), buf->buf);
-
-       return commit;
-}
-
 static int do_merge(struct repository *r,
                    struct commit *commit,
                    const char *arg, int arg_len,
@@ -3816,7 +3907,7 @@ static int do_merge(struct repository *r,
        struct commit *head_commit, *merge_commit, *i;
        struct commit_list *bases, *j;
        struct commit_list *to_merge = NULL, **tail = &to_merge;
-       const char *strategy = !opts->xopts_nr &&
+       const char *strategy = !opts->xopts.nr &&
                (!opts->strategy ||
                 !strcmp(opts->strategy, "recursive") ||
                 !strcmp(opts->strategy, "ort")) ?
@@ -3853,7 +3944,7 @@ static int do_merge(struct repository *r,
                k = strcspn(p, " \t\n");
                if (!k)
                        continue;
-               merge_commit = lookup_label(p, k, &ref_name);
+               merge_commit = lookup_label(r, p, k, &ref_name);
                if (!merge_commit) {
                        ret = error(_("unable to parse '%.*s'"), k, p);
                        goto leave_merge;
@@ -3926,7 +4017,8 @@ static int do_merge(struct repository *r,
 
        if (commit) {
                const char *encoding = get_commit_output_encoding();
-               const char *message = logmsg_reencode(commit, NULL, encoding);
+               const char *message = repo_logmsg_reencode(r, commit, NULL,
+                                                          encoding);
                const char *body;
                int len;
 
@@ -3939,7 +4031,7 @@ static int do_merge(struct repository *r,
                find_commit_subject(message, &body);
                len = strlen(body);
                ret = write_message(body, len, git_path_merge_msg(r), 0);
-               unuse_commit_buffer(commit, message);
+               repo_unuse_commit_buffer(r, commit, message);
                if (ret) {
                        error_errno(_("could not write '%s'"),
                                    git_path_merge_msg(r));
@@ -3999,9 +4091,9 @@ static int do_merge(struct repository *r,
                        strvec_push(&cmd.args, "octopus");
                else {
                        strvec_push(&cmd.args, strategy);
-                       for (k = 0; k < opts->xopts_nr; k++)
+                       for (k = 0; k < opts->xopts.nr; k++)
                                strvec_pushf(&cmd.args,
-                                            "-X%s", opts->xopts[k]);
+                                            "-X%s", opts->xopts.v[k]);
                }
                if (!(flags & TODO_EDIT_MERGE_MSG))
                        strvec_push(&cmd.args, "--no-edit");
@@ -4030,14 +4122,16 @@ static int do_merge(struct repository *r,
                ret = run_command(&cmd);
 
                /* force re-reading of the cache */
-               if (!ret && (discard_index(r->index) < 0 ||
-                            repo_read_index(r) < 0))
-                       ret = error(_("could not read index"));
+               if (!ret) {
+                       discard_index(r->index);
+                       if (repo_read_index(r) < 0)
+                               ret = error(_("could not read index"));
+               }
                goto leave_merge;
        }
 
        merge_commit = to_merge->item;
-       bases = get_merge_bases(head_commit, merge_commit);
+       bases = repo_get_merge_bases(r, head_commit, merge_commit);
        if (bases && oideq(&merge_commit->object.oid,
                           &bases->item->object.oid)) {
                ret = 0;
@@ -4077,6 +4171,7 @@ static int do_merge(struct repository *r,
        if (ret < 0) {
                error(_("could not even attempt to merge '%.*s'"),
                      merge_arg_len, arg);
+               unlink(git_path_merge_msg(r));
                goto leave_merge;
        }
        /*
@@ -4205,7 +4300,7 @@ void todo_list_filter_update_refs(struct repository *r,
                if (!is_null_oid(&rec->after))
                        continue;
 
-               for (j = 0; !found && j < todo_list->total_nr; j++) {
+               for (j = 0; !found && j < todo_list->nr; j++) {
                        struct todo_item *item = &todo_list->items[j];
                        const char *arg = todo_list->buf.buf + item->arg_offset;
 
@@ -4235,7 +4330,7 @@ void todo_list_filter_update_refs(struct repository *r,
         * For each todo_item, check if its ref is in the update_refs list.
         * If not, then add it as an un-updated ref.
         */
-       for (i = 0; i < todo_list->total_nr; i++) {
+       for (i = 0; i < todo_list->nr; i++) {
                struct todo_item *item = &todo_list->items[i];
                const char *arg = todo_list->buf.buf + item->arg_offset;
                int j, found = 0;
@@ -4392,7 +4487,7 @@ void create_autostash(struct repository *r, const char *path)
                if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
                        die(_("Cannot autostash"));
                strbuf_trim_trailing_newline(&buf);
-               if (get_oid(buf.buf, &oid))
+               if (repo_get_oid(r, buf.buf, &oid))
                        die(_("Unexpected stash response: '%s'"),
                            buf.buf);
                strbuf_reset(&buf);
@@ -4405,8 +4500,8 @@ void create_autostash(struct repository *r, const char *path)
                printf(_("Created autostash: %s\n"), buf.buf);
                if (reset_head(r, &ropts) < 0)
                        die(_("could not reset --hard"));
-               if (discard_index(r->index) < 0 ||
-                       repo_read_index(r) < 0)
+               discard_index(r->index);
+               if (repo_read_index(r) < 0)
                        die(_("could not read index"));
        }
        strbuf_release(&buf);
@@ -4500,7 +4595,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,
                                RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
                .head_msg = reflog_message(opts, "start", "checkout %s",
                                           onto_name),
-               .default_reflog_action = "rebase"
+               .default_reflog_action = sequencer_reflog_action(opts)
        };
        if (reset_head(r, &ropts)) {
                apply_autostash(rebase_path_autostash());
@@ -4517,9 +4612,9 @@ static int stopped_at_head(struct repository *r)
        struct commit *commit;
        struct commit_message message;
 
-       if (get_oid("HEAD", &head) ||
+       if (repo_get_oid(r, "HEAD", &head) ||
            !(commit = lookup_commit(r, &head)) ||
-           parse_commit(commit) || get_message(commit, &message))
+           repo_parse_commit(r, commit) || get_message(commit, &message))
                fprintf(stderr, _("Stopped at HEAD\n"));
        else {
                fprintf(stderr, _("Stopped at %s\n"), message.label);
@@ -4564,16 +4659,75 @@ N_("Could not execute the todo command\n"
 "    git rebase --edit-todo\n"
 "    git rebase --continue\n");
 
+static int pick_one_commit(struct repository *r,
+                          struct todo_list *todo_list,
+                          struct replay_opts *opts,
+                          int *check_todo, int* reschedule)
+{
+       int res;
+       struct todo_item *item = todo_list->items + todo_list->current;
+       const char *arg = todo_item_get_arg(todo_list, item);
+       if (is_rebase_i(opts))
+               opts->reflog_message = reflog_message(
+                       opts, command_to_string(item->command), NULL);
+
+       res = do_pick_commit(r, item, opts, is_final_fixup(todo_list),
+                            check_todo);
+       if (is_rebase_i(opts) && res < 0) {
+               /* Reschedule */
+               *reschedule = 1;
+               return -1;
+       }
+       if (item->command == TODO_EDIT) {
+               struct commit *commit = item->commit;
+               if (!res) {
+                       if (!opts->verbose)
+                               term_clear_line();
+                       fprintf(stderr, _("Stopped at %s...  %.*s\n"),
+                               short_commit_name(r, commit), item->arg_len, arg);
+               }
+               return error_with_patch(r, commit,
+                                       arg, item->arg_len, opts, res, !res);
+       }
+       if (is_rebase_i(opts) && !res)
+               record_in_rewritten(&item->commit->object.oid,
+                                   peek_command(todo_list, 1));
+       if (res && is_fixup(item->command)) {
+               if (res == 1)
+                       intend_to_amend();
+               return error_failed_squash(r, item->commit, opts,
+                                          item->arg_len, arg);
+       } else if (res && is_rebase_i(opts) && item->commit) {
+               int to_amend = 0;
+               struct object_id oid;
+
+               /*
+                * If we are rewording and have either
+                * fast-forwarded already, or are about to
+                * create a new root commit, we want to amend,
+                * otherwise we do not.
+                */
+               if (item->command == TODO_REWORD &&
+                   !repo_get_oid(r, "HEAD", &oid) &&
+                   (oideq(&item->commit->object.oid, &oid) ||
+                    (opts->have_squash_onto &&
+                     oideq(&opts->squash_onto, &oid))))
+                       to_amend = 1;
+
+               return res | error_with_patch(r, item->commit,
+                                             arg, item->arg_len, opts,
+                                             res, to_amend);
+       }
+       return res;
+}
+
 static int pick_commits(struct repository *r,
                        struct todo_list *todo_list,
                        struct replay_opts *opts)
 {
        int res = 0, reschedule = 0;
-       char *prev_reflog_action;
 
-       /* Note that 0 for 3rd parameter of setenv means set only if not set */
-       setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
-       prev_reflog_action = xstrdup(getenv(GIT_REFLOG_ACTION));
+       opts->reflog_message = sequencer_reflog_action(opts);
        if (opts->allow_ff)
                assert(!(opts->signoff || opts->no_commit ||
                         opts->record_origin || should_edit(opts) ||
@@ -4582,12 +4736,17 @@ static int pick_commits(struct repository *r,
        if (read_and_refresh_cache(r, opts))
                return -1;
 
+       unlink(rebase_path_message());
+       unlink(rebase_path_stopped_sha());
+       unlink(rebase_path_amend());
+       unlink(rebase_path_patch());
+
        while (todo_list->current < todo_list->nr) {
                struct todo_item *item = todo_list->items + todo_list->current;
                const char *arg = todo_item_get_arg(todo_list, item);
                int check_todo = 0;
 
-               if (save_todo(todo_list, opts))
+               if (save_todo(todo_list, opts, reschedule))
                        return -1;
                if (is_rebase_i(opts)) {
                        if (item->command != TODO_COMMENT) {
@@ -4605,10 +4764,7 @@ static int pick_commits(struct repository *r,
                                                todo_list->total_nr,
                                                opts->verbose ? "\n" : "\r");
                        }
-                       unlink(rebase_path_message());
                        unlink(rebase_path_author_script());
-                       unlink(rebase_path_stopped_sha());
-                       unlink(rebase_path_amend());
                        unlink(git_path_merge_head(r));
                        unlink(git_path_auto_merge(r));
                        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
@@ -4620,68 +4776,10 @@ static int pick_commits(struct repository *r,
                        }
                }
                if (item->command <= TODO_SQUASH) {
-                       if (is_rebase_i(opts))
-                               setenv(GIT_REFLOG_ACTION, reflog_message(opts,
-                                       command_to_string(item->command), NULL),
-                                       1);
-                       res = do_pick_commit(r, item, opts,
-                                            is_final_fixup(todo_list),
-                                            &check_todo);
-                       if (is_rebase_i(opts))
-                               setenv(GIT_REFLOG_ACTION, prev_reflog_action, 1);
-                       if (is_rebase_i(opts) && res < 0) {
-                               /* Reschedule */
-                               advise(_(rescheduled_advice),
-                                      get_item_line_length(todo_list,
-                                                           todo_list->current),
-                                      get_item_line(todo_list,
-                                                    todo_list->current));
-                               todo_list->current--;
-                               if (save_todo(todo_list, opts))
-                                       return -1;
-                       }
-                       if (item->command == TODO_EDIT) {
-                               struct commit *commit = item->commit;
-                               if (!res) {
-                                       if (!opts->verbose)
-                                               term_clear_line();
-                                       fprintf(stderr,
-                                               _("Stopped at %s...  %.*s\n"),
-                                               short_commit_name(commit),
-                                               item->arg_len, arg);
-                               }
-                               return error_with_patch(r, commit,
-                                       arg, item->arg_len, opts, res, !res);
-                       }
-                       if (is_rebase_i(opts) && !res)
-                               record_in_rewritten(&item->commit->object.oid,
-                                       peek_command(todo_list, 1));
-                       if (res && is_fixup(item->command)) {
-                               if (res == 1)
-                                       intend_to_amend();
-                               return error_failed_squash(r, item->commit, opts,
-                                       item->arg_len, arg);
-                       } else if (res && is_rebase_i(opts) && item->commit) {
-                               int to_amend = 0;
-                               struct object_id oid;
-
-                               /*
-                                * If we are rewording and have either
-                                * fast-forwarded already, or are about to
-                                * create a new root commit, we want to amend,
-                                * otherwise we do not.
-                                */
-                               if (item->command == TODO_REWORD &&
-                                   !get_oid("HEAD", &oid) &&
-                                   (oideq(&item->commit->object.oid, &oid) ||
-                                    (opts->have_squash_onto &&
-                                     oideq(&opts->squash_onto, &oid))))
-                                       to_amend = 1;
-
-                               return res | error_with_patch(r, item->commit,
-                                               arg, item->arg_len, opts,
-                                               res, to_amend);
-                       }
+                       res = pick_one_commit(r, todo_list, opts, &check_todo,
+                                             &reschedule);
+                       if (!res && item->command == TODO_EDIT)
+                               return 0;
                } else if (item->command == TODO_EXEC) {
                        char *end_of_arg = (char *)(arg + item->arg_len);
                        int saved = *end_of_arg;
@@ -4729,39 +4827,32 @@ static int pick_commits(struct repository *r,
                               get_item_line_length(todo_list,
                                                    todo_list->current),
                               get_item_line(todo_list, todo_list->current));
-                       todo_list->current--;
-                       if (save_todo(todo_list, opts))
+                       if (save_todo(todo_list, opts, reschedule))
                                return -1;
                        if (item->commit)
-                               return error_with_patch(r,
-                                                       item->commit,
-                                                       arg, item->arg_len,
-                                                       opts, res, 0);
+                               write_rebase_head(&item->commit->object.oid);
                } else if (is_rebase_i(opts) && check_todo && !res &&
                           reread_todo_if_changed(r, todo_list, opts)) {
                        return -1;
                }
 
-               todo_list->current++;
                if (res)
                        return res;
+
+               todo_list->current++;
        }
 
        if (is_rebase_i(opts)) {
                struct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;
                struct stat st;
 
-               /* Stopped in the middle, as planned? */
-               if (todo_list->current < todo_list->nr)
-                       return 0;
-
                if (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&
                                starts_with(head_ref.buf, "refs/")) {
                        const char *msg;
                        struct object_id head, orig;
                        int res;
 
-                       if (get_oid("HEAD", &head)) {
+                       if (repo_get_oid(r, "HEAD", &head)) {
                                res = error(_("cannot read HEAD"));
 cleanup_head_ref:
                                strbuf_release(&head_ref);
@@ -4808,8 +4899,8 @@ cleanup_head_ref:
                        log_tree_opt.disable_stdin = 1;
 
                        if (read_oneliner(&buf, rebase_path_orig_head(), 0) &&
-                           !get_oid(buf.buf, &orig) &&
-                           !get_oid("HEAD", &head)) {
+                           !repo_get_oid(r, buf.buf, &orig) &&
+                           !repo_get_oid(r, "HEAD", &head)) {
                                diff_tree_oid(&orig, &head, "",
                                              &log_tree_opt.diffopt);
                                log_tree_diff_flush(&log_tree_opt);
@@ -4820,8 +4911,7 @@ cleanup_head_ref:
                if (!stat(rebase_path_rewritten_list(), &st) &&
                                st.st_size > 0) {
                        struct child_process child = CHILD_PROCESS_INIT;
-                       const char *post_rewrite_hook =
-                               find_hook("post-rewrite");
+                       struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT;
 
                        child.in = open(rebase_path_rewritten_list(), O_RDONLY);
                        child.git_cmd = 1;
@@ -4831,18 +4921,9 @@ cleanup_head_ref:
                        /* we don't care if this copying failed */
                        run_command(&child);
 
-                       if (post_rewrite_hook) {
-                               struct child_process hook = CHILD_PROCESS_INIT;
-
-                               hook.in = open(rebase_path_rewritten_list(),
-                                       O_RDONLY);
-                               hook.stdout_to_stderr = 1;
-                               hook.trace2_hook_name = "post-rewrite";
-                               strvec_push(&hook.args, post_rewrite_hook);
-                               strvec_push(&hook.args, "rebase");
-                               /* we don't care if this hook failed */
-                               run_command(&hook);
-                       }
+                       hook_opt.path_to_stdin = rebase_path_rewritten_list();
+                       strvec_push(&hook_opt.args, "rebase");
+                       run_hooks_opt("post-rewrite", &hook_opt);
                }
                apply_autostash(rebase_path_autostash());
 
@@ -4870,14 +4951,14 @@ cleanup_head_ref:
 
 static int continue_single_pick(struct repository *r, struct replay_opts *opts)
 {
-       struct strvec argv = STRVEC_INIT;
-       int ret;
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
        if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
            !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
                return error(_("no cherry-pick or revert in progress"));
 
-       strvec_push(&argv, "commit");
+       cmd.git_cmd = 1;
+       strvec_push(&cmd.args, "commit");
 
        /*
         * continue_single_pick() handles the case of recovering from a
@@ -4890,11 +4971,9 @@ static int continue_single_pick(struct repository *r, struct replay_opts *opts)
                 * Include --cleanup=strip as well because we don't want the
                 * "# Conflicts:" messages.
                 */
-               strvec_pushl(&argv, "--no-edit", "--cleanup=strip", NULL);
+               strvec_pushl(&cmd.args, "--no-edit", "--cleanup=strip", NULL);
 
-       ret = run_command_v_opt(argv.v, RUN_GIT_CMD);
-       strvec_clear(&argv);
-       return ret;
+       return run_command(&cmd);
 }
 
 static int commit_staged_changes(struct repository *r,
@@ -4909,11 +4988,16 @@ static int commit_staged_changes(struct repository *r,
 
        is_clean = !has_uncommitted_changes(r, 0);
 
+       if (!is_clean && !file_exists(rebase_path_message())) {
+               const char *gpg_opt = gpg_sign_opt_quoted(opts);
+
+               return error(_(staged_changes_advice), gpg_opt, gpg_opt);
+       }
        if (file_exists(rebase_path_amend())) {
                struct strbuf rev = STRBUF_INIT;
                struct object_id head, to_amend;
 
-               if (get_oid("HEAD", &head))
+               if (repo_get_oid(r, "HEAD", &head))
                        return error(_("cannot amend non-existing commit"));
                if (!read_oneliner(&rev, rebase_path_amend(), 0))
                        return error(_("invalid file: '%s'"), rebase_path_amend());
@@ -4988,18 +5072,31 @@ static int commit_staged_changes(struct repository *r,
                                 * We need to update the squash message to skip
                                 * the latest commit message.
                                 */
+                               int res = 0;
                                struct commit *commit;
+                               const char *msg;
                                const char *path = rebase_path_squash_msg();
                                const char *encoding = get_commit_output_encoding();
 
-                               if (parse_head(r, &commit) ||
-                                   !(p = logmsg_reencode(commit, NULL, encoding)) ||
-                                   write_message(p, strlen(p), path, 0)) {
-                                       unuse_commit_buffer(commit, p);
-                                       return error(_("could not write file: "
+                               if (parse_head(r, &commit))
+                                       return error(_("could not parse HEAD"));
+
+                               p = repo_logmsg_reencode(r, commit, NULL, encoding);
+                               if (!p)  {
+                                       res = error(_("could not parse commit %s"),
+                                                   oid_to_hex(&commit->object.oid));
+                                       goto unuse_commit_buffer;
+                               }
+                               find_commit_subject(p, &msg);
+                               if (write_message(msg, strlen(msg), path, 0)) {
+                                       res = error(_("could not write file: "
                                                       "'%s'"), path);
+                                       goto unuse_commit_buffer;
                                }
-                               unuse_commit_buffer(commit, p);
+                       unuse_commit_buffer:
+                               repo_unuse_commit_buffer(r, commit, p);
+                               if (res)
+                                       return res;
                        }
                }
 
@@ -5063,6 +5160,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
                        unlink(rebase_path_dropped());
                }
 
+               opts->reflog_message = reflog_message(opts, "continue", NULL);
                if (commit_staged_changes(r, opts, &todo_list)) {
                        res = -1;
                        goto release_todo_list;
@@ -5114,7 +5212,7 @@ static int single_pick(struct repository *r,
                        TODO_PICK : TODO_REVERT;
        item.commit = cmit;
 
-       setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+       opts->reflog_message = sequencer_reflog_action(opts);
        return do_pick_commit(r, &item, opts, 0, &check_todo);
 }
 
@@ -5137,7 +5235,7 @@ int sequencer_pick_revisions(struct repository *r,
                if (!strlen(name))
                        continue;
 
-               if (!get_oid(name, &oid)) {
+               if (!repo_get_oid(r, name, &oid)) {
                        if (!lookup_commit_reference_gently(r, &oid, 1)) {
                                enum object_type type = oid_object_info(r,
                                                                        &oid,
@@ -5180,7 +5278,7 @@ int sequencer_pick_revisions(struct repository *r,
        if (walk_revs_populate_todo(&todo_list, opts) ||
                        create_seq_dir(r) < 0)
                return -1;
-       if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT))
+       if (repo_get_oid(r, "HEAD", &oid) && (opts->action == REPLAY_REVERT))
                return error(_("can't revert as initial commit"));
        if (save_head(oid_to_hex(&oid)))
                return -1;
@@ -5278,6 +5376,7 @@ struct label_state {
        struct oidmap commit2label;
        struct hashmap labels;
        struct strbuf buf;
+       int max_label_length;
 };
 
 static const char *label_oid(struct object_id *oid, const char *label,
@@ -5296,7 +5395,7 @@ static const char *label_oid(struct object_id *oid, const char *label,
         * For "uninteresting" commits, i.e. commits that are not to be
         * rebased, and which can therefore not be labeled, we use a unique
         * abbreviation of the commit name. This is slightly more complicated
-        * than calling find_unique_abbrev() because we also need to make
+        * than calling repo_find_unique_abbrev() because we also need to make
         * sure that the abbreviation does not conflict with any other
         * label.
         *
@@ -5312,7 +5411,8 @@ static const char *label_oid(struct object_id *oid, const char *label,
                strbuf_grow(&state->buf, GIT_MAX_HEXSZ);
                label = p = state->buf.buf;
 
-               find_unique_abbrev_r(p, oid, default_abbrev);
+               repo_find_unique_abbrev_r(the_repository, p, oid,
+                                         default_abbrev);
 
                /*
                 * We may need to extend the abbreviated hash so that there is
@@ -5333,6 +5433,8 @@ static const char *label_oid(struct object_id *oid, const char *label,
                }
        } else {
                struct strbuf *buf = &state->buf;
+               int label_is_utf8 = 1; /* start with this assumption */
+               size_t max_len = buf->len + state->max_label_length;
 
                /*
                 * Sanitize labels by replacing non-alpha-numeric characters
@@ -5341,14 +5443,34 @@ static const char *label_oid(struct object_id *oid, const char *label,
                 *
                 * Note that we retain non-ASCII UTF-8 characters (identified
                 * via the most significant bit). They should be all acceptable
-                * in file names. We do not validate the UTF-8 here, that's not
-                * the job of this function.
+                * in file names.
+                *
+                * As we will use the labels as names of (loose) refs, it is
+                * vital that the name not be longer than the maximum component
+                * size of the file system (`NAME_MAX`). We are careful to
+                * truncate the label accordingly, allowing for the `.lock`
+                * suffix and for the label to be UTF-8 encoded (i.e. we avoid
+                * truncating in the middle of a character).
                 */
-               for (; *label; label++)
-                       if ((*label & 0x80) || isalnum(*label))
+               for (; *label && buf->len + 1 < max_len; label++)
+                       if (isalnum(*label) ||
+                           (!label_is_utf8 && (*label & 0x80)))
                                strbuf_addch(buf, *label);
+                       else if (*label & 0x80) {
+                               const char *p = label;
+
+                               utf8_width(&p, NULL);
+                               if (p) {
+                                       if (buf->len + (p - label) > max_len)
+                                               break;
+                                       strbuf_add(buf, label, p - label);
+                                       label = p - 1;
+                               } else {
+                                       label_is_utf8 = 0;
+                                       strbuf_addch(buf, *label);
+                               }
                        /* avoid leading dash and double-dashes */
-                       else if (buf->len && buf->buf[buf->len - 1] != '-')
+                       else if (buf->len && buf->buf[buf->len - 1] != '-')
                                strbuf_addch(buf, '-');
                if (!buf->len) {
                        strbuf_addstr(buf, "rev-");
@@ -5410,7 +5532,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
        struct string_entry *entry;
        struct oidset interesting = OIDSET_INIT, child_seen = OIDSET_INIT,
                shown = OIDSET_INIT;
-       struct label_state state = { OIDMAP_INIT, { NULL }, STRBUF_INIT };
+       struct label_state state =
+               { OIDMAP_INIT, { NULL }, STRBUF_INIT, GIT_MAX_LABEL_LENGTH };
 
        int abbr = flags & TODO_LIST_ABBREVIATE_CMDS;
        const char *cmd_pick = abbr ? "p" : "pick",
@@ -5418,6 +5541,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                *cmd_reset = abbr ? "t" : "reset",
                *cmd_merge = abbr ? "m" : "merge";
 
+       git_config_get_int("rebase.maxlabellength", &state.max_label_length);
+
        oidmap_init(&commit2todo, 0);
        oidmap_init(&state.commit2label, 0);
        hashmap_init(&state.labels, labels_cmp, NULL, 0);
@@ -5454,7 +5579,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                if (!is_empty && (commit->object.flags & PATCHSAME)) {
                        if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS)
                                warning(_("skipped previously applied commit %s"),
-                                       short_commit_name(commit));
+                                       short_commit_name(the_repository, commit));
                        skipped_commit = 1;
                        continue;
                }
@@ -5690,7 +5815,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
                if (!is_empty && (commit->object.flags & PATCHSAME)) {
                        if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS)
                                warning(_("skipped previously applied commit %s"),
-                                       short_commit_name(commit));
+                                       short_commit_name(r, commit));
                        skipped_commit = 1;
                        continue;
                }
@@ -5732,8 +5857,8 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list,
 
                base_items[i].command = TODO_EXEC;
                base_items[i].offset_in_buf = base_offset;
-               base_items[i].arg_offset = base_offset + strlen("exec ");
-               base_items[i].arg_len = command_len - strlen("exec ");
+               base_items[i].arg_offset = base_offset;
+               base_items[i].arg_len = command_len;
 
                base_offset += command_len + 1;
        }
@@ -5782,7 +5907,8 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list,
        todo_list->alloc = alloc;
 }
 
-static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
+static void todo_list_to_strbuf(struct repository *r,
+                               struct todo_list *todo_list,
                                struct strbuf *buf, int num, unsigned flags)
 {
        struct todo_item *item;
@@ -5811,7 +5937,7 @@ static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_lis
                /* add commit id */
                if (item->commit) {
                        const char *oid = flags & TODO_LIST_SHORTEN_IDS ?
-                                         short_commit_name(item->commit) :
+                                         short_commit_name(r, item->commit) :
                                          oid_to_hex(&item->commit->object.oid);
 
                        if (item->command == TODO_FIXUP) {
@@ -5874,7 +6000,7 @@ static int skip_unnecessary_picks(struct repository *r,
                        continue;
                if (item->command != TODO_PICK)
                        break;
-               if (parse_commit(item->commit)) {
+               if (repo_parse_commit(r, item->commit)) {
                        return error(_("could not parse commit '%s'"),
                                oid_to_hex(&item->commit->object.oid));
                }
@@ -6045,7 +6171,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
        struct object_id oid = onto->object.oid;
        int res;
 
-       find_unique_abbrev_r(shortonto, &oid, DEFAULT_ABBREV);
+       repo_find_unique_abbrev_r(r, shortonto, &oid,
+                                 DEFAULT_ABBREV);
 
        if (buf->len == 0) {
                struct todo_item *item = append_new_todo(todo_list);
@@ -6096,7 +6223,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
        todo_list_to_strbuf(r, &new_todo, &buf2, -1, 0);
        strbuf_swap(&new_todo.buf, &buf2);
        strbuf_release(&buf2);
-       new_todo.total_nr -= new_todo.nr;
+       /* Nothing is done yet, and we're reparsing, so let's reset the count */
+       new_todo.total_nr = 0;
        if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
                BUG("invalid todo list after expanding IDs:\n%s",
                    new_todo.buf.buf);
@@ -6117,7 +6245,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
        if (checkout_onto(r, opts, onto_name, &oid, orig_head))
                goto cleanup;
 
-       if (require_clean_work_tree(r, "rebase", "", 1, 1))
+       if (require_clean_work_tree(r, "rebase", NULL, 1, 1))
                goto cleanup;
 
        todo_list_write_total_nr(&new_todo);
@@ -6168,7 +6296,7 @@ static int skip_fixupish(const char *subject, const char **p) {
 int todo_list_rearrange_squash(struct todo_list *todo_list)
 {
        struct hashmap subject2item;
-       int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
+       int rearranged = 0, *next, *tail, i, nr = 0;
        char **subjects;
        struct commit_todo_item commit_todo;
        struct todo_item *items = NULL;
@@ -6206,12 +6334,15 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
                        return error(_("the script was already rearranged."));
                }
 
-               parse_commit(item->commit);
-               commit_buffer = logmsg_reencode(item->commit, NULL, "UTF-8");
+               repo_parse_commit(the_repository, item->commit);
+               commit_buffer = repo_logmsg_reencode(the_repository,
+                                                    item->commit, NULL,
+                                                    "UTF-8");
                find_commit_subject(commit_buffer, &subject);
                format_subject(&buf, subject, " ");
                subject = subjects[i] = strbuf_detach(&buf, &subject_len);
-               unuse_commit_buffer(item->commit, commit_buffer);
+               repo_unuse_commit_buffer(the_repository, item->commit,
+                                        commit_buffer);
                if (skip_fixupish(subject, &p)) {
                        struct commit *commit2;
 
@@ -6277,6 +6408,8 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
        }
 
        if (rearranged) {
+               ALLOC_ARRAY(items, todo_list->nr);
+
                for (i = 0; i < todo_list->nr; i++) {
                        enum todo_command command = todo_list->items[i].command;
                        int cur = i;
@@ -6289,16 +6422,15 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
                                continue;
 
                        while (cur >= 0) {
-                               ALLOC_GROW(items, nr + 1, alloc);
                                items[nr++] = todo_list->items[cur];
                                cur = next[cur];
                        }
                }
 
+               assert(nr == todo_list->nr);
+               todo_list->alloc = nr;
                FREE_AND_NULL(todo_list->items);
                todo_list->items = items;
-               todo_list->nr = nr;
-               todo_list->alloc = alloc;
        }
 
        free(next);
@@ -6321,8 +6453,8 @@ int sequencer_determine_whence(struct repository *r, enum commit_whence *whence)
                if (file_exists(git_path_seq_dir()))
                        *whence = FROM_CHERRY_PICK_MULTI;
                if (file_exists(rebase_path()) &&
-                   !get_oid("REBASE_HEAD", &rebase_head) &&
-                   !get_oid("CHERRY_PICK_HEAD", &cherry_pick_head) &&
+                   !repo_get_oid(r, "REBASE_HEAD", &rebase_head) &&
+                   !repo_get_oid(r, "CHERRY_PICK_HEAD", &cherry_pick_head) &&
                    oideq(&rebase_head, &cherry_pick_head))
                        *whence = FROM_REBASE_PICK;
                else
index 563fe5993340a0d0a18b148d4280c1d5a3f8d307..913a0f652d9ab356bc066f162fa1e5197f6900eb 100644 (file)
@@ -1,11 +1,12 @@
 #ifndef SEQUENCER_H
 #define SEQUENCER_H
 
-#include "cache.h"
 #include "strbuf.h"
+#include "strvec.h"
 #include "wt-status.h"
 
 struct commit;
+struct index_state;
 struct repository;
 
 const char *git_path_commit_editmsg(void);
@@ -60,8 +61,10 @@ struct replay_opts {
        /* Merge strategy */
        char *default_strategy;  /* from config options */
        char *strategy;
-       char **xopts;
-       size_t xopts_nr, xopts_alloc;
+       struct strvec xopts;
+
+       /* Reflog */
+       char *reflog_action;
 
        /* Used by fixup/squash */
        struct strbuf current_fixups;
@@ -73,8 +76,16 @@ struct replay_opts {
 
        /* Only used by REPLAY_NONE */
        struct rev_info *revs;
+
+       /* Private use */
+       const char *reflog_message;
 };
-#define REPLAY_OPTS_INIT { .edit = -1, .action = -1, .current_fixups = STRBUF_INIT }
+#define REPLAY_OPTS_INIT {                     \
+       .edit = -1,                             \
+       .action = -1,                           \
+       .current_fixups = STRBUF_INIT,          \
+       .xopts = STRVEC_INIT,                   \
+}
 
 /*
  * Note that ordering matters in this enum. Not only must it match the mapping
@@ -152,6 +163,7 @@ int sequencer_pick_revisions(struct repository *repo,
 int sequencer_continue(struct repository *repo, struct replay_opts *opts);
 int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
 int sequencer_skip(struct repository *repo, struct replay_opts *opts);
+void replay_opts_release(struct replay_opts *opts);
 int sequencer_remove_state(struct replay_opts *opts);
 
 #define TODO_LIST_KEEP_EMPTY (1U << 0)
@@ -240,7 +252,6 @@ int read_oneliner(struct strbuf *buf,
        const char *path, unsigned flags);
 int read_author_script(const char *path, char **name, char **email, char **date,
                       int allow_missing);
-void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
 int write_basic_state(struct replay_opts *opts, const char *head_name,
                      struct commit *onto, const struct object_id *orig_head);
 void sequencer_post_commit_cleanup(struct repository *r, int verbose);
diff --git a/serve.c b/serve.c
index 733347f602aa1ede3e45fee94050163790d65bf9..a1d71134d49cc88ead5af690315b27ae23215e56 100644 (file)
--- a/serve.c
+++ b/serve.c
@@ -1,23 +1,26 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "repository.h"
 #include "config.h"
+#include "hash-ll.h"
 #include "pkt-line.h"
 #include "version.h"
 #include "ls-refs.h"
 #include "protocol-caps.h"
 #include "serve.h"
 #include "upload-pack.h"
+#include "bundle-uri.h"
+#include "trace2.h"
 
 static int advertise_sid = -1;
 static int client_hash_algo = GIT_HASH_SHA1;
 
-static int always_advertise(struct repository *r,
-                           struct strbuf *value)
+static int always_advertise(struct repository *r UNUSED,
+                           struct strbuf *value UNUSED)
 {
        return 1;
 }
 
-static int agent_advertise(struct repository *r,
+static int agent_advertise(struct repository *r UNUSED,
                           struct strbuf *value)
 {
        if (value)
@@ -33,7 +36,7 @@ static int object_format_advertise(struct repository *r,
        return 1;
 }
 
-static void object_format_receive(struct repository *r,
+static void object_format_receive(struct repository *r UNUSED,
                                  const char *algo_name)
 {
        if (!algo_name)
@@ -47,7 +50,7 @@ static void object_format_receive(struct repository *r,
 static int session_id_advertise(struct repository *r, struct strbuf *value)
 {
        if (advertise_sid == -1 &&
-           git_config_get_bool("transfer.advertisesid", &advertise_sid))
+           repo_config_get_bool(r, "transfer.advertisesid", &advertise_sid))
                advertise_sid = 0;
        if (!advertise_sid)
                return 0;
@@ -56,7 +59,7 @@ static int session_id_advertise(struct repository *r, struct strbuf *value)
        return 1;
 }
 
-static void session_id_receive(struct repository *r,
+static void session_id_receive(struct repository *r UNUSED,
                               const char *client_sid)
 {
        if (!client_sid)
@@ -135,6 +138,11 @@ static struct protocol_capability capabilities[] = {
                .advertise = always_advertise,
                .command = cap_object_info,
        },
+       {
+               .name = "bundle-uri",
+               .advertise = bundle_uri_advertise,
+               .command = bundle_uri_command,
+       },
 };
 
 void protocol_v2_advertise_capabilities(void)
index 0ec6c0c16546a7a2ebb45a8e8a03e3270cedf214..e2fe0f91432d8089e74c7e562728aca329dec66c 100644 (file)
@@ -1,12 +1,17 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "refs.h"
 #include "object.h"
 #include "commit.h"
 #include "tag.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "path.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "server-info.h"
 #include "strbuf.h"
 
 struct update_info_ctx {
diff --git a/server-info.h b/server-info.h
new file mode 100644 (file)
index 0000000..13bbde2
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef SERVER_INFO_H
+#define SERVER_INFO_H
+
+/* Dumb servers support */
+int update_server_info(int);
+
+#endif /* SERVER_INFO_H */
diff --git a/setup.c b/setup.c
index cefd5f63c4680f7f656084ef72f74784f86e4562..b38702718fbc8db7bb1ba718ecd69b7c99da80e4 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -1,11 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "copy.h"
+#include "environment.h"
+#include "exec-cmd.h"
+#include "gettext.h"
+#include "object-name.h"
+#include "refs.h"
 #include "repository.h"
 #include "config.h"
 #include "dir.h"
+#include "setup.h"
 #include "string-list.h"
 #include "chdir-notify.h"
-#include "promisor-remote.h"
+#include "path.h"
 #include "quote.h"
+#include "trace2.h"
+#include "worktree.h"
 
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
@@ -510,7 +520,9 @@ no_prevention_needed:
        startup_info->original_cwd = NULL;
 }
 
-static int read_worktree_config(const char *var, const char *value, void *vdata)
+static int read_worktree_config(const char *var, const char *value,
+                               const struct config_context *ctx UNUSED,
+                               void *vdata)
 {
        struct repository_format *data = vdata;
 
@@ -546,6 +558,8 @@ static enum extension_result handle_extension_v0(const char *var,
                        data->precious_objects = git_config_bool(var, value);
                        return EXTENSION_OK;
                } else if (!strcmp(ext, "partialclone")) {
+                       if (!value)
+                               return config_error_nonbool(var);
                        data->partial_clone = xstrdup(value);
                        return EXTENSION_OK;
                } else if (!strcmp(ext, "worktreeconfig")) {
@@ -577,17 +591,29 @@ static enum extension_result handle_extension(const char *var,
                                     "extensions.objectformat", value);
                data->hash_algo = format;
                return EXTENSION_OK;
+       } else if (!strcmp(ext, "refstorage")) {
+               unsigned int format;
+
+               if (!value)
+                       return config_error_nonbool(var);
+               format = ref_storage_format_by_name(value);
+               if (format == REF_STORAGE_FORMAT_UNKNOWN)
+                       return error(_("invalid value for '%s': '%s'"),
+                                    "extensions.refstorage", value);
+               data->ref_storage_format = format;
+               return EXTENSION_OK;
        }
        return EXTENSION_UNKNOWN;
 }
 
-static int check_repo_format(const char *var, const char *value, void *vdata)
+static int check_repo_format(const char *var, const char *value,
+                            const struct config_context *ctx, void *vdata)
 {
        struct repository_format *data = vdata;
        const char *ext;
 
        if (strcmp(var, "core.repositoryformatversion") == 0)
-               data->version = git_config_int(var, value);
+               data->version = git_config_int(var, value, ctx->kvi);
        else if (skip_prefix(var, "extensions.", &ext)) {
                switch (handle_extension_v0(var, value, ext, data)) {
                case EXTENSION_ERROR:
@@ -610,7 +636,7 @@ static int check_repo_format(const char *var, const char *value, void *vdata)
                }
        }
 
-       return read_worktree_config(var, value, vdata);
+       return read_worktree_config(var, value, ctx, vdata);
 }
 
 static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
@@ -643,11 +669,10 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
        }
 
        repository_format_precious_objects = candidate->precious_objects;
-       repository_format_worktree_config = candidate->worktree_config;
        string_list_clear(&candidate->unknown_extensions, 0);
        string_list_clear(&candidate->v1_only_extensions, 0);
 
-       if (repository_format_worktree_config) {
+       if (candidate->worktree_config) {
                /*
                 * pick up core.bare and core.worktree from per-worktree
                 * config if present
@@ -680,29 +705,39 @@ int upgrade_repository_format(int target_version)
        struct strbuf err = STRBUF_INIT;
        struct strbuf repo_version = STRBUF_INIT;
        struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
+       int ret;
 
        strbuf_git_common_path(&sb, the_repository, "config");
        read_repository_format(&repo_fmt, sb.buf);
        strbuf_release(&sb);
 
-       if (repo_fmt.version >= target_version)
-               return 0;
+       if (repo_fmt.version >= target_version) {
+               ret = 0;
+               goto out;
+       }
 
        if (verify_repository_format(&repo_fmt, &err) < 0) {
-               error("cannot upgrade repository format from %d to %d: %s",
-                     repo_fmt.version, target_version, err.buf);
-               strbuf_release(&err);
-               return -1;
+               ret = error("cannot upgrade repository format from %d to %d: %s",
+                           repo_fmt.version, target_version, err.buf);
+               goto out;
+       }
+       if (!repo_fmt.version && repo_fmt.unknown_extensions.nr) {
+               ret = error("cannot upgrade repository format: "
+                           "unknown extension %s",
+                           repo_fmt.unknown_extensions.items[0].string);
+               goto out;
        }
-       if (!repo_fmt.version && repo_fmt.unknown_extensions.nr)
-               return error("cannot upgrade repository format: "
-                            "unknown extension %s",
-                            repo_fmt.unknown_extensions.items[0].string);
 
        strbuf_addf(&repo_version, "%d", target_version);
        git_config_set("core.repositoryformatversion", repo_version.buf);
+
+       ret = 1;
+
+out:
+       clear_repository_format(&repo_fmt);
        strbuf_release(&repo_version);
-       return 1;
+       strbuf_release(&err);
+       return ret;
 }
 
 static void init_repository_format(struct repository_format *format)
@@ -1109,7 +1144,8 @@ struct safe_directory_data {
        int is_safe;
 };
 
-static int safe_directory_cb(const char *key, const char *value, void *d)
+static int safe_directory_cb(const char *key, const char *value,
+                            const struct config_context *ctx UNUSED, void *d)
 {
        struct safe_directory_data *data = d;
 
@@ -1165,7 +1201,9 @@ static int ensure_valid_ownership(const char *gitfile,
        return data.is_safe;
 }
 
-static int allowed_bare_repo_cb(const char *key, const char *value, void *d)
+static int allowed_bare_repo_cb(const char *key, const char *value,
+                               const struct config_context *ctx UNUSED,
+                               void *d)
 {
        enum allowed_bare_repo *allowed_bare_repo = d;
 
@@ -1205,19 +1243,6 @@ static const char *allowed_bare_repo_to_string(
        return NULL;
 }
 
-enum discovery_result {
-       GIT_DIR_NONE = 0,
-       GIT_DIR_EXPLICIT,
-       GIT_DIR_DISCOVERED,
-       GIT_DIR_BARE,
-       /* these are errors */
-       GIT_DIR_HIT_CEILING = -1,
-       GIT_DIR_HIT_MOUNT_POINT = -2,
-       GIT_DIR_INVALID_GITFILE = -3,
-       GIT_DIR_INVALID_OWNERSHIP = -4,
-       GIT_DIR_DISALLOWED_BARE = -5,
-};
-
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
@@ -1345,6 +1370,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
                }
 
                if (is_git_directory(dir->buf)) {
+                       trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
                        if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT)
                                return GIT_DIR_DISALLOWED_BARE;
                        if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
@@ -1368,21 +1394,23 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
        }
 }
 
-int discover_git_directory(struct strbuf *commondir,
-                          struct strbuf *gitdir)
+enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
+                                                   struct strbuf *gitdir)
 {
        struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
        size_t gitdir_offset = gitdir->len, cwd_len;
        size_t commondir_offset = commondir->len;
        struct repository_format candidate = REPOSITORY_FORMAT_INIT;
+       enum discovery_result result;
 
        if (strbuf_getcwd(&dir))
-               return -1;
+               return GIT_DIR_CWD_FAILURE;
 
        cwd_len = dir.len;
-       if (setup_git_directory_gently_1(&dir, gitdir, NULL, 0) <= 0) {
+       result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0);
+       if (result <= 0) {
                strbuf_release(&dir);
-               return -1;
+               return result;
        }
 
        /*
@@ -1412,16 +1440,11 @@ int discover_git_directory(struct strbuf *commondir,
                strbuf_setlen(commondir, commondir_offset);
                strbuf_setlen(gitdir, gitdir_offset);
                clear_repository_format(&candidate);
-               return -1;
+               return GIT_DIR_INVALID_FORMAT;
        }
 
-       /* take ownership of candidate.partial_clone */
-       the_repository->repository_format_partial_clone =
-               candidate.partial_clone;
-       candidate.partial_clone = NULL;
-
        clear_repository_format(&candidate);
-       return 0;
+       return result;
 }
 
 const char *setup_git_directory_gently(int *nongit_ok)
@@ -1503,10 +1526,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
                }
                *nongit_ok = 1;
                break;
-       case GIT_DIR_NONE:
+       case GIT_DIR_CWD_FAILURE:
+       case GIT_DIR_INVALID_FORMAT:
                /*
                 * As a safeguard against setup_git_directory_gently_1 returning
-                * this value, fallthrough to BUG. Otherwise it is possible to
+                * these values, fallthrough to BUG. Otherwise it is possible to
                 * set startup_info->have_repository to 1 when we did nothing to
                 * find a repository.
                 */
@@ -1552,6 +1576,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
                }
                if (startup_info->have_repository) {
                        repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+                       repo_set_ref_storage_format(the_repository,
+                                                   repo_fmt.ref_storage_format);
+                       the_repository->repository_format_worktree_config =
+                               repo_fmt.worktree_config;
                        /* take ownership of repo_fmt.partial_clone */
                        the_repository->repository_format_partial_clone =
                                repo_fmt.partial_clone;
@@ -1643,6 +1671,10 @@ void check_repository_format(struct repository_format *fmt)
        check_repository_format_gently(get_git_dir(), fmt, NULL);
        startup_info->have_repository = 1;
        repo_set_hash_algo(the_repository, fmt->hash_algo);
+       repo_set_ref_storage_format(the_repository,
+                                   fmt->ref_storage_format);
+       the_repository->repository_format_worktree_config =
+               fmt->worktree_config;
        the_repository->repository_format_partial_clone =
                xstrdup_or_null(fmt->partial_clone);
        clear_repository_format(&repo_fmt);
@@ -1699,3 +1731,555 @@ int daemonize(void)
        return 0;
 #endif
 }
+
+#ifdef NO_TRUSTABLE_FILEMODE
+#define TEST_FILEMODE 0
+#else
+#define TEST_FILEMODE 1
+#endif
+
+#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
+
+static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
+                            DIR *dir)
+{
+       size_t path_baselen = path->len;
+       size_t template_baselen = template_path->len;
+       struct dirent *de;
+
+       /* Note: if ".git/hooks" file exists in the repository being
+        * re-initialized, /etc/core-git/templates/hooks/update would
+        * cause "git init" to fail here.  I think this is sane but
+        * it means that the set of templates we ship by default, along
+        * with the way the namespace under .git/ is organized, should
+        * be really carefully chosen.
+        */
+       safe_create_dir(path->buf, 1);
+       while ((de = readdir(dir)) != NULL) {
+               struct stat st_git, st_template;
+               int exists = 0;
+
+               strbuf_setlen(path, path_baselen);
+               strbuf_setlen(template_path, template_baselen);
+
+               if (de->d_name[0] == '.')
+                       continue;
+               strbuf_addstr(path, de->d_name);
+               strbuf_addstr(template_path, de->d_name);
+               if (lstat(path->buf, &st_git)) {
+                       if (errno != ENOENT)
+                               die_errno(_("cannot stat '%s'"), path->buf);
+               }
+               else
+                       exists = 1;
+
+               if (lstat(template_path->buf, &st_template))
+                       die_errno(_("cannot stat template '%s'"), template_path->buf);
+
+               if (S_ISDIR(st_template.st_mode)) {
+                       DIR *subdir = opendir(template_path->buf);
+                       if (!subdir)
+                               die_errno(_("cannot opendir '%s'"), template_path->buf);
+                       strbuf_addch(path, '/');
+                       strbuf_addch(template_path, '/');
+                       copy_templates_1(path, template_path, subdir);
+                       closedir(subdir);
+               }
+               else if (exists)
+                       continue;
+               else if (S_ISLNK(st_template.st_mode)) {
+                       struct strbuf lnk = STRBUF_INIT;
+                       if (strbuf_readlink(&lnk, template_path->buf,
+                                           st_template.st_size) < 0)
+                               die_errno(_("cannot readlink '%s'"), template_path->buf);
+                       if (symlink(lnk.buf, path->buf))
+                               die_errno(_("cannot symlink '%s' '%s'"),
+                                         lnk.buf, path->buf);
+                       strbuf_release(&lnk);
+               }
+               else if (S_ISREG(st_template.st_mode)) {
+                       if (copy_file(path->buf, template_path->buf, st_template.st_mode))
+                               die_errno(_("cannot copy '%s' to '%s'"),
+                                         template_path->buf, path->buf);
+               }
+               else
+                       error(_("ignoring template %s"), template_path->buf);
+       }
+}
+
+static void copy_templates(const char *template_dir, const char *init_template_dir)
+{
+       struct strbuf path = STRBUF_INIT;
+       struct strbuf template_path = STRBUF_INIT;
+       size_t template_len;
+       struct repository_format template_format = REPOSITORY_FORMAT_INIT;
+       struct strbuf err = STRBUF_INIT;
+       DIR *dir;
+       char *to_free = NULL;
+
+       if (!template_dir)
+               template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
+       if (!template_dir)
+               template_dir = init_template_dir;
+       if (!template_dir)
+               template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
+       if (!template_dir[0]) {
+               free(to_free);
+               return;
+       }
+
+       strbuf_addstr(&template_path, template_dir);
+       strbuf_complete(&template_path, '/');
+       template_len = template_path.len;
+
+       dir = opendir(template_path.buf);
+       if (!dir) {
+               warning(_("templates not found in %s"), template_dir);
+               goto free_return;
+       }
+
+       /* Make sure that template is from the correct vintage */
+       strbuf_addstr(&template_path, "config");
+       read_repository_format(&template_format, template_path.buf);
+       strbuf_setlen(&template_path, template_len);
+
+       /*
+        * No mention of version at all is OK, but anything else should be
+        * verified.
+        */
+       if (template_format.version >= 0 &&
+           verify_repository_format(&template_format, &err) < 0) {
+               warning(_("not copying templates from '%s': %s"),
+                         template_dir, err.buf);
+               strbuf_release(&err);
+               goto close_free_return;
+       }
+
+       strbuf_addstr(&path, get_git_common_dir());
+       strbuf_complete(&path, '/');
+       copy_templates_1(&path, &template_path, dir);
+close_free_return:
+       closedir(dir);
+free_return:
+       free(to_free);
+       strbuf_release(&path);
+       strbuf_release(&template_path);
+       clear_repository_format(&template_format);
+}
+
+/*
+ * If the git_dir is not directly inside the working tree, then git will not
+ * find it by default, and we need to set the worktree explicitly.
+ */
+static int needs_work_tree_config(const char *git_dir, const char *work_tree)
+{
+       if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
+               return 0;
+       if (skip_prefix(git_dir, work_tree, &git_dir) &&
+           !strcmp(git_dir, "/.git"))
+               return 0;
+       return 1;
+}
+
+void initialize_repository_version(int hash_algo,
+                                  unsigned int ref_storage_format,
+                                  int reinit)
+{
+       char repo_version_string[10];
+       int repo_version = GIT_REPO_VERSION;
+
+       if (hash_algo != GIT_HASH_SHA1 ||
+           ref_storage_format != REF_STORAGE_FORMAT_FILES)
+               repo_version = GIT_REPO_VERSION_READ;
+
+       /* This forces creation of new config file */
+       xsnprintf(repo_version_string, sizeof(repo_version_string),
+                 "%d", repo_version);
+       git_config_set("core.repositoryformatversion", repo_version_string);
+
+       if (hash_algo != GIT_HASH_SHA1)
+               git_config_set("extensions.objectformat",
+                              hash_algos[hash_algo].name);
+       else if (reinit)
+               git_config_set_gently("extensions.objectformat", NULL);
+
+       if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
+               git_config_set("extensions.refstorage",
+                              ref_storage_format_to_name(ref_storage_format));
+}
+
+static int is_reinit(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       char junk[2];
+       int ret;
+
+       git_path_buf(&buf, "HEAD");
+       ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1;
+       strbuf_release(&buf);
+       return ret;
+}
+
+void create_reference_database(unsigned int ref_storage_format,
+                              const char *initial_branch, int quiet)
+{
+       struct strbuf err = STRBUF_INIT;
+       int reinit = is_reinit();
+
+       /*
+        * We need to create a "refs" dir in any case so that older versions of
+        * Git can tell that this is a repository. This serves two main purposes:
+        *
+        * - Clients will know to stop walking the parent-directory chain when
+        *   detecting the Git repository. Otherwise they may end up detecting
+        *   a Git repository in a parent directory instead.
+        *
+        * - Instead of failing to detect a repository with unknown reference
+        *   format altogether, old clients will print an error saying that
+        *   they do not understand the reference format extension.
+        */
+       safe_create_dir(git_path("refs"), 1);
+       adjust_shared_perm(git_path("refs"));
+
+       repo_set_ref_storage_format(the_repository, ref_storage_format);
+       if (refs_init_db(&err))
+               die("failed to set up refs db: %s", err.buf);
+
+       /*
+        * Point the HEAD symref to the initial branch with if HEAD does
+        * not yet exist.
+        */
+       if (!reinit) {
+               char *ref;
+
+               if (!initial_branch)
+                       initial_branch = git_default_branch_name(quiet);
+
+               ref = xstrfmt("refs/heads/%s", initial_branch);
+               if (check_refname_format(ref, 0) < 0)
+                       die(_("invalid initial branch name: '%s'"),
+                           initial_branch);
+
+               if (create_symref("HEAD", ref, NULL) < 0)
+                       exit(1);
+               free(ref);
+       }
+
+       if (reinit && initial_branch)
+               warning(_("re-init: ignored --initial-branch=%s"),
+                       initial_branch);
+
+       strbuf_release(&err);
+}
+
+static int create_default_files(const char *template_path,
+                               const char *original_git_dir,
+                               const struct repository_format *fmt,
+                               int prev_bare_repository,
+                               int init_shared_repository)
+{
+       struct stat st1;
+       struct strbuf buf = STRBUF_INIT;
+       char *path;
+       int reinit;
+       int filemode;
+       const char *init_template_dir = NULL;
+       const char *work_tree = get_git_work_tree();
+
+       /*
+        * First copy the templates -- we might have the default
+        * config file there, in which case we would want to read
+        * from it after installing.
+        *
+        * Before reading that config, we also need to clear out any cached
+        * values (since we've just potentially changed what's available on
+        * disk).
+        */
+       git_config_get_pathname("init.templatedir", &init_template_dir);
+       copy_templates(template_path, init_template_dir);
+       free((char *)init_template_dir);
+       git_config_clear();
+       reset_shared_repository();
+       git_config(git_default_config, NULL);
+
+       reinit = is_reinit();
+
+       /*
+        * We must make sure command-line options continue to override any
+        * values we might have just re-read from the config.
+        */
+       if (init_shared_repository != -1)
+               set_shared_repository(init_shared_repository);
+       /*
+        * TODO: heed core.bare from config file in templates if no
+        *       command-line override given
+        */
+       is_bare_repository_cfg = prev_bare_repository || !work_tree;
+       /* TODO (continued):
+        *
+        * Unfortunately, the line above is equivalent to
+        *    is_bare_repository_cfg = !work_tree;
+        * which ignores the config entirely even if no `--[no-]bare`
+        * command line option was present.
+        *
+        * To see why, note that before this function, there was this call:
+        *    prev_bare_repository = is_bare_repository()
+        * expanding the right hand side:
+        *                 = is_bare_repository_cfg && !get_git_work_tree()
+        *                 = is_bare_repository_cfg && !work_tree
+        * note that the last simplification above is valid because nothing
+        * calls repo_init() or set_git_work_tree() between any of the
+        * relevant calls in the code, and thus the !get_git_work_tree()
+        * calls will return the same result each time.  So, what we are
+        * interested in computing is the right hand side of the line of
+        * code just above this comment:
+        *     prev_bare_repository || !work_tree
+        *        = is_bare_repository_cfg && !work_tree || !work_tree
+        *        = !work_tree
+        * because "A && !B || !B == !B" for all boolean values of A & B.
+        */
+
+       /*
+        * We would have created the above under user's umask -- under
+        * shared-repository settings, we would need to fix them up.
+        */
+       if (get_shared_repository()) {
+               adjust_shared_perm(get_git_dir());
+       }
+
+       initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, 0);
+
+       /* Check filemode trustability */
+       path = git_path_buf(&buf, "config");
+       filemode = TEST_FILEMODE;
+       if (TEST_FILEMODE && !lstat(path, &st1)) {
+               struct stat st2;
+               filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
+                               !lstat(path, &st2) &&
+                               st1.st_mode != st2.st_mode &&
+                               !chmod(path, st1.st_mode));
+               if (filemode && !reinit && (st1.st_mode & S_IXUSR))
+                       filemode = 0;
+       }
+       git_config_set("core.filemode", filemode ? "true" : "false");
+
+       if (is_bare_repository())
+               git_config_set("core.bare", "true");
+       else {
+               git_config_set("core.bare", "false");
+               /* allow template config file to override the default */
+               if (log_all_ref_updates == LOG_REFS_UNSET)
+                       git_config_set("core.logallrefupdates", "true");
+               if (needs_work_tree_config(original_git_dir, work_tree))
+                       git_config_set("core.worktree", work_tree);
+       }
+
+       if (!reinit) {
+               /* Check if symlink is supported in the work tree */
+               path = git_path_buf(&buf, "tXXXXXX");
+               if (!close(xmkstemp(path)) &&
+                   !unlink(path) &&
+                   !symlink("testing", path) &&
+                   !lstat(path, &st1) &&
+                   S_ISLNK(st1.st_mode))
+                       unlink(path); /* good */
+               else
+                       git_config_set("core.symlinks", "false");
+
+               /* Check if the filesystem is case-insensitive */
+               path = git_path_buf(&buf, "CoNfIg");
+               if (!access(path, F_OK))
+                       git_config_set("core.ignorecase", "true");
+               probe_utf8_pathname_composition();
+       }
+
+       strbuf_release(&buf);
+       return reinit;
+}
+
+static void create_object_directory(void)
+{
+       struct strbuf path = STRBUF_INIT;
+       size_t baselen;
+
+       strbuf_addstr(&path, get_object_directory());
+       baselen = path.len;
+
+       safe_create_dir(path.buf, 1);
+
+       strbuf_setlen(&path, baselen);
+       strbuf_addstr(&path, "/pack");
+       safe_create_dir(path.buf, 1);
+
+       strbuf_setlen(&path, baselen);
+       strbuf_addstr(&path, "/info");
+       safe_create_dir(path.buf, 1);
+
+       strbuf_release(&path);
+}
+
+static void separate_git_dir(const char *git_dir, const char *git_link)
+{
+       struct stat st;
+
+       if (!stat(git_link, &st)) {
+               const char *src;
+
+               if (S_ISREG(st.st_mode))
+                       src = read_gitfile(git_link);
+               else if (S_ISDIR(st.st_mode))
+                       src = git_link;
+               else
+                       die(_("unable to handle file type %d"), (int)st.st_mode);
+
+               if (rename(src, git_dir))
+                       die_errno(_("unable to move %s to %s"), src, git_dir);
+               repair_worktrees(NULL, NULL);
+       }
+
+       write_file(git_link, "gitdir: %s", git_dir);
+}
+
+static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
+{
+       const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
+       /*
+        * If we already have an initialized repo, don't allow the user to
+        * specify a different algorithm, as that could cause corruption.
+        * Otherwise, if the user has specified one on the command line, use it.
+        */
+       if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
+               die(_("attempt to reinitialize repository with different hash"));
+       else if (hash != GIT_HASH_UNKNOWN)
+               repo_fmt->hash_algo = hash;
+       else if (env) {
+               int env_algo = hash_algo_by_name(env);
+               if (env_algo == GIT_HASH_UNKNOWN)
+                       die(_("unknown hash algorithm '%s'"), env);
+               repo_fmt->hash_algo = env_algo;
+       }
+}
+
+static void validate_ref_storage_format(struct repository_format *repo_fmt,
+                                       unsigned int format)
+{
+       const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
+
+       if (repo_fmt->version >= 0 &&
+           format != REF_STORAGE_FORMAT_UNKNOWN &&
+           format != repo_fmt->ref_storage_format) {
+               die(_("attempt to reinitialize repository with different reference storage format"));
+       } else if (format != REF_STORAGE_FORMAT_UNKNOWN) {
+               repo_fmt->ref_storage_format = format;
+       } else if (name) {
+               format = ref_storage_format_by_name(name);
+               if (format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), name);
+               repo_fmt->ref_storage_format = format;
+       }
+}
+
+int init_db(const char *git_dir, const char *real_git_dir,
+           const char *template_dir, int hash,
+           unsigned int ref_storage_format,
+           const char *initial_branch,
+           int init_shared_repository, unsigned int flags)
+{
+       int reinit;
+       int exist_ok = flags & INIT_DB_EXIST_OK;
+       char *original_git_dir = real_pathdup(git_dir, 1);
+       struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
+       int prev_bare_repository;
+
+       if (real_git_dir) {
+               struct stat st;
+
+               if (!exist_ok && !stat(git_dir, &st))
+                       die(_("%s already exists"), git_dir);
+
+               if (!exist_ok && !stat(real_git_dir, &st))
+                       die(_("%s already exists"), real_git_dir);
+
+               set_git_dir(real_git_dir, 1);
+               git_dir = get_git_dir();
+               separate_git_dir(git_dir, original_git_dir);
+       }
+       else {
+               set_git_dir(git_dir, 1);
+               git_dir = get_git_dir();
+       }
+       startup_info->have_repository = 1;
+
+       /* Ensure `core.hidedotfiles` is processed */
+       git_config(platform_core_config, NULL);
+
+       safe_create_dir(git_dir, 0);
+
+       prev_bare_repository = is_bare_repository();
+
+       /* Check to see if the repository version is right.
+        * Note that a newly created repository does not have
+        * config file, so this will not fail.  What we are catching
+        * is an attempt to reinitialize new repository with an old tool.
+        */
+       check_repository_format(&repo_fmt);
+
+       validate_hash_algorithm(&repo_fmt, hash);
+       validate_ref_storage_format(&repo_fmt, ref_storage_format);
+
+       reinit = create_default_files(template_dir, original_git_dir,
+                                     &repo_fmt, prev_bare_repository,
+                                     init_shared_repository);
+
+       /*
+        * Now that we have set up both the hash algorithm and the ref storage
+        * format we can update the repository's settings accordingly.
+        */
+       repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+       repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format);
+
+       if (!(flags & INIT_DB_SKIP_REFDB))
+               create_reference_database(repo_fmt.ref_storage_format,
+                                         initial_branch, flags & INIT_DB_QUIET);
+       create_object_directory();
+
+       if (get_shared_repository()) {
+               char buf[10];
+               /* We do not spell "group" and such, so that
+                * the configuration can be read by older version
+                * of git. Note, we use octal numbers for new share modes,
+                * and compatibility values for PERM_GROUP and
+                * PERM_EVERYBODY.
+                */
+               if (get_shared_repository() < 0)
+                       /* force to the mode value */
+                       xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
+               else if (get_shared_repository() == PERM_GROUP)
+                       xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
+               else if (get_shared_repository() == PERM_EVERYBODY)
+                       xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
+               else
+                       BUG("invalid value for shared_repository");
+               git_config_set("core.sharedrepository", buf);
+               git_config_set("receive.denyNonFastforwards", "true");
+       }
+
+       if (!(flags & INIT_DB_QUIET)) {
+               int len = strlen(git_dir);
+
+               if (reinit)
+                       printf(get_shared_repository()
+                              ? _("Reinitialized existing shared Git repository in %s%s\n")
+                              : _("Reinitialized existing Git repository in %s%s\n"),
+                              git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+               else
+                       printf(get_shared_repository()
+                              ? _("Initialized empty shared Git repository in %s%s\n")
+                              : _("Initialized empty Git repository in %s%s\n"),
+                              git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+       }
+
+       clear_repository_format(&repo_fmt);
+       free(original_git_dir);
+       return 0;
+}
diff --git a/setup.h b/setup.h
new file mode 100644 (file)
index 0000000..3599aec
--- /dev/null
+++ b/setup.h
@@ -0,0 +1,214 @@
+#ifndef SETUP_H
+#define SETUP_H
+
+#include "string-list.h"
+
+int is_inside_git_dir(void);
+int is_inside_work_tree(void);
+int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
+int get_common_dir(struct strbuf *sb, const char *gitdir);
+
+/*
+ * Return true if the given path is a git directory; note that this _just_
+ * looks at the directory itself. If you want to know whether "foo/.git"
+ * is a repository, you must feed that path, not just "foo".
+ */
+int is_git_directory(const char *path);
+
+/*
+ * Return 1 if the given path is the root of a git repository or
+ * submodule, else 0. Will not return 1 for bare repositories with the
+ * exception of creating a bare repository in "foo/.git" and calling
+ * is_git_repository("foo").
+ *
+ * If we run into read errors, we err on the side of saying "yes, it is",
+ * as we usually consider sub-repos precious, and would prefer to err on the
+ * side of not disrupting or deleting them.
+ */
+int is_nonbare_repository_dir(struct strbuf *path);
+
+#define READ_GITFILE_ERR_STAT_FAILED 1
+#define READ_GITFILE_ERR_NOT_A_FILE 2
+#define READ_GITFILE_ERR_OPEN_FAILED 3
+#define READ_GITFILE_ERR_READ_FAILED 4
+#define READ_GITFILE_ERR_INVALID_FORMAT 5
+#define READ_GITFILE_ERR_NO_PATH 6
+#define READ_GITFILE_ERR_NOT_A_REPO 7
+#define READ_GITFILE_ERR_TOO_LARGE 8
+void read_gitfile_error_die(int error_code, const char *path, const char *dir);
+const char *read_gitfile_gently(const char *path, int *return_error_code);
+#define read_gitfile(path) read_gitfile_gently((path), NULL)
+const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
+#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
+
+void setup_work_tree(void);
+
+/*
+ * discover_git_directory_reason() is similar to discover_git_directory(),
+ * except it returns an enum value instead. It is important to note that
+ * a zero-valued return here is actually GIT_DIR_NONE, which is different
+ * from discover_git_directory.
+ */
+enum discovery_result {
+       GIT_DIR_EXPLICIT = 1,
+       GIT_DIR_DISCOVERED = 2,
+       GIT_DIR_BARE = 3,
+       /* these are errors */
+       GIT_DIR_HIT_CEILING = -1,
+       GIT_DIR_HIT_MOUNT_POINT = -2,
+       GIT_DIR_INVALID_GITFILE = -3,
+       GIT_DIR_INVALID_OWNERSHIP = -4,
+       GIT_DIR_DISALLOWED_BARE = -5,
+       GIT_DIR_INVALID_FORMAT = -6,
+       GIT_DIR_CWD_FAILURE = -7,
+};
+enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
+                                                   struct strbuf *gitdir);
+
+/*
+ * Find the commondir and gitdir of the repository that contains the current
+ * working directory, without changing the working directory or other global
+ * state. The result is appended to commondir and gitdir.  If the discovered
+ * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will
+ * both have the same result appended to the buffer.  The return value is
+ * either 0 upon success and -1 if no repository was found.
+ */
+static inline int discover_git_directory(struct strbuf *commondir,
+                                        struct strbuf *gitdir)
+{
+       if (discover_git_directory_reason(commondir, gitdir) <= 0)
+               return -1;
+       return 0;
+}
+
+const char *setup_git_directory_gently(int *);
+const char *setup_git_directory(void);
+char *prefix_path(const char *prefix, int len, const char *path);
+char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
+
+int check_filename(const char *prefix, const char *name);
+void verify_filename(const char *prefix,
+                    const char *name,
+                    int diagnose_misspelt_rev);
+void verify_non_filename(const char *prefix, const char *name);
+int path_inside_repo(const char *prefix, const char *path);
+
+void sanitize_stdfds(void);
+int daemonize(void);
+
+/*
+ * GIT_REPO_VERSION is the version we write by default. The
+ * _READ variant is the highest number we know how to
+ * handle.
+ */
+#define GIT_REPO_VERSION 0
+#define GIT_REPO_VERSION_READ 1
+
+/*
+ * You _have_ to initialize a `struct repository_format` using
+ * `= REPOSITORY_FORMAT_INIT` before calling `read_repository_format()`.
+ */
+struct repository_format {
+       int version;
+       int precious_objects;
+       char *partial_clone; /* value of extensions.partialclone */
+       int worktree_config;
+       int is_bare;
+       int hash_algo;
+       unsigned int ref_storage_format;
+       int sparse_index;
+       char *work_tree;
+       struct string_list unknown_extensions;
+       struct string_list v1_only_extensions;
+};
+
+/*
+ * Always use this to initialize a `struct repository_format`
+ * to a well-defined, default state before calling
+ * `read_repository()`.
+ */
+#define REPOSITORY_FORMAT_INIT \
+{ \
+       .version = -1, \
+       .is_bare = -1, \
+       .hash_algo = GIT_HASH_SHA1, \
+       .ref_storage_format = REF_STORAGE_FORMAT_FILES, \
+       .unknown_extensions = STRING_LIST_INIT_DUP, \
+       .v1_only_extensions = STRING_LIST_INIT_DUP, \
+}
+
+/*
+ * Read the repository format characteristics from the config file "path" into
+ * "format" struct. Returns the numeric version. On error, or if no version is
+ * found in the configuration, -1 is returned, format->version is set to -1,
+ * and all other fields in the struct are set to the default configuration
+ * (REPOSITORY_FORMAT_INIT). Always initialize the struct using
+ * REPOSITORY_FORMAT_INIT before calling this function.
+ */
+int read_repository_format(struct repository_format *format, const char *path);
+
+/*
+ * Free the memory held onto by `format`, but not the struct itself.
+ * (No need to use this after `read_repository_format()` fails.)
+ */
+void clear_repository_format(struct repository_format *format);
+
+/*
+ * Verify that the repository described by repository_format is something we
+ * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
+ * any errors encountered.
+ */
+int verify_repository_format(const struct repository_format *format,
+                            struct strbuf *err);
+
+/*
+ * Check the repository format version in the path found in get_git_dir(),
+ * and die if it is a version we don't understand. Generally one would
+ * set_git_dir() before calling this, and use it only for "are we in a valid
+ * repo?".
+ *
+ * If successful and fmt is not NULL, fill fmt with data.
+ */
+void check_repository_format(struct repository_format *fmt);
+
+#define INIT_DB_QUIET      (1 << 0)
+#define INIT_DB_EXIST_OK   (1 << 1)
+#define INIT_DB_SKIP_REFDB (1 << 2)
+
+int init_db(const char *git_dir, const char *real_git_dir,
+           const char *template_dir, int hash_algo,
+           unsigned int ref_storage_format,
+           const char *initial_branch, int init_shared_repository,
+           unsigned int flags);
+void initialize_repository_version(int hash_algo,
+                                  unsigned int ref_storage_format,
+                                  int reinit);
+void create_reference_database(unsigned int ref_storage_format,
+                              const char *initial_branch, int quiet);
+
+/*
+ * NOTE NOTE NOTE!!
+ *
+ * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must
+ * not be changed. Old repositories have core.sharedrepository written in
+ * numeric format, and therefore these values are preserved for compatibility
+ * reasons.
+ */
+enum sharedrepo {
+       PERM_UMASK          = 0,
+       OLD_PERM_GROUP      = 1,
+       OLD_PERM_EVERYBODY  = 2,
+       PERM_GROUP          = 0660,
+       PERM_EVERYBODY      = 0664
+};
+int git_config_perm(const char *var, const char *value);
+
+struct startup_info {
+       int have_repository;
+       const char *prefix;
+       const char *original_cwd;
+};
+extern struct startup_info *startup_info;
+extern const char *tmp_original_cwd;
+
+#endif /* SETUP_H */
index 133496bd4d9f2979dfe2765e98a50186463dde1d..f69fd166105a44a8be74fa6cd74841f06aa1a12d 100644 (file)
@@ -31,7 +31,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 /* closeout.c - close standard output and standard error
    Copyright (C) 1998-2007 Free Software Foundation, Inc.
@@ -47,7 +47,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
 #include <stdio.h>
diff --git a/sha1/openssl.h b/sha1/openssl.h
new file mode 100644 (file)
index 0000000..006c1f4
--- /dev/null
@@ -0,0 +1,49 @@
+/* wrappers for the EVP API of OpenSSL 3+ */
+#ifndef SHA1_OPENSSL_H
+#define SHA1_OPENSSL_H
+#include <openssl/evp.h>
+
+struct openssl_SHA1_CTX {
+       EVP_MD_CTX *ectx;
+};
+
+typedef struct openssl_SHA1_CTX openssl_SHA1_CTX;
+
+static inline void openssl_SHA1_Init(struct openssl_SHA1_CTX *ctx)
+{
+       const EVP_MD *type = EVP_sha1();
+
+       ctx->ectx = EVP_MD_CTX_new();
+       if (!ctx->ectx)
+               die("EVP_MD_CTX_new: out of memory");
+
+       EVP_DigestInit_ex(ctx->ectx, type, NULL);
+}
+
+static inline void openssl_SHA1_Update(struct openssl_SHA1_CTX *ctx,
+                                       const void *data,
+                                       size_t len)
+{
+       EVP_DigestUpdate(ctx->ectx, data, len);
+}
+
+static inline void openssl_SHA1_Final(unsigned char *digest,
+                                      struct openssl_SHA1_CTX *ctx)
+{
+       EVP_DigestFinal_ex(ctx->ectx, digest, NULL);
+       EVP_MD_CTX_free(ctx->ectx);
+}
+
+static inline void openssl_SHA1_Clone(struct openssl_SHA1_CTX *dst,
+                                       const struct openssl_SHA1_CTX *src)
+{
+       EVP_MD_CTX_copy_ex(dst->ectx, src->ectx);
+}
+
+#define platform_SHA_CTX openssl_SHA1_CTX
+#define platform_SHA1_Init openssl_SHA1_Init
+#define platform_SHA1_Clone openssl_SHA1_Clone
+#define platform_SHA1_Update openssl_SHA1_Update
+#define platform_SHA1_Final openssl_SHA1_Final
+
+#endif /* SHA1_OPENSSL_H */
index dede2cbddf94f0c62ef01ba5e48a93026408c5d6..f993ef9c69091740ea2248d6c34452ecb58fba02 100644 (file)
@@ -88,7 +88,7 @@
 /*
  * Should define Big Endian for a whitelist of known processors. See
  * https://sourceforge.net/p/predef/wiki/Endianness/ and
- * http://www.oracle.com/technetwork/server-storage/solaris/portingtosolaris-138514.html
+ * https://web.archive.org/web/20140421151132/http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html
  */
 #define SHA1DC_BIGENDIAN
 
index 5c300e812e0a11960dba743773720246e1a84cf1..9b675a046ee699189f54e31cf9a4e8bfc6d304a6 100644 (file)
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "sha1dc_git.h"
+#include "hex.h"
 
 #ifdef DC_SHA1_EXTERNAL
 /*
index 41e1c3fd3f787e04d6e4fa9eb7c56b617f1c5fa5..60e3ce84395ceaa70648a828ea1610a7fe9705a6 100644 (file)
@@ -17,6 +17,7 @@ void git_SHA1DCInit(SHA1_CTX *);
 void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
 void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len);
 
+#define platform_SHA_IS_SHA1DC /* used by "test-tool sha1-is-sha1dc" */
 #define platform_SHA_CTX SHA1_CTX
 #define platform_SHA1_Init git_SHA1DCInit
 #define platform_SHA1_Update git_SHA1DCUpdate
index 501da5ed9197ec61497e244879c7f60ba58617c1..17a90f1052526c84847aa6d530a3e54e2558329f 100644 (file)
@@ -7,22 +7,25 @@
 
 typedef gcry_md_hd_t gcrypt_SHA256_CTX;
 
-inline void gcrypt_SHA256_Init(gcrypt_SHA256_CTX *ctx)
+static inline void gcrypt_SHA256_Init(gcrypt_SHA256_CTX *ctx)
 {
-       gcry_md_open(ctx, GCRY_MD_SHA256, 0);
+       gcry_error_t err = gcry_md_open(ctx, GCRY_MD_SHA256, 0);
+       if (err)
+               die("gcry_md_open: %s", gcry_strerror(err));
 }
 
-inline void gcrypt_SHA256_Update(gcrypt_SHA256_CTX *ctx, const void *data, size_t len)
+static inline void gcrypt_SHA256_Update(gcrypt_SHA256_CTX *ctx, const void *data, size_t len)
 {
        gcry_md_write(*ctx, data, len);
 }
 
-inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX *ctx)
+static inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX *ctx)
 {
        memcpy(digest, gcry_md_read(*ctx, GCRY_MD_SHA256), SHA256_DIGEST_SIZE);
+       gcry_md_close(*ctx);
 }
 
-inline void gcrypt_SHA256_Clone(gcrypt_SHA256_CTX *dst, const gcrypt_SHA256_CTX *src)
+static inline void gcrypt_SHA256_Clone(gcrypt_SHA256_CTX *dst, const gcrypt_SHA256_CTX *src)
 {
        gcry_md_copy(dst, *src);
 }
diff --git a/sha256/openssl.h b/sha256/openssl.h
new file mode 100644 (file)
index 0000000..c1083d9
--- /dev/null
@@ -0,0 +1,49 @@
+/* wrappers for the EVP API of OpenSSL 3+ */
+#ifndef SHA256_OPENSSL_H
+#define SHA256_OPENSSL_H
+#include <openssl/evp.h>
+
+struct openssl_SHA256_CTX {
+       EVP_MD_CTX *ectx;
+};
+
+typedef struct openssl_SHA256_CTX openssl_SHA256_CTX;
+
+static inline void openssl_SHA256_Init(struct openssl_SHA256_CTX *ctx)
+{
+       const EVP_MD *type = EVP_sha256();
+
+       ctx->ectx = EVP_MD_CTX_new();
+       if (!ctx->ectx)
+               die("EVP_MD_CTX_new: out of memory");
+
+       EVP_DigestInit_ex(ctx->ectx, type, NULL);
+}
+
+static inline void openssl_SHA256_Update(struct openssl_SHA256_CTX *ctx,
+                                       const void *data,
+                                       size_t len)
+{
+       EVP_DigestUpdate(ctx->ectx, data, len);
+}
+
+static inline void openssl_SHA256_Final(unsigned char *digest,
+                                      struct openssl_SHA256_CTX *ctx)
+{
+       EVP_DigestFinal_ex(ctx->ectx, digest, NULL);
+       EVP_MD_CTX_free(ctx->ectx);
+}
+
+static inline void openssl_SHA256_Clone(struct openssl_SHA256_CTX *dst,
+                                       const struct openssl_SHA256_CTX *src)
+{
+       EVP_MD_CTX_copy_ex(dst->ectx, src->ectx);
+}
+
+#define platform_SHA256_CTX openssl_SHA256_CTX
+#define platform_SHA256_Init openssl_SHA256_Init
+#define platform_SHA256_Clone openssl_SHA256_Clone
+#define platform_SHA256_Update openssl_SHA256_Update
+#define platform_SHA256_Final openssl_SHA256_Final
+
+#endif /* SHA256_OPENSSL_H */
index 17f9bcdb5f38270c4f5910a2e1c930432ba350e5..7711798127e49efaa18b6403ecf103f05e92f7e7 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -1,20 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hex.h"
 #include "repository.h"
 #include "tempfile.h"
 #include "lockfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "commit.h"
 #include "tag.h"
 #include "pkt-line.h"
-#include "remote.h"
 #include "refs.h"
 #include "oid-array.h"
+#include "path.h"
 #include "diff.h"
 #include "revision.h"
 #include "commit-slab.h"
 #include "list-objects.h"
 #include "commit-reach.h"
 #include "shallow.h"
+#include "statinfo.h"
+#include "trace.h"
 
 void set_alternate_shallow_file(struct repository *r, const char *path, int override)
 {
@@ -30,12 +33,14 @@ int register_shallow(struct repository *r, const struct object_id *oid)
 {
        struct commit_graft *graft =
                xmalloc(sizeof(struct commit_graft));
-       struct commit *commit = lookup_commit(the_repository, oid);
+       struct commit *commit = lookup_commit(r, oid);
 
        oidcpy(&graft->oid, oid);
        graft->nr_parent = -1;
-       if (commit && commit->object.parsed)
+       if (commit && commit->object.parsed) {
+               free_commit_list(commit->parents);
                commit->parents = NULL;
+       }
        return register_commit_graft(r, graft, 0);
 }
 
@@ -247,7 +252,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
                struct commit *c = p->item;
                struct commit_list *parent;
 
-               if (parse_commit(c))
+               if (repo_parse_commit(the_repository, c))
                        die("unable to parse commit %s",
                            oid_to_hex(&c->object.oid));
 
@@ -301,7 +306,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
        if (graft->nr_parent != -1)
                return 0;
        if (data->flags & QUICK) {
-               if (!has_object_file(&graft->oid))
+               if (!repo_has_object_file(the_repository, &graft->oid))
                        return 0;
        } else if (data->flags & SEEN_ONLY) {
                struct commit *c = lookup_commit(the_repository, &graft->oid);
@@ -466,7 +471,7 @@ void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa)
        ALLOC_ARRAY(info->ours, sa->nr);
        ALLOC_ARRAY(info->theirs, sa->nr);
        for (i = 0; i < sa->nr; i++) {
-               if (has_object_file(sa->oid + i)) {
+               if (repo_has_object_file(the_repository, sa->oid + i)) {
                        struct commit_graft *graft;
                        graft = lookup_commit_graft(the_repository,
                                                    &sa->oid[i]);
@@ -494,7 +499,7 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info)
        for (i = dst = 0; i < info->nr_theirs; i++) {
                if (i != dst)
                        info->theirs[dst] = info->theirs[i];
-               if (has_object_file(oid + info->theirs[i]))
+               if (repo_has_object_file(the_repository, oid + info->theirs[i]))
                        dst++;
        }
        info->nr_theirs = dst;
@@ -583,7 +588,7 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,
                if (c->object.flags & BOTTOM)
                        continue;
 
-               if (parse_commit(c))
+               if (repo_parse_commit(the_repository, c))
                        die("unable to parse commit %s",
                            oid_to_hex(&c->object.oid));
 
@@ -791,7 +796,7 @@ static void post_assign_shallow(struct shallow_info *info,
                for (j = 0; j < bitmap_nr; j++)
                        if (bitmap[0][j] &&
                            /* Step 7, reachability test at commit level */
-                           !in_merge_bases_many(c, ca.nr, ca.commits)) {
+                           !repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits)) {
                                update_refstatus(ref_status, info->ref->nr, *bitmap);
                                dst++;
                                break;
@@ -819,9 +824,10 @@ int delayed_reachability_test(struct shallow_info *si, int c)
                        si->nr_commits = ca.nr;
                }
 
-               si->reachable[c] = in_merge_bases_many(commit,
-                                                      si->nr_commits,
-                                                      si->commits);
+               si->reachable[c] = repo_in_merge_bases_many(the_repository,
+                                                           commit,
+                                                           si->nr_commits,
+                                                           si->commits);
                si->need_reachability_test[c] = 0;
        }
        return si->reachable[c];
index aba6ff5829405647070c5b2a475318e2fd76282d..e9ca7e4bc80451a74dc10fb6d4e1a0b190dc4dcc 100644 (file)
--- a/shallow.h
+++ b/shallow.h
@@ -6,6 +6,8 @@
 #include "repository.h"
 #include "strbuf.h"
 
+struct oid_array;
+
 void set_alternate_shallow_file(struct repository *r, const char *path, int override);
 int register_shallow(struct repository *r, const struct object_id *oid);
 int unregister_shallow(const struct object_id *oid);
index 33f43edbf9a6b2e156f15628869c1d6c1755cd6f..aeb80fc4d5a33e1b5c6f2e1bbdf4cc3279802eef 100644 (file)
@@ -37,13 +37,13 @@ space := $(empty) $(empty)
 QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
 QUIET_SUBDIR1  =
 
-ifneq ($(findstring w,$(MAKEFLAGS)),w)
+ifneq ($(findstring w,$(firstword -$(MAKEFLAGS))),w)
 PRINT_DIR = --no-print-directory
 else # "make -w"
 NO_SUBDIR = :
 endif
 
-ifneq ($(findstring s,$(MAKEFLAGS)),s)
+ifneq ($(findstring s,$(firstword -$(MAKEFLAGS))),s)
 ifndef V
 ## common
        QUIET_SUBDIR0  = +@subdir=
@@ -60,6 +60,7 @@ ifndef V
        QUIET_AR       = @echo '   ' AR $@;
        QUIET_LINK     = @echo '   ' LINK $@;
        QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
+       QUIET_CP       = @echo '   ' CP $< $@;
        QUIET_LNCP     = @echo '   ' LN/CP $@;
        QUIET_XGETTEXT = @echo '   ' XGETTEXT $@;
        QUIET_MSGINIT  = @echo '   ' MSGINIT $@;
@@ -69,8 +70,11 @@ ifndef V
        QUIET_SP       = @echo '   ' SP $<;
        QUIET_HDR      = @echo '   ' HDR $(<:hcc=h);
        QUIET_RC       = @echo '   ' RC $@;
-       QUIET_SPATCH   = @echo '   ' SPATCH $<;
-       QUIET_SPATCH_T = @echo '   ' SPATCH TEST $(@:.build/%=%);
+
+## Used in "Makefile": SPATCH
+       QUIET_SPATCH                    = @echo '   ' SPATCH $< \>$@;
+       QUIET_SPATCH_TEST               = @echo '   ' SPATCH TEST $(@:.build/%=%);
+       QUIET_SPATCH_CAT                = @echo '   ' SPATCH CAT $(@:%.patch=%.d/)\*\*.patch \>$@;
 
 ## Used in "Documentation/Makefile"
        QUIET_ASCIIDOC  = @echo '   ' ASCIIDOC $@;
diff --git a/shell.c b/shell.c
index 7ff4109db7058b5677be2fa8775f699b54e4d4f1..2ece8b16e2e8e1e3473dbc7bae6deedea51ee4e8 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -1,10 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "quote.h"
 #include "exec-cmd.h"
 #include "strbuf.h"
 #include "run-command.h"
 #include "alias.h"
-#include "prompt.h"
 
 #define COMMAND_DIR "git-shell-commands"
 #define HELP_COMMAND COMMAND_DIR "/help"
@@ -52,21 +51,24 @@ static void cd_to_homedir(void)
 static void run_shell(void)
 {
        int done = 0;
-       static const char *help_argv[] = { HELP_COMMAND, NULL };
+       struct child_process help_cmd = CHILD_PROCESS_INIT;
 
        if (!access(NOLOGIN_COMMAND, F_OK)) {
                /* Interactive login disabled. */
-               const char *argv[] = { NOLOGIN_COMMAND, NULL };
+               struct child_process nologin_cmd = CHILD_PROCESS_INIT;
                int status;
 
-               status = run_command_v_opt(argv, 0);
+               strvec_push(&nologin_cmd.args, NOLOGIN_COMMAND);
+               status = run_command(&nologin_cmd);
                if (status < 0)
                        exit(127);
                exit(status);
        }
 
        /* Print help if enabled */
-       run_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE);
+       help_cmd.silent_exec_failure = 1;
+       strvec_push(&help_cmd.args, HELP_COMMAND);
+       run_command(&help_cmd);
 
        do {
                const char *prog;
@@ -125,9 +127,13 @@ static void run_shell(void)
                           !strcmp(prog, "exit") || !strcmp(prog, "bye")) {
                        done = 1;
                } else if (is_valid_cmd_name(prog)) {
+                       struct child_process cmd = CHILD_PROCESS_INIT;
+
                        full_cmd = make_cmd(prog);
                        argv[0] = full_cmd;
-                       code = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
+                       cmd.silent_exec_failure = 1;
+                       strvec_pushv(&cmd.args, argv);
+                       code = run_command(&cmd);
                        if (code == -1 && errno == ENOENT) {
                                fprintf(stderr, "unrecognized command '%s'\n", prog);
                        }
index 3f7e9aabcaef4b1ff2e5a5122cbea8bc97a6b7d5..28d04f951af73d8f9bdf58f6100a5eb3fe51b72c 100644 (file)
@@ -2,6 +2,7 @@
 #define SHORTLOG_H
 
 #include "string-list.h"
+#include "date.h"
 
 struct commit;
 
@@ -15,13 +16,16 @@ struct shortlog {
        int in2;
        int user_format;
        int abbrev;
+       struct date_mode date_mode;
 
        enum {
                SHORTLOG_GROUP_AUTHOR = (1 << 0),
                SHORTLOG_GROUP_COMMITTER = (1 << 1),
                SHORTLOG_GROUP_TRAILER = (1 << 2),
+               SHORTLOG_GROUP_FORMAT = (1 << 3),
        } groups;
        struct string_list trailers;
+       struct string_list format;
 
        int email;
        struct string_list mailmap;
@@ -29,6 +33,7 @@ struct shortlog {
 };
 
 void shortlog_init(struct shortlog *log);
+void shortlog_finish_setup(struct shortlog *log);
 
 void shortlog_add_commit(struct shortlog *log, struct commit *commit);
 
index 85bddfdcd4f57a37be390d424d3628d1f34877a7..266a67342be7245ae3c1dfae5a1a5ad6647f95df 100644 (file)
@@ -1,9 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "color.h"
 #include "config.h"
+#include "editor.h"
+#include "gettext.h"
 #include "sideband.h"
 #include "help.h"
 #include "pkt-line.h"
+#include "write-or-die.h"
 
 struct keyword_entry {
        /*
@@ -66,7 +69,10 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref
  * of the line. This should be called for a single line only, which is
  * passed as the first N characters of the SRC array.
  *
- * NEEDSWORK: use "size_t n" instead for clarity.
+ * It is fine to use "int n" here instead of "size_t n" as all calls to this
+ * function pass an 'int' parameter. Additionally, the buffer involved in
+ * storing these 'int' values takes input from a packet via the pkt-line
+ * interface, which is capable of transferring only 64kB at a time.
  */
 static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
 {
index 022677b6abaf918f51804a8fcbf18776aa3f2be5..66123bdbabb04123c6f0f4b64a8262584e3d2555 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "sigchain.h"
 
 #define SIGCHAIN_MAX_SIGNALS 32
index e4a54ce19433dd0cdd553996762af642ace0d9b8..3578feb28376e308ebb1ab1adc069c6860bbe72b 100644 (file)
@@ -1,4 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "read-cache-ll.h"
 #include "repository.h"
 #include "sparse-index.h"
 #include "tree.h"
@@ -7,7 +11,7 @@
 #include "cache-tree.h"
 #include "config.h"
 #include "dir.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 
 struct modify_index_context {
        struct index_state *write;
@@ -128,9 +132,6 @@ int is_sparse_index_allowed(struct index_state *istate, int flags)
        if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
                return 0;
 
-       if (!istate->repo)
-               istate->repo = the_repository;
-
        if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
                int test_env;
 
@@ -299,7 +300,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
         * If the index is already full, then keep it full. We will convert
         * it to a sparse index on write, if possible.
         */
-       if (!istate || istate->sparse_index == INDEX_EXPANDED)
+       if (istate->sparse_index == INDEX_EXPANDED)
                return;
 
        /*
@@ -327,9 +328,6 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
                        pl = NULL;
        }
 
-       if (!istate->repo)
-               istate->repo = the_repository;
-
        /*
         * A NULL pattern set indicates we are expanding a full index, so
         * we use a special region name that indicates the full expansion.
@@ -393,7 +391,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
                strbuf_setlen(&base, 0);
                strbuf_add(&base, ce->name, strlen(ce->name));
 
-               read_tree_at(istate->repo, tree, &base, &ps,
+               read_tree_at(istate->repo, tree, &base, 0, &ps,
                             add_path_to_index, &ctx);
 
                /* free directory entries. full entries are re-used */
@@ -424,6 +422,8 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
 
 void ensure_full_index(struct index_state *istate)
 {
+       if (!istate)
+               BUG("ensure_full_index() must get an index!");
        expand_index(istate, NULL);
 }
 
@@ -493,24 +493,42 @@ void clear_skip_worktree_from_present_files(struct index_state *istate)
        int dir_found = 1;
 
        int i;
+       int path_count[2] = {0, 0};
+       int restarted = 0;
 
        if (!core_apply_sparse_checkout ||
            sparse_expect_files_outside_of_patterns)
                return;
 
+       trace2_region_enter("index", "clear_skip_worktree_from_present_files",
+                           istate->repo);
 restart:
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce = istate->cache[i];
 
-               if (ce_skip_worktree(ce) &&
-                   path_found(ce->name, &last_dirname, &dir_len, &dir_found)) {
-                       if (S_ISSPARSEDIR(ce->ce_mode)) {
-                               ensure_full_index(istate);
-                               goto restart;
+               if (ce_skip_worktree(ce)) {
+                       path_count[restarted]++;
+                       if (path_found(ce->name, &last_dirname, &dir_len, &dir_found)) {
+                               if (S_ISSPARSEDIR(ce->ce_mode)) {
+                                       if (restarted)
+                                               BUG("ensure-full-index did not fully flatten?");
+                                       ensure_full_index(istate);
+                                       restarted = 1;
+                                       goto restart;
+                               }
+                               ce->ce_flags &= ~CE_SKIP_WORKTREE;
                        }
-                       ce->ce_flags &= ~CE_SKIP_WORKTREE;
                }
        }
+
+       if (path_count[0])
+               trace2_data_intmax("index", istate->repo,
+                                  "sparse_path_count", path_count[0]);
+       if (restarted)
+               trace2_data_intmax("index", istate->repo,
+                                  "sparse_path_count_full", path_count[1]);
+       trace2_region_leave("index", "clear_skip_worktree_from_present_files",
+                           istate->repo);
 }
 
 /*
@@ -529,12 +547,9 @@ void expand_to_path(struct index_state *istate,
        if (in_expand_to_path)
                return;
 
-       if (!istate || !istate->sparse_index)
+       if (!istate->sparse_index)
                return;
 
-       if (!istate->repo)
-               istate->repo = the_repository;
-
        in_expand_to_path = 1;
 
        /*
index 59a92d819ecdac1430b69db081b79cfc7e8ca84a..a16f3e67d75913daffc7ded0e08118ca233d1f5d 100644 (file)
@@ -37,4 +37,6 @@ struct pattern_list;
  */
 void expand_index(struct index_state *istate, struct pattern_list *pl);
 
+void ensure_full_index(struct index_state *istate);
+
 #endif
index 9d0ccc30d00e35965f47d55253166f3d8ce142e2..8c38687c04b81ceaa57746a96daf670874e31643 100644 (file)
@@ -1,5 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hash.h"
+#include "mem-pool.h"
+#include "read-cache-ll.h"
 #include "split-index.h"
+#include "strbuf.h"
 #include "ewah/ewok.h"
 
 struct split_index *init_split_index(struct index_state *istate)
@@ -90,7 +95,8 @@ void move_cache_to_base_index(struct index_state *istate)
                mem_pool_combine(istate->ce_mem_pool, istate->split_index->base->ce_mem_pool);
        }
 
-       CALLOC_ARRAY(si->base, 1);
+       ALLOC_ARRAY(si->base, 1);
+       index_state_init(si->base, istate->repo);
        si->base->version = istate->version;
        /* zero timestamp disables racy test in ce_write_index() */
        si->base->timestamp = istate->timestamp;
index 7a435ca2c970111b40ac9edba65726e79bd7941d..15a29cd08c96be6eeff23f609ec7c8d9533175b1 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef SPLIT_INDEX_H
 #define SPLIT_INDEX_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 struct index_state;
 struct strbuf;
diff --git a/statinfo.c b/statinfo.c
new file mode 100644 (file)
index 0000000..3c6bc04
--- /dev/null
@@ -0,0 +1,130 @@
+#include "git-compat-util.h"
+#include "environment.h"
+#include "statinfo.h"
+
+/*
+ * Munge st_size into an unsigned int.
+ */
+static unsigned int munge_st_size(off_t st_size) {
+       unsigned int sd_size = st_size;
+
+       /*
+        * If the file is an exact multiple of 4 GiB, modify the value so it
+        * doesn't get marked as racily clean (zero).
+        */
+       if (!sd_size && st_size)
+               return 0x80000000;
+       else
+               return sd_size;
+}
+
+void fill_stat_data(struct stat_data *sd, struct stat *st)
+{
+       sd->sd_ctime.sec = (unsigned int)st->st_ctime;
+       sd->sd_mtime.sec = (unsigned int)st->st_mtime;
+       sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
+       sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
+       sd->sd_dev = st->st_dev;
+       sd->sd_ino = st->st_ino;
+       sd->sd_uid = st->st_uid;
+       sd->sd_gid = st->st_gid;
+       sd->sd_size = munge_st_size(st->st_size);
+}
+
+static void set_times(struct stat *st, const struct stat_data *sd)
+{
+       st->st_ctime = sd->sd_ctime.sec;
+       st->st_mtime = sd->sd_mtime.sec;
+#ifdef NO_NSEC
+       ; /* nothing */
+#else
+#ifdef USE_ST_TIMESPEC
+       st->st_ctimespec.tv_nsec = sd->sd_ctime.nsec;
+       st->st_mtimespec.tv_nsec = sd->sd_mtime.nsec;
+#else
+       st->st_ctim.tv_nsec = sd->sd_ctime.nsec;
+       st->st_mtim.tv_nsec = sd->sd_mtime.nsec;
+#endif
+#endif
+}
+
+void fake_lstat_data(const struct stat_data *sd, struct stat *st)
+{
+       set_times(st, sd);
+       st->st_dev = sd->sd_dev;
+       st->st_ino = sd->sd_ino;
+       st->st_uid = sd->sd_uid;
+       st->st_gid = sd->sd_gid;
+       st->st_size = sd->sd_size;
+}
+
+int match_stat_data(const struct stat_data *sd, struct stat *st)
+{
+       int changed = 0;
+
+       if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
+               changed |= MTIME_CHANGED;
+       if (trust_ctime && check_stat &&
+           sd->sd_ctime.sec != (unsigned int)st->st_ctime)
+               changed |= CTIME_CHANGED;
+
+#ifdef USE_NSEC
+       if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+               changed |= MTIME_CHANGED;
+       if (trust_ctime && check_stat &&
+           sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+               changed |= CTIME_CHANGED;
+#endif
+
+       if (check_stat) {
+               if (sd->sd_uid != (unsigned int) st->st_uid ||
+                       sd->sd_gid != (unsigned int) st->st_gid)
+                       changed |= OWNER_CHANGED;
+               if (sd->sd_ino != (unsigned int) st->st_ino)
+                       changed |= INODE_CHANGED;
+       }
+
+#ifdef USE_STDEV
+       /*
+        * st_dev breaks on network filesystems where different
+        * clients will have different views of what "device"
+        * the filesystem is on
+        */
+       if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
+                       changed |= INODE_CHANGED;
+#endif
+
+       if (sd->sd_size != munge_st_size(st->st_size))
+               changed |= DATA_CHANGED;
+
+       return changed;
+}
+
+void stat_validity_clear(struct stat_validity *sv)
+{
+       FREE_AND_NULL(sv->sd);
+}
+
+int stat_validity_check(struct stat_validity *sv, const char *path)
+{
+       struct stat st;
+
+       if (stat(path, &st) < 0)
+               return sv->sd == NULL;
+       if (!sv->sd)
+               return 0;
+       return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
+}
+
+void stat_validity_update(struct stat_validity *sv, int fd)
+{
+       struct stat st;
+
+       if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+               stat_validity_clear(sv);
+       else {
+               if (!sv->sd)
+                       CALLOC_ARRAY(sv->sd, 1);
+               fill_stat_data(sv->sd, &st);
+       }
+}
diff --git a/statinfo.h b/statinfo.h
new file mode 100644 (file)
index 0000000..5b21a30
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef STATINFO_H
+#define STATINFO_H
+
+struct index_state;
+
+/*
+ * The "cache_time" is just the low 32 bits of the
+ * time. It doesn't matter if it overflows - we only
+ * check it for equality in the 32 bits we save.
+ */
+struct cache_time {
+       uint32_t sec;
+       uint32_t nsec;
+};
+
+struct stat_data {
+       struct cache_time sd_ctime;
+       struct cache_time sd_mtime;
+       unsigned int sd_dev;
+       unsigned int sd_ino;
+       unsigned int sd_uid;
+       unsigned int sd_gid;
+       unsigned int sd_size;
+};
+
+/*
+ * A struct to encapsulate the concept of whether a file has changed
+ * since we last checked it. This uses criteria similar to those used
+ * for the index.
+ */
+struct stat_validity {
+       struct stat_data *sd;
+};
+
+#define MTIME_CHANGED  0x0001
+#define CTIME_CHANGED  0x0002
+#define OWNER_CHANGED  0x0004
+#define MODE_CHANGED    0x0008
+#define INODE_CHANGED   0x0010
+#define DATA_CHANGED    0x0020
+#define TYPE_CHANGED    0x0040
+
+/*
+ * Record to sd the data from st that we use to check whether a file
+ * might have changed.
+ */
+void fill_stat_data(struct stat_data *sd, struct stat *st);
+
+/*
+ * The inverse of the above.  When we know the cache_entry that
+ * contains sd is up-to-date, but still need to pretend we called
+ * lstat() to learn that fact, this function fills "st" enough to
+ * fool ie_match_stat().
+ */
+void fake_lstat_data(const struct stat_data *sd, struct stat *st);
+
+/*
+ * Return 0 if st is consistent with a file not having been changed
+ * since sd was filled.  If there are differences, return a
+ * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
+ * INODE_CHANGED, and DATA_CHANGED.
+ */
+int match_stat_data(const struct stat_data *sd, struct stat *st);
+
+void stat_validity_clear(struct stat_validity *sv);
+
+/*
+ * Returns 1 if the path is a regular file (or a symlink to a regular
+ * file) and matches the saved stat_validity, 0 otherwise.  A missing
+ * or inaccessible file is considered a match if the struct was just
+ * initialized, or if the previous update found an inaccessible file.
+ */
+int stat_validity_check(struct stat_validity *sv, const char *path);
+
+/*
+ * Update the stat_validity from a file opened at descriptor fd. If
+ * the file is missing, inaccessible, or not a regular file, then
+ * future calls to stat_validity_check will match iff one of those
+ * conditions continues to be true.
+ */
+void stat_validity_update(struct stat_validity *sv, int fd);
+
+#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
+#define DTYPE(de)      ((de)->d_type)
+#else
+#undef DT_UNKNOWN
+#undef DT_DIR
+#undef DT_REG
+#undef DT_LNK
+#define DT_UNKNOWN     0
+#define DT_DIR         1
+#define DT_REG         2
+#define DT_LNK         3
+#define DTYPE(de)      DT_UNKNOWN
+#endif
+
+#endif
index 0890b1405c5cc6888396bac8558549ebdb2e2977..7827178d8e5e374582357f48a14a77bb15d98320 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,5 +1,7 @@
-#include "cache.h"
-#include "refs.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex-ll.h"
+#include "strbuf.h"
 #include "string-list.h"
 #include "utf8.h"
 #include "date.h"
@@ -357,7 +359,8 @@ static void add_lines(struct strbuf *out,
        strbuf_complete_line(out);
 }
 
-void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size)
+void strbuf_add_commented_lines(struct strbuf *out, const char *buf,
+                               size_t size, char comment_line_char)
 {
        static char prefix1[3];
        static char prefix2[2];
@@ -369,7 +372,8 @@ void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size
        add_lines(out, prefix1, prefix2, buf, size);
 }
 
-void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...)
+void strbuf_commented_addf(struct strbuf *sb, char comment_line_char,
+                          const char *fmt, ...)
 {
        va_list params;
        struct strbuf buf = STRBUF_INIT;
@@ -379,7 +383,7 @@ void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...)
        strbuf_vaddf(&buf, fmt, params);
        va_end(params);
 
-       strbuf_add_commented_lines(sb, buf.buf, buf.len);
+       strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_line_char);
        if (incomplete_line)
                sb->buf[--sb->len] = '\0';
 
@@ -407,36 +411,19 @@ void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap)
        strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
-                  void *context)
+int strbuf_expand_step(struct strbuf *sb, const char **formatp)
 {
-       for (;;) {
-               const char *percent;
-               size_t consumed;
-
-               percent = strchrnul(format, '%');
-               strbuf_add(sb, format, percent - format);
-               if (!*percent)
-                       break;
-               format = percent + 1;
+       const char *format = *formatp;
+       const char *percent = strchrnul(format, '%');
 
-               if (*format == '%') {
-                       strbuf_addch(sb, '%');
-                       format++;
-                       continue;
-               }
-
-               consumed = fn(sb, format, context);
-               if (consumed)
-                       format += consumed;
-               else
-                       strbuf_addch(sb, '%');
-       }
+       strbuf_add(sb, format, percent - format);
+       if (!*percent)
+               return 0;
+       *formatp = percent + 1;
+       return 1;
 }
 
-size_t strbuf_expand_literal_cb(struct strbuf *sb,
-                               const char *placeholder,
-                               void *context UNUSED)
+size_t strbuf_expand_literal(struct strbuf *sb, const char *placeholder)
 {
        int ch;
 
@@ -455,22 +442,6 @@ size_t strbuf_expand_literal_cb(struct strbuf *sb,
        return 0;
 }
 
-size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
-               void *context)
-{
-       struct strbuf_expand_dict_entry *e = context;
-       size_t len;
-
-       for (; e->placeholder && (len = strlen(e->placeholder)); e++) {
-               if (!strncmp(placeholder, e->placeholder, len)) {
-                       if (e->value)
-                               strbuf_addstr(sb, e->value);
-                       return len;
-               }
-       }
-       return 0;
-}
-
 void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
 {
        size_t i, len = src->len;
@@ -713,11 +684,11 @@ static int strbuf_getdelim(struct strbuf *sb, FILE *fp, int term)
        return 0;
 }
 
-int strbuf_getline(struct strbuf *sb, FILE *fp)
+int strbuf_getdelim_strip_crlf(struct strbuf *sb, FILE *fp, int term)
 {
-       if (strbuf_getwholeline(sb, fp, '\n'))
+       if (strbuf_getwholeline(sb, fp, term))
                return EOF;
-       if (sb->buf[sb->len - 1] == '\n') {
+       if (term == '\n' && sb->buf[sb->len - 1] == '\n') {
                strbuf_setlen(sb, sb->len - 1);
                if (sb->len && sb->buf[sb->len - 1] == '\r')
                        strbuf_setlen(sb, sb->len - 1);
@@ -725,6 +696,11 @@ int strbuf_getline(struct strbuf *sb, FILE *fp)
        return 0;
 }
 
+int strbuf_getline(struct strbuf *sb, FILE *fp)
+{
+       return strbuf_getdelim_strip_crlf(sb, fp, '\n');
+}
+
 int strbuf_getline_lf(struct strbuf *sb, FILE *fp)
 {
        return strbuf_getdelim(sb, fp, '\n');
@@ -803,25 +779,6 @@ void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s)
        }
 }
 
-int is_rfc3986_reserved_or_unreserved(char ch)
-{
-       if (is_rfc3986_unreserved(ch))
-               return 1;
-       switch (ch) {
-               case '!': case '*': case '\'': case '(': case ')': case ';':
-               case ':': case '@': case '&': case '=': case '+': case '$':
-               case ',': case '/': case '?': case '#': case '[': case ']':
-                       return 1;
-       }
-       return 0;
-}
-
-int is_rfc3986_unreserved(char ch)
-{
-       return isalnum(ch) ||
-               ch == '-' || ch == '_' || ch == '.' || ch == '~';
-}
-
 static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
                                 char_predicate allow_unencoded_fn)
 {
@@ -892,42 +849,6 @@ void strbuf_humanise_rate(struct strbuf *buf, off_t bytes)
        strbuf_humanise(buf, bytes, 1);
 }
 
-void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
-{
-       if (!*path)
-               die("The empty string is not a valid path");
-       if (!is_absolute_path(path)) {
-               struct stat cwd_stat, pwd_stat;
-               size_t orig_len = sb->len;
-               char *cwd = xgetcwd();
-               char *pwd = getenv("PWD");
-               if (pwd && strcmp(pwd, cwd) &&
-                   !stat(cwd, &cwd_stat) &&
-                   (cwd_stat.st_dev || cwd_stat.st_ino) &&
-                   !stat(pwd, &pwd_stat) &&
-                   pwd_stat.st_dev == cwd_stat.st_dev &&
-                   pwd_stat.st_ino == cwd_stat.st_ino)
-                       strbuf_addstr(sb, pwd);
-               else
-                       strbuf_addstr(sb, cwd);
-               if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
-                       strbuf_addch(sb, '/');
-               free(cwd);
-       }
-       strbuf_addstr(sb, path);
-}
-
-void strbuf_add_real_path(struct strbuf *sb, const char *path)
-{
-       if (sb->len) {
-               struct strbuf resolved = STRBUF_INIT;
-               strbuf_realpath(&resolved, path, 1);
-               strbuf_addbuf(sb, &resolved);
-               strbuf_release(&resolved);
-       } else
-               strbuf_realpath(sb, path, 1);
-}
-
 int printf_ln(const char *fmt, ...)
 {
        int ret;
@@ -1014,37 +935,20 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm,
         * we want for %z, but the computation for %s has to convert to number
         * of seconds.
         */
-       for (;;) {
-               const char *percent = strchrnul(fmt, '%');
-               strbuf_add(&munged_fmt, fmt, percent - fmt);
-               if (!*percent)
-                       break;
-               fmt = percent + 1;
-               switch (*fmt) {
-               case '%':
+       while (strbuf_expand_step(&munged_fmt, &fmt)) {
+               if (skip_prefix(fmt, "%", &fmt))
                        strbuf_addstr(&munged_fmt, "%%");
-                       fmt++;
-                       break;
-               case 's':
+               else if (skip_prefix(fmt, "s", &fmt))
                        strbuf_addf(&munged_fmt, "%"PRItime,
                                    (timestamp_t)tm_to_time_t(tm) -
                                    3600 * (tz_offset / 100) -
                                    60 * (tz_offset % 100));
-                       fmt++;
-                       break;
-               case 'z':
+               else if (skip_prefix(fmt, "z", &fmt))
                        strbuf_addf(&munged_fmt, "%+05d", tz_offset);
-                       fmt++;
-                       break;
-               case 'Z':
-                       if (suppress_tz_name) {
-                               fmt++;
-                               break;
-                       }
-                       /* FALLTHROUGH */
-               default:
+               else if (suppress_tz_name && skip_prefix(fmt, "Z", &fmt))
+                       ; /* nothing */
+               else
                        strbuf_addch(&munged_fmt, '%');
-               }
        }
        fmt = munged_fmt.buf;
 
@@ -1072,21 +976,6 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm,
        strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
-                                  const struct object_id *oid, int abbrev_len)
-{
-       int r;
-       strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
-       r = repo_find_unique_abbrev_r(repo, sb->buf + sb->len, oid, abbrev_len);
-       strbuf_setlen(sb, sb->len + r);
-}
-
-void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
-                             int abbrev_len)
-{
-       strbuf_repo_add_unique_abbrev(sb, the_repository, oid, abbrev_len);
-}
-
 /*
  * Returns the length of a line, without trailing spaces.
  *
@@ -1116,10 +1005,10 @@ static size_t cleanup(char *line, size_t len)
  *
  * If last line does not have a newline at the end, one is added.
  *
- * Enable skip_comments to skip every line starting with comment
- * character.
+ * Pass a non-NUL comment_line_char to skip every line starting
+ * with it.
  */
-void strbuf_stripspace(struct strbuf *sb, int skip_comments)
+void strbuf_stripspace(struct strbuf *sb, char comment_line_char)
 {
        size_t empties = 0;
        size_t i, j, len, newlen;
@@ -1132,7 +1021,8 @@ void strbuf_stripspace(struct strbuf *sb, int skip_comments)
                eol = memchr(sb->buf + i, '\n', sb->len - i);
                len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
 
-               if (skip_comments && len && sb->buf[i] == comment_line_char) {
+               if (comment_line_char && len &&
+                   sb->buf[i] == comment_line_char) {
                        newlen = 0;
                        continue;
                }
@@ -1153,50 +1043,8 @@ void strbuf_stripspace(struct strbuf *sb, int skip_comments)
        strbuf_setlen(sb, j);
 }
 
-int strbuf_normalize_path(struct strbuf *src)
+void strbuf_strip_file_from_path(struct strbuf *sb)
 {
-       struct strbuf dst = STRBUF_INIT;
-
-       strbuf_grow(&dst, src->len);
-       if (normalize_path_copy(dst.buf, src->buf) < 0) {
-               strbuf_release(&dst);
-               return -1;
-       }
-
-       /*
-        * normalize_path does not tell us the new length, so we have to
-        * compute it by looking for the new NUL it placed
-        */
-       strbuf_setlen(&dst, strlen(dst.buf));
-       strbuf_swap(src, &dst);
-       strbuf_release(&dst);
-       return 0;
-}
-
-int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
-                             const char *const *env)
-{
-       char *path2 = NULL;
-       int fd, res = 0;
-
-       if (!is_absolute_path(path))
-               path = path2 = xstrdup(git_path("%s", path));
-
-       fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
-       if (fd < 0)
-               res = error_errno(_("could not open '%s' for writing"), path);
-       else if (write_in_full(fd, buffer->buf, buffer->len) < 0) {
-               res = error_errno(_("could not write to '%s'"), path);
-               close(fd);
-       } else if (close(fd) < 0)
-               res = error_errno(_("could not close '%s'"), path);
-       else {
-               strbuf_reset(buffer);
-               if (launch_editor(path, buffer, env) < 0)
-                       res = error_errno(_("could not edit '%s'"), path);
-               unlink(path);
-       }
-
-       free(path2);
-       return res;
+       char *path_sep = find_last_dir_sep(sb->buf);
+       strbuf_setlen(sb, path_sep ? path_sep - sb->buf + 1 : 0);
 }
index 76965a17d444829b4a5b5edc099d12bf1fcc7ea3..e959caca876ac759debb0c77cd7c6d1046cbc25d 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -1,12 +1,20 @@
 #ifndef STRBUF_H
 #define STRBUF_H
 
+/*
+ * NOTE FOR STRBUF DEVELOPERS
+ *
+ * strbuf is a low-level primitive; as such it should interact only
+ * with other low-level primitives. Do not introduce new functions
+ * which interact with higher-level APIs.
+ */
+
 struct string_list;
 
 /**
- * strbuf's are meant to be used with all the usual C string and memory
+ * strbufs are meant to be used with all the usual C string and memory
  * APIs. Given that the length of the buffer is known, it's often better to
- * use the mem* functions than a str* one (memchr vs. strchr e.g.).
+ * use the mem* functions than a str* one (e.g., memchr vs. strchr).
  * Though, one has to be careful about the fact that str* functions often
  * stop on NULs and that strbufs may have embedded NULs.
  *
@@ -16,7 +24,7 @@ struct string_list;
  * strbufs have some invariants that are very important to keep in mind:
  *
  *  - The `buf` member is never NULL, so it can be used in any usual C
- *    string operations safely. strbuf's _have_ to be initialized either by
+ *    string operations safely. strbufs _have_ to be initialized either by
  *    `strbuf_init()` or by `= STRBUF_INIT` before the invariants, though.
  *
  *    Do *not* assume anything on what `buf` really is (e.g. if it is
@@ -29,7 +37,7 @@ struct string_list;
  *
  *  - The `buf` member is a byte array that has at least `len + 1` bytes
  *    allocated. The extra byte is used to store a `'\0'`, allowing the
- *    `buf` member to be a valid C-string. Every strbuf function ensure this
+ *    `buf` member to be a valid C-string. All strbuf functions ensure this
  *    invariant is preserved.
  *
  *    NOTE: It is OK to "play" with the buffer directly if you work it this
@@ -72,10 +80,6 @@ struct strbuf {
 extern char strbuf_slopbuf[];
 #define STRBUF_INIT  { .buf = strbuf_slopbuf }
 
-/*
- * Predeclare this here, since cache.h includes this file before it defines the
- * struct.
- */
 struct object_id;
 
 /**
@@ -283,7 +287,8 @@ void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
  * by a comment character and a blank.
  */
 void strbuf_add_commented_lines(struct strbuf *out,
-                               const char *buf, size_t size);
+                               const char *buf, size_t size,
+                               char comment_line_char);
 
 
 /**
@@ -318,58 +323,19 @@ const char *strbuf_join_argv(struct strbuf *buf, int argc,
                             const char **argv, char delim);
 
 /**
- * This function can be used to expand a format string containing
- * placeholders. To that end, it parses the string and calls the specified
- * function for every percent sign found.
- *
- * The callback function is given a pointer to the character after the `%`
- * and a pointer to the struct strbuf.  It is expected to add the expanded
- * version of the placeholder to the strbuf, e.g. to add a newline
- * character if the letter `n` appears after a `%`.  The function returns
- * the length of the placeholder recognized and `strbuf_expand()` skips
- * over it.
- *
- * The format `%%` is automatically expanded to a single `%` as a quoting
- * mechanism; callers do not need to handle the `%` placeholder themselves,
- * and the callback function will not be invoked for this placeholder.
- *
- * All other characters (non-percent and not skipped ones) are copied
- * verbatim to the strbuf.  If the callback returned zero, meaning that the
- * placeholder is unknown, then the percent sign is copied, too.
- *
- * In order to facilitate caching and to make it possible to give
- * parameters to the callback, `strbuf_expand()` passes a context
- * pointer with any kind of data.
+ * Used with `strbuf_expand_step` to expand the literals %n and %x
+ * followed by two hexadecimal digits. Returns the number of recognized
+ * characters.
  */
-typedef size_t (*expand_fn_t) (struct strbuf *sb,
-                              const char *placeholder,
-                              void *context);
-void strbuf_expand(struct strbuf *sb,
-                  const char *format,
-                  expand_fn_t fn,
-                  void *context);
+size_t strbuf_expand_literal(struct strbuf *sb, const char *placeholder);
 
 /**
- * Used as callback for `strbuf_expand` to only expand literals
- * (i.e. %n and %xNN). The context argument is ignored.
+ * If the string pointed to by `formatp` contains a percent sign ("%"),
+ * advance it to point to the character following the next one and
+ * return 1, otherwise return 0.  Append the substring before that
+ * percent sign to `sb`, or the whole string if there is none.
  */
-size_t strbuf_expand_literal_cb(struct strbuf *sb,
-                               const char *placeholder,
-                               void *context);
-
-/**
- * Used as callback for `strbuf_expand()`, expects an array of
- * struct strbuf_expand_dict_entry as context, i.e. pairs of
- * placeholder and replacement string.  The array needs to be
- * terminated by an entry with placeholder set to NULL.
- */
-struct strbuf_expand_dict_entry {
-       const char *placeholder;
-       const char *value;
-};
-size_t strbuf_expand_dict_cb(struct strbuf *sb,
-                            const char *placeholder,
-                            void *context);
+int strbuf_expand_step(struct strbuf *sb, const char **formatp);
 
 /**
  * Append the contents of one strbuf to another, quoting any
@@ -412,8 +378,8 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
  * Add a formatted string prepended by a comment character and a
  * blank to the buffer.
  */
-__attribute__((format (printf, 2, 3)))
-void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
+__attribute__((format (printf, 3, 4)))
+void strbuf_commented_addf(struct strbuf *sb, char comment_line_char, const char *fmt, ...);
 
 __attribute__((format (printf,2,0)))
 void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
@@ -475,6 +441,18 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
  */
 ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
 
+/**
+ * Read from a FILE * until the specified terminator is encountered,
+ * overwriting the existing contents of the strbuf.
+ *
+ * Reading stops after the terminator or at EOF.  The terminator is
+ * removed from the buffer before returning.  If the terminator is LF
+ * and if it is preceded by a CR, then the whole CRLF is stripped.
+ * Returns 0 unless there was nothing left before EOF, in which case
+ * it returns `EOF`.
+ */
+int strbuf_getdelim_strip_crlf(struct strbuf *sb, FILE *fp, int term);
+
 /**
  * Read a line from a FILE *, overwriting the existing contents of
  * the strbuf.  The strbuf_getline*() family of functions share
@@ -527,28 +505,6 @@ int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term);
  */
 int strbuf_getcwd(struct strbuf *sb);
 
-/**
- * Add a path to a buffer, converting a relative path to an
- * absolute one in the process.  Symbolic links are not
- * resolved.
- */
-void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
-
-/**
- * Canonize `path` (make it absolute, resolve symlinks, remove extra
- * slashes) and append it to `sb`.  Die with an informative error
- * message if there is a problem.
- *
- * The directory part of `path` (i.e., everything up to the last
- * dir_sep) must denote a valid, existing directory, but the last
- * component need not exist.
- *
- * Callers that don't mind links should use the more lightweight
- * strbuf_add_absolute_path() instead.
- */
-void strbuf_add_real_path(struct strbuf *sb, const char *path);
-
-
 /**
  * Normalize in-place the path contained in the strbuf. See
  * normalize_path_copy() for details. If an error occurs, the contents of "sb"
@@ -557,10 +513,11 @@ void strbuf_add_real_path(struct strbuf *sb, const char *path);
 int strbuf_normalize_path(struct strbuf *sb);
 
 /**
- * Strip whitespace from a buffer. The second parameter controls if
- * comments are considered contents to be removed or not.
+ * Strip whitespace from a buffer. If comment_line_char is non-NUL,
+ * then lines beginning with that character are considered comments,
+ * thus removed.
  */
-void strbuf_stripspace(struct strbuf *buf, int skip_comments);
+void strbuf_stripspace(struct strbuf *buf, char comment_line_char);
 
 static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
 {
@@ -630,39 +587,16 @@ void strbuf_add_separated_string_list(struct strbuf *str,
  */
 void strbuf_list_free(struct strbuf **list);
 
-/**
- * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
- * the strbuf `sb`.
- */
-struct repository;
-void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
-                                  const struct object_id *oid, int abbrev_len);
-void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
-                             int abbrev_len);
-
-/**
- * Launch the user preferred editor to edit a file and fill the buffer
- * with the file's contents upon the user completing their editing. The
- * third argument can be used to set the environment which the editor is
- * run in. If the buffer is NULL the editor is launched as usual but the
- * file's contents are not read into the buffer upon completion.
- */
-int launch_editor(const char *path, struct strbuf *buffer,
-                 const char *const *env);
-
-int launch_sequence_editor(const char *path, struct strbuf *buffer,
-                          const char *const *env);
-
 /*
- * In contrast to `launch_editor()`, this function writes out the contents
- * of the specified file first, then clears the `buffer`, then launches
- * the editor and reads back in the file contents into the `buffer`.
- * Finally, it deletes the temporary file.
+ * Remove the filename from the provided path string. If the path
+ * contains a trailing separator, then the path is considered a directory
+ * and nothing is modified.
  *
- * If `path` is relative, it refers to a file in the `.git` directory.
+ * Examples:
+ * - "/path/to/file" -> "/path/to/"
+ * - "/path/to/dir/" -> "/path/to/dir/"
  */
-int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
-                             const char *const *env);
+void strbuf_strip_file_from_path(struct strbuf *sb);
 
 void strbuf_add_lines(struct strbuf *sb,
                      const char *prefix,
@@ -695,14 +629,14 @@ static inline void strbuf_complete_line(struct strbuf *sb)
 
 /*
  * Copy "name" to "sb", expanding any special @-marks as handled by
- * interpret_branch_name(). The result is a non-qualified branch name
+ * repo_interpret_branch_name(). The result is a non-qualified branch name
  * (so "foo" or "origin/master" instead of "refs/heads/foo" or
  * "refs/remotes/origin/master").
  *
  * Note that the resulting name may not be a syntactically valid refname.
  *
  * If "allowed" is non-zero, restrict the set of allowed expansions. See
- * interpret_branch_name() for details.
+ * repo_interpret_branch_name() for details.
  */
 void strbuf_branchname(struct strbuf *sb, const char *name,
                       unsigned allowed);
@@ -717,9 +651,6 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
 typedef int (*char_predicate)(char ch);
 
-int is_rfc3986_unreserved(char ch);
-int is_rfc3986_reserved_or_unreserved(char ch);
-
 void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
                             char_predicate allow_unencoded_fn);
 
@@ -740,4 +671,36 @@ char *xstrvfmt(const char *fmt, va_list ap);
 __attribute__((format (printf, 1, 2)))
 char *xstrfmt(const char *fmt, ...);
 
+int starts_with(const char *str, const char *prefix);
+int istarts_with(const char *str, const char *prefix);
+
+/*
+ * If the string "str" is the same as the string in "prefix", then the "arg"
+ * parameter is set to the "def" parameter and 1 is returned.
+ * If the string "str" begins with the string found in "prefix" and then a
+ * "=" sign, then the "arg" parameter is set to "str + strlen(prefix) + 1"
+ * (i.e., to the point in the string right after the prefix and the "=" sign),
+ * and 1 is returned.
+ *
+ * Otherwise, return 0 and leave "arg" untouched.
+ *
+ * When we accept both a "--key" and a "--key=<val>" option, this function
+ * can be used instead of !strcmp(arg, "--key") and then
+ * skip_prefix(arg, "--key=", &arg) to parse such an option.
+ */
+int skip_to_optional_arg_default(const char *str, const char *prefix,
+                                const char **arg, const char *def);
+
+static inline int skip_to_optional_arg(const char *str, const char *prefix,
+                                      const char **arg)
+{
+       return skip_to_optional_arg_default(str, prefix, arg, "");
+}
+
+static inline int ends_with(const char *str, const char *suffix)
+{
+       size_t len;
+       return strip_suffix(str, suffix, &len);
+}
+
 #endif /* STRBUF_H */
index 7b2f8b2b9384b8c9c516be005c9b9f811348b0c7..10adf625b2e7ab23ffde1c8c826f6c10691792d8 100644 (file)
@@ -1,10 +1,13 @@
 /*
  * Copyright (c) 2011, Google Inc.
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "convert.h"
+#include "environment.h"
 #include "streaming.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "replace-object.h"
 #include "packfile.h"
 
@@ -38,7 +41,7 @@ struct git_istream {
 
        union {
                struct {
-                       char *buf; /* from read_object() */
+                       char *buf; /* from oid_object_info_extended() */
                        unsigned long read_ptr;
                } incore;
 
@@ -388,12 +391,17 @@ static ssize_t read_istream_incore(struct git_istream *st, char *buf, size_t sz)
 static int open_istream_incore(struct git_istream *st, struct repository *r,
                               const struct object_id *oid, enum object_type *type)
 {
-       st->u.incore.buf = read_object_file_extended(r, oid, type, &st->size, 0);
+       struct object_info oi = OBJECT_INFO_INIT;
+
        st->u.incore.read_ptr = 0;
        st->close = close_istream_incore;
        st->read = read_istream_incore;
 
-       return st->u.incore.buf ? 0 : -1;
+       oi.typep = type;
+       oi.sizep = &st->size;
+       oi.contentp = (void **)&st->u.incore.buf;
+       return oid_object_info_extended(r, oid, &oi,
+                                       OBJECT_INFO_DIE_IF_CORRUPT);
 }
 
 /*****************************************************************************
index 5e4e6acfd0dc947bcb92680fbae71de1fc486a78..bd27f59e5764aec64cd1cf927baf213fcec4d893 100644 (file)
@@ -3,10 +3,12 @@
  */
 #ifndef STREAMING_H
 #define STREAMING_H 1
-#include "cache.h"
+
+#include "object.h"
 
 /* opaque */
 struct git_istream;
+struct stream_filter;
 
 struct git_istream *open_istream(struct repository *, const struct object_id *,
                                 enum object_type *, unsigned long *,
index 549fc416d68ea4de2d1eb6f513af7bf66f37ccb9..954569f381d8d7ea2e1c6f1333b1e8379ffe2fe8 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "string-list.h"
 
 void string_list_init_nodup(struct string_list *list)
@@ -156,7 +156,7 @@ void filter_string_list(struct string_list *list, int free_util,
        list->nr = dst;
 }
 
-static int item_is_not_empty(struct string_list_item *item, void *unused)
+static int item_is_not_empty(struct string_list_item *item, void *data UNUSED)
 {
        return *item->string != '\0';
 }
@@ -202,6 +202,15 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
        list->nr = list->alloc = 0;
 }
 
+void string_list_setlen(struct string_list *list, size_t nr)
+{
+       if (list->strdup_strings)
+               BUG("cannot setlen a string_list which owns its entries");
+       if (nr > list->nr)
+               BUG("cannot grow a string_list with setlen");
+       list->nr = nr;
+}
+
 struct string_list_item *string_list_append_nodup(struct string_list *list,
                                                  char *string)
 {
@@ -300,7 +309,7 @@ int string_list_split(struct string_list *list, const char *string,
 }
 
 int string_list_split_in_place(struct string_list *list, char *string,
-                              int delim, int maxsplit)
+                              const char *delim, int maxsplit)
 {
        int count = 0;
        char *p = string, *end;
@@ -314,7 +323,7 @@ int string_list_split_in_place(struct string_list *list, char *string,
                        string_list_append(list, p);
                        return count;
                }
-               end = strchr(p, delim);
+               end = strpbrk(p, delim);
                if (end) {
                        *end = '\0';
                        string_list_append(list, p);
index d5a744e1438600080b9970e3f7cdedbe7f2a3708..122b3186419880403da702c928724aaefd8e34f3 100644 (file)
@@ -134,6 +134,16 @@ typedef void (*string_list_clear_func_t)(void *p, const char *str);
 /** Call a custom clear function on each util pointer */
 void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
 
+/*
+ * Set the length of a string_list to `nr`, provided that (a) `list`
+ * does not own its own storage, and (b) that `nr` is no larger than
+ * `list->nr`.
+ *
+ * Useful when "shrinking" `list` to write over existing entries that
+ * are no longer used without reallocating.
+ */
+void string_list_setlen(struct string_list *list, size_t nr);
+
 /**
  * Apply `func` to each item. If `func` returns nonzero, the
  * iteration aborts and the return value is propagated.
@@ -141,7 +151,12 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
 int for_each_string_list(struct string_list *list,
                         string_list_each_func_t func, void *cb_data);
 
-/** Iterate over each item, as a macro. */
+/**
+ * Iterate over each item, as a macro.
+ *
+ * Be sure that 'list' is non-NULL. The macro cannot perform NULL
+ * checks due to -Werror=address errors.
+ */
 #define for_each_string_list_item(item,list)            \
        for (item = (list)->items;                      \
             item && item < (list)->items + (list)->nr; \
@@ -265,5 +280,5 @@ int string_list_split(struct string_list *list, const char *string,
  * list->strdup_strings must *not* be set.
  */
 int string_list_split_in_place(struct string_list *list, char *string,
-                              int delim, int maxsplit);
+                              const char *delim, int maxsplit);
 #endif /* STRING_LIST_H */
index 61a76ce6cb920f33744e82111c800da8439ed975..178f4f37480e1104e329674964a5d5c24d5ba7e5 100644 (file)
--- a/strvec.c
+++ b/strvec.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "strvec.h"
 #include "strbuf.h"
 
index 6d4232294dbee7ad2928b0ac12e1860dcf04a12d..1daf5a975254b9b9ea286c5486f4d07f3874bb26 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Generic implementation of background process infrastructure.
  */
+#include "git-compat-util.h"
 #include "sub-process.h"
 #include "sigchain.h"
 #include "pkt-line.h"
index e85f21fa1a7c2bc59f032b5b2a2cffd6cab771a5..6a61638a8ace0b760e1254bcbea14061c3843a25 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef SUBPROCESS_H
 #define SUBPROCESS_H
 
-#include "git-compat-util.h"
 #include "hashmap.h"
 #include "run-command.h"
 
index cd7ee236a120bc99d91ec615e94a6cd4240937f0..f4dd482abc95e7c1668623be7c24ec163745e189 100644 (file)
@@ -1,12 +1,18 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "path.h"
 #include "repository.h"
 #include "config.h"
 #include "submodule-config.h"
 #include "submodule.h"
 #include "strbuf.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "parse-options.h"
+#include "thread-utils.h"
 #include "tree-walk.h"
 
 /*
@@ -298,11 +304,14 @@ static int parse_fetch_recurse(const char *opt, const char *arg,
        }
 }
 
-int parse_submodule_fetchjobs(const char *var, const char *value)
+int parse_submodule_fetchjobs(const char *var, const char *value,
+                             const struct key_value_info *kvi)
 {
-       int fetchjobs = git_config_int(var, value);
+       int fetchjobs = git_config_int(var, value, kvi);
        if (fetchjobs < 0)
                die(_("negative values not allowed for submodule.fetchJobs"));
+       if (!fetchjobs)
+               fetchjobs = online_cpus();
        return fetchjobs;
 }
 
@@ -418,7 +427,8 @@ struct parse_config_parameter {
  * config store (.git/config, etc).  Callers are responsible for
  * checking for overrides in the main config store when appropriate.
  */
-static int parse_config(const char *var, const char *value, void *data)
+static int parse_config(const char *var, const char *value,
+                       const struct config_context *ctx UNUSED, void *data)
 {
        struct parse_config_parameter *me = data;
        struct submodule *submodule;
@@ -506,7 +516,9 @@ static int parse_config(const char *var, const char *value, void *data)
                        submodule->recommend_shallow =
                                git_config_bool(var, value);
        } else if (!strcmp(item.buf, "branch")) {
-               if (!me->overwrite && submodule->branch)
+               if (!value)
+                       ret = config_error_nonbool(var);
+               else if (!me->overwrite && submodule->branch)
                        warn_multiple_config(me->treeish_name, submodule->name,
                                             "branch");
                else {
@@ -533,7 +545,7 @@ static int gitmodule_oid_from_commit(const struct object_id *treeish_name,
        }
 
        strbuf_addf(rev, "%s:.gitmodules", oid_to_hex(treeish_name));
-       if (get_oid(rev->buf, gitmodules_oid) >= 0)
+       if (repo_get_oid(the_repository, rev->buf, gitmodules_oid) >= 0)
                ret = 1;
 
        return ret;
@@ -586,7 +598,8 @@ static const struct submodule *config_from(struct submodule_cache *cache,
        if (submodule)
                goto out;
 
-       config = read_object_file(&oid, &type, &config_size);
+       config = repo_read_object_file(the_repository, &oid, &type,
+                                      &config_size);
        if (!config || type != OBJ_BLOB)
                goto out;
 
@@ -596,7 +609,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
        parameter.gitmodules_oid = &oid;
        parameter.overwrite = 0;
        git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
-                       config, config_size, &parameter, NULL);
+                           config, config_size, &parameter, CONFIG_SCOPE_UNKNOWN, NULL);
        strbuf_release(&rev);
        free(config);
 
@@ -650,7 +663,6 @@ static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void
                        config_source.file = file;
                } else if (repo_get_oid(repo, GITMODULES_INDEX, &oid) >= 0 ||
                           repo_get_oid(repo, GITMODULES_HEAD, &oid) >= 0) {
-                       config_source.repo = repo;
                        config_source.blob = oidstr = xstrdup(oid_to_hex(&oid));
                        if (repo != the_repository)
                                add_submodule_odb_by_path(repo->objects->odb->path);
@@ -658,7 +670,7 @@ static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void
                        goto out;
                }
 
-               config_with_options(fn, data, &config_source, &opts);
+               config_with_options(fn, data, &config_source, repo, &opts);
 
 out:
                free(oidstr);
@@ -666,7 +678,8 @@ out:
        }
 }
 
-static int gitmodules_cb(const char *var, const char *value, void *data)
+static int gitmodules_cb(const char *var, const char *value,
+                        const struct config_context *ctx, void *data)
 {
        struct repository *repo = data;
        struct parse_config_parameter parameter;
@@ -676,7 +689,7 @@ static int gitmodules_cb(const char *var, const char *value, void *data)
        parameter.gitmodules_oid = null_oid();
        parameter.overwrite = 1;
 
-       return parse_config(var, value, &parameter);
+       return parse_config(var, value, ctx, &parameter);
 }
 
 void repo_read_gitmodules(struct repository *repo, int skip_if_read)
@@ -704,7 +717,8 @@ void gitmodules_config_oid(const struct object_id *commit_oid)
 
        if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
                git_config_from_blob_oid(gitmodules_cb, rev.buf,
-                                        the_repository, &oid, the_repository);
+                                        the_repository, &oid, the_repository,
+                                        CONFIG_SCOPE_UNKNOWN);
        }
        strbuf_release(&rev);
 
@@ -793,7 +807,9 @@ void submodule_free(struct repository *r)
                submodule_cache_clear(r->submodule_cache);
 }
 
-static int config_print_callback(const char *var, const char *value, void *cb_data)
+static int config_print_callback(const char *var, const char *value,
+                                const struct config_context *ctx UNUSED,
+                                void *cb_data)
 {
        char *wanted_key = cb_data;
 
@@ -835,13 +851,15 @@ struct fetch_config {
        int *recurse_submodules;
 };
 
-static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
+static int gitmodules_fetch_config(const char *var, const char *value,
+                                  const struct config_context *ctx,
+                                  void *cb)
 {
        struct fetch_config *config = cb;
        if (!strcmp(var, "submodule.fetchjobs")) {
                if (config->max_children)
                        *(config->max_children) =
-                               parse_submodule_fetchjobs(var, value);
+                               parse_submodule_fetchjobs(var, value, ctx->kvi);
                return 0;
        } else if (!strcmp(var, "fetch.recursesubmodules")) {
                if (config->recurse_submodules)
@@ -863,11 +881,12 @@ void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules)
 }
 
 static int gitmodules_update_clone_config(const char *var, const char *value,
+                                         const struct config_context *ctx,
                                          void *cb)
 {
        int *max_jobs = cb;
        if (!strcmp(var, "submodule.fetchjobs"))
-               *max_jobs = parse_submodule_fetchjobs(var, value);
+               *max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
        return 0;
 }
 
index 28a8ca6bf46845906cb4bc2071a594a383eefc72..958f320ac6ce08a0b0ac856415a173d5fa7c2600 100644 (file)
@@ -1,11 +1,8 @@
 #ifndef SUBMODULE_CONFIG_CACHE_H
 #define SUBMODULE_CONFIG_CACHE_H
 
-#include "cache.h"
 #include "config.h"
-#include "hashmap.h"
 #include "submodule.h"
-#include "strbuf.h"
 #include "tree-walk.h"
 
 /**
@@ -51,7 +48,8 @@ struct repository;
 
 void submodule_cache_free(struct submodule_cache *cache);
 
-int parse_submodule_fetchjobs(const char *var, const char *value);
+int parse_submodule_fetchjobs(const char *var, const char *value,
+                             const struct key_value_info *kvi);
 int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
 struct option;
 int option_fetch_parse_recurse_submodules(const struct option *opt,
index bf7a2c79183e17eb3b6c4c8487938de6c01be565..213da79f66116f3db835036140affb98406a934a 100644 (file)
@@ -1,5 +1,5 @@
-
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "repository.h"
 #include "config.h"
 #include "submodule-config.h"
@@ -7,6 +7,9 @@
 #include "dir.h"
 #include "diff.h"
 #include "commit.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "revision.h"
 #include "run-command.h"
 #include "diffcore.h"
 #include "string-list.h"
 #include "oid-array.h"
 #include "strvec.h"
-#include "blob.h"
 #include "thread-utils.h"
-#include "quote.h"
+#include "path.h"
 #include "remote.h"
 #include "worktree.h"
 #include "parse-options.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "commit-reach.h"
-#include "shallow.h"
+#include "read-cache-ll.h"
+#include "setup.h"
+#include "trace2.h"
 
 static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
 static int initialized_fetch_ref_tips;
@@ -65,7 +71,7 @@ int is_writing_gitmodules_ok(void)
 {
        struct object_id oid;
        return file_exists(GITMODULES_FILE) ||
-               (get_oid(GITMODULES_INDEX, &oid) < 0 && get_oid(GITMODULES_HEAD, &oid) < 0);
+               (repo_get_oid(the_repository, GITMODULES_INDEX, &oid) < 0 && repo_get_oid(the_repository, GITMODULES_HEAD, &oid) < 0);
 }
 
 /*
@@ -274,8 +280,7 @@ int is_tree_submodule_active(struct repository *repo,
        free(key);
 
        /* submodule.active is set */
-       sl = repo_config_get_value_multi(repo, "submodule.active");
-       if (sl) {
+       if (!repo_config_get_string_multi(repo, "submodule.active", &sl)) {
                struct pathspec ps;
                struct strvec args = STRVEC_INIT;
                const struct string_list_item *item;
@@ -832,7 +837,7 @@ static void changed_submodule_data_clear(struct changed_submodule_data *cs_data)
 }
 
 static void collect_changed_submodules_cb(struct diff_queue_struct *q,
-                                         struct diff_options *options,
+                                         struct diff_options *options UNUSED,
                                          void *data)
 {
        struct collect_changed_submodules_cb_data *me = data;
@@ -1130,6 +1135,12 @@ static int push_submodule(const char *path,
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
                struct child_process cp = CHILD_PROCESS_INIT;
                strvec_push(&cp.args, "push");
+               /*
+                * When recursing into a submodule, treat any "only" configurations as "on-
+                * demand", since "only" would not work (we need all submodules to be pushed
+                * in order to be able to push the superproject).
+                */
+               strvec_push(&cp.args, "--recurse-submodules=only-is-on-demand");
                if (dry_run)
                        strvec_push(&cp.args, "--dry-run");
 
@@ -1619,7 +1630,7 @@ get_fetch_task_from_changed(struct submodule_parallel_fetch *spf,
                if (!task->repo) {
                        strbuf_addf(err, _("Could not access submodule '%s' at commit %s\n"),
                                    cs_data->path,
-                                   find_unique_abbrev(cs_data->super_oid, DEFAULT_ABBREV));
+                                   repo_find_unique_abbrev(the_repository, cs_data->super_oid, DEFAULT_ABBREV));
 
                        fetch_task_release(task);
                        free(task);
@@ -1630,8 +1641,8 @@ get_fetch_task_from_changed(struct submodule_parallel_fetch *spf,
                        strbuf_addf(err,
                                    _("Fetching submodule %s%s at commit %s\n"),
                                    spf->prefix, task->sub->path,
-                                   find_unique_abbrev(cs_data->super_oid,
-                                                      DEFAULT_ABBREV));
+                                   repo_find_unique_abbrev(the_repository, cs_data->super_oid,
+                                                           DEFAULT_ABBREV));
 
                spf->changed_count++;
                /*
@@ -1733,7 +1744,7 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
        return 0;
 }
 
-static int fetch_start_failure(struct strbuf *err,
+static int fetch_start_failure(struct strbuf *err UNUSED,
                               void *cb, void *task_cb)
 {
        struct submodule_parallel_fetch *spf = cb;
@@ -1754,7 +1765,7 @@ static int commit_missing_in_sub(const struct object_id *oid, void *data)
        return type != OBJ_COMMIT;
 }
 
-static int fetch_finish(int retvalue, struct strbuf *err,
+static int fetch_finish(int retvalue, struct strbuf *err UNUSED,
                        void *cb, void *task_cb)
 {
        struct submodule_parallel_fetch *spf = cb;
@@ -1819,6 +1830,17 @@ int fetch_submodules(struct repository *r,
 {
        int i;
        struct submodule_parallel_fetch spf = SPF_INIT;
+       const struct run_process_parallel_opts opts = {
+               .tr2_category = "submodule",
+               .tr2_label = "parallel/fetch",
+
+               .processes = max_parallel_jobs,
+
+               .get_next_task = get_next_submodule,
+               .start_failure = fetch_start_failure,
+               .task_finished = fetch_finish,
+               .data = &spf,
+       };
 
        spf.r = r;
        spf.command_line_option = command_line_option;
@@ -1840,12 +1862,7 @@ int fetch_submodules(struct repository *r,
 
        calculate_changed_submodule_paths(r, &spf.changed_submodule_names);
        string_list_sort(&spf.changed_submodule_names);
-       run_processes_parallel_tr2(max_parallel_jobs,
-                                  get_next_submodule,
-                                  fetch_start_failure,
-                                  fetch_finish,
-                                  &spf,
-                                  "submodule", "parallel/fetch");
+       run_processes_parallel(&opts);
 
        if (spf.submodules_with_errors.len > 0)
                fprintf(stderr, _("Errors during submodule fetch:\n%s"),
@@ -2042,14 +2059,6 @@ void submodule_unset_core_worktree(const struct submodule *sub)
        strbuf_release(&config_path);
 }
 
-static const char *get_super_prefix_or_empty(void)
-{
-       const char *s = get_super_prefix();
-       if (!s)
-               s = "";
-       return s;
-}
-
 static int submodule_has_dirty_index(const struct submodule *sub)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -2068,7 +2077,7 @@ static int submodule_has_dirty_index(const struct submodule *sub)
        return finish_command(&cp);
 }
 
-static void submodule_reset_index(const char *path)
+static void submodule_reset_index(const char *path, const char *super_prefix)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
        prepare_submodule_repo_env(&cp.env);
@@ -2077,10 +2086,10 @@ static void submodule_reset_index(const char *path)
        cp.no_stdin = 1;
        cp.dir = path;
 
-       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
-                    get_super_prefix_or_empty(), path);
        /* TODO: determine if this might overwright untracked files */
        strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
+       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
+                    (super_prefix ? super_prefix : ""), path);
 
        strvec_push(&cp.args, empty_tree_oid_hex());
 
@@ -2093,10 +2102,9 @@ static void submodule_reset_index(const char *path)
  * For edge cases (a submodule coming into existence or removing a submodule)
  * pass NULL for old or new respectively.
  */
-int submodule_move_head(const char *path,
-                        const char *old_head,
-                        const char *new_head,
-                        unsigned flags)
+int submodule_move_head(const char *path, const char *super_prefix,
+                       const char *old_head, const char *new_head,
+                       unsigned flags)
 {
        int ret = 0;
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -2134,7 +2142,7 @@ int submodule_move_head(const char *path,
                if (old_head) {
                        if (!submodule_uses_gitfile(path))
                                absorb_git_dir_into_superproject(path,
-                                       ABSORB_GITDIR_RECURSE_SUBMODULES);
+                                                                super_prefix);
                } else {
                        struct strbuf gitdir = STRBUF_INIT;
                        submodule_name_to_gitdir(&gitdir, the_repository,
@@ -2143,7 +2151,7 @@ int submodule_move_head(const char *path,
                        strbuf_release(&gitdir);
 
                        /* make sure the index is clean as well */
-                       submodule_reset_index(path);
+                       submodule_reset_index(path, super_prefix);
                }
 
                if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
@@ -2161,9 +2169,9 @@ int submodule_move_head(const char *path,
        cp.no_stdin = 1;
        cp.dir = path;
 
-       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
-                    get_super_prefix_or_empty(), path);
        strvec_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
+       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
+                    (super_prefix ? super_prefix : ""), path);
 
        if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
                strvec_push(&cp.args, "-n");
@@ -2263,7 +2271,8 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
  * Embeds a single submodules git directory into the superprojects git dir,
  * non recursively.
  */
-static void relocate_single_git_dir_into_superproject(const char *path)
+static void relocate_single_git_dir_into_superproject(const char *path,
+                                                     const char *super_prefix)
 {
        char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
        struct strbuf new_gitdir = STRBUF_INIT;
@@ -2293,7 +2302,7 @@ static void relocate_single_git_dir_into_superproject(const char *path)
        real_new_git_dir = real_pathdup(new_gitdir.buf, 1);
 
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
-               get_super_prefix_or_empty(), path,
+               super_prefix ? super_prefix : "", path,
                real_old_git_dir, real_new_git_dir);
 
        relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
@@ -2304,13 +2313,32 @@ static void relocate_single_git_dir_into_superproject(const char *path)
        strbuf_release(&new_gitdir);
 }
 
+static void absorb_git_dir_into_superproject_recurse(const char *path,
+                                                    const char *super_prefix)
+{
+
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       cp.dir = path;
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       strvec_pushl(&cp.args, "submodule--helper",
+                    "absorbgitdirs", NULL);
+       strvec_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ?
+                    super_prefix : "", path);
+
+       prepare_submodule_repo_env(&cp.env);
+       if (run_command(&cp))
+               die(_("could not recurse into submodule '%s'"), path);
+}
+
 /*
  * Migrate the git directory of the submodule given by path from
  * having its git directory within the working tree to the git dir nested
  * in its superprojects git dir under modules/.
  */
 void absorb_git_dir_into_superproject(const char *path,
-                                     unsigned flags)
+                                     const char *super_prefix)
 {
        int err_code;
        const char *sub_git_dir;
@@ -2352,36 +2380,14 @@ void absorb_git_dir_into_superproject(const char *path,
                char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
 
                if (!starts_with(real_sub_git_dir, real_common_git_dir))
-                       relocate_single_git_dir_into_superproject(path);
+                       relocate_single_git_dir_into_superproject(path, super_prefix);
 
                free(real_sub_git_dir);
                free(real_common_git_dir);
        }
        strbuf_release(&gitdir);
 
-       if (flags & ABSORB_GITDIR_RECURSE_SUBMODULES) {
-               struct child_process cp = CHILD_PROCESS_INIT;
-               struct strbuf sb = STRBUF_INIT;
-
-               if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES)
-                       BUG("we don't know how to pass the flags down?");
-
-               strbuf_addstr(&sb, get_super_prefix_or_empty());
-               strbuf_addstr(&sb, path);
-               strbuf_addch(&sb, '/');
-
-               cp.dir = path;
-               cp.git_cmd = 1;
-               cp.no_stdin = 1;
-               strvec_pushl(&cp.args, "--super-prefix", sb.buf,
-                            "submodule--helper",
-                            "absorbgitdirs", NULL);
-               prepare_submodule_repo_env(&cp.env);
-               if (run_command(&cp))
-                       die(_("could not recurse into submodule '%s'"), path);
-
-               strbuf_release(&sb);
-       }
+       absorb_git_dir_into_superproject_recurse(path, super_prefix);
 }
 
 int get_superproject_working_tree(struct strbuf *buf)
index 6a9fec6de1159f0389df2391c2d18e8d5fd7d297..c55a25ca37d2425d398f59c98ff177c8dfa6fdaf 100644 (file)
@@ -150,9 +150,8 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
 
 #define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
 #define SUBMODULE_MOVE_HEAD_FORCE   (1<<1)
-int submodule_move_head(const char *path,
-                       const char *old,
-                       const char *new_head,
+int submodule_move_head(const char *path, const char *super_prefix,
+                       const char *old_head, const char *new_head,
                        unsigned flags);
 
 void submodule_unset_core_worktree(const struct submodule *sub);
@@ -164,9 +163,8 @@ void submodule_unset_core_worktree(const struct submodule *sub);
  */
 void prepare_submodule_repo_env(struct strvec *env);
 
-#define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0)
 void absorb_git_dir_into_superproject(const char *path,
-                                     unsigned flags);
+                                     const char *super_prefix);
 
 /*
  * Return the absolute path of the working tree of the superproject, which this
index c667baa949b685c6c78c41eb8d272410c7574fe4..b29e340c2da43e9ac35adcda05322b1e068017ca 100644 (file)
@@ -1,4 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "setup.h"
+#include "symlinks.h"
 
 static int threaded_check_leading_path(struct cache_def *cache, const char *name,
                                       int len, int warn_on_lstat_err);
diff --git a/symlinks.h b/symlinks.h
new file mode 100644 (file)
index 0000000..7ae3d5b
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef SYMLINKS_H
+#define SYMLINKS_H
+
+#include "strbuf.h"
+
+struct cache_def {
+       struct strbuf path;
+       int flags;
+       int track_flags;
+       int prefix_len_stat_func;
+};
+#define CACHE_DEF_INIT { \
+       .path = STRBUF_INIT, \
+}
+static inline void cache_def_clear(struct cache_def *cache)
+{
+       strbuf_release(&cache->path);
+}
+
+int has_symlink_leading_path(const char *name, int len);
+int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
+int check_leading_path(const char *name, int len, int warn_on_lstat_err);
+int has_dirs_only_path(const char *name, int len, int prefix_len);
+void invalidate_lstat_cache(void);
+void schedule_dir_for_removal(const char *name, int len);
+void remove_scheduled_dirs(void);
+
+#endif /* SYMLINKS_H */
index 9930e283512b663133161fd474fc2101c19ec283..b9cea1795d84e3c4e334a89822e059e8d67512ef 100644 (file)
@@ -22,3 +22,4 @@ t[0-9][0-9][0-9][0-9]/* -whitespace
 /t7500/* eol=lf
 /t8005/*.txt eol=lf
 /t9*/*.dump eol=lf
+/t0040*.sh whitespace=-indent-with-non-tab
index 882782a519c97b65d2a6d487ac6849a540015fe4..b7a6fefe28d614830a63b0b13e6c597cfd6f9f52 100644 (file)
@@ -17,6 +17,7 @@ TAR ?= $(TAR)
 RM ?= rm -f
 PROVE ?= prove
 DEFAULT_TEST_TARGET ?= test
+DEFAULT_UNIT_TEST_TARGET ?= unit-tests-raw
 TEST_LINT ?= test-lint
 
 ifdef TEST_OUTPUT_DIRECTORY
@@ -41,11 +42,12 @@ TPERF = $(sort $(wildcard perf/p[0-9][0-9][0-9][0-9]-*.sh))
 TINTEROP = $(sort $(wildcard interop/i[0-9][0-9][0-9][0-9]-*.sh))
 CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
 CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
+UNIT_TESTS = $(sort $(filter-out %.pdb unit-tests/bin/t-basic%,$(wildcard unit-tests/bin/t-*)))
 
 # `test-chainlint` (which is a dependency of `test-lint`, `test` and `prove`)
 # checks all tests in all scripts via a single invocation, so tell individual
-# scripts not to "chainlint" themselves
-CHAINLINTSUPPRESS = GIT_TEST_CHAIN_LINT=0 && export GIT_TEST_CHAIN_LINT &&
+# scripts not to run the external "chainlint.pl" script themselves
+CHAINLINTSUPPRESS = GIT_TEST_EXT_CHAIN_LINT=0 && export GIT_TEST_EXT_CHAIN_LINT &&
 
 all: $(DEFAULT_TEST_TARGET)
 
@@ -65,6 +67,17 @@ prove: pre-clean check-chainlint $(TEST_LINT)
 $(T):
        @echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
+$(UNIT_TESTS):
+       @echo "*** $@ ***"; $@
+
+.PHONY: unit-tests unit-tests-raw unit-tests-prove
+unit-tests: $(DEFAULT_UNIT_TEST_TARGET)
+
+unit-tests-raw: $(UNIT_TESTS)
+
+unit-tests-prove:
+       @echo "*** prove - unit tests ***"; $(PROVE) $(GIT_PROVE_OPTS) $(UNIT_TESTS)
+
 pre-clean:
        $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
 
@@ -90,20 +103,12 @@ check-chainlint:
                echo "# chainlint: $(CHAINLINTTMP_SQ)/tests" && \
                for i in $(CHAINLINTTESTS); do \
                        echo "# chainlint: $$i" && \
-                       sed -e '/^[     ]*$$/d' chainlint/$$i.expect; \
+                       cat chainlint/$$i.expect; \
                done \
        } >'$(CHAINLINTTMP_SQ)'/expect && \
        $(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
-               grep -v '^[     ]*$$' >'$(CHAINLINTTMP_SQ)'/actual && \
-       if test -f ../GIT-BUILD-OPTIONS; then \
-               . ../GIT-BUILD-OPTIONS; \
-       fi && \
-       if test -x ../git$$X; then \
-               DIFFW="../git$$X --no-pager diff -w --no-index"; \
-       else \
-               DIFFW="diff -w -u"; \
-       fi && \
-       $$DIFFW '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
+               sed -e 's/^[1-9][0-9]* //' >'$(CHAINLINTTMP_SQ)'/actual && \
+       diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
 
 test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
        test-lint-filenames
@@ -140,9 +145,7 @@ aggregate-results-and-cleanup: $(T)
        $(MAKE) clean
 
 aggregate-results:
-       for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
-               echo "$$f"; \
-       done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
+       @'$(SHELL_PATH_SQ)' ./aggregate-results.sh '$(TEST_RESULTS_DIRECTORY_SQ)'
 
 valgrind:
        $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
@@ -151,4 +154,4 @@ perf:
        $(MAKE) -C perf/ all
 
 .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \
-       check-chainlint clean-chainlint test-chainlint
+       check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)
index 979b2d4833d983ef50a71c3a9394bd9c8b6e4051..621d3b8c095441a8a8985b7f12363e26f8ab4d98 100644 (file)
--- a/t/README
+++ b/t/README
@@ -32,7 +32,7 @@ the tests.
     ok 2 - plain with GIT_WORK_TREE
     ok 3 - plain bare
 
-Since the tests all output TAP (see http://testanything.org) they can
+Since the tests all output TAP (see https://testanything.org) they can
 be run with any TAP harness. Here's an example of parallel testing
 powered by a recent version of prove(1):
 
@@ -262,8 +262,8 @@ The argument for --run, <test-selector>, is a list of description
 substrings or globs or individual test numbers or ranges with an
 optional negation prefix (of '!') that define what tests in a test
 suite to include (or exclude, if negated) in the run.  A range is two
-numbers separated with a dash and matches a range of tests with both
-ends been included.  You may omit the first or the second number to
+numbers separated with a dash and specifies an inclusive range of tests
+to run.  You may omit the first or the second number to
 mean "from the first test" or "up to the very last test" respectively.
 
 The argument to --run is split on commas into separate strings,
@@ -274,10 +274,10 @@ text that you want to match includes a comma, use the glob character
 on all tests that match either the glob *rebase* or the glob
 *merge?cherry-pick*.
 
-If --run starts with an unprefixed number or range the initial
-set of tests to run is empty. If the first item starts with '!'
+If --run starts with an unprefixed number or range, the initial
+set of tests to run is empty.  If the first item starts with '!',
 all the tests are added to the initial set.  After initial set is
-determined every test number or range is added or excluded from
+determined, every test number or range is added or excluded from
 the set one by one, from left to right.
 
 For example, to run only tests up to a specific test (21), one
@@ -442,6 +442,10 @@ GIT_TEST_INDEX_VERSION=<n> exercises the index read/write code path
 for the index version specified.  Can be set to any valid version
 (currently 2, 3, or 4).
 
+GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=<boolean> if enabled will
+use the boundary-based bitmap traversal algorithm. See the documentation
+of `pack.useBitmapBoundaryTraversal` for more details.
+
 GIT_TEST_PACK_SPARSE=<boolean> if disabled will default the pack-objects
 builtin to use the non-sparse object walk. This can still be overridden by
 the --sparse command-line argument.
@@ -449,10 +453,6 @@ the --sparse command-line argument.
 GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
 by overriding the minimum number of cache entries required per thread.
 
-GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when false, disables the
-built-in version of git add -i. See 'add.interactive.useBuiltin' in
-git-config(1).
-
 GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
 of the index for the whole test suite by bypassing the default number of
 cache entries and thread minimums. Setting this to 1 will make the
@@ -479,7 +479,10 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to
 use in the test scripts. Recognized values for <hash-algo> are "sha1"
 and "sha256".
 
-GIT_TEST_WRITE_REV_INDEX=<boolean>, when true enables the
+GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format
+to use in the test scripts. Recognized values for <format> are "files".
+
+GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the
 'pack.writeReverseIndex' setting.
 
 GIT_TEST_SPARSE_INDEX=<boolean>, when true enables index writes to use the
@@ -579,11 +582,11 @@ This test harness library does the following things:
 
 Recommended style
 -----------------
-Here are some recommented styles when writing test case.
 
- - Keep test title the same line with test helper function itself.
+ - Keep the test_expect_* function call and test title on
+   the same line.
 
-   Take test_expect_success helper for example, write it like:
+   For example, with test_expect_success, write it like:
 
   test_expect_success 'test title' '
       ... test body ...
@@ -595,10 +598,9 @@ Here are some recommented styles when writing test case.
       'test title' \
       '... test body ...'
 
+ - End the line with an opening single quote.
 
- - End the line with a single quote.
-
- - Indent the body of here-document, and use "<<-" instead of "<<"
+ - Indent here-document bodies, and use "<<-" instead of "<<"
    to strip leading TABs used for indentation:
 
   test_expect_success 'test something' '
@@ -624,7 +626,7 @@ Here are some recommented styles when writing test case.
   '
 
  - Quote or escape the EOF delimiter that begins a here-document if
-   there is no parameter and other expansion in it, to signal readers
+   there is no parameter or other expansion in it, to signal readers
    that they can skim it more casually:
 
   cmd <<-\EOF
@@ -638,7 +640,7 @@ Do's & don'ts
 Here are a few examples of things you probably should and shouldn't do
 when writing tests.
 
-Here are the "do's:"
+The "do's:"
 
  - Put all code inside test_expect_success and other assertions.
 
@@ -888,7 +890,7 @@ see test-lib-functions.sh for the full list and their options.
    rare case where your test depends on more than one:
 
        test_expect_success PERL,PYTHON 'yo dawg' \
-           ' test $(perl -E 'print eval "1 +" . qx[python -c "print 2"]') == "4" '
+           ' test $(perl -E '\''print eval "1 +" . qx[python -c "print(2)"]'\'') = "4" '
 
  - test_expect_failure [<prereq>] <message> <script>
 
@@ -1102,6 +1104,12 @@ see test-lib-functions.sh for the full list and their options.
    the symbolic link in the file system and a part that does; then only
    the latter part need be protected by a SYMLINKS prerequisite (see below).
 
+ - test_path_is_executable
+
+   This tests whether a file is executable and prints an error message
+   if not. This must be used only under the POSIXPERM prerequisite
+   (see below).
+
  - test_oid_init
 
    This function loads facts and useful object IDs related to the hash
@@ -1231,8 +1239,8 @@ and it knows that the object ID of an empty tree is a certain
 because the things the very basic core test tries to achieve is
 to serve as a basis for people who are changing the Git internals
 drastically.  For these people, after making certain changes,
-not seeing failures from the basic test _is_ a failure.  And
-such drastic changes to the core Git that even changes these
+not seeing failures from the basic test _is_ a failure.  Any
+Git core changes so drastic that they change even these
 otherwise supposedly stable object IDs should be accompanied by
 an update to t0000-basic.sh.
 
@@ -1242,7 +1250,7 @@ knowledge of the core Git internals.  If all the test scripts
 hardcoded the object IDs like t0000-basic.sh does, that defeats
 the purpose of t0000-basic.sh, which is to isolate that level of
 validation in one place.  Your test also ends up needing
-updating when such a change to the internal happens, so do _not_
+an update whenever the internals change, so do _not_
 do it and leave the low level of validation to t0000-basic.sh.
 
 Test coverage
@@ -1273,7 +1281,7 @@ Devel::Cover module. To install it do:
    sudo aptitude install libdevel-cover-perl
 
    # From the CPAN with cpanminus
-   curl -L http://cpanmin.us | perl - --sudo --self-upgrade
+   curl -L https://cpanmin.us/ | perl - --sudo --self-upgrade
    cpanm --sudo Devel::Cover
 
 Then, at the top-level:
index 7f2b83bdc8181f6d653f40bf99a40bf8e7cc03f5..6e3bcc4aec7cb922b9fc9ed4469c79b1699c76cc 100755 (executable)
@@ -8,7 +8,7 @@ broken=0
 total=0
 missing_prereq=
 
-while read file
+for file in "$1"/t*-*.counts
 do
        while read type value
        do
index f1b9a6ce4daee67c7aadfd2a9d19cb1c4033224c..5e21e84f3884eb8c787ab82a5d1360d2b7cffb74 100644 (file)
@@ -72,6 +72,32 @@ test_expect_success 'blame 1 author' '
        check_count A 2
 '
 
+test_expect_success 'blame working copy' '
+       test_when_finished "git restore file" &&
+       echo "1A quick brown fox jumps over the" >file &&
+       echo "another lazy dog" >>file &&
+       check_count A 1 "Not Committed Yet" 1
+'
+
+test_expect_success 'blame with --contents' '
+       check_count --contents=file A 2
+'
+
+test_expect_success 'blame with --contents in a bare repo' '
+       git clone --bare . bare-contents.git &&
+       (
+               cd bare-contents.git &&
+               echo "1A quick brown fox jumps over the" >contents &&
+               check_count --contents=contents A 1
+       )
+'
+
+test_expect_success 'blame with --contents changed' '
+       echo "1A quick brown fox jumps over the" >contents &&
+       echo "another lazy dog" >>contents &&
+       check_count --contents=contents A 1 "External file (--contents)" 1
+'
+
 test_expect_success 'blame in a bare repo without starting commit' '
        git clone --bare . bare.git &&
        (
@@ -98,6 +124,10 @@ test_expect_success 'blame 2 authors' '
        check_count A 2 B 2
 '
 
+test_expect_success 'blame with --contents and revision' '
+       check_count -h testTag --contents=file A 2 "External file (--contents)" 2
+'
+
 test_expect_success 'setup B1 lines (branch1)' '
        git checkout -b branch1 main &&
        echo "3A slow green fox jumps into the" >>file &&
index 976db4b8a01b804e7e697c308f769233083a576c..556ee91a15b7c2124b42b0af8ef6481a787f5188 100755 (executable)
@@ -67,6 +67,7 @@ sub new {
        bless {
                parser => $parser,
                buff => $s,
+               lineno => 1,
                heretags => []
        } => $class;
 }
@@ -75,9 +76,12 @@ sub scan_heredoc_tag {
        my $self = shift @_;
        ${$self->{buff}} =~ /\G(-?)/gc;
        my $indented = $1;
-       my $tag = $self->scan_token();
+       my $token = $self->scan_token();
+       return "<<$indented" unless $token;
+       my $tag = $token->[0];
        $tag =~ s/['"\\]//g;
-       push(@{$self->{heretags}}, $indented ? "\t$tag" : "$tag");
+       $$token[0] = $indented ? "\t$tag" : "$tag";
+       push(@{$self->{heretags}}, $token);
        return "<<$indented$tag";
 }
 
@@ -95,7 +99,9 @@ sub scan_op {
 sub scan_sqstring {
        my $self = shift @_;
        ${$self->{buff}} =~ /\G([^']*'|.*\z)/sgc;
-       return "'" . $1;
+       my $s = $1;
+       $self->{lineno} += () = $s =~ /\n/sg;
+       return "'" . $s;
 }
 
 sub scan_dqstring {
@@ -113,7 +119,7 @@ sub scan_dqstring {
                if ($c eq '\\') {
                        $s .= '\\', last unless $$b =~ /\G(.)/sgc;
                        $c = $1;
-                       next if $c eq "\n"; # line splice
+                       $self->{lineno}++, next if $c eq "\n"; # line splice
                        # backslash escapes only $, `, ", \ in dq-string
                        $s .= '\\' unless $c =~ /^[\$`"\\]$/;
                        $s .= $c;
@@ -121,6 +127,7 @@ sub scan_dqstring {
                }
                die("internal error scanning dq-string '$c'\n");
        }
+       $self->{lineno} += () = $s =~ /\n/sg;
        return $s;
 }
 
@@ -135,6 +142,7 @@ sub scan_balanced {
                $depth--;
                last if $depth == 0;
        }
+       $self->{lineno} += () = $s =~ /\n/sg;
        return $s;
 }
 
@@ -149,7 +157,7 @@ sub scan_dollar {
        my $self = shift @_;
        my $b = $self->{buff};
        return $self->scan_balanced('(', ')') if $$b =~ /\G\((?=\()/gc; # $((...))
-       return '(' . join(' ', $self->scan_subst()) . ')' if $$b =~ /\G\(/gc; # $(...)
+       return '(' . join(' ', map {$_->[0]} $self->scan_subst()) . ')' if $$b =~ /\G\(/gc; # $(...)
        return $self->scan_balanced('{', '}') if $$b =~ /\G\{/gc; # ${...}
        return $1 if $$b =~ /\G(\w+)/gc; # $var
        return $1 if $$b =~ /\G([@*#?$!0-9-])/gc; # $*, $1, $$, etc.
@@ -161,8 +169,19 @@ sub swallow_heredocs {
        my $b = $self->{buff};
        my $tags = $self->{heretags};
        while (my $tag = shift @$tags) {
-               my $indent = $tag =~ s/^\t// ? '\\s*' : '';
-               $$b =~ /(?:\G|\n)$indent\Q$tag\E(?:\n|\z)/gc;
+               my $start = pos($$b);
+               my $indent = $$tag[0] =~ s/^\t// ? '\\s*' : '';
+               $$b =~ /(?:\G|\n)$indent\Q$$tag[0]\E(?:\n|\z)/gc;
+               if (pos($$b) > $start) {
+                       my $body = substr($$b, $start, pos($$b) - $start);
+                       $self->{lineno} += () = $body =~ /\n/sg;
+                       next;
+               }
+               push(@{$self->{parser}->{problems}}, ['UNCLOSED-HEREDOC', $tag]);
+               $$b =~ /(?:\G|\n).*\z/gc; # consume rest of input
+               my $body = substr($$b, $start, pos($$b) - $start);
+               $self->{lineno} += () = $body =~ /\n/sg;
+               last;
        }
 }
 
@@ -170,34 +189,37 @@ sub scan_token {
        my $self = shift @_;
        my $b = $self->{buff};
        my $token = '';
+       my ($start, $startln);
 RESTART:
+       $startln = $self->{lineno};
        $$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
-       return "\n" if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
+       $start = pos($$b) || 0;
+       $self->{lineno}++, return ["\n", $start, pos($$b), $startln, $startln] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
        while (1) {
                # slurp up non-special characters
                $token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc;
                # handle special characters
                last unless $$b =~ /\G(.)/sgc;
                my $c = $1;
-               last if $c =~ /^[ \t]$/; # whitespace ends token
+               pos($$b)--, last if $c =~ /^[ \t]$/; # whitespace ends token
                pos($$b)--, last if length($token) && $c =~ /^[;&|<>(){}\n]$/;
                $token .= $self->scan_sqstring(), next if $c eq "'";
                $token .= $self->scan_dqstring(), next if $c eq '"';
                $token .= $c . $self->scan_dollar(), next if $c eq '$';
-               $self->swallow_heredocs(), $token = $c, last if $c eq "\n";
+               $self->{lineno}++, $self->swallow_heredocs(), $token = $c, last if $c eq "\n";
                $token = $self->scan_op($c), last if $c =~ /^[;&|<>]$/;
                $token = $c, last if $c =~ /^[(){}]$/;
                if ($c eq '\\') {
                        $token .= '\\', last unless $$b =~ /\G(.)/sgc;
                        $c = $1;
-                       next if $c eq "\n" && length($token); # line splice
-                       goto RESTART if $c eq "\n"; # line splice
+                       $self->{lineno}++, next if $c eq "\n" && length($token); # line splice
+                       $self->{lineno}++, goto RESTART if $c eq "\n"; # line splice
                        $token .= '\\' . $c;
                        next;
                }
                die("internal error scanning character '$c'\n");
        }
-       return length($token) ? $token : undef;
+       return length($token) ? [$token, $start, pos($$b), $startln, $self->{lineno}] : undef;
 }
 
 # ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
@@ -239,14 +261,14 @@ sub stop_at {
        my ($self, $token) = @_;
        return 1 unless defined($token);
        my $stop = ${$self->{stop}}[-1] if @{$self->{stop}};
-       return defined($stop) && $token =~ $stop;
+       return defined($stop) && $token->[0] =~ $stop;
 }
 
 sub expect {
        my ($self, $expect) = @_;
        my $token = $self->next_token();
-       return $token if defined($token) && $token eq $expect;
-       push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token : "<end-of-input>") . "'\n");
+       return $token if defined($token) && $token->[0] eq $expect;
+       push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token->[0] : "<end-of-input>") . "'\n");
        $self->untoken($token) if defined($token);
        return ();
 }
@@ -255,7 +277,7 @@ sub optional_newlines {
        my $self = shift @_;
        my @tokens;
        while (my $token = $self->peek()) {
-               last unless $token eq "\n";
+               last unless $token->[0] eq "\n";
                push(@tokens, $self->next_token());
        }
        return @tokens;
@@ -278,7 +300,7 @@ sub parse_case_pattern {
        my @tokens;
        while (defined(my $token = $self->next_token())) {
                push(@tokens, $token);
-               last if $token eq ')';
+               last if $token->[0] eq ')';
        }
        return @tokens;
 }
@@ -293,13 +315,13 @@ sub parse_case {
             $self->optional_newlines());
        while (1) {
                my $token = $self->peek();
-               last unless defined($token) && $token ne 'esac';
+               last unless defined($token) && $token->[0] ne 'esac';
                push(@tokens,
                     $self->parse_case_pattern(),
                     $self->optional_newlines(),
                     $self->parse(qr/^(?:;;|esac)$/)); # item body
                $token = $self->peek();
-               last unless defined($token) && $token ne 'esac';
+               last unless defined($token) && $token->[0] ne 'esac';
                push(@tokens,
                     $self->expect(';;'),
                     $self->optional_newlines());
@@ -315,7 +337,7 @@ sub parse_for {
             $self->next_token(), # variable
             $self->optional_newlines());
        my $token = $self->peek();
-       if (defined($token) && $token eq 'in') {
+       if (defined($token) && $token->[0] eq 'in') {
                push(@tokens,
                     $self->expect('in'),
                     $self->optional_newlines());
@@ -339,11 +361,11 @@ sub parse_if {
                     $self->optional_newlines(),
                     $self->parse(qr/^(?:elif|else|fi)$/)); # if/elif body
                my $token = $self->peek();
-               last unless defined($token) && $token eq 'elif';
+               last unless defined($token) && $token->[0] eq 'elif';
                push(@tokens, $self->expect('elif'));
        }
        my $token = $self->peek();
-       if (defined($token) && $token eq 'else') {
+       if (defined($token) && $token->[0] eq 'else') {
                push(@tokens,
                     $self->expect('else'),
                     $self->optional_newlines(),
@@ -380,7 +402,7 @@ sub parse_bash_array_assignment {
        my @tokens = $self->expect('(');
        while (defined(my $token = $self->next_token())) {
                push(@tokens, $token);
-               last if $token eq ')';
+               last if $token->[0] eq ')';
        }
        return @tokens;
 }
@@ -398,29 +420,31 @@ sub parse_cmd {
        my $self = shift @_;
        my $cmd = $self->next_token();
        return () unless defined($cmd);
-       return $cmd if $cmd eq "\n";
+       return $cmd if $cmd->[0] eq "\n";
 
        my $token;
        my @tokens = $cmd;
-       if ($cmd eq '!') {
+       if ($cmd->[0] eq '!') {
                push(@tokens, $self->parse_cmd());
                return @tokens;
-       } elsif (my $f = $compound{$cmd}) {
+       } elsif (my $f = $compound{$cmd->[0]}) {
                push(@tokens, $self->$f());
-       } elsif (defined($token = $self->peek()) && $token eq '(') {
-               if ($cmd !~ /\w=$/) {
+       } elsif (defined($token = $self->peek()) && $token->[0] eq '(') {
+               if ($cmd->[0] !~ /\w=$/) {
                        push(@tokens, $self->parse_func());
                        return @tokens;
                }
-               $tokens[-1] .= join(' ', $self->parse_bash_array_assignment());
+               my @array = $self->parse_bash_array_assignment();
+               $tokens[-1]->[0] .= join(' ', map {$_->[0]} @array);
+               $tokens[-1]->[2] = $array[$#array][2] if @array;
        }
 
        while (defined(my $token = $self->next_token())) {
                $self->untoken($token), last if $self->stop_at($token);
                push(@tokens, $token);
-               last if $token =~ /^(?:[;&\n|]|&&|\|\|)$/;
+               last if $token->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
        }
-       push(@tokens, $self->next_token()) if $tokens[-1] ne "\n" && defined($token = $self->peek()) && $token eq "\n";
+       push(@tokens, $self->next_token()) if $tokens[-1]->[0] ne "\n" && defined($token = $self->peek()) && $token->[0] eq "\n";
        return @tokens;
 }
 
@@ -453,11 +477,18 @@ package TestParser;
 
 use base 'ShellParser';
 
+sub new {
+       my $class = shift @_;
+       my $self = $class->SUPER::new(@_);
+       $self->{problems} = [];
+       return $self;
+}
+
 sub find_non_nl {
        my $tokens = shift @_;
        my $n = shift @_;
        $n = $#$tokens if !defined($n);
-       $n-- while $n >= 0 && $$tokens[$n] eq "\n";
+       $n-- while $n >= 0 && $$tokens[$n]->[0] eq "\n";
        return $n;
 }
 
@@ -467,7 +498,7 @@ sub ends_with {
        for my $needle (reverse(@$needles)) {
                return undef if $n < 0;
                $n = find_non_nl($tokens, $n), next if $needle eq "\n";
-               return undef if $$tokens[$n] !~ $needle;
+               return undef if $$tokens[$n]->[0] !~ $needle;
                $n--;
        }
        return 1;
@@ -486,13 +517,13 @@ sub parse_loop_body {
        my $self = shift @_;
        my @tokens = $self->SUPER::parse_loop_body(@_);
        # did loop signal failure via "|| return" or "|| exit"?
-       return @tokens if !@tokens || grep(/^(?:return|exit|\$\?)$/, @tokens);
+       return @tokens if !@tokens || grep {$_->[0] =~ /^(?:return|exit|\$\?)$/} @tokens;
        # did loop upstream of a pipe signal failure via "|| echo 'impossible
        # text'" as the final command in the loop body?
        return @tokens if ends_with(\@tokens, [qr/^\|\|$/, "\n", qr/^echo$/, qr/^.+$/]);
        # flag missing "return/exit" handling explicit failure in loop body
        my $n = find_non_nl(\@tokens);
-       splice(@tokens, $n + 1, 0, '?!LOOP?!');
+       push(@{$self->{problems}}, ['LOOP', $tokens[$n]]);
        return @tokens;
 }
 
@@ -505,8 +536,13 @@ my @safe_endings = (
 
 sub accumulate {
        my ($self, $tokens, $cmd) = @_;
+       my $problems = $self->{problems};
+
+       # no previous command to check for missing "&&"
        goto DONE unless @$tokens;
-       goto DONE if @$cmd == 1 && $$cmd[0] eq "\n";
+
+       # new command is empty line; can't yet check if previous is missing "&&"
+       goto DONE if @$cmd == 1 && $$cmd[0]->[0] eq "\n";
 
        # did previous command end with "&&", "|", "|| return" or similar?
        goto DONE if match_ending($tokens, \@safe_endings);
@@ -514,20 +550,20 @@ sub accumulate {
        # if this command handles "$?" specially, then okay for previous
        # command to be missing "&&"
        for my $token (@$cmd) {
-               goto DONE if $token =~ /\$\?/;
+               goto DONE if $token->[0] =~ /\$\?/;
        }
 
        # if this command is "false", "return 1", or "exit 1" (which signal
        # failure explicitly), then okay for all preceding commands to be
        # missing "&&"
-       if ($$cmd[0] =~ /^(?:false|return|exit)$/) {
-               @$tokens = grep(!/^\?!AMP\?!$/, @$tokens);
+       if ($$cmd[0]->[0] =~ /^(?:false|return|exit)$/) {
+               @$problems = grep {$_->[0] ne 'AMP'} @$problems;
                goto DONE;
        }
 
        # flag missing "&&" at end of previous command
        my $n = find_non_nl($tokens);
-       splice(@$tokens, $n + 1, 0, '?!AMP?!') unless $n < 0;
+       push(@$problems, ['AMP', $tokens->[$n]]) unless $n < 0;
 
 DONE:
        $self->SUPER::accumulate($tokens, $cmd);
@@ -553,7 +589,7 @@ sub new {
 # composition of multiple strings and non-string character runs; for instance,
 # `"test body"` unwraps to `test body`; `word"a b"42'c d'` to `worda b42c d`
 sub unwrap {
-       my $token = @_ ? shift @_ : $_;
+       my $token = (@_ ? shift @_ : $_)->[0];
        # simple case: 'sqstring' or "dqstring"
        return $token if $token =~ s/^'([^']*)'$/$1/;
        return $token if $token =~ s/^"([^"]*)"$/$1/;
@@ -584,13 +620,25 @@ sub check_test {
        $self->{ntests}++;
        my $parser = TestParser->new(\$body);
        my @tokens = $parser->parse();
-       return unless $emit_all || grep(/\?![^?]+\?!/, @tokens);
+       my $problems = $parser->{problems};
+       return unless $emit_all || @$problems;
        my $c = main::fd_colors(1);
-       my $checked = join(' ', @tokens);
-       $checked =~ s/^\n//;
-       $checked =~ s/^ //mg;
-       $checked =~ s/ $//mg;
+       my $lineno = $_[1]->[3];
+       my $start = 0;
+       my $checked = '';
+       for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
+               my ($label, $token) = @$_;
+               my $pos = $token->[2];
+               $checked .= substr($body, $start, $pos - $start) . " ?!$label?! ";
+               $start = $pos;
+       }
+       $checked .= substr($body, $start);
+       $checked =~ s/^/$lineno++ . ' '/mge;
+       $checked =~ s/^\d+ \n//;
+       $checked =~ s/(\s) \?!/$1?!/mg;
+       $checked =~ s/\?! (\s)/?!$1/mg;
        $checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$c->{reset}/mg;
+       $checked =~ s/^\d+/$c->{dim}$&$c->{reset}/mg;
        $checked .= "\n" unless $checked =~ /\n$/;
        push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked");
 }
@@ -598,9 +646,9 @@ sub check_test {
 sub parse_cmd {
        my $self = shift @_;
        my @tokens = $self->SUPER::parse_cmd();
-       return @tokens unless @tokens && $tokens[0] =~ /^test_expect_(?:success|failure)$/;
+       return @tokens unless @tokens && $tokens[0]->[0] =~ /^test_expect_(?:success|failure)$/;
        my $n = $#tokens;
-       $n-- while $n >= 0 && $tokens[$n] =~ /^(?:[;&\n|]|&&|\|\|)$/;
+       $n-- while $n >= 0 && $tokens[$n]->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
        $self->check_test($tokens[1], $tokens[2]) if $n == 2; # title body
        $self->check_test($tokens[2], $tokens[3]) if $n > 2;  # prereq title body
        return @tokens;
@@ -622,25 +670,39 @@ if (eval {require Time::HiRes; Time::HiRes->import(); 1;}) {
 # thread and ignore %ENV changes in subthreads.
 $ENV{TERM} = $ENV{USER_TERM} if $ENV{USER_TERM};
 
-my @NOCOLORS = (bold => '', rev => '', reset => '', blue => '', green => '', red => '');
+my @NOCOLORS = (bold => '', rev => '', dim => '', reset => '', blue => '', green => '', red => '');
 my %COLORS = ();
 sub get_colors {
        return \%COLORS if %COLORS;
-       if (exists($ENV{NO_COLOR}) ||
-           system("tput sgr0 >/dev/null 2>&1") != 0 ||
-           system("tput bold >/dev/null 2>&1") != 0 ||
-           system("tput rev  >/dev/null 2>&1") != 0 ||
-           system("tput setaf 1 >/dev/null 2>&1") != 0) {
+       if (exists($ENV{NO_COLOR})) {
                %COLORS = @NOCOLORS;
                return \%COLORS;
        }
-       %COLORS = (bold  => `tput bold`,
-                  rev   => `tput rev`,
-                  reset => `tput sgr0`,
-                  blue  => `tput setaf 4`,
-                  green => `tput setaf 2`,
-                  red   => `tput setaf 1`);
-       chomp(%COLORS);
+       if ($ENV{TERM} =~ /xterm|xterm-\d+color|xterm-new|xterm-direct|nsterm|nsterm-\d+color|nsterm-direct/) {
+               %COLORS = (bold  => "\e[1m",
+                          rev   => "\e[7m",
+                          dim   => "\e[2m",
+                          reset => "\e[0m",
+                          blue  => "\e[34m",
+                          green => "\e[32m",
+                          red   => "\e[31m");
+               return \%COLORS;
+       }
+       if (system("tput sgr0 >/dev/null 2>&1") == 0 &&
+           system("tput bold >/dev/null 2>&1") == 0 &&
+           system("tput rev  >/dev/null 2>&1") == 0 &&
+           system("tput dim  >/dev/null 2>&1") == 0 &&
+           system("tput setaf 1 >/dev/null 2>&1") == 0) {
+               %COLORS = (bold  => `tput bold`,
+                          rev   => `tput rev`,
+                          dim   => `tput dim`,
+                          reset => `tput sgr0`,
+                          blue  => `tput setaf 4`,
+                          green => `tput setaf 2`,
+                          red   => `tput setaf 1`);
+               return \%COLORS;
+       }
+       %COLORS = @NOCOLORS;
        return \%COLORS;
 }
 
@@ -656,7 +718,7 @@ sub ncores {
        # Windows
        return $ENV{NUMBER_OF_PROCESSORS} if exists($ENV{NUMBER_OF_PROCESSORS});
        # Linux / MSYS2 / Cygwin / WSL
-       do { local @ARGV='/proc/cpuinfo'; return scalar(grep(/^processor\s*:/, <>)); } if -r '/proc/cpuinfo';
+       do { local @ARGV='/proc/cpuinfo'; return scalar(grep(/^processor[\s\d]*:/, <>)); } if -r '/proc/cpuinfo';
        # macOS & BSD
        return qx/sysctl -n hw.ncpu/ if $^O =~ /(?:^darwin$|bsd)/;
        return 1;
index 48ed4eb1246efc67b0064b03ea531ea3fad3a320..056e03003d3e7847ead4f14e47b69de630cb507e 100644 (file)
@@ -1,11 +1,11 @@
-test_done ( ) {
+test_done () {
        case "$test_failure" in
-       0 )
+       0)
                test_at_end_hook_
 
                exit 0 ;;
 
-       * )
+       *)
                if test $test_external_has_tap -eq 0
                then
                        say_color error "# failed $test_failure among $msg"
@@ -14,5 +14,5 @@ test_done ( ) {
 
                exit 1 ;;
 
-               esac
+       esac
 }
index f76fde1ffba91d7becf17c0990c39ac25a7083f0..b47827d7499f607289cc6ec78a7b0030ee2449c0 100644 (file)
@@ -1,4 +1,8 @@
 (
+
        nothing &&
+
        something
+
+
 )
index d10b2eeaf2754f5d6609f438c523ee26c92e0b05..df2beea8887f3504e5fbab25aef96eb3763ded84 100644 (file)
@@ -1,6 +1,8 @@
 (
        {
+               # show a
                echo a &&
+               # show b
                echo b
        }
 )
index a3bcea492a915f62cc6870a3e194fd86c19344a7..1c873263647907de91d461b4bf19b48ea5e85c3a 100644 (file)
@@ -12,9 +12,9 @@
 ) &&
 
 {
-       echo a ; ?!AMP?! echo b
+       echo a; ?!AMP?! echo b
 } &&
-{ echo a ; ?!AMP?! echo b ; } &&
+{ echo a; ?!AMP?! echo b; } &&
 
 {
        echo "${var}9" &&
index 1e4b054bda0faa803ecf12c8f4622ed0b1b29023..641c157b98c0af678b15fb2cc25645eac8650602 100644 (file)
@@ -1,7 +1,10 @@
 (
        case "$x" in
+       # found foo
        x) foo ;;
+       # found other
        *)
+               # treat it as bar
                bar
                ;;
        esac
index 28f9114f42de6b4dd6682ba7fdea5cd2bd3de263..20d0bb5333083208285e87408e5f16fb7263594c 100644 (file)
@@ -1,9 +1,9 @@
 JGIT_DAEMON_PID= &&
 git init --bare empty.git &&
-> empty.git/git-daemon-export-ok &&
+>empty.git/git-daemon-export-ok &&
 mkfifo jgit_daemon_output &&
 {
-       jgit daemon --port="$JGIT_DAEMON_PORT" . > jgit_daemon_output &
+       jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
        JGIT_DAEMON_PID=$!
 } &&
 test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
index 1732d221c32e98bf40438167769869cb597ed4a1..4cd18e2edfc80a4bcb30ea409da0e6afac0d341b 100644 (file)
@@ -1,16 +1,16 @@
 case "$(git ls-files)" in
-one ) echo pass one ;;
-* ) echo bad one ; return 1 ;;
+one) echo pass one ;;
+*) echo bad one; return 1 ;;
 esac &&
 (
        case "$(git ls-files)" in
-       two ) echo pass two ;;
-       * ) echo bad two ; exit 1 ;;
-esac
+       two) echo pass two ;;
+       *) echo bad two; exit 1 ;;
+       esac
 ) &&
 case "$(git ls-files)" in
-dir/two"$LF"one ) echo pass both ;;
-* ) echo bad ; return 1 ;;
+dir/two"$LF"one) echo pass both ;;
+*) echo bad; return 1 ;;
 esac &&
 
 for i in 1 2 3 4 ; do
index f4bada946322a6bee2634efc27b1e67f684c1c5e..e6b3b2193e869136e222ec582853ba3f17254e53 100644 (file)
@@ -1,7 +1,7 @@
-OUT=$(( ( large_git ; echo $? 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
 test_match_signal 13 "$OUT" &&
 
-{ test-tool sigchain > actual ; ret=$? ; } &&
+{ test-tool sigchain >actual; ret=$?; } &&
 {
        test_match_signal 15 "$ret" ||
        test "$ret" = 3
index af0369d3285b177dac4d2ccbf6528aa6b76ec2f8..83810ea7ec7d34d439c69b12bbfe2d70be16e3df 100644 (file)
@@ -4,7 +4,7 @@ mkdir sub && (
        nuff said
 ) &&
 
-cut "-d " -f actual | ( read s1 s2 s3 &&
+cut "-d " -f actual | (read s1 s2 s3 &&
 test -f $s1 ?!AMP?!
 test $(cat $s2) = tree2path1 &&
-test $(cat $s3) = tree3path1 )
+test $(cat $s3) = tree3path1)
index 0f87db9ae6891f8536c6eec73b71e5f049ca9667..2192a2870a1ae3d098c78126eb06608f74a32437 100644 (file)
@@ -15,7 +15,8 @@
 ) | wuzzle &&
 (
        bop
-) | fazz       fozz &&
+) | fazz \
+       fozz &&
 (
        bup
 ) |
index ab2f79e845703731124315bee2d90b6eff329126..ec42f2c30c98630ef0538936bbaa5f96c0fa7626 100644 (file)
@@ -1,2 +1,2 @@
-OUT=$(( ( large_git 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
 test_match_signal 13 "$OUT"
index f76fde1ffba91d7becf17c0990c39ac25a7083f0..a68f1f9d7c26745169ab4c45b3e6f5f7c5b3c6b3 100644 (file)
@@ -1,4 +1,8 @@
 (
+       # comment 1
        nothing &&
+       # comment 2
        something
+       # comment 3
+       # comment 4
 )
index 75477bb1add492288504244af4bd57ec45dd601c..cd584a435730045608f1ce9162274fb6fa53a2c8 100644 (file)
@@ -1,2 +1,12 @@
-run_sub_test_lib_test_err run-inv-range-start "--run invalid range start" --run="a-5" <<-EOF &&
-check_sub_test_lib_test_err run-inv-range-start <<-EOF_OUT 3 <<-EOF_ERR
+run_sub_test_lib_test_err run-inv-range-start \
+       "--run invalid range start" \
+       --run="a-5" <<-\EOF &&
+test_expect_success "passing test #1" "true"
+test_done
+EOF
+check_sub_test_lib_test_err run-inv-range-start \
+       <<-\EOF_OUT 3<<-EOF_ERR
+> FATAL: Unexpected exit with code 1
+EOF_OUT
+> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
+EOF_ERR
index bf9ced60d4c4a360266fc942430bbe79f6689345..37eab80738e4018e48b966cb9933ca332ae71f5e 100644 (file)
@@ -1,3 +1,5 @@
-echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' > expect &&
-test_must_fail git commit --fixup=reword:HEAD~ $1 2 > actual &&
+
+echo 'fatal: reword option of --fixup is mutually exclusive with'      '--patch/--interactive/--all/--include/--only' >expect &&
+test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
 test_cmp expect actual
+
index 10724987a5fbb6ba19ba0972c77b5689ca52ab28..087eda15e49144a9cbe24f9fc526f489b63aa068 100644 (file)
@@ -6,6 +6,7 @@ grep "^\.git$" output.txt &&
 (
        cd client$version &&
        GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. $(cat ../input)
-) > output &&
-       cut -d ' ' -f 2 < output | sort > actual &&
+) >output &&
+       cut -d ' ' -f 2 <output | sort >actual &&
        test_cmp expect actual
+
index f42f2d41ba8c68398631c256e2abb9705d32c6ba..8507721192aeb4b5486ca7e7ddd2ecd7a484ebe0 100644 (file)
@@ -1,3 +1,4 @@
-git ls-tree $tree path > current &&
-cat > expected <<EOF &&
+git ls-tree $tree path >current &&
+cat >expected <<\EOF &&
+EOF
 test_output
index 2d961a58c6676cee9db9ecfb573d4bac51f2d071..765a35bb4c47e31d369e481a3531ab7eaec2b097 100644 (file)
@@ -1,4 +1,4 @@
-if ! condition ; then echo nope ; else yep ; fi &&
+if ! condition; then echo nope; else yep; fi &&
 test_prerequisite !MINGW &&
 mail uucp!address &&
 echo !whatever!
index a21007a63f171c92482e47c381a759f7e6f20e03..02c0d15cca5cd42d7909f9fccbb95a932e217953 100644 (file)
@@ -1,5 +1,5 @@
 for it
 do
-       path=$(expr "$it" : ( [^:]*) ) &&
+       path=$(expr "$it" : ([^:]*)) &&
        git update-index --add "$path" || exit
 done
index a5810c9bddd8352c74f3213be1fd54d1986a72f4..d2237f1e38fad73938ff6335dac9de538e337caa 100644 (file)
@@ -2,8 +2,11 @@
        for i in a b c
        do
                echo $i ?!AMP?!
-               cat <<-EOF ?!LOOP?!
+               cat <<-\EOF ?!LOOP?!
+               bar
+               EOF
        done ?!AMP?!
+
        for i in a b c; do
                echo $i &&
                cat $i ?!LOOP?!
index a14388e6b9faeb67cdc79eea38a0cb3f94b240f8..dd7c997a3c340d633d9cbf7a78f4b29727a22ce4 100644 (file)
@@ -1,8 +1,8 @@
-sha1_file ( ) {
+sha1_file() {
        echo "$*" | sed "s#..#.git/objects/&/#"
 } &&
 
-remove_object ( ) {
+remove_object() {
        file=$(sha1_file "$*") &&
        test -e "$file" ?!AMP?!
        rm -f "$file"
index 2af9ced71cc331414ce22e5d4ef3fc1320b3c15d..7d9c2b560701f66eb854b1b31c8b3820347f7eb1 100644 (file)
@@ -1,2 +1,4 @@
 (
-       cat <<-INPUT)
+       cat <<-\INPUT)
+       fizz
+       INPUT
index fb6cf7285d02649a8df406db3e11ca4d59b9eeae..f92a7ce9992420e2e5483ddcd83d3118d5dfd516 100644 (file)
@@ -1,5 +1,11 @@
-cat > expect <<-EOF &&
+cat >expect <<- EOF &&
+header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
+num_commits: $1
+chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
+EOF
 
-cat > expect <<-EOF ?!AMP?!
+cat >expect << -EOF ?!AMP?!
+this is not indented
+-EOF
 
 cleanup
index f8b3aa73c4f180be48afff988c0f7cece67e45d4..b7364c82c89feba3baf9a149937f1e43bf91ff23 100644 (file)
@@ -1,5 +1,8 @@
 (
-       x=$(bobble <<-END &&
+       x=$(bobble <<-\END &&
+               fossil
+               vegetable
+               END
                wiffle) ?!AMP?!
        echo $x
 )
index be64b26869ada1f4d7d9715cb754c2aa826c6978..6c13bdcbfb5d12500ffc01915b853d0169abf13e 100644 (file)
@@ -1,5 +1,7 @@
 (
-       cat <<-TXT && echo "multi-line
+       cat <<-\TXT && echo "multi-line
        string" ?!AMP?!
+       fizzle
+       TXT
        bap
 )
index 110059ba58420e5924de64edb0ec44346b43cb34..91b961242a1cabb7cc3f5edecaa6d99eb3eeef9e 100644 (file)
@@ -1,7 +1,25 @@
-boodle wobba        gorgo snoot        wafta snurb <<EOF &&
+boodle wobba \
+       gorgo snoot \
+       wafta snurb <<EOF &&
+quoth the raven,
+nevermore...
+EOF
 
 cat <<-Arbitrary_Tag_42 >foo &&
+snoz
+boz
+woz
+Arbitrary_Tag_42
 
-cat <<zump >boo &&
+cat <<"zump" >boo &&
+snoz
+boz
+woz
+zump
 
-horticulture <<EOF
+horticulture <<\EOF
+gomez
+morticia
+wednesday
+pugsly
+EOF
index 44d86c35976ce1957aa0b4fb90f6b7e31f230d3c..cbaaf857d47a0bf23813933e768b6a9128670737 100644 (file)
@@ -8,7 +8,9 @@
                echo foo
        else
                echo foo &&
-               cat <<-EOF
+               cat <<-\EOF
+               bar
+               EOF
        fi ?!AMP?!
        echo poodle
 ) &&
index ffac8f901857eef401cdcfa6d60734c92a96b416..134d3a14f5c0c1efc51e21fe1fa29b52b5f9a0ab 100644 (file)
@@ -1,4 +1,10 @@
-line 1 line 2 line 3 line 4 &&
+line 1 \
+line 2 \
+line 3 \
+line 4 &&
 (
-       line 5  line 6  line 7  line 8
+       line 5 \
+       line 6 \
+       line 7 \
+       line 8
 )
index dd0dace077f0e093ccda9dc33b3296830e13c8d2..6bad21853006d38834bf1b4ebc2da46e3c6faebc 100644 (file)
@@ -1,6 +1,6 @@
 (
-       foobar &&
-       barfoo ?!AMP?!
+       foobar && # comment 1
+       barfoo ?!AMP?! # wrong position for &&
        flibble "not a # comment"
 ) &&
 
index 0ad23bb35e4fb173eb808f777fef2f2a453c8c1a..7ce3a348060dcfaf91299282eb4eee2b471f58bc 100644 (file)
@@ -1,18 +1,18 @@
-( while test $i -le $blobcount
-do
-       printf "Generating blob $i/$blobcount\r" >& 2 &&
+(while test $i -le $blobcount
+ do
+       printf "Generating blob $i/$blobcount\r" >&2 &&
        printf "blob\nmark :$i\ndata $blobsize\n" &&
-
+       #test-tool genrandom $i $blobsize &&
        printf "%-${blobsize}s" $i &&
        echo "M 100644 :$i $i" >> commit &&
        i=$(($i+1)) ||
        echo $? > exit-status
-done &&
-echo "commit refs/heads/main" &&
-echo "author A U Thor <author@email.com> 123456789 +0000" &&
-echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
-echo "data 5" &&
-echo ">2gb" &&
-cat commit ) |
+ done &&
+ echo "commit refs/heads/main" &&
+ echo "author A U Thor <author@email.com> 123456789 +0000" &&
+ echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+ echo "data 5" &&
+ echo ">2gb" &&
+ cat commit) |
 git fast-import --big-file-threshold=2 &&
 test ! -f exit-status
index 2a86885ee6a330450a76591248b60b89e601816f..3836049cc4190e4a37c35511279cd3fd36040b65 100644 (file)
@@ -2,18 +2,24 @@
        (cd foo &&
                bar
        ) &&
+
        (cd foo &&
                bar
        ) ?!AMP?!
+
        (
                cd foo &&
                bar) &&
+
        (
                cd foo &&
                bar) ?!AMP?!
+
        (cd foo &&
                bar) &&
+
        (cd foo &&
                bar) ?!AMP?!
+
        foobar
 )
index e3bef63f7548cb0c187ae938280029dd470922bb..29b3832a986af30d7026eef513cf01dc769316a2 100644 (file)
@@ -1,7 +1,30 @@
 cat <<ARBITRARY >foop &&
+naddle
+fub <<EOF
+       nozzle
+       noodle
+EOF
+formp
+ARBITRARY
 
 (
-       cat <<-INPUT_END &&
-       cat <<-EOT ?!AMP?!
+       cat <<-\INPUT_END &&
+       fish are mice
+       but geese go slow
+       data <<EOF
+               perl is lerp
+               and nothing else
+       EOF
+       toink
+       INPUT_END
+
+       cat <<-\EOT ?!AMP?!
+       text goes here
+       data <<EOF
+               data goes here
+       EOF
+       more test here
+       EOT
+
        foobar
 )
index 4793a0e8e12aeb0f4d4c4497a2e77a87a2d6d2b6..3461df40e5129423ab58d92f84518d145f2f8b5d 100644 (file)
@@ -1,31 +1,31 @@
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-       for j in 0 1 2 3 4 5 6 7 8 9 ;
+       for j in 0 1 2 3 4 5 6 7 8 9;
        do
-               echo "$i$j" > "path$i$j" ?!LOOP?!
+               echo "$i$j" >"path$i$j" ?!LOOP?!
        done ?!LOOP?!
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-       for j in 0 1 2 3 4 5 6 7 8 9 ;
+       for j in 0 1 2 3 4 5 6 7 8 9;
        do
-               echo "$i$j" > "path$i$j" || return 1
+               echo "$i$j" >"path$i$j" || return 1
        done
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-       for j in 0 1 2 3 4 5 6 7 8 9 ;
+       for j in 0 1 2 3 4 5 6 7 8 9;
        do
-               echo "$i$j" > "path$i$j" ?!LOOP?!
+               echo "$i$j" >"path$i$j" ?!LOOP?!
        done || return 1
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-       for j in 0 1 2 3 4 5 6 7 8 9 ;
+       for j in 0 1 2 3 4 5 6 7 8 9;
        do
-               echo "$i$j" > "path$i$j" || return 1
+               echo "$i$j" >"path$i$j" || return 1
        done || return 1
 done
index be4b27a305bec54678ae4669a1666405ec06f966..9138cf386d359267143945d13c1882b0feb5c6f4 100644 (file)
@@ -2,6 +2,8 @@
        foo &&
        (
                bar &&
+               # bottles wobble while fiddles gobble
+               # minor numbers of cows (or do they?)
                baz &&
                snaff
        ) ?!AMP?!
index 02e0a9f1bb5f302a2903817b5d8ef7ff25b2efa7..73ff28546ae720bd7ce8e15cd9597e27e6afae41 100644 (file)
@@ -4,6 +4,7 @@
                echo a &&
                echo b
        ) >file &&
+
        cd foo &&
        (
                echo a ?!AMP?!
index 2cfc0282970db02dd37eaf1c0c079e22a233cef1..811971b1a3c495da2adf0064c24007c6a7bf913d 100644 (file)
@@ -2,7 +2,9 @@
        foo |
        bar |
        baz &&
+
        fish |
        cow ?!AMP?!
+
        sunder
 )
index 029d129299a0a5c68d45661071ba3ae144cd5377..75d6f607e295638e9e0c5029711357bc2e9d2fda 100644 (file)
@@ -1,10 +1,30 @@
 (
-       echo wobba             gorgo snoot             wafta snurb <<-EOF &&
+       echo wobba \
+              gorgo snoot \
+              wafta snurb <<-EOF &&
+       quoth the raven,
+       nevermore...
+       EOF
+
        cat <<EOF >bip ?!AMP?!
-       echo <<-EOF >bop
+       fish fly high
+EOF
+
+       echo <<-\EOF >bop
+       gomez
+       morticia
+       wednesday
+       pugsly
+       EOF
 ) &&
 (
-       cat <<-ARBITRARY >bup &&
-       cat <<-ARBITRARY3 >bup3 &&
+       cat <<-\ARBITRARY >bup &&
+       glink
+       FIZZ
+       ARBITRARY
+       cat <<-"ARBITRARY3" >bup3 &&
+       glink
+       FIZZ
+       ARBITRARY3
        meep
 )
index b7015361bfe6a3555d02e97d2bdc0413b8f8c432..8f694990e8d9f1ce1205399f54b61436cfe4100c 100644 (file)
@@ -2,13 +2,18 @@
        (foo && bar) &&
        (foo && bar) |
        (foo && bar) >baz &&
+
        (foo; ?!AMP?! bar) &&
        (foo; ?!AMP?! bar) |
        (foo; ?!AMP?! bar) >baz &&
+
        (foo || exit 1) &&
        (foo || exit 1) |
        (foo || exit 1) >baz &&
+
        (foo && bar) ?!AMP?!
+
        (foo && bar; ?!AMP?! baz) ?!AMP?!
+
        foobar
 )
index 69167da2f27a3098297191c6cbfc737eacb91e8a..02f3129232a0d114bf90211b9a6508385d0115bd 100644 (file)
@@ -4,13 +4,18 @@ sub2
 sub3
 sub4" &&
        chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+$chks
+TXT
 ) &&
        chkms="main-sub1
 main-sub2
 main-sub3
 main-sub4" &&
        chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+$chkms
+TXT
 ) &&
+
        subfiles=$(git ls-files) &&
        check_equal "$subfiles" "$chkms
 $chks"
index 342360bcd05941c55c924e041a4715e9fe201361..6a387917a7af18bc39b70c91b091b91eb6a7ee82 100644 (file)
@@ -4,22 +4,22 @@ git config filter.rot13.clean ./rot13.sh &&
 {
     echo "*.t filter=rot13" ?!AMP?!
     echo "*.i ident"
-} > .gitattributes &&
+} >.gitattributes &&
 
 {
     echo a b c d e f g h i j k l m ?!AMP?!
     echo n o p q r s t u v w x y z ?!AMP?!
     echo '$Id$'
-} > test &&
-cat test > test.t &&
-cat test > test.o &&
-cat test > test.i &&
+} >test &&
+cat test >test.t &&
+cat test >test.o &&
+cat test >test.i &&
 git add test test.t test.i &&
 rm -f test test.t test.i &&
 git checkout -- test test.t test.i &&
 
-echo "content-test2" > test2.o &&
-echo "content-test3 - filename with special characters" > "test3 'sq',$x=.o" ?!AMP?!
+echo "content-test2" >test2.o &&
+echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!AMP?!
 
 downstream_url_for_sed=$(
        printf "%sn" "$downstream_url" |
diff --git a/t/chainlint/unclosed-here-doc-indent.expect b/t/chainlint/unclosed-here-doc-indent.expect
new file mode 100644 (file)
index 0000000..7c30a1a
--- /dev/null
@@ -0,0 +1,4 @@
+command_which_is_run &&
+cat >expect <<-\EOF ?!UNCLOSED-HEREDOC?! &&
+we forget to end the here-doc
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc-indent.test b/t/chainlint/unclosed-here-doc-indent.test
new file mode 100644 (file)
index 0000000..5c841a9
--- /dev/null
@@ -0,0 +1,4 @@
+command_which_is_run &&
+cat >expect <<-\EOF &&
+we forget to end the here-doc
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.expect b/t/chainlint/unclosed-here-doc.expect
new file mode 100644 (file)
index 0000000..d65e50f
--- /dev/null
@@ -0,0 +1,7 @@
+command_which_is_run &&
+cat >expect <<\EOF ?!UNCLOSED-HEREDOC?! &&
+       we try to end the here-doc below,
+       but the indentation throws us off
+       since the operator is not "<<-".
+       EOF
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.test b/t/chainlint/unclosed-here-doc.test
new file mode 100644 (file)
index 0000000..69d3786
--- /dev/null
@@ -0,0 +1,7 @@
+command_which_is_run &&
+cat >expect <<\EOF &&
+       we try to end the here-doc below,
+       but the indentation throws us off
+       since the operator is not "<<-".
+       EOF
+command_which_is_gobbled
index f272aa21fee1950a97a9eeddee9436a11c4e1f3f..06c1567f481e8cbc21cf50fdf58bfb9a502d439e 100644 (file)
@@ -2,8 +2,11 @@
        while true
        do
                echo foo ?!AMP?!
-               cat <<-EOF ?!LOOP?!
+               cat <<-\EOF ?!LOOP?!
+               bar
+               EOF
        done ?!AMP?!
+
        while true; do
                echo foo &&
                cat bar ?!LOOP?!
index fd3303552bec0d3ca243845634179eb35890866a..dd8107cd7da24b01541f0bccf4cff332940f31e9 100755 (executable)
@@ -45,6 +45,7 @@ while (<>) {
        /\bhead\s+-c\b/ and err 'head -c is not portable (use test_copy_bytes BYTES <file >out)';
        /(?:\$\(seq|^\s*seq\b)/ and err 'seq is not portable (use test_seq)';
        /\bgrep\b.*--file\b/ and err 'grep --file FILE is not portable (use grep -f FILE)';
+       /\b[ef]grep\b/ and err 'egrep/fgrep obsolescent (use grep -E/-F)';
        /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
        /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
                err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
index cb881139f7316f475ea072c1b549aafe7cca2ad1..8a3fd0009a042c67019a3e89e5727dc4b7240600 100644 (file)
@@ -1,7 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "advice.h"
 #include "config.h"
+#include "setup.h"
 
 int cmd__advise_if_enabled(int argc, const char **argv)
 {
index ff35f5999b367106f04743582ca9fb490b80501f..af43ee1cb5ee27c6aad608c7a233e234a7146f89 100644 (file)
@@ -1,6 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "git-compat-util.h"
 #include "pack-bitmap.h"
+#include "setup.h"
 
 static int bitmap_list_commits(void)
 {
index 6c900ca668467dcdbc92c6544ad173565c3a5f08..1281e66876f35d380d5360be23883c5ed27aafcf 100644 (file)
@@ -1,7 +1,9 @@
-#include "git-compat-util.h"
-#include "bloom.h"
 #include "test-tool.h"
+#include "bloom.h"
+#include "hex.h"
 #include "commit.h"
+#include "repository.h"
+#include "setup.h"
 
 static struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
 
@@ -38,7 +40,6 @@ static void get_bloom_filter_for_commit(const struct object_id *commit_oid)
 {
        struct commit *c;
        struct bloom_filter *filter;
-       setup_git_directory();
        c = lookup_commit(the_repository, commit_oid);
        filter = get_or_compute_bloom_filter(the_repository, c, 1,
                                             &settings,
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
new file mode 100644 (file)
index 0000000..09dc787
--- /dev/null
@@ -0,0 +1,135 @@
+#include "test-tool.h"
+#include "parse-options.h"
+#include "bundle-uri.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "string-list.h"
+#include "transport.h"
+#include "remote.h"
+
+enum input_mode {
+       KEY_VALUE_PAIRS,
+       CONFIG_FILE,
+};
+
+static int cmd__bundle_uri_parse(int argc, const char **argv, enum input_mode mode)
+{
+       const char *key_value_usage[] = {
+               "test-tool bundle-uri parse-key-values <input>",
+               NULL
+       };
+       const char *config_usage[] = {
+               "test-tool bundle-uri parse-config <input>",
+               NULL
+       };
+       const char **usage = key_value_usage;
+       struct option options[] = {
+               OPT_END(),
+       };
+       struct strbuf sb = STRBUF_INIT;
+       struct bundle_list list;
+       int err = 0;
+       FILE *fp;
+
+       if (mode == CONFIG_FILE)
+               usage = config_usage;
+
+       argc = parse_options(argc, argv, NULL, options, usage,
+                            PARSE_OPT_STOP_AT_NON_OPTION);
+
+       init_bundle_list(&list);
+
+       list.baseURI = xstrdup("<uri>");
+
+       switch (mode) {
+       case KEY_VALUE_PAIRS:
+               if (argc != 1)
+                       goto usage;
+               fp = fopen(argv[0], "r");
+               if (!fp)
+                       die("failed to open '%s'", argv[0]);
+               while (strbuf_getline(&sb, fp) != EOF) {
+                       if (bundle_uri_parse_line(&list, sb.buf))
+                               err = error("bad line: '%s'", sb.buf);
+               }
+               fclose(fp);
+               break;
+
+       case CONFIG_FILE:
+               if (argc != 1)
+                       goto usage;
+               err = bundle_uri_parse_config_format("<uri>", argv[0], &list);
+               break;
+       }
+       strbuf_release(&sb);
+
+       print_bundle_list(stdout, &list);
+
+       clear_bundle_list(&list);
+
+       return !!err;
+
+usage:
+       usage_with_options(usage, options);
+}
+
+static int cmd_ls_remote(int argc, const char **argv)
+{
+       const char *dest;
+       struct remote *remote;
+       struct transport *transport;
+       int status = 0;
+
+       dest = argc > 1 ? argv[1] : NULL;
+
+       remote = remote_get(dest);
+       if (!remote) {
+               if (dest)
+                       die(_("bad repository '%s'"), dest);
+               die(_("no remote configured to get bundle URIs from"));
+       }
+       if (!remote->url_nr)
+               die(_("remote '%s' has no configured URL"), dest);
+
+       transport = transport_get(remote, NULL);
+       if (transport_get_remote_bundle_uri(transport) < 0) {
+               error(_("could not get the bundle-uri list"));
+               status = 1;
+               goto cleanup;
+       }
+
+       print_bundle_list(stdout, transport->bundles);
+
+cleanup:
+       if (transport_disconnect(transport))
+               return 1;
+       return status;
+}
+
+int cmd__bundle_uri(int argc, const char **argv)
+{
+       const char *usage[] = {
+               "test-tool bundle-uri <subcommand> [<options>]",
+               NULL
+       };
+       struct option options[] = {
+               OPT_END(),
+       };
+
+       argc = parse_options(argc, argv, NULL, options, usage,
+                            PARSE_OPT_STOP_AT_NON_OPTION |
+                            PARSE_OPT_KEEP_ARGV0);
+       if (argc == 1)
+               goto usage;
+
+       if (!strcmp(argv[1], "parse-key-values"))
+               return cmd__bundle_uri_parse(argc - 1, argv + 1, KEY_VALUE_PAIRS);
+       if (!strcmp(argv[1], "parse-config"))
+               return cmd__bundle_uri_parse(argc - 1, argv + 1, CONFIG_FILE);
+       if (!strcmp(argv[1], "ls-remote"))
+               return cmd_ls_remote(argc - 1, argv + 1);
+       error("there is no test-tool bundle-uri tool '%s'", argv[1]);
+
+usage:
+       usage_with_options(usage, options);
+}
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
new file mode 100644 (file)
index 0000000..e723639
--- /dev/null
@@ -0,0 +1,69 @@
+#define USE_THE_INDEX_VARIABLE
+#include "test-tool.h"
+#include "gettext.h"
+#include "hex.h"
+#include "tree.h"
+#include "cache-tree.h"
+#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
+
+static char const * const test_cache_tree_usage[] = {
+       N_("test-tool cache-tree <options> (control|prime|update)"),
+       NULL
+};
+
+int cmd__cache_tree(int argc, const char **argv)
+{
+       struct object_id oid;
+       struct tree *tree;
+       int empty = 0;
+       int invalidate_qty = 0;
+       int i;
+
+       struct option options[] = {
+               OPT_BOOL(0, "empty", &empty,
+                        N_("clear the cache tree before each iteration")),
+               OPT_INTEGER_F(0, "invalidate", &invalidate_qty,
+                             N_("number of entries in the cache tree to invalidate (default 0)"),
+                             PARSE_OPT_NONEG),
+               OPT_END()
+       };
+
+       setup_git_directory();
+
+       argc = parse_options(argc, argv, NULL, options, test_cache_tree_usage, 0);
+
+       if (repo_read_index(the_repository) < 0)
+               die(_("unable to read index file"));
+
+       oidcpy(&oid, &the_index.cache_tree->oid);
+       tree = parse_tree_indirect(&oid);
+       if (!tree)
+               die(_("not a tree object: %s"), oid_to_hex(&oid));
+
+       if (empty) {
+               /* clear the cache tree & allocate a new one */
+               cache_tree_free(&the_index.cache_tree);
+               the_index.cache_tree = cache_tree();
+       } else if (invalidate_qty) {
+               /* invalidate the specified number of unique paths */
+               float f_interval = (float)the_index.cache_nr / invalidate_qty;
+               int interval = f_interval < 1.0 ? 1 : (int)f_interval;
+               for (i = 0; i < invalidate_qty && i * interval < the_index.cache_nr; i++)
+                       cache_tree_invalidate_path(&the_index, the_index.cache[i * interval]->name);
+       }
+
+       if (argc != 1)
+               usage_with_options(test_cache_tree_usage, options);
+       else if (!strcmp(argv[0], "prime"))
+               prime_cache_tree(the_repository, &the_index, tree);
+       else if (!strcmp(argv[0], "update"))
+               cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+       /* use "control" subcommand to specify no-op */
+       else if (!!strcmp(argv[0], "control"))
+               die(_("Unhandled subcommand '%s'"), argv[0]);
+
+       return 0;
+}
index dc28890a183f1d123b3356131e90fd588aa5a439..0e5538833a8fb58bf5a8f3910af40ff3bfe8c07f 100644 (file)
@@ -94,7 +94,7 @@ int cmd__chmtime(int argc, const char **argv)
        if (timespec_arg(argv[i], &set_time, &set_eq)) {
                ++i;
        } else {
-               if (get == 0) {
+               if (get == 0 && verbose == 0) {
                        fprintf(stderr, "Not a base-10 integer: %s\n", argv[i] + 1);
                        goto usage;
                }
index 4ba9eb65606d42f70f345a1b18bcd575db31e4cf..ed444ca4c25fbf2c581d5f950609c218b53a341c 100644 (file)
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "config.h"
+#include "setup.h"
 #include "string-list.h"
 
 /*
@@ -14,6 +14,8 @@
  * get_value_multi -> prints all values for the entered key in increasing order
  *                  of priority
  *
+ * get -> print return value for the entered key
+ *
  * get_int -> print integer value for the entered key or die
  *
  * get_bool -> print bool value for the entered key or die
@@ -30,6 +32,9 @@
  * iterate -> iterate over all values using git_config(), and print some
  *            data for each
  *
+ * git_config_int -> iterate over all values using git_config() and print the
+ *                   integer value for the entered key or die
+ *
  * Examples:
  *
  * To print the value with highest priority for key "foo.bAr Baz.rock":
  *
  */
 
-static int iterate_cb(const char *var, const char *value, void *data UNUSED)
+static int iterate_cb(const char *var, const char *value,
+                     const struct config_context *ctx,
+                     void *data UNUSED)
 {
+       const struct key_value_info *kvi = ctx->kvi;
        static int nr;
 
        if (nr++)
@@ -46,15 +54,29 @@ static int iterate_cb(const char *var, const char *value, void *data UNUSED)
 
        printf("key=%s\n", var);
        printf("value=%s\n", value ? value : "(null)");
-       printf("origin=%s\n", current_config_origin_type());
-       printf("name=%s\n", current_config_name());
-       printf("lno=%d\n", current_config_line());
-       printf("scope=%s\n", config_scope_name(current_config_scope()));
+       printf("origin=%s\n", config_origin_type_name(kvi->origin_type));
+       printf("name=%s\n", kvi->filename ? kvi->filename : "");
+       printf("lno=%d\n", kvi->linenr);
+       printf("scope=%s\n", config_scope_name(kvi->scope));
 
        return 0;
 }
 
-static int early_config_cb(const char *var, const char *value, void *vdata)
+static int parse_int_cb(const char *var, const char *value,
+                       const struct config_context *ctx, void *data)
+{
+       const char *key_to_match = data;
+
+       if (!strcmp(key_to_match, var)) {
+               int parsed = git_config_int(value, value, ctx->kvi);
+               printf("%d\n", parsed);
+       }
+       return 0;
+}
+
+static int early_config_cb(const char *var, const char *value,
+                          const struct config_context *ctx UNUSED,
+                          void *vdata)
 {
        const char *key = vdata;
 
@@ -95,8 +117,7 @@ int cmd__config(int argc, const char **argv)
                        goto exit1;
                }
        } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
-               strptr = git_config_get_value_multi(argv[2]);
-               if (strptr) {
+               if (!git_config_get_value_multi(argv[2], &strptr)) {
                        for (i = 0; i < strptr->nr; i++) {
                                v = strptr->items[i].string;
                                if (!v)
@@ -109,6 +130,26 @@ int cmd__config(int argc, const char **argv)
                        printf("Value not found for \"%s\"\n", argv[2]);
                        goto exit1;
                }
+       } else if (argc == 3 && !strcmp(argv[1], "get")) {
+               int ret;
+
+               if (!(ret = git_config_get(argv[2])))
+                       goto exit0;
+               else if (ret == 1)
+                       printf("Value not found for \"%s\"\n", argv[2]);
+               else if (ret == -CONFIG_INVALID_KEY)
+                       printf("Key \"%s\" is invalid\n", argv[2]);
+               else if (ret == -CONFIG_NO_SECTION_OR_NAME)
+                       printf("Key \"%s\" has no section\n", argv[2]);
+               else
+                       /*
+                        * A normal caller should just check "ret <
+                        * 0", but for our own tests let's BUG() if
+                        * our whitelist of git_config_parse_key()
+                        * return values isn't exhaustive.
+                        */
+                       BUG("Key \"%s\" has unknown return %d", argv[2], ret);
+               goto exit1;
        } else if (argc == 3 && !strcmp(argv[1], "get_int")) {
                if (!git_config_get_int(argv[2], &val)) {
                        printf("%d\n", val);
@@ -141,7 +182,7 @@ int cmd__config(int argc, const char **argv)
                                goto exit2;
                        }
                }
-               if (!git_configset_get_value(&cs, argv[2], &v)) {
+               if (!git_configset_get_value(&cs, argv[2], &v, NULL)) {
                        if (!v)
                                printf("(NULL)\n");
                        else
@@ -159,8 +200,7 @@ int cmd__config(int argc, const char **argv)
                                goto exit2;
                        }
                }
-               strptr = git_configset_get_value_multi(&cs, argv[2]);
-               if (strptr) {
+               if (!git_configset_get_value_multi(&cs, argv[2], &strptr)) {
                        for (i = 0; i < strptr->nr; i++) {
                                v = strptr->items[i].string;
                                if (!v)
@@ -176,6 +216,9 @@ int cmd__config(int argc, const char **argv)
        } else if (!strcmp(argv[1], "iterate")) {
                git_config(iterate_cb, NULL);
                goto exit0;
+       } else if (argc == 3 && !strcmp(argv[1], "git_config_int")) {
+               git_config(parse_int_cb, (void *) argv[2]);
+               goto exit0;
        }
 
        die("%s: Please check the syntax and the function name", argv[0]);
index e6c1b1e22bb36da604af3238341f6b7b14753796..597027a96e96c923ff630567a8de2f8c47dad3b2 100644 (file)
@@ -1,5 +1,4 @@
 #include "test-tool.h"
-#include "cache.h"
 
 /*
  * Usage: test-tool crontab <file> -l|<input>
index 92c4c2313e78a305dd0da1e7a853e58e325d2f84..e5659df40beb01c9eedbad4d65a0e8ff0a81b1b7 100644 (file)
@@ -1,5 +1,4 @@
 #include "test-tool.h"
-#include "cache.h"
 
 static int rc;
 
@@ -11,9 +10,14 @@ static void report_error(const char *class, int ch)
 
 static int is_in(const char *s, int ch)
 {
-       /* We can't find NUL using strchr.  It's classless anyway. */
+       /*
+        * We can't find NUL using strchr. Accept it as the first
+        * character in the spec -- there are no empty classes.
+        */
        if (ch == '\0')
-               return 0;
+               return ch == *s;
+       if (*s == '\0')
+               s++;
        return !!strchr(s, ch);
 }
 
@@ -23,13 +27,29 @@ static int is_in(const char *s, int ch)
                if (is_in(s, i) != t(i))        \
                        report_error(#t, i);    \
        }                                       \
+       if (t(EOF))                             \
+               report_error(#t, EOF);          \
 }
 
 #define DIGIT "0123456789"
 #define LOWER "abcdefghijklmnopqrstuvwxyz"
 #define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define ASCII \
+       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+       "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
+       "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
+       "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
+       "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
+       "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
+       "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+#define CNTRL \
+       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+       "\x7f"
 
-int cmd__ctype(int argc, const char **argv)
+int cmd__ctype(int argc UNUSED, const char **argv UNUSED)
 {
        TEST_CLASS(isdigit, DIGIT);
        TEST_CLASS(isspace, " \n\r\t");
@@ -38,6 +58,13 @@ int cmd__ctype(int argc, const char **argv)
        TEST_CLASS(is_glob_special, "*?[\\");
        TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
        TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+       TEST_CLASS(isascii, ASCII);
+       TEST_CLASS(islower, LOWER);
+       TEST_CLASS(isupper, UPPER);
+       TEST_CLASS(iscntrl, CNTRL);
+       TEST_CLASS(ispunct, PUNCT);
+       TEST_CLASS(isxdigit, DIGIT "abcdefABCDEF");
+       TEST_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
 
        return rc;
 }
index 45951b1df87c7bf90a25cc1497344681ae42a161..0683d46574fa41e6b4186e8ec49d06ce65d93256 100644 (file)
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "date.h"
+#include "trace.h"
 
 static const char *usage_msg = "\n"
 "  test-tool date relative [time_t]...\n"
@@ -81,7 +81,7 @@ static void parse_approxidate(const char **argv)
 {
        for (; *argv; argv++) {
                timestamp_t t;
-               t = approxidate_relative(*argv);
+               t = approxidate(*argv);
                printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
        }
 }
@@ -90,7 +90,7 @@ static void parse_approx_timestamp(const char **argv)
 {
        for (; *argv; argv++) {
                timestamp_t t;
-               t = approxidate_relative(*argv);
+               t = approxidate(*argv);
                printf("%s -> %"PRItime"\n", *argv, t);
        }
 }
@@ -104,7 +104,7 @@ static void getnanos(const char **argv)
        printf("%lf\n", seconds);
 }
 
-int cmd__date(int argc, const char **argv)
+int cmd__date(int argc UNUSED, const char **argv)
 {
        const char *x;
 
index b15481ea596dcdd2337a4dd9d99761ecb5514ea4..6bc787a4743be08fcd1b977ced58273c3bf274d0 100644 (file)
@@ -11,7 +11,6 @@
 #include "test-tool.h"
 #include "git-compat-util.h"
 #include "delta.h"
-#include "cache.h"
 
 static const char usage_str[] =
        "test-tool delta (-d|-p) <from_file> <data_file> <out_file>";
index 659b6bfa81df6d0bcffb8d0a975492d54d79d6bc..6b297bd75361407fbba7cb6dcab0190d5ef5a1b2 100644 (file)
@@ -15,7 +15,7 @@ static const char *error_name(int error_number)
 
 /*
  * usage:
- * tool-test dir-iterator [--follow-symlinks] [--pedantic] directory_path
+ * tool-test dir-iterator [--pedantic] directory_path
  */
 int cmd__dir_iterator(int argc, const char **argv)
 {
@@ -24,9 +24,7 @@ int cmd__dir_iterator(int argc, const char **argv)
        int iter_status;
 
        for (++argv, --argc; *argv && starts_with(*argv, "--"); ++argv, --argc) {
-               if (strcmp(*argv, "--follow-symlinks") == 0)
-                       flags |= DIR_ITERATOR_FOLLOW_SYMLINKS;
-               else if (strcmp(*argv, "--pedantic") == 0)
+               if (strcmp(*argv, "--pedantic") == 0)
                        flags |= DIR_ITERATOR_PEDANTIC;
                else
                        die("invalid option '%s'", *argv);
index e37396dd9c2c2fe01b9bf2acc8d6a2c6456869fc..73e551cfc222695c31bfc6897d12dc025c87b439 100644 (file)
@@ -155,7 +155,7 @@ static int cmd_dropcaches(void)
 
 #endif
 
-int cmd__drop_caches(int argc, const char **argv)
+int cmd__drop_caches(int argc UNUSED, const char **argv UNUSED)
 {
        cmd_sync();
        return cmd_dropcaches();
index 0d6d7f1ecbf198af006b4d80b8027a196d5caab3..c38f546e4f096fbd3e9b6cb13ab96bb800f56504 100644 (file)
@@ -1,8 +1,12 @@
+#define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
+#include "hash.h"
+#include "hex.h"
 #include "tree.h"
 #include "cache-tree.h"
-
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
 {
@@ -55,19 +59,19 @@ static int dump_cache_tree(struct cache_tree *it,
        return errs;
 }
 
-int cmd__dump_cache_tree(int ac, const char **av)
+int cmd__dump_cache_tree(int ac UNUSED, const char **av UNUSED)
 {
        struct index_state istate;
        struct cache_tree *another = cache_tree();
        int ret;
 
        setup_git_directory();
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                die("unable to read index file");
        istate = the_index;
        istate.cache_tree = another;
        cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
-       ret = dump_cache_tree(active_cache_tree, another, "");
+       ret = dump_cache_tree(the_index.cache_tree, another, "");
        cache_tree_free(&another);
 
        return ret;
index 975f0ac8905125f01ce37be5b7bc862cf058fbcf..4f215fea025290f868defcae8bdca5aeefc627ba 100644 (file)
@@ -1,7 +1,9 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
-int cmd__dump_fsmonitor(int ac, const char **av)
+int cmd__dump_fsmonitor(int ac UNUSED, const char **av UNUSED)
 {
        struct index_state *istate = the_repository->index;
        int i;
index a209880eb37a2ce4de4b9f916ec3dbf52b987957..f29d18ef9490b7a0dd8fd689082a68b4cd5332b0 100644 (file)
@@ -1,14 +1,18 @@
+#define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "split-index.h"
 #include "ewah/ewok.h"
 
-static void show_bit(size_t pos, void *data)
+static void show_bit(size_t pos, void *data UNUSED)
 {
        printf(" %d", (int)pos);
 }
 
-int cmd__dump_split_index(int ac, const char **av)
+int cmd__dump_split_index(int ac UNUSED, const char **av)
 {
        struct split_index *si;
        int i;
index 99010614f6da9435e259e7e9a012e0154cbd9478..b4af9712fe5f2cfb815e22dc41a588890faecea5 100644 (file)
@@ -1,7 +1,10 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
 #include "dir.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 static int compare_untracked(const void *a_, const void *b_)
 {
@@ -40,7 +43,7 @@ static void dump(struct untracked_cache_dir *ucd, struct strbuf *base)
        strbuf_setlen(base, len);
 }
 
-int cmd__dump_untracked_cache(int ac, const char **av)
+int cmd__dump_untracked_cache(int ac UNUSED, const char **av UNUSED)
 {
        struct untracked_cache *uc;
        struct strbuf base = STRBUF_INIT;
@@ -51,7 +54,7 @@ int cmd__dump_untracked_cache(int ac, const char **av)
        xsetenv("GIT_CONFIG_VALUE_0", "keep", 1);
 
        setup_git_directory();
-       if (read_cache() < 0)
+       if (repo_read_index(the_repository) < 0)
                die("unable to read index file");
        uc = the_index.untracked;
        if (!uc) {
similarity index 71%
rename from builtin/env--helper.c
rename to t/helper/test-env-helper.c
index ea04c166364fe5776cd196950c3ee44a866076d7..1c486888a42d0da490f6b9a4d90941b09eda754b 100644 (file)
@@ -1,9 +1,9 @@
-#include "builtin.h"
-#include "config.h"
+#include "test-tool.h"
+#include "parse.h"
 #include "parse-options.h"
 
 static char const * const env__helper_usage[] = {
-       N_("git env--helper --type=[bool|ulong] <options> <env-var>"),
+       "test-tool env-helper --type=[bool|ulong] <options> <env-var>",
        NULL
 };
 
@@ -24,12 +24,12 @@ static int option_parse_type(const struct option *opt, const char *arg,
        else if (!strcmp(arg, "ulong"))
                *cmdmode = ENV_HELPER_TYPE_ULONG;
        else
-               die(_("unrecognized --type argument, %s"), arg);
+               die("unrecognized --type argument, %s", arg);
 
        return 0;
 }
 
-int cmd_env__helper(int argc, const char **argv, const char *prefix)
+int cmd__env_helper(int argc, const char **argv)
 {
        int exit_code = 0;
        const char *env_variable = NULL;
@@ -39,17 +39,17 @@ int cmd_env__helper(int argc, const char **argv, const char *prefix)
        unsigned long ret_ulong, default_ulong;
        enum cmdmode cmdmode = 0;
        struct option opts[] = {
-               OPT_CALLBACK_F(0, "type", &cmdmode, N_("type"),
-                              N_("value is given this type"), PARSE_OPT_NONEG,
+               OPT_CALLBACK_F(0, "type", &cmdmode, "type",
+                              "value is given this type", PARSE_OPT_NONEG,
                               option_parse_type),
-               OPT_STRING(0, "default", &env_default, N_("value"),
-                          N_("default for git_env_*(...) to fall back on")),
+               OPT_STRING(0, "default", &env_default, "value",
+                          "default for git_env_*(...) to fall back on"),
                OPT_BOOL(0, "exit-code", &exit_code,
-                        N_("be quiet only use git_env_*() value as exit code")),
+                        "be quiet only use git_env_*() value as exit code"),
                OPT_END(),
        };
 
-       argc = parse_options(argc, argv, prefix, opts, env__helper_usage,
+       argc = parse_options(argc, argv, NULL, opts, env__helper_usage,
                             PARSE_OPT_KEEP_UNKNOWN_OPT);
        if (env_default && !*env_default)
                usage_with_options(env__helper_usage, opts);
@@ -64,7 +64,7 @@ int cmd_env__helper(int argc, const char **argv, const char *prefix)
                if (env_default) {
                        default_int = git_parse_maybe_bool(env_default);
                        if (default_int == -1) {
-                               error(_("option `--default' expects a boolean value with `--type=bool`, not `%s`"),
+                               error("option `--default' expects a boolean value with `--type=bool`, not `%s`",
                                      env_default);
                                usage_with_options(env__helper_usage, opts);
                        }
@@ -79,7 +79,7 @@ int cmd_env__helper(int argc, const char **argv, const char *prefix)
        case ENV_HELPER_TYPE_ULONG:
                if (env_default) {
                        if (!git_parse_ulong(env_default, &default_ulong)) {
-                               error(_("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`"),
+                               error("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`",
                                      env_default);
                                usage_with_options(env__helper_usage, opts);
                        }
index b9d1200eb988e19f3f0a2c0aeed941a6eb44b9bb..8f59f6be4cff6c53da68ebb4ea7201e71793cbe1 100644 (file)
@@ -1,9 +1,10 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "git-compat-util.h"
 #include "object.h"
 #include "decorate.h"
+#include "repository.h"
 
-int cmd__example_decorate(int argc, const char **argv)
+int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED)
 {
        struct decoration n;
        struct object_id one_oid = { {1} };
@@ -71,5 +72,7 @@ int cmd__example_decorate(int argc, const char **argv)
        if (objects_noticed != 2)
                BUG("should have 2 objects");
 
+       clear_decoration(&n, NULL);
+
        return 0;
 }
index 12beee99ad2f4e70e804b21895522d6d362929d2..27323cb3672c35152e83e8741585a6c9520159f0 100644 (file)
@@ -8,7 +8,7 @@ int cmd_main(int argc, const char **argv)
        struct strbuf buf = STRBUF_INIT;
        FILE *f;
        int i;
-       const char *child_argv[] = { NULL, NULL };
+       struct child_process cmd = CHILD_PROCESS_INIT;
 
        /* First, print all parameters into $TRASH_DIRECTORY/ssh-output */
        if (!trash_directory)
@@ -17,6 +17,7 @@ int cmd_main(int argc, const char **argv)
        f = fopen(buf.buf, "w");
        if (!f)
                die("Could not write to %s", buf.buf);
+       strbuf_release(&buf);
        for (i = 0; i < argc; i++)
                fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:");
        fprintf(f, "\n");
@@ -25,6 +26,7 @@ int cmd_main(int argc, const char **argv)
        /* Now, evaluate the *last* parameter */
        if (argc < 2)
                return 0;
-       child_argv[0] = argv[argc - 1];
-       return run_command_v_opt(child_argv, RUN_USING_SHELL);
+       cmd.use_shell = 1;
+       strvec_push(&cmd.args, argv[argc - 1]);
+       return run_command(&cmd);
 }
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
deleted file mode 100644 (file)
index 45665ec..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * "git fast-rebase" builtin command
- *
- * FAST: Forking Any Subprocesses (is) Taboo
- *
- * This is meant SOLELY as a demo of what is possible.  sequencer.c and
- * rebase.c should be refactored to use the ideas here, rather than attempting
- * to extend this file to replace those (unless Phillip or Dscho say that
- * refactoring is too hard and we need a clean slate, but I'm guessing that
- * refactoring is the better route).
- */
-
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "test-tool.h"
-
-#include "cache-tree.h"
-#include "commit.h"
-#include "lockfile.h"
-#include "merge-ort.h"
-#include "refs.h"
-#include "revision.h"
-#include "sequencer.h"
-#include "strvec.h"
-#include "tree.h"
-
-static const char *short_commit_name(struct commit *commit)
-{
-       return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV);
-}
-
-static struct commit *peel_committish(const char *name)
-{
-       struct object *obj;
-       struct object_id oid;
-
-       if (get_oid(name, &oid))
-               return NULL;
-       obj = parse_object(the_repository, &oid);
-       return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
-}
-
-static char *get_author(const char *message)
-{
-       size_t len;
-       const char *a;
-
-       a = find_commit_header(message, "author", &len);
-       if (a)
-               return xmemdupz(a, len);
-
-       return NULL;
-}
-
-static struct commit *create_commit(struct tree *tree,
-                                   struct commit *based_on,
-                                   struct commit *parent)
-{
-       struct object_id ret;
-       struct object *obj;
-       struct commit_list *parents = NULL;
-       char *author;
-       char *sign_commit = NULL;
-       struct commit_extra_header *extra;
-       struct strbuf msg = STRBUF_INIT;
-       const char *out_enc = get_commit_output_encoding();
-       const char *message = logmsg_reencode(based_on, NULL, out_enc);
-       const char *orig_message = NULL;
-       const char *exclude_gpgsig[] = { "gpgsig", NULL };
-
-       commit_list_insert(parent, &parents);
-       extra = read_commit_extra_headers(based_on, exclude_gpgsig);
-       find_commit_subject(message, &orig_message);
-       strbuf_addstr(&msg, orig_message);
-       author = get_author(message);
-       reset_ident_date();
-       if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
-                                &ret, author, NULL, sign_commit, extra)) {
-               error(_("failed to write commit object"));
-               return NULL;
-       }
-       free(author);
-       strbuf_release(&msg);
-
-       obj = parse_object(the_repository, &ret);
-       return (struct commit *)obj;
-}
-
-int cmd__fast_rebase(int argc, const char **argv)
-{
-       struct commit *onto;
-       struct commit *last_commit = NULL, *last_picked_commit = NULL;
-       struct object_id head;
-       struct lock_file lock = LOCK_INIT;
-       struct strvec rev_walk_args = STRVEC_INIT;
-       struct rev_info revs;
-       struct commit *commit;
-       struct merge_options merge_opt;
-       struct tree *next_tree, *base_tree, *head_tree;
-       struct merge_result result;
-       struct strbuf reflog_msg = STRBUF_INIT;
-       struct strbuf branch_name = STRBUF_INIT;
-       int ret = 0;
-
-       /*
-        * test-tool stuff doesn't set up the git directory by default; need to
-        * do that manually.
-        */
-       setup_git_directory();
-
-       if (argc == 2 && !strcmp(argv[1], "-h")) {
-               printf("Sorry, I am not a psychiatrist; I can not give you the help you need.  Oh, you meant usage...\n");
-               exit(129);
-       }
-
-       if (argc != 5 || strcmp(argv[1], "--onto"))
-               die("usage: read the code, figure out how to use it, then do so");
-
-       onto = peel_committish(argv[2]);
-       strbuf_addf(&branch_name, "refs/heads/%s", argv[4]);
-
-       /* Sanity check */
-       if (get_oid("HEAD", &head))
-               die(_("Cannot read HEAD"));
-       assert(oideq(&onto->object.oid, &head));
-
-       hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
-       if (repo_read_index(the_repository) < 0)
-               BUG("Could not read index");
-
-       repo_init_revisions(the_repository, &revs, NULL);
-       revs.verbose_header = 1;
-       revs.max_parents = 1;
-       revs.cherry_mark = 1;
-       revs.limited = 1;
-       revs.reverse = 1;
-       revs.right_only = 1;
-       revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
-       revs.topo_order = 1;
-       strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
-
-       if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
-               ret = error(_("unhandled options"));
-               goto cleanup;
-       }
-
-       strvec_clear(&rev_walk_args);
-
-       if (prepare_revision_walk(&revs) < 0) {
-               ret = error(_("error preparing revisions"));
-               goto cleanup;
-       }
-
-       init_merge_options(&merge_opt, the_repository);
-       memset(&result, 0, sizeof(result));
-       merge_opt.show_rename_progress = 1;
-       merge_opt.branch1 = "HEAD";
-       head_tree = get_commit_tree(onto);
-       result.tree = head_tree;
-       last_commit = onto;
-       while ((commit = get_revision(&revs))) {
-               struct commit *base;
-
-               fprintf(stderr, "Rebasing %s...\r",
-                       oid_to_hex(&commit->object.oid));
-               assert(commit->parents && !commit->parents->next);
-               base = commit->parents->item;
-
-               next_tree = get_commit_tree(commit);
-               base_tree = get_commit_tree(base);
-
-               merge_opt.branch2 = short_commit_name(commit);
-               merge_opt.ancestor = xstrfmt("parent of %s", merge_opt.branch2);
-
-               merge_incore_nonrecursive(&merge_opt,
-                                         base_tree,
-                                         result.tree,
-                                         next_tree,
-                                         &result);
-
-               free((char*)merge_opt.ancestor);
-               merge_opt.ancestor = NULL;
-               if (!result.clean)
-                       break;
-               last_picked_commit = commit;
-               last_commit = create_commit(result.tree, commit, last_commit);
-       }
-
-       merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean);
-
-       if (result.clean < 0)
-               exit(128);
-
-       if (result.clean) {
-               fprintf(stderr, "\nDone.\n");
-               strbuf_addf(&reflog_msg, "finish rebase %s onto %s",
-                           oid_to_hex(&last_picked_commit->object.oid),
-                           oid_to_hex(&last_commit->object.oid));
-               if (update_ref(reflog_msg.buf, branch_name.buf,
-                              &last_commit->object.oid,
-                              &last_picked_commit->object.oid,
-                              REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
-                       error(_("could not update %s"), argv[4]);
-                       die("Failed to update %s", argv[4]);
-               }
-               if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
-                       die(_("unable to update HEAD"));
-
-               prime_cache_tree(the_repository, the_repository->index,
-                                result.tree);
-       } else {
-               fprintf(stderr, "\nAborting: Hit a conflict.\n");
-               strbuf_addf(&reflog_msg, "rebase progress up to %s",
-                           oid_to_hex(&last_picked_commit->object.oid));
-               if (update_ref(reflog_msg.buf, "HEAD",
-                              &last_commit->object.oid,
-                              &head,
-                              REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
-                       error(_("could not update %s"), argv[4]);
-                       die("Failed to update %s", argv[4]);
-               }
-       }
-       if (write_locked_index(&the_index, &lock,
-                              COMMIT_LOCK | SKIP_IF_UNCHANGED))
-               die(_("unable to write %s"), get_index_file());
-
-       ret = (result.clean == 0);
-cleanup:
-       strbuf_release(&reflog_msg);
-       strbuf_release(&branch_name);
-       release_revisions(&revs);
-       return ret;
-}
diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c
new file mode 100644 (file)
index 0000000..e8bd793
--- /dev/null
@@ -0,0 +1,50 @@
+#include "test-tool.h"
+#include "object-name.h"
+#include "object-store.h"
+#include "packfile.h"
+#include "parse-options.h"
+#include "setup.h"
+
+/*
+ * Display the path(s), one per line, of the packfile(s) containing
+ * the given object.
+ *
+ * If '--check-count <n>' is passed, then error out if the number of
+ * packfiles containing the object is not <n>.
+ */
+
+static const char *find_pack_usage[] = {
+       "test-tool find-pack [--check-count <n>] <object>",
+       NULL
+};
+
+int cmd__find_pack(int argc, const char **argv)
+{
+       struct object_id oid;
+       struct packed_git *p;
+       int count = -1, actual_count = 0;
+       const char *prefix = setup_git_directory();
+
+       struct option options[] = {
+               OPT_INTEGER('c', "check-count", &count, "expected number of packs"),
+               OPT_END(),
+       };
+
+       argc = parse_options(argc, argv, prefix, options, find_pack_usage, 0);
+       if (argc != 1)
+               usage(find_pack_usage[0]);
+
+       if (repo_get_oid(the_repository, argv[0], &oid))
+               die("cannot parse %s as an object name", argv[0]);
+
+       for (p = get_all_packs(the_repository); p; p = p->next)
+               if (find_pack_entry_one(oid.hash, p)) {
+                       printf("%s\n", p->pack_name);
+                       actual_count++;
+               }
+
+       if (count > -1 && count != actual_count)
+               die("bad packfile count %d instead of %d", actual_count, count);
+
+       return 0;
+}
index 54a4856c48c55af0af4e3ffe7cc3a62dce993842..8280984d08fe84b23722948709e83252e5d40895 100644 (file)
@@ -4,14 +4,16 @@
  */
 
 #include "test-tool.h"
-#include "cache.h"
 #include "parse-options.h"
 #include "fsmonitor-ipc.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "thread-utils.h"
 #include "trace2.h"
 
 #ifndef HAVE_FSMONITOR_DAEMON_BACKEND
-int cmd__fsmonitor_client(int argc, const char **argv)
+int cmd__fsmonitor_client(int argc UNUSED, const char **argv UNUSED)
 {
        die("fsmonitor--daemon not available on this platform");
 }
index 8ca988d6216e7895d8afea772e110b7eeaa875d1..47af843b6816005f2370fb130bc9f2bde873c029 100644 (file)
@@ -17,15 +17,16 @@ int cmd__genzeros(int argc, const char **argv)
 
        /* Writing out individual NUL bytes is slow... */
        while (count < 0)
-               if (write(1, zeros, ARRAY_SIZE(zeros)) < 0)
-                       return -1;
+               if (xwrite(1, zeros, ARRAY_SIZE(zeros)) < 0)
+                       die_errno("write error");
 
        while (count > 0) {
-               n = write(1, zeros, count < ARRAY_SIZE(zeros) ?
-                         count : ARRAY_SIZE(zeros));
+               n = xwrite(1, zeros,
+                          count < ARRAY_SIZE(zeros)
+                          ? count : ARRAY_SIZE(zeros));
 
                if (n < 0)
-                       return -1;
+                       die_errno("write error");
 
                count -= n;
        }
index f40d9ad0c2ddd293f2b1a1e7e142885ede078ae6..b235da594f749b1a6b680d0b674c5bb822b1b978 100644 (file)
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
 
 #define NUM_SECONDS 3
 
index 5860dab0ffac976bc3e6d0ef2da2153566477018..45d829c908fec95ded535ebcfe502c3bb4aa5441 100644 (file)
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 
 int cmd_hash_impl(int ac, const char **av, int algo)
 {
index 36ff07bd4beaefabe5f42d119b0eac26cf3ae8d9..0eb0b3d49cecc57ae08a4760065eb7b530a5f191 100644 (file)
@@ -2,6 +2,7 @@
 #include "git-compat-util.h"
 #include "hashmap.h"
 #include "strbuf.h"
+#include "string-list.h"
 
 struct test_entry
 {
@@ -150,6 +151,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
  */
 int cmd__hashmap(int argc, const char **argv)
 {
+       struct string_list parts = STRING_LIST_INIT_NODUP;
        struct strbuf line = STRBUF_INIT;
        int icase;
        struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase);
@@ -159,21 +161,26 @@ int cmd__hashmap(int argc, const char **argv)
 
        /* process commands from stdin */
        while (strbuf_getline(&line, stdin) != EOF) {
-               char *cmd, *p1 = NULL, *p2 = NULL;
+               char *cmd, *p1, *p2;
                unsigned int hash = 0;
                struct test_entry *entry;
 
                /* break line into command and up to two parameters */
-               cmd = strtok(line.buf, DELIM);
+               string_list_setlen(&parts, 0);
+               string_list_split_in_place(&parts, line.buf, DELIM, 2);
+               string_list_remove_empty_items(&parts, 0);
+
                /* ignore empty lines */
-               if (!cmd || *cmd == '#')
+               if (!parts.nr)
+                       continue;
+               if (!*parts.items[0].string || *parts.items[0].string == '#')
                        continue;
 
-               p1 = strtok(NULL, DELIM);
-               if (p1) {
+               cmd = parts.items[0].string;
+               p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
+               p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
+               if (p1)
                        hash = icase ? strihash(p1) : strhash(p1);
-                       p2 = strtok(NULL, DELIM);
-               }
 
                if (!strcmp("add", cmd) && p1 && p2) {
 
@@ -260,6 +267,7 @@ int cmd__hashmap(int argc, const char **argv)
                }
        }
 
+       string_list_clear(&parts, 0);
        strbuf_release(&line);
        hashmap_clear_and_free(&map, struct test_entry, ent);
        return 0;
index 811e89c1bcb0289e63588c2dbb801f34982cbc56..05f55eca21afb8792d60498fd9e1fc88d5499a72 100644 (file)
@@ -4,7 +4,7 @@
 /*
  * Read stdin and print a hexdump to stdout.
  */
-int cmd__hexdump(int argc, const char **argv)
+int cmd__hexdump(int argc UNUSED, const char **argv UNUSED)
 {
        char buf[1024];
        ssize_t i, len;
diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c
deleted file mode 100644 (file)
index fcd1096..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "test-tool.h"
-#include "cache.h"
-
-int cmd__index_version(int argc, const char **argv)
-{
-       struct cache_header hdr;
-       int version;
-
-       memset(&hdr,0,sizeof(hdr));
-       if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))
-               return 0;
-       version = ntohl(hdr.hdr_version);
-       printf("%d\n", version);
-       return 0;
-}
index 8c3edacc0007df7ddb52f2f65d00e44b95edeb5a..afe393f5974131b390f2eca932960cbd962f0b6d 100644 (file)
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "json-writer.h"
+#include "string-list.h"
 
 static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}";
 static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}";
@@ -395,35 +395,41 @@ static int unit_tests(void)
        return 0;
 }
 
-static void get_s(int line_nr, char **s_in)
+struct line {
+       struct string_list *parts;
+       size_t consumed_nr;
+       int nr;
+};
+
+static void get_s(struct line *line, char **s_in)
 {
-       *s_in = strtok(NULL, " ");
-       if (!*s_in)
-               die("line[%d]: expected: <s>", line_nr);
+       if (line->consumed_nr > line->parts->nr)
+               die("line[%d]: expected: <s>", line->nr);
+       *s_in = line->parts->items[line->consumed_nr++].string;
 }
 
-static void get_i(int line_nr, intmax_t *s_in)
+static void get_i(struct line *line, intmax_t *s_in)
 {
        char *s;
        char *endptr;
 
-       get_s(line_nr, &s);
+       get_s(line, &s);
 
        *s_in = strtol(s, &endptr, 10);
        if (*endptr || errno == ERANGE)
-               die("line[%d]: invalid integer value", line_nr);
+               die("line[%d]: invalid integer value", line->nr);
 }
 
-static void get_d(int line_nr, double *s_in)
+static void get_d(struct line *line, double *s_in)
 {
        char *s;
        char *endptr;
 
-       get_s(line_nr, &s);
+       get_s(line, &s);
 
        *s_in = strtod(s, &endptr);
        if (*endptr || errno == ERANGE)
-               die("line[%d]: invalid float value", line_nr);
+               die("line[%d]: invalid float value", line->nr);
 }
 
 static int pretty;
@@ -454,6 +460,7 @@ static char *get_trimmed_line(char *buf, int buf_size)
 
 static int scripted(void)
 {
+       struct string_list parts = STRING_LIST_INIT_NODUP;
        struct json_writer jw = JSON_WRITER_INIT;
        char buf[MAX_LINE_LENGTH];
        char *line;
@@ -471,66 +478,77 @@ static int scripted(void)
                die("expected first line to be 'object' or 'array'");
 
        while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) {
+               struct line state = { 0 };
                char *verb;
                char *key;
                char *s_value;
                intmax_t i_value;
                double d_value;
 
-               line_nr++;
+               state.parts = &parts;
+               state.nr = ++line_nr;
+
+               /* break line into command and zero or more tokens */
+               string_list_setlen(&parts, 0);
+               string_list_split_in_place(&parts, line, " ", -1);
+               string_list_remove_empty_items(&parts, 0);
+
+               /* ignore empty lines */
+               if (!parts.nr || !*parts.items[0].string)
+                       continue;
 
-               verb = strtok(line, " ");
+               verb = parts.items[state.consumed_nr++].string;
 
                if (!strcmp(verb, "end")) {
                        jw_end(&jw);
                }
                else if (!strcmp(verb, "object-string")) {
-                       get_s(line_nr, &key);
-                       get_s(line_nr, &s_value);
+                       get_s(&state, &key);
+                       get_s(&state, &s_value);
                        jw_object_string(&jw, key, s_value);
                }
                else if (!strcmp(verb, "object-int")) {
-                       get_s(line_nr, &key);
-                       get_i(line_nr, &i_value);
+                       get_s(&state, &key);
+                       get_i(&state, &i_value);
                        jw_object_intmax(&jw, key, i_value);
                }
                else if (!strcmp(verb, "object-double")) {
-                       get_s(line_nr, &key);
-                       get_i(line_nr, &i_value);
-                       get_d(line_nr, &d_value);
+                       get_s(&state, &key);
+                       get_i(&state, &i_value);
+                       get_d(&state, &d_value);
                        jw_object_double(&jw, key, i_value, d_value);
                }
                else if (!strcmp(verb, "object-true")) {
-                       get_s(line_nr, &key);
+                       get_s(&state, &key);
                        jw_object_true(&jw, key);
                }
                else if (!strcmp(verb, "object-false")) {
-                       get_s(line_nr, &key);
+                       get_s(&state, &key);
                        jw_object_false(&jw, key);
                }
                else if (!strcmp(verb, "object-null")) {
-                       get_s(line_nr, &key);
+                       get_s(&state, &key);
                        jw_object_null(&jw, key);
                }
                else if (!strcmp(verb, "object-object")) {
-                       get_s(line_nr, &key);
+                       get_s(&state, &key);
                        jw_object_inline_begin_object(&jw, key);
                }
                else if (!strcmp(verb, "object-array")) {
-                       get_s(line_nr, &key);
+                       get_s(&state, &key);
                        jw_object_inline_begin_array(&jw, key);
                }
                else if (!strcmp(verb, "array-string")) {
-                       get_s(line_nr, &s_value);
+                       get_s(&state, &s_value);
                        jw_array_string(&jw, s_value);
                }
                else if (!strcmp(verb, "array-int")) {
-                       get_i(line_nr, &i_value);
+                       get_i(&state, &i_value);
                        jw_array_intmax(&jw, i_value);
                }
                else if (!strcmp(verb, "array-double")) {
-                       get_i(line_nr, &i_value);
-                       get_d(line_nr, &d_value);
+                       get_i(&state, &i_value);
+                       get_d(&state, &d_value);
                        jw_array_double(&jw, i_value, d_value);
                }
                else if (!strcmp(verb, "array-true"))
@@ -553,6 +571,7 @@ static int scripted(void)
        printf("%s\n", jw.json.buf);
 
        jw_release(&jw);
+       string_list_clear(&parts, 0);
        return 0;
 }
 
index cd1b4c9736ef2c658dd3467506cbe5c1612cd4d2..187a115d5743f39f7b93e0bf128ead62262a12bb 100644 (file)
@@ -1,6 +1,12 @@
+#define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
+#include "environment.h"
+#include "name-hash.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
+#include "trace.h"
 
 static int single;
 static int multi;
@@ -32,7 +38,7 @@ static void dump_run(void)
        struct dir_entry *dir;
        struct cache_entry *ce;
 
-       read_cache();
+       repo_read_index(the_repository);
        if (single) {
                test_lazy_init_name_hash(&the_index, 0);
        } else {
@@ -49,7 +55,7 @@ static void dump_run(void)
                                ent /* member name */)
                printf("name %08x %s\n", ce->ent.hash, ce->name);
 
-       discard_cache();
+       discard_index(&the_index);
 }
 
 /*
@@ -66,7 +72,7 @@ static uint64_t time_runs(int try_threaded)
 
        for (i = 0; i < count; i++) {
                t0 = getnanotime();
-               read_cache();
+               repo_read_index(the_repository);
                t1 = getnanotime();
                nr_threads_used = test_lazy_init_name_hash(&the_index, try_threaded);
                t2 = getnanotime();
@@ -89,7 +95,7 @@ static uint64_t time_runs(int try_threaded)
                                   the_index.cache_nr);
                fflush(stdout);
 
-               discard_cache();
+               discard_index(&the_index);
        }
 
        avg = sum / count;
@@ -113,9 +119,9 @@ static void analyze_run(void)
        int i;
        int nr;
 
-       read_cache();
+       repo_read_index(the_repository);
        cache_nr_limit = the_index.cache_nr;
-       discard_cache();
+       discard_index(&the_index);
 
        nr = analyze;
        while (1) {
@@ -128,23 +134,23 @@ static void analyze_run(void)
                        nr = cache_nr_limit;
 
                for (i = 0; i < count; i++) {
-                       read_cache();
+                       repo_read_index(the_repository);
                        the_index.cache_nr = nr; /* cheap truncate of index */
                        t1s = getnanotime();
                        test_lazy_init_name_hash(&the_index, 0);
                        t2s = getnanotime();
                        sum_single += (t2s - t1s);
                        the_index.cache_nr = cache_nr_limit;
-                       discard_cache();
+                       discard_index(&the_index);
 
-                       read_cache();
+                       repo_read_index(the_repository);
                        the_index.cache_nr = nr; /* cheap truncate of index */
                        t1m = getnanotime();
                        nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
                        t2m = getnanotime();
                        sum_multi += (t2m - t1m);
                        the_index.cache_nr = cache_nr_limit;
-                       discard_cache();
+                       discard_index(&the_index);
 
                        if (!nr_threads_used)
                                printf("    [size %8d] [single %f]   non-threaded code path used\n",
index 4079fdee06776c8179fbc15283141f3890ff013e..d0db5ff26f0772373b5ccba7ad0aa89df01548ff 100644 (file)
@@ -1,17 +1,21 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "match-trees.h"
+#include "object-name.h"
+#include "repository.h"
+#include "setup.h"
 #include "tree.h"
 
-int cmd__match_trees(int ac, const char **av)
+int cmd__match_trees(int ac UNUSED, const char **av)
 {
        struct object_id hash1, hash2, shifted;
        struct tree *one, *two;
 
        setup_git_directory();
 
-       if (get_oid(av[1], &hash1))
+       if (repo_get_oid(the_repository, av[1], &hash1))
                die("cannot parse %s as an object name", av[1]);
-       if (get_oid(av[2], &hash2))
+       if (repo_get_oid(the_repository, av[2], &hash2))
                die("cannot parse %s as an object name", av[2]);
        one = parse_tree_indirect(&hash1);
        if (!one)
index 335e5bb3a9008c8ce1cb31198bd54808c70566dc..42ccc8705167b07266a5c3902fba7cb7a8081c64 100644 (file)
@@ -1,6 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "mem-pool.h"
 #include "mergesort.h"
+#include "strbuf.h"
 
 static uint32_t minstd_rand(uint32_t *state)
 {
index d1324d086a24c0575e6beadaeef2ce1e8b6a80f0..aafe398ef07e538ef774fedc72cd27189391f2fb 100644 (file)
@@ -1,14 +1,16 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 #include "oid-array.h"
+#include "setup.h"
+#include "strbuf.h"
 
-static int print_oid(const struct object_id *oid, void *data)
+static int print_oid(const struct object_id *oid, void *data UNUSED)
 {
        puts(oid_to_hex(oid));
        return 0;
 }
 
-int cmd__oid_array(int argc, const char **argv)
+int cmd__oid_array(int argc UNUSED, const char **argv UNUSED)
 {
        struct oid_array array = OID_ARRAY_INIT;
        struct strbuf line = STRBUF_INIT;
index 0acf99931ee176982d61078dc0e2d882377c05fd..bd30244a54cc88370ab27da5d25108b78c296f85 100644 (file)
@@ -1,7 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "object-name.h"
 #include "oidmap.h"
+#include "repository.h"
+#include "setup.h"
 #include "strbuf.h"
+#include "string-list.h"
 
 /* key is an oid and value is a name (could be a refname for example) */
 struct test_entry {
@@ -21,8 +25,9 @@ struct test_entry {
  * iterate -> oidkey1 namevalue1\noidkey2 namevalue2\n...
  *
  */
-int cmd__oidmap(int argc, const char **argv)
+int cmd__oidmap(int argc UNUSED, const char **argv UNUSED)
 {
+       struct string_list parts = STRING_LIST_INIT_NODUP;
        struct strbuf line = STRBUF_INIT;
        struct oidmap map = OIDMAP_INIT;
 
@@ -33,23 +38,28 @@ int cmd__oidmap(int argc, const char **argv)
 
        /* process commands from stdin */
        while (strbuf_getline(&line, stdin) != EOF) {
-               char *cmd, *p1 = NULL, *p2 = NULL;
+               char *cmd, *p1, *p2;
                struct test_entry *entry;
                struct object_id oid;
 
                /* break line into command and up to two parameters */
-               cmd = strtok(line.buf, DELIM);
+               string_list_setlen(&parts, 0);
+               string_list_split_in_place(&parts, line.buf, DELIM, 2);
+               string_list_remove_empty_items(&parts, 0);
+
                /* ignore empty lines */
-               if (!cmd || *cmd == '#')
+               if (!parts.nr)
+                       continue;
+               if (!*parts.items[0].string || *parts.items[0].string == '#')
                        continue;
 
-               p1 = strtok(NULL, DELIM);
-               if (p1)
-                       p2 = strtok(NULL, DELIM);
+               cmd = parts.items[0].string;
+               p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
+               p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
 
                if (!strcmp("put", cmd) && p1 && p2) {
 
-                       if (get_oid(p1, &oid)) {
+                       if (repo_get_oid(the_repository, p1, &oid)) {
                                printf("Unknown oid: %s\n", p1);
                                continue;
                        }
@@ -67,7 +77,7 @@ int cmd__oidmap(int argc, const char **argv)
 
                } else if (!strcmp("get", cmd) && p1) {
 
-                       if (get_oid(p1, &oid)) {
+                       if (repo_get_oid(the_repository, p1, &oid)) {
                                printf("Unknown oid: %s\n", p1);
                                continue;
                        }
@@ -80,7 +90,7 @@ int cmd__oidmap(int argc, const char **argv)
 
                } else if (!strcmp("remove", cmd) && p1) {
 
-                       if (get_oid(p1, &oid)) {
+                       if (repo_get_oid(the_repository, p1, &oid)) {
                                printf("Unknown oid: %s\n", p1);
                                continue;
                        }
@@ -106,6 +116,7 @@ int cmd__oidmap(int argc, const char **argv)
                }
        }
 
+       string_list_clear(&parts, 0);
        strbuf_release(&line);
        oidmap_free(&map, 1);
        return 0;
index d48a409f4e4fb377a7cbc4684fa05737ea32f2c1..c7a1d4c6420118e06923b2aa0f28cc6f8e983edc 100644 (file)
@@ -1,14 +1,16 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 #include "oidtree.h"
+#include "setup.h"
+#include "strbuf.h"
 
-static enum cb_next print_oid(const struct object_id *oid, void *data)
+static enum cb_next print_oid(const struct object_id *oid, void *data UNUSED)
 {
        puts(oid_to_hex(oid));
        return CB_CONTINUE;
 }
 
-int cmd__oidtree(int argc, const char **argv)
+int cmd__oidtree(int argc UNUSED, const char **argv UNUSED)
 {
        struct oidtree ot;
        struct strbuf line = STRBUF_INIT;
index 8cb0d53840f3dc60d583fa1b72f95572d2148d11..47dc21171125cde0bd67877517b53a26e8a289eb 100644 (file)
@@ -2,7 +2,7 @@
 #include "git-compat-util.h"
 #include "thread-utils.h"
 
-int cmd__online_cpus(int argc, const char **argv)
+int cmd__online_cpus(int argc UNUSED, const char **argv UNUSED)
 {
        printf("%d\n", online_cpus());
        return 0;
index f7b79daf4c03843c5003100b63666f8dbdbfd1d5..67a964ef9041a061497c0b10698413d360f561e1 100644 (file)
@@ -1,9 +1,10 @@
-#include "git-compat-util.h"
 #include "test-tool.h"
+#include "hex.h"
 #include "strbuf.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "packfile.h"
 #include "pack-mtimes.h"
+#include "setup.h"
 
 static void dump_mtimes(struct packed_git *p)
 {
index 506835521a463aebd1a06259cf96e05d71e3833b..ded8116cc5119f9d22d2d083cb8fa189e3783904 100644 (file)
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "parse-options.h"
+#include "strbuf.h"
 #include "string-list.h"
 #include "trace2.h"
 
@@ -21,6 +21,19 @@ static struct {
        int unset;
 } length_cb;
 
+static int mode34_callback(const struct option *opt, const char *arg, int unset)
+{
+       if (unset)
+               *(int *)opt->value = 0;
+       else if (!strcmp(arg, "3"))
+               *(int *)opt->value = 3;
+       else if (!strcmp(arg, "4"))
+               *(int *)opt->value = 4;
+       else
+               return error("invalid value for '%s': '%s'", "--mode34", arg);
+       return 0;
+}
+
 static int length_callback(const struct option *opt, const char *arg, int unset)
 {
        length_cb.called = 1;
@@ -124,6 +137,9 @@ int cmd__parse_options(int argc, const char **argv)
                OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
                OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1),
                OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2),
+               OPT_CALLBACK_F(0, "mode34", &integer, "(3|4)",
+                       "set integer to 3 or 4 (cmdmode option)",
+                       PARSE_OPT_CMDMODE, mode34_callback),
                OPT_CALLBACK('L', "length", &integer, "str",
                        "get length of <str>", length_callback),
                OPT_FILENAME('F', "file", &file, "set file to <file>"),
@@ -133,6 +149,8 @@ int cmd__parse_options(int argc, const char **argv)
                OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
                OPT_STRING('o', NULL, &string, "str", "get another string"),
                OPT_NOOP_NOARG(0, "obsolete"),
+               OPT_SET_INT_F(0, "longhelp", &integer, "help text of this entry\n"
+                             "spans multiple lines", 0, PARSE_OPT_NONEG),
                OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
                OPT_GROUP("Magic arguments"),
                OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
@@ -263,14 +281,14 @@ int cmd__parse_options_flags(int argc, const char **argv)
        return parse_options_flags__cmd(argc, argv, test_flags);
 }
 
-static int subcmd_one(int argc, const char **argv, const char *prefix)
+static int subcmd_one(int argc, const char **argv, const char *prefix UNUSED)
 {
        printf("fn: subcmd_one\n");
        print_args(argc, argv);
        return 0;
 }
 
-static int subcmd_two(int argc, const char **argv, const char *prefix)
+static int subcmd_two(int argc, const char **argv, const char *prefix UNUSED)
 {
        printf("fn: subcmd_two\n");
        print_args(argc, argv);
index b3e08cef4b3f86ba95509f1d66345f7ca6016ea3..89ecefd1cdbec1da7c2cd43dbceebeb05ac61708 100644 (file)
@@ -1,12 +1,11 @@
 #include "test-tool.h"
 #include "parse-options.h"
 #include "pathspec.h"
-#include "gettext.h"
 
 int cmd__parse_pathspec_file(int argc, const char **argv)
 {
        struct pathspec pathspec;
-       const char *pathspec_from_file = NULL;
+       char *pathspec_from_file = NULL;
        int pathspec_file_nul = 0, i;
 
        static const char *const usage[] = {
@@ -29,5 +28,6 @@ int cmd__parse_pathspec_file(int argc, const char **argv)
                printf("%s\n", pathspec.items[i].original);
 
        clear_pathspec(&pathspec);
+       free(pathspec_from_file);
        return 0;
 }
index 3f102cfddd3f4b6fe85391a8a2fc3a11b9b509c7..910a12861442264ca951a1a2ef27e6fb20eb7985 100644 (file)
@@ -1,7 +1,8 @@
-#include "cache.h"
 #include "test-tool.h"
+#include "hex.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "setup.h"
 
 /*
  * Prints the size of the object corresponding to the given hash in a specific
index d20e1b7a18d613a97b00e29fa491dc7e1654b21a..70396fa38454e86d661b9a4857e3ef463ee40f63 100644 (file)
@@ -1,6 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "abspath.h"
+#include "environment.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "setup.h"
 #include "string-list.h"
+#include "trace.h"
 #include "utf8.h"
 
 /*
@@ -8,7 +13,8 @@
  * GIT_CEILING_DIRECTORIES.  If the path is unusable for some reason,
  * die with an explanation.
  */
-static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+static int normalize_ceiling_entry(struct string_list_item *item,
+                                  void *data UNUSED)
 {
        char *ceil = item->string;
 
index 5258fdddba0eb36829c04216fbb71b1a196810d0..5d0b2a2e10fa66e16dd954f97ba7561b106fce50 100644 (file)
@@ -1,5 +1,4 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "grep.h"
 
 int cmd__pcre2_config(int argc, const char **argv)
index c5e052e537805075682f16204f1ab16991a8e0fe..4daa82f00fcb5f0b178aa912033c906fe14c7718 100644 (file)
@@ -1,6 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "test-tool.h"
 #include "pkt-line.h"
+#include "sideband.h"
+#include "write-or-die.h"
+#include "parse-options.h"
 
 static void pack_line(const char *line)
 {
@@ -63,12 +66,33 @@ static void unpack(void)
        }
 }
 
-static void unpack_sideband(void)
+static void unpack_sideband(int argc, const char **argv)
 {
        struct packet_reader reader;
-       packet_reader_init(&reader, 0, NULL, 0,
-                          PACKET_READ_GENTLE_ON_EOF |
-                          PACKET_READ_CHOMP_NEWLINE);
+       int options = PACKET_READ_GENTLE_ON_EOF;
+       int chomp_newline = 1;
+       int reader_use_sideband = 0;
+       const char *const unpack_sideband_usage[] = {
+               "test_tool unpack_sideband [options...]", NULL
+       };
+       struct option cmd_options[] = {
+               OPT_BOOL(0, "reader-use-sideband", &reader_use_sideband,
+                        "set use_sideband bit for packet reader (Default: off)"),
+               OPT_BOOL(0, "chomp-newline", &chomp_newline,
+                        "chomp newline in packet (Default: on)"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, "", cmd_options, unpack_sideband_usage,
+                            0);
+       if (argc > 0)
+               usage_msg_opt(_("too many arguments"), unpack_sideband_usage,
+                             cmd_options);
+
+       if (chomp_newline)
+               options |= PACKET_READ_CHOMP_NEWLINE;
+       packet_reader_init(&reader, 0, NULL, 0, options);
+       reader.use_sideband = reader_use_sideband;
 
        while (packet_reader_read(&reader) != PACKET_READ_EOF) {
                int band;
@@ -78,6 +102,17 @@ static void unpack_sideband(void)
                case PACKET_READ_EOF:
                        break;
                case PACKET_READ_NORMAL:
+                       /*
+                        * When the "use_sideband" field of the reader is turned
+                        * on, sideband packets other than the payload have been
+                        * parsed and consumed in packet_reader_read(), and only
+                        * the payload arrives here.
+                        */
+                       if (reader.use_sideband) {
+                               write_or_die(1, reader.line, reader.pktlen - 1);
+                               break;
+                       }
+
                        band = reader.line[0] & 0xff;
                        if (band < 1 || band > 2)
                                continue; /* skip non-sideband packets */
@@ -96,15 +131,31 @@ static void unpack_sideband(void)
 
 static int send_split_sideband(void)
 {
+       const char *foo = "Foo.\n";
+       const char *bar = "Bar.\n";
        const char *part1 = "Hello,";
        const char *primary = "\001primary: regular output\n";
        const char *part2 = " world!\n";
 
+       /* Each sideband message has a trailing newline character. */
+       send_sideband(1, 2, foo, strlen(foo), LARGE_PACKET_MAX);
+       send_sideband(1, 2, bar, strlen(bar), LARGE_PACKET_MAX);
+
+       /*
+        * One sideband message is divided into part1 and part2
+        * by the primary message.
+        */
        send_sideband(1, 2, part1, strlen(part1), LARGE_PACKET_MAX);
        packet_write(1, primary, strlen(primary));
        send_sideband(1, 2, part2, strlen(part2), LARGE_PACKET_MAX);
        packet_response_end(1);
 
+       /*
+        * We use unpack_sideband() to consume packets. A flush packet
+        * is required to end parsing.
+        */
+       packet_flush(1);
+
        return 0;
 }
 
@@ -125,7 +176,7 @@ int cmd__pkt_line(int argc, const char **argv)
        else if (!strcmp(argv[1], "unpack"))
                unpack();
        else if (!strcmp(argv[1], "unpack-sideband"))
-               unpack_sideband();
+               unpack_sideband(argc - 1, argv + 1);
        else if (!strcmp(argv[1], "send-split-sideband"))
                send_split_sideband();
        else if (!strcmp(argv[1], "receive-sideband"))
index 133b5e6f4ae5fdbd90f6594223df60f918a036f4..f0bf255f5f0581d91b5f189dbf06f9b3cf910cc9 100644 (file)
@@ -1,8 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "prio-queue.h"
 
-static int intcmp(const void *va, const void *vb, void *data)
+static int intcmp(const void *va, const void *vb, void *data UNUSED)
 {
        const int *a = va, *b = vb;
        return *a - *b;
@@ -17,7 +16,7 @@ static void show(int *v)
        free(v);
 }
 
-int cmd__prio_queue(int argc, const char **argv)
+int cmd__prio_queue(int argc UNUSED, const char **argv)
 {
        struct prio_queue pq = { intcmp };
 
index cc08506cf0bb73b1aefda41079f60cba1edcb9c1..f30022d2225b0a81cb026a9ccc24366ebdb9d2b4 100644 (file)
@@ -1,12 +1,13 @@
-#include "cache.h"
+#include "test-tool.h"
 #include "connect.h"
+#include "hex.h"
 #include "parse-options.h"
 #include "pkt-line.h"
+#include "setup.h"
 #include "sigchain.h"
-#include "test-tool.h"
 
 static const char *proc_receive_usage[] = {
-       "test-tool proc-receive [<options>...]",
+       "test-tool proc-receive [<options>]",
        NULL
 };
 
index 6cc9735b60127b2f5bab7d1728dc013a27b7adf4..66acb6a06c99b062952c0c34e6f527ecb06bc222 100644 (file)
@@ -19,7 +19,6 @@
  */
 #define GIT_TEST_PROGRESS_ONLY
 #include "test-tool.h"
-#include "gettext.h"
 #include "parse-options.h"
 #include "progress.h"
 #include "strbuf.h"
index 2f65c7f6a55bc146ea69ea9ef85d45a1f6172ac3..1e159a754db6db5d02eaa81072b0ab19fa863004 100644 (file)
@@ -1,10 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "commit.h"
 #include "commit-reach.h"
-#include "config.h"
-#include "parse-options.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
 #include "ref-filter.h"
+#include "setup.h"
 #include "string-list.h"
 #include "tag.h"
 
@@ -57,7 +58,7 @@ int cmd__reach(int ac, const char **av)
                if (buf.len < 3)
                        continue;
 
-               if (get_oid_committish(buf.buf + 2, &oid))
+               if (repo_get_oid_committish(the_repository, buf.buf + 2, &oid))
                        die("failed to resolve %s", buf.buf + 2);
 
                orig = parse_object(r, &oid);
@@ -106,13 +107,17 @@ int cmd__reach(int ac, const char **av)
        if (!strcmp(av[1], "ref_newer"))
                printf("%s(A,B):%d\n", av[1], ref_newer(&oid_A, &oid_B));
        else if (!strcmp(av[1], "in_merge_bases"))
-               printf("%s(A,B):%d\n", av[1], in_merge_bases(A, B));
+               printf("%s(A,B):%d\n", av[1],
+                      repo_in_merge_bases(the_repository, A, B));
        else if (!strcmp(av[1], "in_merge_bases_many"))
-               printf("%s(A,X):%d\n", av[1], in_merge_bases_many(A, X_nr, X_array));
+               printf("%s(A,X):%d\n", av[1],
+                      repo_in_merge_bases_many(the_repository, A, X_nr, X_array));
        else if (!strcmp(av[1], "is_descendant_of"))
                printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
        else if (!strcmp(av[1], "get_merge_bases_many")) {
-               struct commit_list *list = get_merge_bases_many(A, X_nr, X_array);
+               struct commit_list *list = repo_get_merge_bases_many(the_repository,
+                                                                    A, X_nr,
+                                                                    X_array);
                printf("%s(A,X):\n", av[1]);
                print_sorted_commit_ids(list);
        } else if (!strcmp(av[1], "reduce_heads")) {
@@ -131,7 +136,7 @@ int cmd__reach(int ac, const char **av)
 
                printf("%s(X,_,_,0,0):%d\n", av[1], can_all_from_reach_with_flag(&X_obj, 2, 4, 0, 0));
        } else if (!strcmp(av[1], "commit_contains")) {
-               struct ref_filter filter;
+               struct ref_filter filter = REF_FILTER_INIT;
                struct contains_cache cache;
                init_contains_cache(&cache);
 
index b736ef16421ba14e81f8e8c1654d936051efecbb..1acd3623465283e4751a75bce0cbb8bf1926bcd7 100644 (file)
@@ -1,6 +1,9 @@
+#define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
 #include "config.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 int cmd__read_cache(int argc, const char **argv)
 {
@@ -20,7 +23,7 @@ int cmd__read_cache(int argc, const char **argv)
        git_config(git_default_config, NULL);
 
        for (i = 0; i < cnt; i++) {
-               read_cache();
+               repo_read_index(the_repository);
                if (name) {
                        int pos;
 
@@ -33,7 +36,7 @@ int cmd__read_cache(int argc, const char **argv)
                               ce_uptodate(the_index.cache[pos]) ? "" : " not");
                        write_file(name, "%d\n", i);
                }
-               discard_cache();
+               discard_index(&the_index);
        }
        return 0;
 }
index 98b73bb8f25aabea7384cde0fb639cff16cf631e..8c7a83f578f41c3d90bcb17628bd5307e5927636 100644 (file)
@@ -1,11 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "commit-graph.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "bloom.h"
+#include "setup.h"
 
-int cmd__read_graph(int argc, const char **argv)
+int cmd__read_graph(int argc UNUSED, const char **argv UNUSED)
 {
        struct commit_graph *graph = NULL;
        struct object_directory *odb;
index 27072ba94d76005b6f49e04f60f2e697efd0e073..4acae41bb993c883970227f394a61603a6e7152d 100644 (file)
@@ -1,9 +1,12 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 #include "midx.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "pack-bitmap.h"
+#include "packfile.h"
+#include "setup.h"
+#include "gettext.h"
 
 static int read_midx_file(const char *object_dir, int show_objects)
 {
@@ -77,7 +80,7 @@ static int read_midx_checksum(const char *object_dir)
 static int read_midx_preferred_pack(const char *object_dir)
 {
        struct multi_pack_index *midx = NULL;
-       struct bitmap_index *bitmap = NULL;
+       uint32_t preferred_pack;
 
        setup_git_directory();
 
@@ -85,23 +88,45 @@ static int read_midx_preferred_pack(const char *object_dir)
        if (!midx)
                return 1;
 
-       bitmap = prepare_bitmap_git(the_repository);
-       if (!bitmap)
+       if (midx_preferred_pack(midx, &preferred_pack) < 0) {
+               warning(_("could not determine MIDX preferred pack"));
                return 1;
-       if (!bitmap_is_midx(bitmap)) {
-               free_bitmap_index(bitmap);
+       }
+
+       printf("%s\n", midx->pack_names[preferred_pack]);
+       return 0;
+}
+
+static int read_midx_bitmapped_packs(const char *object_dir)
+{
+       struct multi_pack_index *midx = NULL;
+       struct bitmapped_pack pack;
+       uint32_t i;
+
+       setup_git_directory();
+
+       midx = load_multi_pack_index(object_dir, 1);
+       if (!midx)
                return 1;
+
+       for (i = 0; i < midx->num_packs; i++) {
+               if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0)
+                       return 1;
+
+               printf("%s\n", pack_basename(pack.p));
+               printf("  bitmap_pos: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_pos);
+               printf("  bitmap_nr: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_nr);
        }
 
-       printf("%s\n", midx->pack_names[midx_preferred_pack(bitmap)]);
-       free_bitmap_index(bitmap);
+       close_midx(midx);
+
        return 0;
 }
 
 int cmd__read_midx(int argc, const char **argv)
 {
        if (!(argc == 2 || argc == 3))
-               usage("read-midx [--show-objects|--checksum|--preferred-pack] <object-dir>");
+               usage("read-midx [--show-objects|--checksum|--preferred-pack|--bitmap] <object-dir>");
 
        if (!strcmp(argv[1], "--show-objects"))
                return read_midx_file(argv[2], 1);
@@ -109,5 +134,7 @@ int cmd__read_midx(int argc, const char **argv)
                return read_midx_checksum(argv[2]);
        else if (!strcmp(argv[1], "--preferred-pack"))
                return read_midx_preferred_pack(argv[2]);
+       else if (!strcmp(argv[1], "--bitmap"))
+               return read_midx_bitmapped_packs(argv[2]);
        return read_midx_file(argv[1], 0);
 }
index ae8a5648daf5c1385afe43f327bf9a5a3d98e1f1..702ec1f128ad42443bc093614dee971c7106af77 100644 (file)
@@ -1,9 +1,13 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 #include "refs.h"
+#include "setup.h"
 #include "worktree.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
+#include "strbuf.h"
+#include "revision.h"
 
 struct flag_definition {
        const char *name;
@@ -115,8 +119,16 @@ static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE),
 static int cmd_pack_refs(struct ref_store *refs, const char **argv)
 {
        unsigned int flags = arg_flags(*argv++, "flags", pack_flags);
+       static struct ref_exclusions exclusions = REF_EXCLUSIONS_INIT;
+       static struct string_list included_refs = STRING_LIST_INIT_NODUP;
+       struct pack_refs_opts pack_opts = { .flags = flags,
+                                           .exclusions = &exclusions,
+                                           .includes = &included_refs };
 
-       return refs_pack_refs(refs, flags);
+       if (pack_opts.flags & PACK_REFS_ALL)
+               string_list_append(pack_opts.includes, "*");
+
+       return refs_pack_refs(refs, &pack_opts);
 }
 
 static int cmd_create_symref(struct ref_store *refs, const char **argv)
@@ -174,6 +186,15 @@ static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
        return refs_for_each_ref_in(refs, prefix, each_ref, NULL);
 }
 
+static int cmd_for_each_ref__exclude(struct ref_store *refs, const char **argv)
+{
+       const char *prefix = notnull(*argv++, "prefix");
+       const char **exclude_patterns = argv;
+
+       return refs_for_each_fullref_in(refs, prefix, exclude_patterns, each_ref,
+                                       NULL);
+}
+
 static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
 {
        struct object_id oid = *null_oid();
@@ -200,7 +221,8 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)
        return ret;
 }
 
-static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
+static int cmd_for_each_reflog(struct ref_store *refs,
+                              const char **argv UNUSED)
 {
        return refs_for_each_reflog(refs, each_ref, NULL);
 }
@@ -255,11 +277,6 @@ static int cmd_delete_reflog(struct ref_store *refs, const char **argv)
        return refs_delete_reflog(refs, refname);
 }
 
-static int cmd_reflog_expire(struct ref_store *refs, const char **argv)
-{
-       die("not supported yet");
-}
-
 static int cmd_delete_ref(struct ref_store *refs, const char **argv)
 {
        const char *msg = notnull(*argv++, "msg");
@@ -281,16 +298,19 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
        const char *new_sha1_buf = notnull(*argv++, "new-sha1");
        const char *old_sha1_buf = notnull(*argv++, "old-sha1");
        unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
-       struct object_id old_oid;
+       struct object_id old_oid, *old_oid_ptr = NULL;
        struct object_id new_oid;
 
-       if (get_oid_hex(old_sha1_buf, &old_oid))
-               die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+       if (*old_sha1_buf) {
+               if (get_oid_hex(old_sha1_buf, &old_oid))
+                       die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+               old_oid_ptr = &old_oid;
+       }
        if (get_oid_hex(new_sha1_buf, &new_oid))
                die("cannot parse %s as %s", new_sha1_buf, the_hash_algo->name);
 
        return refs_update_ref(refs, msg, refname,
-                              &new_oid, &old_oid,
+                              &new_oid, old_oid_ptr,
                               flags, UPDATE_REFS_DIE_ON_ERR);
 }
 
@@ -305,6 +325,7 @@ static struct command commands[] = {
        { "delete-refs", cmd_delete_refs },
        { "rename-ref", cmd_rename_ref },
        { "for-each-ref", cmd_for_each_ref },
+       { "for-each-ref--exclude", cmd_for_each_ref__exclude },
        { "resolve-ref", cmd_resolve_ref },
        { "verify-ref", cmd_verify_ref },
        { "for-each-reflog", cmd_for_each_reflog },
@@ -313,7 +334,6 @@ static struct command commands[] = {
        { "reflog-exists", cmd_reflog_exists },
        { "create-reflog", cmd_create_reflog },
        { "delete-reflog", cmd_delete_reflog },
-       { "reflog-expire", cmd_reflog_expire },
        /*
         * backend transaction functions can't be tested separately
         */
@@ -322,7 +342,7 @@ static struct command commands[] = {
        { NULL, NULL }
 };
 
-int cmd__ref_store(int argc, const char **argv)
+int cmd__ref_store(int argc UNUSED, const char **argv)
 {
        struct ref_store *refs;
        const char *func;
index 1f0a28cbb64de427ff8d8c542e0b1166250093e2..00237ef0d9e08fb9747010891b64d1d5ca421a29 100644 (file)
@@ -1,3 +1,4 @@
+#include "reftable/system.h"
 #include "reftable/reftable-tests.h"
 #include "test-tool.h"
 
index bd871a735b4fbb19ed99e9bfab56f13d138a0567..80042eafc20603e0c84cb83d3ba400cd316010c7 100644 (file)
@@ -30,7 +30,7 @@ static int test_regex_bug(void)
        if (regexec(&r, str, 1, m, 0))
                die("no match of pattern '%s' to string '%s'", pat, str);
 
-       /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957  */
+       /* https://sourceware.org/bugzilla/show_bug.cgi?id=3957 */
        if (m[0].rm_so == 3) /* matches '\n' when it should not */
                die("regex bug confirmed: re-build git with NO_REGEX=1");
 
index 56f0e3c1bef293dd505e28af4b48dfce11490b3c..0c7c5aa4dd7641a72b930b57eeff17dbf90c4c16 100644 (file)
@@ -1,11 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "commit-graph.h"
 #include "commit.h"
-#include "config.h"
-#include "object-store.h"
+#include "environment.h"
+#include "hex.h"
 #include "object.h"
 #include "repository.h"
+#include "setup.h"
 #include "tree.h"
 
 static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
index 4a45d5bac2af5678eda5ad8b49c7747a3973878e..f346951bc28c99e4791d841fac0d873a128cc973 100644 (file)
@@ -9,17 +9,19 @@
  */
 
 #include "test-tool.h"
-#include "cache.h"
 #include "commit.h"
 #include "diff.h"
+#include "repository.h"
 #include "revision.h"
+#include "setup.h"
 
 static void print_commit(struct commit *commit)
 {
        struct strbuf sb = STRBUF_INIT;
        struct pretty_print_context ctx = {0};
        ctx.date_mode.type = DATE_NORMAL;
-       format_commit_message(commit, " %m %s", &sb, &ctx);
+       repo_format_commit_message(the_repository, commit, " %m %s", &sb,
+                                  &ctx);
        printf("%s\n", sb.buf);
        strbuf_release(&sb);
 }
index c9283b47afafdccf67fa6784c877518a404c868f..c0ed8722c8779b7617382959ff92f2e0a2ea8db6 100644 (file)
@@ -9,8 +9,6 @@
  */
 
 #include "test-tool.h"
-#include "git-compat-util.h"
-#include "cache.h"
 #include "run-command.h"
 #include "strvec.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "thread-utils.h"
 #include "wildmatch.h"
-#include "gettext.h"
 
 static int number_callbacks;
 static int parallel_next(struct child_process *cp,
                         struct strbuf *err,
                         void *cb,
-                        void **task_cb)
+                        void **task_cb UNUSED)
 {
        struct child_process *d = cb;
        if (number_callbacks >= 4)
@@ -40,10 +37,10 @@ static int parallel_next(struct child_process *cp,
        return 1;
 }
 
-static int no_job(struct child_process *cp,
+static int no_job(struct child_process *cp UNUSED,
                  struct strbuf *err,
-                 void *cb,
-                 void **task_cb)
+                 void *cb UNUSED,
+                 void **task_cb UNUSED)
 {
        if (err)
                strbuf_addstr(err, "no further jobs available\n");
@@ -52,10 +49,10 @@ static int no_job(struct child_process *cp,
        return 0;
 }
 
-static int task_finished(int result,
+static int task_finished(int result UNUSED,
                         struct strbuf *err,
-                        void *pp_cb,
-                        void *pp_task_cb)
+                        void *pp_cb UNUSED,
+                        void *pp_task_cb UNUSED)
 {
        if (err)
                strbuf_addstr(err, "asking for a quick stop\n");
@@ -136,7 +133,7 @@ static const char * const testsuite_usage[] = {
 static int testsuite(int argc, const char **argv)
 {
        struct testsuite suite = TESTSUITE_INIT;
-       int max_jobs = 1, i, ret;
+       int max_jobs = 1, i, ret = 0;
        DIR *dir;
        struct dirent *d;
        struct option options[] = {
@@ -152,6 +149,12 @@ static int testsuite(int argc, const char **argv)
                         "write JUnit-style XML files"),
                OPT_END()
        };
+       struct run_process_parallel_opts opts = {
+               .get_next_task = next_test,
+               .start_failure = test_failed,
+               .task_finished = test_finished,
+               .data = &suite,
+       };
 
        argc = parse_options(argc, argv, NULL, options,
                        testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION);
@@ -192,8 +195,8 @@ static int testsuite(int argc, const char **argv)
        fprintf(stderr, "Running %"PRIuMAX" tests (%d at a time)\n",
                (uintmax_t)suite.tests.nr, max_jobs);
 
-       ret = run_processes_parallel(max_jobs, next_test, test_failed,
-                                    test_finished, &suite);
+       opts.processes = max_jobs;
+       run_processes_parallel(&opts);
 
        if (suite.failed.nr > 0) {
                ret = 1;
@@ -206,7 +209,7 @@ static int testsuite(int argc, const char **argv)
        string_list_clear(&suite.tests, 0);
        string_list_clear(&suite.failed, 0);
 
-       return !!ret;
+       return ret;
 }
 
 static uint64_t my_random_next = 1234;
@@ -381,13 +384,17 @@ int cmd__run_command(int argc, const char **argv)
 {
        struct child_process proc = CHILD_PROCESS_INIT;
        int jobs;
+       int ret;
+       struct run_process_parallel_opts opts = {
+               .data = &proc,
+       };
 
        if (argc > 1 && !strcmp(argv[1], "testsuite"))
-               exit(testsuite(argc - 1, argv + 1));
+               return testsuite(argc - 1, argv + 1);
        if (!strcmp(argv[1], "inherited-handle"))
-               exit(inherit_handle(argv[0]));
+               return inherit_handle(argv[0]);
        if (!strcmp(argv[1], "inherited-handle-child"))
-               exit(inherit_handle_child());
+               return inherit_handle_child();
 
        if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
                return !!quote_stress_test(argc - 1, argv + 1);
@@ -404,41 +411,52 @@ int cmd__run_command(int argc, const char **argv)
                argv += 2;
                argc -= 2;
        }
-       if (argc < 3)
-               return 1;
+       if (argc < 3) {
+               ret = 1;
+               goto cleanup;
+       }
        strvec_pushv(&proc.args, (const char **)argv + 2);
 
        if (!strcmp(argv[1], "start-command-ENOENT")) {
-               if (start_command(&proc) < 0 && errno == ENOENT)
-                       return 0;
+               if (start_command(&proc) < 0 && errno == ENOENT) {
+                       ret = 0;
+                       goto cleanup;
+               }
                fprintf(stderr, "FAIL %s\n", argv[1]);
                return 1;
        }
-       if (!strcmp(argv[1], "run-command"))
-               exit(run_command(&proc));
+       if (!strcmp(argv[1], "run-command")) {
+               ret = run_command(&proc);
+               goto cleanup;
+       }
 
        if (!strcmp(argv[1], "--ungroup")) {
                argv += 1;
                argc -= 1;
-               run_processes_parallel_ungroup = 1;
+               opts.ungroup = 1;
        }
 
        jobs = atoi(argv[2]);
        strvec_clear(&proc.args);
        strvec_pushv(&proc.args, (const char **)argv + 3);
 
-       if (!strcmp(argv[1], "run-command-parallel"))
-               exit(run_processes_parallel(jobs, parallel_next,
-                                           NULL, NULL, &proc));
-
-       if (!strcmp(argv[1], "run-command-abort"))
-               exit(run_processes_parallel(jobs, parallel_next,
-                                           NULL, task_finished, &proc));
-
-       if (!strcmp(argv[1], "run-command-no-jobs"))
-               exit(run_processes_parallel(jobs, no_job,
-                                           NULL, task_finished, &proc));
-
-       fprintf(stderr, "check usage\n");
-       return 1;
+       if (!strcmp(argv[1], "run-command-parallel")) {
+               opts.get_next_task = parallel_next;
+       } else if (!strcmp(argv[1], "run-command-abort")) {
+               opts.get_next_task = parallel_next;
+               opts.task_finished = task_finished;
+       } else if (!strcmp(argv[1], "run-command-no-jobs")) {
+               opts.get_next_task = no_job;
+               opts.task_finished = task_finished;
+       } else {
+               ret = 1;
+               fprintf(stderr, "check usage\n");
+               goto cleanup;
+       }
+       opts.processes = jobs;
+       run_processes_parallel(&opts);
+       ret = 0;
+cleanup:
+       child_process_clear(&proc);
+       return ret;
 }
index 026c802479d012b30bc625c820f5d0fcc25e997c..0a816a96e28b6c43a0b4927418c97bb11ba5298c 100644 (file)
@@ -1,19 +1,22 @@
+#define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
 #include "lockfile.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "tree.h"
 #include "cache-tree.h"
 
-int cmd__scrap_cache_tree(int ac, const char **av)
+int cmd__scrap_cache_tree(int ac UNUSED, const char **av UNUSED)
 {
        struct lock_file index_lock = LOCK_INIT;
 
        setup_git_directory();
-       hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
-       if (read_cache() < 0)
+       repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
+       if (repo_read_index(the_repository) < 0)
                die("unable to read index file");
-       cache_tree_free(&active_cache_tree);
-       active_cache_tree = NULL;
+       cache_tree_free(&the_index.cache_tree);
+       the_index.cache_tree = NULL;
        if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                die("unable to write index file");
        return 0;
index 824e5c0a95819f8d11393a74da4b231728bb0615..054cbcf5d83946b225774dc9da6b0ec1d112e79d 100644 (file)
@@ -1,7 +1,8 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "serve.h"
+#include "setup.h"
 
 static char const * const serve_usage[] = {
        N_("test-tool serve-v2 [<options>]"),
index d860c387c3846d69b7bd63a144ede2f93da60886..dcb7f6c0032b556959c6635db3c89fd49e8790d0 100644 (file)
@@ -1,7 +1,15 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
 
 int cmd__sha1(int ac, const char **av)
 {
        return cmd_hash_impl(ac, av, GIT_HASH_SHA1);
 }
+
+int cmd__sha1_is_sha1dc(int argc UNUSED, const char **argv UNUSED)
+{
+#ifdef platform_SHA_IS_SHA1DC
+       return 0;
+#endif
+       return 1;
+}
index 0ac6a99d5f2a38d89317564185e91ee5126fd3ac..08cf14900140d37733e5afcad7c9fdc97c3b7eae 100644 (file)
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
 
 int cmd__sha256(int ac, const char **av)
 {
index d013bccddaebd9c7fb0eb4b4c4e1be0643f82260..2d5ecf738317e7a8a3897bc5ff5a7cc0e51de2da 100644 (file)
@@ -1,5 +1,4 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "sigchain.h"
 
 #define X(f) \
@@ -14,7 +13,7 @@ X(two)
 X(three)
 #undef X
 
-int cmd__sigchain(int argc, const char **argv)
+int cmd__sigchain(int argc UNUSED, const char **argv UNUSED)
 {
        sigchain_push(SIGTERM, one);
        sigchain_push(SIGTERM, two);
index 28365ff85b69bbda4ca47b5284b2cbd7bebea9e9..fb5927775daef80040ea9675742aa2cb47d46d4e 100644 (file)
@@ -3,13 +3,13 @@
  */
 
 #include "test-tool.h"
-#include "cache.h"
-#include "strbuf.h"
+#include "gettext.h"
 #include "simple-ipc.h"
 #include "parse-options.h"
 #include "thread-utils.h"
 #include "strvec.h"
 #include "run-command.h"
+#include "trace2.h"
 
 #ifndef SUPPORTS_SIMPLE_IPC
 int cmd__simple_ipc(int argc, const char **argv)
@@ -277,7 +277,8 @@ static int daemon__run_server(void)
 
 static start_bg_wait_cb bg_wait_cb;
 
-static int bg_wait_cb(const struct child_process *cp, void *cb_data)
+static int bg_wait_cb(const struct child_process *cp UNUSED,
+                     void *cb_data UNUSED)
 {
        int s = ipc_get_active_state(cl_args.path);
 
index 44e4a6d143e24cbd05b5d94404a5f5d4e732c880..d8473cf2fcfeb61bdbced865455dd2e3a35b71bb 100644 (file)
@@ -1,7 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
 
-int cmd__strcmp_offset(int argc, const char **argv)
+int cmd__strcmp_offset(int argc UNUSED, const char **argv)
 {
        int result;
        size_t offset;
index 2123dda85bf10033dcbf0d801028b3705e73a507..e2aad611d1c8bbc110c877c7d7555c046b82e7db 100644 (file)
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "strbuf.h"
 #include "string-list.h"
 
 /*
@@ -62,7 +62,7 @@ int cmd__string_list(int argc, const char **argv)
                struct string_list list = STRING_LIST_INIT_NODUP;
                int i;
                char *s = xstrdup(argv[2]);
-               int delim = *argv[3];
+               const char *delim = argv[3];
                int maxsplit = atoi(argv[4]);
 
                i = string_list_split_in_place(&list, s, delim, maxsplit);
@@ -111,7 +111,7 @@ int cmd__string_list(int argc, const char **argv)
                 */
                if (sb.len && sb.buf[sb.len - 1] == '\n')
                        strbuf_setlen(&sb, sb.len - 1);
-               string_list_split_in_place(&list, sb.buf, '\n', -1);
+               string_list_split_in_place(&list, sb.buf, "\n", -1);
 
                string_list_sort(&list);
 
index 22a41c409263c077843598e4cc51881a52b0ce30..9df2f03ac80de2b9cf977b0d7e252eb904c903f3 100644 (file)
@@ -1,10 +1,13 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "config.h"
+#include "hash.h"
+#include "object-name.h"
+#include "repository.h"
+#include "setup.h"
 #include "submodule-config.h"
 #include "submodule.h"
 
-static void die_usage(int argc, const char **argv, const char *msg)
+static void die_usage(int argc UNUSED, const char **argv, const char *msg)
 {
        fprintf(stderr, "%s\n", msg);
        fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
@@ -42,7 +45,7 @@ int cmd__submodule_config(int argc, const char **argv)
 
                if (commit[0] == '\0')
                        oidclr(&commit_oid);
-               else if (get_oid(commit, &commit_oid) < 0)
+               else if (repo_get_oid(the_repository, commit, &commit_oid) < 0)
                        die_usage(argc, argv, "Commit not found.");
 
                if (lookup_name) {
index dc1c14bde3741715f9dee0768f061e31ba2d7330..ecd40ded995c0f88b2b58de0d08fd2a1831fca26 100644 (file)
@@ -1,4 +1,6 @@
 #include "test-tool.h"
+#include "repository.h"
+#include "setup.h"
 #include "submodule-config.h"
 
 static void die_usage(const char **argv, const char *msg)
index b7d117cd557a84eabcdc25df9ae874e0192e3dd7..50c154d0370bfbdd91b70747133977a752f2b504 100644 (file)
@@ -1,8 +1,10 @@
 #include "test-tool.h"
 #include "test-tool-utils.h"
-#include "cache.h"
 #include "parse-options.h"
 #include "remote.h"
+#include "repository.h"
+#include "setup.h"
+#include "strbuf.h"
 #include "submodule-config.h"
 #include "submodule.h"
 
@@ -111,10 +113,94 @@ static int cmd__submodule_resolve_relative_url(int argc, const char **argv)
        return 0;
 }
 
+static int cmd__submodule_config_list(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       const char *const usage[] = {
+               "test-tool submodule config-list <key>",
+               NULL
+       };
+       argc = parse_options(argc, argv, "test-tools", options, usage,
+                            PARSE_OPT_KEEP_ARGV0);
+
+       setup_git_directory();
+
+       if (argc == 2)
+               return print_config_from_gitmodules(the_repository, argv[1]);
+       usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_set(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       const char *const usage[] = {
+               "test-tool submodule config-set <key> <value>",
+               NULL
+       };
+       argc = parse_options(argc, argv, "test-tools", options, usage,
+                            PARSE_OPT_KEEP_ARGV0);
+
+       setup_git_directory();
+
+       /* Equivalent to ACTION_SET in builtin/config.c */
+       if (argc == 3) {
+               if (!is_writing_gitmodules_ok())
+                       die("please make sure that the .gitmodules file is in the working tree");
+
+               return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
+       }
+       usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_unset(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       const char *const usage[] = {
+               "test-tool submodule config-unset <key>",
+               NULL
+       };
+
+       setup_git_directory();
+
+       if (argc == 2) {
+               if (!is_writing_gitmodules_ok())
+                       die("please make sure that the .gitmodules file is in the working tree");
+               return config_set_in_gitmodules_file_gently(argv[1], NULL);
+       }
+       usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       const char *const usage[] = {
+               "test-tool submodule config-writeable",
+               NULL
+       };
+       setup_git_directory();
+
+       if (argc == 1)
+               return is_writing_gitmodules_ok() ? 0 : -1;
+
+       usage_with_options(usage, options);
+}
+
 static struct test_cmd cmds[] = {
        { "check-name", cmd__submodule_check_name },
        { "is-active", cmd__submodule_is_active },
        { "resolve-relative-url", cmd__submodule_resolve_relative_url},
+       { "config-list", cmd__submodule_config_list },
+       { "config-set", cmd__submodule_config_set },
+       { "config-unset", cmd__submodule_config_unset },
+       { "config-writeable", cmd__submodule_config_writeable },
 };
 
 int cmd__submodule(int argc, const char **argv)
index ff22f2fa2c57efbda1246f1aab0da494eb026fd1..c344f1694df28da085340b5f556ecae9bfcbb56f 100644 (file)
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "run-command.h"
+#include "setup.h"
 
 int cmd__subprocess(int argc, const char **argv)
 {
index d1d013bcd920b197163b7c780f0131b0a1356541..37ba996539b72f66316722cc73094d44aef6e61c 100644 (file)
@@ -13,6 +13,8 @@ static struct test_cmd cmds[] = {
        { "advise", cmd__advise_if_enabled },
        { "bitmap", cmd__bitmap },
        { "bloom", cmd__bloom },
+       { "bundle-uri", cmd__bundle_uri },
+       { "cache-tree", cmd__cache_tree },
        { "chmtime", cmd__chmtime },
        { "config", cmd__config },
        { "crontab", cmd__crontab },
@@ -26,8 +28,9 @@ static struct test_cmd cmds[] = {
        { "dump-fsmonitor", cmd__dump_fsmonitor },
        { "dump-split-index", cmd__dump_split_index },
        { "dump-untracked-cache", cmd__dump_untracked_cache },
+       { "env-helper", cmd__env_helper },
        { "example-decorate", cmd__example_decorate },
-       { "fast-rebase", cmd__fast_rebase },
+       { "find-pack", cmd__find_pack },
        { "fsmonitor-client", cmd__fsmonitor_client },
        { "genrandom", cmd__genrandom },
        { "genzeros", cmd__genzeros },
@@ -35,7 +38,6 @@ static struct test_cmd cmds[] = {
        { "hashmap", cmd__hashmap },
        { "hash-speed", cmd__hash_speed },
        { "hexdump", cmd__hexdump },
-       { "index-version", cmd__index_version },
        { "json-writer", cmd__json_writer },
        { "lazy-init-name-hash", cmd__lazy_init_name_hash },
        { "match-trees", cmd__match_trees },
@@ -72,6 +74,7 @@ static struct test_cmd cmds[] = {
        { "scrap-cache-tree", cmd__scrap_cache_tree },
        { "serve-v2", cmd__serve_v2 },
        { "sha1", cmd__sha1 },
+       { "sha1-is-sha1dc", cmd__sha1_is_sha1dc },
        { "sha256", cmd__sha256 },
        { "sigchain", cmd__sigchain },
        { "simple-ipc", cmd__simple_ipc },
@@ -82,6 +85,7 @@ static struct test_cmd cmds[] = {
        { "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
        { "subprocess", cmd__subprocess },
        { "trace2", cmd__trace2 },
+       { "truncate", cmd__truncate },
        { "userdiff", cmd__userdiff },
        { "urlmatch-normalization", cmd__urlmatch_normalization },
        { "xml-encode", cmd__xml_encode },
index 6b46b6444b657c3debdc286c50e81db5ba495f8b..8a1a7c63daa4dabf1b92d35420f2b2f5c36b136c 100644 (file)
@@ -1,12 +1,13 @@
 #ifndef TEST_TOOL_H
 #define TEST_TOOL_H
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "git-compat-util.h"
 
 int cmd__advise_if_enabled(int argc, const char **argv);
 int cmd__bitmap(int argc, const char **argv);
 int cmd__bloom(int argc, const char **argv);
+int cmd__bundle_uri(int argc, const char **argv);
+int cmd__cache_tree(int argc, const char **argv);
 int cmd__chmtime(int argc, const char **argv);
 int cmd__config(int argc, const char **argv);
 int cmd__crontab(int argc, const char **argv);
@@ -21,8 +22,9 @@ int cmd__dump_fsmonitor(int argc, const char **argv);
 int cmd__dump_split_index(int argc, const char **argv);
 int cmd__dump_untracked_cache(int argc, const char **argv);
 int cmd__dump_reftable(int argc, const char **argv);
+int cmd__env_helper(int argc, const char **argv);
 int cmd__example_decorate(int argc, const char **argv);
-int cmd__fast_rebase(int argc, const char **argv);
+int cmd__find_pack(int argc, const char **argv);
 int cmd__fsmonitor_client(int argc, const char **argv);
 int cmd__genrandom(int argc, const char **argv);
 int cmd__genzeros(int argc, const char **argv);
@@ -30,7 +32,6 @@ int cmd__getcwd(int argc, const char **argv);
 int cmd__hashmap(int argc, const char **argv);
 int cmd__hash_speed(int argc, const char **argv);
 int cmd__hexdump(int argc, const char **argv);
-int cmd__index_version(int argc, const char **argv);
 int cmd__json_writer(int argc, const char **argv);
 int cmd__lazy_init_name_hash(int argc, const char **argv);
 int cmd__match_trees(int argc, const char **argv);
@@ -65,6 +66,7 @@ int cmd__run_command(int argc, const char **argv);
 int cmd__scrap_cache_tree(int argc, const char **argv);
 int cmd__serve_v2(int argc, const char **argv);
 int cmd__sha1(int argc, const char **argv);
+int cmd__sha1_is_sha1dc(int argc, const char **argv);
 int cmd__oid_array(int argc, const char **argv);
 int cmd__sha256(int argc, const char **argv);
 int cmd__sigchain(int argc, const char **argv);
@@ -76,6 +78,7 @@ int cmd__submodule_config(int argc, const char **argv);
 int cmd__submodule_nested_repo_config(int argc, const char **argv);
 int cmd__subprocess(int argc, const char **argv);
 int cmd__trace2(int argc, const char **argv);
+int cmd__truncate(int argc, const char **argv);
 int cmd__userdiff(int argc, const char **argv);
 int cmd__urlmatch_normalization(int argc, const char **argv);
 int cmd__xml_encode(int argc, const char **argv);
index a714130ece77c334c982b8a9520d7fb1b9fa6a3d..1adac29a575254492f206bf4c4e4b6cab08f999d 100644 (file)
@@ -1,9 +1,10 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "strvec.h"
 #include "run-command.h"
 #include "exec-cmd.h"
 #include "config.h"
+#include "repository.h"
+#include "trace2.h"
 
 typedef int(fn_unit_test)(int argc, const char **argv);
 
@@ -44,7 +45,7 @@ static int get_i(int *p_value, const char *data)
  * [] "def_param" events for all of the "interesting" pre-defined
  * config settings.
  */
-static int ut_001return(int argc, const char **argv)
+static int ut_001return(int argc UNUSED, const char **argv)
 {
        int rc;
 
@@ -64,7 +65,7 @@ static int ut_001return(int argc, const char **argv)
  * [] "def_param" events for all of the "interesting" pre-defined
  * config settings.
  */
-static int ut_002exit(int argc, const char **argv)
+static int ut_002exit(int argc UNUSED, const char **argv)
 {
        int rc;
 
@@ -132,6 +133,7 @@ static int ut_003error(int argc, const char **argv)
  */
 static int ut_004child(int argc, const char **argv)
 {
+       struct child_process cmd = CHILD_PROCESS_INIT;
        int result;
 
        /*
@@ -141,7 +143,8 @@ static int ut_004child(int argc, const char **argv)
        if (!argc)
                return 0;
 
-       result = run_command_v_opt(argv, 0);
+       strvec_pushv(&cmd.args, argv);
+       result = run_command(&cmd);
        exit(result);
 }
 
@@ -198,7 +201,7 @@ static int ut_006data(int argc, const char **argv)
        return 0;
 }
 
-static int ut_007BUG(int argc, const char **argv)
+static int ut_007BUG(int argc UNUSED, const char **argv UNUSED)
 {
        /*
         * Exercise BUG() to ensure that the message is printed to trace2.
@@ -206,7 +209,7 @@ static int ut_007BUG(int argc, const char **argv)
        BUG("the bug message");
 }
 
-static int ut_008bug(int argc, const char **argv)
+static int ut_008bug(int argc UNUSED, const char **argv UNUSED)
 {
        bug("a bug message");
        bug("another bug message");
@@ -214,7 +217,7 @@ static int ut_008bug(int argc, const char **argv)
        return 0;
 }
 
-static int ut_009bug_BUG(int argc, const char **argv)
+static int ut_009bug_BUG(int argc UNUSED, const char **argv UNUSED)
 {
        bug("a bug message");
        bug("another bug message");
@@ -222,12 +225,243 @@ static int ut_009bug_BUG(int argc, const char **argv)
        return 0;
 }
 
-static int ut_010bug_BUG(int argc, const char **argv)
+static int ut_010bug_BUG(int argc UNUSED, const char **argv UNUSED)
 {
        bug("a %s message", "bug");
        BUG("a %s message", "BUG");
 }
 
+/*
+ * Single-threaded timer test.  Create several intervals using the
+ * TEST1 timer.  The test script can verify that an aggregate Trace2
+ * "timer" event is emitted indicating that we started+stopped the
+ * timer the requested number of times.
+ */
+static int ut_100timer(int argc, const char **argv)
+{
+       const char *usage_error =
+               "expect <count> <ms_delay>";
+
+       int count = 0;
+       int delay = 0;
+       int k;
+
+       if (argc != 2)
+               die("%s", usage_error);
+       if (get_i(&count, argv[0]))
+               die("%s", usage_error);
+       if (get_i(&delay, argv[1]))
+               die("%s", usage_error);
+
+       for (k = 0; k < count; k++) {
+               trace2_timer_start(TRACE2_TIMER_ID_TEST1);
+               sleep_millisec(delay);
+               trace2_timer_stop(TRACE2_TIMER_ID_TEST1);
+       }
+
+       return 0;
+}
+
+struct ut_101_data {
+       int count;
+       int delay;
+};
+
+static void *ut_101timer_thread_proc(void *_ut_101_data)
+{
+       struct ut_101_data *data = _ut_101_data;
+       int k;
+
+       trace2_thread_start("ut_101");
+
+       for (k = 0; k < data->count; k++) {
+               trace2_timer_start(TRACE2_TIMER_ID_TEST2);
+               sleep_millisec(data->delay);
+               trace2_timer_stop(TRACE2_TIMER_ID_TEST2);
+       }
+
+       trace2_thread_exit();
+       return NULL;
+}
+
+/*
+ * Multi-threaded timer test.  Create several threads that each create
+ * several intervals using the TEST2 timer.  The test script can verify
+ * that an individual Trace2 "th_timer" events for each thread and an
+ * aggregate "timer" event are generated.
+ */
+static int ut_101timer(int argc, const char **argv)
+{
+       const char *usage_error =
+               "expect <count> <ms_delay> <threads>";
+
+       struct ut_101_data data = { 0, 0 };
+       int nr_threads = 0;
+       int k;
+       pthread_t *pids = NULL;
+
+       if (argc != 3)
+               die("%s", usage_error);
+       if (get_i(&data.count, argv[0]))
+               die("%s", usage_error);
+       if (get_i(&data.delay, argv[1]))
+               die("%s", usage_error);
+       if (get_i(&nr_threads, argv[2]))
+               die("%s", usage_error);
+
+       CALLOC_ARRAY(pids, nr_threads);
+
+       for (k = 0; k < nr_threads; k++) {
+               if (pthread_create(&pids[k], NULL, ut_101timer_thread_proc, &data))
+                       die("failed to create thread[%d]", k);
+       }
+
+       for (k = 0; k < nr_threads; k++) {
+               if (pthread_join(pids[k], NULL))
+                       die("failed to join thread[%d]", k);
+       }
+
+       free(pids);
+
+       return 0;
+}
+
+/*
+ * Single-threaded counter test.  Add several values to the TEST1 counter.
+ * The test script can verify that the final sum is reported in the "counter"
+ * event.
+ */
+static int ut_200counter(int argc, const char **argv)
+{
+       const char *usage_error =
+               "expect <v1> [<v2> [...]]";
+       int value;
+       int k;
+
+       if (argc < 1)
+               die("%s", usage_error);
+
+       for (k = 0; k < argc; k++) {
+               if (get_i(&value, argv[k]))
+                       die("invalid value[%s] -- %s",
+                           argv[k], usage_error);
+               trace2_counter_add(TRACE2_COUNTER_ID_TEST1, value);
+       }
+
+       return 0;
+}
+
+/*
+ * Multi-threaded counter test.  Create seveal threads that each increment
+ * the TEST2 global counter.  The test script can verify that an individual
+ * "th_counter" event is generated with a partial sum for each thread and
+ * that a final aggregate "counter" event is generated.
+ */
+
+struct ut_201_data {
+       int v1;
+       int v2;
+};
+
+static void *ut_201counter_thread_proc(void *_ut_201_data)
+{
+       struct ut_201_data *data = _ut_201_data;
+
+       trace2_thread_start("ut_201");
+
+       trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v1);
+       trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v2);
+
+       trace2_thread_exit();
+       return NULL;
+}
+
+static int ut_201counter(int argc, const char **argv)
+{
+       const char *usage_error =
+               "expect <v1> <v2> <threads>";
+
+       struct ut_201_data data = { 0, 0 };
+       int nr_threads = 0;
+       int k;
+       pthread_t *pids = NULL;
+
+       if (argc != 3)
+               die("%s", usage_error);
+       if (get_i(&data.v1, argv[0]))
+               die("%s", usage_error);
+       if (get_i(&data.v2, argv[1]))
+               die("%s", usage_error);
+       if (get_i(&nr_threads, argv[2]))
+               die("%s", usage_error);
+
+       CALLOC_ARRAY(pids, nr_threads);
+
+       for (k = 0; k < nr_threads; k++) {
+               if (pthread_create(&pids[k], NULL, ut_201counter_thread_proc, &data))
+                       die("failed to create thread[%d]", k);
+       }
+
+       for (k = 0; k < nr_threads; k++) {
+               if (pthread_join(pids[k], NULL))
+                       die("failed to join thread[%d]", k);
+       }
+
+       free(pids);
+
+       return 0;
+}
+
+static int ut_300redact_start(int argc, const char **argv)
+{
+       if (!argc)
+               die("expect <argv...>");
+
+       trace2_cmd_start(argv);
+
+       return 0;
+}
+
+static int ut_301redact_child_start(int argc, const char **argv)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       int k;
+
+       if (!argc)
+               die("expect <argv...>");
+
+       for (k = 0; argv[k]; k++)
+               strvec_push(&cmd.args, argv[k]);
+
+       trace2_child_start(&cmd);
+
+       strvec_clear(&cmd.args);
+
+       return 0;
+}
+
+static int ut_302redact_exec(int argc, const char **argv)
+{
+       if (!argc)
+               die("expect <exe> <argv...>");
+
+       trace2_exec(argv[0], &argv[1]);
+
+       return 0;
+}
+
+static int ut_303redact_def_param(int argc, const char **argv)
+{
+       struct key_value_info kvi = KVI_INIT;
+
+       if (argc < 2)
+               die("expect <key> <value>");
+
+       trace2_def_param(argv[0], argv[1], &kvi);
+
+       return 0;
+}
+
 /*
  * Usage:
  *     test-tool trace2 <ut_name_1> <ut_usage_1>
@@ -248,6 +482,17 @@ static struct unit_test ut_table[] = {
        { ut_008bug,      "008bug",    "" },
        { ut_009bug_BUG,  "009bug_BUG","" },
        { ut_010bug_BUG,  "010bug_BUG","" },
+
+       { ut_100timer,    "100timer",  "<count> <ms_delay>" },
+       { ut_101timer,    "101timer",  "<count> <ms_delay> <threads>" },
+
+       { ut_200counter,  "200counter", "<v1> [<v2> [<v3> [...]]]" },
+       { ut_201counter,  "201counter", "<v1> <v2> <threads>" },
+
+       { ut_300redact_start,       "300redact_start",       "<argv...>" },
+       { ut_301redact_child_start, "301redact_child_start", "<argv...>" },
+       { ut_302redact_exec,        "302redact_exec",        "<exe> <argv...>" },
+       { ut_303redact_def_param,   "303redact_def_param",   "<key> <value>" },
 };
 /* clang-format on */
 
diff --git a/t/helper/test-truncate.c b/t/helper/test-truncate.c
new file mode 100644 (file)
index 0000000..3931dea
--- /dev/null
@@ -0,0 +1,25 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+
+/*
+ * Truncate a file to the given size.
+ */
+int cmd__truncate(int argc, const char **argv)
+{
+       char *p = NULL;
+       uintmax_t sz = 0;
+       int fd = -1;
+
+       if (argc != 3)
+               die("expected filename and size");
+
+       sz = strtoumax(argv[2], &p, 0);
+       if (*p)
+               die("invalid size");
+
+       fd = xopen(argv[1], O_WRONLY | O_CREAT, 0600);
+
+       if (ftruncate(fd, (off_t) sz) < 0)
+               die_errno("failed to truncate file");
+       return 0;
+}
index a2b56b9cae5e3736220afcc30919d1c8bca0b69a..0ce31ce59f54fe972b2d8c1fd16af6bb5a2c24fd 100644 (file)
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "setup.h"
 #include "userdiff.h"
 #include "config.h"
 
@@ -12,7 +12,9 @@ static int driver_cb(struct userdiff_driver *driver,
        return 0;
 }
 
-static int cmd__userdiff_config(const char *var, const char *value, void *cb UNUSED)
+static int cmd__userdiff_config(const char *var, const char *value,
+                               const struct config_context *ctx UNUSED,
+                               void *cb UNUSED)
 {
        if (userdiff_config(var, value) < 0)
                return -1;
index 2c103d1824cfc7f035aea4a6453c9706f9b4491e..b4ff5f986ae00b099ead5d75e5e8b196de88f67c 100644 (file)
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "wildmatch.h"
 
 int cmd__wildmatch(int argc, const char **argv)
 {
index 8837717d36a77c04346279b570e1a0a4506fe838..f084034d38e0328e0792d096225c31ddd91d74c7 100644 (file)
@@ -1,6 +1,9 @@
+#define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
 #include "lockfile.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 int cmd__write_cache(int argc, const char **argv)
 {
@@ -9,9 +12,10 @@ int cmd__write_cache(int argc, const char **argv)
        if (argc == 2)
                cnt = strtol(argv[1], NULL, 0);
        setup_git_directory();
-       read_cache();
+       repo_read_index(the_repository);
        for (i = 0; i < cnt; i++) {
-               hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+               repo_hold_locked_index(the_repository, &index_lock,
+                                      LOCK_DIE_ON_ERROR);
                if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                        die("unable to write index file");
        }
index a648bbd961c2361ea8fa2385cb3f63a002e634c3..b2f330d1a44542065a8fc79e9eb6bf2b9f5ed8a9 100644 (file)
@@ -6,7 +6,7 @@ static const char *utf8_replace_character = "&#xfffd;";
  * Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded
  * in an XML file.
  */
-int cmd__xml_encode(int argc, const char **argv)
+int cmd__xml_encode(int argc UNUSED, const char **argv UNUSED)
 {
        unsigned char buf[1024], tmp[4], *tmp2 = NULL;
        ssize_t cur = 0, len = 1, remaining = 0;
index 3e0a2911d4f9baa889eef85877a6332b27015dfc..62f4481b6e4097db907806aa6330ae0b64793d5d 100644 (file)
@@ -68,7 +68,7 @@ generate_wrappers () {
        wrap_git .bin/git.a "$DIR_A" &&
        wrap_git .bin/git.b "$DIR_B" &&
        write_script .bin/git <<-\EOF &&
-       echo >&2 fatal: test tried to run generic git
+       echo >&2 fatal: test tried to run generic git: $*
        exit 1
        EOF
        PATH=$(pwd)/.bin:$PATH
diff --git a/t/lib-bundle-uri-protocol.sh b/t/lib-bundle-uri-protocol.sh
new file mode 100644 (file)
index 0000000..a4a1af8
--- /dev/null
@@ -0,0 +1,216 @@
+# Set up and run tests of the 'bundle-uri' command in protocol v2
+#
+# The test that includes this script should set BUNDLE_URI_PROTOCOL
+# to one of "file", "git", or "http".
+
+BUNDLE_URI_TEST_PARENT=
+BUNDLE_URI_TEST_URI=
+BUNDLE_URI_TEST_BUNDLE_URI=
+case "$BUNDLE_URI_PROTOCOL" in
+file)
+       BUNDLE_URI_PARENT=file_parent
+       BUNDLE_URI_REPO_URI="file://$PWD/file_parent"
+       BUNDLE_URI_BUNDLE_URI="$BUNDLE_URI_REPO_URI/fake.bdl"
+       test_set_prereq BUNDLE_URI_FILE
+       ;;
+git)
+       . "$TEST_DIRECTORY"/lib-git-daemon.sh
+       start_git_daemon --export-all --enable=receive-pack
+       BUNDLE_URI_PARENT="$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent"
+       BUNDLE_URI_REPO_URI="$GIT_DAEMON_URL/parent"
+       BUNDLE_URI_BUNDLE_URI="https://example.com/fake.bdl"
+       test_set_prereq BUNDLE_URI_GIT
+       ;;
+http)
+       . "$TEST_DIRECTORY"/lib-httpd.sh
+       start_httpd
+       BUNDLE_URI_PARENT="$HTTPD_DOCUMENT_ROOT_PATH/http_parent"
+       BUNDLE_URI_REPO_URI="$HTTPD_URL/smart/http_parent"
+       BUNDLE_URI_BUNDLE_URI="https://example.com/fake.bdl"
+       test_set_prereq BUNDLE_URI_HTTP
+       ;;
+*)
+       BUG "Need to pass valid BUNDLE_URI_PROTOCOL (was \"$BUNDLE_URI_PROTOCOL\")"
+       ;;
+esac
+
+test_expect_success "setup protocol v2 $BUNDLE_URI_PROTOCOL:// tests" '
+       git init "$BUNDLE_URI_PARENT" &&
+       test_commit -C "$BUNDLE_URI_PARENT" one &&
+       git -C "$BUNDLE_URI_PARENT" config uploadpack.advertiseBundleURIs true
+'
+
+case "$BUNDLE_URI_PROTOCOL" in
+http)
+       test_expect_success "setup config for $BUNDLE_URI_PROTOCOL:// tests" '
+               git -C "$BUNDLE_URI_PARENT" config http.receivepack true
+       '
+       ;;
+*)
+       ;;
+esac
+BUNDLE_URI_BUNDLE_URI_ESCAPED=$(echo "$BUNDLE_URI_BUNDLE_URI" | test_uri_escape)
+
+test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: no bundle-uri" '
+       test_when_finished "rm -f log" &&
+       test_when_finished "git -C \"$BUNDLE_URI_PARENT\" config uploadpack.advertiseBundleURIs true" &&
+       git -C "$BUNDLE_URI_PARENT" config uploadpack.advertiseBundleURIs false &&
+
+       GIT_TRACE_PACKET="$PWD/log" \
+       git \
+               -c protocol.version=2 \
+               ls-remote --symref "$BUNDLE_URI_REPO_URI" \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log &&
+
+       ! grep bundle-uri log
+'
+
+test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: have bundle-uri" '
+       test_when_finished "rm -f log" &&
+
+       GIT_TRACE_PACKET="$PWD/log" \
+       git \
+               -c protocol.version=2 \
+               ls-remote --symref "$BUNDLE_URI_REPO_URI" \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log &&
+
+       # Server advertised bundle-uri capability
+       grep "< bundle-uri" log
+'
+
+test_expect_success "clone with $BUNDLE_URI_PROTOCOL:// using protocol v2: request bundle-uris" '
+       test_when_finished "rm -rf log* cloned*" &&
+
+       GIT_TRACE_PACKET="$PWD/log" \
+       git \
+               -c transfer.bundleURI=false \
+               -c protocol.version=2 \
+               clone "$BUNDLE_URI_REPO_URI" cloned \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log &&
+
+       # Server advertised bundle-uri capability
+       grep "< bundle-uri" log &&
+
+       # Client did not issue bundle-uri command
+       ! grep "> command=bundle-uri" log &&
+
+       GIT_TRACE_PACKET="$PWD/log" \
+       git \
+               -c transfer.bundleURI=true \
+               -c protocol.version=2 \
+               clone "$BUNDLE_URI_REPO_URI" cloned2 \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log &&
+
+       # Server advertised bundle-uri capability
+       grep "< bundle-uri" log &&
+
+       # Client issued bundle-uri command
+       grep "> command=bundle-uri" log &&
+
+       GIT_TRACE_PACKET="$PWD/log3" \
+       git \
+               -c transfer.bundleURI=true \
+               -c protocol.version=2 \
+               clone --bundle-uri="$BUNDLE_URI_BUNDLE_URI" \
+               "$BUNDLE_URI_REPO_URI" cloned3 \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log3 &&
+
+       # Server advertised bundle-uri capability
+       grep "< bundle-uri" log3 &&
+
+       # Client did not issue bundle-uri command (--bundle-uri override)
+       ! grep "> command=bundle-uri" log3
+'
+
+# The remaining tests will all assume transfer.bundleURI=true
+#
+# This test can be removed when transfer.bundleURI is enabled by default.
+test_expect_success 'enable transfer.bundleURI for remaining tests' '
+       git config --global transfer.bundleURI true
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2" '
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.only.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED" &&
+
+       # All data about bundle URIs
+       cat >expect <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "only"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED
+       EOF
+
+       test-tool bundle-uri \
+               ls-remote \
+               "$BUNDLE_URI_REPO_URI" \
+               >actual &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2 and extra data" '
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.only.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED" &&
+
+       # Extra data should be ignored
+       test_config -C "$BUNDLE_URI_PARENT" bundle.only.extra bogus &&
+
+       # All data about bundle URIs
+       cat >expect <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "only"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED
+       EOF
+
+       test-tool bundle-uri \
+               ls-remote \
+               "$BUNDLE_URI_REPO_URI" \
+               >actual &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2 with list" '
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.bundle1.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-1.bdl" &&
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.bundle2.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-2.bdl" &&
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.bundle3.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-3.bdl" &&
+
+       # All data about bundle URIs
+       cat >expect <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "bundle1"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-1.bdl
+       [bundle "bundle2"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-2.bdl
+       [bundle "bundle3"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-3.bdl
+       EOF
+
+       test-tool bundle-uri \
+               ls-remote \
+               "$BUNDLE_URI_REPO_URI" \
+               >actual &&
+       test_cmp_config_output expect actual
+'
diff --git a/t/lib-chunk.sh b/t/lib-chunk.sh
new file mode 100644 (file)
index 0000000..a7cd9c3
--- /dev/null
@@ -0,0 +1,17 @@
+# Shell library for working with "chunk" files (commit-graph, midx, etc).
+
+# corrupt_chunk_file <fn> <chunk> <offset> <bytes>
+#
+# Corrupt a chunk-based file (like a commit-graph) by overwriting the bytes
+# found in the chunk specified by the 4-byte <chunk> identifier. If <offset> is
+# "clear", replace the chunk entirely. Otherwise, overwrite data <offset> bytes
+# into the chunk.
+#
+# The <bytes> are interpreted as pairs of hex digits (so "000000FE" would be
+# big-endian 254).
+corrupt_chunk_file () {
+       fn=$1; shift
+       perl "$TEST_DIRECTORY"/lib-chunk/corrupt-chunk-file.pl \
+               "$@" <"$fn" >"$fn.tmp" &&
+       mv "$fn.tmp" "$fn"
+}
diff --git a/t/lib-chunk/corrupt-chunk-file.pl b/t/lib-chunk/corrupt-chunk-file.pl
new file mode 100644 (file)
index 0000000..0e11aad
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+my ($chunk, $seek, $bytes) = @ARGV;
+$bytes =~ s/../chr(hex($&))/ge;
+
+binmode STDIN;
+binmode STDOUT;
+
+# A few helpers to read bytes, or read and copy them to the
+# output.
+sub get {
+       my $n = shift;
+       return unless $n;
+       read(STDIN, my $buf, $n)
+               or die "read error or eof: $!\n";
+       return $buf;
+}
+sub copy {
+       my $buf = get(@_);
+       print $buf;
+       return $buf;
+}
+
+# Some platforms' perl builds don't support 64-bit integers, and hence do not
+# allow packing/unpacking quadwords with "Q". The chunk format uses 64-bit file
+# offsets to support files of any size, but in practice our test suite will
+# only use small files. So we can fake it by asking for two 32-bit values and
+# discarding the first (most significant) one, which is equivalent as long as
+# it's just zero.
+sub unpack_quad {
+       my $bytes = shift;
+       my ($n1, $n2) = unpack("NN", $bytes);
+       die "quad value exceeds 32 bits" if $n1;
+       return $n2;
+}
+sub pack_quad {
+       my $n = shift;
+       my $ret = pack("NN", 0, $n);
+       # double check that our original $n did not exceed the 32-bit limit.
+       # This is presumably impossible on a 32-bit system (which would have
+       # truncated much earlier), but would still alert us on a 64-bit build
+       # of a new test that would fail on a 32-bit build (though we'd
+       # presumably see the die() from unpack_quad() in such a case).
+       die "quad round-trip failed" if unpack_quad($ret) != $n;
+       return $ret;
+}
+
+# read until we find table-of-contents entry for chunk;
+# note that we cheat a bit by assuming 4-byte alignment and
+# that no ToC entry will accidentally look like a header.
+#
+# If we don't find the entry, copy() will hit EOF and exit
+# (which should cause the caller to fail the test).
+while (copy(4) ne $chunk) { }
+my $offset = unpack_quad(copy(8));
+
+# In clear mode, our length will change. So figure out
+# the length by comparing to the offset of the next chunk, and
+# then adjust that offset (and all subsequent) ones.
+my $len;
+if ($seek eq "clear") {
+       my $id;
+       do {
+               $id = copy(4);
+               my $next = unpack_quad(get(8));
+               if (!defined $len) {
+                       $len = $next - $offset;
+               }
+               print pack_quad($next - $len + length($bytes));
+       } while (unpack("N", $id));
+}
+
+# and now copy up to our existing chunk data
+copy($offset - tell(STDIN));
+if ($seek eq "clear") {
+       # if clearing, skip past existing data
+       get($len);
+} else {
+       # otherwise, copy up to the requested offset,
+       # and skip past the overwritten bytes
+       copy($seek);
+       get(length($bytes));
+}
+
+# now write out the requested bytes, along
+# with any other remaining data
+print $bytes;
+while (read(STDIN, my $buf, 4096)) {
+       print $buf;
+}
index 5d79e1a4e967619125a06ae1a2ecf38c0fe3fcab..89b26676fbb94b0b4f570ec9a51ca74cce7620c5 100755 (executable)
@@ -14,24 +14,37 @@ graph_git_two_modes() {
        test_cmp expect output
 }
 
+# graph_git_behavior <name> <directory> <branch> <compare>
+#
+# Ensures that a handful of traversal operations produce the same
+# results with and without the commit-graph in use.
+#
+# NOTE: it is a bug to call this function with <directory> containing
+# any characters in $IFS.
 graph_git_behavior() {
        MSG=$1
        DIR=$2
        BRANCH=$3
        COMPARE=$4
        test_expect_success "check normal git operations: $MSG" '
-               cd "$TRASH_DIRECTORY/$DIR" &&
-               graph_git_two_modes "log --oneline $BRANCH" &&
-               graph_git_two_modes "log --topo-order $BRANCH" &&
-               graph_git_two_modes "log --graph $COMPARE..$BRANCH" &&
-               graph_git_two_modes "branch -vv" &&
-               graph_git_two_modes "merge-base -a $BRANCH $COMPARE"
+               graph_git_two_modes "${DIR:+-C $DIR} log --oneline $BRANCH" &&
+               graph_git_two_modes "${DIR:+-C $DIR} log --topo-order $BRANCH" &&
+               graph_git_two_modes "${DIR:+-C $DIR} log --graph $COMPARE..$BRANCH" &&
+               graph_git_two_modes "${DIR:+-C $DIR} branch -vv" &&
+               graph_git_two_modes "${DIR:+-C $DIR} merge-base -a $BRANCH $COMPARE"
        '
 }
 
 graph_read_expect() {
        OPTIONAL=""
        NUM_CHUNKS=3
+       DIR="."
+       if test "$1" = -C
+       then
+               shift
+               DIR="$1"
+               shift
+       fi
        if test -n "$2"
        then
                OPTIONAL=" $2"
@@ -47,12 +60,15 @@ graph_read_expect() {
        then
                OPTIONS=" read_generation_data"
        fi
-       cat >expect <<- EOF
+       cat >"$DIR/expect" <<-EOF
        header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
        num_commits: $1
        chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
        options:$OPTIONS
        EOF
-       test-tool read-graph >output &&
-       test_cmp expect output
+       (
+               cd "$DIR" &&
+               test-tool read-graph >output &&
+               test_cmp expect output
+       )
 }
index 5ea8bc9f1dcfe644da4b9ce720f2a523ff172475..15fc9a31e2cc03689e76876693e39367626784ff 100644 (file)
@@ -43,6 +43,13 @@ helper_test_clean() {
        reject $1 https example.com store-user
        reject $1 https example.com user1
        reject $1 https example.com user2
+       reject $1 https example.com user-expiry
+       reject $1 https example.com user-expiry-overwrite
+       reject $1 https example.com user4
+       reject $1 https example.com user-distinct-pass
+       reject $1 https example.com user-overwrite
+       reject $1 https example.com user-erase1
+       reject $1 https example.com user-erase2
        reject $1 http path.tld user
        reject $1 https timeout.tld user
        reject $1 https sso.tld
@@ -166,6 +173,49 @@ helper_test() {
                EOF
        '
 
+       test_expect_success "helper ($HELPER) overwrites on store" '
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-overwrite
+               password=pass1
+               EOF
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-overwrite
+               password=pass2
+               EOF
+               check fill $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-overwrite
+               --
+               protocol=https
+               host=example.com
+               username=user-overwrite
+               password=pass2
+               EOF
+               check reject $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-overwrite
+               password=pass2
+               EOF
+               check fill $HELPER <<-\EOF
+               protocol=https
+               host=example.com
+               username=user-overwrite
+               --
+               protocol=https
+               host=example.com
+               username=user-overwrite
+               password=askpass-password
+               --
+               askpass: Password for '\''https://user-overwrite@example.com'\'':
+               EOF
+       '
+
        test_expect_success "helper ($HELPER) can forget host" '
                check reject $HELPER <<-\EOF &&
                protocol=https
@@ -220,6 +270,31 @@ helper_test() {
                EOF
        '
 
+       test_expect_success "helper ($HELPER) does not erase a password distinct from input" '
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-distinct-pass
+               password=pass1
+               EOF
+               check reject $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-distinct-pass
+               password=pass2
+               EOF
+               check fill $HELPER <<-\EOF
+               protocol=https
+               host=example.com
+               username=user-distinct-pass
+               --
+               protocol=https
+               host=example.com
+               username=user-distinct-pass
+               password=pass1
+               EOF
+       '
+
        test_expect_success "helper ($HELPER) can forget user" '
                check reject $HELPER <<-\EOF &&
                protocol=https
@@ -270,6 +345,66 @@ helper_test() {
                password=
                EOF
        '
+
+       test_expect_success "helper ($HELPER) erases all matching credentials" '
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-erase1
+               password=pass1
+               EOF
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-erase2
+               password=pass1
+               EOF
+               check reject $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               EOF
+               check fill $HELPER <<-\EOF
+               protocol=https
+               host=example.com
+               --
+               protocol=https
+               host=example.com
+               username=askpass-username
+               password=askpass-password
+               --
+               askpass: Username for '\''https://example.com'\'':
+               askpass: Password for '\''https://askpass-username@example.com'\'':
+               EOF
+       '
+
+       : ${GIT_TEST_LONG_CRED_BUFFER:=1024}
+       # 23 bytes accounts for "wwwauth[]=basic realm=" plus NUL
+       LONG_VALUE_LEN=$((GIT_TEST_LONG_CRED_BUFFER - 23))
+       LONG_VALUE=$(perl -e 'print "a" x shift' $LONG_VALUE_LEN)
+
+       test_expect_success "helper ($HELPER) not confused by long header" '
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=victim.example.com
+               username=user
+               password=to-be-stolen
+               EOF
+
+               check fill $HELPER <<-EOF
+               protocol=https
+               host=badguy.example.com
+               wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com
+               --
+               protocol=https
+               host=badguy.example.com
+               username=askpass-username
+               password=askpass-password
+               wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com
+               --
+               askpass: Username for '\''https://badguy.example.com'\'':
+               askpass: Password for '\''https://askpass-username@badguy.example.com'\'':
+               EOF
+       '
 }
 
 helper_test_timeout() {
@@ -298,6 +433,110 @@ helper_test_timeout() {
        '
 }
 
+helper_test_password_expiry_utc() {
+       HELPER=$1
+
+       test_expect_success "helper ($HELPER) stores password_expiry_utc" '
+               check approve $HELPER <<-\EOF
+               protocol=https
+               host=example.com
+               username=user-expiry
+               password=pass
+               password_expiry_utc=9999999999
+               EOF
+       '
+
+       test_expect_success "helper ($HELPER) gets password_expiry_utc" '
+               check fill $HELPER <<-\EOF
+               protocol=https
+               host=example.com
+               username=user-expiry
+               --
+               protocol=https
+               host=example.com
+               username=user-expiry
+               password=pass
+               password_expiry_utc=9999999999
+               --
+               EOF
+       '
+
+       test_expect_success "helper ($HELPER) overwrites when password_expiry_utc changes" '
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-expiry-overwrite
+               password=pass1
+               password_expiry_utc=9999999998
+               EOF
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-expiry-overwrite
+               password=pass2
+               password_expiry_utc=9999999999
+               EOF
+               check fill $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-expiry-overwrite
+               --
+               protocol=https
+               host=example.com
+               username=user-expiry-overwrite
+               password=pass2
+               password_expiry_utc=9999999999
+               EOF
+               check reject $HELPER <<-\EOF &&
+               protocol=https
+               host=example.com
+               username=user-expiry-overwrite
+               password=pass2
+               EOF
+               check fill $HELPER <<-\EOF
+               protocol=https
+               host=example.com
+               username=user-expiry-overwrite
+               --
+               protocol=https
+               host=example.com
+               username=user-expiry-overwrite
+               password=askpass-password
+               --
+               askpass: Password for '\''https://user-expiry-overwrite@example.com'\'':
+               EOF
+       '
+}
+
+helper_test_oauth_refresh_token() {
+       HELPER=$1
+
+       test_expect_success "helper ($HELPER) stores oauth_refresh_token" '
+               check approve $HELPER <<-\EOF
+               protocol=https
+               host=example.com
+               username=user4
+               password=pass
+               oauth_refresh_token=xyzzy
+               EOF
+       '
+
+       test_expect_success "helper ($HELPER) gets oauth_refresh_token" '
+               check fill $HELPER <<-\EOF
+               protocol=https
+               host=example.com
+               username=user4
+               --
+               protocol=https
+               host=example.com
+               username=user4
+               password=pass
+               oauth_refresh_token=xyzzy
+               --
+               EOF
+       '
+}
+
 write_script askpass <<\EOF
 echo >&2 askpass: $*
 what=$(echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z)
index 8d1e408bb58f5e097f242d5c1fd2c7bb036deb6e..c4dc2d46dc1ef4d3c95bda858d208045ded864c2 100644 (file)
@@ -105,10 +105,67 @@ index $file1..$file2 100644
  }
 EOF
 
+       cat >expect_diffstat <<EOF
+ file1 => file2 | 21 ++++++++++-----------
+ 1 file changed, 10 insertions(+), 11 deletions(-)
+EOF
+
        STRATEGY=$1
 
+       test_expect_success "setup attributes files for tests with $STRATEGY" '
+               git checkout -b master &&
+               echo "file* diff=driver" >.gitattributes &&
+               git add file1 file2 .gitattributes &&
+               git commit -m "adding files" &&
+               git checkout -b branchA &&
+               echo "file* diff=driverA" >.gitattributes &&
+               git add .gitattributes &&
+               git commit -m "adding driverA as diff driver" &&
+               git checkout master &&
+               git clone --bare --no-local . bare.git
+       '
+
+       test_expect_success "$STRATEGY diff from attributes" '
+               test_must_fail git -c diff.driver.algorithm=$STRATEGY diff --no-index file1 file2 > output &&
+               test_cmp expect output
+       '
+
+       test_expect_success "diff from attributes with bare repo with source" '
+               git -C bare.git --attr-source=branchA -c diff.driver.algorithm=myers \
+                       -c diff.driverA.algorithm=$STRATEGY \
+                       diff HEAD:file1 HEAD:file2 >output &&
+               test_cmp expect output
+       '
+
+       test_expect_success "diff from attributes with bare repo with invalid source" '
+               test_must_fail git -C bare.git --attr-source=invalid-branch diff \
+                       HEAD:file1 HEAD:file2
+       '
+
+       test_expect_success "$STRATEGY diff from attributes has valid diffstat" '
+               echo "file* diff=driver" >.gitattributes &&
+               git config diff.driver.algorithm "$STRATEGY" &&
+               test_must_fail git diff --stat --no-index file1 file2 > output &&
+               test_cmp expect_diffstat output
+       '
+
        test_expect_success "$STRATEGY diff" '
-               test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output &&
+               test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output &&
+               test_cmp expect output
+       '
+
+       test_expect_success "$STRATEGY diff command line precedence before attributes" '
+               echo "file* diff=driver" >.gitattributes &&
+               git config diff.driver.algorithm myers &&
+               test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output &&
+               test_cmp expect output
+       '
+
+       test_expect_success "$STRATEGY diff attributes precedence before config" '
+               git config diff.algorithm default &&
+               echo "file* diff=driver" >.gitattributes &&
+               git config diff.driver.algorithm "$STRATEGY" &&
+               test_must_fail git diff --no-index file1 file2 > output &&
                test_cmp expect output
        '
 
index 114785586abde524fb13cb29e4b1f357ae9a3286..add11e88fc00d7c452f62c0f6da0835fd12f53a2 100644 (file)
@@ -13,7 +13,7 @@ test_lazy_prereq GPG '
        gpg_version=$(gpg --version 2>&1)
        test $? != 127 || exit 1
 
-       # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+       # As said here: https://web.archive.org/web/20130212022238/https://www.gnupg.org/faq/gnupg-faq.html#why-does-gnupg-1.0.6-bail-out-on-keyrings-used-with-1.0.7
        # the gpg version 1.0.6 did not parse trust packets correctly, so for
        # that version, creation of signed tags using the generated key fails.
        case "$gpg_version" in
@@ -40,6 +40,28 @@ test_lazy_prereq GPG '
                #               > lib-gpg/ownertrust
                mkdir "$GNUPGHOME" &&
                chmod 0700 "$GNUPGHOME" &&
+               (gpgconf --kill all || : ) &&
+               gpg --homedir "${GNUPGHOME}" --import \
+                       "$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
+               gpg --homedir "${GNUPGHOME}" --import-ownertrust \
+                       "$TEST_DIRECTORY"/lib-gpg/ownertrust &&
+               gpg --homedir "${GNUPGHOME}" --update-trustdb &&
+               gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \
+                       --sign -u committer@example.com
+               ;;
+       esac
+'
+
+test_lazy_prereq GPG2 '
+       gpg_version=$(gpg --version 2>&1)
+       test $? != 127 || exit 1
+
+       case "$gpg_version" in
+       "gpg (GnuPG) "[01].*)
+               say "This test requires a GPG version >= v2.0.0"
+               exit 1
+               ;;
+       *)
                (gpgconf --kill all || : ) &&
                gpg --homedir "${GNUPGHOME}" --import \
                        "$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
@@ -135,8 +157,9 @@ test_lazy_prereq GPGSSH '
 '
 
 test_lazy_prereq GPGSSH_VERIFYTIME '
+       test_have_prereq GPGSSH &&
        # Check if ssh-keygen has a verify-time option by passing an invalid date to it
-       ssh-keygen -Overify-time=INVALID -Y check-novalidate -s doesnotmatter 2>&1 | grep -q -F "Invalid \"verify-time\"" &&
+       ssh-keygen -Overify-time=INVALID -Y check-novalidate -n "git" -s doesnotmatter 2>&1 | grep -q -F "Invalid \"verify-time\"" &&
 
        # Set up keys with key lifetimes
        ssh-keygen -t ed25519 -N "" -C "timeboxed valid key" -f "${GPGSSH_KEY_TIMEBOXEDVALID}" >/dev/null &&
index 1f6b9b08d1de626397396f9878812a7a6e6f0ef8..d83bafeab32d40d6fd373e084757221ac7181d06 100644 (file)
@@ -25,6 +25,7 @@
 #    LIB_HTTPD_DAV               enable DAV
 #    LIB_HTTPD_SVN               enable SVN at given location (e.g. "svn")
 #    LIB_HTTPD_SSL               enable SSL
+#    LIB_HTTPD_PROXY             enable proxy
 #
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
 #
@@ -54,20 +55,31 @@ fi
 
 HTTPD_PARA=""
 
-for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
+for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' \
+                         '/usr/sbin/apache2' \
+                         "$(command -v httpd)" \
+                         "$(command -v apache2)"
 do
-       if test -x "$DEFAULT_HTTPD_PATH"
+       if test -n "$DEFAULT_HTTPD_PATH" && test -x "$DEFAULT_HTTPD_PATH"
        then
                break
        fi
 done
 
+if test -x "$DEFAULT_HTTPD_PATH"
+then
+       DETECTED_HTTPD_ROOT="$("$DEFAULT_HTTPD_PATH" -V 2>/dev/null | sed -n 's/^ -D HTTPD_ROOT="\(.*\)"$/\1/p')"
+fi
+
 for DEFAULT_HTTPD_MODULE_PATH in '/usr/libexec/apache2' \
                                 '/usr/lib/apache2/modules' \
                                 '/usr/lib64/httpd/modules' \
-                                '/usr/lib/httpd/modules'
+                                '/usr/lib/httpd/modules' \
+                                '/usr/libexec/httpd' \
+                                '/usr/lib/apache2' \
+                                "${DETECTED_HTTPD_ROOT:+${DETECTED_HTTPD_ROOT}/modules}"
 do
-       if test -d "$DEFAULT_HTTPD_MODULE_PATH"
+       if test -n "$DEFAULT_HTTPD_MODULE_PATH" && test -d "$DEFAULT_HTTPD_MODULE_PATH"
        then
                break
        fi
@@ -98,16 +110,19 @@ then
 fi
 
 HTTPD_VERSION=$($LIB_HTTPD_PATH -v | \
-       sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q')
+       sed -n 's/^Server version: Apache\/\([0-9.]*\).*$/\1/p; q')
+HTTPD_VERSION_MAJOR=$(echo $HTTPD_VERSION | cut -d. -f1)
+HTTPD_VERSION_MINOR=$(echo $HTTPD_VERSION | cut -d. -f2)
 
-if test -n "$HTTPD_VERSION"
+if test -n "$HTTPD_VERSION_MAJOR"
 then
        if test -z "$LIB_HTTPD_MODULE_PATH"
        then
-               if ! test $HTTPD_VERSION -ge 2
+               if ! test "$HTTPD_VERSION_MAJOR" -eq 2 ||
+                  ! test "$HTTPD_VERSION_MINOR" -ge 4
                then
                        test_skip_or_die GIT_TEST_HTTPD \
-                               "at least Apache version 2 is required"
+                               "at least Apache version 2.4 is required"
                fi
                if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
                then
@@ -122,6 +137,20 @@ else
                "Could not identify web server at '$LIB_HTTPD_PATH'"
 fi
 
+if test -n "$LIB_HTTPD_DAV" && test -f /etc/os-release
+then
+       case "$(grep "^ID=" /etc/os-release | cut -d= -f2-)" in
+       alpine)
+               # The WebDAV module in Alpine Linux is broken at least up to
+               # Alpine v3.16 as the default DBM driver is missing.
+               #
+               # https://gitlab.alpinelinux.org/alpine/aports/-/issues/13112
+               test_skip_or_die GIT_TEST_HTTPD \
+                       "Apache WebDAV module does not have default DBM backend driver"
+               ;;
+       esac
+fi
+
 install_script () {
        write_script "$HTTPD_ROOT_PATH/$1" <"$TEST_PATH/$1"
 }
@@ -129,6 +158,7 @@ install_script () {
 prepare_httpd() {
        mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
        cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
+       cp "$TEST_PATH"/proxy-passwd "$HTTPD_ROOT_PATH"
        install_script incomplete-length-upload-pack-v2-http.sh
        install_script incomplete-body-upload-pack-v2-http.sh
        install_script error-no-report.sh
@@ -136,6 +166,7 @@ prepare_httpd() {
        install_script error-smart-http.sh
        install_script error.sh
        install_script apply-one-time-perl.sh
+       install_script nph-custom-auth.sh
 
        ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
 
@@ -172,6 +203,30 @@ prepare_httpd() {
                        export LIB_HTTPD_SVN LIB_HTTPD_SVNPATH
                fi
        fi
+
+       if test -n "$LIB_HTTPD_PROXY"
+       then
+               HTTPD_PARA="$HTTPD_PARA -DPROXY"
+       fi
+}
+
+enable_http2 () {
+       HTTPD_PARA="$HTTPD_PARA -DHTTP2"
+       test_set_prereq HTTP2
+}
+
+enable_cgipassauth () {
+       # We are looking for 2.4.13 or more recent. Since we only support
+       # 2.4 and up, no need to check for older major/minor.
+       if test "$HTTPD_VERSION_MAJOR" = 2 &&
+          test "$HTTPD_VERSION_MINOR" = 4 &&
+          test "$(echo $HTTPD_VERSION | cut -d. -f3)" -lt 13
+       then
+               echo >&4 "apache $HTTPD_VERSION too old for CGIPassAuth"
+               return
+       fi
+       HTTPD_PARA="$HTTPD_PARA -DUSE_CGIPASSAUTH"
+       test_set_prereq CGIPASSAUTH
 }
 
 start_httpd() {
@@ -211,8 +266,12 @@ test_http_push_nonff () {
                git commit -a -m path2 --amend &&
 
                test_must_fail git push -v origin >output 2>&1 &&
-               (cd "$REMOTE_REPO" &&
-                test $HEAD = $(git rev-parse --verify HEAD))
+               (
+                       cd "$REMOTE_REPO" &&
+                       echo "$HEAD" >expect &&
+                       git rev-parse --verify HEAD >actual &&
+                       test_cmp expect actual
+               )
        '
 
        test_expect_success 'non-fast-forward push show ref status' '
@@ -220,7 +279,7 @@ test_http_push_nonff () {
        '
 
        test_expect_success 'non-fast-forward push shows help message' '
-               test_i18ngrep "Updates were rejected because" output
+               test_grep "Updates were rejected because" output
        '
 
        test_expect_${EXPECT_CAS_RESULT} 'force with lease aka cas' '
@@ -274,11 +333,11 @@ expect_askpass() {
                none)
                        ;;
                pass)
-                       echo "askpass: Password for 'http://$2@$dest': "
+                       echo "askpass: Password for '$HTTPD_PROTO://$2@$dest': "
                        ;;
                both)
-                       echo "askpass: Username for 'http://$dest': "
-                       echo "askpass: Password for 'http://$2@$dest': "
+                       echo "askpass: Username for '$HTTPD_PROTO://$dest': "
+                       echo "askpass: Password for '$HTTPD_PROTO://$2@$dest': "
                        ;;
                *)
                        false
index 706799391bd4047a1c15f8a32f77875cb8dcb44d..022276a6b9ace5565211327053f9900ff4a1fb06 100644 (file)
@@ -29,17 +29,11 @@ ErrorLog error.log
        LoadModule setenvif_module modules/mod_setenvif.so
 </IfModule>
 
-<IfVersion < 2.4>
-LockFile accept.lock
-</IfVersion>
-
-<IfVersion < 2.1>
-<IfModule !mod_auth.c>
-       LoadModule auth_module modules/mod_auth.so
-</IfModule>
-</IfVersion>
+<IfDefine HTTP2>
+LoadModule http2_module modules/mod_http2.so
+Protocols h2 h2c
+</IfDefine>
 
-<IfVersion >= 2.1>
 <IfModule !mod_auth_basic.c>
        LoadModule auth_basic_module modules/mod_auth_basic.so
 </IfModule>
@@ -52,9 +46,23 @@ LockFile accept.lock
 <IfModule !mod_authz_host.c>
        LoadModule authz_host_module modules/mod_authz_host.so
 </IfModule>
-</IfVersion>
 
-<IfVersion >= 2.4>
+<IfDefine PROXY>
+<IfModule !mod_proxy.c>
+       LoadModule proxy_module modules/mod_proxy.so
+</IfModule>
+<IfModule !mod_proxy_http.c>
+       LoadModule proxy_http_module modules/mod_proxy_http.so
+</IfModule>
+ProxyRequests On
+<Proxy "*">
+       AuthType Basic
+       AuthName "proxy-auth"
+       AuthUserFile proxy-passwd
+       Require valid-user
+</Proxy>
+</IfDefine>
+
 <IfModule !mod_authn_core.c>
        LoadModule authn_core_module modules/mod_authn_core.so
 </IfModule>
@@ -64,19 +72,27 @@ LockFile accept.lock
 <IfModule !mod_access_compat.c>
        LoadModule access_compat_module modules/mod_access_compat.so
 </IfModule>
-<IfModule !mod_mpm_prefork.c>
-       LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
-</IfModule>
 <IfModule !mod_unixd.c>
        LoadModule unixd_module modules/mod_unixd.so
 </IfModule>
-</IfVersion>
+
+<IfDefine HTTP2>
+<IfModule !mod_mpm_event.c>
+       LoadModule mpm_event_module modules/mod_mpm_event.so
+</IfModule>
+</IfDefine>
+<IfDefine !HTTP2>
+<IfModule !mod_mpm_prefork.c>
+       LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+</IfModule>
+</IfDefine>
 
 PassEnv GIT_VALGRIND
 PassEnv GIT_VALGRIND_OPTIONS
 PassEnv GNUPGHOME
 PassEnv ASAN_OPTIONS
 PassEnv LSAN_OPTIONS
+PassEnv UBSAN_OPTIONS
 PassEnv GIT_TRACE
 PassEnv GIT_CONFIG_NOSYSTEM
 PassEnv GIT_TEST_SIDEBAND_ALL
@@ -86,6 +102,8 @@ PassEnv LC_ALL
 Alias /dumb/ www/
 Alias /auth/dumb/ www/auth/dumb/
 
+SetEnv PERL_PATH ${PERL_PATH}
+
 <LocationMatch /smart/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
@@ -110,6 +128,10 @@ Alias /auth/dumb/ www/auth/dumb/
        Header set Set-Cookie name=value
 </LocationMatch>
 <LocationMatch /smart_headers/>
+       <RequireAll>
+               Require expr %{HTTP:x-magic-one} == 'abra'
+               Require expr %{HTTP:x-magic-two} == 'cadabra'
+       </RequireAll>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
 </LocationMatch>
@@ -122,6 +144,13 @@ Alias /auth/dumb/ www/auth/dumb/
        SetEnv GIT_HTTP_EXPORT_ALL
        SetEnv GIT_PROTOCOL
 </LocationMatch>
+<LocationMatch /custom_auth/>
+       SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+       SetEnv GIT_HTTP_EXPORT_ALL
+       <IfDefine USE_CGIPASSAUTH>
+       CGIPassAuth on
+       </IfDefine>
+</LocationMatch>
 ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pack-v2-http.sh/
 ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/
 ScriptAlias /smart/no_report/git-receive-pack error-no-report.sh/
@@ -131,6 +160,7 @@ ScriptAlias /broken_smart/ broken-smart-http.sh/
 ScriptAlias /error_smart/ error-smart-http.sh/
 ScriptAlias /error/ error.sh/
 ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
+ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
 <Directory ${GIT_EXEC_PATH}>
        Options FollowSymlinks
 </Directory>
@@ -192,18 +222,6 @@ RewriteRule ^/intern-redir/(.*)/foo$ /smart/$1 [PT]
 RewriteRule ^/redir-objects/(.*/info/refs)$ /dumb/$1 [PT]
 RewriteRule ^/redir-objects/(.*/objects/.*)$ /dumb/$1 [R=301]
 
-# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
-# And as RewriteCond does not allow testing for non-matches, we match
-# the desired case first (one has abra, two has cadabra), and let it
-# pass by marking the RewriteRule as [L], "last rule, do not process
-# any other matching RewriteRules after this"), and then have another
-# RewriteRule that matches all other cases and lets them fail via '[F]',
-# "fail the request".
-RewriteCond %{HTTP:x-magic-one} =abra
-RewriteCond %{HTTP:x-magic-two} =cadabra
-RewriteRule ^/smart_headers/.* - [L]
-RewriteRule ^/smart_headers/.* - [F]
-
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
 
@@ -212,7 +230,6 @@ SSLCertificateKeyFile httpd.pem
 SSLRandomSeed startup file:/dev/urandom 512
 SSLRandomSeed connect file:/dev/urandom 512
 SSLSessionCache none
-SSLMutex file:ssl_mutex
 SSLEngine On
 </IfDefine>
 
index 09a0abdff7c4775f5ba5fd50f5753b3aed35d60b..d7f9fed6aee8b2a8a665f74507fc1ae41065d22f 100644 (file)
@@ -13,7 +13,7 @@ then
        export LC_ALL
 
        "$GIT_EXEC_PATH/git-http-backend" >out
-       perl -pe "$(cat one-time-perl)" out >out_modified
+       "$PERL_PATH" -pe "$(cat one-time-perl)" out >out_modified
 
        if cmp -s out out_modified
        then
diff --git a/t/lib-httpd/nph-custom-auth.sh b/t/lib-httpd/nph-custom-auth.sh
new file mode 100644 (file)
index 0000000..f5345e7
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+VALID_CREDS_FILE=custom-auth.valid
+CHALLENGE_FILE=custom-auth.challenge
+
+#
+# If $VALID_CREDS_FILE exists in $HTTPD_ROOT_PATH, consider each line as a valid
+# credential for the current request. Each line in the file is considered a
+# valid HTTP Authorization header value. For example:
+#
+# Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+#
+# If $CHALLENGE_FILE exists in $HTTPD_ROOT_PATH, output the contents as headers
+# in a 401 response if no valid authentication credentials were included in the
+# request. For example:
+#
+# WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+# WWW-Authenticate: Basic realm="example.com"
+#
+
+if test -n "$HTTP_AUTHORIZATION" && \
+       grep -Fqsx "${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
+then
+       # Note that although git-http-backend returns a status line, it
+       # does so using a CGI 'Status' header. Because this script is an
+       # No Parsed Headers (NPH) script, we must return a real HTTP
+       # status line.
+       # This is only a test script, so we don't bother to check for
+       # the actual status from git-http-backend and always return 200.
+       echo 'HTTP/1.1 200 OK'
+       exec "$GIT_EXEC_PATH"/git-http-backend
+fi
+
+echo 'HTTP/1.1 401 Authorization Required'
+if test -f "$CHALLENGE_FILE"
+then
+       cat "$CHALLENGE_FILE"
+fi
+echo
index 99a34d648742f669e7b501298a92fab401bc1971..d9c122f34828919da09284529b9e5a70b4ffeec1 100644 (file)
@@ -1 +1 @@
-user@host:xb4E8pqD81KQs
+user@host:$apr1$LGPmCZWj$9vxEwj5Z5GzQLBMxp3mCx1
diff --git a/t/lib-httpd/proxy-passwd b/t/lib-httpd/proxy-passwd
new file mode 100644 (file)
index 0000000..2ad7705
--- /dev/null
@@ -0,0 +1 @@
+proxuser:$apr1$RxS6MLkD$DYsqQdflheq4GPNxzJpx5.
index 6dab2579cbf9658c3ac2bd55c8a66333d67eda47..812e8253f0eea30c2411fd06ba518351a8253f9a 100644 (file)
@@ -1,7 +1,7 @@
 RANDFILE                = $ENV::RANDFILE_PATH
 
 [ req ]
-default_bits            = 1024
+default_bits            = 2048
 distinguished_name      = req_distinguished_name
 prompt                  = no
 [ req_distinguished_name ]
index cfd76bf987bd902503cfcacd3e8ac1b9c4deb09c..89ca1f7805547eec1f4ee8e90c1afefc30b35da6 100644 (file)
@@ -29,8 +29,12 @@ set_and_save_state () {
 
 # verify_state <path> <expected-worktree-content> <expected-index-content>
 verify_state () {
-       test "$(cat "$1")" = "$2" &&
-       test "$(git show :"$1")" = "$3"
+       echo "$2" >expect &&
+       test_cmp expect "$1" &&
+
+       echo "$3" >expect &&
+       git show :"$1" >actual &&
+       test_cmp expect actual
 }
 
 # verify_saved_state <path>
@@ -46,5 +50,6 @@ save_head () {
 }
 
 verify_saved_head () {
-       test "$(cat _head)" = "$(git rev-parse HEAD)"
+       git rev-parse HEAD >actual &&
+       test_cmp _head actual
 }
index b57541356bd03d139334a144339b01a92be2c70a..11d2dc9fe341b61d22c467ea8ae6a9d13ff1ac18 100644 (file)
@@ -8,18 +8,21 @@
 # - check that non-commit messages have a certain line count with $EXPECT_COUNT
 # - check the commit count in the commit message header with $EXPECT_HEADER_COUNT
 # - rewrite a rebase -i script as directed by $FAKE_LINES.
-#   $FAKE_LINES consists of a sequence of words separated by spaces.
-#   The following word combinations are possible:
+#   $FAKE_LINES consists of a sequence of words separated by spaces;
+#   spaces inside the words are encoded as underscores.
+#   The following words are possible:
 #
-#   "<lineno>" -- add a "pick" line with the SHA1 taken from the
-#       specified line.
+#   "<cmd>" -- override the command for the next line specification. Can be
+#       "pick", "squash", "fixup[_-(c|C)]", "edit", "reword", "drop",
+#       "merge[_-(c|C)_<rev>]", or "bad" for an invalid command.
 #
-#   "<cmd> <lineno>" -- add a line with the specified command
-#       ("pick", "squash", "fixup"|"fixup_-C"|"fixup_-c", "edit", "reword" or "drop")
-#       and the SHA1 taken from the specified line.
+#   "<lineno>" -- add a command, using the specified line as a template.
+#       If the command has not been overridden, the line will be copied
+#       verbatim, usually resulting in a "pick" line.
 #
-#   "_" -- add a space, like "fixup_-C" implies "fixup -C" and
-#       "exec_cmd_with_args" add an "exec cmd with args" line.
+#   "fakesha" -- add a command ("pick" by default), using a fake SHA1.
+#
+#   "exec_[command...]", "break" -- add the specified command.
 #
 #   "#" -- Add a comment line.
 #
@@ -49,7 +52,7 @@ set_fake_editor () {
        action=\&
        for line in $FAKE_LINES; do
                case $line in
-               pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m)
+               pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|t|merge|m)
                        action="$line";;
                exec_*|x_*|break|b)
                        echo "$line" | sed 's/_/ /g' >> "$1";;
@@ -60,11 +63,11 @@ set_fake_editor () {
                ">")
                        echo >> "$1";;
                bad)
-                       action="badcmd";;
+                       action="pickled";;
                fakesha)
                        test \& != "$action" || action=pick
                        echo "$action XXXXXXX False commit" >> "$1"
-                       action=pick;;
+                       action=\&;;
                *)
                        sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1"
                        action=\&;;
@@ -211,6 +214,9 @@ check_reworded_commits () {
 # usage: set_replace_editor <file>
 #
 # Replace the todo file with the exact contents of the given file.
+# N.B. sets GIT_SEQUENCE_EDITOR rather than EDITOR so it can be
+# combined with set_fake_editor to reword commits and replace the
+# todo list
 set_replace_editor () {
        cat >script <<-\EOF &&
        cat FILENAME >"$1"
@@ -219,6 +225,7 @@ set_replace_editor () {
        cat "$1"
        EOF
 
-       sed -e "s/FILENAME/$1/g" <script | write_script fake-editor.sh &&
-       test_set_editor "$(pwd)/fake-editor.sh"
+       sed -e "s/FILENAME/$1/g" script |
+               write_script fake-sequence-editor.sh &&
+       test_set_sequence_editor "$(pwd)/fake-sequence-editor.sh"
 }
index 2d31fcfda1f338973cfc868b7f01ae925b39d326..36f767cb7488bff81696f985e3614a5fffcda495 100644 (file)
@@ -168,20 +168,16 @@ replace_gitfile_with_git_dir () {
 # Note that this only supports submodules at the root level of the
 # superproject, with the default name, i.e. same as its path.
 test_git_directory_is_unchanged () {
-       (
-               cd ".git/modules/$1" &&
-               # does core.worktree point at the right place?
-               test "$(git config core.worktree)" = "../../../$1" &&
-               # remove it temporarily before comparing, as
-               # "$1/.git/config" lacks it...
-               git config --unset core.worktree
-       ) &&
+       # does core.worktree point at the right place?
+       echo "../../../$1" >expect &&
+       git -C ".git/modules/$1" config core.worktree >actual &&
+       test_cmp expect actual &&
+       # remove it temporarily before comparing, as
+       # "$1/.git/config" lacks it...
+       git -C ".git/modules/$1" config --unset core.worktree &&
        diff -r ".git/modules/$1" "$1/.git" &&
-       (
-               # ... and then restore.
-               cd ".git/modules/$1" &&
-               git config core.worktree "../../../$1"
-       )
+       # ... and then restore.
+       git -C ".git/modules/$1" config core.worktree "../../../$1"
 }
 
 test_git_directory_exists () {
@@ -189,7 +185,9 @@ test_git_directory_exists () {
        if test -f sub1/.git
        then
                # does core.worktree point at the right place?
-               test "$(git -C .git/modules/$1 config core.worktree)" = "../../../$1"
+               echo "../../../$1" >expect &&
+               git -C ".git/modules/$1" config core.worktree >actual &&
+               test_cmp expect actual
        fi
 }
 
@@ -804,7 +802,7 @@ test_submodule_recursing_with_args_common () {
                        git branch -t no_submodule origin/no_submodule &&
                        $command no_submodule &&
                        test_superproject_content origin/no_submodule &&
-                       ! test_path_is_dir sub1 &&
+                       test_path_is_missing sub1 &&
                        test_must_fail git config -f .git/modules/sub1/config core.worktree &&
                        test_must_fail git config -f .git/modules/sub1/modules/sub2/config core.worktree
                )
@@ -832,7 +830,7 @@ test_submodule_recursing_with_args_common () {
                        cd submodule_update &&
                        git branch -t invalid_sub1 origin/invalid_sub1 &&
                        test_must_fail $command invalid_sub1 2>err &&
-                       test_i18ngrep sub1 err &&
+                       test_grep sub1 err &&
                        test_superproject_content origin/add_sub1 &&
                        test_submodule_content sub1 origin/add_sub1
                )
index c481c012d2fc17a7944f308c2d6ef68acb284dee..325566e18ebc7fa7359d079935d6d7f81cd4dc54 100755 (executable)
@@ -49,6 +49,14 @@ test_perf "read-tree br_base br_ballast ($nr_files)" '
        git read-tree -n -m br_base br_ballast
 '
 
+test_perf "read-tree br_ballast_plus_1 ($nr_files)" '
+       # Run read-tree 100 times for clearer performance results & comparisons
+       for i in  $(test_seq 100)
+       do
+               git read-tree -n -m br_ballast_plus_1 || return 1
+       done
+'
+
 test_perf "switch between br_base br_ballast ($nr_files)" '
        git checkout -q br_base &&
        git checkout -q br_ballast
diff --git a/t/perf/p0090-cache-tree.sh b/t/perf/p0090-cache-tree.sh
new file mode 100755 (executable)
index 0000000..a8eabca
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description="Tests performance of cache tree update operations"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+count=100
+
+test_expect_success 'setup cache tree' '
+       git write-tree
+'
+
+test_cache_tree () {
+       test_perf "$1, $3" "
+               for i in \$(test_seq $count)
+               do
+                       test-tool cache-tree $4 $2
+               done
+       "
+}
+
+test_cache_tree_update_functions () {
+       test_cache_tree 'no-op' 'control' "$1" "$2"
+       test_cache_tree 'prime_cache_tree' 'prime' "$1" "$2"
+       test_cache_tree 'cache_tree_update' 'update' "$1" "$2"
+}
+
+test_cache_tree_update_functions "clean" ""
+test_cache_tree_update_functions "invalidate 2" "--invalidate 2"
+test_cache_tree_update_functions "invalidate 50" "--invalidate 50"
+test_cache_tree_update_functions "empty" "--empty"
+
+test_done
diff --git a/t/perf/p1500-graph-walks.sh b/t/perf/p1500-graph-walks.sh
new file mode 100755 (executable)
index 0000000..e14e762
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='Commit walk performance tests'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup' '
+       git for-each-ref --format="%(refname)" "refs/heads/*" "refs/tags/*" >allrefs &&
+       sort -r allrefs | head -n 50 >refs &&
+       for ref in $(cat refs)
+       do
+               git branch -f ref-$ref $ref &&
+               echo ref-$ref ||
+               return 1
+       done >branches &&
+       for ref in $(cat refs)
+       do
+               git tag -f tag-$ref $ref &&
+               echo tag-$ref ||
+               return 1
+       done >tags &&
+       git commit-graph write --reachable
+'
+
+test_perf 'ahead-behind counts: git for-each-ref' '
+       git for-each-ref --format="%(ahead-behind:HEAD)" --stdin <refs
+'
+
+test_perf 'ahead-behind counts: git branch' '
+       xargs git branch -l --format="%(ahead-behind:HEAD)" <branches
+'
+
+test_perf 'ahead-behind counts: git tag' '
+       xargs git tag -l --format="%(ahead-behind:HEAD)" <tags
+'
+
+test_perf 'contains: git for-each-ref --merged' '
+       git for-each-ref --merged=HEAD --stdin <refs
+'
+
+test_perf 'contains: git branch --merged' '
+       xargs git branch --merged=HEAD <branches
+'
+
+test_perf 'contains: git tag --merged' '
+       xargs git tag --merged=HEAD <tags
+'
+
+test_done
index fce8151d41cbbd9eadd098b26c864d61b5b74bcc..39e92b0841437bee9cbd939de6e216fae81a7ff2 100755 (executable)
@@ -43,6 +43,7 @@ test_expect_success 'setup repo and indexes' '
        done &&
 
        git sparse-checkout init --cone &&
+       git tag -a v1.0 -m "Final" &&
        git sparse-checkout set $SPARSE_CONE &&
        git checkout -b wide $OLD_COMMIT &&
 
@@ -124,5 +125,15 @@ test_perf_on_all git read-tree -mu HEAD
 test_perf_on_all git checkout-index -f --all
 test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
 test_perf_on_all "git rm -f $SPARSE_CONE/a && git checkout HEAD -- $SPARSE_CONE/a"
+test_perf_on_all git grep --cached bogus -- "f2/f1/f1/*"
+test_perf_on_all git write-tree
+test_perf_on_all git describe --dirty
+test_perf_on_all 'echo >>new && git describe --dirty'
+test_perf_on_all git diff-files
+test_perf_on_all git diff-files -- $SPARSE_CONE/a
+test_perf_on_all git diff-tree HEAD
+test_perf_on_all git diff-tree HEAD -- $SPARSE_CONE/a
+test_perf_on_all "git worktree add ../temp && git worktree remove ../temp"
+test_perf_on_all git check-attr -a -- $SPARSE_CONE/a
 
 test_done
index 0684b690af020442847d2ef94162f311e20abc8a..ceec60656b59119c2bb82651f8ae6057899f41c5 100755 (executable)
@@ -12,8 +12,7 @@ test_lookup_pack_bitmap () {
        test_perf_large_repo
 
        test_expect_success 'setup bitmap config' '
-               git config pack.writebitmaps true &&
-               git config pack.writeReverseIndex true
+               git config pack.writebitmaps true
        '
 
        # we need to create the tag up front such that it is covered by the repack and
diff --git a/t/perf/p5332-multi-pack-reuse.sh b/t/perf/p5332-multi-pack-reuse.sh
new file mode 100755 (executable)
index 0000000..5c6c575
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='tests pack performance with multi-pack reuse'
+
+. ./perf-lib.sh
+. "${TEST_DIRECTORY}/perf/lib-pack.sh"
+
+packdir=.git/objects/pack
+
+test_perf_large_repo
+
+find_pack () {
+       for idx in $packdir/pack-*.idx
+       do
+               if git show-index <$idx | grep -q "$1"
+               then
+                       basename $idx
+               fi || return 1
+       done
+}
+
+repack_into_n_chunks () {
+       git repack -adk &&
+
+       test "$1" -eq 1 && return ||
+
+       find $packdir -type f | sort >packs.before &&
+
+       # partition the repository into $1 chunks of consecutive commits, and
+       # then create $1 packs with the objects reachable from each chunk
+       # (excluding any objects reachable from the previous chunks)
+       sz="$(($(git rev-list --count --all) / $1))"
+       for rev in $(git rev-list --all | awk "NR % $sz == 0" | tac)
+       do
+               pack="$(echo "$rev" | git pack-objects --revs \
+                       --honor-pack-keep --delta-base-offset $packdir/pack)" &&
+               touch $packdir/pack-$pack.keep || return 1
+       done
+
+       # grab any remaining objects not packed by the previous step(s)
+       git pack-objects --revs --all --honor-pack-keep --delta-base-offset \
+               $packdir/pack &&
+
+       find $packdir -type f | sort >packs.after &&
+
+       # and install the whole thing
+       for f in $(comm -12 packs.before packs.after)
+       do
+               rm -f "$f" || return 1
+       done
+       rm -fr $packdir/*.keep
+}
+
+for nr_packs in 1 10 100
+do
+       test_expect_success "create $nr_packs-pack scenario" '
+               repack_into_n_chunks $nr_packs
+       '
+
+       test_expect_success "setup bitmaps for $nr_packs-pack scenario" '
+               find $packdir -type f -name "*.idx" | sed -e "s/.*\/\(.*\)$/+\1/g" |
+               git multi-pack-index write --stdin-packs --bitmap \
+                       --preferred-pack="$(find_pack $(git rev-parse HEAD))"
+       '
+
+       for reuse in single multi
+       do
+               test_perf "clone for $nr_packs-pack scenario ($reuse-pack reuse)" "
+                       git for-each-ref --format='%(objectname)' refs/heads refs/tags >in &&
+                       git -c pack.allowPackReuse=$reuse pack-objects \
+                               --revs --delta-base-offset --use-bitmap-index \
+                               --stdout <in >result
+               "
+
+               test_size "clone size for $nr_packs-pack scenario ($reuse-pack reuse)" '
+                       wc -c <result
+               '
+       done
+done
+
+test_done
diff --git a/t/perf/p6300-for-each-ref.sh b/t/perf/p6300-for-each-ref.sh
new file mode 100755 (executable)
index 0000000..fa7289c
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='performance of for-each-ref'
+. ./perf-lib.sh
+
+test_perf_fresh_repo
+
+ref_count_per_type=10000
+test_iteration_count=10
+
+test_expect_success "setup" '
+       test_commit_bulk $(( 1 + $ref_count_per_type )) &&
+
+       # Create refs
+       test_seq $ref_count_per_type |
+               sed "s,.*,update refs/heads/branch_& HEAD~&\nupdate refs/custom/special_& HEAD~&," |
+               git update-ref --stdin &&
+
+       # Create annotated tags
+       for i in $(test_seq $ref_count_per_type)
+       do
+               # Base tags
+               echo "tag tag_$i" &&
+               echo "mark :$i" &&
+               echo "from HEAD~$i" &&
+               printf "tagger %s <%s> %s\n" \
+                       "$GIT_COMMITTER_NAME" \
+                       "$GIT_COMMITTER_EMAIL" \
+                       "$GIT_COMMITTER_DATE" &&
+               echo "data <<EOF" &&
+               echo "tag $i" &&
+               echo "EOF" &&
+
+               # Nested tags
+               echo "tag nested_$i" &&
+               echo "from :$i" &&
+               printf "tagger %s <%s> %s\n" \
+                       "$GIT_COMMITTER_NAME" \
+                       "$GIT_COMMITTER_EMAIL" \
+                       "$GIT_COMMITTER_DATE" &&
+               echo "data <<EOF" &&
+               echo "nested tag $i" &&
+               echo "EOF" || return 1
+       done | git fast-import
+'
+
+test_for_each_ref () {
+       title="for-each-ref"
+       if test $# -gt 0; then
+               title="$title ($1)"
+               shift
+       fi
+       args="$@"
+
+       test_perf "$title" "
+               for i in \$(test_seq $test_iteration_count); do
+                       git for-each-ref $args >/dev/null
+               done
+       "
+}
+
+run_tests () {
+       test_for_each_ref "$1"
+       test_for_each_ref "$1, no sort" --no-sort
+       test_for_each_ref "$1, --count=1" --count=1
+       test_for_each_ref "$1, --count=1, no sort" --no-sort --count=1
+       test_for_each_ref "$1, tags" refs/tags/
+       test_for_each_ref "$1, tags, no sort" --no-sort refs/tags/
+       test_for_each_ref "$1, tags, dereferenced" '--format="%(refname) %(objectname) %(*objectname)"' refs/tags/
+       test_for_each_ref "$1, tags, dereferenced, no sort" --no-sort '--format="%(refname) %(objectname) %(*objectname)"' refs/tags/
+
+       test_perf "for-each-ref ($1, tags) + cat-file --batch-check (dereferenced)" "
+               for i in \$(test_seq $test_iteration_count); do
+                       git for-each-ref --format='%(objectname)^{} %(refname) %(objectname)' refs/tags/ | \
+                               git cat-file --batch-check='%(objectname) %(rest)' >/dev/null
+               done
+       "
+}
+
+run_tests "loose"
+
+test_expect_success 'pack refs' '
+       git pack-refs --all
+'
+run_tests "packed"
+
+test_done
diff --git a/t/perf/p7102-reset.sh b/t/perf/p7102-reset.sh
new file mode 100755 (executable)
index 0000000..9b039e8
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description='performance of reset'
+. ./perf-lib.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+test_perf 'reset --hard with change in tree' '
+       base=$(git rev-parse HEAD) &&
+       test_commit --no-tag A &&
+       new=$(git rev-parse HEAD) &&
+
+       for i in $(test_seq 10)
+       do
+               git reset --hard $new &&
+               git reset --hard $base || return $?
+       done
+'
+
+test_done
diff --git a/t/perf/p7822-grep-perl-character.sh b/t/perf/p7822-grep-perl-character.sh
new file mode 100755 (executable)
index 0000000..87009c6
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description="git-grep's perl regex
+
+If GIT_PERF_GREP_THREADS is set to a list of threads (e.g. '1 4 8'
+etc.) we will test the patterns under those numbers of threads.
+"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+if test -n "$GIT_PERF_GREP_THREADS"
+then
+       test_set_prereq PERF_GREP_ENGINES_THREADS
+fi
+
+for pattern in \
+       '\\bhow' \
+       '\\bÆvar' \
+       '\\d+ \\bÆvar' \
+       '\\bBelón\\b' \
+       '\\w{12}\\b'
+do
+       echo '$pattern' >pat
+       if ! test_have_prereq PERF_GREP_ENGINES_THREADS
+       then
+               test_perf "grep -P '$pattern'" --prereq PCRE "
+                       git -P grep -f pat || :
+               "
+       else
+               for threads in $GIT_PERF_GREP_THREADS
+               do
+                       test_perf "grep -P '$pattern' with $threads threads" --prereq PTHREADS,PCRE "
+                               git -c grep.threads=$threads -P grep -f pat || :
+                       "
+               done
+       fi
+done
+
+test_done
index e7786775a9016151e063335047256bfea93d2f63..ab0c7634119282e6b2914f3ea50064b4daa92574 100644 (file)
@@ -15,7 +15,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # These variables must be set before the inclusion of test-lib.sh below,
 # because it will change our working directory.
@@ -31,7 +31,7 @@ unset GIT_CONFIG_NOSYSTEM
 GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config"
 export GIT_CONFIG_SYSTEM
 
-if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
+if test -n "$GIT_TEST_INSTALLED" && test -z "$PERF_SET_GIT_TEST_INSTALLED"
 then
        error "Do not use GIT_TEST_INSTALLED with the perf tests.
 
index 33da4d2aba2587b4591b0cd1c3785e6ff8119996..486ead21980ec27de640745fe2b67fe869a08b4c 100755 (executable)
@@ -91,10 +91,10 @@ set_git_test_installed () {
 run_dirs_helper () {
        mydir=${1%/}
        shift
-       while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+       while test $# -gt 0 && test "$1" != -- && test ! -f "$1"; do
                shift
        done
-       if test $# -gt 0 -a "$1" = --; then
+       if test $# -gt 0 && test "$1" = --; then
                shift
        fi
 
@@ -124,7 +124,7 @@ run_dirs_helper () {
 }
 
 run_dirs () {
-       while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+       while test $# -gt 0 && test "$1" != -- && test ! -f "$1"; do
                run_dirs_helper "$@"
                shift
        done
@@ -180,7 +180,8 @@ run_subsection () {
        GIT_PERF_AGGREGATING_LATER=t
        export GIT_PERF_AGGREGATING_LATER
 
-       if test $# = 0 -o "$1" = -- -o -f "$1"; then
+       if test $# = 0 || test "$1" = -- || test -f "$1"
+       then
                set -- . "$@"
        fi
 
@@ -232,10 +233,10 @@ then
        )
 elif test -n "$GIT_PERF_SUBSECTION"
 then
-       egrep "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names >/dev/null ||
+       grep -E "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names >/dev/null ||
                die "subsection '$GIT_PERF_SUBSECTION' not found in '$GIT_PERF_CONFIG_FILE'"
 
-       egrep "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names | while read -r subsec
+       grep -E "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names | while read -r subsec
        do
                (
                        GIT_PERF_SUBSECTION="$subsec"
index 502b4bcf9ea0ad06cddaad50b999dd8024af28eb..6e300be2ac55aff51ed0e2d8ef16a978f5efedb9 100755 (executable)
@@ -815,7 +815,8 @@ test_expect_success 'test_oid provides sane info by default' '
        grep "^00*\$" actual &&
        rawsz="$(test_oid rawsz)" &&
        hexsz="$(test_oid hexsz)" &&
-       test "$hexsz" -eq $(wc -c <actual) &&
+       # +1 accounts for the trailing newline
+       test $(( $hexsz + 1)) -eq $(wc -c <actual) &&
        test $(( $rawsz * 2)) -eq "$hexsz"
 '
 
@@ -826,7 +827,7 @@ test_expect_success 'test_oid can look up data for SHA-1' '
        grep "^00*\$" actual &&
        rawsz="$(test_oid rawsz)" &&
        hexsz="$(test_oid hexsz)" &&
-       test $(wc -c <actual) -eq 40 &&
+       test $(wc -c <actual) -eq 41 &&
        test "$rawsz" -eq 20 &&
        test "$hexsz" -eq 40
 '
@@ -838,7 +839,7 @@ test_expect_success 'test_oid can look up data for SHA-256' '
        grep "^00*\$" actual &&
        rawsz="$(test_oid rawsz)" &&
        hexsz="$(test_oid hexsz)" &&
-       test $(wc -c <actual) -eq 64 &&
+       test $(wc -c <actual) -eq 65 &&
        test "$rawsz" -eq 32 &&
        test "$hexsz" -eq 64
 '
@@ -1013,7 +1014,7 @@ test_expect_success 'validate object ID for a known tree' '
 '
 
 test_expect_success 'showing tree with git ls-tree' '
-    git ls-tree $tree >current
+       git ls-tree $tree >current
 '
 
 test_expect_success 'git ls-tree output for a known tree' '
index d479303efa03df6f818496f1cdcafe390bee0126..b131d665db40e9858d4a3f9c13ab71eb77d6cc5f 100755 (executable)
@@ -168,8 +168,8 @@ test_expect_success 'reinit' '
                git -c init.defaultBranch=initial init >out1 2>err1 &&
                git init >out2 2>err2
        ) &&
-       test_i18ngrep "Initialized empty" again/out1 &&
-       test_i18ngrep "Reinitialized existing" again/out2 &&
+       test_grep "Initialized empty" again/out1 &&
+       test_grep "Reinitialized existing" again/out2 &&
        test_must_be_empty again/err1 &&
        test_must_be_empty again/err2
 '
@@ -332,7 +332,7 @@ test_expect_success 'init with separate gitdir' '
 
 test_expect_success 'explicit bare & --separate-git-dir incompatible' '
        test_must_fail git init --bare --separate-git-dir goop.git bare.git 2>err &&
-       test_i18ngrep "cannot be used together" err
+       test_grep "cannot be used together" err
 '
 
 test_expect_success 'implicit bare & --separate-git-dir incompatible' '
@@ -340,7 +340,7 @@ test_expect_success 'implicit bare & --separate-git-dir incompatible' '
        mkdir -p bare.git &&
        test_must_fail env GIT_DIR=. \
                git -C bare.git init --separate-git-dir goop.git 2>err &&
-       test_i18ngrep "incompatible" err
+       test_grep "incompatible" err
 '
 
 test_expect_success 'bare & --separate-git-dir incompatible within worktree' '
@@ -349,7 +349,7 @@ test_expect_success 'bare & --separate-git-dir incompatible within worktree' '
        git clone --bare . bare.git &&
        git -C bare.git worktree add --detach ../linkwt &&
        test_must_fail git -C linkwt init --separate-git-dir seprepo 2>err &&
-       test_i18ngrep "incompatible" err
+       test_grep "incompatible" err
 '
 
 test_lazy_prereq GETCWD_IGNORES_PERMS '
@@ -532,6 +532,76 @@ test_expect_success 'init rejects attempts to initialize with different hash' '
        test_must_fail git -C sha256 init --object-format=sha1
 '
 
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage is not allowed with repo version 0' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config extensions.refStorage files &&
+       test_must_fail git -C refstorage rev-parse 2>err &&
+       grep "repo version is 0, but v1-only extension found" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with files backend' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config core.repositoryformatversion 1 &&
+       git -C refstorage config extensions.refStorage files &&
+       test_commit -C refstorage A &&
+       git -C refstorage rev-parse --verify HEAD
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown backend' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config core.repositoryformatversion 1 &&
+       git -C refstorage config extensions.refStorage garbage &&
+       test_must_fail git -C refstorage rev-parse 2>err &&
+       grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' '
+       test_when_finished "rm -rf refformat" &&
+       GIT_DEFAULT_REF_FORMAT=files git init refformat &&
+       echo 0 >expect &&
+       git -C refformat config core.repositoryformatversion >actual &&
+       test_cmp expect actual &&
+       test_must_fail git -C refformat config extensions.refstorage
+'
+
+test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' '
+       test_when_finished "rm -rf refformat" &&
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err &&
+       test_cmp expect err
+'
+
+test_expect_success 'init with --ref-format=files' '
+       test_when_finished "rm -rf refformat" &&
+       git init --ref-format=files refformat &&
+       echo files >expect &&
+       git -C refformat rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 're-init with same format' '
+       test_when_finished "rm -rf refformat" &&
+       git init --ref-format=files refformat &&
+       git init --ref-format=files refformat &&
+       echo files >expect &&
+       git -C refformat rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'init with --ref-format=garbage' '
+       test_when_finished "rm -rf refformat" &&
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail git init --ref-format=garbage refformat 2>err &&
+       test_cmp expect err
+'
+
 test_expect_success MINGW 'core.hidedotfiles = false' '
        git config --global core.hidedotfiles false &&
        rm -rf newdir &&
@@ -563,7 +633,7 @@ test_expect_success '--initial-branch' '
 
        : re-initializing should not change the branch name &&
        git init --initial-branch=ignore initial-branch-option 2>err &&
-       test_i18ngrep "ignored --initial-branch" err &&
+       test_grep "ignored --initial-branch" err &&
        git -C initial-branch-option symbolic-ref HEAD >actual &&
        grep hello actual
 '
@@ -579,7 +649,7 @@ test_expect_success 'advice on unconfigured init.defaultBranch' '
        GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= git -c color.advice=always \
                init unconfigured-default-branch-name 2>err &&
        test_decode_color <err >decoded &&
-       test_i18ngrep "<YELLOW>hint: " decoded
+       test_grep "<YELLOW>hint: " decoded
 '
 
 test_expect_success 'overridden default main branch name (env)' '
@@ -592,15 +662,20 @@ test_expect_success 'overridden default main branch name (env)' '
 test_expect_success 'invalid default branch name' '
        test_must_fail env GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME="with space" \
                git init initial-branch-invalid 2>err &&
-       test_i18ngrep "invalid branch name" err
+       test_grep "invalid branch name" err
 '
 
 test_expect_success 'branch -m with the initial branch' '
        git init rename-initial &&
        git -C rename-initial branch -m renamed &&
-       test renamed = $(git -C rename-initial symbolic-ref --short HEAD) &&
+       echo renamed >expect &&
+       git -C rename-initial symbolic-ref --short HEAD >actual &&
+       test_cmp expect actual &&
+
        git -C rename-initial branch -m renamed again &&
-       test again = $(git -C rename-initial symbolic-ref --short HEAD)
+       echo again >expect &&
+       git -C rename-initial symbolic-ref --short HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_done
index 26eaca095a26a6a654364bdd8acf208af30b2d00..736516cc6a478ee548b79f7d9a0f2cae0b51430e 100755 (executable)
@@ -22,18 +22,20 @@ test_expect_success 'initial setup' '
 test_expect_success 'bad setup: invalid .git file format' '
        echo "gitdir $REAL" >.git &&
        test_must_fail git rev-parse 2>.err &&
-       test_i18ngrep "invalid gitfile format" .err
+       test_grep "invalid gitfile format" .err
 '
 
 test_expect_success 'bad setup: invalid .git file path' '
        echo "gitdir: $REAL.not" >.git &&
        test_must_fail git rev-parse 2>.err &&
-       test_i18ngrep "not a git repository" .err
+       test_grep "not a git repository" .err
 '
 
 test_expect_success 'final setup + check rev-parse --git-dir' '
        echo "gitdir: $REAL" >.git &&
-       test "$REAL" = "$(git rev-parse --git-dir)"
+       echo "$REAL" >expect &&
+       git rev-parse --git-dir >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'check hash-object' '
index d0284fe2d7592d52a2aa9d6bfe38e342c94ec417..774b52c2980ca10e6183cf74ad10427f00c2514b 100755 (executable)
@@ -19,13 +19,48 @@ attr_check () {
        test_must_be_empty err
 }
 
+attr_check_object_mode_basic () {
+       path="$1" &&
+       expect="$2" &&
+       check_opts="$3" &&
+       git check-attr $check_opts builtin_objectmode -- "$path" >actual 2>err &&
+       echo "$path: builtin_objectmode: $expect" >expect &&
+       test_cmp expect actual
+}
+
+attr_check_object_mode () {
+       attr_check_object_mode_basic "$@" &&
+       test_must_be_empty err
+}
+
 attr_check_quote () {
        path="$1" quoted_path="$2" expect="$3" &&
 
        git check-attr test -- "$path" >actual &&
        echo "\"$quoted_path\": test: $expect" >expect &&
        test_cmp expect actual
+}
+
+attr_check_source () {
+       path="$1" expect="$2" source="$3" git_opts="$4" &&
+
+       echo "$path: test: $expect" >expect &&
+
+       git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
+       test_cmp expect actual &&
+       test_must_be_empty err &&
+
+       git $git_opts --attr-source="$source" check-attr test -- "$path" >actual 2>err &&
+       test_cmp expect actual &&
+       test_must_be_empty err
+
+       git $git_opts -c "attr.tree=$source" check-attr test -- "$path" >actual 2>err &&
+       test_cmp expect actual &&
+       test_must_be_empty err
 
+       GIT_ATTR_SOURCE="$source" git $git_opts check-attr test -- "$path" >actual 2>err &&
+       test_cmp expect actual &&
+       test_must_be_empty err
 }
 
 test_expect_success 'open-quoted pathname' '
@@ -33,7 +68,6 @@ test_expect_success 'open-quoted pathname' '
        attr_check a unspecified
 '
 
-
 test_expect_success 'setup' '
        mkdir -p a/b/d a/c b &&
        (
@@ -80,12 +114,23 @@ test_expect_success 'setup' '
        EOF
 '
 
+test_expect_success 'setup branches' '
+       mkdir -p foo/bar &&
+       test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
+               "f test=f\na/i test=n\n" tag-1 &&
+       test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
+               "g test=g\na/i test=m\n" tag-2 &&
+       rm foo/bar/.gitattributes
+'
+
 test_expect_success 'command line checks' '
        test_must_fail git check-attr &&
        test_must_fail git check-attr -- &&
        test_must_fail git check-attr test &&
        test_must_fail git check-attr test -- &&
        test_must_fail git check-attr -- f &&
+       test_must_fail git check-attr --source &&
+       test_must_fail git check-attr --source not-a-valid-ref &&
        echo "f" | test_must_fail git check-attr --stdin &&
        echo "f" | test_must_fail git check-attr --stdin -- f &&
        echo "f" | test_must_fail git check-attr --stdin test -- f &&
@@ -203,9 +248,12 @@ test_expect_success 'attribute test: read paths from stdin' '
        test_cmp expect actual
 '
 
-test_expect_success 'attribute test: --all option' '
+test_expect_success 'setup --all option' '
        grep -v unspecified <expect-all | sort >specified-all &&
-       sed -e "s/:.*//" <expect-all | uniq >stdin-all &&
+       sed -e "s/:.*//" <expect-all | uniq >stdin-all
+'
+
+test_expect_success 'attribute test: --all option' '
        git check-attr --stdin --all <stdin-all >tmp &&
        sort tmp >actual &&
        test_cmp specified-all actual
@@ -229,7 +277,7 @@ test_expect_success 'root subdir attribute test' '
 test_expect_success 'negative patterns' '
        echo "!f test=bar" >.gitattributes &&
        git check-attr test -- '"'"'!f'"'"' 2>errors &&
-       test_i18ngrep "Negative patterns are ignored" errors
+       test_grep "Negative patterns are ignored" errors
 '
 
 test_expect_success 'patterns starting with exclamation' '
@@ -284,6 +332,15 @@ test_expect_success 'using --git-dir and --work-tree' '
        )
 '
 
+test_expect_success 'using --source' '
+       attr_check_source foo/bar/f f tag-1 &&
+       attr_check_source foo/bar/a/i n tag-1 &&
+       attr_check_source foo/bar/f unspecified tag-2 &&
+       attr_check_source foo/bar/a/i m tag-2 &&
+       attr_check_source foo/bar/g g tag-2 &&
+       attr_check_source foo/bar/g unspecified tag-1
+'
+
 test_expect_success 'setup bare' '
        git clone --template= --bare . bare.git
 '
@@ -303,6 +360,86 @@ test_expect_success 'bare repository: check that .gitattribute is ignored' '
        )
 '
 
+bad_attr_source_err="fatal: bad --attr-source or GIT_ATTR_SOURCE"
+
+test_expect_success '--attr-source is bad' '
+       test_when_finished rm -rf empty &&
+       git init empty &&
+       (
+               cd empty &&
+               echo "$bad_attr_source_err" >expect_err &&
+               test_must_fail git --attr-source=HEAD check-attr test -- f/path 2>err &&
+               test_cmp expect_err err
+       )
+'
+
+test_expect_success 'attr.tree when HEAD is unborn' '
+       test_when_finished rm -rf empty &&
+       git init empty &&
+       (
+               cd empty &&
+               echo "f/path: test: unspecified" >expect &&
+               git -c attr.tree=HEAD check-attr test -- f/path >actual 2>err &&
+               test_must_be_empty err &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'bad attr source defaults to reading .gitattributes file' '
+       test_when_finished rm -rf empty &&
+       git init empty &&
+       (
+               cd empty &&
+               echo "f/path test=val" >.gitattributes &&
+               echo "f/path: test: val" >expect &&
+               git -c attr.tree=HEAD check-attr test -- f/path >actual 2>err &&
+               test_must_be_empty err &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'bare repo defaults to reading .gitattributes from HEAD' '
+       test_when_finished rm -rf test bare_with_gitattribute &&
+       git init test &&
+       test_commit -C test gitattributes .gitattributes "f/path test=val" &&
+       git clone --bare test bare_with_gitattribute &&
+       echo "f/path: test: val" >expect &&
+       git -C bare_with_gitattribute check-attr test -- f/path >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'precedence of --attr-source, GIT_ATTR_SOURCE, then attr.tree' '
+       test_when_finished rm -rf empty &&
+       git init empty &&
+       (
+               cd empty &&
+               git checkout -b attr-source &&
+               test_commit "val1" .gitattributes "f/path test=val1" &&
+               git checkout -b attr-tree &&
+               test_commit "val2" .gitattributes "f/path test=val2" &&
+               git checkout attr-source &&
+               echo "f/path: test: val1" >expect &&
+               GIT_ATTR_SOURCE=attr-source git -c attr.tree=attr-tree --attr-source=attr-source \
+               check-attr test -- f/path >actual &&
+               test_cmp expect actual &&
+               GIT_ATTR_SOURCE=attr-source git -c attr.tree=attr-tree \
+               check-attr test -- f/path >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'bare repository: with --source' '
+       (
+               cd bare.git &&
+               attr_check_source foo/bar/f f tag-1 &&
+               attr_check_source foo/bar/a/i n tag-1 &&
+               attr_check_source foo/bar/f unspecified tag-2 &&
+               attr_check_source foo/bar/a/i m tag-2 &&
+               attr_check_source foo/bar/g g tag-2 &&
+               attr_check_source foo/bar/g unspecified tag-1
+       )
+'
+
 test_expect_success 'bare repository: check that --cached honors index' '
        (
                cd bare.git &&
@@ -373,7 +510,7 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
        mkdir subdir &&
        ln -s ../attr subdir/.gitattributes &&
        attr_check_basic subdir/file unspecified &&
-       test_i18ngrep "unable to access.*gitattributes" err
+       test_grep "unable to access.*gitattributes" err
 '
 
 test_expect_success 'large attributes line ignored in tree' '
@@ -400,7 +537,7 @@ test_expect_success 'large attributes line ignores trailing content in tree' '
 
 test_expect_success EXPENSIVE 'large attributes file ignored in tree' '
        test_when_finished "rm .gitattributes" &&
-       dd if=/dev/zero of=.gitattributes bs=101M count=1 2>/dev/null &&
+       dd if=/dev/zero of=.gitattributes bs=1048576 count=101 2>/dev/null &&
        git check-attr --all path >/dev/null 2>err &&
        echo "warning: ignoring overly large gitattributes file ${SQ}.gitattributes${SQ}" >expect &&
        test_cmp expect err
@@ -428,11 +565,73 @@ test_expect_success 'large attributes line ignores trailing content in index' '
 
 test_expect_success EXPENSIVE 'large attributes file ignored in index' '
        test_when_finished "git update-index --remove .gitattributes" &&
-       blob=$(dd if=/dev/zero bs=101M count=1 2>/dev/null | git hash-object -w --stdin) &&
+       blob=$(dd if=/dev/zero bs=1048576 count=101 2>/dev/null | git hash-object -w --stdin) &&
        git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
        git check-attr --cached --all path >/dev/null 2>err &&
        echo "warning: ignoring overly large gitattributes blob ${SQ}.gitattributes${SQ}" >expect &&
        test_cmp expect err
 '
 
+test_expect_success 'builtin object mode attributes work (dir and regular paths)' '
+       >normal &&
+       attr_check_object_mode normal 100644 &&
+       mkdir dir &&
+       attr_check_object_mode dir 040000
+'
+
+test_expect_success POSIXPERM 'builtin object mode attributes work (executable)' '
+       >exec &&
+       chmod +x exec &&
+       attr_check_object_mode exec 100755
+'
+
+test_expect_success SYMLINKS 'builtin object mode attributes work (symlinks)' '
+       ln -s to_sym sym &&
+       attr_check_object_mode sym 120000
+'
+
+test_expect_success 'native object mode attributes work with --cached' '
+       >normal &&
+       git add normal &&
+       empty_blob=$(git rev-parse :normal) &&
+       git update-index --index-info <<-EOF &&
+       100755 $empty_blob 0    exec
+       120000 $empty_blob 0    symlink
+       EOF
+       attr_check_object_mode normal 100644 --cached &&
+       attr_check_object_mode exec 100755 --cached &&
+       attr_check_object_mode symlink 120000 --cached
+'
+
+test_expect_success 'check object mode attributes work for submodules' '
+       mkdir sub &&
+       (
+               cd sub &&
+               git init &&
+               mv .git .real &&
+               echo "gitdir: .real" >.git &&
+               test_commit first
+       ) &&
+       attr_check_object_mode sub 160000 &&
+       attr_check_object_mode sub unspecified --cached &&
+       git add sub &&
+       attr_check_object_mode sub 160000 --cached
+'
+
+test_expect_success 'we do not allow user defined builtin_* attributes' '
+       echo "foo* builtin_foo" >.gitattributes &&
+       git add .gitattributes 2>actual &&
+       echo "builtin_foo is not a valid attribute name: .gitattributes:1" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'user defined builtin_objectmode values are ignored' '
+       echo "foo* builtin_objectmode=12345" >.gitattributes &&
+       git add .gitattributes &&
+       >foo_1 &&
+       attr_check_object_mode_basic foo_1 100644 &&
+       echo "builtin_objectmode is not a valid attribute name: .gitattributes:1" >expect &&
+       test_cmp expect err
+'
+
 test_done
index 2490162071e700e8a69d0e6311b6f22eda4e7046..e18b1602864ec432553266e89d3796e94741e8e0 100755 (executable)
@@ -88,6 +88,13 @@ check_parse 2008-02-14 bad
 check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
 check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
 check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '20080214T20:30:45' '2008-02-14 20:30:45 +0000'
+check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'
+check_parse '20080214T20' '2008-02-14 20:00:00 +0000'
+check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'
+check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'
+check_parse '20080214T000000.20' '2008-02-14 00:00:00 +0000'
+check_parse '20080214T00:00:00.20' '2008-02-14 00:00:00 +0000'
 check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'
 check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'
 check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'
@@ -99,6 +106,7 @@ check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500'
 check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'
 check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'
 check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
+check_parse 'Thu, 7 Apr 2005 15:14:13 -0700' '2005-04-07 15:14:13 -0700'
 
 check_approxidate() {
        echo "$1 -> $2 +0000" >expect
index e56f4b9ac59572288f69cbd3554ad27df736c6b4..ff4fd9348cca6470f4dbacbc4c9a3d775f15c06d 100755 (executable)
@@ -5,6 +5,12 @@ test_description='basic sanity checks for git var'
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
+sane_unset_all_editors () {
+       sane_unset GIT_EDITOR &&
+       sane_unset VISUAL &&
+       sane_unset EDITOR
+}
+
 test_expect_success 'get GIT_AUTHOR_IDENT' '
        test_tick &&
        echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
@@ -47,6 +53,178 @@ test_expect_success 'get GIT_DEFAULT_BRANCH with configuration' '
        )
 '
 
+test_expect_success 'get GIT_EDITOR without configuration' '
+       (
+               sane_unset_all_editors &&
+               test_expect_code 1 git var GIT_EDITOR >out &&
+               test_must_be_empty out
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration' '
+       test_config core.editor foo &&
+       (
+               sane_unset_all_editors &&
+               echo foo >expect &&
+               git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with environment variable GIT_EDITOR' '
+       (
+               sane_unset_all_editors &&
+               echo bar >expect &&
+               GIT_EDITOR=bar git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with environment variable EDITOR' '
+       (
+               sane_unset_all_editors &&
+               echo bar >expect &&
+               EDITOR=bar git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration and environment variable GIT_EDITOR' '
+       test_config core.editor foo &&
+       (
+               sane_unset_all_editors &&
+               echo bar >expect &&
+               GIT_EDITOR=bar git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration and environment variable EDITOR' '
+       test_config core.editor foo &&
+       (
+               sane_unset_all_editors &&
+               echo foo >expect &&
+               EDITOR=bar git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR without configuration' '
+       (
+               sane_unset GIT_SEQUENCE_EDITOR &&
+               git var GIT_EDITOR >expect &&
+               git var GIT_SEQUENCE_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration' '
+       test_config sequence.editor foo &&
+       (
+               sane_unset GIT_SEQUENCE_EDITOR &&
+               echo foo >expect &&
+               git var GIT_SEQUENCE_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with environment variable' '
+       (
+               sane_unset GIT_SEQUENCE_EDITOR &&
+               echo bar >expect &&
+               GIT_SEQUENCE_EDITOR=bar git var GIT_SEQUENCE_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration and environment variable' '
+       test_config sequence.editor foo &&
+       (
+               sane_unset GIT_SEQUENCE_EDITOR &&
+               echo bar >expect &&
+               GIT_SEQUENCE_EDITOR=bar git var GIT_SEQUENCE_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success POSIXPERM 'GIT_SHELL_PATH points to a valid executable' '
+       shellpath=$(git var GIT_SHELL_PATH) &&
+       test_path_is_executable "$shellpath"
+'
+
+# We know in this environment that our shell will be one of a few fixed values
+# that all end in "sh".
+test_expect_success MINGW 'GIT_SHELL_PATH points to a suitable shell' '
+       shellpath=$(git var GIT_SHELL_PATH) &&
+       case "$shellpath" in
+       *sh) ;;
+       *) return 1;;
+       esac
+'
+
+test_expect_success 'GIT_ATTR_SYSTEM produces expected output' '
+       test_must_fail env GIT_ATTR_NOSYSTEM=1 git var GIT_ATTR_SYSTEM &&
+       (
+               sane_unset GIT_ATTR_NOSYSTEM &&
+               systempath=$(git var GIT_ATTR_SYSTEM) &&
+               test "$systempath" != ""
+       )
+'
+
+test_expect_success 'GIT_ATTR_GLOBAL points to the correct location' '
+       TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+       globalpath=$(XDG_CONFIG_HOME="$TRASHDIR/.config" git var GIT_ATTR_GLOBAL) &&
+       test "$globalpath" = "$TRASHDIR/.config/git/attributes" &&
+       (
+               sane_unset XDG_CONFIG_HOME &&
+               globalpath=$(HOME="$TRASHDIR" git var GIT_ATTR_GLOBAL) &&
+               test "$globalpath" = "$TRASHDIR/.config/git/attributes"
+       )
+'
+
+test_expect_success 'GIT_CONFIG_SYSTEM points to the correct location' '
+       TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+       test_must_fail env GIT_CONFIG_NOSYSTEM=1 git var GIT_CONFIG_SYSTEM &&
+       (
+               sane_unset GIT_CONFIG_NOSYSTEM &&
+               systempath=$(git var GIT_CONFIG_SYSTEM) &&
+               test "$systempath" != "" &&
+               systempath=$(GIT_CONFIG_SYSTEM=/dev/null git var GIT_CONFIG_SYSTEM) &&
+               if test_have_prereq MINGW
+               then
+                       test "$systempath" = "nul"
+               else
+                       test "$systempath" = "/dev/null"
+               fi &&
+               systempath=$(GIT_CONFIG_SYSTEM="$TRASHDIR/gitconfig" git var GIT_CONFIG_SYSTEM) &&
+               test "$systempath" = "$TRASHDIR/gitconfig"
+       )
+'
+
+test_expect_success 'GIT_CONFIG_GLOBAL points to the correct location' '
+       TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+       HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var GIT_CONFIG_GLOBAL >actual &&
+       echo "$TRASHDIR/foo/git/config" >expected &&
+       echo "$TRASHDIR/.gitconfig" >>expected &&
+       test_cmp expected actual &&
+       (
+               sane_unset XDG_CONFIG_HOME &&
+               HOME="$TRASHDIR" git var GIT_CONFIG_GLOBAL >actual &&
+               echo "$TRASHDIR/.config/git/config" >expected &&
+               echo "$TRASHDIR/.gitconfig" >>expected &&
+               test_cmp expected actual &&
+               globalpath=$(GIT_CONFIG_GLOBAL=/dev/null git var GIT_CONFIG_GLOBAL) &&
+               if test_have_prereq MINGW
+               then
+                       test "$globalpath" = "nul"
+               else
+                       test "$globalpath" = "/dev/null"
+               fi &&
+               globalpath=$(GIT_CONFIG_GLOBAL="$TRASHDIR/gitconfig" git var GIT_CONFIG_GLOBAL) &&
+               test "$globalpath" = "$TRASHDIR/gitconfig"
+       )
+'
+
 # For git var -l, we check only a representative variable;
 # testing the whole output would make our test too brittle with
 # respect to unrelated changes in the test suite's environment.
@@ -64,8 +242,39 @@ test_expect_success 'git var -l lists config' '
        test_cmp expect actual.bare
 '
 
+test_expect_success 'git var -l lists multiple global configs' '
+       TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+       HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var -l >actual &&
+       grep "^GIT_CONFIG_GLOBAL=" actual >filtered &&
+       echo "GIT_CONFIG_GLOBAL=$TRASHDIR/foo/git/config" >expected &&
+       echo "GIT_CONFIG_GLOBAL=$TRASHDIR/.gitconfig" >>expected &&
+       test_cmp expected filtered
+'
+
+test_expect_success 'git var -l does not split multiline editors' '
+       (
+               GIT_EDITOR="!f() {
+                       echo Hello!
+               }; f" &&
+               export GIT_EDITOR &&
+               echo "GIT_EDITOR=$GIT_EDITOR" >expected &&
+               git var -l >var &&
+               sed -n -e "/^GIT_EDITOR/,\$p" var | head -n 3 >actual &&
+               test_cmp expected actual
+       )
+'
+
 test_expect_success 'listing and asking for variables are exclusive' '
        test_must_fail git var -l GIT_COMMITTER_IDENT
 '
 
+test_expect_success '`git var -l` works even without HOME' '
+       (
+               XDG_CONFIG_HOME= &&
+               export XDG_CONFIG_HOME &&
+               unset HOME &&
+               git var -l
+       )
+'
+
 test_done
index c70d11bc914d09df5baa8cf74e9a2c292fc861b8..361446b2f4c9ed39cc5d1b9c301751420c0a670d 100755 (executable)
@@ -49,7 +49,7 @@ broken_c_unquote_verbose () {
 
 stderr_contains () {
        regexp="$1"
-       if test_i18ngrep "$regexp" "$HOME/stderr"
+       if test_grep "$regexp" "$HOME/stderr"
        then
                return 0
        else
@@ -942,7 +942,7 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
        ln -s ignore subdir/.gitignore &&
        test_must_fail git check-ignore subdir/file >actual 2>err &&
        test_must_be_empty actual &&
-       test_i18ngrep "unable to access.*gitignore" err
+       test_grep "unable to access.*gitignore" err
 '
 
 test_done
index dbfc5c826764bca4ec5561b603d8016f7d50f20d..1d273d91c2125ae2c16ae1a14c628cf23fcecc06 100755 (executable)
@@ -100,17 +100,17 @@ test_expect_success "--help does not work for guides" "
 
 test_expect_success 'git help' '
        git help >help.output &&
-       test_i18ngrep "^   clone  " help.output &&
-       test_i18ngrep "^   add    " help.output &&
-       test_i18ngrep "^   log    " help.output &&
-       test_i18ngrep "^   commit " help.output &&
-       test_i18ngrep "^   fetch  " help.output
+       test_grep "^   clone  " help.output &&
+       test_grep "^   add    " help.output &&
+       test_grep "^   log    " help.output &&
+       test_grep "^   commit " help.output &&
+       test_grep "^   fetch  " help.output
 '
 
 test_expect_success 'git help -g' '
        git help -g >help.output &&
-       test_i18ngrep "^   everyday   " help.output &&
-       test_i18ngrep "^   tutorial   " help.output
+       test_grep "^   everyday   " help.output &&
+       test_grep "^   tutorial   " help.output
 '
 
 test_expect_success 'git help fails for non-existing html pages' '
@@ -257,7 +257,7 @@ do
                        export GIT_CEILING_DIRECTORIES &&
                        test_expect_code 129 git -C sub $builtin -h >output 2>&1
                ) &&
-               test_i18ngrep usage output
+               test_grep usage output
        '
 done <builtins
 
index 9ad76080aa49d46d03e2bb805d039e6a88d87e5c..08814173cb111c941c59e2fa764ac271fcbe9dab 100755 (executable)
@@ -6,15 +6,17 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 TEST_DATA="$TEST_DIRECTORY/t0013"
 
-if test -z "$DC_SHA1"
+test_lazy_prereq SHA1_IS_SHA1DC 'test-tool sha1-is-sha1dc'
+
+if ! test_have_prereq SHA1_IS_SHA1DC
 then
-       skip_all='skipping sha1 collision tests, DC_SHA1 not set'
+       skip_all='skipping sha1 collision tests, not using sha1collisiondetection'
        test_done
 fi
 
 test_expect_success 'test-sha1 detects shattered pdf' '
        test_must_fail test-tool sha1 <"$TEST_DATA/shattered-1.pdf" 2>err &&
-       test_i18ngrep collision err &&
+       test_grep collision err &&
        grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err
 '
 
index 8d3d9144c06d6129b91d3513c1ec605a3fb4da73..95568342be35f3208d16706ed674d8db5e0932ed 100755 (executable)
@@ -8,7 +8,7 @@ test_expect_success 'nested aliases - internal execution' '
        git config alias.nested-internal-1 nested-internal-2 &&
        git config alias.nested-internal-2 status &&
        git nested-internal-1 >output &&
-       test_i18ngrep "^On branch " output
+       test_grep "^On branch " output
 '
 
 test_expect_success 'nested aliases - mixed execution' '
@@ -16,7 +16,7 @@ test_expect_success 'nested aliases - mixed execution' '
        git config alias.nested-external-2 "!git nested-external-3" &&
        git config alias.nested-external-3 status &&
        git nested-external-1 >output &&
-       test_i18ngrep "^On branch " output
+       test_grep "^On branch " output
 '
 
 test_expect_success 'looping aliases - internal execution' '
@@ -24,7 +24,7 @@ test_expect_success 'looping aliases - internal execution' '
        git config alias.loop-internal-2 loop-internal-3 &&
        git config alias.loop-internal-3 loop-internal-2 &&
        test_must_fail git loop-internal-1 2>output &&
-       test_i18ngrep "^fatal: alias loop detected: expansion of" output
+       test_grep "^fatal: alias loop detected: expansion of" output
 '
 
 # This test is disabled until external loops are fixed, because would block
@@ -34,7 +34,7 @@ test_expect_success 'looping aliases - internal execution' '
 #      git config alias.loop-mixed-1 loop-mixed-2 &&
 #      git config alias.loop-mixed-2 "!git loop-mixed-1" &&
 #      test_must_fail git loop-mixed-1 2>output &&
-#      test_i18ngrep "^fatal: alias loop detected: expansion of" output
+#      test_grep "^fatal: alias loop detected: expansion of" output
 #'
 
 test_expect_success 'run-command formats empty args properly' '
index 2e42fba9567d5b35bf72eda214d336a7c3564566..fc14ba091cb247e5fc7395b0b89f615e43193c85 100755 (executable)
@@ -1,87 +1,87 @@
 #!/bin/sh
 
-test_description='test env--helper'
+test_description='test test-tool env-helper'
 
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
-test_expect_success 'env--helper usage' '
-       test_must_fail git env--helper &&
-       test_must_fail git env--helper --type=bool &&
-       test_must_fail git env--helper --type=ulong &&
-       test_must_fail git env--helper --type=bool &&
-       test_must_fail git env--helper --type=bool --default &&
-       test_must_fail git env--helper --type=bool --default= &&
-       test_must_fail git env--helper --defaultxyz
+test_expect_success 'test-tool env-helper usage' '
+       test_must_fail test-tool env-helper &&
+       test_must_fail test-tool env-helper --type=bool &&
+       test_must_fail test-tool env-helper --type=ulong &&
+       test_must_fail test-tool env-helper --type=bool &&
+       test_must_fail test-tool env-helper --type=bool --default &&
+       test_must_fail test-tool env-helper --type=bool --default= &&
+       test_must_fail test-tool env-helper --defaultxyz
 '
 
-test_expect_success 'env--helper bad default values' '
-       test_must_fail git env--helper --type=bool --default=1xyz MISSING &&
-       test_must_fail git env--helper --type=ulong --default=1xyz MISSING
+test_expect_success 'test-tool env-helper bad default values' '
+       test_must_fail test-tool env-helper --type=bool --default=1xyz MISSING &&
+       test_must_fail test-tool env-helper --type=ulong --default=1xyz MISSING
 '
 
-test_expect_success 'env--helper --type=bool' '
+test_expect_success 'test-tool env-helper --type=bool' '
        # Test various --default bool values
        echo true >expected &&
-       git env--helper --type=bool --default=1 MISSING >actual &&
+       test-tool env-helper --type=bool --default=1 MISSING >actual &&
        test_cmp expected actual &&
-       git env--helper --type=bool --default=yes MISSING >actual &&
+       test-tool env-helper --type=bool --default=yes MISSING >actual &&
        test_cmp expected actual &&
-       git env--helper --type=bool --default=true MISSING >actual &&
+       test-tool env-helper --type=bool --default=true MISSING >actual &&
        test_cmp expected actual &&
        echo false >expected &&
-       test_must_fail git env--helper --type=bool --default=0 MISSING >actual &&
+       test_must_fail test-tool env-helper --type=bool --default=0 MISSING >actual &&
        test_cmp expected actual &&
-       test_must_fail git env--helper --type=bool --default=no MISSING >actual &&
+       test_must_fail test-tool env-helper --type=bool --default=no MISSING >actual &&
        test_cmp expected actual &&
-       test_must_fail git env--helper --type=bool --default=false MISSING >actual &&
+       test_must_fail test-tool env-helper --type=bool --default=false MISSING >actual &&
        test_cmp expected actual &&
 
        # No output with --exit-code
-       git env--helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
+       test-tool env-helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
-       test_must_fail git env--helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
+       test_must_fail test-tool env-helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
 
        # Existing variable
-       EXISTS=true git env--helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
+       EXISTS=true test-tool env-helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
        test_must_fail \
                env EXISTS=false \
-               git env--helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
+               test-tool env-helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err
 '
 
-test_expect_success 'env--helper --type=ulong' '
+test_expect_success 'test-tool env-helper --type=ulong' '
        echo 1234567890 >expected &&
-       git env--helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
+       test-tool env-helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
        test_cmp expected actual.out &&
        test_must_be_empty actual.err &&
 
        echo 0 >expected &&
-       test_must_fail git env--helper --type=ulong --default=0 MISSING >actual &&
+       test_must_fail test-tool env-helper --type=ulong --default=0 MISSING >actual &&
        test_cmp expected actual &&
 
-       git env--helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
+       test-tool env-helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
 
-       EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
+       EXISTS=1234567890 test-tool env-helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
 
        echo 1234567890 >expected &&
-       EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
+       EXISTS=1234567890 test-tool env-helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
        test_cmp expected actual.out &&
        test_must_be_empty actual.err
 '
 
-test_expect_success 'env--helper reads config thanks to trace2' '
+test_expect_success 'test-tool env-helper reads config thanks to trace2' '
        mkdir home &&
        git config -f home/.gitconfig include.path cycle &&
        git config -f home/cycle include.path .gitconfig &&
@@ -93,7 +93,7 @@ test_expect_success 'env--helper reads config thanks to trace2' '
 
        test_must_fail \
                env HOME="$(pwd)/home" GIT_TEST_ENV_HELPER=true \
-               git -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
+               test-tool -C cycle env-helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
        grep "exceeded maximum include depth" err
 '
 
index 35cc8c3b39896cb5caf6740d6847f5dec66e1dea..81946e87ccb9b478789164a6db54d9a80278288d 100755 (executable)
@@ -125,7 +125,7 @@ test_expect_success 'update with autocrlf=input' '
        munge_cr append dir/two &&
        git update-index -- one dir/two &&
        differs=$(git diff-index --cached HEAD) &&
-       verbose test -z "$differs"
+       test -z "$differs"
 
 '
 
@@ -138,7 +138,7 @@ test_expect_success 'update with autocrlf=true' '
        munge_cr append dir/two &&
        git update-index -- one dir/two &&
        differs=$(git diff-index --cached HEAD) &&
-       verbose test -z "$differs"
+       test -z "$differs"
 
 '
 
@@ -153,7 +153,7 @@ test_expect_success 'checkout with autocrlf=true' '
        test "$one" = $(git hash-object --stdin <one) &&
        test "$two" = $(git hash-object --stdin <dir/two) &&
        differs=$(git diff-index --cached HEAD) &&
-       verbose test -z "$differs"
+       test -z "$differs"
 '
 
 test_expect_success 'checkout with autocrlf=input' '
@@ -167,7 +167,7 @@ test_expect_success 'checkout with autocrlf=input' '
        test "$one" = $(git hash-object --stdin <one) &&
        test "$two" = $(git hash-object --stdin <dir/two) &&
        differs=$(git diff-index --cached HEAD) &&
-       verbose test -z "$differs"
+       test -z "$differs"
 '
 
 test_expect_success 'apply patch (autocrlf=input)' '
@@ -177,7 +177,7 @@ test_expect_success 'apply patch (autocrlf=input)' '
        git read-tree --reset -u HEAD &&
 
        git apply patch.file &&
-       verbose test "$patched" = "$(git hash-object --stdin <one)"
+       test "$patched" = "$(git hash-object --stdin <one)"
 '
 
 test_expect_success 'apply patch --cached (autocrlf=input)' '
@@ -187,7 +187,7 @@ test_expect_success 'apply patch --cached (autocrlf=input)' '
        git read-tree --reset -u HEAD &&
 
        git apply --cached patch.file &&
-       verbose test "$patched" = $(git rev-parse :one)
+       test "$patched" = $(git rev-parse :one)
 '
 
 test_expect_success 'apply patch --index (autocrlf=input)' '
@@ -197,8 +197,8 @@ test_expect_success 'apply patch --index (autocrlf=input)' '
        git read-tree --reset -u HEAD &&
 
        git apply --index patch.file &&
-       verbose test "$patched" = $(git rev-parse :one) &&
-       verbose test "$patched" = $(git hash-object --stdin <one)
+       test "$patched" = $(git rev-parse :one) &&
+       test "$patched" = $(git hash-object --stdin <one)
 '
 
 test_expect_success 'apply patch (autocrlf=true)' '
@@ -208,7 +208,7 @@ test_expect_success 'apply patch (autocrlf=true)' '
        git read-tree --reset -u HEAD &&
 
        git apply patch.file &&
-       verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
+       test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
 '
 
 test_expect_success 'apply patch --cached (autocrlf=true)' '
@@ -218,7 +218,7 @@ test_expect_success 'apply patch --cached (autocrlf=true)' '
        git read-tree --reset -u HEAD &&
 
        git apply --cached patch.file &&
-       verbose test "$patched" = $(git rev-parse :one)
+       test "$patched" = $(git rev-parse :one)
 '
 
 test_expect_success 'apply patch --index (autocrlf=true)' '
@@ -228,8 +228,8 @@ test_expect_success 'apply patch --index (autocrlf=true)' '
        git read-tree --reset -u HEAD &&
 
        git apply --index patch.file &&
-       verbose test "$patched" = $(git rev-parse :one) &&
-       verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
+       test "$patched" = $(git rev-parse :one) &&
+       test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
 '
 
 test_expect_success '.gitattributes says two is binary' '
@@ -240,7 +240,7 @@ test_expect_success '.gitattributes says two is binary' '
        git read-tree --reset -u HEAD &&
 
        ! has_cr dir/two &&
-       verbose has_cr one &&
+       has_cr one &&
        ! has_cr three
 '
 
@@ -259,8 +259,8 @@ test_expect_success '.gitattributes says two and three are text' '
        echo "t* crlf" >.gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       verbose has_cr dir/two &&
-       verbose has_cr three
+       has_cr dir/two &&
+       has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (1)' '
@@ -273,7 +273,7 @@ test_expect_success 'in-tree .gitattributes (1)' '
        git read-tree --reset -u HEAD &&
 
        ! has_cr one &&
-       verbose has_cr three
+       has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (2)' '
@@ -283,7 +283,7 @@ test_expect_success 'in-tree .gitattributes (2)' '
        git checkout-index -f -q -u -a &&
 
        ! has_cr one &&
-       verbose has_cr three
+       has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (3)' '
@@ -294,7 +294,7 @@ test_expect_success 'in-tree .gitattributes (3)' '
        git checkout-index -u one dir/two three &&
 
        ! has_cr one &&
-       verbose has_cr three
+       has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (4)' '
@@ -305,7 +305,7 @@ test_expect_success 'in-tree .gitattributes (4)' '
        git checkout-index -u .gitattributes &&
 
        ! has_cr one &&
-       verbose has_cr three
+       has_cr three
 '
 
 test_expect_success 'checkout with existing .gitattributes' '
index abecd75e4e430b6a1182690ae54a0e5d4e76bc32..0b4997022bf88a713845f45f67ab55b03992a659 100755 (executable)
@@ -8,8 +8,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
-TEST_ROOT="$PWD"
-PATH=$TEST_ROOT:$PATH
+PATH=$PWD:$PATH
+TEST_ROOT="$(pwd)"
 
 write_script <<\EOF "$TEST_ROOT/rot13.sh"
 tr \
@@ -263,7 +263,7 @@ test_expect_success 'required filter with absent clean field' '
 
        echo test >test.ac &&
        test_must_fail git add test.ac 2>stderr &&
-       test_i18ngrep "fatal: test.ac: clean filter .absentclean. failed" stderr
+       test_grep "fatal: test.ac: clean filter .absentclean. failed" stderr
 '
 
 test_expect_success 'required filter with absent smudge field' '
@@ -276,7 +276,7 @@ test_expect_success 'required filter with absent smudge field' '
        git add test.as &&
        rm -f test.as &&
        test_must_fail git checkout -- test.as 2>stderr &&
-       test_i18ngrep "fatal: test.as: smudge filter absentsmudge failed" stderr
+       test_grep "fatal: test.as: smudge filter absentsmudge failed" stderr
 '
 
 test_expect_success 'filtering large input to small output should use little memory' '
@@ -733,7 +733,7 @@ test_expect_success 'process filter should restart after unexpected write failur
                git checkout --quiet --no-progress . 2>git-stderr.log &&
 
                grep "smudge write error" git-stderr.log &&
-               test_i18ngrep "error: external filter" git-stderr.log &&
+               test_grep "error: external filter" git-stderr.log &&
 
                cat >expected.log <<-EOF &&
                        START
index f9bbb91f64e35d284a4ac79a6b2ecaa1d2913455..575805513a3d7f6d27fee7ceefcb75e71753f1f9 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test am with auto.crlf'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >patchfile <<\EOF
index a94ac1eae377c023ae3e02eaae393db6e8d232d3..2f57c8669cb5af3dd6fc79bfca4b486f50fa4d19 100755 (executable)
@@ -70,7 +70,8 @@ create_NNO_MIX_files () {
                                cp CRLF        ${pfx}_CRLF.txt &&
                                cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
                                cp LF_mix_CR   ${pfx}_LF_mix_CR.txt &&
-                               cp CRLF_nul    ${pfx}_CRLF_nul.txt
+                               cp CRLF_nul    ${pfx}_CRLF_nul.txt ||
+                               return 1
                        done
                done
        done
@@ -101,7 +102,8 @@ commit_check_warn () {
        do
                fname=${pfx}_$f.txt &&
                cp $f $fname &&
-               git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
+               git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+               return 1
        done &&
        git commit -m "core.autocrlf $crlf" &&
        check_warning "$lfname" ${pfx}_LF.err &&
@@ -121,15 +123,19 @@ commit_chk_wrnNNO () {
        lfmixcr=$1 ; shift
        crlfnul=$1 ; shift
        pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
-       #Commit files on top of existing file
-       create_gitattributes "$attr" $aeol &&
-       for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-       do
-               fname=${pfx}_$f.txt &&
-               cp $f $fname &&
-               printf Z >>"$fname" &&
-               git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
-       done
+
+       test_expect_success 'setup commit NNO files' '
+               #Commit files on top of existing file
+               create_gitattributes "$attr" $aeol &&
+               for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+               do
+                       fname=${pfx}_$f.txt &&
+                       cp $f $fname &&
+                       printf Z >>"$fname" &&
+                       git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+                       return 1
+               done
+       '
 
        test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" '
                check_warning "$lfwarn" ${pfx}_LF.err
@@ -163,15 +169,19 @@ commit_MIX_chkwrn () {
        lfmixcr=$1 ; shift
        crlfnul=$1 ; shift
        pfx=MIX_attr_${attr}_aeol_${aeol}_${crlf}
-       #Commit file with CLRF_mix_LF on top of existing file
-       create_gitattributes "$attr" $aeol &&
-       for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-       do
-               fname=${pfx}_$f.txt &&
-               cp CRLF_mix_LF $fname &&
-               printf Z >>"$fname" &&
-               git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
-       done
+
+       test_expect_success 'setup commit file with mixed EOL' '
+               #Commit file with CLRF_mix_LF on top of existing file
+               create_gitattributes "$attr" $aeol &&
+               for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+               do
+                       fname=${pfx}_$f.txt &&
+                       cp CRLF_mix_LF $fname &&
+                       printf Z >>"$fname" &&
+                       git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+                       return 1
+               done
+       '
 
        test_expect_success "commit file with mixed EOL onto LF crlf=$crlf attr=$attr" '
                check_warning "$lfwarn" ${pfx}_LF.err
@@ -289,17 +299,17 @@ checkout_files () {
        lfmixcrlf=$1 ; shift
        lfmixcr=$1 ; shift
        crlfnul=$1 ; shift
-       create_gitattributes "$attr" $ident $aeol &&
-       git config core.autocrlf $crlf &&
+       test_expect_success "setup config for checkout attr=$attr ident=$ident aeol=$aeol core.autocrlf=$crlf" '
+               create_gitattributes "$attr" $ident $aeol &&
+               git config core.autocrlf $crlf
+       '
        pfx=eol_${ceol}_crlf_${crlf}_attr_${attr}_ &&
        for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul
        do
-               rm crlf_false_attr__$f.txt &&
-               if test -z "$ceol"; then
-                       git checkout -- crlf_false_attr__$f.txt
-               else
-                       git -c core.eol=$ceol checkout -- crlf_false_attr__$f.txt
-               fi
+               test_expect_success "setup $f checkout ${ceol:+ with -c core.eol=$ceol}"  '
+                       rm -f crlf_false_attr__$f.txt &&
+                       git ${ceol:+-c core.eol=$ceol} checkout -- crlf_false_attr__$f.txt
+               '
        done
 
        test_expect_success "ls-files --eol attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol" '
index c196fdb0ee21a27b6093902dcafe133abfc98ce0..1b55f59c237c59d625241aea56679fbf9bb3bb25 100755 (executable)
@@ -92,23 +92,23 @@ do
                # In these cases the BOM is prohibited.
                cp bebom.utf${i}be.raw bebom.utf${i}be &&
                test_must_fail git add bebom.utf${i}be 2>err.out &&
-               test_i18ngrep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
-               test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+               test_grep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
+               test_grep "use UTF-${i} as working-tree-encoding" err.out &&
 
                cp lebom.utf${i}le.raw lebom.utf${i}be &&
                test_must_fail git add lebom.utf${i}be 2>err.out &&
-               test_i18ngrep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
-               test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+               test_grep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
+               test_grep "use UTF-${i} as working-tree-encoding" err.out &&
 
                cp bebom.utf${i}be.raw bebom.utf${i}le &&
                test_must_fail git add bebom.utf${i}le 2>err.out &&
-               test_i18ngrep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
-               test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+               test_grep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
+               test_grep "use UTF-${i} as working-tree-encoding" err.out &&
 
                cp lebom.utf${i}le.raw lebom.utf${i}le &&
                test_must_fail git add lebom.utf${i}le 2>err.out &&
-               test_i18ngrep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
-               test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out
+               test_grep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
+               test_grep "use UTF-${i} as working-tree-encoding" err.out
        '
 
        test_expect_success "check required UTF-${i} BOM" '
@@ -118,13 +118,13 @@ do
 
                cp nobom.utf${i}be.raw nobom.utf${i} &&
                test_must_fail git add nobom.utf${i} 2>err.out &&
-               test_i18ngrep "fatal: BOM is required .* utf-${i}" err.out &&
-               test_i18ngrep "use UTF-${i}BE or UTF-${i}LE" err.out &&
+               test_grep "fatal: BOM is required .* utf-${i}" err.out &&
+               test_grep "use UTF-${i}BE or UTF-${i}LE" err.out &&
 
                cp nobom.utf${i}le.raw nobom.utf${i} &&
                test_must_fail git add nobom.utf${i} 2>err.out &&
-               test_i18ngrep "fatal: BOM is required .* utf-${i}" err.out &&
-               test_i18ngrep "use UTF-${i}BE or UTF-${i}LE" err.out
+               test_grep "fatal: BOM is required .* utf-${i}" err.out &&
+               test_grep "use UTF-${i}BE or UTF-${i}LE" err.out
        '
 
        test_expect_success "eol conversion for UTF-${i} encoded files on checkout" '
@@ -169,7 +169,7 @@ test_expect_success 'check unsupported encodings' '
        echo "*.set text working-tree-encoding" >.gitattributes &&
        printf "set" >t.set &&
        test_must_fail git add t.set 2>err.out &&
-       test_i18ngrep "true/false are no valid working-tree-encodings" err.out &&
+       test_grep "true/false are no valid working-tree-encodings" err.out &&
 
        echo "*.unset text -working-tree-encoding" >.gitattributes &&
        printf "unset" >t.unset &&
@@ -182,7 +182,7 @@ test_expect_success 'check unsupported encodings' '
        echo "*.garbage text working-tree-encoding=garbage" >.gitattributes &&
        printf "garbage" >t.garbage &&
        test_must_fail git add t.garbage 2>err.out &&
-       test_i18ngrep "failed to encode" err.out
+       test_grep "failed to encode" err.out
 '
 
 test_expect_success 'error if encoding round trip is not the same during refresh' '
@@ -201,7 +201,7 @@ test_expect_success 'error if encoding round trip is not the same during refresh
        git update-ref refs/heads/main $COMMIT &&
 
        test_must_fail git checkout HEAD^ 2>err.out &&
-       test_i18ngrep "error: .* overwritten by checkout:" err.out
+       test_grep "error: .* overwritten by checkout:" err.out
 '
 
 test_expect_success 'error if encoding garbage is already in Git' '
@@ -217,7 +217,7 @@ test_expect_success 'error if encoding garbage is already in Git' '
        git update-ref refs/heads/main $COMMIT &&
 
        git diff 2>err.out &&
-       test_i18ngrep "error: BOM is required" err.out
+       test_grep "error: BOM is required" err.out
 '
 
 test_lazy_prereq ICONV_SHIFT_JIS '
index 0a5713c5248280d88a241b8027b5c98e4f321718..d1b3be872576789541410fa66d4473a006db99b2 100755 (executable)
@@ -17,396 +17,378 @@ printf_git_stripspace () {
     printf "$1" | git stripspace
 }
 
-test_expect_success \
-    'long lines without spaces should be unchanged' '
-    echo "$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
-
-    echo "$ttt$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
-
-    echo "$ttt$ttt$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
-
-    echo "$ttt$ttt$ttt$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual
+test_expect_success 'long lines without spaces should be unchanged' '
+       echo "$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual &&
+
+       echo "$ttt$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual &&
+
+       echo "$ttt$ttt$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual &&
+
+       echo "$ttt$ttt$ttt$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success \
-    'lines with spaces at the beginning should be unchanged' '
-    echo "$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+test_expect_success 'lines with spaces at the beginning should be unchanged' '
+       echo "$sss$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual &&
 
-    echo "$sss$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+       echo "$sss$sss$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual &&
 
-    echo "$sss$sss$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual
+       echo "$sss$sss$sss$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success \
-    'lines with intermediate spaces should be unchanged' '
-    echo "$ttt$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+test_expect_success 'lines with intermediate spaces should be unchanged' '
+       echo "$ttt$sss$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual &&
 
-    echo "$ttt$sss$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual
+       echo "$ttt$sss$sss$ttt" >expect &&
+       git stripspace <expect >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success \
-    'consecutive blank lines should be unified' '
-    printf "$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'consecutive blank lines should be unified' '
+       printf "$ttt\n\n$ttt\n" > expect &&
+       printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt\n\n$ttt\n" > expect &&
+       printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+       printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n\n$ttt\n" > expect &&
+       printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n\n$ttt$ttt\n" > expect &&
+       printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+       printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n\n$ttt\n" > expect &&
+       printf "$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt\n\n$ttt\n" > expect &&
+       printf "$ttt$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt$ttt$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+       printf "$ttt$ttt$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n\n$ttt\n" > expect &&
+       printf "$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt$ttt\n" > expect &&
-    printf "$ttt\n\t\n \n\n  \t\t\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n\n$ttt$ttt\n" > expect &&
+       printf "$ttt\n\t\n \n\n  \t\t\n$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
-    printf "$ttt\n\t\n \n\n  \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual
+       printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+       printf "$ttt\n\t\n \n\n  \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success \
-    'only consecutive blank lines should be completely removed' '
+test_expect_success 'only consecutive blank lines should be completely removed' '
+       printf "\n" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       printf "\n\n\n" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "\n\n\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
-
-    printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
-    test_must_be_empty actual
+       printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
+       test_must_be_empty actual
 '
 
-test_expect_success \
-    'consecutive blank lines at the beginning should be removed' '
-    printf "$ttt\n" > expect &&
-    printf "\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'consecutive blank lines at the beginning should be removed' '
+       printf "$ttt\n" > expect &&
+       printf "\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n" > expect &&
-    printf "\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n" > expect &&
+       printf "\n\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt\n" > expect &&
-    printf "\n\n\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt\n" > expect &&
+       printf "\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n" > expect &&
-    printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt$ttt\n" > expect &&
+       printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt$ttt\n" > expect &&
-    printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt$ttt$ttt\n" > expect &&
+       printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n" > expect &&
+       printf "$ttt\n" > expect &&
 
-    printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual
+       printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success \
-    'consecutive blank lines at the end should be removed' '
-    printf "$ttt\n" > expect &&
-    printf "$ttt\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'consecutive blank lines at the end should be removed' '
+       printf "$ttt\n" > expect &&
+       printf "$ttt\n\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n" > expect &&
+       printf "$ttt\n\n\n\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt\n" > expect &&
-    printf "$ttt$ttt\n\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt\n" > expect &&
+       printf "$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n" > expect &&
-    printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt$ttt\n" > expect &&
+       printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt$ttt\n" > expect &&
-    printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt$ttt$ttt\n" > expect &&
+       printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n" > expect &&
+       printf "$ttt\n" > expect &&
 
-    printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual &&
-    test_cmp expect actual
+       printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success \
-    'text without newline at end should end with newline' '
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$ttt"
+test_expect_success 'text without newline at end should end with newline' '
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt" &&
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt" &&
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt" &&
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$ttt"
 '
 
 # text plus spaces at the end:
 
-test_expect_success \
-    'text plus spaces without newline at end should end with newline' '
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss$sss"
+test_expect_success 'text plus spaces without newline at end should end with newline' '
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss" &&
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss" &&
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$sss" &&
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss" &&
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss$sss" &&
+       test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss$sss"
 '
 
-test_expect_success \
-    'text plus spaces without newline at end should not show spaces' '
-    printf "$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$ttt$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$sss$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null
+test_expect_success 'text plus spaces without newline at end should not show spaces' '
+       printf "$ttt$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       printf "$ttt$ttt$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       printf "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       printf "$ttt$sss$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       printf "$ttt$ttt$sss$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       printf "$ttt$sss$sss$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null
 '
 
-test_expect_success \
-    'text plus spaces without newline should show the correct lines' '
-    printf "$ttt\n" >expect &&
-    printf "$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'text plus spaces without newline should show the correct lines' '
+       printf "$ttt\n" >expect &&
+       printf "$ttt$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n" >expect &&
-    printf "$ttt$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n" >expect &&
+       printf "$ttt$sss$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n" >expect &&
-    printf "$ttt$sss$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n" >expect &&
+       printf "$ttt$sss$sss$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt\n" >expect &&
-    printf "$ttt$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt\n" >expect &&
+       printf "$ttt$ttt$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt\n" >expect &&
-    printf "$ttt$ttt$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt\n" >expect &&
+       printf "$ttt$ttt$sss$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n" >expect &&
-    printf "$ttt$ttt$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual
+       printf "$ttt$ttt$ttt\n" >expect &&
+       printf "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success \
-    'text plus spaces at end should not show spaces' '
-    echo "$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$ttt$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$sss$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null
+test_expect_success 'text plus spaces at end should not show spaces' '
+       echo "$ttt$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       echo "$ttt$ttt$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       echo "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       echo "$ttt$sss$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       echo "$ttt$ttt$sss$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null &&
+       echo "$ttt$sss$sss$sss" | git stripspace >tmp &&
+       ! grep "  " tmp >/dev/null
 '
 
-test_expect_success \
-    'text plus spaces at end should be cleaned and newline must remain' '
-    echo "$ttt" >expect &&
-    echo "$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'text plus spaces at end should be cleaned and newline must remain' '
+       echo "$ttt" >expect &&
+       echo "$ttt$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    echo "$ttt" >expect &&
-    echo "$ttt$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+       echo "$ttt" >expect &&
+       echo "$ttt$sss$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    echo "$ttt" >expect &&
-    echo "$ttt$sss$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+       echo "$ttt" >expect &&
+       echo "$ttt$sss$sss$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    echo "$ttt$ttt" >expect &&
-    echo "$ttt$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+       echo "$ttt$ttt" >expect &&
+       echo "$ttt$ttt$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    echo "$ttt$ttt" >expect &&
-    echo "$ttt$ttt$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+       echo "$ttt$ttt" >expect &&
+       echo "$ttt$ttt$sss$sss" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    echo "$ttt$ttt$ttt" >expect &&
-    echo "$ttt$ttt$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual
+       echo "$ttt$ttt$ttt" >expect &&
+       echo "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+       test_cmp expect actual
 '
 
 # spaces only:
 
-test_expect_success \
-    'spaces with newline at end should be replaced with empty string' '
-    echo | git stripspace >actual &&
-    test_must_be_empty actual &&
+test_expect_success 'spaces with newline at end should be replaced with empty string' '
+       echo | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    echo "$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       echo "$sss" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    echo "$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       echo "$sss$sss" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    echo "$sss$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       echo "$sss$sss$sss" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    echo "$sss$sss$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual
+       echo "$sss$sss$sss$sss" | git stripspace >actual &&
+       test_must_be_empty actual
 '
 
-test_expect_success \
-    'spaces without newline at end should not show spaces' '
-    printf "" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null &&
-    printf "$sss" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null &&
-    printf "$sss$sss" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null &&
-    printf "$sss$sss$sss" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null &&
-    printf "$sss$sss$sss$sss" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null
+test_expect_success 'spaces without newline at end should not show spaces' '
+       printf "" | git stripspace >tmp &&
+       ! grep " " tmp >/dev/null &&
+       printf "$sss" | git stripspace >tmp &&
+       ! grep " " tmp >/dev/null &&
+       printf "$sss$sss" | git stripspace >tmp &&
+       ! grep " " tmp >/dev/null &&
+       printf "$sss$sss$sss" | git stripspace >tmp &&
+       ! grep " " tmp >/dev/null &&
+       printf "$sss$sss$sss$sss" | git stripspace >tmp &&
+       ! grep " " tmp >/dev/null
 '
 
-test_expect_success \
-    'spaces without newline at end should be replaced with empty string' '
-    printf "" | git stripspace >actual &&
-    test_must_be_empty actual &&
+test_expect_success 'spaces without newline at end should be replaced with empty string' '
+       printf "" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       printf "$sss$sss" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "$sss$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+       printf "$sss$sss$sss" | git stripspace >actual &&
+       test_must_be_empty actual &&
 
-    printf "$sss$sss$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual
+       printf "$sss$sss$sss$sss" | git stripspace >actual &&
+       test_must_be_empty actual
 '
 
-test_expect_success \
-    'consecutive text lines should be unchanged' '
-    printf "$ttt$ttt\n$ttt\n" >expect &&
-    printf "$ttt$ttt\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'consecutive text lines should be unchanged' '
+       printf "$ttt$ttt\n$ttt\n" >expect &&
+       printf "$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n$ttt$ttt\n$ttt\n" >expect &&
-    printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n$ttt$ttt\n$ttt\n" >expect &&
+       printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect &&
-    printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect &&
+       printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect &&
-    printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect &&
+       printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect &&
-    printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+       printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect &&
+       printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual &&
 
-    printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect &&
-    printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual
+       printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect &&
+       printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'strip comments, too' '
index aecb308cf668057995a167090a421b906983f32b..dc3496897abc96f488b43bfcbd1dd54258e75876 100755 (executable)
@@ -71,4 +71,13 @@ test_expect_success 'safe.directory=*, but is reset' '
        expect_rejected_dir
 '
 
+test_expect_success 'safe.directory in included file' '
+       cat >gitconfig-include <<-EOF &&
+       [safe]
+               directory = "$(pwd)"
+       EOF
+       git config --global --add include.path "$(pwd)/gitconfig-include" &&
+       git status
+'
+
 test_done
index ecbdc8238db988ee23ea3c66ddbf30fbd3777520..038b8b788d7dea688c2efa807e1d1856b8f33c13 100755 (executable)
@@ -7,13 +7,26 @@ TEST_PASSES_SANITIZE_LEAK=true
 
 pwd="$(pwd)"
 
-expect_accepted () {
-       git "$@" rev-parse --git-dir
+expect_accepted_implicit () {
+       test_when_finished 'rm "$pwd/trace.perf"' &&
+       GIT_TRACE2_PERF="$pwd/trace.perf" git "$@" rev-parse --git-dir &&
+       # Note: we're intentionally only checking that the bare repo has a
+       # directory *prefix* of $pwd
+       grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
+}
+
+expect_accepted_explicit () {
+       test_when_finished 'rm "$pwd/trace.perf"' &&
+       GIT_DIR="$1" GIT_TRACE2_PERF="$pwd/trace.perf" git rev-parse --git-dir &&
+       ! grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
 }
 
 expect_rejected () {
-       test_must_fail git "$@" rev-parse --git-dir 2>err &&
-       grep -F "cannot use bare repository" err
+       test_when_finished 'rm "$pwd/trace.perf"' &&
+       test_env GIT_TRACE2_PERF="$pwd/trace.perf" \
+               test_must_fail git "$@" rev-parse --git-dir 2>err &&
+       grep -F "cannot use bare repository" err &&
+       grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
 }
 
 test_expect_success 'setup bare repo in worktree' '
@@ -22,12 +35,13 @@ test_expect_success 'setup bare repo in worktree' '
 '
 
 test_expect_success 'safe.bareRepository unset' '
-       expect_accepted -C outer-repo/bare-repo
+       test_unconfig --global safe.bareRepository &&
+       expect_accepted_implicit -C outer-repo/bare-repo
 '
 
 test_expect_success 'safe.bareRepository=all' '
        test_config_global safe.bareRepository all &&
-       expect_accepted -C outer-repo/bare-repo
+       expect_accepted_implicit -C outer-repo/bare-repo
 '
 
 test_expect_success 'safe.bareRepository=explicit' '
@@ -47,8 +61,21 @@ test_expect_success 'safe.bareRepository in the repository' '
 
 test_expect_success 'safe.bareRepository on the command line' '
        test_config_global safe.bareRepository explicit &&
-       expect_accepted -C outer-repo/bare-repo \
+       expect_accepted_implicit -C outer-repo/bare-repo \
                -c safe.bareRepository=all
 '
 
+test_expect_success 'safe.bareRepository in included file' '
+       cat >gitconfig-include <<-\EOF &&
+       [safe]
+               bareRepository = explicit
+       EOF
+       git config --global --add include.path "$(pwd)/gitconfig-include" &&
+       expect_rejected -C outer-repo/bare-repo
+'
+
+test_expect_success 'no trace when GIT_DIR is explicitly provided' '
+       expect_accepted_explicit "$pwd/outer-repo/bare-repo"
+'
+
 test_done
index 5cc62306e39c4f7ab2f6fec35d3cafc7bd65be86..ec974867e4337ca503cb817fd79926193c518710 100755 (executable)
@@ -13,29 +13,36 @@ usage: test-tool parse-options <options>
 
     A helper function for the parse-options API.
 
-    --yes                 get a boolean
+    --[no-]yes            get a boolean
     -D, --no-doubt        begins with 'no-'
+    --doubt               opposite of --no-doubt
     -B, --no-fear         be brave
-    -b, --boolean         increment by one
-    -4, --or4             bitwise-or boolean with ...0100
-    --neg-or4             same as --no-or4
+    -b, --[no-]boolean    increment by one
+    -4, --[no-]or4        bitwise-or boolean with ...0100
+    --[no-]neg-or4        same as --no-or4
 
-    -i, --integer <n>     get a integer
+    -i, --[no-]integer <n>
+                          get a integer
     -j <n>                get a integer, too
     -m, --magnitude <n>   get a magnitude
-    --set23               set integer to 23
+    --[no-]set23          set integer to 23
     --mode1               set integer to 1 (cmdmode option)
     --mode2               set integer to 2 (cmdmode option)
-    -L, --length <str>    get length of <str>
-    -F, --file <file>     set file to <file>
+    --[no-]mode34 (3|4)   set integer to 3 or 4 (cmdmode option)
+    -L, --[no-]length <str>
+                          get length of <str>
+    -F, --[no-]file <file>
+                          set file to <file>
 
 String options
-    -s, --string <string>
+    -s, --[no-]string <string>
                           get a string
-    --string2 <str>       get another string
-    --st <st>             get another string (pervert ordering)
+    --[no-]string2 <str>  get another string
+    --[no-]st <st>        get another string (pervert ordering)
     -o <str>              get another string
-    --list <str>          add str to list
+    --longhelp            help text of this entry
+                          spans multiple lines
+    --[no-]list <str>     add str to list
 
 Magic arguments
     -NUM                  set integer to NUM
@@ -44,16 +51,17 @@ Magic arguments
     --no-ambiguous        negative ambiguity
 
 Standard options
-    --abbrev[=<n>]        use <n> digits to display object names
-    -v, --verbose         be verbose
-    -n, --dry-run         dry run
-    -q, --quiet           be quiet
-    --expect <string>     expected output in the variable dump
+    --[no-]abbrev[=<n>]   use <n> digits to display object names
+    -v, --[no-]verbose    be verbose
+    -n, --[no-]dry-run    dry run
+    -q, --[no-]quiet      be quiet
+    --[no-]expect <string>
+                          expected output in the variable dump
 
 Alias
-    -A, --alias-source <string>
+    -A, --[no-]alias-source <string>
                           get a string
-    -Z, --alias-target <string>
+    -Z, --[no-]alias-target <string>
                           alias of --alias-source
 
 EOF
@@ -359,19 +367,41 @@ test_expect_success 'OPT_NEGBIT() works' '
 '
 
 test_expect_success 'OPT_CMDMODE() works' '
-       test-tool parse-options --expect="integer: 1" --mode1
+       test-tool parse-options --expect="integer: 1" --mode1 &&
+       test-tool parse-options --expect="integer: 3" --mode34=3
 '
 
-test_expect_success 'OPT_CMDMODE() detects incompatibility' '
+test_expect_success 'OPT_CMDMODE() detects incompatibility (1)' '
        test_must_fail test-tool parse-options --mode1 --mode2 >output 2>output.err &&
        test_must_be_empty output &&
-       test_i18ngrep "incompatible with --mode" output.err
+       test_grep "mode1" output.err &&
+       test_grep "mode2" output.err &&
+       test_grep "cannot be used together" output.err
 '
 
-test_expect_success 'OPT_CMDMODE() detects incompatibility with something else' '
+test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
        test_must_fail test-tool parse-options --set23 --mode2 >output 2>output.err &&
        test_must_be_empty output &&
-       test_i18ngrep "incompatible with something else" output.err
+       test_grep "mode2" output.err &&
+       test_grep "set23" output.err &&
+       test_grep "cannot be used together" output.err
+'
+
+test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
+       test_must_fail test-tool parse-options --mode2 --set23 >output 2>output.err &&
+       test_must_be_empty output &&
+       test_grep "mode2" output.err &&
+       test_grep "set23" output.err &&
+       test_grep "cannot be used together" output.err
+'
+
+test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
+       test_must_fail test-tool parse-options --mode2 --mode34=3 \
+               >output 2>output.err &&
+       test_must_be_empty output &&
+       test_grep "mode2" output.err &&
+       test_grep "mode34.3" output.err &&
+       test_grep "cannot be used together" output.err
 '
 
 test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
@@ -709,4 +739,16 @@ test_expect_success 'subcommands are incompatible with KEEP_DASHDASH unless in c
        grep ^BUG err
 '
 
+test_expect_success 'negative magnitude' '
+       test_must_fail test-tool parse-options --magnitude -1 >out 2>err &&
+       grep "non-negative integer" err &&
+       test_must_be_empty out
+'
+
+test_expect_success 'magnitude with units but no numbers' '
+       test_must_fail test-tool parse-options --magnitude m >out 2>err &&
+       grep "non-negative integer" err &&
+       test_must_be_empty out
+'
+
 test_done
index c4fc34eb18eb1401c5331667408c496e663c9c69..1464294bd1bfc36d48f06a3ead7826daeb9a745c 100755 (executable)
@@ -5,6 +5,7 @@ test_description='Test commands behavior when given invalid argument value'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup ' '
@@ -20,8 +21,8 @@ test_expect_success 'tag --contains <existent_tag>' '
 test_expect_success 'tag --contains <inexistent_tag>' '
        test_must_fail git tag --contains "notag" >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "error" actual.err &&
-       test_i18ngrep ! "usage" actual.err
+       test_grep "error" actual.err &&
+       test_grep ! "usage" actual.err
 '
 
 test_expect_success 'tag --no-contains <existent_tag>' '
@@ -33,27 +34,27 @@ test_expect_success 'tag --no-contains <existent_tag>' '
 test_expect_success 'tag --no-contains <inexistent_tag>' '
        test_must_fail git tag --no-contains "notag" >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "error" actual.err &&
-       test_i18ngrep ! "usage" actual.err
+       test_grep "error" actual.err &&
+       test_grep ! "usage" actual.err
 '
 
 test_expect_success 'tag usage error' '
        test_must_fail git tag --noopt >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "usage" actual.err
+       test_grep "usage" actual.err
 '
 
 test_expect_success 'branch --contains <existent_commit>' '
        git branch --contains "main" >actual 2>actual.err &&
-       test_i18ngrep "main" actual &&
+       test_grep "main" actual &&
        test_line_count = 0 actual.err
 '
 
 test_expect_success 'branch --contains <inexistent_commit>' '
        test_must_fail git branch --no-contains "nocommit" >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "error" actual.err &&
-       test_i18ngrep ! "usage" actual.err
+       test_grep "error" actual.err &&
+       test_grep ! "usage" actual.err
 '
 
 test_expect_success 'branch --no-contains <existent_commit>' '
@@ -65,14 +66,14 @@ test_expect_success 'branch --no-contains <existent_commit>' '
 test_expect_success 'branch --no-contains <inexistent_commit>' '
        test_must_fail git branch --no-contains "nocommit" >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "error" actual.err &&
-       test_i18ngrep ! "usage" actual.err
+       test_grep "error" actual.err &&
+       test_grep ! "usage" actual.err
 '
 
 test_expect_success 'branch usage error' '
        test_must_fail git branch --noopt >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "usage" actual.err
+       test_grep "usage" actual.err
 '
 
 test_expect_success 'for-each-ref --contains <existent_object>' '
@@ -84,8 +85,8 @@ test_expect_success 'for-each-ref --contains <existent_object>' '
 test_expect_success 'for-each-ref --contains <inexistent_object>' '
        test_must_fail git for-each-ref --no-contains "noobject" >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "error" actual.err &&
-       test_i18ngrep ! "usage" actual.err
+       test_grep "error" actual.err &&
+       test_grep ! "usage" actual.err
 '
 
 test_expect_success 'for-each-ref --no-contains <existent_object>' '
@@ -97,14 +98,14 @@ test_expect_success 'for-each-ref --no-contains <existent_object>' '
 test_expect_success 'for-each-ref --no-contains <inexistent_object>' '
        test_must_fail git for-each-ref --no-contains "noobject" >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "error" actual.err &&
-       test_i18ngrep ! "usage" actual.err
+       test_grep "error" actual.err &&
+       test_grep ! "usage" actual.err
 '
 
 test_expect_success 'for-each-ref usage error' '
        test_must_fail git for-each-ref --noopt >actual 2>actual.err &&
        test_line_count = 0 actual &&
-       test_i18ngrep "usage" actual.err
+       test_grep "usage" actual.err
 '
 
 test_done
index 6bada3702257649551558884237f71bea3fdadc2..c3eb1158ef9ae2eb10f3f7a6d5c882e41f3c576a 100755 (executable)
@@ -15,12 +15,22 @@ test_expect_success SYMLINKS setup '
 
 test_expect_success SYMLINKS 'update-index --add beyond symlinks' '
        test_must_fail git update-index --add c/d &&
-       ! ( git ls-files | grep c/d )
+       cat >expect <<-\EOF &&
+       a
+       b/d
+       EOF
+       git ls-files >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success SYMLINKS 'add beyond symlinks' '
        test_must_fail git add c/d &&
-       ! ( git ls-files | grep c/d )
+       cat >expect <<-\EOF &&
+       a
+       b/d
+       EOF
+       git ls-files >actual &&
+       test_cmp expect actual
 '
 
 test_done
index 68e29c904a62c9ef51d6a1bfc3449795d50ac10f..0afa3d0d312ca6d1788b76d3b0dfcba04f8397e7 100755 (executable)
@@ -10,20 +10,27 @@ TEST_PASSES_SANITIZE_LEAK=true
 
 norm_path() {
        expected=$(test-tool path-utils print_path "$2")
-       test_expect_success $3 "normalize path: $1 => $2" \
-       "test \"\$(test-tool path-utils normalize_path_copy '$1')\" = '$expected'"
+       test_expect_success $3 "normalize path: $1 => $2" "
+               echo '$expected' >expect &&
+               test-tool path-utils normalize_path_copy '$1' >actual &&
+               test_cmp expect actual
+       "
 }
 
 relative_path() {
        expected=$(test-tool path-utils print_path "$3")
-       test_expect_success $4 "relative path: $1 $2 => $3" \
-       "test \"\$(test-tool path-utils relative_path '$1' '$2')\" = '$expected'"
+       test_expect_success $4 "relative path: $1 $2 => $3" "
+               echo '$expected' >expect &&
+               test-tool path-utils relative_path '$1' '$2' >actual &&
+               test_cmp expect actual
+       "
 }
 
 test_submodule_relative_url() {
        test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" "
-               actual=\$(test-tool submodule resolve-relative-url '$1' '$2' '$3') &&
-               test \"\$actual\" = '$4'
+               echo '$4' >expect &&
+               test-tool submodule resolve-relative-url '$1' '$2' '$3' >actual &&
+               test_cmp expect actual
        "
 }
 
@@ -64,9 +71,11 @@ ancestor() {
                expected=$(($expected-$rootslash+$rootoff))
                ;;
        esac
-       test_expect_success $4 "longest ancestor: $1 $2 => $expected" \
-       "actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
-        test \"\$actual\" = '$expected'"
+       test_expect_success $4 "longest ancestor: $1 $2 => $expected" "
+               echo '$expected' >expect &&
+               test-tool path-utils longest_ancestor_length '$1' '$2' >actual &&
+               test_cmp expect actual
+       "
 }
 
 # Some absolute path tests should be skipped on Windows due to path mangling
@@ -166,8 +175,10 @@ ancestor D:/Users/me C:/ -1 MINGW
 ancestor //server/share/my-directory //server/share/ 14 MINGW
 
 test_expect_success 'strip_path_suffix' '
-       test c:/msysgit = $(test-tool path-utils strip_path_suffix \
-               c:/msysgit/libexec//git-core libexec/git-core)
+       echo c:/msysgit >expect &&
+       test-tool path-utils strip_path_suffix \
+               c:/msysgit/libexec//git-core libexec/git-core >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'absolute path rejects the empty string' '
@@ -188,35 +199,61 @@ test_expect_success 'real path rejects the empty string' '
 '
 
 test_expect_success POSIX 'real path works on absolute paths 1' '
+       echo / >expect &&
+       test-tool path-utils real_path "/" >actual &&
+       test_cmp expect actual &&
+
        nopath="hopefully-absent-path" &&
-       test "/" = "$(test-tool path-utils real_path "/")" &&
-       test "/$nopath" = "$(test-tool path-utils real_path "/$nopath")"
+       echo "/$nopath" >expect &&
+       test-tool path-utils real_path "/$nopath" >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'real path works on absolute paths 2' '
-       nopath="hopefully-absent-path" &&
        # Find an existing top-level directory for the remaining tests:
        d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
-       test "$d" = "$(test-tool path-utils real_path "$d")" &&
-       test "$d/$nopath" = "$(test-tool path-utils real_path "$d/$nopath")"
+       echo "$d" >expect &&
+       test-tool path-utils real_path "$d" >actual &&
+       test_cmp expect actual &&
+
+       nopath="hopefully-absent-path" &&
+       echo "$d/$nopath" >expect &&
+       test-tool path-utils real_path "$d/$nopath" >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success POSIX 'real path removes extra leading slashes' '
+       echo "/" >expect &&
+       test-tool path-utils real_path "///" >actual &&
+       test_cmp expect actual &&
+
        nopath="hopefully-absent-path" &&
-       test "/" = "$(test-tool path-utils real_path "///")" &&
-       test "/$nopath" = "$(test-tool path-utils real_path "///$nopath")" &&
+       echo "/$nopath" >expect &&
+       test-tool path-utils real_path "///$nopath" >actual &&
+       test_cmp expect actual &&
+
        # Find an existing top-level directory for the remaining tests:
        d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
-       test "$d" = "$(test-tool path-utils real_path "//$d")" &&
-       test "$d/$nopath" = "$(test-tool path-utils real_path "//$d/$nopath")"
+       echo "$d" >expect &&
+       test-tool path-utils real_path "//$d" >actual &&
+       test_cmp expect actual &&
+
+       echo "$d/$nopath" >expect &&
+       test-tool path-utils real_path "//$d/$nopath" >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'real path removes other extra slashes' '
-       nopath="hopefully-absent-path" &&
        # Find an existing top-level directory for the remaining tests:
        d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
-       test "$d" = "$(test-tool path-utils real_path "$d///")" &&
-       test "$d/$nopath" = "$(test-tool path-utils real_path "$d///$nopath")"
+       echo "$d" >expect &&
+       test-tool path-utils real_path "$d///" >actual &&
+       test_cmp expect actual &&
+
+       nopath="hopefully-absent-path" &&
+       echo "$d/$nopath" >expect &&
+       test-tool path-utils real_path "$d///$nopath" >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success SYMLINKS 'real path works on symlinks' '
@@ -227,19 +264,29 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
        mkdir third &&
        dir="$(cd .git && pwd -P)" &&
        dir2=third/../second/other/.git &&
-       test "$dir" = "$(test-tool path-utils real_path $dir2)" &&
+       echo "$dir" >expect &&
+       test-tool path-utils real_path $dir2 >actual &&
+       test_cmp expect actual &&
        file="$dir"/index &&
-       test "$file" = "$(test-tool path-utils real_path $dir2/index)" &&
+       echo "$file" >expect &&
+       test-tool path-utils real_path $dir2/index >actual &&
+       test_cmp expect actual &&
        basename=blub &&
-       test "$dir/$basename" = "$(cd .git && test-tool path-utils real_path "$basename")" &&
+       echo "$dir/$basename" >expect &&
+       test-tool -C .git path-utils real_path "$basename" >actual &&
+       test_cmp expect actual &&
        ln -s ../first/file .git/syml &&
        sym="$(cd first && pwd -P)"/file &&
-       test "$sym" = "$(test-tool path-utils real_path "$dir2/syml")"
+       echo "$sym" >expect &&
+       test-tool path-utils real_path "$dir2/syml" >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
        ln -s target symlink &&
-       test "$(test-tool path-utils prefix_path prefix "$(pwd)/symlink")" = "symlink"
+       echo "symlink" >expect &&
+       test-tool path-utils prefix_path prefix "$(pwd)/symlink" >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'prefix_path works with only absolute path to work tree' '
@@ -255,7 +302,10 @@ test_expect_success 'prefix_path rejects absolute path to dir with same beginnin
 test_expect_success SYMLINKS 'prefix_path works with absolute path to a symlink to work tree having  same beginning as work tree' '
        git init repo &&
        ln -s repo repolink &&
-       test "a" = "$(cd repo && test-tool path-utils prefix_path prefix "$(pwd)/../repolink/a")"
+       echo "a" >expect &&
+       repo_path="$(cd repo && pwd)" &&
+       test-tool -C repo path-utils prefix_path prefix "$repo_path/../repolink/a" >actual &&
+       test_cmp expect actual
 '
 
 relative_path /foo/a/b/c/      /foo/a/b/       c/
index 7b5423eebdafa4f35b90ebe6194683e59e8bb46c..20986b693cfbe8de91552f43daa11be630842f34 100755 (executable)
@@ -19,12 +19,12 @@ test_expect_success MINGW 'subprocess inherits only std handles' '
 
 test_expect_success 'start_command reports ENOENT (slash)' '
        test-tool run-command start-command-ENOENT ./does-not-exist 2>err &&
-       test_i18ngrep "\./does-not-exist" err
+       test_grep "\./does-not-exist" err
 '
 
 test_expect_success 'start_command reports ENOENT (no slash)' '
        test-tool run-command start-command-ENOENT does-not-exist 2>err &&
-       test_i18ngrep "does-not-exist" err
+       test_grep "does-not-exist" err
 '
 
 test_expect_success 'run_command can run a command' '
@@ -49,7 +49,7 @@ test_expect_success !RUNS_COMMANDS_FROM_PWD 'run_command is restricted to PATH'
        echo yikes
        EOF
        test_must_fail test-tool run-command run-command should-not-run 2>err &&
-       test_i18ngrep "should-not-run" err
+       test_grep "should-not-run" err
 '
 
 test_expect_success !MINGW 'run_command can run a script without a #! line' '
@@ -130,7 +130,8 @@ World
 EOF
 
 test_expect_success 'run_command runs in parallel with more jobs available than tasks' '
-       test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+       test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+       test_must_be_empty out &&
        test_cmp expect actual
 '
 
@@ -141,7 +142,8 @@ test_expect_success 'run_command runs ungrouped in parallel with more jobs avail
 '
 
 test_expect_success 'run_command runs in parallel with as many jobs as tasks' '
-       test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+       test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+       test_must_be_empty out &&
        test_cmp expect actual
 '
 
@@ -152,7 +154,8 @@ test_expect_success 'run_command runs ungrouped in parallel with as many jobs as
 '
 
 test_expect_success 'run_command runs in parallel with more tasks than jobs available' '
-       test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+       test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+       test_must_be_empty out &&
        test_cmp expect actual
 '
 
@@ -172,7 +175,8 @@ asking for a quick stop
 EOF
 
 test_expect_success 'run_command is asked to abort gracefully' '
-       test-tool run-command run-command-abort 3 false 2>actual &&
+       test-tool run-command run-command-abort 3 false >out 2>actual &&
+       test_must_be_empty out &&
        test_cmp expect actual
 '
 
@@ -187,7 +191,8 @@ no further jobs available
 EOF
 
 test_expect_success 'run_command outputs ' '
-       test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+       test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+       test_must_be_empty out &&
        test_cmp expect actual
 '
 
index 46d4839194bb2322b2a8cd9b80618a855e0fb070..1fee6d90102cc6f9b90786a6324f56e7086b91bf 100755 (executable)
@@ -18,6 +18,14 @@ test_split () {
        "
 }
 
+test_split_in_place() {
+       cat >expected &&
+       test_expect_success "split (in place) $1 at $2, max $3" "
+               test-tool string-list split_in_place '$1' '$2' '$3' >actual &&
+               test_cmp expected actual
+       "
+}
+
 test_split "foo:bar:baz" ":" "-1" <<EOF
 3
 [0]: "foo"
@@ -61,6 +69,49 @@ test_split ":" ":" "-1" <<EOF
 [1]: ""
 EOF
 
+test_split_in_place "foo:;:bar:;:baz:;:" ":;" "-1" <<EOF
+10
+[0]: "foo"
+[1]: ""
+[2]: ""
+[3]: "bar"
+[4]: ""
+[5]: ""
+[6]: "baz"
+[7]: ""
+[8]: ""
+[9]: ""
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "0" <<EOF
+1
+[0]: "foo:;:bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "1" <<EOF
+2
+[0]: "foo"
+[1]: ";:bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "2" <<EOF
+3
+[0]: "foo"
+[1]: ""
+[2]: ":bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:" ":;" "-1" <<EOF
+7
+[0]: "foo"
+[1]: ""
+[2]: ""
+[3]: "bar"
+[4]: ""
+[5]: ""
+[6]: ""
+EOF
+
 test_expect_success "test filter_string_list" '
        test "x-" = "x$(test-tool string-list filter - y)" &&
        test "x-" = "x$(test-tool string-list filter no y)" &&
index 04b811622bc4b1299095d7663928400679fc56be..7d0a0da8c01b8b69a9d9ca607c55145d96e59ad1 100755 (executable)
@@ -106,13 +106,7 @@ test_expect_success SYMLINKS 'setup dirs with symlinks' '
        ln -s d dir4/a/e &&
        ln -s ../b dir4/a/f &&
 
-       mkdir -p dir5/a/b &&
-       mkdir -p dir5/a/c &&
-       ln -s ../c dir5/a/b/d &&
-       ln -s ../ dir5/a/b/e &&
-       ln -s ../../ dir5/a/b/f &&
-
-       ln -s dir4 dir6
+       ln -s dir4 dir5
 '
 
 test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default' '
@@ -131,44 +125,10 @@ test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default
        test_cmp expected-no-follow-sorted-output actual-no-follow-sorted-output
 '
 
-test_expect_success SYMLINKS 'dir-iterator should follow symlinks w/ follow flag' '
-       cat >expected-follow-sorted-output <<-EOF &&
-       [d] (a) [a] ./dir4/a
-       [d] (a/f) [f] ./dir4/a/f
-       [d] (a/f/c) [c] ./dir4/a/f/c
-       [d] (b) [b] ./dir4/b
-       [d] (b/c) [c] ./dir4/b/c
-       [f] (a/d) [d] ./dir4/a/d
-       [f] (a/e) [e] ./dir4/a/e
-       EOF
-
-       test-tool dir-iterator --follow-symlinks ./dir4 >out &&
-       sort out >actual-follow-sorted-output &&
-
-       test_cmp expected-follow-sorted-output actual-follow-sorted-output
-'
-
 test_expect_success SYMLINKS 'dir-iterator does not resolve top-level symlinks' '
-       test_must_fail test-tool dir-iterator ./dir6 >out &&
+       test_must_fail test-tool dir-iterator ./dir5 >out &&
 
        grep "ENOTDIR" out
 '
 
-test_expect_success SYMLINKS 'dir-iterator resolves top-level symlinks w/ follow flag' '
-       cat >expected-follow-sorted-output <<-EOF &&
-       [d] (a) [a] ./dir6/a
-       [d] (a/f) [f] ./dir6/a/f
-       [d] (a/f/c) [c] ./dir6/a/f/c
-       [d] (b) [b] ./dir6/b
-       [d] (b/c) [c] ./dir6/b/c
-       [f] (a/d) [d] ./dir6/a/d
-       [f] (a/e) [e] ./dir6/a/e
-       EOF
-
-       test-tool dir-iterator --follow-symlinks ./dir6 >out &&
-       sort out >actual-follow-sorted-output &&
-
-       test_cmp expected-follow-sorted-output actual-follow-sorted-output
-'
-
 test_done
index 4675e852517688179c2783803ebfa56e730cd24c..4b90b74d5d515ec80b18b67e2b6f32780f860f22 100755 (executable)
@@ -2,15 +2,18 @@
 
 test_description='git for-each-repo builtin'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'run based on configured value' '
        git init one &&
        git init two &&
        git init three &&
+       git init ~/four &&
        git -C two commit --allow-empty -m "DID NOT RUN" &&
        git config run.key "$TRASH_DIRECTORY/one" &&
        git config --add run.key "$TRASH_DIRECTORY/three" &&
+       git config --add run.key "~/four" &&
        git for-each-repo --config=run.key commit --allow-empty -m "ran" &&
        git -C one log -1 --pretty=format:%s >message &&
        grep ran message &&
@@ -18,12 +21,16 @@ test_expect_success 'run based on configured value' '
        ! grep ran message &&
        git -C three log -1 --pretty=format:%s >message &&
        grep ran message &&
+       git -C ~/four log -1 --pretty=format:%s >message &&
+       grep ran message &&
        git for-each-repo --config=run.key -- commit --allow-empty -m "ran again" &&
        git -C one log -1 --pretty=format:%s >message &&
        grep again message &&
        git -C two log -1 --pretty=format:%s >message &&
        ! grep again message &&
        git -C three log -1 --pretty=format:%s >message &&
+       grep again message &&
+       git -C ~/four log -1 --pretty=format:%s >message &&
        grep again message
 '
 
@@ -33,4 +40,23 @@ test_expect_success 'do nothing on empty config' '
        git for-each-repo --config=bogus.config -- help --no-such-option
 '
 
+test_expect_success 'error on bad config keys' '
+       test_expect_code 129 git for-each-repo --config=a &&
+       test_expect_code 129 git for-each-repo --config=a.b. &&
+       test_expect_code 129 git for-each-repo --config="'\''.b"
+'
+
+test_expect_success 'error on NULL value for config keys' '
+       cat >>.git/config <<-\EOF &&
+       [empty]
+               key
+       EOF
+       cat >expect <<-\EOF &&
+       error: missing value for '\''empty.key'\''
+       EOF
+       test_expect_code 129 git for-each-repo --config=empty.key 2>actual.raw &&
+       grep ^error actual.raw >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 8d59905ef099bf0ab46d0681568dd17d7ed85d98..f18f9284a5bf3961a3f5373f887b808e55cd9b75 100755 (executable)
@@ -6,6 +6,7 @@ test_description='check that the most basic functions work
 Verify wrappers and compatibility functions.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'character classes (isspace, isalpha etc.)' '
@@ -43,13 +44,71 @@ test_expect_success 'incomplete sideband messages are reassembled' '
 test_expect_success 'eof on sideband message is reported' '
        printf 1234 >input &&
        test-tool pkt-line receive-sideband <input 2>err &&
-       test_i18ngrep "unexpected disconnect" err
+       test_grep "unexpected disconnect" err
 '
 
 test_expect_success 'missing sideband designator is reported' '
        printf 0004 >input &&
        test-tool pkt-line receive-sideband <input 2>err &&
-       test_i18ngrep "missing sideband" err
+       test_grep "missing sideband" err
+'
+
+test_expect_success 'unpack-sideband: --no-chomp-newline' '
+       test_when_finished "rm -f expect-out expect-err" &&
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line unpack-sideband \
+               --no-chomp-newline <split-sideband >out 2>err &&
+       cat >expect-out <<-EOF &&
+               primary: regular output
+       EOF
+       cat >expect-err <<-EOF &&
+               Foo.
+               Bar.
+               Hello, world!
+       EOF
+       test_cmp expect-out out &&
+       test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: --chomp-newline (default)' '
+       test_when_finished "rm -f expect-out expect-err" &&
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line unpack-sideband \
+               --chomp-newline <split-sideband >out 2>err &&
+       printf "primary: regular output" >expect-out &&
+       printf "Foo.Bar.Hello, world!" >expect-err &&
+       test_cmp expect-out out &&
+       test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: packet_reader_read() consumes sideband, no chomp payload' '
+       test_when_finished "rm -f expect-out expect-err" &&
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line unpack-sideband \
+               --reader-use-sideband \
+               --no-chomp-newline <split-sideband >out 2>err &&
+       cat >expect-out <<-EOF &&
+               primary: regular output
+       EOF
+       printf "remote: Foo.        \n"           >expect-err &&
+       printf "remote: Bar.        \n"          >>expect-err &&
+       printf "remote: Hello, world!        \n" >>expect-err &&
+       test_cmp expect-out out &&
+       test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: packet_reader_read() consumes sideband, chomp payload' '
+       test_when_finished "rm -f expect-out expect-err" &&
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line unpack-sideband \
+               --reader-use-sideband \
+               --chomp-newline <split-sideband >out 2>err &&
+       printf "primary: regular output" >expect-out &&
+       printf "remote: Foo.        \n"           >expect-err &&
+       printf "remote: Bar.        \n"          >>expect-err &&
+       printf "remote: Hello, world!        \n" >>expect-err &&
+       test_cmp expect-out out &&
+       test_cmp expect-err err
 '
 
 test_done
diff --git a/t/t0080-unit-test-output.sh b/t/t0080-unit-test-output.sh
new file mode 100755 (executable)
index 0000000..961b54b
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='Test the output of the unit test framework'
+
+. ./test-lib.sh
+
+test_expect_success 'TAP output from unit tests' '
+       cat >expect <<-EOF &&
+       ok 1 - passing test
+       ok 2 - passing test and assertion return 1
+       # check "1 == 2" failed at t/unit-tests/t-basic.c:76
+       #    left: 1
+       #   right: 2
+       not ok 3 - failing test
+       ok 4 - failing test and assertion return 0
+       not ok 5 - passing TEST_TODO() # TODO
+       ok 6 - passing TEST_TODO() returns 1
+       # todo check ${SQ}check(x)${SQ} succeeded at t/unit-tests/t-basic.c:25
+       not ok 7 - failing TEST_TODO()
+       ok 8 - failing TEST_TODO() returns 0
+       # check "0" failed at t/unit-tests/t-basic.c:30
+       # skipping test - missing prerequisite
+       # skipping check ${SQ}1${SQ} at t/unit-tests/t-basic.c:32
+       ok 9 - test_skip() # SKIP
+       ok 10 - skipped test returns 1
+       # skipping test - missing prerequisite
+       ok 11 - test_skip() inside TEST_TODO() # SKIP
+       ok 12 - test_skip() inside TEST_TODO() returns 1
+       # check "0" failed at t/unit-tests/t-basic.c:48
+       not ok 13 - TEST_TODO() after failing check
+       ok 14 - TEST_TODO() after failing check returns 0
+       # check "0" failed at t/unit-tests/t-basic.c:56
+       not ok 15 - failing check after TEST_TODO()
+       ok 16 - failing check after TEST_TODO() returns 0
+       # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/unit-tests/t-basic.c:61
+       #    left: "\011hello\\\\"
+       #   right: "there\"\012"
+       # check "!strcmp("NULL", NULL)" failed at t/unit-tests/t-basic.c:62
+       #    left: "NULL"
+       #   right: NULL
+       # check "${SQ}a${SQ} == ${SQ}\n${SQ}" failed at t/unit-tests/t-basic.c:63
+       #    left: ${SQ}a${SQ}
+       #   right: ${SQ}\012${SQ}
+       # check "${SQ}\\\\${SQ} == ${SQ}\\${SQ}${SQ}" failed at t/unit-tests/t-basic.c:64
+       #    left: ${SQ}\\\\${SQ}
+       #   right: ${SQ}\\${SQ}${SQ}
+       not ok 17 - messages from failing string and char comparison
+       # BUG: test has no checks at t/unit-tests/t-basic.c:91
+       not ok 18 - test with no checks
+       ok 19 - test with no checks returns 0
+       1..19
+       EOF
+
+       ! "$GIT_BUILD_DIR"/t/unit-tests/bin/t-basic >actual &&
+       test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh
new file mode 100755 (executable)
index 0000000..67b1121
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+test_description='test `test-tool find-pack`'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+       test_commit four &&
+       test_commit five
+'
+
+test_expect_success 'repack everything into a single packfile' '
+       git repack -a -d --no-write-bitmap-index &&
+
+       head_commit_pack=$(test-tool find-pack HEAD) &&
+       head_tree_pack=$(test-tool find-pack HEAD^{tree}) &&
+       one_pack=$(test-tool find-pack HEAD:one.t) &&
+       three_pack=$(test-tool find-pack HEAD:three.t) &&
+       old_commit_pack=$(test-tool find-pack HEAD~4) &&
+
+       test-tool find-pack --check-count 1 HEAD &&
+       test-tool find-pack --check-count=1 HEAD^{tree} &&
+       ! test-tool find-pack --check-count=0 HEAD:one.t &&
+       ! test-tool find-pack -c 2 HEAD:one.t &&
+       test-tool find-pack -c 1 HEAD:three.t &&
+
+       # Packfile exists at the right path
+       case "$head_commit_pack" in
+               ".git/objects/pack/pack-"*".pack") true ;;
+               *) false ;;
+       esac &&
+       test -f "$head_commit_pack" &&
+
+       # Everything is in the same pack
+       test "$head_commit_pack" = "$head_tree_pack" &&
+       test "$head_commit_pack" = "$one_pack" &&
+       test "$head_commit_pack" = "$three_pack" &&
+       test "$head_commit_pack" = "$old_commit_pack"
+'
+
+test_expect_success 'add more packfiles' '
+       git rev-parse HEAD^{tree} HEAD:two.t HEAD:four.t >objects &&
+       git pack-objects .git/objects/pack/mypackname1 >packhash1 <objects &&
+
+       git rev-parse HEAD~ HEAD~^{tree} HEAD:five.t >objects &&
+       git pack-objects .git/objects/pack/mypackname2 >packhash2 <objects &&
+
+       head_commit_pack=$(test-tool find-pack HEAD) &&
+
+       # HEAD^{tree} is in 2 packfiles
+       test-tool find-pack HEAD^{tree} >head_tree_packs &&
+       grep "$head_commit_pack" head_tree_packs &&
+       grep mypackname1 head_tree_packs &&
+       ! grep mypackname2 head_tree_packs &&
+       test-tool find-pack --check-count 2 HEAD^{tree} &&
+       ! test-tool find-pack --check-count 1 HEAD^{tree} &&
+
+       # HEAD:five.t is also in 2 packfiles
+       test-tool find-pack HEAD:five.t >five_packs &&
+       grep "$head_commit_pack" five_packs &&
+       ! grep mypackname1 five_packs &&
+       grep mypackname2 five_packs &&
+       test-tool find-pack -c 2 HEAD:five.t &&
+       ! test-tool find-pack --check-count=0 HEAD:five.t
+'
+
+test_expect_success 'add more commits (as loose objects)' '
+       test_commit six &&
+       test_commit seven &&
+
+       test -z "$(test-tool find-pack HEAD)" &&
+       test -z "$(test-tool find-pack HEAD:six.t)" &&
+       test-tool find-pack --check-count 0 HEAD &&
+       test-tool find-pack -c 0 HEAD:six.t &&
+       ! test-tool find-pack -c 1 HEAD:seven.t
+'
+
+test_done
index b6d2f591acdd999483b2d28af8bbbe3d30008ea7..8798feefe3f6700ee90d3854299802a60536df7d 100755 (executable)
@@ -5,29 +5,50 @@ test_description='git bugreport'
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
-# Headers "[System Info]" will be followed by a non-empty line if we put some
-# information there; we can make sure all our headers were followed by some
-# information to check if the command was successful.
-HEADER_PATTERN="^\[.*\]$"
-
-check_all_headers_populated () {
-       while read -r line
-       do
-               if test "$(grep "$HEADER_PATTERN" "$line")"
-               then
-                       echo "$line"
-                       read -r nextline
-                       if test -z "$nextline"; then
-                               return 1;
-                       fi
-               fi
-       done
-}
-
-test_expect_success 'creates a report with content in the right places' '
-       test_when_finished rm git-bugreport-check-headers.txt &&
-       git bugreport -s check-headers &&
-       check_all_headers_populated <git-bugreport-check-headers.txt
+test_expect_success 'create a report' '
+       git bugreport -s format &&
+       test_file_not_empty git-bugreport-format.txt
+'
+
+test_expect_success 'report contains wanted template (before first section)' '
+       sed -ne "/^\[/q;p" git-bugreport-format.txt >actual &&
+       cat >expect <<-\EOF &&
+       Thank you for filling out a Git bug report!
+       Please answer the following questions to help us understand your issue.
+
+       What did you do before the bug happened? (Steps to reproduce your issue)
+
+       What did you expect to happen? (Expected behavior)
+
+       What happened instead? (Actual behavior)
+
+       What'\''s different between what you expected and what actually happened?
+
+       Anything else you want to add:
+
+       Please review the rest of the bug report below.
+       You can delete any lines you don'\''t wish to share.
+
+
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'sanity check "System Info" section' '
+       test_when_finished rm -f git-bugreport-format.txt &&
+
+       sed -ne "/^\[System Info\]$/,/^$/p" <git-bugreport-format.txt >system &&
+
+       # The beginning should match "git version --build-info" verbatim,
+       # but rather than checking bit-for-bit equality, just test some basics.
+       grep "git version [0-9]." system &&
+       grep "shell-path: ." system &&
+
+       # After the version, there should be some more info.
+       # This is bound to differ from environment to environment,
+       # so we just do some rather high-level checks.
+       grep "uname: ." system &&
+       grep "compiler info: ." system
 '
 
 test_expect_success 'dies if file with same name as report already exists' '
@@ -44,7 +65,14 @@ test_expect_success '--output-directory puts the report in the provided dir' '
 
 test_expect_success 'incorrect arguments abort with usage' '
        test_must_fail git bugreport --false 2>output &&
-       test_i18ngrep usage output &&
+       test_grep usage output &&
+       test_path_is_missing git-bugreport-*
+'
+
+test_expect_success 'incorrect positional arguments abort with usage and hint' '
+       test_must_fail git bugreport false 2>output &&
+       test_grep usage output &&
+       test_grep false output &&
        test_path_is_missing git-bugreport-*
 '
 
index a16cc3d298357be89e58514db83c1d3f04b3ad4d..70a3223f2199b06833166863e58489ba0f632e59 100755 (executable)
@@ -12,7 +12,9 @@ test_expect_success 'branch -d @{-1}' '
        test_commit A &&
        git checkout -b junk &&
        git checkout - &&
-       test "$(git symbolic-ref HEAD)" = refs/heads/main &&
+       echo refs/heads/main >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
        git branch -d @{-1} &&
        test_must_fail git rev-parse --verify refs/heads/junk
 '
@@ -21,7 +23,9 @@ test_expect_success 'branch -d @{-12} when there is not enough switches yet' '
        git reflog expire --expire=now &&
        git checkout -b junk2 &&
        git checkout - &&
-       test "$(git symbolic-ref HEAD)" = refs/heads/main &&
+       echo refs/heads/main >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
        test_must_fail git branch -d @{-12} &&
        git rev-parse --verify refs/heads/main
 '
index 2cbf7b95907384b4b4b5ac91f801026810b85f0a..47d96a2a13f93b235f6f3d0c9f8f06922f6ee872 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/perl
-use 5.008;
+use 5.008001;
 use lib (split(/:/, $ENV{GITPERLLIB}));
 use strict;
 use warnings;
index 80e76a4695ed8d5abbaf8deb1ea2a55d2f535d41..c312657a12cd34334ce58361c7f0a73f384daff6 100755 (executable)
@@ -2,7 +2,7 @@
 
 test_description='test trace2 facility (normal target)'
 
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
@@ -283,4 +283,22 @@ test_expect_success 'using global config with include' '
        test_cmp expect actual
 '
 
+test_expect_success 'unsafe URLs are redacted by default' '
+       test_when_finished \
+               "rm -r trace.normal unredacted.normal clone clone2" &&
+
+       test_config_global \
+               "url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+       test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+       GIT_TRACE2="$(pwd)/trace.normal" \
+               git clone https://user:pwd@example.com/ clone &&
+       ! grep user:pwd trace.normal &&
+
+       GIT_TRACE2_REDACT=0 GIT_TRACE2="$(pwd)/unredacted.normal" \
+               git clone https://user:pwd@example.com/ clone2 &&
+       grep "start .* clone https://user:pwd@example.com" unredacted.normal &&
+       grep "remote.origin.url=https://user:pwd@example.com" unredacted.normal
+'
+
 test_done
index 22d0845544e97a8ae960759ffe027313d1b5acdc..290b6eaaab16052b84ca3c4e5527c8910f67ebf0 100755 (executable)
@@ -2,7 +2,7 @@
 
 test_description='test trace2 facility (perf target)'
 
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
@@ -173,4 +173,118 @@ test_expect_success 'using global config, perf stream, return code 0' '
        test_cmp expect actual
 '
 
+# Exercise the stopwatch timers in a loop and confirm that we have
+# as many start/stop intervals as expected.  We cannot really test the
+# actual (total, min, max) timer values, so we have to assume that they
+# are good, but we can verify the interval count.
+#
+# The timer "test/test1" should only emit a global summary "timer" event.
+# The timer "test/test2" should emit per-thread "th_timer" events and a
+# global summary "timer" event.
+
+have_timer_event () {
+       thread=$1 event=$2 category=$3 name=$4 intervals=$5 file=$6 &&
+
+       pattern="d0|${thread}|${event}||||${category}|name:${name} intervals:${intervals}" &&
+
+       grep "${pattern}" ${file}
+}
+
+test_expect_success 'stopwatch timer test/test1' '
+       test_when_finished "rm trace.perf actual" &&
+       test_config_global trace2.perfBrief 1 &&
+       test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+       # Use the timer "test1" 5 times from "main".
+       test-tool trace2 100timer 5 10 &&
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+       have_timer_event "main" "timer" "test" "test1" 5 actual
+'
+
+test_expect_success PTHREADS 'stopwatch timer test/test2' '
+       test_when_finished "rm trace.perf actual" &&
+       test_config_global trace2.perfBrief 1 &&
+       test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+       # Use the timer "test2" 5 times each in 3 threads.
+       test-tool trace2 101timer 5 10 3 &&
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+       # So we should have 3 per-thread events of 5 each.
+       have_timer_event "th01:ut_101" "th_timer" "test" "test2" 5 actual &&
+       have_timer_event "th02:ut_101" "th_timer" "test" "test2" 5 actual &&
+       have_timer_event "th03:ut_101" "th_timer" "test" "test2" 5 actual &&
+
+       # And we should have 15 total uses.
+       have_timer_event "main" "timer" "test" "test2" 15 actual
+'
+
+# Exercise the global counters and confirm that we get the expected values.
+#
+# The counter "test/test1" should only emit a global summary "counter" event.
+# The counter "test/test2" could emit per-thread "th_counter" events and a
+# global summary "counter" event.
+
+have_counter_event () {
+       thread=$1 event=$2 category=$3 name=$4 value=$5 file=$6 &&
+
+       pattern="d0|${thread}|${event}||||${category}|name:${name} value:${value}" &&
+
+       grep "${patern}" ${file}
+}
+
+test_expect_success 'global counter test/test1' '
+       test_when_finished "rm trace.perf actual" &&
+       test_config_global trace2.perfBrief 1 &&
+       test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+       # Use the counter "test1" and add n integers.
+       test-tool trace2 200counter 1 2 3 4 5 &&
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+       have_counter_event "main" "counter" "test" "test1" 15 actual
+'
+
+test_expect_success PTHREADS 'global counter test/test2' '
+       test_when_finished "rm trace.perf actual" &&
+       test_config_global trace2.perfBrief 1 &&
+       test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+       # Add 2 integers to the counter "test2" in each of 3 threads.
+       test-tool trace2 201counter 7 13 3 &&
+
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+       # So we should have 3 per-thread events of 5 each.
+       have_counter_event "th01:ut_201" "th_counter" "test" "test2" 20 actual &&
+       have_counter_event "th02:ut_201" "th_counter" "test" "test2" 20 actual &&
+       have_counter_event "th03:ut_201" "th_counter" "test" "test2" 20 actual &&
+
+       # And we should have a single event with the total across all threads.
+       have_counter_event "main" "counter" "test" "test2" 60 actual
+'
+
+test_expect_success 'unsafe URLs are redacted by default' '
+       test_when_finished \
+               "rm -r actual trace.perf unredacted.perf clone clone2" &&
+
+       test_config_global \
+               "url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+       test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+       GIT_TRACE2_PERF="$(pwd)/trace.perf" \
+               git clone https://user:pwd@example.com/ clone &&
+       ! grep user:pwd trace.perf &&
+
+       GIT_TRACE2_REDACT=0 GIT_TRACE2_PERF="$(pwd)/unredacted.perf" \
+               git clone https://user:pwd@example.com/ clone2 &&
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <unredacted.perf >actual &&
+       grep "d0|main|start|.* clone https://user:pwd@example.com" actual &&
+       grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual
+'
+
 test_done
index 299999f0f896e85de175819568e4ff7662a3ef03..7a50bae6463f4af8006821c951d2d99a6a650e78 100644 (file)
@@ -64,6 +64,12 @@ while (<>) {
            goto SKIP_LINE;
        }
     }
+    elsif ($tokens[$col_event] =~ m/timer/) {
+       # This also captures "th_timer" events
+       $tokens[$col_rest] =~ s/ total:\d+\.\d*/ total:_T_TOTAL_/;
+       $tokens[$col_rest] =~ s/ min:\d+\.\d*/ min:_T_MIN_/;
+       $tokens[$col_rest] =~ s/ max:\d+\.\d*/ max:_T_MAX_/;
+    }
 
     # t_abs and t_rel are either blank or a float.  Replace the float
     # with a constant for matching the HEREDOC in the test script.
index 6d3374ff773c1ef3b612b2a0df9aa9df4525790e..147643d582643efebf794000fa48e3d460c76173 100755 (executable)
@@ -323,4 +323,44 @@ test_expect_success 'discard traces when there are too many files' '
        head -n2 trace_target_dir/git-trace2-discard | tail -n1 | grep \"event\":\"too_many_files\"
 '
 
+# In the following "...redact..." tests, skip testing the GIT_TRACE2_REDACT=0
+# case because we would need to exactly model the full JSON event stream like
+# we did in the basic tests above and I do not think it is worth it.
+
+test_expect_success 'unsafe URLs are redacted by default in cmd_start events' '
+       test_when_finished \
+               "rm -r trace.event" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+               test-tool trace2 300redact_start git clone https://user:pwd@example.com/ clone2 &&
+       ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in child_start events' '
+       test_when_finished \
+               "rm -r trace.event" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+               test-tool trace2 301redact_child_start git clone https://user:pwd@example.com/ clone2 &&
+       ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in exec events' '
+       test_when_finished \
+               "rm -r trace.event" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+               test-tool trace2 302redact_exec git clone https://user:pwd@example.com/ clone2 &&
+       ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in def_param events' '
+       test_when_finished \
+               "rm -r trace.event" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+               test-tool trace2 303redact_def_param url https://user:pwd@example.com/ &&
+       ! grep user:pwd trace.event
+'
+
 test_done
index 3485c0534e6d39be9ef6f285fa2ec80400dc47cd..400f6bdbca13c2a93cb8521e21e08fbd4e1eb3bd 100755 (executable)
@@ -35,6 +35,16 @@ test_expect_success 'setup helper scripts' '
        test -z "$pass" || echo password=$pass
        EOF
 
+       write_script git-credential-verbatim-with-expiry <<-\EOF &&
+       user=$1; shift
+       pass=$1; shift
+       pexpiry=$1; shift
+       . ./dump
+       test -z "$user" || echo username=$user
+       test -z "$pass" || echo password=$pass
+       test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
+       EOF
+
        PATH="$PWD:$PATH"
 '
 
@@ -109,6 +119,43 @@ test_expect_success 'credential_fill continues through partial response' '
        EOF
 '
 
+test_expect_success 'credential_fill populates password_expiry_utc' '
+       check fill "verbatim-with-expiry one two 9999999999" <<-\EOF
+       protocol=http
+       host=example.com
+       --
+       protocol=http
+       host=example.com
+       username=one
+       password=two
+       password_expiry_utc=9999999999
+       --
+       verbatim-with-expiry: get
+       verbatim-with-expiry: protocol=http
+       verbatim-with-expiry: host=example.com
+       EOF
+'
+
+test_expect_success 'credential_fill ignores expired password' '
+       check fill "verbatim-with-expiry one two 5" "verbatim three four" <<-\EOF
+       protocol=http
+       host=example.com
+       --
+       protocol=http
+       host=example.com
+       username=three
+       password=four
+       --
+       verbatim-with-expiry: get
+       verbatim-with-expiry: protocol=http
+       verbatim-with-expiry: host=example.com
+       verbatim: get
+       verbatim: protocol=http
+       verbatim: host=example.com
+       verbatim: username=one
+       EOF
+'
+
 test_expect_success 'credential_fill passes along metadata' '
        check fill "verbatim one two" <<-\EOF
        protocol=ftp
@@ -149,6 +196,42 @@ test_expect_success 'credential_approve calls all helpers' '
        EOF
 '
 
+test_expect_success 'credential_approve stores password expiry' '
+       check approve useless <<-\EOF
+       protocol=http
+       host=example.com
+       username=foo
+       password=bar
+       password_expiry_utc=9999999999
+       --
+       --
+       useless: store
+       useless: protocol=http
+       useless: host=example.com
+       useless: username=foo
+       useless: password=bar
+       useless: password_expiry_utc=9999999999
+       EOF
+'
+
+test_expect_success 'credential_approve stores oauth refresh token' '
+       check approve useless <<-\EOF
+       protocol=http
+       host=example.com
+       username=foo
+       password=bar
+       oauth_refresh_token=xyzzy
+       --
+       --
+       useless: store
+       useless: protocol=http
+       useless: host=example.com
+       useless: username=foo
+       useless: password=bar
+       useless: oauth_refresh_token=xyzzy
+       EOF
+'
+
 test_expect_success 'do not bother storing password-less credential' '
        check approve useless <<-\EOF
        protocol=http
@@ -159,6 +242,17 @@ test_expect_success 'do not bother storing password-less credential' '
        EOF
 '
 
+test_expect_success 'credential_approve does not store expired password' '
+       check approve useless <<-\EOF
+       protocol=http
+       host=example.com
+       username=foo
+       password=bar
+       password_expiry_utc=5
+       --
+       --
+       EOF
+'
 
 test_expect_success 'credential_reject calls all helpers' '
        check reject useless "verbatim one two" <<-\EOF
@@ -181,6 +275,24 @@ test_expect_success 'credential_reject calls all helpers' '
        EOF
 '
 
+test_expect_success 'credential_reject erases credential regardless of expiry' '
+       check reject useless <<-\EOF
+       protocol=http
+       host=example.com
+       username=foo
+       password=bar
+       password_expiry_utc=5
+       --
+       --
+       useless: erase
+       useless: protocol=http
+       useless: host=example.com
+       useless: username=foo
+       useless: password=bar
+       useless: password_expiry_utc=5
+       EOF
+'
+
 test_expect_success 'usernames can be preserved' '
        check fill "verbatim \"\" three" <<-\EOF
        protocol=http
@@ -714,8 +826,8 @@ test_expect_success 'credential config with partial URLs' '
 
        git -c credential.$partial.helper=yep \
                -c credential.with%0anewline.username=uh-oh \
-               credential fill <stdin >stdout 2>stderr &&
-       test_i18ngrep "skipping credential lookup for key" stderr
+               credential fill <stdin 2>stderr &&
+       test_grep "skipping credential lookup for key" stderr
 '
 
 test_done
index 698b7159f030905fb63ce4f6856d7ecf49f50b02..8300faadea9a76f19e3d2c82f5ff600f38bfe18f 100755 (executable)
@@ -29,6 +29,8 @@ test_atexit 'git credential-cache exit'
 
 # test that the daemon works with no special setup
 helper_test cache
+helper_test_password_expiry_utc cache
+helper_test_oauth_refresh_token cache
 
 test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
        test_when_finished "
index f028fd1418285107a90e170a6ea1cd7657e31bb8..095574bfc6edf2aaf835b2ff43bb8cd35f792591 100755 (executable)
@@ -45,6 +45,8 @@ test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
 helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
 
 helper_test "$GIT_TEST_CREDENTIAL_HELPER"
+helper_test_password_expiry_utc "$GIT_TEST_CREDENTIAL_HELPER"
+helper_test_oauth_refresh_token "$GIT_TEST_CREDENTIAL_HELPER"
 
 if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then
        say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)"
index 1e864cf3172bec45e949a840d026e507a117fec8..6b6424b3df17135666b332b7857ec8382c813ece 100755 (executable)
@@ -49,7 +49,7 @@ test_expect_success 'convert shallow clone to partial clone' '
        test_cmp_config -C client 1 core.repositoryformatversion
 '
 
-test_expect_success SHA1 'convert to partial clone with noop extension' '
+test_expect_success SHA1,REFFILES 'convert to partial clone with noop extension' '
        rm -fr server client &&
        test_create_repo server &&
        test_commit -C server my_commit 1 &&
@@ -60,7 +60,7 @@ test_expect_success SHA1 'convert to partial clone with noop extension' '
        git -C client fetch --unshallow --filter="blob:none"
 '
 
-test_expect_success SHA1 'converting to partial clone fails with unrecognized extension' '
+test_expect_success SHA1,REFFILES 'converting to partial clone fails with unrecognized extension' '
        rm -fr server client &&
        test_create_repo server &&
        test_commit -C server my_commit 1 &&
@@ -215,6 +215,20 @@ test_expect_success 'fetching of missing objects' '
        grep "$HASH" out
 '
 
+test_expect_success 'fetching of a promised object that promisor remote no longer has' '
+       rm -f err &&
+       test_create_repo unreliable-server &&
+       git -C unreliable-server config uploadpack.allowanysha1inwant 1 &&
+       git -C unreliable-server config uploadpack.allowfilter 1 &&
+       test_commit -C unreliable-server foo &&
+
+       git clone --filter=blob:none --no-checkout "file://$(pwd)/unreliable-server" unreliable-client &&
+
+       rm -rf unreliable-server/.git/objects/* &&
+       test_must_fail git -C unreliable-client checkout HEAD 2>err &&
+       grep "could not fetch.*from promisor remote" err
+'
+
 test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
        # ref-in-want requires protocol version 2
        git -C server config protocol.version 2 &&
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
new file mode 100755 (executable)
index 0000000..cd3969e
--- /dev/null
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+test_description='assert (unbuilt) Documentation/*.txt 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.'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+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
+       then
+               BUG "please keep the list of txt and help mismatches sorted"
+       fi
+'
+
+help_to_synopsis () {
+       builtin="$1" &&
+       out_dir="out/$builtin" &&
+       out="$out_dir/help.synopsis" &&
+       if test -f "$out"
+       then
+               echo "$out" &&
+               return 0
+       fi &&
+       mkdir -p "$out_dir" &&
+       test_expect_code 129 git $builtin -h >"$out.raw" 2>&1 &&
+       sed -n \
+               -e '1,/^$/ {
+                       /^$/d;
+                       s/^usage: //;
+                       s/^ *or: //;
+                       p;
+               }' <"$out.raw" >"$out" &&
+       echo "$out"
+}
+
+builtin_to_txt () {
+       echo "$GIT_BUILD_DIR/Documentation/git-$1.txt"
+}
+
+txt_to_synopsis () {
+       builtin="$1" &&
+       out_dir="out/$builtin" &&
+       out="$out_dir/txt.synopsis" &&
+       if test -f "$out"
+       then
+               echo "$out" &&
+               return 0
+       fi &&
+       b2t="$(builtin_to_txt "$builtin")" &&
+       sed -n \
+               -e '/^\[verse\]$/,/^$/ {
+                       /^$/d;
+                       /^\[verse\]$/d;
+
+                       s/{litdd}/--/g;
+                       s/'\''\(git[ a-z-]*\)'\''/\1/g;
+
+                       p;
+               }' \
+               <"$b2t" >"$out" &&
+       echo "$out"
+}
+
+check_dashed_labels () {
+       ! grep -E "<[^>_-]+_" "$1"
+}
+
+HT="   "
+
+align_after_nl () {
+       builtin="$1" &&
+       len=$(printf "git %s " "$builtin" | wc -c) &&
+       pad=$(printf "%${len}s" "") &&
+
+       sed "s/^[ $HT][ $HT]*/$pad/"
+}
+
+test_debug '>failing'
+while read builtin
+do
+       # -h output assertions
+       test_expect_success "$builtin -h output has no \t" '
+               h2s="$(help_to_synopsis "$builtin")" &&
+               ! grep "$HT" "$h2s"
+       '
+
+       test_expect_success "$builtin -h output has dashed labels" '
+               check_dashed_labels "$(help_to_synopsis "$builtin")"
+       '
+
+       test_expect_success "$builtin -h output has consistent spacing" '
+               h2s="$(help_to_synopsis "$builtin")" &&
+               sed -n \
+                       -e "/^ / {
+                               s/[^ ].*//;
+                               p;
+                       }" \
+                       <"$h2s" >help &&
+               sort -u help >help.ws &&
+               if test -s help.ws
+               then
+                       test_line_count = 1 help.ws
+               fi
+       '
+
+       txt="$(builtin_to_txt "$builtin")" &&
+       preq="$(echo BUILTIN_TXT_$builtin | tr '[:lower:]-' '[:upper:]_')" &&
+
+       if test -f "$txt"
+       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")"
+       '
+
+       # *.txt output consistency assertions
+       result=
+       if grep -q "^$builtin$" "$TEST_DIRECTORY"/t0450/txt-help-mismatches
+       then
+               result=failure
+       else
+               result=success
+       fi &&
+       test_expect_$result "$preq" "$builtin -h output and SYNOPSIS agree" '
+               t2s="$(txt_to_synopsis "$builtin")" &&
+               if test "$builtin" = "merge-tree"
+               then
+                       test_when_finished "rm -f t2s.new" &&
+                       sed -e '\''s/ (deprecated)$//g'\'' <"$t2s" >t2s.new
+                       t2s=t2s.new
+               fi &&
+               h2s="$(help_to_synopsis "$builtin")" &&
+
+               # The *.txt and -h use different spacing for the
+               # alignment of continued usage output, normalize it.
+               align_after_nl "$builtin" <"$t2s" >txt &&
+               align_after_nl "$builtin" <"$h2s" >help &&
+               test_cmp txt help
+       '
+
+       if test_have_prereq "$preq" && test -e txt && test -e help
+       then
+               test_debug '
+                       if test_cmp txt help >cmp 2>/dev/null
+                       then
+                               echo "=== DONE: $builtin ==="
+                       else
+                               echo "=== TODO: $builtin ===" &&
+                               cat cmp
+                       fi >>failing
+               '
+
+               # Not in test_expect_success in case --run is being
+               # used with --debug
+               rm -f txt help tmp 2>/dev/null
+       fi
+done <builtins
+
+test_debug 'say "$(cat failing)"'
+
+test_done
diff --git a/t/t0450/txt-help-mismatches b/t/t0450/txt-help-mismatches
new file mode 100644 (file)
index 0000000..a0777ac
--- /dev/null
@@ -0,0 +1,58 @@
+add
+am
+apply
+archive
+bisect
+blame
+branch
+check-ref-format
+checkout
+checkout-index
+clone
+column
+config
+credential
+credential-cache
+credential-store
+fast-export
+fast-import
+fetch-pack
+fmt-merge-msg
+for-each-ref
+format-patch
+fsck-objects
+fsmonitor--daemon
+gc
+grep
+index-pack
+init-db
+log
+ls-files
+ls-tree
+mailinfo
+mailsplit
+maintenance
+merge
+merge-file
+merge-index
+merge-one-file
+multi-pack-index
+name-rev
+notes
+pack-objects
+push
+range-diff
+rebase
+remote
+remote-ext
+remote-fd
+repack
+reset
+restore
+rev-parse
+show
+stage
+switch
+update-index
+update-ref
+whatchanged
index 516a6112fdcf1907fb3827cc23e7ad0b24b39099..88c524f6558eedd0775da7500d84a90ba19edeba 100755 (executable)
@@ -26,7 +26,7 @@ TEST_PASSES_SANITIZE_LEAK=true
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
 read_tree_twoway () {
-    git read-tree -m "$1" "$2" && git ls-files --stage
+       git read-tree -m "$1" "$2" && git ls-files --stage
 }
 
 compare_change () {
@@ -370,7 +370,7 @@ test_expect_success 'read-tree supports the super-prefix' '
        cat <<-EOF >expect &&
                error: Updating '\''fictional/a'\'' would lose untracked files in it
        EOF
-       test_must_fail git --super-prefix fictional/ read-tree -u -m "$treeH" "$treeM" 2>actual &&
+       test_must_fail git read-tree --super-prefix fictional/ -u -m "$treeH" "$treeM" 2>actual &&
        test_cmp expect actual
 '
 
index bd5313caec9240b52bf9e8d663fd0c515d321064..a7c2ed0d7c0a31180e6af1abe9dcc6366e62e2c5 100755 (executable)
@@ -37,315 +37,312 @@ check_cache_at () {
        esac
 }
 
-test_expect_success \
-    setup \
-    'echo frotz >frotz &&
-     echo nitfol >nitfol &&
-     echo bozbar >bozbar &&
-     echo rezrov >rezrov &&
-     git update-index --add nitfol bozbar rezrov &&
-     treeH=$(git write-tree) &&
-     echo treeH $treeH &&
-     git ls-tree $treeH &&
-
-     echo gnusto >bozbar &&
-     git update-index --add frotz bozbar --force-remove rezrov &&
-     git ls-files --stage >M.out &&
-     treeM=$(git write-tree) &&
-     echo treeM $treeM &&
-     git ls-tree $treeM &&
-     cp bozbar bozbar.M &&
-     cp frotz frotz.M &&
-     cp nitfol nitfol.M &&
-     git diff-tree $treeH $treeM'
-
-test_expect_success \
-    '1, 2, 3 - no carry forward' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >1-3.out &&
-     cmp M.out 1-3.out &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     check_cache_at bozbar clean &&
-     check_cache_at frotz clean &&
-     check_cache_at nitfol clean'
-
-test_expect_success \
-    '4 - carry forward local addition.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo "+100644 X 0 yomin" >expected &&
-     echo yomin >yomin &&
-     git update-index --add yomin &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >4.out &&
-     test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
-     compare_change 4diff.out expected &&
-     check_cache_at yomin clean &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     echo yomin >yomin1 &&
-     diff yomin yomin1 &&
-     rm -f yomin1'
-
-test_expect_success \
-    '5 - carry forward local addition.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     read_tree_u_must_succeed -m -u $treeH &&
-     echo yomin >yomin &&
-     git update-index --add yomin &&
-     echo yomin yomin >yomin &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >5.out &&
-     test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
-     compare_change 5diff.out expected &&
-     check_cache_at yomin dirty &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     : dirty index should have prevented -u from checking it out. &&
-     echo yomin yomin >yomin1 &&
-     diff yomin yomin1 &&
-     rm -f yomin1'
-
-test_expect_success \
-    '6 - local addition already has the same.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo frotz >frotz &&
-     git update-index --add frotz &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >6.out &&
-     test_cmp M.out 6.out &&
-     check_cache_at frotz clean &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     echo frotz >frotz1 &&
-     diff frotz frotz1 &&
-     rm -f frotz1'
-
-test_expect_success \
-    '7 - local addition already has the same.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo frotz >frotz &&
-     git update-index --add frotz &&
-     echo frotz frotz >frotz &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >7.out &&
-     test_cmp M.out 7.out &&
-     check_cache_at frotz dirty &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp nitfol.M nitfol &&
-     : dirty index should have prevented -u from checking it out. &&
-     echo frotz frotz >frotz1 &&
-     diff frotz frotz1 &&
-     rm -f frotz1'
-
-test_expect_success \
-    '8 - conflicting addition.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo frotz frotz >frotz &&
-     git update-index --add frotz &&
-     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
-    '9 - conflicting addition.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo frotz frotz >frotz &&
-     git update-index --add frotz &&
-     echo frotz >frotz &&
-     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
-    '10 - path removed.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo rezrov >rezrov &&
-     git update-index --add rezrov &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >10.out &&
-     cmp M.out 10.out &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol
+test_expect_success setup '
+       echo frotz >frotz &&
+       echo nitfol >nitfol &&
+       echo bozbar >bozbar &&
+       echo rezrov >rezrov &&
+       git update-index --add nitfol bozbar rezrov &&
+       treeH=$(git write-tree) &&
+       echo treeH $treeH &&
+       git ls-tree $treeH &&
+
+       echo gnusto >bozbar &&
+       git update-index --add frotz bozbar --force-remove rezrov &&
+       git ls-files --stage >M.out &&
+       treeM=$(git write-tree) &&
+       echo treeM $treeM &&
+       git ls-tree $treeM &&
+       cp bozbar bozbar.M &&
+       cp frotz frotz.M &&
+       cp nitfol nitfol.M &&
+       git diff-tree $treeH $treeM
 '
 
-test_expect_success \
-    '11 - dirty path removed.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo rezrov >rezrov &&
-     git update-index --add rezrov &&
-     echo rezrov rezrov >rezrov &&
-     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
-    '12 - unmatching local changes being removed.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo rezrov rezrov >rezrov &&
-     git update-index --add rezrov &&
-     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
-    '13 - unmatching local changes being removed.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo rezrov rezrov >rezrov &&
-     git update-index --add rezrov &&
-     echo rezrov >rezrov &&
-     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+test_expect_success '1, 2, 3 - no carry forward' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >1-3.out &&
+       cmp M.out 1-3.out &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       test_cmp nitfol.M nitfol &&
+       check_cache_at bozbar clean &&
+       check_cache_at frotz clean &&
+       check_cache_at nitfol clean
+'
+
+test_expect_success '4 - carry forward local addition.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo "+100644 X 0       yomin" >expected &&
+       echo yomin >yomin &&
+       git update-index --add yomin &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >4.out &&
+       test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
+       compare_change 4diff.out expected &&
+       check_cache_at yomin clean &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       test_cmp nitfol.M nitfol &&
+       echo yomin >yomin1 &&
+       diff yomin yomin1 &&
+       rm -f yomin1
+'
+
+test_expect_success '5 - carry forward local addition.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       read_tree_u_must_succeed -m -u $treeH &&
+       echo yomin >yomin &&
+       git update-index --add yomin &&
+       echo yomin yomin >yomin &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >5.out &&
+       test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
+       compare_change 5diff.out expected &&
+       check_cache_at yomin dirty &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       test_cmp nitfol.M nitfol &&
+       : dirty index should have prevented -u from checking it out. &&
+       echo yomin yomin >yomin1 &&
+       diff yomin yomin1 &&
+       rm -f yomin1
+'
+
+test_expect_success '6 - local addition already has the same.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo frotz >frotz &&
+       git update-index --add frotz &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >6.out &&
+       test_cmp M.out 6.out &&
+       check_cache_at frotz clean &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       test_cmp nitfol.M nitfol &&
+       echo frotz >frotz1 &&
+       diff frotz frotz1 &&
+       rm -f frotz1
+'
+
+test_expect_success '7 - local addition already has the same.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo frotz >frotz &&
+       git update-index --add frotz &&
+       echo frotz frotz >frotz &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >7.out &&
+       test_cmp M.out 7.out &&
+       check_cache_at frotz dirty &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp nitfol.M nitfol &&
+       : dirty index should have prevented -u from checking it out. &&
+       echo frotz frotz >frotz1 &&
+       diff frotz frotz1 &&
+       rm -f frotz1
+'
+
+test_expect_success '8 - conflicting addition.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo frotz frotz >frotz &&
+       git update-index --add frotz &&
+       ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '9 - conflicting addition.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo frotz frotz >frotz &&
+       git update-index --add frotz &&
+       echo frotz >frotz &&
+       ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '10 - path removed.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo rezrov >rezrov &&
+       git update-index --add rezrov &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >10.out &&
+       cmp M.out 10.out &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       test_cmp nitfol.M nitfol
+'
+
+test_expect_success '11 - dirty path removed.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo rezrov >rezrov &&
+       git update-index --add rezrov &&
+       echo rezrov rezrov >rezrov &&
+       ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '12 - unmatching local changes being removed.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo rezrov rezrov >rezrov &&
+       git update-index --add rezrov &&
+       ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '13 - unmatching local changes being removed.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo rezrov rezrov >rezrov &&
+       git update-index --add rezrov &&
+       echo rezrov >rezrov &&
+       ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
 
 cat >expected <<EOF
 -100644 X 0    nitfol
 +100644 X 0    nitfol
 EOF
 
-test_expect_success \
-    '14 - unchanged in two heads.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo nitfol nitfol >nitfol &&
-     git update-index --add nitfol &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >14.out &&
-     test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
-     compare_change 14diff.out expected &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     check_cache_at nitfol clean &&
-     echo nitfol nitfol >nitfol1 &&
-     diff nitfol nitfol1 &&
-     rm -f nitfol1'
-
-test_expect_success \
-    '15 - unchanged in two heads.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo nitfol nitfol >nitfol &&
-     git update-index --add nitfol &&
-     echo nitfol nitfol nitfol >nitfol &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >15.out &&
-     test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
-     compare_change 15diff.out expected &&
-     check_cache_at nitfol dirty &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     echo nitfol nitfol nitfol >nitfol1 &&
-     diff nitfol nitfol1 &&
-     rm -f nitfol1'
-
-test_expect_success \
-    '16 - conflicting local change.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo bozbar bozbar >bozbar &&
-     git update-index --add bozbar &&
-     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
-    '17 - conflicting local change.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo bozbar bozbar >bozbar &&
-     git update-index --add bozbar &&
-     echo bozbar bozbar bozbar >bozbar &&
-     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
-    '18 - local change already having a good result.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo gnusto >bozbar &&
-     git update-index --add bozbar &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >18.out &&
-     test_cmp M.out 18.out &&
-     check_cache_at bozbar clean &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol
+test_expect_success '14 - unchanged in two heads.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo nitfol nitfol >nitfol &&
+       git update-index --add nitfol &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >14.out &&
+       test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
+       compare_change 14diff.out expected &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       check_cache_at nitfol clean &&
+       echo nitfol nitfol >nitfol1 &&
+       diff nitfol nitfol1 &&
+       rm -f nitfol1
 '
 
-test_expect_success \
-    '19 - local change already having a good result, further modified.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo gnusto >bozbar &&
-     git update-index --add bozbar &&
-     echo gnusto gnusto >bozbar &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >19.out &&
-     test_cmp M.out 19.out &&
-     check_cache_at bozbar dirty &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     echo gnusto gnusto >bozbar1 &&
-     diff bozbar bozbar1 &&
-     rm -f bozbar1'
-
-test_expect_success \
-    '20 - no local change, use new tree.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo bozbar >bozbar &&
-     git update-index --add bozbar &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >20.out &&
-     test_cmp M.out 20.out &&
-     check_cache_at bozbar clean &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol
+test_expect_success '15 - unchanged in two heads.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo nitfol nitfol >nitfol &&
+       git update-index --add nitfol &&
+       echo nitfol nitfol nitfol >nitfol &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >15.out &&
+       test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
+       compare_change 15diff.out expected &&
+       check_cache_at nitfol dirty &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       echo nitfol nitfol nitfol >nitfol1 &&
+       diff nitfol nitfol1 &&
+       rm -f nitfol1
 '
 
-test_expect_success \
-    '21 - no local change, dirty cache.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo bozbar >bozbar &&
-     git update-index --add bozbar &&
-     echo gnusto gnusto >bozbar &&
-     if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+test_expect_success '16 - conflicting local change.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo bozbar bozbar >bozbar &&
+       git update-index --add bozbar &&
+       ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '17 - conflicting local change.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo bozbar bozbar >bozbar &&
+       git update-index --add bozbar &&
+       echo bozbar bozbar bozbar >bozbar &&
+       ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '18 - local change already having a good result.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo gnusto >bozbar &&
+       git update-index --add bozbar &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >18.out &&
+       test_cmp M.out 18.out &&
+       check_cache_at bozbar clean &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       test_cmp nitfol.M nitfol
+'
+
+test_expect_success '19 - local change already having a good result, further modified.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo gnusto >bozbar &&
+       git update-index --add bozbar &&
+       echo gnusto gnusto >bozbar &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >19.out &&
+       test_cmp M.out 19.out &&
+       check_cache_at bozbar dirty &&
+       test_cmp frotz.M frotz &&
+       test_cmp nitfol.M nitfol &&
+       echo gnusto gnusto >bozbar1 &&
+       diff bozbar bozbar1 &&
+       rm -f bozbar1
+'
+
+test_expect_success '20 - no local change, use new tree.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo bozbar >bozbar &&
+       git update-index --add bozbar &&
+       read_tree_u_must_succeed -m -u $treeH $treeM &&
+       git ls-files --stage >20.out &&
+       test_cmp M.out 20.out &&
+       check_cache_at bozbar clean &&
+       test_cmp bozbar.M bozbar &&
+       test_cmp frotz.M frotz &&
+       test_cmp nitfol.M nitfol
+'
+
+test_expect_success '21 - no local change, dirty cache.' '
+       rm -f .git/index nitfol bozbar rezrov frotz &&
+       read_tree_u_must_succeed --reset -u $treeH &&
+       echo bozbar >bozbar &&
+       git update-index --add bozbar &&
+       echo gnusto gnusto >bozbar &&
+       ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
 
 # Also make sure we did not break DF vs DF/DF case.
-test_expect_success \
-    'DF vs DF/DF case setup.' \
-    'rm -f .git/index &&
-     echo DF >DF &&
-     git update-index --add DF &&
-     treeDF=$(git write-tree) &&
-     echo treeDF $treeDF &&
-     git ls-tree $treeDF &&
-
-     rm -f DF &&
-     mkdir DF &&
-     echo DF/DF >DF/DF &&
-     git update-index --add --remove DF DF/DF &&
-     treeDFDF=$(git write-tree) &&
-     echo treeDFDF $treeDFDF &&
-     git ls-tree $treeDFDF &&
-     git ls-files --stage >DFDF.out'
-
-test_expect_success \
-    'DF vs DF/DF case test.' \
-    'rm -f .git/index &&
-     rm -fr DF &&
-     echo DF >DF &&
-     git update-index --add DF &&
-     read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
-     git ls-files --stage >DFDFcheck.out &&
-     test_cmp DFDF.out DFDFcheck.out &&
-     check_cache_at DF/DF clean'
+test_expect_success 'DF vs DF/DF case setup.' '
+       rm -f .git/index &&
+       echo DF >DF &&
+       git update-index --add DF &&
+       treeDF=$(git write-tree) &&
+       echo treeDF $treeDF &&
+       git ls-tree $treeDF &&
+
+       rm -f DF &&
+       mkdir DF &&
+       echo DF/DF >DF/DF &&
+       git update-index --add --remove DF DF/DF &&
+       treeDFDF=$(git write-tree) &&
+       echo treeDFDF $treeDFDF &&
+       git ls-tree $treeDFDF &&
+       git ls-files --stage >DFDF.out
+'
+
+test_expect_success 'DF vs DF/DF case test.' '
+       rm -f .git/index &&
+       rm -fr DF &&
+       echo DF >DF &&
+       git update-index --add DF &&
+       read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
+       git ls-files --stage >DFDFcheck.out &&
+       test_cmp DFDF.out DFDFcheck.out &&
+       check_cache_at DF/DF clean
+'
 
 test_done
index 12e30d77d096d5108396f90ecd4f9088c0f27413..26be4a2b5a007afa7ef1a9bd4438d16173c6aad5 100755 (executable)
@@ -41,7 +41,8 @@ test_expect_success 'reset should remove remnants from a failed merge' '
        git ls-files -s &&
        read_tree_u_must_succeed --reset -u HEAD &&
        git ls-files -s >actual &&
-       ! test -f old
+       ! test -f old &&
+       test_cmp expect actual
 '
 
 test_expect_success 'two-way reset should remove remnants too' '
@@ -56,7 +57,8 @@ test_expect_success 'two-way reset should remove remnants too' '
        git ls-files -s &&
        read_tree_u_must_succeed --reset -u HEAD HEAD &&
        git ls-files -s >actual &&
-       ! test -f old
+       ! test -f old &&
+       test_cmp expect actual
 '
 
 test_expect_success 'Porcelain reset should remove remnants too' '
@@ -71,7 +73,8 @@ test_expect_success 'Porcelain reset should remove remnants too' '
        git ls-files -s &&
        git reset --hard &&
        git ls-files -s >actual &&
-       ! test -f old
+       ! test -f old &&
+       test_cmp expect actual
 '
 
 test_expect_success 'Porcelain checkout -f should remove remnants too' '
@@ -86,7 +89,8 @@ test_expect_success 'Porcelain checkout -f should remove remnants too' '
        git ls-files -s &&
        git checkout -f &&
        git ls-files -s >actual &&
-       ! test -f old
+       ! test -f old &&
+       test_cmp expect actual
 '
 
 test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
@@ -101,7 +105,8 @@ test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
        git ls-files -s &&
        git checkout -f HEAD &&
        git ls-files -s >actual &&
-       ! test -f old
+       ! test -f old &&
+       test_cmp expect actual
 '
 
 test_done
index 23b8942edba45c32822886ee93acef9b5474ea02..e0c6482797e1203a9e97ec27714d5ea48aaf1b6c 100755 (executable)
@@ -6,7 +6,7 @@ test_description='git cat-file'
 
 test_cmdmode_usage () {
        test_expect_code 129 "$@" 2>err &&
-       grep "^error:.*is incompatible with" err
+       grep "^error: .* cannot be used together" err
 }
 
 for switches in \
@@ -89,7 +89,8 @@ done
 for opt in --buffer \
        --follow-symlinks \
        --batch-all-objects \
-       -z
+       -z \
+       -Z
 do
        test_expect_success "usage: bad option combination: $opt without batch mode" '
                test_incompatible_usage git cat-file $opt &&
@@ -109,26 +110,12 @@ strlen () {
     echo_without_newline "$1" | wc -c | sed -e 's/^ *//'
 }
 
-maybe_remove_timestamp () {
-       if test -z "$2"; then
-               echo_without_newline "$1"
-       else
-               echo_without_newline "$(printf '%s\n' "$1" | remove_timestamp)"
-       fi
-}
-
-remove_timestamp () {
-       sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//'
-}
-
-
 run_tests () {
     type=$1
     sha1=$2
     size=$3
     content=$4
     pretty_content=$5
-    no_ts=$6
 
     batch_output="$sha1 $type $size
 $content"
@@ -163,21 +150,21 @@ $content"
 
     test -z "$content" ||
     test_expect_success "Content of $type is correct" '
-       maybe_remove_timestamp "$content" $no_ts >expect &&
-       maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual &&
+       echo_without_newline "$content" >expect &&
+       git cat-file $type $sha1 >actual &&
        test_cmp expect actual
     '
 
     test_expect_success "Pretty content of $type is correct" '
-       maybe_remove_timestamp "$pretty_content" $no_ts >expect &&
-       maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual &&
+       echo_without_newline "$pretty_content" >expect &&
+       git cat-file -p $sha1 >actual &&
        test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "--batch output of $type is correct" '
-       maybe_remove_timestamp "$batch_output" $no_ts >expect &&
-       maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual &&
+       echo "$batch_output" >expect &&
+       echo $sha1 | git cat-file --batch >actual &&
        test_cmp expect actual
     '
 
@@ -191,9 +178,8 @@ $content"
     do
        test -z "$content" ||
                test_expect_success "--batch-command $opt output of $type content is correct" '
-               maybe_remove_timestamp "$batch_output" $no_ts >expect &&
-               maybe_remove_timestamp "$(test_write_lines "contents $sha1" |
-               git cat-file --batch-command $opt)" $no_ts >actual &&
+               echo "$batch_output" >expect &&
+               test_write_lines "contents $sha1" | git cat-file --batch-command $opt >actual &&
                test_cmp expect actual
        '
 
@@ -228,10 +214,9 @@ $content"
     test_expect_success "--batch without type ($type)" '
        {
                echo "$size" &&
-               maybe_remove_timestamp "$content" $no_ts
+               echo "$content"
        } >expect &&
-       echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full &&
-       maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+       echo $sha1 | git cat-file --batch="%(objectsize)" >actual &&
        test_cmp expect actual
     '
 
@@ -239,10 +224,9 @@ $content"
     test_expect_success "--batch without size ($type)" '
        {
                echo "$type" &&
-               maybe_remove_timestamp "$content" $no_ts
+               echo "$content"
        } >expect &&
-       echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full &&
-       maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+       echo $sha1 | git cat-file --batch="%(objecttype)" >actual &&
        test_cmp expect actual
     '
 }
@@ -284,7 +268,7 @@ test_expect_success '--batch-check without %(rest) considers whole line' '
 
 tree_sha1=$(git write-tree)
 tree_size=$(($(test_oid rawsz) + 13))
-tree_pretty_content="100644 blob $hello_sha1   hello"
+tree_pretty_content="100644 blob $hello_sha1   hello${LF}"
 
 run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
 
@@ -292,30 +276,32 @@ commit_message="Initial commit"
 commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
 commit_size=$(($(test_oid hexsz) + 137))
 commit_content="tree $tree_sha1
-author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
 $commit_message"
 
-run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content" 1
+run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content"
 
 tag_header_without_timestamp="object $hello_sha1
 type blob
 tag hellotag
 tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 tag_description="This is a tag"
-tag_content="$tag_header_without_timestamp 0000000000 +0000
+tag_content="$tag_header_without_timestamp 0 +0000
 
 $tag_description"
 
 tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
 tag_size=$(strlen "$tag_content")
 
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1
+run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content"
 
-test_expect_success \
-    "Reach a blob from a tag pointing to it" \
-    "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\""
+test_expect_success "Reach a blob from a tag pointing to it" '
+       echo_without_newline "$hello_content" >expect &&
+       git cat-file blob $tag_sha1 >actual &&
+       test_cmp expect actual
+'
 
 for batch in batch batch-check batch-command
 do
@@ -351,30 +337,47 @@ do
 done
 
 test_expect_success "--batch-check for a non-existent named object" '
-    test "foobar42 missing
-foobar84 missing" = \
-    "$( ( echo foobar42 && echo_without_newline foobar84 ) | git cat-file --batch-check)"
+       cat >expect <<-EOF &&
+       foobar42 missing
+       foobar84 missing
+       EOF
+
+       printf "foobar42\nfoobar84" >in &&
+       git cat-file --batch-check <in >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success "--batch-check for a non-existent hash" '
-    test "0000000000000000000000000000000000000042 missing
-0000000000000000000000000000000000000084 missing" = \
-    "$( ( echo 0000000000000000000000000000000000000042 &&
-        echo_without_newline 0000000000000000000000000000000000000084 ) |
-       git cat-file --batch-check)"
+       cat >expect <<-EOF &&
+       0000000000000000000000000000000000000042 missing
+       0000000000000000000000000000000000000084 missing
+       EOF
+
+       printf "0000000000000000000000000000000000000042\n0000000000000000000000000000000000000084" >in &&
+       git cat-file --batch-check <in >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success "--batch for an existent and a non-existent hash" '
-    test "$tag_sha1 tag $tag_size
-$tag_content
-0000000000000000000000000000000000000000 missing" = \
-    "$( ( echo $tag_sha1 &&
-        echo_without_newline 0000000000000000000000000000000000000000 ) |
-       git cat-file --batch)"
+       cat >expect <<-EOF &&
+       $tag_sha1 tag $tag_size
+       $tag_content
+       0000000000000000000000000000000000000000 missing
+       EOF
+
+       printf "$tag_sha1\n0000000000000000000000000000000000000000" >in &&
+       git cat-file --batch <in >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success "--batch-check for an empty line" '
-    test " missing" = "$(echo | git cat-file --batch-check)"
+       cat >expect <<-EOF &&
+        missing
+       EOF
+
+       echo >in &&
+       git cat-file --batch-check <in >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'empty --batch-check notices missing object' '
@@ -390,23 +393,34 @@ deadbeef
 
 "
 
-batch_output="$hello_sha1 blob $hello_size
-$hello_content
-$commit_sha1 commit $commit_size
-$commit_content
-$tag_sha1 tag $tag_size
-$tag_content
-deadbeef missing
- missing"
+printf "%s\0" \
+       "$hello_sha1 blob $hello_size" \
+       "$hello_content" \
+       "$commit_sha1 commit $commit_size" \
+       "$commit_content" \
+       "$tag_sha1 tag $tag_size" \
+       "$tag_content" \
+       "deadbeef missing" \
+       " missing" >batch_output
 
 test_expect_success '--batch with multiple sha1s gives correct format' '
-       test "$(maybe_remove_timestamp "$batch_output" 1)" = "$(maybe_remove_timestamp "$(echo_without_newline "$batch_input" | git cat-file --batch)" 1)"
+       tr "\0" "\n" <batch_output >expect &&
+       echo_without_newline "$batch_input" >in &&
+       git cat-file --batch <in >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '--batch, -z with multiple sha1s gives correct format' '
        echo_without_newline_nul "$batch_input" >in &&
-       test "$(maybe_remove_timestamp "$batch_output" 1)" = \
-       "$(maybe_remove_timestamp "$(git cat-file --batch -z <in)" 1)"
+       tr "\0" "\n" <batch_output >expect &&
+       git cat-file --batch -z <in >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--batch, -Z with multiple sha1s gives correct format' '
+       echo_without_newline_nul "$batch_input" >in &&
+       git cat-file --batch -Z <in >actual &&
+       test_cmp batch_output actual
 '
 
 batch_check_input="$hello_sha1
@@ -417,36 +431,55 @@ deadbeef
 
 "
 
-batch_check_output="$hello_sha1 blob $hello_size
-$tree_sha1 tree $tree_size
-$commit_sha1 commit $commit_size
-$tag_sha1 tag $tag_size
-deadbeef missing
- missing"
+printf "%s\0" \
+       "$hello_sha1 blob $hello_size" \
+       "$tree_sha1 tree $tree_size" \
+       "$commit_sha1 commit $commit_size" \
+       "$tag_sha1 tag $tag_size" \
+       "deadbeef missing" \
+       " missing" >batch_check_output
 
 test_expect_success "--batch-check with multiple sha1s gives correct format" '
-    test "$batch_check_output" = \
-    "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
+       tr "\0" "\n" <batch_check_output >expect &&
+       echo_without_newline "$batch_check_input" >in &&
+       git cat-file --batch-check <in >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success "--batch-check, -z with multiple sha1s gives correct format" '
-    echo_without_newline_nul "$batch_check_input" >in &&
-    test "$batch_check_output" = "$(git cat-file --batch-check -z <in)"
+       tr "\0" "\n" <batch_check_output >expect &&
+       echo_without_newline_nul "$batch_check_input" >in &&
+       git cat-file --batch-check -z <in >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+test_expect_success "--batch-check, -Z with multiple sha1s gives correct format" '
+       echo_without_newline_nul "$batch_check_input" >in &&
+       git cat-file --batch-check -Z <in >actual &&
+       test_cmp batch_check_output actual
+'
+
+test_expect_success FUNNYNAMES 'setup with newline in input' '
        touch -- "newline${LF}embedded" &&
        git add -- "newline${LF}embedded" &&
        git commit -m "file with newline embedded" &&
        test_tick &&
 
-       printf "HEAD:newline${LF}embedded" >in &&
-       git cat-file --batch-check -z <in >actual &&
+       printf "HEAD:newline${LF}embedded" >in
+'
 
+test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+       git cat-file --batch-check -z <in >actual &&
        echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
        test_cmp expect actual
 '
 
+test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
+       git cat-file --batch-check -Z <in >actual &&
+       printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+       test_cmp expect actual
+'
+
 batch_command_multiple_info="info $hello_sha1
 info $tree_sha1
 info $commit_sha1
@@ -470,7 +503,13 @@ test_expect_success '--batch-command with multiple info calls gives correct form
        echo "$batch_command_multiple_info" | tr "\n" "\0" >in &&
        git cat-file --batch-command --buffer -z <in >actual &&
 
-       test_cmp expect actual
+       test_cmp expect actual &&
+
+       echo "$batch_command_multiple_info" | tr "\n" "\0" >in &&
+       tr "\n" "\0" <expect >expect_nul &&
+       git cat-file --batch-command --buffer -Z <in >actual &&
+
+       test_cmp expect_nul actual
 '
 
 batch_command_multiple_contents="contents $hello_sha1
@@ -480,27 +519,30 @@ contents deadbeef
 flush"
 
 test_expect_success '--batch-command with multiple command calls gives correct format' '
-       remove_timestamp >expect <<-EOF &&
-       $hello_sha1 blob $hello_size
-       $hello_content
-       $commit_sha1 commit $commit_size
-       $commit_content
-       $tag_sha1 tag $tag_size
-       $tag_content
-       deadbeef missing
-       EOF
+       printf "%s\0" \
+               "$hello_sha1 blob $hello_size" \
+               "$hello_content" \
+               "$commit_sha1 commit $commit_size" \
+               "$commit_content" \
+               "$tag_sha1 tag $tag_size" \
+               "$tag_content" \
+               "deadbeef missing" >expect_nul &&
+       tr "\0" "\n" <expect_nul >expect &&
 
        echo "$batch_command_multiple_contents" >in &&
-       git cat-file --batch-command --buffer <in >actual_raw &&
+       git cat-file --batch-command --buffer <in >actual &&
 
-       remove_timestamp <actual_raw >actual &&
        test_cmp expect actual &&
 
        echo "$batch_command_multiple_contents" | tr "\n" "\0" >in &&
-       git cat-file --batch-command --buffer -z <in >actual_raw &&
+       git cat-file --batch-command --buffer -z <in >actual &&
 
-       remove_timestamp <actual_raw >actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+
+       echo "$batch_command_multiple_contents" | tr "\n" "\0" >in &&
+       git cat-file --batch-command --buffer -Z <in >actual &&
+
+       test_cmp expect_nul actual
 '
 
 test_expect_success 'setup blobs which are likely to delta' '
@@ -603,7 +645,8 @@ do
                        fatal: Not a valid object name $(test_oid deadbeef_short)
                        EOF
                        test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
-                       test_must_be_empty out
+                       test_must_be_empty out &&
+                       test_cmp expect.err err.actual
                '
 
                test_expect_success "cat-file $arg1 $arg2 error on missing full OID" '
@@ -839,6 +882,13 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for brok
        test_cmp expect actual
 '
 
+test_expect_success 'git cat-file --batch-check --follow-symlinks -Z works for broken in-repo, same-dir links' '
+       printf "HEAD:broken-same-dir-link\0" >in &&
+       printf "dangling 25\0HEAD:broken-same-dir-link\0" >expect &&
+       git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
        echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
        test_cmp found actual
@@ -853,6 +903,15 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for pare
        test_cmp expect actual
 '
 
+test_expect_success 'git cat-file --batch-check --follow-symlinks -Z works for parent-dir links' '
+       echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+       test_cmp found actual &&
+       printf "notdir 29\0HEAD:dir/parent-dir-link/nope\0" >expect &&
+       printf "HEAD:dir/parent-dir-link/nope\0" >in &&
+       git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
        echo dangling 22 >expect &&
        echo HEAD:dir/link-dir/nope >>expect &&
@@ -967,6 +1026,13 @@ test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
        test_cmp expect actual
 '
 
+test_expect_success 'git cat-file --batch-check --follow-symlink -Z breaks loops' '
+       printf "loop 10\0HEAD:loop1\0" >expect &&
+       printf "HEAD:loop1\0" >in &&
+       git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
        echo HEAD:morx | git cat-file --batch >expect &&
        echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
@@ -1034,6 +1100,42 @@ test_expect_success 'cat-file --batch="batman" with --batch-all-objects will wor
        cmp expect actual
 '
 
+test_expect_success 'cat-file %(objectsize:disk) with --batch-all-objects' '
+       # our state has both loose and packed objects,
+       # so find both for our expected output
+       {
+               find .git/objects/?? -type f |
+               awk -F/ "{ print \$0, \$3\$4 }" |
+               while read path oid
+               do
+                       size=$(test_file_size "$path") &&
+                       echo "$oid $size" ||
+                       return 1
+               done &&
+               rawsz=$(test_oid rawsz) &&
+               find .git/objects/pack -name "*.idx" |
+               while read idx
+               do
+                       git show-index <"$idx" >idx.raw &&
+                       sort -nr <idx.raw >idx.sorted &&
+                       packsz=$(test_file_size "${idx%.idx}.pack") &&
+                       end=$((packsz - rawsz)) &&
+                       while read start oid rest
+                       do
+                               size=$((end - start)) &&
+                               end=$start &&
+                               echo "$oid $size" ||
+                               return 1
+                       done <idx.sorted ||
+                       return 1
+               done
+       } >expect.raw &&
+       sort <expect.raw >expect &&
+       git cat-file --batch-all-objects \
+               --batch-check="%(objectname) %(objectsize:disk)" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'set up replacement object' '
        orig=$(git rev-parse HEAD) &&
        git cat-file commit $orig >orig &&
index ac5ad8c7402d2bd3f43d38e5a676d864ea415d37..ac3d173767ae72be7bc43e4df269d88fb040f7ba 100755 (executable)
@@ -203,23 +203,34 @@ done
 test_expect_success 'too-short tree' '
        echo abc >malformed-tree &&
        test_must_fail git hash-object -t tree malformed-tree 2>err &&
-       test_i18ngrep "too-short tree object" err
+       grep "too-short tree object" err
 '
 
 test_expect_success 'malformed mode in tree' '
-       hex_sha1=$(echo foo | git hash-object --stdin -w) &&
-       bin_sha1=$(echo $hex_sha1 | hex2oct) &&
-       printf "9100644 \0$bin_sha1" >tree-with-malformed-mode &&
+       hex_oid=$(echo foo | git hash-object --stdin -w) &&
+       bin_oid=$(echo $hex_oid | hex2oct) &&
+       printf "9100644 \0$bin_oid" >tree-with-malformed-mode &&
        test_must_fail git hash-object -t tree tree-with-malformed-mode 2>err &&
-       test_i18ngrep "malformed mode in tree entry" err
+       grep "malformed mode in tree entry" err
 '
 
 test_expect_success 'empty filename in tree' '
-       hex_sha1=$(echo foo | git hash-object --stdin -w) &&
-       bin_sha1=$(echo $hex_sha1 | hex2oct) &&
-       printf "100644 \0$bin_sha1" >tree-with-empty-filename &&
+       hex_oid=$(echo foo | git hash-object --stdin -w) &&
+       bin_oid=$(echo $hex_oid | hex2oct) &&
+       printf "100644 \0$bin_oid" >tree-with-empty-filename &&
        test_must_fail git hash-object -t tree tree-with-empty-filename 2>err &&
-       test_i18ngrep "empty filename in tree entry" err
+       grep "empty filename in tree entry" err
+'
+
+test_expect_success 'duplicate filename in tree' '
+       hex_oid=$(echo foo | git hash-object --stdin -w) &&
+       bin_oid=$(echo $hex_oid | hex2oct) &&
+       {
+               printf "100644 file\0$bin_oid" &&
+               printf "100644 file\0$bin_oid"
+       } >tree-with-duplicate-filename &&
+       test_must_fail git hash-object -t tree tree-with-duplicate-filename 2>err &&
+       grep "duplicateEntries" err
 '
 
 test_expect_success 'corrupt commit' '
index 3c0819452653878ae51779798ce90712dc30a901..22875ba598c2a82bba6c7504c420c690441f7167 100755 (executable)
@@ -60,11 +60,11 @@ test_expect_success 'allow missing object with --missing' '
 '
 
 test_expect_success 'mktree refuses to read ls-tree -r output (1)' '
-       test_must_fail git mktree <all >actual
+       test_must_fail git mktree <all
 '
 
 test_expect_success 'mktree refuses to read ls-tree -r output (2)' '
-       test_must_fail git mktree <all.withsub >actual
+       test_must_fail git mktree <all.withsub
 '
 
 test_done
index 742f0fa909fd6e8a7fe2b376b3e5083a30d71c73..595b24c0adb3081230fe65884fd436e1f12896d4 100755 (executable)
@@ -12,6 +12,7 @@ test_description='sparse checkout tests
 '
 
 TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
index a9953b6a71c360a0c39d058ba61f9ff026ec04bf..cca4380e4319a024416c4bbe2e097f63e409af48 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='git read-tree in partial clones'
 
 TEST_NO_CREATE_REPO=1
-
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'read-tree in partial clone prefetches in one batch' '
@@ -19,7 +19,7 @@ test_expect_success 'read-tree in partial clone prefetches in one batch' '
        git -C server config uploadpack.allowfilter 1 &&
        git -C server config uploadpack.allowanysha1inwant 1 &&
        git clone --bare --filter=blob:none "file://$(pwd)/server" client &&
-       GIT_TRACE_PACKET="$(pwd)/trace" git -C client read-tree $TREE &&
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C client read-tree $TREE $TREE &&
 
        # "done" marks the end of negotiation (once per fetch). Expect that
        # only one fetch occurs.
index 4f3aa17c994240173fdb4de70da62611700e11fd..c71932b02423734908428a9b2be776fec229ce9e 100755 (executable)
@@ -5,6 +5,12 @@ test_description='adding and checking out large blobs'
 
 . ./test-lib.sh
 
+test_expect_success 'core.bigFileThreshold must be non-negative' '
+       test_must_fail git -c core.bigFileThreshold=-1 rev-parse >out 2>err &&
+       grep "bad numeric config value" err &&
+       test_must_be_empty out
+'
+
 test_expect_success setup '
        # clone does not allow us to pass core.bigfilethreshold to
        # new repos, so set core.bigfilethreshold globally
index 35261afc9d6d02fe1d9d1572591406b293522fa8..5e0f0a334f4d43dd76199a50037c5900da2cb63b 100755 (executable)
@@ -125,7 +125,7 @@ test_expect_success 'fetch into corrupted repo with index-pack' '
                cd bit-error-cp &&
                test_must_fail git -c transfer.unpackLimit=1 \
                        fetch ../no-bit-error 2>stderr &&
-               test_i18ngrep ! -i collision stderr
+               test_grep ! -i collision stderr
        )
 '
 
index b563d6c263ec1cee0d4bfc93cba1e85e4833f4e6..e49b8024ac53659e17f22eb39e252aba73e56a6b 100755 (executable)
@@ -47,7 +47,7 @@ test_expect_success 'setup' '
 test_expect_success 'git sparse-checkout list (not sparse)' '
        test_must_fail git -C repo sparse-checkout list >list 2>err &&
        test_must_be_empty list &&
-       test_i18ngrep "this worktree is not sparse" err
+       test_grep "this worktree is not sparse" err
 '
 
 test_expect_success 'git sparse-checkout list (not sparse)' '
@@ -55,7 +55,7 @@ test_expect_success 'git sparse-checkout list (not sparse)' '
        rm repo/.git/info/sparse-checkout &&
        git -C repo sparse-checkout list >list 2>err &&
        test_must_be_empty list &&
-       test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
+       test_grep "this worktree is not sparse (sparse-checkout file may not exist)" err
 '
 
 test_expect_success 'git sparse-checkout list (populated)' '
@@ -230,7 +230,7 @@ test_expect_success 'cone mode: match patterns' '
        git -C repo config --worktree core.sparseCheckoutCone true &&
        rm -rf repo/a repo/folder1 repo/folder2 &&
        git -C repo read-tree -mu HEAD 2>err &&
-       test_i18ngrep ! "disabling cone patterns" err &&
+       test_grep ! "disabling cone patterns" err &&
        git -C repo reset --hard &&
        check_files repo a folder1 folder2
 '
@@ -238,9 +238,9 @@ test_expect_success 'cone mode: match patterns' '
 test_expect_success 'cone mode: warn on bad pattern' '
        test_when_finished mv sparse-checkout repo/.git/info/ &&
        cp repo/.git/info/sparse-checkout . &&
-       echo "!/deep/deeper/*" >>repo/.git/info/sparse-checkout &&
+       echo "!/deep/deeper/*/" >>repo/.git/info/sparse-checkout &&
        git -C repo read-tree -mu HEAD 2>err &&
-       test_i18ngrep "unrecognized negative pattern" err
+       test_grep "unrecognized negative pattern" err
 '
 
 test_expect_success 'sparse-checkout disable' '
@@ -283,7 +283,7 @@ test_expect_success 'sparse-index enabled and disabled' '
 test_expect_success 'cone mode: init and set' '
        git -C repo sparse-checkout init --cone &&
        git -C repo config --list >config &&
-       test_i18ngrep "core.sparsecheckoutcone=true" config &&
+       test_grep "core.sparsecheckoutcone=true" config &&
        list_files repo >dir  &&
        echo a >expect &&
        test_cmp expect dir &&
@@ -334,7 +334,7 @@ test_expect_success 'cone mode: set with nested folders' '
 
 test_expect_success 'cone mode: add independent path' '
        git -C repo sparse-checkout set deep/deeper1 &&
-       git -C repo sparse-checkout add folder1 &&
+       git -C repo sparse-checkout add --end-of-options folder1 &&
        cat >expect <<-\EOF &&
        /*
        !/*/
@@ -386,7 +386,7 @@ test_expect_success 'not-up-to-date does not block rest of sparsification' '
 
        git -C repo sparse-checkout set deep/deeper1 2>err &&
 
-       test_i18ngrep "The following paths are not up to date" err &&
+       test_grep "The following paths are not up to date" err &&
        test_cmp expect repo/.git/info/sparse-checkout &&
        check_files repo/deep a deeper1 deeper2 &&
        check_files repo/deep/deeper1 a deepest &&
@@ -401,8 +401,8 @@ test_expect_success 'revert to old sparse-checkout on empty update' '
                git add file &&
                git commit -m "test" &&
                git sparse-checkout set nothing 2>err &&
-               test_i18ngrep ! "Sparse checkout leaves no entry on working directory" err &&
-               test_i18ngrep ! ".git/index.lock" err &&
+               test_grep ! "Sparse checkout leaves no entry on working directory" err &&
+               test_grep ! ".git/index.lock" err &&
                git sparse-checkout set --no-cone file
        )
 '
@@ -411,14 +411,14 @@ test_expect_success 'fail when lock is taken' '
        test_when_finished rm -rf repo/.git/info/sparse-checkout.lock &&
        touch repo/.git/info/sparse-checkout.lock &&
        test_must_fail git -C repo sparse-checkout set deep 2>err &&
-       test_i18ngrep "Unable to create .*\.lock" err
+       test_grep "Unable to create .*\.lock" err
 '
 
 test_expect_success '.gitignore should not warn about cone mode' '
        git -C repo config --worktree core.sparseCheckoutCone true &&
        echo "**/bin/*" >repo/.gitignore &&
        git -C repo reset --hard 2>err &&
-       test_i18ngrep ! "disabling cone patterns" err
+       test_grep ! "disabling cone patterns" err
 '
 
 test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status' '
@@ -426,10 +426,10 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status'
        echo dirty >dirty/folder1/a &&
 
        git -C dirty sparse-checkout init --no-cone 2>err &&
-       test_i18ngrep "warning.*The following paths are not up to date" err &&
+       test_grep "warning.*The following paths are not up to date" err &&
 
        git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
-       test_i18ngrep "warning.*The following paths are not up to date" err &&
+       test_grep "warning.*The following paths are not up to date" err &&
        test_path_is_file dirty/folder1/a &&
 
        git -C dirty sparse-checkout disable 2>err &&
@@ -453,14 +453,14 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged stat
        git -C unmerged update-index --index-info <input &&
 
        git -C unmerged sparse-checkout init --no-cone 2>err &&
-       test_i18ngrep "warning.*The following paths are unmerged" err &&
+       test_grep "warning.*The following paths are unmerged" err &&
 
        git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
-       test_i18ngrep "warning.*The following paths are unmerged" err &&
+       test_grep "warning.*The following paths are unmerged" err &&
        test_path_is_file dirty/folder1/a &&
 
        git -C unmerged sparse-checkout disable 2>err &&
-       test_i18ngrep "warning.*The following paths are unmerged" err &&
+       test_grep "warning.*The following paths are unmerged" err &&
 
        git -C unmerged reset --hard &&
        git -C unmerged sparse-checkout init --no-cone &&
@@ -480,24 +480,24 @@ test_expect_failure 'sparse-checkout reapply' '
        git -C tweak update-index --index-info <input &&
 
        git -C tweak sparse-checkout init --cone 2>err &&
-       test_i18ngrep "warning.*The following paths are not up to date" err &&
-       test_i18ngrep "warning.*The following paths are unmerged" err &&
+       test_grep "warning.*The following paths are not up to date" err &&
+       test_grep "warning.*The following paths are unmerged" err &&
 
        git -C tweak sparse-checkout set folder2 deep/deeper1 2>err &&
-       test_i18ngrep "warning.*The following paths are not up to date" err &&
-       test_i18ngrep "warning.*The following paths are unmerged" err &&
+       test_grep "warning.*The following paths are not up to date" err &&
+       test_grep "warning.*The following paths are unmerged" err &&
 
        git -C tweak sparse-checkout reapply 2>err &&
-       test_i18ngrep "warning.*The following paths are not up to date" err &&
+       test_grep "warning.*The following paths are not up to date" err &&
        test_path_is_file tweak/deep/deeper2/a &&
-       test_i18ngrep "warning.*The following paths are unmerged" err &&
+       test_grep "warning.*The following paths are unmerged" err &&
        test_path_is_file tweak/folder1/a &&
 
        git -C tweak checkout HEAD deep/deeper2/a &&
        git -C tweak sparse-checkout reapply 2>err &&
-       test_i18ngrep ! "warning.*The following paths are not up to date" err &&
+       test_grep ! "warning.*The following paths are not up to date" err &&
        test_path_is_missing tweak/deep/deeper2/a &&
-       test_i18ngrep "warning.*The following paths are unmerged" err &&
+       test_grep "warning.*The following paths are unmerged" err &&
        test_path_is_file tweak/folder1/a &&
 
        # NEEDSWORK: We are asking to update a file outside of the
@@ -555,7 +555,7 @@ test_expect_success 'cone mode: set with core.ignoreCase=true' '
        check_files repo a folder1
 '
 
-test_expect_success 'interaction with submodules' '
+test_expect_success 'setup submodules' '
        git clone repo super &&
        (
                cd super &&
@@ -566,11 +566,22 @@ test_expect_success 'interaction with submodules' '
                git commit -m "add submodule" &&
                git sparse-checkout init --cone &&
                git sparse-checkout set folder1
-       ) &&
+       )
+'
+
+test_expect_success 'interaction with submodules' '
        check_files super a folder1 modules &&
        check_files super/modules/child a deep folder1 folder2
 '
 
+test_expect_success 'check-rules interaction with submodules' '
+       git -C super ls-tree --name-only -r HEAD >all-files &&
+       git -C super sparse-checkout check-rules >check-rules-matches <all-files &&
+
+       test_grep ! "modules/" check-rules-matches &&
+       test_grep "folder1/" check-rules-matches
+'
+
 test_expect_success 'different sparse-checkouts with worktrees' '
        git -C repo sparse-checkout set --cone deep folder1 &&
        git -C repo worktree add --detach ../worktree &&
@@ -605,7 +616,7 @@ check_read_tree_errors () {
        then
                test_must_be_empty err
        else
-               test_i18ngrep "$ERRORS" err
+               test_grep "$ERRORS" err
        fi &&
        check_files $REPO $FILES
 }
@@ -667,6 +678,15 @@ test_expect_success 'pattern-checks: starting "*"' '
        check_read_tree_errors repo "a deep" "disabling cone pattern matching"
 '
 
+test_expect_success 'pattern-checks: non directory pattern' '
+       cat >repo/.git/info/sparse-checkout <<-\EOF &&
+       /deep/deeper1/a
+       EOF
+       check_read_tree_errors repo deep "disabling cone pattern matching" &&
+       check_files repo/deep deeper1 &&
+       check_files repo/deep/deeper1 a
+'
+
 test_expect_success 'pattern-checks: contained glob characters' '
        for c in "[a]" "\\" "?" "*"
        do
@@ -866,6 +886,12 @@ test_expect_success 'by default, cone mode will error out when passed files' '
        grep ".gitignore.*is not a directory" error
 '
 
+test_expect_success 'error on mistyped command line options' '
+       test_must_fail git -C repo sparse-checkout add --sikp-checks .gitignore 2>error &&
+
+       grep "unknown option.*sikp-checks" error
+'
+
 test_expect_success 'by default, non-cone mode will warn on individual files' '
        git -C repo sparse-checkout reapply --no-cone &&
        git -C repo sparse-checkout add .gitignore 2>warning &&
@@ -873,4 +899,156 @@ test_expect_success 'by default, non-cone mode will warn on individual files' '
        grep "pass a leading slash before paths.*if you want a single file" warning
 '
 
+test_expect_success 'setup bare repo' '
+       git clone --bare "file://$(pwd)/repo" bare
+'
+test_expect_success 'list fails outside work tree' '
+       test_must_fail git -C bare sparse-checkout list 2>err &&
+       test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'add fails outside work tree' '
+       test_must_fail git -C bare sparse-checkout add deeper 2>err &&
+       test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'set fails outside work tree' '
+       test_must_fail git -C bare sparse-checkout set deeper 2>err &&
+       test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'init fails outside work tree' '
+       test_must_fail git -C bare sparse-checkout init 2>err &&
+       test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'reapply fails outside work tree' '
+       test_must_fail git -C bare sparse-checkout reapply 2>err &&
+       test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'disable fails outside work tree' '
+       test_must_fail git -C bare sparse-checkout disable 2>err &&
+       test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'setup clean' '
+       git -C repo clean -fdx
+'
+
+test_expect_success 'check-rules cone mode' '
+       cat >rules <<-\EOF &&
+       folder1
+       deep/deeper1/deepest
+       EOF
+
+       git -C bare ls-tree -r --name-only HEAD >all-files &&
+       git -C bare sparse-checkout check-rules --cone \
+               --rules-file ../rules >check-rules-file <all-files &&
+
+       git -C repo sparse-checkout set --cone --stdin <rules&&
+       git -C repo ls-files -t >out &&
+       sed -n "/^S /!s/^. //p" out >ls-files &&
+
+       git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
+
+       test_grep "deep/deeper1/deepest/a" check-rules-file &&
+       test_grep ! "deep/deeper2" check-rules-file &&
+
+       test_cmp check-rules-file ls-files &&
+       test_cmp check-rules-file check-rules-default
+'
+
+test_expect_success 'check-rules non-cone mode' '
+       cat >rules <<-\EOF &&
+       deep/deeper1/deepest/a
+       EOF
+
+       git -C bare ls-tree -r --name-only HEAD >all-files &&
+       git -C bare sparse-checkout check-rules --no-cone --rules-file ../rules\
+               >check-rules-file <all-files &&
+
+       cat rules | git -C repo sparse-checkout set --no-cone --stdin &&
+       git -C repo ls-files -t >out &&
+       sed -n "/^S /!s/^. //p" out >ls-files &&
+
+       git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
+
+       cat >expect <<-\EOF &&
+       deep/deeper1/deepest/a
+       EOF
+
+       test_cmp expect check-rules-file &&
+       test_cmp check-rules-file ls-files &&
+       test_cmp check-rules-file check-rules-default
+'
+
+test_expect_success 'check-rules cone mode is default' '
+       cat >rules <<-\EOF &&
+       folder1
+       EOF
+
+       cat >all-files <<-\EOF &&
+       toplevel
+       folder2/file
+       folder1/file
+       EOF
+
+       cat >expect <<-\EOF &&
+       toplevel
+       folder1/file
+       EOF
+
+       git -C repo sparse-checkout set --no-cone &&
+       git -C repo sparse-checkout check-rules \
+               --rules-file ../rules >actual <all-files &&
+
+       git -C bare sparse-checkout check-rules \
+               --rules-file ../rules >actual-bare <all-files &&
+
+       test_cmp expect actual &&
+       test_cmp expect actual-bare
+'
+
+test_expect_success 'check-rules quoting' '
+       cat >rules <<-EOF &&
+       "folder\" a"
+       EOF
+       cat >files <<-EOF &&
+       "folder\" a/file"
+       "folder\" b/file"
+       EOF
+       cat >expect <<-EOF &&
+       "folder\" a/file"
+       EOF
+       git sparse-checkout check-rules --cone \
+               --rules-file rules >actual <files &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'check-rules null termination' '
+       cat >rules <<-EOF &&
+       "folder\" a"
+       EOF
+
+       lf_to_nul >files <<-EOF &&
+       folder" a/a
+       folder" a/b
+       folder" b/fileQ
+       EOF
+
+       cat >expect <<-EOF &&
+       folder" a/aQfolder" a/bQ
+       EOF
+
+       git sparse-checkout check-rules --cone -z \
+               --rules-file rules >actual.nul <files &&
+       nul_to_q <actual.nul >actual &&
+       echo >>actual &&
+
+       test_cmp expect actual
+'
+
+
 test_done
index 4844922e570ffdf2e570969b40849fe17941b06a..2f1ae5fd3bc409a1109216d1562456fe11564306 100755 (executable)
@@ -162,6 +162,19 @@ init_repos () {
        git -C sparse-index sparse-checkout set deep
 }
 
+init_repos_as_submodules () {
+       git reset --hard &&
+       init_repos &&
+       git submodule add ./full-checkout &&
+       git submodule add ./sparse-checkout &&
+       git submodule add ./sparse-index &&
+
+       git submodule status >actual &&
+       grep full-checkout actual &&
+       grep sparse-checkout actual &&
+       grep sparse-index actual
+}
+
 run_on_sparse () {
        (
                cd sparse-checkout &&
@@ -324,8 +337,8 @@ test_expect_success 'status reports sparse-checkout' '
        init_repos &&
        git -C sparse-checkout status >full &&
        git -C sparse-index status >sparse &&
-       test_i18ngrep "You are in a sparse checkout with " full &&
-       test_i18ngrep "You are in a sparse checkout." sparse
+       test_grep "You are in a sparse checkout with " full &&
+       test_grep "You are in a sparse checkout." sparse
 '
 
 test_expect_success 'add, commit, checkout' '
@@ -1169,7 +1182,7 @@ test_expect_success 'checkout-index outside sparse definition' '
        # Without --ignore-skip-worktree-bits, outside-of-cone files will trigger
        # an error
        test_sparse_match test_must_fail git checkout-index -- folder1/a &&
-       test_i18ngrep "folder1/a has skip-worktree enabled" sparse-checkout-err &&
+       test_grep "folder1/a has skip-worktree enabled" sparse-checkout-err &&
        test_path_is_missing folder1/a &&
 
        # With --ignore-skip-worktree-bits, outside-of-cone files are checked out
@@ -1364,7 +1377,7 @@ test_expect_success 'index.sparse disabled inline uses full index' '
        ! test_region index ensure_full_index trace2.txt
 '
 
-ensure_not_expanded () {
+run_sparse_index_trace2 () {
        rm -f trace2.txt &&
        if test -z "$WITHOUT_UNTRACKED_TXT"
        then
@@ -1384,7 +1397,16 @@ ensure_not_expanded () {
                        git -C sparse-index "$@" \
                        >sparse-index-out \
                        2>sparse-index-error || return 1
-       fi &&
+       fi
+}
+
+ensure_expanded () {
+       run_sparse_index_trace2 "$@" &&
+       test_region index ensure_full_index trace2.txt
+}
+
+ensure_not_expanded () {
+       run_sparse_index_trace2 "$@" &&
        test_region ! index ensure_full_index trace2.txt
 }
 
@@ -1501,6 +1523,31 @@ test_expect_success 'sparse-index is not expanded: stash' '
        ensure_not_expanded stash pop
 '
 
+test_expect_success 'describe tested on all' '
+       init_repos &&
+
+       # Add tag to be read by describe
+
+       run_on_all git tag -a v1.0 -m "Version 1" &&
+       test_all_match git describe --dirty &&
+       run_on_all rm g &&
+       test_all_match git describe --dirty
+'
+
+
+test_expect_success 'sparse-index is not expanded: describe' '
+       init_repos &&
+
+       # Add tag to be read by describe
+
+       git -C sparse-index tag -a v1.0 -m "Version 1" &&
+
+       ensure_not_expanded describe --dirty &&
+       echo "test" >>sparse-index/g &&
+       ensure_not_expanded describe --dirty &&
+       ensure_not_expanded describe
+'
+
 test_expect_success 'sparse index is not expanded: diff' '
        init_repos &&
 
@@ -1983,4 +2030,305 @@ test_expect_success 'sparse index is not expanded: rm' '
        ensure_not_expanded rm -r deep
 '
 
+test_expect_success 'grep with and --cached' '
+       init_repos &&
+
+       test_all_match git grep --cached a &&
+       test_all_match git grep --cached a -- "folder1/*"
+'
+
+test_expect_success 'grep is not expanded' '
+       init_repos &&
+
+       ensure_not_expanded grep a &&
+       ensure_not_expanded grep a -- deep/* &&
+
+       # All files within the folder1/* pathspec are sparse,
+       # so this command does not find any matches
+       ensure_not_expanded ! grep a -- folder1/* &&
+
+       # test out-of-cone pathspec with or without wildcard
+       ensure_not_expanded grep --cached a -- "folder1/a" &&
+       ensure_not_expanded grep --cached a -- "folder1/*" &&
+
+       # test in-cone pathspec with or without wildcard
+       ensure_not_expanded grep --cached a -- "deep/a" &&
+       ensure_not_expanded grep --cached a -- "deep/*"
+'
+
+# NEEDSWORK: when running `grep` in the superproject with --recurse-submodules,
+# Git expands the index of the submodules unexpectedly. Even though `grep`
+# builtin is marked as "command_requires_full_index = 0", this config is only
+# useful for the superproject. Namely, the submodules have their own configs,
+# which are _not_ populated by the one-time sparse-index feature switch.
+test_expect_failure 'grep within submodules is not expanded' '
+       init_repos_as_submodules &&
+
+       # do not use ensure_not_expanded() here, becasue `grep` should be
+       # run in the superproject, not in "./sparse-index"
+       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+       git grep --cached --recurse-submodules a -- "*/folder1/*" &&
+       test_region ! index ensure_full_index trace2.txt
+'
+
+# NEEDSWORK: this test is not actually testing the code. The design purpose
+# of this test is to verify the grep result when the submodules are using a
+# sparse-index. Namely, we want "folder1/" as a tree (a sparse directory); but
+# because of the index expansion, we are now grepping the "folder1/a" blob.
+# Because of the problem stated above 'grep within submodules is not expanded',
+# we don't have the ideal test environment yet.
+test_expect_success 'grep sparse directory within submodules' '
+       init_repos_as_submodules &&
+
+       cat >expect <<-\EOF &&
+       full-checkout/folder1/a:a
+       sparse-checkout/folder1/a:a
+       sparse-index/folder1/a:a
+       EOF
+       git grep --cached --recurse-submodules a -- "*/folder1/*" >actual &&
+       test_cmp actual expect
+'
+
+test_expect_success 'write-tree' '
+       init_repos &&
+
+       test_all_match git write-tree &&
+
+       write_script edit-contents <<-\EOF &&
+       echo text >>"$1"
+       EOF
+
+       # make a change inside the sparse cone
+       run_on_all ../edit-contents deep/a &&
+       test_all_match git update-index deep/a &&
+       test_all_match git write-tree &&
+       test_all_match git status --porcelain=v2 &&
+
+       # make a change outside the sparse cone
+       run_on_all mkdir -p folder1 &&
+       run_on_all cp a folder1/a &&
+       run_on_all ../edit-contents folder1/a &&
+       test_all_match git update-index folder1/a &&
+       test_all_match git write-tree &&
+       test_all_match git status --porcelain=v2 &&
+
+       # check that SKIP_WORKTREE files are not materialized
+       test_path_is_missing sparse-checkout/folder2/a &&
+       test_path_is_missing sparse-index/folder2/a
+'
+
+test_expect_success 'sparse-index is not expanded: write-tree' '
+       init_repos &&
+
+       ensure_not_expanded write-tree &&
+
+       echo "test1" >>sparse-index/a &&
+       git -C sparse-index update-index a &&
+       ensure_not_expanded write-tree
+'
+
+test_expect_success 'diff-files with pathspec inside sparse definition' '
+       init_repos &&
+
+       write_script edit-contents <<-\EOF &&
+       echo text >>"$1"
+       EOF
+
+       run_on_all ../edit-contents deep/a &&
+
+       test_all_match git diff-files &&
+
+       test_all_match git diff-files -- deep/a &&
+
+       # test wildcard
+       test_all_match git diff-files -- "deep/*"
+'
+
+test_expect_success 'diff-files with pathspec outside sparse definition' '
+       init_repos &&
+
+       test_sparse_match git diff-files -- folder2/a &&
+
+       write_script edit-contents <<-\EOF &&
+       echo text >>"$1"
+       EOF
+
+       # The directory "folder1" is outside the cone of interest
+       # and will not exist in the sparse checkout repositories.
+       # Create it as needed, add file "folder1/a" there with
+       # contents that is different from the staged version.
+       run_on_all mkdir -p folder1 &&
+       run_on_all cp a folder1/a &&
+
+       run_on_all ../edit-contents folder1/a &&
+       test_all_match git diff-files &&
+       test_all_match git diff-files -- folder1/a &&
+       test_all_match git diff-files -- "folder*/a"
+'
+
+test_expect_success 'sparse index is not expanded: diff-files' '
+       init_repos &&
+
+       write_script edit-contents <<-\EOF &&
+       echo text >>"$1"
+       EOF
+
+       run_on_all ../edit-contents deep/a &&
+
+       ensure_not_expanded diff-files &&
+       ensure_not_expanded diff-files -- deep/a &&
+       ensure_not_expanded diff-files -- "deep/*"
+'
+
+test_expect_success 'diff-tree' '
+       init_repos &&
+
+       # Test change inside sparse cone
+       tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
+       tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
+       test_all_match git diff-tree $tree1 $tree2 &&
+       test_all_match git diff-tree $tree1 $tree2 -- deep/a &&
+       test_all_match git diff-tree HEAD update-deep &&
+       test_all_match git diff-tree HEAD update-deep -- deep/a &&
+
+       # Test change outside sparse cone
+       tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
+       test_all_match git diff-tree $tree1 $tree3 &&
+       test_all_match git diff-tree $tree1 $tree3 -- folder1/a &&
+       test_all_match git diff-tree HEAD update-folder1 &&
+       test_all_match git diff-tree HEAD update-folder1 -- folder1/a &&
+
+       # Check that SKIP_WORKTREE files are not materialized
+       test_path_is_missing sparse-checkout/folder1/a &&
+       test_path_is_missing sparse-index/folder1/a &&
+       test_path_is_missing sparse-checkout/folder2/a &&
+       test_path_is_missing sparse-index/folder2/a
+'
+
+test_expect_success 'sparse-index is not expanded: diff-tree' '
+       init_repos &&
+
+       tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
+       tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
+       tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
+
+       ensure_not_expanded diff-tree $tree1 $tree2 &&
+       ensure_not_expanded diff-tree $tree1 $tree2 -- deep/a &&
+       ensure_not_expanded diff-tree HEAD update-deep &&
+       ensure_not_expanded diff-tree HEAD update-deep -- deep/a &&
+       ensure_not_expanded diff-tree $tree1 $tree3 &&
+       ensure_not_expanded diff-tree $tree1 $tree3 -- folder1/a &&
+       ensure_not_expanded diff-tree HEAD update-folder1 &&
+       ensure_not_expanded diff-tree HEAD update-folder1 -- folder1/a
+'
+
+test_expect_success 'worktree' '
+       init_repos &&
+
+       write_script edit-contents <<-\EOF &&
+       echo text >>"$1"
+       EOF
+
+       for repo in full-checkout sparse-checkout sparse-index
+       do
+               worktree=${repo}-wt &&
+               git -C $repo worktree add ../$worktree &&
+
+               # Compare worktree content with "ls"
+               (cd $repo && ls) >worktree_contents &&
+               (cd $worktree && ls) >new_worktree_contents &&
+               test_cmp worktree_contents new_worktree_contents &&
+
+               # Compare index content with "ls-files --sparse"
+               git -C $repo ls-files --sparse >index_contents &&
+               git -C $worktree ls-files --sparse >new_index_contents &&
+               test_cmp index_contents new_index_contents &&
+
+               git -C $repo worktree remove ../$worktree || return 1
+       done &&
+
+       test_all_match git worktree add .worktrees/hotfix &&
+       run_on_all ../edit-contents .worktrees/hotfix/deep/a &&
+       test_all_match test_must_fail git worktree remove .worktrees/hotfix
+'
+
+test_expect_success 'worktree is not expanded' '
+       init_repos &&
+
+       ensure_not_expanded worktree add .worktrees/hotfix &&
+       ensure_not_expanded worktree remove .worktrees/hotfix
+'
+
+test_expect_success 'check-attr with pathspec inside sparse definition' '
+       init_repos &&
+
+       echo "a -crlf myAttr" >>.gitattributes &&
+       run_on_all cp ../.gitattributes ./deep &&
+
+       test_all_match git check-attr -a -- deep/a &&
+
+       test_all_match git add deep/.gitattributes &&
+       test_all_match git check-attr -a --cached -- deep/a
+'
+
+test_expect_success 'check-attr with pathspec outside sparse definition' '
+       init_repos &&
+
+       echo "a -crlf myAttr" >>.gitattributes &&
+       run_on_sparse mkdir folder1 &&
+       run_on_all cp ../.gitattributes ./folder1 &&
+       run_on_all cp a folder1/a &&
+
+       test_all_match git check-attr -a -- folder1/a &&
+
+       git -C full-checkout add folder1/.gitattributes &&
+       test_sparse_match git add --sparse folder1/.gitattributes &&
+       test_all_match git commit -m "add .gitattributes" &&
+       test_sparse_match git sparse-checkout reapply &&
+       test_all_match git check-attr -a --cached -- folder1/a
+'
+
+# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due
+# to an underlying issue in oneway_diff() within diff-lib.c.
+# 'do_oneway_diff()' is not called as expected for paths that could match
+# inside of a sparse directory. Specifically, the 'ce_path_match()' function
+# fails to recognize files inside a sparse directory (e.g., when 'folder1/'
+# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to
+# proceed with 'do_oneway_diff()' if the pathspec could match inside of a
+# sparse directory.
+test_expect_failure 'diff --check with pathspec outside sparse definition' '
+       init_repos &&
+
+       write_script edit-contents <<-\EOF &&
+       echo "a " >"$1"
+       EOF
+
+       test_all_match git config core.whitespace -trailing-space,-space-before-tab &&
+
+       echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes &&
+       run_on_all mkdir -p folder1 &&
+       run_on_all cp ../.gitattributes ./folder1 &&
+       test_all_match git add --sparse folder1/.gitattributes &&
+       run_on_all ../edit-contents folder1/a &&
+       test_all_match git add --sparse folder1/a &&
+
+       test_sparse_match git sparse-checkout reapply &&
+       test_all_match test_must_fail git diff --check --cached -- folder1/a
+'
+
+test_expect_success 'sparse-index is not expanded: check-attr' '
+       init_repos &&
+
+       echo "a -crlf myAttr" >>.gitattributes &&
+       mkdir ./sparse-index/folder1 &&
+       cp ./sparse-index/a ./sparse-index/folder1/a &&
+       cp .gitattributes ./sparse-index/deep &&
+       cp .gitattributes ./sparse-index/folder1 &&
+
+       git -C sparse-index add deep/.gitattributes &&
+       git -C sparse-index add --sparse folder1/.gitattributes &&
+       ensure_not_expanded check-attr -a --cached -- deep/a &&
+       ensure_not_expanded check-attr -a --cached -- folder1/a
+'
+
 test_done
index e8505bd39cd732a9c2339a226dfa07e95bee54e8..f4e27521344920ca5cf85b20a263487e2d794b52 100755 (executable)
@@ -98,6 +98,23 @@ test_expect_success 'subsections are not canonicalized by git-config' '
        test_cmp_config two section.SubSection.key
 '
 
+test_missing_key () {
+       local key="$1" &&
+       local title="$2" &&
+       test_expect_success "value for $title is not printed" '
+               test_must_fail git config "$key" >out 2>err &&
+               test_must_be_empty out &&
+               test_must_be_empty err
+       '
+}
+
+test_missing_key 'missingsection.missingkey' 'missing section and missing key'
+test_missing_key 'missingsection.penguin' 'missing section and existing key'
+test_missing_key 'section.missingkey' 'existing section and missing key'
+test_missing_key 'section.MissingSubSection.missingkey' 'missing subsection and missing key'
+test_missing_key 'section.SubSection.missingkey' 'existing subsection and missing key'
+test_missing_key 'section.MissingSubSection.key' 'missing subsection and existing key'
+
 cat > .git/config <<\EOF
 [alpha]
 bar = foo
@@ -436,7 +453,7 @@ test_expect_success 'get bool variable with empty value' '
 
 test_expect_success 'no arguments, but no crash' '
        test_must_fail git config >output 2>&1 &&
-       test_i18ngrep usage output
+       test_grep usage output
 '
 
 cat > .git/config << EOF
@@ -703,25 +720,25 @@ test_expect_success 'invalid unit' '
        git config aninvalid.unit "1auto" &&
        test_cmp_config 1auto aninvalid.unit &&
        test_must_fail git config --int --get aninvalid.unit 2>actual &&
-       test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
+       test_grep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
 '
 
 test_expect_success 'invalid unit boolean' '
        git config commit.gpgsign "1true" &&
        test_cmp_config 1true commit.gpgsign &&
        test_must_fail git config --bool --get commit.gpgsign 2>actual &&
-       test_i18ngrep "bad boolean config value .1true. for .commit.gpgsign." actual
+       test_grep "bad boolean config value .1true. for .commit.gpgsign." actual
 '
 
 test_expect_success 'line number is reported correctly' '
        printf "[bool]\n\tvar\n" >invalid &&
        test_must_fail git config -f invalid --path bool.var 2>actual &&
-       test_i18ngrep "line 2" actual
+       test_grep "line 2" actual
 '
 
 test_expect_success 'invalid stdin config' '
        echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
-       test_i18ngrep "bad config line 1 in standard input" output
+       test_grep "bad config line 1 in standard input" output
 '
 
 cat > expect << EOF
@@ -902,7 +919,7 @@ test_expect_success !MINGW 'get --path copes with unset $HOME' '
                git config --get --path path.normal >>result &&
                git config --get --path path.trailingtilde >>result
        ) &&
-       test_i18ngrep "[Ff]ailed to expand.*~/" msg &&
+       test_grep "[Ff]ailed to expand.*~/" msg &&
        test_cmp expect result
 '
 
@@ -969,7 +986,7 @@ test_expect_success 'get --type=color barfs on non-color' '
 
 test_expect_success 'set --type=color barfs on non-color' '
        test_must_fail git config --type=color foo.color "not-a-color" 2>error &&
-       test_i18ngrep "cannot parse color" error
+       test_grep "cannot parse color" error
 '
 
 cat > expect << EOF
@@ -1430,12 +1447,12 @@ test_expect_success 'git --config-env with missing value' '
 
 test_expect_success 'git --config-env fails with invalid parameters' '
        test_must_fail git --config-env=foo.flag config --bool foo.flag 2>error &&
-       test_i18ngrep "invalid config format: foo.flag" error &&
+       test_grep "invalid config format: foo.flag" error &&
        test_must_fail git --config-env=foo.flag= config --bool foo.flag 2>error &&
-       test_i18ngrep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
+       test_grep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
        sane_unset NONEXISTENT &&
        test_must_fail git --config-env=foo.flag=NONEXISTENT config --bool foo.flag 2>error &&
-       test_i18ngrep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
+       test_grep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
 '
 
 test_expect_success 'git -c and --config-env work together' '
@@ -1488,55 +1505,49 @@ test_expect_success 'git config ignores pairs without count' '
        test_must_be_empty error
 '
 
-test_expect_success 'git config ignores pairs with zero count' '
-       test_must_fail env \
-               GIT_CONFIG_COUNT=0 \
-               GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
-               git config pair.one
-'
-
 test_expect_success 'git config ignores pairs exceeding count' '
        GIT_CONFIG_COUNT=1 \
                GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
                GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
-               git config --get-regexp "pair.*" >actual &&
+               git config --get-regexp "pair.*" >actual 2>error &&
        cat >expect <<-EOF &&
        pair.one value
        EOF
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test_must_be_empty error
 '
 
 test_expect_success 'git config ignores pairs with zero count' '
        test_must_fail env \
                GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
-               git config pair.one >error &&
+               git config pair.one 2>error &&
        test_must_be_empty error
 '
 
 test_expect_success 'git config ignores pairs with empty count' '
        test_must_fail env \
                GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
-               git config pair.one >error &&
+               git config pair.one 2>error &&
        test_must_be_empty error
 '
 
 test_expect_success 'git config fails with invalid count' '
        test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
-       test_i18ngrep "bogus count" error &&
+       test_grep "bogus count" error &&
        test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
-       test_i18ngrep "too many entries" error
+       test_grep "too many entries" error
 '
 
 test_expect_success 'git config fails with missing config key' '
        test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
                git config --list 2>error &&
-       test_i18ngrep "missing config key" error
+       test_grep "missing config key" error
 '
 
 test_expect_success 'git config fails with missing config value' '
        test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
                git config --list 2>error &&
-       test_i18ngrep "missing config value" error
+       test_grep "missing config value" error
 '
 
 test_expect_success 'git config fails with invalid config pair key' '
@@ -1601,12 +1612,12 @@ test_expect_success 'git config --edit respects core.editor' '
 # malformed configuration files
 test_expect_success 'barf on syntax error' '
        cat >.git/config <<-\EOF &&
-       # broken section line
+       # broken key=value
        [section]
        key garbage
        EOF
-       test_must_fail git config --get section.key >actual 2>error &&
-       test_i18ngrep " line 3 " error
+       test_must_fail git config --get section.key 2>error &&
+       test_grep " line 3 " error
 '
 
 test_expect_success 'barf on incomplete section header' '
@@ -1615,18 +1626,18 @@ test_expect_success 'barf on incomplete section header' '
        [section
        key = value
        EOF
-       test_must_fail git config --get section.key >actual 2>error &&
-       test_i18ngrep " line 2 " error
+       test_must_fail git config --get section.key 2>error &&
+       test_grep " line 2 " error
 '
 
 test_expect_success 'barf on incomplete string' '
        cat >.git/config <<-\EOF &&
-       # broken section line
+       # broken value string
        [section]
        key = "value string
        EOF
-       test_must_fail git config --get section.key >actual 2>error &&
-       test_i18ngrep " line 3 " error
+       test_must_fail git config --get section.key 2>error &&
+       test_grep " line 3 " error
 '
 
 test_expect_success 'urlmatch' '
@@ -1657,6 +1668,21 @@ test_expect_success 'urlmatch' '
        test_cmp expect actual
 '
 
+test_expect_success 'urlmatch with --show-scope' '
+       cat >.git/config <<-\EOF &&
+       [http "https://weak.example.com"]
+               sslVerify = false
+               cookieFile = /tmp/cookie.txt
+       EOF
+
+       cat >expect <<-EOF &&
+       local   http.cookiefile /tmp/cookie.txt
+       local   http.sslverify false
+       EOF
+       git config --get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'urlmatch favors more specific URLs' '
        cat >.git/config <<-\EOF &&
        [http "https://example.com/"]
@@ -2044,6 +2070,12 @@ test_expect_success '--show-origin blob ref' '
        test_cmp expect output
 '
 
+test_expect_success '--show-origin with --default' '
+       git config --show-origin --default foo some.key >actual &&
+       echo "command line:     foo" >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success '--show-scope with --list' '
        cat >expect <<-EOF &&
        global  user.global=true
@@ -2112,6 +2144,12 @@ test_expect_success '--show-scope with --show-origin' '
        test_cmp expect output
 '
 
+test_expect_success '--show-scope with --default' '
+       git config --show-scope --default foo some.key >actual &&
+       echo "command   foo" >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'override global and system config' '
        test_when_finished rm -f \"\$HOME\"/.gitconfig &&
        cat >"$HOME"/.gitconfig <<-EOF &&
@@ -2228,17 +2266,17 @@ test_expect_success 'identical mixed --type specifiers are allowed' '
 
 test_expect_success 'non-identical modern --type specifiers are not allowed' '
        test_must_fail git config --type=int --type=bool section.big 2>error &&
-       test_i18ngrep "only one type at a time" error
+       test_grep "only one type at a time" error
 '
 
 test_expect_success 'non-identical legacy --type specifiers are not allowed' '
        test_must_fail git config --int --bool section.big 2>error &&
-       test_i18ngrep "only one type at a time" error
+       test_grep "only one type at a time" error
 '
 
 test_expect_success 'non-identical mixed --type specifiers are not allowed' '
        test_must_fail git config --type=int --bool section.big 2>error &&
-       test_i18ngrep "only one type at a time" error
+       test_grep "only one type at a time" error
 '
 
 test_expect_success '--type allows valid type specifiers' '
@@ -2255,7 +2293,13 @@ test_expect_success 'unset type specifiers may be reset to conflicting ones' '
 
 test_expect_success '--type rejects unknown specifiers' '
        test_must_fail git config --type=nonsense section.foo 2>error &&
-       test_i18ngrep "unrecognized --type argument" error
+       test_grep "unrecognized --type argument" error
+'
+
+test_expect_success '--type=int requires at least one digit' '
+       test_must_fail git config --type int --default m some.key >out 2>error &&
+       grep "bad numeric config value" error &&
+       test_must_be_empty out
 '
 
 test_expect_success '--replace-all does not invent newlines' '
@@ -2295,7 +2339,7 @@ test_expect_success 'set all config with value-pattern' '
 
        # multiple matches => failure
        test_must_fail git config --file=config abc.key three o+ 2>err &&
-       test_i18ngrep "has multiple values" err &&
+       test_grep "has multiple values" err &&
 
        # multiple values, no match => add
        git config --file=config abc.key three a+ &&
index 93a2f91f8a5181c5307c6de14ab6f2823d2b959a..e5a0d65caa3e4cc363319826f37a4254bfc99b33 100755 (executable)
@@ -8,6 +8,8 @@ test_description='Test shared repository initialization'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Remove a default ACL from the test dir if possible.
@@ -25,6 +27,7 @@ test_expect_success 'shared = 0400 (faulty permission u-w)' '
 for u in 002 022
 do
        test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" '
+               test_when_finished "rm -rf sub" &&
                mkdir sub && (
                        cd sub &&
                        umask $u &&
@@ -42,16 +45,35 @@ do
                        ;;
                esac
        '
-       rm -rf sub
 done
 
 test_expect_success 'shared=all' '
-       mkdir sub &&
-       cd sub &&
        git init --template= --shared=all &&
        test 2 = $(git config core.sharedrepository)
 '
 
+test_expect_failure 'template can set core.bare' '
+       test_when_finished "rm -rf subdir" &&
+       test_when_finished "rm -rf templates" &&
+       test_config core.bare true &&
+       umask 0022 &&
+       mkdir -p templates/ &&
+       cp .git/config templates/config &&
+       git init --template=templates subdir &&
+       test_path_exists subdir/HEAD
+'
+
+test_expect_success 'template can set core.bare but overridden by command line' '
+       test_when_finished "rm -rf subdir" &&
+       test_when_finished "rm -rf templates" &&
+       test_config core.bare true &&
+       umask 0022 &&
+       mkdir -p templates/ &&
+       cp .git/config templates/config &&
+       git init --no-bare --template=templates subdir &&
+       test_path_exists subdir/.git/HEAD
+'
+
 test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
        : > a1 &&
        git add a1 &&
@@ -89,7 +111,7 @@ do
                rm -f .git/info/refs &&
                git update-server-info &&
                actual="$(test_modebits .git/info/refs)" &&
-               verbose test "x$actual" = "x-$y"
+               test "x$actual" = "x-$y"
 
        '
 
@@ -99,7 +121,7 @@ do
                rm -f .git/info/refs &&
                git update-server-info &&
                actual="$(test_modebits .git/info/refs)" &&
-               verbose test "x$actual" = "x-$x"
+               test "x$actual" = "x-$x"
 
        '
 
@@ -132,6 +154,7 @@ test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
 '
 
 test_expect_success POSIXPERM 'forced modes' '
+       test_when_finished "rm -rf new" &&
        mkdir -p templates/hooks &&
        echo update-server-info >templates/hooks/post-update &&
        chmod +x templates/hooks/post-update &&
@@ -140,7 +163,8 @@ test_expect_success POSIXPERM 'forced modes' '
        (
                cd new &&
                umask 002 &&
-               git init --shared=0660 --template=templates &&
+               git init --shared=0660 --template=../templates &&
+               test_path_is_file .git/hooks/post-update &&
                >frotz &&
                git add frotz &&
                git commit -a -m initial &&
@@ -173,6 +197,7 @@ test_expect_success POSIXPERM 'forced modes' '
 '
 
 test_expect_success POSIXPERM 'remote init does not use config from cwd' '
+       test_when_finished "rm -rf child.git" &&
        git config core.sharedrepository 0666 &&
        umask 0022 &&
        git init --bare child.git &&
@@ -192,7 +217,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (local)' '
 '
 
 test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)' '
-       rm -rf child.git &&
+       test_when_finished "rm -rf child.git" &&
        umask 0022 &&
        git init --bare --shared=0666 child.git &&
        test_path_is_missing child.git/foo &&
@@ -203,7 +228,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)'
 '
 
 test_expect_success POSIXPERM 'template can set core.sharedrepository' '
-       rm -rf child.git &&
+       test_when_finished "rm -rf child.git" &&
        umask 0022 &&
        git config core.sharedrepository 0666 &&
        cp .git/config templates/config &&
index 0acabb6d11b48aca54e73025965f4c4e6b561d34..179474fa651e159bf68714620ab4f1be2c259fe5 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Test repository version check'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -27,7 +28,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'gitdir selection on normal repos' '
-       echo $(test_oid version) >expect &&
+       test_oid version >expect &&
        git config core.repositoryformatversion >actual &&
        git -C test config core.repositoryformatversion >actual2 &&
        test_cmp expect actual &&
@@ -36,7 +37,7 @@ test_expect_success 'gitdir selection on normal repos' '
 
 test_expect_success 'gitdir selection on unsupported repo' '
        # Make sure it would stop at test2, not trash
-       test_expect_code 1 git -C test2 config core.repositoryformatversion >actual
+       test_expect_code 1 git -C test2 config core.repositoryformatversion
 '
 
 test_expect_success 'gitdir not required mode' '
index 335d3f3211aa874fd3a8e0d0006dd9fc53a4e589..31b89dd9693afc95f30e0bb9e11dc704fda24b41 100755 (executable)
@@ -9,6 +9,7 @@ test_description='Test repository with default ACL'
 # => this must come before . ./test-lib.sh
 umask 077
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # We need an arbitrary other user give permission to using ACLs. root
@@ -18,7 +19,7 @@ test_expect_success 'checking for a working acl setup' '
        if setfacl -m d:m:rwx -m u:root:rwx . &&
           getfacl . | grep user:root:rwx &&
           touch should-have-readable-acl &&
-          getfacl should-have-readable-acl | egrep "mask::?rw-"
+          getfacl should-have-readable-acl | grep -E "mask::?rw-"
        then
                test_set_prereq SETFACL
        fi
@@ -34,7 +35,7 @@ check_perms_and_acl () {
        getfacl "$1" > actual &&
        grep -q "user:root:rwx" actual &&
        grep -q "user:${LOGNAME}:rwx" actual &&
-       egrep "mask::?r--" actual > /dev/null 2>&1 &&
+       grep -E "mask::?r--" actual > /dev/null 2>&1 &&
        grep -q "group::---" actual || false
 }
 
index 0a7099d6f52b68cf9abc263933267925a994378a..b9852fe40e4a045ae4a355a85aa6e2d14d758479 100755 (executable)
@@ -63,7 +63,7 @@ test_expect_success 'parse errors in blobs are properly attributed' '
        git commit -m broken &&
 
        test_must_fail git config --blob=HEAD:config some.value 2>err &&
-       test_i18ngrep "HEAD:config" err
+       test_grep "HEAD:config" err
 '
 
 test_expect_success 'can parse blob ending with CR' '
index b38e158d3b212a0157f5d0f862103713c6a39e57..3bfec07f1abcc1d997f428e84acf6cc3490b1b6a 100755 (executable)
@@ -58,6 +58,8 @@ test_expect_success 'setup default config' '
                skin = false
                nose = 1
                horns
+       [value]
+               less
        EOF
 '
 
@@ -116,10 +118,53 @@ test_expect_success 'find value with the highest priority' '
        check_config get_value case.baz "hask"
 '
 
+test_expect_success 'return value for an existing key' '
+       test-tool config get lamb.chop >out 2>err &&
+       test_must_be_empty out &&
+       test_must_be_empty err
+'
+
+test_expect_success 'return value for value-less key' '
+       test-tool config get value.less >out 2>err &&
+       test_must_be_empty out &&
+       test_must_be_empty err
+'
+
+test_expect_success 'return value for a missing key' '
+       cat >expect <<-\EOF &&
+       Value not found for "missing.key"
+       EOF
+       test_expect_code 1 test-tool config get missing.key >actual 2>err &&
+       test_cmp actual expect &&
+       test_must_be_empty err
+'
+
+test_expect_success 'return value for a bad key: CONFIG_INVALID_KEY' '
+       cat >expect <<-\EOF &&
+       Key "fails.iskeychar.-" is invalid
+       EOF
+       test_expect_code 1 test-tool config get fails.iskeychar.- >actual 2>err &&
+       test_cmp actual expect &&
+       test_must_be_empty out
+'
+
+test_expect_success 'return value for a bad key: CONFIG_NO_SECTION_OR_NAME' '
+       cat >expect <<-\EOF &&
+       Key "keynosection" has no section
+       EOF
+       test_expect_code 1 test-tool config get keynosection >actual 2>err &&
+       test_cmp actual expect &&
+       test_must_be_empty out
+'
+
 test_expect_success 'find integer value for a key' '
        check_config get_int lamb.chop 65
 '
 
+test_expect_success 'parse integer value during iteration' '
+       check_config git_config_int lamb.chop 65
+'
+
 test_expect_success 'find string value for a key' '
        check_config get_string case.baz hask &&
        check_config expect_code 1 get_string case.ba "Value not found for \"case.ba\""
@@ -127,13 +172,18 @@ test_expect_success 'find string value for a key' '
 
 test_expect_success 'check line error when NULL string is queried' '
        test_expect_code 128 test-tool config get_string case.foo 2>result &&
-       test_i18ngrep "fatal: .*case\.foo.*\.git/config.*line 7" result
+       test_grep "fatal: .*case\.foo.*\.git/config.*line 7" result
 '
 
 test_expect_success 'find integer if value is non parse-able' '
        check_config expect_code 128 get_int lamb.head
 '
 
+test_expect_success 'non parse-able integer value during iteration' '
+       check_config expect_code 128 git_config_int lamb.head 2>result &&
+       grep "fatal: bad numeric config value .* in file \.git/config" result
+'
+
 test_expect_success 'find bool value for the entered key' '
        check_config get_bool goat.head 1 &&
        check_config get_bool goat.skin 0 &&
@@ -146,6 +196,71 @@ test_expect_success 'find multiple values' '
        check_config get_value_multi case.baz sam bat hask
 '
 
+test_NULL_in_multi () {
+       local op="$1" &&
+       local file="$2" &&
+
+       test_expect_success "$op: NULL value in config${file:+ in $file}" '
+               config="$file" &&
+               if test -z "$config"
+               then
+                       config=.git/config &&
+                       test_when_finished "mv $config.old $config" &&
+                       mv "$config" "$config".old
+               fi &&
+
+               # Value-less in the middle of a list
+               cat >"$config" <<-\EOF &&
+               [a]key=x
+               [a]key
+               [a]key=y
+               EOF
+               case "$op" in
+               *_multi)
+                       cat >expect <<-\EOF
+                       x
+                       (NULL)
+                       y
+                       EOF
+                       ;;
+               *)
+                       cat >expect <<-\EOF
+                       y
+                       EOF
+                       ;;
+               esac &&
+               test-tool config "$op" a.key $file >actual &&
+               test_cmp expect actual &&
+
+               # Value-less at the end of a least
+               cat >"$config" <<-\EOF &&
+               [a]key=x
+               [a]key=y
+               [a]key
+               EOF
+               case "$op" in
+               *_multi)
+                       cat >expect <<-\EOF
+                       x
+                       y
+                       (NULL)
+                       EOF
+                       ;;
+               *)
+                       cat >expect <<-\EOF
+                       (NULL)
+                       EOF
+                       ;;
+               esac &&
+               test-tool config "$op" a.key $file >actual &&
+               test_cmp expect actual
+       '
+}
+
+test_NULL_in_multi "get_value_multi"
+test_NULL_in_multi "configset_get_value" "my.config"
+test_NULL_in_multi "configset_get_value_multi" "my.config"
+
 test_expect_success 'find value from a configset' '
        cat >config2 <<-\EOF &&
        [case]
@@ -207,7 +322,7 @@ test_expect_success 'proper error on error in default config files' '
        cp .git/config .git/config.old &&
        test_when_finished "mv .git/config.old .git/config" &&
        echo "[" >>.git/config &&
-       echo "fatal: bad config line 34 in file .git/config" >expect &&
+       echo "fatal: bad config line 36 in file .git/config" >expect &&
        test_expect_code 128 test-tool config get_value foo.bar 2>actual &&
        test_cmp expect actual
 '
@@ -227,14 +342,14 @@ test_expect_success 'check line errors for malformed values' '
                br
        EOF
        test_expect_code 128 git br 2>result &&
-       test_i18ngrep "missing value for .alias\.br" result &&
-       test_i18ngrep "fatal: .*\.git/config" result &&
-       test_i18ngrep "fatal: .*line 2" result
+       test_grep "missing value for .alias\.br" result &&
+       test_grep "fatal: .*\.git/config" result &&
+       test_grep "fatal: .*line 2" result
 '
 
 test_expect_success 'error on modifying repo config without repo' '
        nongit test_must_fail git config a.b c 2>err &&
-       test_i18ngrep "not in a git directory" err
+       test_grep "not in a git directory" err
 '
 
 cmdline_config="'foo.bar=from-cmdline'"
index 537435b90ae9314a8062c81186fd00853d38b230..523aa99a1e264661c9af03e450ff14c345831617 100755 (executable)
@@ -78,7 +78,7 @@ test_with_config () {
 
 test_expect_success 'ignore .git/ with incompatible repository version' '
        test_with_config "[core]repositoryformatversion = 999999" 2>err &&
-       test_i18ngrep "warning:.* Expected git repo version <= [1-9]" err
+       test_grep "warning:.* Expected git repo version <= [1-9]" err
 '
 
 test_expect_failure 'ignore .git/ with invalid repository version' '
index 09b10c144ba9d162635a12d66b8796b9cd94fb6c..1a90d31201a612b69944bffba27b347eeb0ab580 100755 (executable)
@@ -26,12 +26,12 @@ test_expect_success 'canonicalizes --default with appropriate type' '
 test_expect_success 'dies when --default cannot be parsed' '
        test_must_fail git config -f config --type=expiry-date --default=x --get \
                not.a.section 2>error &&
-       test_i18ngrep "failed to format default config value" error
+       test_grep "failed to format default config value" error
 '
 
 test_expect_success 'does not allow --default without --get' '
        test_must_fail git config --default=quux --unset a.section >output 2>&1 &&
-       test_i18ngrep "\-\-default is only applicable to" output
+       test_grep "\-\-default is only applicable to" output
 '
 
 test_done
index cf58cf025cd2af621f6d58cdaf02d3be0480190c..f18843bf7aa293d26ef1aabc7fbb8f2c1a8dca03 100755 (executable)
@@ -9,8 +9,6 @@ test_description='Test git update-ref and basic ref logging'
 Z=$ZERO_OID
 
 m=refs/heads/main
-n_dir=refs/heads/gu
-n=$n_dir/fixes
 outside=refs/foo
 bare=bare-repo
 
@@ -62,10 +60,10 @@ test_expect_success "delete $m without oldvalue verification" '
        test_must_fail git show-ref --verify -q $m
 '
 
-test_expect_success "fail to create $n" '
-       test_when_finished "rm -f .git/$n_dir" &&
-       touch .git/$n_dir &&
-       test_must_fail git update-ref $n $A
+test_expect_success "fail to create $n due to file/directory conflict" '
+       test_when_finished "git update-ref -d refs/heads/gu" &&
+       git update-ref refs/heads/gu $A &&
+       test_must_fail git update-ref refs/heads/gu/fixes $A
 '
 
 test_expect_success "create $m (by HEAD)" '
@@ -92,7 +90,8 @@ test_expect_success "deleting current branch adds message to HEAD's log" '
        git symbolic-ref HEAD $m &&
        git update-ref -m delete-$m -d $m &&
        test_must_fail git show-ref --verify -q $m &&
-       grep "delete-$m$" .git/logs/HEAD
+       test-tool ref-store main for-each-reflog-ent HEAD >actual &&
+       grep "delete-$m$" actual
 '
 
 test_expect_success "deleting by HEAD adds message to HEAD's log" '
@@ -101,7 +100,8 @@ test_expect_success "deleting by HEAD adds message to HEAD's log" '
        git symbolic-ref HEAD $m &&
        git update-ref -m delete-by-head -d HEAD &&
        test_must_fail git show-ref --verify -q $m &&
-       grep "delete-by-head$" .git/logs/HEAD
+       test-tool ref-store main for-each-reflog-ent HEAD >actual &&
+       grep "delete-by-head$" actual
 '
 
 test_expect_success 'update-ref does not create reflogs by default' '
@@ -132,7 +132,7 @@ test_expect_success 'creates no reflog in bare repository' '
 
 test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' '
        test_when_finished "git -C $bare config --unset core.logAllRefUpdates && \
-               rm $bare/logs/$m" &&
+               test-tool ref-store main delete-reflog $m" &&
        git -C $bare config core.logAllRefUpdates true &&
        git -C $bare update-ref $m $bareB &&
        git -C $bare rev-parse $bareB >expect &&
@@ -221,27 +221,27 @@ test_expect_success 'delete symref without dereference when the referred ref is
 '
 
 test_expect_success 'update-ref -d is not confused by self-reference' '
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self" &&
        git symbolic-ref refs/heads/self refs/heads/self &&
-       test_when_finished "rm -f .git/refs/heads/self" &&
-       test_path_is_file .git/refs/heads/self &&
+       git symbolic-ref --no-recurse refs/heads/self &&
        test_must_fail git update-ref -d refs/heads/self &&
-       test_path_is_file .git/refs/heads/self
+       git symbolic-ref --no-recurse refs/heads/self
 '
 
 test_expect_success 'update-ref --no-deref -d can delete self-reference' '
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self" &&
        git symbolic-ref refs/heads/self refs/heads/self &&
-       test_when_finished "rm -f .git/refs/heads/self" &&
-       test_path_is_file .git/refs/heads/self &&
+       git symbolic-ref --no-recurse refs/heads/self &&
        git update-ref --no-deref -d refs/heads/self &&
        test_must_fail git show-ref --verify -q refs/heads/self
 '
 
-test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
+test_expect_success REFFILES 'update-ref --no-deref -d can delete reference to bad ref' '
        >.git/refs/heads/bad &&
        test_when_finished "rm -f .git/refs/heads/bad" &&
        git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
        test_when_finished "git update-ref -d refs/heads/ref-to-bad" &&
-       test_path_is_file .git/refs/heads/ref-to-bad &&
+       git symbolic-ref --no-recurse refs/heads/ref-to-bad &&
        git update-ref --no-deref -d refs/heads/ref-to-bad &&
        test_must_fail git show-ref --verify -q refs/heads/ref-to-bad
 '
@@ -265,7 +265,10 @@ test_expect_success "(not) changed .git/$m" '
        ! test $B = $(git show-ref -s --verify $m)
 '
 
-rm -f .git/logs/refs/heads/main
+test_expect_success "clean up reflog" '
+       test-tool ref-store main delete-reflog $m
+'
+
 test_expect_success "create $m (logged by touch)" '
        test_config core.logAllRefUpdates false &&
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
@@ -285,7 +288,7 @@ test_expect_success "set $m (logged by touch)" '
        test $A = $(git show-ref -s --verify $m)
 '
 
-test_expect_success 'empty directory removal' '
+test_expect_success REFFILES 'empty directory removal' '
        git branch d1/d2/r1 HEAD &&
        git branch d1/r2 HEAD &&
        test_path_is_file .git/refs/heads/d1/d2/r1 &&
@@ -297,7 +300,7 @@ test_expect_success 'empty directory removal' '
        test_path_is_file .git/logs/refs/heads/d1/r2
 '
 
-test_expect_success 'symref empty directory removal' '
+test_expect_success REFFILES 'symref empty directory removal' '
        git branch e1/e2/r1 HEAD &&
        git branch e1/r2 HEAD &&
        git checkout e1/e2/r1 &&
@@ -318,7 +321,7 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000   Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
 EOF
 test_expect_success "verifying $m's log (logged by touch)" '
-       test_when_finished "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+       test_when_finished "git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect" &&
        test-tool ref-store main for-each-reflog-ent $m >actual &&
        test_cmp actual expect
 '
@@ -348,20 +351,34 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000 Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
 EOF
 test_expect_success "verifying $m's log (logged by config)" '
-       test_when_finished "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+       test_when_finished "git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect" &&
        test-tool ref-store main for-each-reflog-ent $m >actual &&
        test_cmp actual expect
 '
 
 test_expect_success 'set up for querying the reflog' '
+       git update-ref -d $m &&
+       test-tool ref-store main delete-reflog $m &&
+
+       GIT_COMMITTER_DATE="1117150320 -0500" git update-ref $m $C &&
+       GIT_COMMITTER_DATE="1117150350 -0500" git update-ref $m $A &&
+       GIT_COMMITTER_DATE="1117150380 -0500" git update-ref $m $B &&
+       GIT_COMMITTER_DATE="1117150680 -0500" git update-ref $m $F &&
+       GIT_COMMITTER_DATE="1117150980 -0500" git update-ref $m $E &&
        git update-ref $m $D &&
-       cat >.git/logs/$m <<-EOF
+       # Delete the last reflog entry so that the tip of m and the reflog for
+       # it disagree.
+       git reflog delete $m@{0} &&
+
+       cat >expect <<-EOF &&
        $Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
        $C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
        $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
-       $F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
-       $Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+       $B $F $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+       $F $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
        EOF
+       test-tool ref-store main for-each-reflog-ent $m >actual &&
+       test_cmp expect actual
 '
 
 ed="Thu, 26 May 2005 18:32:00 -0500"
@@ -409,13 +426,12 @@ test_expect_success 'Query "main@{2005-05-26 23:33:01}" (middle of history with
        test_when_finished "rm -f o e" &&
        git rev-parse --verify "main@{2005-05-26 23:33:01}" >o 2>e &&
        echo "$B" >expect &&
-       test_cmp expect o &&
-       test_i18ngrep -F "warning: log for ref $m has gap after $gd" e
+       test_cmp expect o
 '
 test_expect_success 'Query "main@{2005-05-26 23:38:00}" (middle of history)' '
        test_when_finished "rm -f o e" &&
        git rev-parse --verify "main@{2005-05-26 23:38:00}" >o 2>e &&
-       echo "$Z" >expect &&
+       echo "$F" >expect &&
        test_cmp expect o &&
        test_must_be_empty e
 '
@@ -431,10 +447,27 @@ test_expect_success 'Query "main@{2005-05-28}" (past end of history)' '
        git rev-parse --verify "main@{2005-05-28}" >o 2>e &&
        echo "$D" >expect &&
        test_cmp expect o &&
-       test_i18ngrep -F "warning: log for ref $m unexpectedly ended on $ld" e
+       test_grep -F "warning: log for ref $m unexpectedly ended on $ld" e
 '
 
-rm -f .git/$m .git/logs/$m expect
+rm -f expect
+git update-ref -d $m
+
+test_expect_success REFFILES 'query reflog with gap' '
+       test_when_finished "git update-ref -d $m" &&
+
+       git update-ref $m $F &&
+       cat >.git/logs/$m <<-EOF &&
+       $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
+       $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
+       $D $F $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+       EOF
+
+       git rev-parse --verify "main@{2005-05-26 23:33:01}" >actual 2>stderr &&
+       echo "$B" >expect &&
+       test_cmp expect actual &&
+       test_grep -F "warning: log for ref $m has gap after $gd" stderr
+'
 
 test_expect_success 'creating initial files' '
        test_when_finished rm -f M &&
@@ -486,7 +519,7 @@ test_expect_success 'git cat-file blob main@{2005-05-26 23:42}:F (expect OTHER)'
 test_expect_success 'given old value for missing pseudoref, do not create' '
        test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
        test_must_fail git rev-parse PSEUDOREF &&
-       test_i18ngrep "unable to resolve reference" err
+       test_grep "unable to resolve reference" err
 '
 
 test_expect_success 'create pseudoref' '
@@ -507,7 +540,7 @@ test_expect_success 'overwrite pseudoref with correct old value' '
 test_expect_success 'do not overwrite pseudoref with wrong old value' '
        test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
        test $C = $(git rev-parse PSEUDOREF) &&
-       test_i18ngrep "cannot lock ref.*expected" err
+       test_grep "cannot lock ref.*expected" err
 '
 
 test_expect_success 'delete pseudoref' '
@@ -519,7 +552,7 @@ test_expect_success 'do not delete pseudoref with wrong old value' '
        git update-ref PSEUDOREF $A &&
        test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
        test $A = $(git rev-parse PSEUDOREF) &&
-       test_i18ngrep "cannot lock ref.*expected" err
+       test_grep "cannot lock ref.*expected" err
 '
 
 test_expect_success 'delete pseudoref with correct old value' '
@@ -536,7 +569,7 @@ test_expect_success 'do not overwrite pseudoref with old OID zero' '
        test_when_finished git update-ref -d PSEUDOREF &&
        test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
        test $A = $(git rev-parse PSEUDOREF) &&
-       test_i18ngrep "already exists" err
+       test_grep "already exists" err
 '
 
 # Test --stdin
@@ -556,7 +589,7 @@ test_expect_success 'stdin test setup' '
 
 test_expect_success '-z fails without --stdin' '
        test_must_fail git update-ref -z $m $m $m 2>err &&
-       test_i18ngrep "usage: git update-ref" err
+       test_grep "usage: git update-ref" err
 '
 
 test_expect_success 'stdin works with no input' '
@@ -674,7 +707,7 @@ test_expect_success 'stdin fails with duplicate refs' '
        create $a $m
        EOF
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
+       test_grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
 '
 
 test_expect_success 'stdin create ref works' '
@@ -1107,7 +1140,7 @@ test_expect_success 'stdin -z fails option with unknown name' '
 test_expect_success 'stdin -z fails with duplicate refs' '
        printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
+       test_grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
 '
 
 test_expect_success 'stdin -z create ref works' '
@@ -1338,7 +1371,7 @@ test_expect_success 'fails with duplicate HEAD update' '
        update HEAD $B
        EOF
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       test_i18ngrep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
+       test_grep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
        echo "refs/heads/target1" >expect &&
        git symbolic-ref HEAD >actual &&
        test_cmp expect actual &&
@@ -1355,7 +1388,7 @@ test_expect_success 'fails with duplicate ref update via symref' '
        update refs/heads/symref2 $B
        EOF
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       test_i18ngrep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
+       test_grep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
        echo "refs/heads/target2" >expect &&
        git symbolic-ref refs/heads/symref2 >actual &&
        test_cmp expect actual &&
@@ -1568,6 +1601,7 @@ test_expect_success 'transaction can create and delete' '
        EOF
        git update-ref --stdin <stdin >actual &&
        printf "%s: ok\n" start commit start commit >expect &&
+       test_cmp expect actual &&
        test_must_fail git show-ref --verify refs/heads/create-and-delete
 '
 
@@ -1595,6 +1629,8 @@ test_expect_success 'transaction cannot restart ongoing transaction' '
        commit
        EOF
        test_must_fail git update-ref --stdin <stdin >actual &&
+       printf "%s: ok\n" start >expect &&
+       test_cmp expect actual &&
        test_must_fail git show-ref --verify refs/heads/restart
 '
 
@@ -1632,7 +1668,7 @@ test_expect_success PIPE 'transaction flushes status updates' '
        test_cmp expected actual
 '
 
-test_expect_success 'directory not created deleting packed ref' '
+test_expect_success REFFILES 'directory not created deleting packed ref' '
        git branch d1/d2/r1 HEAD &&
        git pack-refs --all &&
        test_path_is_missing .git/refs/heads/d1/d2 &&
index 0c204089b83595bc516e9c26416cd67191d3c083..3241d3591799415e423e4f6155e4111de778a964 100755 (executable)
@@ -33,7 +33,8 @@ test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
 reset_to_sane
 
 test_expect_success 'symbolic-ref refuses bare sha1' '
-       test_must_fail git symbolic-ref HEAD $(git rev-parse HEAD)
+       rev=$(git rev-parse HEAD) &&
+       test_must_fail git symbolic-ref HEAD "$rev"
 '
 
 reset_to_sane
@@ -170,9 +171,57 @@ test_expect_success 'symbolic-ref refuses invalid target for non-HEAD' '
 '
 
 test_expect_success 'symbolic-ref allows top-level target for non-HEAD' '
-       git symbolic-ref refs/heads/top-level FETCH_HEAD &&
-       git update-ref FETCH_HEAD HEAD &&
+       git symbolic-ref refs/heads/top-level ORIG_HEAD &&
+       git update-ref ORIG_HEAD HEAD &&
        test_cmp_rev top-level HEAD
 '
 
+test_expect_success 'symbolic-ref pointing at another' '
+       git update-ref refs/heads/maint-2.37 HEAD &&
+       git symbolic-ref refs/heads/maint refs/heads/maint-2.37 &&
+       git checkout maint &&
+
+       git symbolic-ref HEAD >actual &&
+       echo refs/heads/maint-2.37 >expect &&
+       test_cmp expect actual &&
+
+       git symbolic-ref --no-recurse HEAD >actual &&
+       echo refs/heads/maint >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref --short handles complex utf8 case' '
+       name="测试-加-增加-加-增加" &&
+       git symbolic-ref TEST_SYMREF "refs/heads/$name" &&
+       # In the real world, we saw problems with this case only
+       # when the locale includes UTF-8. Set it here to try to make things as
+       # hard as possible for us to pass, but in practice we should do the
+       # right thing regardless (and of course some platforms may not even
+       # have this locale).
+       LC_ALL=en_US.UTF-8 git symbolic-ref --short TEST_SYMREF >actual &&
+       echo "$name" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref --short handles name with suffix' '
+       git symbolic-ref TEST_SYMREF "refs/remotes/origin/HEAD" &&
+       git symbolic-ref --short TEST_SYMREF >actual &&
+       echo "origin" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref --short handles almost-matching name' '
+       git symbolic-ref TEST_SYMREF "refs/headsXfoo" &&
+       git symbolic-ref --short TEST_SYMREF >actual &&
+       echo "headsXfoo" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref --short handles name with percent' '
+       git symbolic-ref TEST_SYMREF "refs/heads/%foo" &&
+       git symbolic-ref --short TEST_SYMREF >actual &&
+       echo "%foo" >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 9252a581abf8a702fffb01bc7c388666207d5251..ec1957b709e0863f62912caafe96ebddf6a53d5b 100755 (executable)
@@ -196,4 +196,86 @@ test_expect_success 'show-ref --verify with dangling ref' '
        )
 '
 
+test_expect_success 'show-ref sub-modes are mutually exclusive' '
+       test_must_fail git show-ref --verify --exclude-existing 2>err &&
+       grep "verify" err &&
+       grep "exclude-existing" err &&
+       grep "cannot be used together" err &&
+
+       test_must_fail git show-ref --verify --exists 2>err &&
+       grep "verify" err &&
+       grep "exists" err &&
+       grep "cannot be used together" err &&
+
+       test_must_fail git show-ref --exclude-existing --exists 2>err &&
+       grep "exclude-existing" err &&
+       grep "exists" err &&
+       grep "cannot be used together" err
+'
+
+test_expect_success '--exists with existing reference' '
+       git show-ref --exists refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+'
+
+test_expect_success '--exists with missing reference' '
+       test_expect_code 2 git show-ref --exists refs/heads/does-not-exist
+'
+
+test_expect_success '--exists does not use DWIM' '
+       test_expect_code 2 git show-ref --exists $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err &&
+       grep "reference does not exist" err
+'
+
+test_expect_success '--exists with HEAD' '
+       git show-ref --exists HEAD
+'
+
+test_expect_success '--exists with bad reference name' '
+       test_when_finished "git update-ref -d refs/heads/bad...name" &&
+       new_oid=$(git rev-parse HEAD) &&
+       test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       git show-ref --exists refs/heads/bad...name
+'
+
+test_expect_success '--exists with arbitrary symref' '
+       test_when_finished "git symbolic-ref -d refs/symref" &&
+       git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
+       git show-ref --exists refs/symref
+'
+
+test_expect_success '--exists with dangling symref' '
+       test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
+       git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
+       git show-ref --exists refs/heads/dangling
+'
+
+test_expect_success '--exists with nonexistent object ID' '
+       test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+       git show-ref --exists refs/heads/missing-oid
+'
+
+test_expect_success '--exists with non-commit object' '
+       tree_oid=$(git rev-parse HEAD^{tree}) &&
+       test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+       git show-ref --exists refs/heads/tree
+'
+
+test_expect_success '--exists with directory fails with generic error' '
+       cat >expect <<-EOF &&
+       error: failed to look up reference: Is a directory
+       EOF
+       test_expect_code 1 git show-ref --exists refs/heads 2>err &&
+       test_cmp expect err
+'
+
+test_expect_success '--exists with non-existent special ref' '
+       test_expect_code 2 git show-ref --exists FETCH_HEAD
+'
+
+test_expect_success '--exists with existing special ref' '
+       test_when_finished "rm .git/FETCH_HEAD" &&
+       git rev-parse HEAD >.git/FETCH_HEAD &&
+       git show-ref --exists FETCH_HEAD
+'
+
 test_done
index 13c2b43bbae8343c2402952bba8ac9635001b453..0369beea33b1aff41662390245fdf2ccf21b1cfb 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='Test git update-ref error handling'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Create some references, perhaps run pack-refs --all, then try to
@@ -27,7 +29,7 @@ test_update_rejected () {
        fi &&
        printf "create $prefix/%s $C\n" $create >input &&
        test_must_fail git update-ref --stdin <input 2>output.err &&
-       test_i18ngrep -F "$error" output.err &&
+       test_grep -F "$error" output.err &&
        git for-each-ref $prefix >actual &&
        test_cmp unchanged actual
 }
@@ -549,7 +551,6 @@ test_expect_success REFFILES 'no bogus intermediate values during delete' '
        git update-ref $prefix/foo $C &&
        git pack-refs --all &&
        git update-ref $prefix/foo $D &&
-       git for-each-ref $prefix >unchanged &&
        # Now try to update the reference, but hold the `packed-refs` lock
        # for a while to see what happens while the process is blocked:
        : >.git/packed-refs.lock &&
@@ -612,7 +613,7 @@ test_expect_success REFFILES 'delete fails cleanly if packed-refs file is locked
        test_when_finished "rm -f .git/packed-refs.lock" &&
        test_must_fail git update-ref -d $prefix/foo >out 2>err &&
        git for-each-ref $prefix >actual &&
-       test_i18ngrep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
+       test_grep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
        test_cmp unchanged actual
 '
 
index 41ba1f1d7fca94e9504e0c198b2e1f7885c41185..9469c79a585f051177a4c9223c0e0f9fd37124dd 100755 (executable)
@@ -5,6 +5,7 @@ test_description='packed-refs entries are covered by loose refs'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index be12fb63506e2a8c242e876526dbe1d60e105f49..f23c0152a858265e4a8c6b0c068e00823aadb444 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='avoid rewriting packed-refs unnecessarily'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Add an identifying mark to the packed-refs file header line. This
index aa59954f6c52faa2a4b5ba3fa32fdd2a2b4b0b85..a0ff8d51f04b2d9ad34060a4f42290577d6673f7 100755 (executable)
@@ -7,6 +7,7 @@ test_description='Test prune and reflog expiration'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_have () {
@@ -28,7 +29,7 @@ check_fsck () {
        '')
                test_must_be_empty fsck.output ;;
        *)
-               test_i18ngrep "$1" fsck.output ;;
+               test_grep "$1" fsck.output ;;
        esac
 }
 
@@ -307,9 +308,9 @@ test_expect_success 'git reflog expire unknown reference' '
        test_config gc.reflogexpireunreachable never &&
 
        test_must_fail git reflog expire main@{123} 2>stderr &&
-       test_i18ngrep "points nowhere" stderr &&
+       test_grep "points nowhere" stderr &&
        test_must_fail git reflog expire does-not-exist 2>stderr &&
-       test_i18ngrep "points nowhere" stderr
+       test_grep "points nowhere" stderr
 '
 
 test_expect_success 'checkout should not delete log for packed ref' '
@@ -445,11 +446,34 @@ test_expect_success 'expire with multiple worktrees' '
        )
 '
 
-test_expect_success REFFILES 'empty reflog' '
+test_expect_success 'expire one of multiple worktrees' '
+       git init main-wt2 &&
+       (
+               cd main-wt2 &&
+               test_tick &&
+               test_commit foo &&
+               git worktree add link-wt &&
+               test_tick &&
+               test_commit -C link-wt foobar &&
+               test_tick &&
+               test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD \
+                       >expect-link-wt &&
+               git reflog expire --verbose --all --expire=$test_tick \
+                       --single-worktree &&
+               test-tool ref-store worktree:main for-each-reflog-ent HEAD \
+                       >actual-main &&
+               test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD \
+                       >actual-link-wt &&
+               test_must_be_empty actual-main &&
+               test_cmp expect-link-wt actual-link-wt
+       )
+'
+
+test_expect_success 'empty reflog' '
        test_when_finished "rm -rf empty" &&
        git init empty &&
        test_commit -C empty A &&
-       >empty/.git/logs/refs/heads/foo &&
+       test-tool ref-store main create-reflog refs/heads/foo &&
        git -C empty reflog expire --all 2>err &&
        test_must_be_empty err
 '
index 934688a1ee82e29f2552bb2444eb06626fe254f8..d2a4822d46fd05c45273f48b0caf48cadb2a07f1 100755 (executable)
@@ -4,6 +4,7 @@ test_description='Test reflog interaction with detached HEAD'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 reset_state () {
index 27731722a5b6b26bad0b1929c274414239cc449e..2092488090de5624103f277b8ad3d44030ce3767 100755 (executable)
@@ -5,6 +5,7 @@ test_description='reference transaction hooks'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -36,7 +37,7 @@ test_expect_success 'hook aborts updating ref in prepared state' '
                fi
        EOF
        test_must_fail git update-ref HEAD POST 2>err &&
-       test_i18ngrep "ref updates aborted by hook" err
+       test_grep "ref updates aborted by hook" err
 '
 
 test_expect_success 'hook gets all queued updates in prepared state' '
index 14f13b57c6d2017d199a5d91d6908665c7f2671d..0eb5e674bc1f0dbc688523dd3f94a5e5bb32b6d7 100755 (executable)
@@ -14,9 +14,13 @@ test_expect_success 'setup' '
                test_commit B &&
                test_commit C &&
 
-               cp .git/logs/HEAD HEAD.old &&
+               git reflog HEAD >expect &&
                git reset --hard HEAD~ &&
-               cp HEAD.old .git/logs/HEAD
+               # Make sure that the reflog does not point to the same commit
+               # as HEAD.
+               git reflog delete HEAD@{0} &&
+               git reflog HEAD >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -25,7 +29,7 @@ test_reflog_updateref () {
        shift
        args="$@"
 
-       test_expect_success REFFILES "get '$exp' with '$args'"  '
+       test_expect_success "get '$exp' with '$args'"  '
                test_when_finished "rm -rf copy" &&
                cp -R repo copy &&
 
diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh
new file mode 100755 (executable)
index 0000000..5d8c86b
--- /dev/null
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+test_description='test exclude_patterns functionality in main ref store'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+for_each_ref__exclude () {
+       GIT_TRACE2_PERF=1 test-tool ref-store main \
+               for-each-ref--exclude "$@" >actual.raw
+       cut -d ' ' -f 2 actual.raw
+}
+
+for_each_ref () {
+       git for-each-ref --format='%(refname)' "$@"
+}
+
+assert_jumps () {
+       local nr="$1"
+       local trace="$2"
+
+       grep -q "name:jumps_made value:$nr$" $trace
+}
+
+assert_no_jumps () {
+       ! assert_jumps ".*" "$1"
+}
+
+test_expect_success 'setup' '
+       test_commit --no-tag base &&
+       base="$(git rev-parse HEAD)" &&
+
+       for name in foo bar baz quux
+       do
+               for i in 1 2 3
+               do
+                       echo "create refs/heads/$name/$i $base" || return 1
+               done || return 1
+       done >in &&
+       echo "delete refs/heads/main" >>in &&
+
+       git update-ref --stdin <in &&
+       git pack-refs --all
+'
+
+test_expect_success 'excluded region in middle' '
+       for_each_ref__exclude refs/heads refs/heads/foo >actual 2>perf &&
+       for_each_ref refs/heads/bar refs/heads/baz refs/heads/quux >expect &&
+
+       test_cmp expect actual &&
+       assert_jumps 1 perf
+'
+
+test_expect_success 'excluded region at beginning' '
+       for_each_ref__exclude refs/heads refs/heads/bar >actual 2>perf &&
+       for_each_ref refs/heads/baz refs/heads/foo refs/heads/quux >expect &&
+
+       test_cmp expect actual &&
+       assert_jumps 1 perf
+'
+
+test_expect_success 'excluded region at end' '
+       for_each_ref__exclude refs/heads refs/heads/quux >actual 2>perf &&
+       for_each_ref refs/heads/foo refs/heads/bar refs/heads/baz >expect &&
+
+       test_cmp expect actual &&
+       assert_jumps 1 perf
+'
+
+test_expect_success 'disjoint excluded regions' '
+       for_each_ref__exclude refs/heads refs/heads/bar refs/heads/quux >actual 2>perf &&
+       for_each_ref refs/heads/baz refs/heads/foo >expect &&
+
+       test_cmp expect actual &&
+       assert_jumps 2 perf
+'
+
+test_expect_success 'adjacent, non-overlapping excluded regions' '
+       for_each_ref__exclude refs/heads refs/heads/bar refs/heads/baz >actual 2>perf &&
+       for_each_ref refs/heads/foo refs/heads/quux >expect &&
+
+       test_cmp expect actual &&
+       assert_jumps 1 perf
+'
+
+test_expect_success 'overlapping excluded regions' '
+       for_each_ref__exclude refs/heads refs/heads/ba refs/heads/baz >actual 2>perf &&
+       for_each_ref refs/heads/foo refs/heads/quux >expect &&
+
+       test_cmp expect actual &&
+       assert_jumps 1 perf
+'
+
+test_expect_success 'several overlapping excluded regions' '
+       for_each_ref__exclude refs/heads \
+               refs/heads/bar refs/heads/baz refs/heads/foo >actual 2>perf &&
+       for_each_ref refs/heads/quux >expect &&
+
+       test_cmp expect actual &&
+       assert_jumps 1 perf
+'
+
+test_expect_success 'non-matching excluded section' '
+       for_each_ref__exclude refs/heads refs/heads/does/not/exist >actual 2>perf &&
+       for_each_ref >expect &&
+
+       test_cmp expect actual &&
+       assert_no_jumps perf
+'
+
+test_expect_success 'meta-characters are discarded' '
+       for_each_ref__exclude refs/heads "refs/heads/ba*" >actual 2>perf &&
+       for_each_ref >expect &&
+
+       test_cmp expect actual &&
+       assert_no_jumps perf
+'
+
+test_done
index ff1c967d550318e0b17ad3c9d1e08654c51e7506..0c00118c2b3f798777bad27a7082e5c0a2f29ea4 100755 (executable)
@@ -47,7 +47,7 @@ test_expect_success 'git branch shows badly named ref as warning' '
        test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git branch >output 2>error &&
-       test_i18ngrep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
+       test_grep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
        ! grep -e "broken\.\.\.ref" output
 '
 
@@ -158,23 +158,23 @@ test_expect_success 'rev-parse skips symref pointing to broken name' '
        git rev-parse --verify one >expect &&
        git rev-parse --verify shadow >actual 2>err &&
        test_cmp expect actual &&
-       test_i18ngrep "ignoring dangling symref refs/tags/shadow" err
+       test_grep "ignoring dangling symref refs/tags/shadow" err
 '
 
 test_expect_success 'for-each-ref emits warnings for broken names' '
        test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
-       printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+       test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
-       printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        git for-each-ref >output 2>error &&
        ! grep -e "broken\.\.\.ref" output &&
        ! grep -e "badname" output &&
        ! grep -e "broken\.\.\.symref" output &&
-       test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
-       test_i18ngrep ! "ignoring broken ref refs/heads/badname" error &&
-       test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
+       test_grep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
+       test_grep ! "ignoring broken ref refs/heads/badname" error &&
+       test_grep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
 '
 
 test_expect_success 'update-ref -d can delete broken name' '
@@ -192,7 +192,7 @@ test_expect_success 'branch -d can delete broken name' '
        test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git branch -d broken...ref >output 2>error &&
-       test_i18ngrep "Deleted branch broken...ref (was broken)" output &&
+       test_grep "Deleted branch broken...ref (was broken)" output &&
        test_must_be_empty error &&
        git branch >output 2>error &&
        ! grep -e "broken\.\.\.ref" error &&
@@ -205,8 +205,9 @@ test_expect_success 'update-ref --no-deref -d can delete symref to broken name'
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+       test_ref_exists refs/heads/badname &&
        git update-ref --no-deref -d refs/heads/badname >output 2>error &&
-       test_path_is_missing .git/refs/heads/badname &&
+       test_ref_missing refs/heads/badname &&
        test_must_be_empty output &&
        test_must_be_empty error
 '
@@ -216,17 +217,19 @@ test_expect_success 'branch -d can delete symref to broken name' '
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+       test_ref_exists refs/heads/badname &&
        git branch -d badname >output 2>error &&
-       test_path_is_missing .git/refs/heads/badname &&
-       test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
+       test_ref_missing refs/heads/badname &&
+       test_grep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
        test_must_be_empty error
 '
 
 test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' '
        test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+       test_ref_exists refs/heads/badname &&
        git update-ref --no-deref -d refs/heads/badname >output 2>error &&
-       test_path_is_missing .git/refs/heads/badname &&
+       test_ref_missing refs/heads/badname &&
        test_must_be_empty output &&
        test_must_be_empty error
 '
@@ -234,9 +237,10 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref to brok
 test_expect_success 'branch -d can delete dangling symref to broken name' '
        test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+       test_ref_exists refs/heads/badname &&
        git branch -d badname >output 2>error &&
-       test_path_is_missing .git/refs/heads/badname &&
-       test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
+       test_ref_missing refs/heads/badname &&
+       test_grep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
        test_must_be_empty error
 '
 
@@ -245,45 +249,50 @@ test_expect_success 'update-ref -d can delete broken name through symref' '
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+       test_ref_exists refs/heads/broken...ref &&
        git update-ref -d refs/heads/badname >output 2>error &&
-       test_path_is_missing .git/refs/heads/broken...ref &&
+       test_ref_missing refs/heads/broken...ref &&
        test_must_be_empty output &&
        test_must_be_empty error
 '
 
 test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '
-       printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+       test_ref_exists refs/heads/broken...symref &&
        git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
-       test_path_is_missing .git/refs/heads/broken...symref &&
+       test_ref_missing refs/heads/broken...symref &&
        test_must_be_empty output &&
        test_must_be_empty error
 '
 
 test_expect_success 'branch -d can delete symref with broken name' '
-       printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+       test_ref_exists refs/heads/broken...symref &&
        git branch -d broken...symref >output 2>error &&
-       test_path_is_missing .git/refs/heads/broken...symref &&
-       test_i18ngrep "Deleted branch broken...symref (was refs/heads/main)" output &&
+       test_ref_missing refs/heads/broken...symref &&
+       test_grep "Deleted branch broken...symref (was refs/heads/main)" output &&
        test_must_be_empty error
 '
 
 test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '
-       printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+       test_ref_exists refs/heads/broken...symref &&
        git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
-       test_path_is_missing .git/refs/heads/broken...symref &&
+       test_ref_missing refs/heads/broken...symref &&
        test_must_be_empty output &&
        test_must_be_empty error
 '
 
 test_expect_success 'branch -d can delete dangling symref with broken name' '
-       printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+       test_ref_exists refs/heads/broken...symref &&
        git branch -d broken...symref >output 2>error &&
-       test_path_is_missing .git/refs/heads/broken...symref &&
-       test_i18ngrep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
+       test_ref_missing refs/heads/broken...symref &&
+       test_grep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
        test_must_be_empty error
 '
 
@@ -292,7 +301,7 @@ test_expect_success 'update-ref -d cannot delete non-ref in .git dir' '
        echo precious >expect &&
        test_must_fail git update-ref -d my-private-file >output 2>error &&
        test_must_be_empty output &&
-       test_i18ngrep -e "refusing to update ref with bad name" error &&
+       test_grep -e "refusing to update ref with bad name" error &&
        test_cmp expect .git/my-private-file
 '
 
index de0f6d5e7fd49ca5276696833d8f9dd034a15629..8a456b1142d1cc463f97c5e8809bcfc83ffa1e96 100755 (executable)
@@ -15,6 +15,7 @@ test_expect_success setup '
        git config --unset i18n.commitencoding &&
        git checkout HEAD^0 &&
        test_commit B fileB two &&
+       orig_head=$(git rev-parse HEAD) &&
        git tag -d A B &&
        git reflog expire --expire=now --all
 '
@@ -115,63 +116,62 @@ test_expect_success 'zlib corrupt loose object output ' '
 '
 
 test_expect_success 'branch pointing to non-commit' '
-       git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+       tree_oid=$(git rev-parse --verify HEAD^{tree}) &&
        test_when_finished "git update-ref -d refs/heads/invalid" &&
+       test-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "not a commit" out
+       test_grep "not a commit" out
 '
 
-test_expect_success 'HEAD link pointing at a funny object' '
-       test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-       mv .git/HEAD .git/SAVED_HEAD &&
+test_expect_success REFFILES 'HEAD link pointing at a funny object' '
+       test_when_finished "git update-ref HEAD $orig_head" &&
        echo $ZERO_OID >.git/HEAD &&
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail env GIT_DIR=.git git fsck 2>out &&
-       test_i18ngrep "detached HEAD points" out
+       test_grep "detached HEAD points" out
 '
 
 test_expect_success 'HEAD link pointing at a funny place' '
-       test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-       mv .git/HEAD .git/SAVED_HEAD &&
-       echo "ref: refs/funny/place" >.git/HEAD &&
+       test_when_finished "git update-ref --no-deref HEAD $orig_head" &&
+       test-tool ref-store main create-symref HEAD refs/funny/place &&
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail env GIT_DIR=.git git fsck 2>out &&
-       test_i18ngrep "HEAD points to something strange" out
+       test_grep "HEAD points to something strange" out
 '
 
-test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
-       test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-       test_when_finished "rm -rf .git/worktrees wt" &&
+test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '
+       test_when_finished "git update-ref HEAD $orig_head" &&
+       test_when_finished "git worktree remove -f wt" &&
        git worktree add wt &&
-       mv .git/HEAD .git/SAVED_HEAD &&
        echo $ZERO_OID >.git/HEAD &&
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail git -C wt fsck 2>out &&
-       test_i18ngrep "main-worktree/HEAD: detached HEAD points" out
+       test_grep "main-worktree/HEAD: detached HEAD points" out
 '
 
-test_expect_success 'other worktree HEAD link pointing at a funny object' '
-       test_when_finished "rm -rf .git/worktrees other" &&
+test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '
+       test_when_finished "git worktree remove -f other" &&
        git worktree add other &&
        echo $ZERO_OID >.git/worktrees/other/HEAD &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "worktrees/other/HEAD: detached HEAD points" out
+       test_grep "worktrees/other/HEAD: detached HEAD points" out
 '
 
 test_expect_success 'other worktree HEAD link pointing at missing object' '
-       test_when_finished "rm -rf .git/worktrees other" &&
+       test_when_finished "git worktree remove -f other" &&
        git worktree add other &&
-       echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
+       object_id=$(echo "Contents missing from repo" | git hash-object --stdin) &&
+       test-tool -C other ref-store main update-ref msg HEAD $object_id "" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "worktrees/other/HEAD: invalid sha1 pointer" out
+       test_grep "worktrees/other/HEAD: invalid sha1 pointer" out
 '
 
 test_expect_success 'other worktree HEAD link pointing at a funny place' '
-       test_when_finished "rm -rf .git/worktrees other" &&
+       test_when_finished "git worktree remove -f other" &&
        git worktree add other &&
-       echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
+       git -C other symbolic-ref HEAD refs/funny/place &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "worktrees/other/HEAD points to something strange" out
+       test_grep "worktrees/other/HEAD points to something strange" out
 '
 
 test_expect_success 'commit with multiple signatures is okay' '
@@ -212,45 +212,45 @@ test_expect_success 'email without @ is okay' '
 test_expect_success 'email with embedded > is not okay' '
        git cat-file commit HEAD >basis &&
        sed "s/@[a-z]/&>/" basis >bad-email &&
-       new=$(git hash-object -t commit -w --stdin <bad-email) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-email) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error in commit $new" out
+       test_grep "error in commit $new" out
 '
 
 test_expect_success 'missing < email delimiter is reported nicely' '
        git cat-file commit HEAD >basis &&
        sed "s/<//" basis >bad-email-2 &&
-       new=$(git hash-object -t commit -w --stdin <bad-email-2) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-email-2) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error in commit $new.* - bad name" out
+       test_grep "error in commit $new.* - bad name" out
 '
 
 test_expect_success 'missing email is reported nicely' '
        git cat-file commit HEAD >basis &&
        sed "s/[a-z]* <[^>]*>//" basis >bad-email-3 &&
-       new=$(git hash-object -t commit -w --stdin <bad-email-3) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-email-3) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error in commit $new.* - missing email" out
+       test_grep "error in commit $new.* - missing email" out
 '
 
 test_expect_success '> in name is reported' '
        git cat-file commit HEAD >basis &&
        sed "s/ </> </" basis >bad-email-4 &&
-       new=$(git hash-object -t commit -w --stdin <bad-email-4) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-email-4) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error in commit $new" out
+       test_grep "error in commit $new" out
 '
 
 # date is 2^64 + 1
@@ -258,23 +258,23 @@ test_expect_success 'integer overflow in timestamps is reported' '
        git cat-file commit HEAD >basis &&
        sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \
                <basis >bad-timestamp &&
-       new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-timestamp) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error in commit $new.*integer overflow" out
+       test_grep "error in commit $new.*integer overflow" out
 '
 
 test_expect_success 'commit with NUL in header' '
        git cat-file commit HEAD >basis &&
        sed "s/author ./author Q/" <basis | q_to_nul >commit-NUL-header &&
-       new=$(git hash-object -t commit -w --stdin <commit-NUL-header) &&
+       new=$(git hash-object --literally -t commit -w --stdin <commit-NUL-header) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error in commit $new.*unterminated header: NUL at offset" out
+       test_grep "error in commit $new.*unterminated header: NUL at offset" out
 '
 
 test_expect_success 'tree object with duplicate entries' '
@@ -292,10 +292,10 @@ test_expect_success 'tree object with duplicate entries' '
                        git cat-file tree $T &&
                        git cat-file tree $T
                ) |
-               git hash-object -w -t tree --stdin
+               git hash-object --literally -w -t tree --stdin
        ) &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error in tree .*contains duplicate file entries" out
+       test_grep "error in tree .*contains duplicate file entries" out
 '
 
 check_duplicate_names () {
@@ -318,8 +318,8 @@ check_duplicate_names () {
                done >badtree &&
                badtree=$(git mktree <badtree) &&
                test_must_fail git fsck 2>out &&
-               test_i18ngrep "$badtree" out &&
-               test_i18ngrep "error in tree .*contains duplicate file entries" out
+               test_grep "$badtree" out &&
+               test_grep "error in tree .*contains duplicate file entries" out
        '
 }
 
@@ -341,9 +341,9 @@ test_expect_success 'unparseable tree object' '
        commit_sha1=$(git commit-tree $tree_sha1) &&
        git update-ref refs/heads/wrong $commit_sha1 &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error: empty filename in tree entry" out &&
-       test_i18ngrep "$tree_sha1" out &&
-       test_i18ngrep ! "fatal: empty filename in tree entry" out
+       test_grep "error: empty filename in tree entry" out &&
+       test_grep "$tree_sha1" out &&
+       test_grep ! "fatal: empty filename in tree entry" out
 '
 
 test_expect_success 'tree entry with type mismatch' '
@@ -360,8 +360,8 @@ test_expect_success 'tree entry with type mismatch' '
        commit=$(git commit-tree $tree) &&
        git update-ref refs/heads/type_mismatch $commit &&
        test_must_fail git fsck >out 2>&1 &&
-       test_i18ngrep "is a blob, not a tree" out &&
-       test_i18ngrep ! "dangling blob" out
+       test_grep "is a blob, not a tree" out &&
+       test_grep ! "dangling blob" out
 '
 
 test_expect_success 'tree entry with bogus mode' '
@@ -391,10 +391,10 @@ test_expect_success 'tag pointing to nonexistent' '
 
        tag=$(git hash-object -t tag -w --stdin <invalid-tag) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/invalid &&
+       git update-ref refs/tags/invalid $tag &&
        test_when_finished "git update-ref -d refs/tags/invalid" &&
        test_must_fail git fsck --tags >out &&
-       test_i18ngrep "broken link" out
+       test_grep "broken link" out
 '
 
 test_expect_success 'tag pointing to something else than its type' '
@@ -411,7 +411,7 @@ test_expect_success 'tag pointing to something else than its type' '
 
        tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/wrong &&
+       git update-ref refs/tags/wrong $tag &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags
 '
@@ -426,9 +426,9 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '
        This is an invalid tag.
        EOF
 
-       tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+       tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/wrong &&
+       git update-ref refs/tags/wrong $tag &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        git fsck --tags 2>out &&
 
@@ -452,10 +452,10 @@ test_expect_success 'tag with bad tagger' '
 
        tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/wrong &&
+       git update-ref refs/tags/wrong $tag &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
-       test_i18ngrep "error in tag .*: invalid author/committer" out
+       test_grep "error in tag .*: invalid author/committer" out
 '
 
 test_expect_success 'tag with NUL in header' '
@@ -471,10 +471,10 @@ test_expect_success 'tag with NUL in header' '
 
        tag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/wrong &&
+       git update-ref refs/tags/wrong $tag &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
-       test_i18ngrep "error in tag $tag.*unterminated header: NUL at offset" out
+       test_grep "error in tag $tag.*unterminated header: NUL at offset" out
 '
 
 test_expect_success 'cleaned up' '
@@ -504,7 +504,7 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
 
        test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
-       test_i18ngrep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
+       test_grep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
 '
 
 # An actual bit corruption is more likely than swapped commits, but
@@ -558,7 +558,7 @@ test_expect_success 'rev-list --verify-objects with commit graph (parent)' '
 test_expect_success 'force fsck to ignore double author' '
        git cat-file commit HEAD >basis &&
        sed "s/^author .*/&,&/" <basis | tr , \\n >multiple-authors &&
-       new=$(git hash-object -t commit -w --stdin <multiple-authors) &&
+       new=$(git hash-object --literally -t commit -w --stdin <multiple-authors) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -573,9 +573,9 @@ test_expect_success 'fsck notices blob entry pointing to null sha1' '
        (git init null-blob &&
         cd null-blob &&
         sha=$(printf "100644 file$_bz$_bzoid" |
-              git hash-object -w --stdin -t tree) &&
+              git hash-object --literally -w --stdin -t tree) &&
          git fsck 2>out &&
-         test_i18ngrep "warning.*null sha1" out
+         test_grep "warning.*null sha1" out
        )
 '
 
@@ -583,9 +583,19 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' '
        (git init null-commit &&
         cd null-commit &&
         sha=$(printf "160000 submodule$_bz$_bzoid" |
-              git hash-object -w --stdin -t tree) &&
+              git hash-object --literally -w --stdin -t tree) &&
          git fsck 2>out &&
-         test_i18ngrep "warning.*null sha1" out
+         test_grep "warning.*null sha1" out
+       )
+'
+
+test_expect_success 'fsck notices excessively large tree entry name' '
+       git init large-name &&
+       (
+               cd large-name &&
+               test_commit a-long-name &&
+               git -c fsck.largePathname=warn:10 fsck 2>out &&
+               grep "warning.*large pathname" out
        )
 '
 
@@ -606,7 +616,7 @@ while read name path pretty; do
                        printf "$mode $type %s\t%s" "$value" "$path" >bad &&
                        bad_tree=$(git mktree <bad) &&
                        git fsck 2>out &&
-                       test_i18ngrep "warning.*tree $bad_tree" out
+                       test_grep "warning.*tree $bad_tree" out
                )'
        done <<-\EOF
        100644 blob
@@ -648,13 +658,13 @@ test_expect_success 'NUL in commit' '
                git commit --allow-empty -m "initial commitQNUL after message" &&
                git cat-file commit HEAD >original &&
                q_to_nul <original >munged &&
-               git hash-object -w -t commit --stdin <munged >name &&
+               git hash-object --literally -w -t commit --stdin <munged >name &&
                git branch bad $(cat name) &&
 
                test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
-               test_i18ngrep nulInCommit warn.1 &&
+               test_grep nulInCommit warn.1 &&
                git fsck 2>warn.2 &&
-               test_i18ngrep nulInCommit warn.2
+               test_grep nulInCommit warn.2
        )
 '
 
@@ -774,7 +784,7 @@ test_expect_success 'fsck --name-objects' '
                tree=$(git rev-parse --verify julius:) &&
                git tag -d julius &&
                test_must_fail git fsck --name-objects >out &&
-               test_i18ngrep "$tree (refs/tags/augustus44\\^:" out
+               test_grep "$tree (refs/tags/augustus44\\^:" out
        )
 '
 
@@ -787,15 +797,15 @@ test_expect_success 'alternate objects are correctly blamed' '
        mkdir alt.git/objects/$(dirname $path) &&
        >alt.git/objects/$(dirname $path)/$(basename $path) &&
        test_must_fail git fsck >out 2>&1 &&
-       test_i18ngrep alt.git out
+       test_grep alt.git out
 '
 
 test_expect_success 'fsck errors in packed objects' '
        git cat-file commit HEAD >basis &&
        sed "s/</one/" basis >one &&
        sed "s/</foo/" basis >two &&
-       one=$(git hash-object -t commit -w one) &&
-       two=$(git hash-object -t commit -w two) &&
+       one=$(git hash-object --literally -t commit -w one) &&
+       two=$(git hash-object --literally -t commit -w two) &&
        pack=$(
                {
                        echo $one &&
@@ -806,8 +816,8 @@ test_expect_success 'fsck errors in packed objects' '
        remove_object $one &&
        remove_object $two &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "error in commit $one.* - bad name" out &&
-       test_i18ngrep "error in commit $two.* - bad name" out &&
+       test_grep "error in commit $one.* - bad name" out &&
+       test_grep "error in commit $two.* - bad name" out &&
        ! grep corrupt out
 '
 
@@ -824,7 +834,7 @@ test_expect_success 'fsck fails on corrupt packfile' '
        test_when_finished "rm -f .git/objects/pack/pack-$pack.*" &&
        remove_object $hsh &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "checksum mismatch" out
+       test_grep "checksum mismatch" out
 '
 
 test_expect_success 'fsck finds problems in duplicate loose objects' '
@@ -861,7 +871,7 @@ test_expect_success 'fsck detects trailing loose garbage (commit)' '
        chmod +w "$file" &&
        echo garbage >>"$file" &&
        test_must_fail git fsck 2>out &&
-       test_i18ngrep "garbage.*$commit" out
+       test_grep "garbage.*$commit" out
 '
 
 test_expect_success 'fsck detects trailing loose garbage (large blob)' '
@@ -871,7 +881,7 @@ test_expect_success 'fsck detects trailing loose garbage (large blob)' '
        chmod +w "$file" &&
        echo garbage >>"$file" &&
        test_must_fail git -c core.bigfilethreshold=5 fsck 2>out &&
-       test_i18ngrep "garbage.*$blob" out
+       test_grep "garbage.*$blob" out
 '
 
 test_expect_success 'fsck detects truncated loose object' '
@@ -887,10 +897,10 @@ test_expect_success 'fsck detects truncated loose object' '
 
        # check both regular and streaming code paths
        test_must_fail git fsck 2>out &&
-       test_i18ngrep corrupt.*$blob out &&
+       test_grep corrupt.*$blob out &&
 
        test_must_fail git -c core.bigfilethreshold=128 fsck 2>out &&
-       test_i18ngrep corrupt.*$blob out
+       test_grep corrupt.*$blob out
 '
 
 # for each of type, we have one version which is referenced by another object
@@ -979,7 +989,7 @@ test_expect_success 'detect corrupt index file in fsck' '
        test_when_finished "mv .git/index.backup .git/index" &&
        corrupt_index_checksum &&
        test_must_fail git fsck --cache 2>errors &&
-       test_i18ngrep "bad index file" errors
+       test_grep "bad index file" errors
 '
 
 test_expect_success 'fsck error and recovery on invalid object type' '
@@ -989,10 +999,7 @@ test_expect_success 'fsck error and recovery on invalid object type' '
 
                garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
 
-               cat >err.expect <<-\EOF &&
-               fatal: invalid object type
-               EOF
-               test_must_fail git fsck >out 2>err &&
+               test_must_fail git fsck 2>err &&
                grep -e "^error" -e "^fatal" err >errors &&
                test_line_count = 1 errors &&
                grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err
@@ -1023,4 +1030,34 @@ test_expect_success 'fsck error on gitattributes with excessive size' '
        test_cmp expected actual
 '
 
+test_expect_success 'fsck detects problems in worktree index' '
+       test_when_finished "git worktree remove -f wt" &&
+       git worktree add wt &&
+
+       echo "this will be removed to break the worktree index" >wt/file &&
+       git -C wt add file &&
+       blob=$(git -C wt rev-parse :file) &&
+       remove_object $blob &&
+
+       test_must_fail git fsck --name-objects >actual 2>&1 &&
+       cat >expect <<-EOF &&
+       missing blob $blob (.git/worktrees/wt/index:file)
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'fsck reports problems in current worktree index without filename' '
+       test_when_finished "rm -f .git/index && git read-tree HEAD" &&
+       echo "this object will be removed to break current worktree index" >file &&
+       git add file &&
+       blob=$(git rev-parse :file) &&
+       remove_object $blob &&
+
+       test_must_fail git fsck --name-objects >actual 2>&1 &&
+       cat >expect <<-EOF &&
+       missing blob $blob (:file)
+       EOF
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t1451-fsck-buffer.sh b/t/t1451-fsck-buffer.sh
new file mode 100755 (executable)
index 0000000..3413da4
--- /dev/null
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='fsck on buffers without NUL termination
+
+The goal here is to make sure that the various fsck parsers never look
+past the end of the buffer they are given, even when encountering broken
+or truncated objects.
+
+We have to use "hash-object" for this because most code paths that read objects
+append an extra NUL for safety after the buffer. But hash-object, since it is
+reading straight from a file (and possibly even mmap-ing it) cannot always do
+so.
+
+These tests _might_ catch such overruns in normal use, but should be run with
+ASan or valgrind for more confidence.
+'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# the general idea for tags and commits is to build up the "base" file
+# progressively, and then test new truncations on top of it.
+reset () {
+       test_expect_success 'reset input to empty' '
+               >base
+       '
+}
+
+add () {
+       content="$1"
+       type=${content%% *}
+       test_expect_success "add $type line" '
+               echo "$content" >>base
+       '
+}
+
+check () {
+       type=$1
+       fsck=$2
+       content=$3
+       test_expect_success "truncated $type ($fsck, \"$content\")" '
+               # do not pipe into hash-object here; we want to increase
+               # the chance that it uses a fixed-size buffer or mmap,
+               # and a pipe would be read into a strbuf.
+               {
+                       cat base &&
+                       echo "$content"
+               } >input &&
+               test_must_fail git hash-object -t "$type" input 2>err &&
+               grep "$fsck" err
+       '
+}
+
+test_expect_success 'create valid objects' '
+       git commit --allow-empty -m foo &&
+       commit=$(git rev-parse --verify HEAD) &&
+       tree=$(git rev-parse --verify HEAD^{tree})
+'
+
+reset
+check commit missingTree ""
+check commit missingTree "tr"
+check commit missingTree "tree"
+check commit badTreeSha1 "tree "
+check commit badTreeSha1 "tree 1234"
+add "tree $tree"
+
+# these expect missingAuthor because "parent" is optional
+check commit missingAuthor ""
+check commit missingAuthor "par"
+check commit missingAuthor "parent"
+check commit badParentSha1 "parent "
+check commit badParentSha1 "parent 1234"
+add "parent $commit"
+
+check commit missingAuthor ""
+check commit missingAuthor "au"
+check commit missingAuthor "author"
+ident_checks () {
+       check $1 missingEmail "$2 "
+       check $1 missingEmail "$2 name"
+       check $1 badEmail "$2 name <"
+       check $1 badEmail "$2 name <email"
+       check $1 missingSpaceBeforeDate "$2 name <email>"
+       check $1 badDate "$2 name <email> "
+       check $1 badDate "$2 name <email> 1234"
+       check $1 badTimezone "$2 name <email> 1234 "
+       check $1 badTimezone "$2 name <email> 1234 +"
+}
+ident_checks commit author
+add "author name <email> 1234 +0000"
+
+check commit missingCommitter ""
+check commit missingCommitter "co"
+check commit missingCommitter "committer"
+ident_checks commit committer
+add "committer name <email> 1234 +0000"
+
+reset
+check tag missingObject ""
+check tag missingObject "obj"
+check tag missingObject "object"
+check tag badObjectSha1 "object "
+check tag badObjectSha1 "object 1234"
+add "object $commit"
+
+check tag missingType ""
+check tag missingType "ty"
+check tag missingType "type"
+check tag badType "type "
+check tag badType "type com"
+add "type commit"
+
+check tag missingTagEntry ""
+check tag missingTagEntry "ta"
+check tag missingTagEntry "tag"
+check tag badTagName "tag "
+add "tag foo"
+
+check tag missingTagger ""
+check tag missingTagger "ta"
+check tag missingTagger "tagger"
+ident_checks tag tagger
+
+# trees are a binary format and can't use our earlier helpers
+test_expect_success 'truncated tree (short hash)' '
+       printf "100644 foo\0\1\1\1\1" >input &&
+       test_must_fail git hash-object -t tree input 2>err &&
+       grep badTree err
+'
+
+test_expect_success 'truncated tree (missing nul)' '
+       # these two things are indistinguishable to the parser. The important
+       # thing about this is example is that there are enough bytes to
+       # make up a hash, and that there is no NUL (and we confirm that the
+       # parser does not walk past the end of the buffer).
+       printf "100644 a long filename, or a hash with missing nul?" >input &&
+       test_must_fail git hash-object -t tree input 2>err &&
+       grep badTree err
+'
+
+test_done
index 81de584ea29bca8cfcbe20769c69072202a53efb..a669e592f1d95ca7e0dc575697342326a7a6654f 100755 (executable)
@@ -195,7 +195,7 @@ test_expect_success 'rev-parse --is-shallow-repository in non-shallow repo' '
 '
 
 test_expect_success 'rev-parse --show-object-format in repo' '
-       echo "$(test_oid algo)" >expect &&
+       test_oid algo >expect &&
        git rev-parse --show-object-format >actual &&
        test_cmp expect actual &&
        git rev-parse --show-object-format=storage >actual &&
@@ -208,6 +208,23 @@ test_expect_success 'rev-parse --show-object-format in repo' '
        grep "unknown mode for --show-object-format: squeamish-ossifrage" err
 '
 
+test_expect_success 'rev-parse --show-ref-format' '
+       test_detect_ref_format >expect &&
+       git rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --show-ref-format with invalid storage' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               git config extensions.refstorage broken &&
+               test_must_fail git rev-parse --show-ref-format 2>err &&
+               grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err
+       )
+'
+
 test_expect_success '--show-toplevel from subdir of working tree' '
        pwd >expect &&
        git -C sub/dir rev-parse --show-toplevel >actual &&
@@ -264,4 +281,27 @@ test_expect_success 'rev-parse --since= unsqueezed ordering' '
        test_cmp expect actual
 '
 
+test_expect_success 'rev-parse --bisect includes bad, excludes good' '
+       test_commit_bulk 6 &&
+
+       git update-ref refs/bisect/bad-1 HEAD~1 &&
+       git update-ref refs/bisect/b HEAD~2 &&
+       git update-ref refs/bisect/bad-3 HEAD~3 &&
+       git update-ref refs/bisect/good-3 HEAD~3 &&
+       git update-ref refs/bisect/bad-4 HEAD~4 &&
+       git update-ref refs/bisect/go HEAD~4 &&
+
+       # Note: refs/bisect/b and refs/bisect/go should be ignored because they
+       # do not match the refs/bisect/bad or refs/bisect/good prefixes.
+       cat >expect <<-EOF &&
+       refs/bisect/bad-1
+       refs/bisect/bad-3
+       refs/bisect/bad-4
+       ^refs/bisect/good-3
+       EOF
+
+       git rev-parse --symbolic-full-name --bisect >actual &&
+       test_cmp expect actual
+'
+
 test_done
index b75558040ffd6ee9174ed37bd2ca746212b57013..ae6528aecea7d3b36cfe51c5f19c7098f05429e3 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test separate work tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index de1d48f3ba29116d073b0a5573a2031fae7fcc1d..f0737593c3fda734060fa9509d1b9561086a7376 100755 (executable)
@@ -3,13 +3,29 @@
 test_description='test git rev-parse --parseopt'
 . ./test-lib.sh
 
+check_invalid_long_option () {
+       spec="$1"
+       opt="$2"
+       test_expect_success "test --parseopt invalid switch $opt help output for $spec" '
+               {
+                       cat <<-\EOF &&
+                       error: unknown option `'${opt#--}\''
+                       EOF
+                       sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/$spec.help"
+               } >expect &&
+               test_expect_code 129 git rev-parse --parseopt -- $opt \
+                       2>output <"$TEST_DIRECTORY/t1502/$spec" &&
+               test_cmp expect output
+       '
+}
+
 test_expect_success 'setup optionspec' '
        sed -e "s/^|//" >optionspec <<\EOF
 |some-command [options] <args>...
 |
 |some-command does foo and bar!
 |--
-|h,help    show the help
+|h,help!   show the help
 |
 |foo       some nifty option --foo
 |bar=      some cool option --bar with an argument
@@ -58,44 +74,8 @@ EOF
 '
 
 test_expect_success 'test --parseopt help output' '
-       sed -e "s/^|//" >expect <<\END_EXPECT &&
-|cat <<\EOF
-|usage: some-command [options] <args>...
-|
-|    some-command does foo and bar!
-|
-|    -h, --help            show the help
-|    --foo                 some nifty option --foo
-|    --bar ...             some cool option --bar with an argument
-|    -b, --baz             a short and long option
-|
-|An option group Header
-|    -C[...]               option C with an optional argument
-|    -d, --data[=...]      short and long option with an optional argument
-|
-|Argument hints
-|    -B <arg>              short option required argument
-|    --bar2 <arg>          long option required argument
-|    -e, --fuz <with-space>
-|                          short and long option required argument
-|    -s[<some>]            short option optional argument
-|    --long[=<data>]       long option optional argument
-|    -g, --fluf[=<path>]   short and long option optional argument
-|    --longest <very-long-argument-hint>
-|                          a very long argument hint
-|    --pair <key=value>    with an equals sign in the hint
-|    --aswitch             help te=t contains? fl*g characters!`
-|    --bswitch <hint>      hint has trailing tab character
-|    --cswitch             switch has trailing tab character
-|    --short-hint <a>      with a one symbol hint
-|
-|Extras
-|    --extra1              line above used to cause a segfault but no longer does
-|
-|EOF
-END_EXPECT
        test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
-       test_cmp expect output
+       test_cmp "$TEST_DIRECTORY/t1502/optionspec.help" output
 '
 
 test_expect_success 'test --parseopt help output no switches' '
@@ -131,7 +111,7 @@ test_expect_success 'test --parseopt help-all output hidden switches' '
 |
 |    some-command does foo and bar!
 |
-|    --hidden1             A hidden switch
+|    --[no-]hidden1        A hidden switch
 |
 |EOF
 END_EXPECT
@@ -140,41 +120,12 @@ END_EXPECT
 '
 
 test_expect_success 'test --parseopt invalid switch help output' '
-       sed -e "s/^|//" >expect <<\END_EXPECT &&
-|error: unknown option `does-not-exist'\''
-|usage: some-command [options] <args>...
-|
-|    some-command does foo and bar!
-|
-|    -h, --help            show the help
-|    --foo                 some nifty option --foo
-|    --bar ...             some cool option --bar with an argument
-|    -b, --baz             a short and long option
-|
-|An option group Header
-|    -C[...]               option C with an optional argument
-|    -d, --data[=...]      short and long option with an optional argument
-|
-|Argument hints
-|    -B <arg>              short option required argument
-|    --bar2 <arg>          long option required argument
-|    -e, --fuz <with-space>
-|                          short and long option required argument
-|    -s[<some>]            short option optional argument
-|    --long[=<data>]       long option optional argument
-|    -g, --fluf[=<path>]   short and long option optional argument
-|    --longest <very-long-argument-hint>
-|                          a very long argument hint
-|    --pair <key=value>    with an equals sign in the hint
-|    --aswitch             help te=t contains? fl*g characters!`
-|    --bswitch <hint>      hint has trailing tab character
-|    --cswitch             switch has trailing tab character
-|    --short-hint <a>      with a one symbol hint
-|
-|Extras
-|    --extra1              line above used to cause a segfault but no longer does
-|
-END_EXPECT
+       {
+               cat <<-\EOF &&
+               error: unknown option `does-not-exist'\''
+               EOF
+               sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/optionspec.help"
+       } >expect &&
        test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&
        test_cmp expect output
 '
@@ -288,7 +239,7 @@ test_expect_success 'test --parseopt help output: "wrapped" options normal "or:"
        |    [--another-option]
        |cmd [--yet-another-option]
        |--
-       |h,help    show the help
+       |h,help!   show the help
        EOF
 
        sed -e "s/^|//" >expect <<-\END_EXPECT &&
@@ -302,14 +253,14 @@ test_expect_success 'test --parseopt help output: "wrapped" options normal "or:"
        |EOF
        END_EXPECT
 
-       test_must_fail git rev-parse --parseopt -- -h >out <spec >actual &&
+       test_must_fail git rev-parse --parseopt -- -h <spec >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'test --parseopt invalid opt-spec' '
        test_write_lines x -- "=, x" >spec &&
        echo "fatal: missing opt-spec before option flags" >expect &&
-       test_must_fail git rev-parse --parseopt -- >out <spec 2>err &&
+       test_must_fail git rev-parse --parseopt -- <spec 2>err &&
        test_cmp expect err
 '
 
@@ -322,7 +273,7 @@ test_expect_success 'test --parseopt help output: multi-line blurb after empty l
        |line
        |blurb
        |--
-       |h,help    show the help
+       |h,help!   show the help
        EOF
 
        sed -e "s/^|//" >expect <<-\END_EXPECT &&
@@ -339,8 +290,36 @@ test_expect_success 'test --parseopt help output: multi-line blurb after empty l
        |EOF
        END_EXPECT
 
-       test_must_fail git rev-parse --parseopt -- -h >out <spec >actual &&
+       test_must_fail git rev-parse --parseopt -- -h <spec >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'test --parseopt help output for optionspec-neg' '
+       test_expect_code 129 git rev-parse --parseopt -- \
+               -h >output <"$TEST_DIRECTORY/t1502/optionspec-neg" &&
+       test_cmp "$TEST_DIRECTORY/t1502/optionspec-neg.help" output
+'
+
+test_expect_success 'test --parseopt valid options for optionspec-neg' '
+       cat >expect <<-\EOF &&
+       set -- --foo --no-foo --no-bar --positive-only --no-negative --
+       EOF
+       git rev-parse --parseopt -- <"$TEST_DIRECTORY/t1502/optionspec-neg" >output \
+              --foo --no-foo --no-bar --positive-only --no-negative &&
+       test_cmp expect output
+'
+
+test_expect_success 'test --parseopt positivated option for optionspec-neg' '
+       cat >expect <<-\EOF &&
+       set -- --no-no-bar --no-no-bar --
+       EOF
+       git rev-parse --parseopt -- <"$TEST_DIRECTORY/t1502/optionspec-neg" >output \
+              --no-no-bar --bar &&
+       test_cmp expect output
+'
+
+check_invalid_long_option optionspec-neg --no-positive-only
+check_invalid_long_option optionspec-neg --negative
+check_invalid_long_option optionspec-neg --no-no-negative
+
 test_done
diff --git a/t/t1502/.gitattributes b/t/t1502/.gitattributes
new file mode 100644 (file)
index 0000000..562b12e
--- /dev/null
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t1502/optionspec-neg b/t/t1502/optionspec-neg
new file mode 100644 (file)
index 0000000..392f43e
--- /dev/null
@@ -0,0 +1,8 @@
+some-command [options] <args>...
+
+some-command does foo and bar!
+--
+foo            can be negated
+no-bar         can be positivated
+positive-only! cannot be negated
+no-negative!   cannot be positivated
diff --git a/t/t1502/optionspec-neg.help b/t/t1502/optionspec-neg.help
new file mode 100644 (file)
index 0000000..7a29f8c
--- /dev/null
@@ -0,0 +1,12 @@
+cat <<\EOF
+usage: some-command [options] <args>...
+
+    some-command does foo and bar!
+
+    --[no-]foo            can be negated
+    --no-bar              can be positivated
+    --bar                 opposite of --no-bar
+    --positive-only       cannot be negated
+    --no-negative         cannot be positivated
+
+EOF
diff --git a/t/t1502/optionspec.help b/t/t1502/optionspec.help
new file mode 100755 (executable)
index 0000000..cbdd54d
--- /dev/null
@@ -0,0 +1,36 @@
+cat <<\EOF
+usage: some-command [options] <args>...
+
+    some-command does foo and bar!
+
+    -h, --help            show the help
+    --[no-]foo            some nifty option --foo
+    --[no-]bar ...        some cool option --bar with an argument
+    -b, --[no-]baz        a short and long option
+
+An option group Header
+    -C[...]               option C with an optional argument
+    -d, --[no-]data[=...] short and long option with an optional argument
+
+Argument hints
+    -B <arg>              short option required argument
+    --[no-]bar2 <arg>     long option required argument
+    -e, --[no-]fuz <with-space>
+                          short and long option required argument
+    -s[<some>]            short option optional argument
+    --[no-]long[=<data>]  long option optional argument
+    -g, --[no-]fluf[=<path>]
+                          short and long option optional argument
+    --[no-]longest <very-long-argument-hint>
+                          a very long argument hint
+    --[no-]pair <key=value>
+                          with an equals sign in the hint
+    --[no-]aswitch        help te=t contains? fl*g characters!`
+    --[no-]bswitch <hint> hint has trailing tab character
+    --[no-]cswitch        switch has trailing tab character
+    --[no-]short-hint <a> with a one symbol hint
+
+Extras
+    --[no-]extra1         line above used to cause a segfault but no longer does
+
+EOF
index 0fafcf9dde385f2c2bac7fbd846098908a72f09b..c1679e31d8afca409323484d588bf63b264f45f2 100755 (executable)
@@ -6,8 +6,12 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_prefix() {
-       test_expect_success "$1" \
-       "test '$2' = \"\$(git rev-parse --show-prefix)\""
+       local expect="$2" &&
+       test_expect_success "$1: git rev-parse --show-prefix is '$2'" '
+               echo "$expect" >expect &&
+               git rev-parse --show-prefix >actual &&
+               test_cmp expect actual
+       '
 }
 
 test_fail() {
index 18688cae17d57e8174f4d1d61eeb2e2ed341eac6..ef40511d8972924fed8abc1e8cb29f9bcf0cda77 100755 (executable)
@@ -107,16 +107,16 @@ test_expect_success 'correct relative file objects (6)' '
 
 test_expect_success 'incorrect revision id' '
        test_must_fail git rev-parse foobar:file.txt 2>error &&
-       test_i18ngrep "invalid object name .foobar." error &&
+       test_grep "invalid object name .foobar." error &&
        test_must_fail git rev-parse foobar 2>error &&
-       test_i18ngrep "unknown revision or path not in the working tree." error
+       test_grep "unknown revision or path not in the working tree." error
 '
 
 test_expect_success 'incorrect file in sha1:path' '
        test_must_fail git rev-parse HEAD:nothing.txt 2>error &&
-       test_i18ngrep "path .nothing.txt. does not exist in .HEAD." error &&
+       test_grep "path .nothing.txt. does not exist in .HEAD." error &&
        test_must_fail git rev-parse HEAD:index-only.txt 2>error &&
-       test_i18ngrep "path .index-only.txt. exists on disk, but not in .HEAD." error &&
+       test_grep "path .index-only.txt. exists on disk, but not in .HEAD." error &&
        (cd subdir &&
         test_must_fail git rev-parse HEAD:file2.txt 2>error &&
         test_did_you_mean HEAD subdir/ file2.txt exists )
@@ -124,9 +124,9 @@ test_expect_success 'incorrect file in sha1:path' '
 
 test_expect_success 'incorrect file in :path and :N:path' '
        test_must_fail git rev-parse :nothing.txt 2>error &&
-       test_i18ngrep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
+       test_grep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
        test_must_fail git rev-parse :1:nothing.txt 2>error &&
-       test_i18ngrep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
+       test_grep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
        test_must_fail git rev-parse :1:file.txt 2>error &&
        test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
        (cd subdir &&
@@ -137,42 +137,42 @@ test_expect_success 'incorrect file in :path and :N:path' '
         test_must_fail git rev-parse :2:file2.txt 2>error &&
         test_did_you_mean :0 subdir/ file2.txt "is in the index") &&
        test_must_fail git rev-parse :disk-only.txt 2>error &&
-       test_i18ngrep "path .disk-only.txt. exists on disk, but not in the index" error
+       test_grep "path .disk-only.txt. exists on disk, but not in the index" error
 '
 
 test_expect_success 'invalid @{n} reference' '
        test_must_fail git rev-parse main@{99999} >output 2>error &&
        test_must_be_empty output &&
-       test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error  &&
+       test_grep "log for [^ ]* only has [0-9][0-9]* entries" error  &&
        test_must_fail git rev-parse --verify main@{99999} >output 2>error &&
        test_must_be_empty output &&
-       test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error
+       test_grep "log for [^ ]* only has [0-9][0-9]* entries" error
 '
 
 test_expect_success 'relative path not found' '
        (
                cd subdir &&
                test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
-               test_i18ngrep subdir/nonexistent.txt error
+               test_grep subdir/nonexistent.txt error
        )
 '
 
 test_expect_success 'relative path outside worktree' '
        test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
        test_must_be_empty output &&
-       test_i18ngrep "outside repository" error
+       test_grep "outside repository" error
 '
 
 test_expect_success 'relative path when cwd is outside worktree' '
        test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
        test_must_be_empty output &&
-       test_i18ngrep "relative path syntax can.t be used outside working tree" error
+       test_grep "relative path syntax can.t be used outside working tree" error
 '
 
 test_expect_success '<commit>:file correctly diagnosed after a pathname' '
        test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error &&
-       test_i18ngrep ! "exists on disk" error &&
-       test_i18ngrep "no such path in the working tree" error &&
+       test_grep ! "exists on disk" error &&
+       test_grep "no such path in the working tree" error &&
        cat >expect <<-\EOF &&
        file.txt
        HEAD:file.txt
@@ -214,13 +214,13 @@ test_expect_success 'dotdot does not peel endpoints' '
 
 test_expect_success 'arg before dashdash must be a revision (missing)' '
        test_must_fail git rev-parse foobar -- 2>stderr &&
-       test_i18ngrep "bad revision" stderr
+       test_grep "bad revision" stderr
 '
 
 test_expect_success 'arg before dashdash must be a revision (file)' '
        >foobar &&
        test_must_fail git rev-parse foobar -- 2>stderr &&
-       test_i18ngrep "bad revision" stderr
+       test_grep "bad revision" stderr
 '
 
 test_expect_success 'arg before dashdash must be a revision (ambiguous)' '
@@ -269,7 +269,7 @@ test_expect_success 'arg after dashdash not interpreted as option' '
 
 test_expect_success 'arg after end-of-options not interpreted as option' '
        test_must_fail git rev-parse --end-of-options --not-real -- 2>err &&
-       test_i18ngrep bad.revision.*--not-real err
+       test_grep bad.revision.*--not-real err
 '
 
 test_expect_success 'end-of-options still allows --' '
index c34714ffe3fbe5545da1d05b9a74cfc6113e43dd..b9af6b3ac035a04858ae0c7e0963166af665bd81 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test <branch>@{upstream} syntax'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
@@ -97,7 +98,8 @@ test_expect_success 'my-side@{u} resolves to correct commit' '
        commit_subject my-side >actual &&
        test_cmp expect actual &&
        echo 5 >expect &&
-       commit_subject my-side@{u} >actual
+       commit_subject my-side@{u} >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'not-tracking@{u} fails' '
@@ -183,6 +185,11 @@ test_expect_success '@{u} error message when no upstream' '
        test_cmp expect actual
 '
 
+test_expect_success '@{u} silent error when no upstream' '
+       test_must_fail git rev-parse --verify --quiet @{u} 2>actual &&
+       test_must_be_empty actual
+'
+
 test_expect_success 'branch@{u} error message with misspelt branch' '
        cat >expect <<-EOF &&
        fatal: no such branch: ${SQ}no-such-branch${SQ}
@@ -258,7 +265,8 @@ test_expect_success '@{reflog}-parsing does not look beyond colon' '
        git add @{yesterday} &&
        git commit -m "funny reflog file" &&
        git hash-object @{yesterday} >expect &&
-       git rev-parse HEAD:@{yesterday} >actual
+       git rev-parse HEAD:@{yesterday} >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '@{upstream}-parsing does not look beyond colon' '
@@ -266,7 +274,8 @@ test_expect_success '@{upstream}-parsing does not look beyond colon' '
        git add @{upstream} &&
        git commit -m "funny upstream file" &&
        git hash-object @{upstream} >expect &&
-       git rev-parse HEAD:@{upstream} >actual
+       git rev-parse HEAD:@{upstream} >actual &&
+       test_cmp expect actual
 '
 
 test_done
index 87a42864145f85ed539b9d1cbb9a3dd5f71fc708..e841309d0eab1022f3442c23795f884ad98a8280 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test various @{X} syntax combinations together'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check() {
index 553a3f601ba7c76efa193d4a33823197c05281e7..c799f5b6acad35633c6efa87a1a5e10aa67b8fd2 100755 (executable)
@@ -221,7 +221,8 @@ test_expect_success 'setup' '
        rm -rf /.git &&
        echo "Initialized empty Git repository in /.git/" > expected &&
        git init > result &&
-       test_cmp expected result
+       test_cmp expected result &&
+       git config --global --add safe.directory /
 '
 
 test_vars 'auto gitdir, root' ".git" "/" ""
@@ -242,7 +243,7 @@ say "auto bare gitdir"
 # DESTROYYYYY!!!!!
 test_expect_success 'setup' '
        rm -rf /refs /objects /info /hooks &&
-       rm -f /expected /ls.expected /me /result &&
+       rm -f /HEAD /expected /ls.expected /me /result &&
        cd / &&
        echo "Initialized empty Git repository in /" > expected &&
        git init --bare > result &&
@@ -255,4 +256,9 @@ test_expect_success 'go to /foo' 'cd /foo'
 
 test_vars 'auto gitdir, root' "/" "" ""
 
+test_expect_success 'cleanup root' '
+       rm -rf /.git /refs /objects /info /hooks /branches /foo &&
+       rm -f /HEAD /config /description /expected /ls.expected /me /result
+'
+
 test_done
index 98cefe3b7039fe726148e47fdf81b1f9fb454e15..70f1e0a998e103704373e2cc4ffd21e09c7c64b0 100755 (executable)
@@ -129,7 +129,7 @@ test_expect_success 'blob and tree' '
 
 test_expect_success 'warn ambiguity when no candidate matches type hint' '
        test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
-       test_i18ngrep "short object ID 000000000 is ambiguous" actual
+       test_grep "short object ID 000000000 is ambiguous" actual
 '
 
 test_expect_success 'disambiguate tree-ish' '
@@ -470,10 +470,10 @@ test_expect_success 'cat-file --batch and --batch-check show ambiguous' '
        echo "0000 ambiguous" >expect &&
        echo 0000 | git cat-file --batch-check >actual 2>err &&
        test_cmp expect actual &&
-       test_i18ngrep hint: err &&
+       test_grep hint: err &&
        echo 0000 | git cat-file --batch >actual 2>err &&
        test_cmp expect actual &&
-       test_i18ngrep hint: err
+       test_grep hint: err
 '
 
 test_done
index d868a0811056c83810bdd468d85e6f3a90622da7..a835a196aa81be9e7aa79f38c3977a03b71b5336 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test <branch>@{push} syntax'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 resolve () {
index 010989f90e63f9ddf5a88d25198998c55782bbd2..62e7fd15964bba7cbc8904471879027219368951 100755 (executable)
@@ -65,6 +65,37 @@ test_expect_success 'out of bounds index.version issues warning' '
        )
 '
 
+test_expect_success 'index.skipHash config option' '
+       rm -f .git/index &&
+       git -c index.skipHash=true add a &&
+       test_trailing_hash .git/index >hash &&
+       echo $(test_oid zero) >expect &&
+       test_cmp expect hash &&
+       git fsck &&
+
+       rm -f .git/index &&
+       git -c feature.manyFiles=true add a &&
+       test_trailing_hash .git/index >hash &&
+       cmp expect hash &&
+
+       rm -f .git/index &&
+       git -c feature.manyFiles=true \
+           -c index.skipHash=false add a &&
+       test_trailing_hash .git/index >hash &&
+       ! cmp expect hash &&
+
+       test_commit start &&
+       git -c protocol.file.allow=always submodule add ./ sub &&
+       git config index.skipHash false &&
+       git -C sub config index.skipHash true &&
+       rm -f .git/modules/sub/index &&
+       >sub/file &&
+       git -C sub add a &&
+       test_trailing_hash .git/modules/sub/index >hash &&
+       test_cmp expect hash &&
+       git -C sub fsck
+'
+
 test_index_version () {
        INDEX_VERSION_CONFIG=$1 &&
        FEATURE_MANY_FILES=$2 &&
@@ -87,7 +118,7 @@ test_index_version () {
                fi &&
                git add a &&
                echo $EXPECTED_OUTPUT_VERSION >expect &&
-               test-tool index-version <.git/index >actual &&
+               git update-index --show-index-version >actual &&
                test_cmp expect actual
        )
 }
index b4ab166369ec06c69add33eb240d69531459276c..a7b7263b35d1d8f142eec5311c9e42c25e398878 100755 (executable)
@@ -43,7 +43,7 @@ test_expect_success 'enable split index' '
        git config splitIndex.maxPercentChange 100 &&
        git update-index --split-index &&
        test-tool dump-split-index .git/index >actual &&
-       indexversion=$(test-tool index-version <.git/index) &&
+       indexversion=$(git update-index --show-index-version) &&
 
        # NEEDSWORK: Stop hard-coding checksums.
        if test "$indexversion" = "4"
index 43fcb7c0bfc85e3d29455ec37cfbf437e00238a9..8b0234cf2d5d96980403d65b844639715acfe91b 100755 (executable)
@@ -95,7 +95,7 @@ test_expect_success 'git hook run -- out-of-repo runs excluded' '
 test_expect_success 'git -c core.hooksPath=<PATH> hook run' '
        mkdir my-hooks &&
        write_script my-hooks/test-hook <<-\EOF &&
-       echo Hook ran $1 >>actual
+       echo Hook ran $1
        EOF
 
        cat >expect <<-\EOF &&
@@ -156,24 +156,32 @@ test_expect_success 'git hook run a hook with a bad shebang' '
        mkdir bad-hooks &&
        write_script bad-hooks/test-hook "/bad/path/no/spaces" </dev/null &&
 
-       # TODO: We should emit the same (or at least a more similar)
-       # error on MINGW (essentially Git for Windows) and all other
-       # platforms.. See the OS-specific code in start_command()
-       if test_have_prereq !MINGW
-       then
-               cat >expect <<-\EOF
-               fatal: cannot run bad-hooks/test-hook: ...
-               EOF
-       else
-               cat >expect <<-\EOF
-               error: cannot spawn bad-hooks/test-hook: ...
-               EOF
-       fi &&
        test_expect_code 1 git \
                -c core.hooksPath=bad-hooks \
                hook run test-hook >out 2>err &&
        test_must_be_empty out &&
-       sed -e "s/test-hook: .*/test-hook: .../" <err >actual &&
+
+       # TODO: We should emit the same (or at least a more similar)
+       # error on MINGW (essentially Git for Windows) and all other
+       # platforms.. See the OS-specific code in start_command()
+       grep -E "^(error|fatal): cannot (exec|spawn) .*bad-hooks/test-hook" err
+'
+
+test_expect_success 'stdin to hooks' '
+       write_script .git/hooks/test-hook <<-\EOF &&
+       echo BEGIN stdin
+       cat
+       echo END stdin
+       EOF
+
+       cat >expect <<-EOF &&
+       BEGIN stdin
+       hello
+       END stdin
+       EOF
+
+       echo hello >input &&
+       git hook run --to-stdin=input test-hook 2>actual &&
        test_cmp expect actual
 '
 
index b16d69ca4ae0e8b61ce00a085273d8ad6f40f104..98e818f09f21fe6ea374dba692c3f4ed619af735 100755 (executable)
@@ -93,7 +93,7 @@ test_expect_success 'checkout all stages of unknown path' '
        rm -f path* .merge_* actual &&
        test_must_fail git checkout-index --stage=all --temp \
                -- does-not-exist 2>stderr &&
-       test_i18ngrep not.in.the.cache stderr
+       test_grep not.in.the.cache stderr
 '
 
 test_expect_success 'checkout all stages/one file to nothing' '
@@ -117,6 +117,26 @@ test_expect_success 'checkout all stages/one file to temporary files' '
        test $(cat $s3) = tree3path1)
 '
 
+test_expect_success '--stage=all implies --temp' '
+       rm -f path* .merge_* actual &&
+       git checkout-index --stage=all -- path1 &&
+       test_path_is_missing path1
+'
+
+test_expect_success 'overriding --stage=all resets implied --temp' '
+       rm -f path* .merge_* actual &&
+       git checkout-index --stage=all --stage=2 -- path1 &&
+       echo tree2path1 >expect &&
+       test_cmp expect path1
+'
+
+test_expect_success '--stage=all --no-temp is rejected' '
+       rm -f path* .merge_* actual &&
+       test_must_fail git checkout-index --stage=all --no-temp -- path1 2>err &&
+       grep -v "already exists" err &&
+       grep "options .--stage=all. and .--no-temp. cannot be used together" err
+'
+
 test_expect_success 'checkout some stages/one file to temporary files' '
        rm -f path* .merge_* actual &&
        git checkout-index --stage=all --temp -- path2 >actual &&
index 112682a45a1df3d5c19eab03ec79c1202d2cab71..67d18cfa104b867bfdd2004df0bc8c9e305cfdc9 100755 (executable)
@@ -22,8 +22,10 @@ test_expect_success \
 git checkout-index symlink &&
 test -f symlink'
 
-test_expect_success \
-'the file must be the blob we added during the setup' '
-test "$(git hash-object -t blob symlink)" = $l'
+test_expect_success 'the file must be the blob we added during the setup' '
+       echo "$l" >expect &&
+       git hash-object -t blob symlink >actual &&
+       test_cmp expect actual
+'
 
 test_done
index 5d119871d416cd4e79b3bff7c56aeac729b276e6..570ba38580d99513fe12a2c6ed1e1a4c6951e0e2 100755 (executable)
@@ -8,7 +8,7 @@ TEST_PASSES_SANITIZE_LEAK=true
 
 test_expect_success 'checkout-index --gobbledegook' '
        test_expect_code 129 git checkout-index --gobbledegook 2>err &&
-       test_i18ngrep "[Uu]sage" err
+       test_grep "[Uu]sage" err
 '
 
 test_expect_success 'checkout-index -h in broken repository' '
@@ -19,18 +19,18 @@ test_expect_success 'checkout-index -h in broken repository' '
                >.git/index &&
                test_expect_code 129 git checkout-index -h >usage 2>&1
        ) &&
-       test_i18ngrep "[Uu]sage" broken/usage
+       test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'checkout-index reports errors (cmdline)' '
        test_must_fail git checkout-index -- does-not-exist 2>stderr &&
-       test_i18ngrep not.in.the.cache stderr
+       test_grep not.in.the.cache stderr
 '
 
 test_expect_success 'checkout-index reports errors (stdin)' '
        echo does-not-exist |
        test_must_fail git checkout-index --stdin 2>stderr &&
-       test_i18ngrep not.in.the.cache stderr
+       test_grep not.in.the.cache stderr
 '
 for mode in 'case' 'utf-8'
 do
@@ -88,8 +88,8 @@ test_expect_success 'checkout-index --temp correctly reports error on missing bl
        git update-index --index-info <objs &&
 
        test_must_fail git checkout-index --temp symlink file 2>stderr &&
-       test_i18ngrep "unable to read sha1 file of file ($missing_blob)" stderr &&
-       test_i18ngrep "unable to read sha1 file of symlink ($missing_blob)" stderr
+       test_grep "unable to read sha1 file of file ($missing_blob)" stderr &&
+       test_grep "unable to read sha1 file of symlink ($missing_blob)" stderr
 '
 
 test_expect_success 'checkout-index --temp correctly reports error for submodules' '
@@ -98,7 +98,7 @@ test_expect_success 'checkout-index --temp correctly reports error for submodule
        git submodule add ./sub &&
        git commit -m sub &&
        test_must_fail git checkout-index --temp sub 2>stderr &&
-       test_i18ngrep "cannot create temporary submodule sub" stderr
+       test_grep "cannot create temporary submodule sub" stderr
 '
 
 test_done
index 9d4b37526a326396fc334580350ff3cef3102b1e..82c3bfeac1ad9b96eb2adb9ae835fb2032222bd5 100755 (executable)
@@ -62,8 +62,8 @@ test_expect_success 'disambiguate checking out from a tree-ish' '
 
 test_expect_success 'accurate error message with more than one ref' '
        test_must_fail git checkout HEAD main -- 2>actual &&
-       test_i18ngrep 2 actual &&
-       test_i18ngrep "one reference expected, 2 given" actual
+       test_grep 2 actual &&
+       test_grep "one reference expected, 2 given" actual
 '
 
 test_done
index d9997e7b6b41a19537eed0bd512be1e8c78dc3d6..3c8135831b827d592c67c8d1316b1d768e87dc26 100755 (executable)
@@ -18,18 +18,18 @@ test_expect_success 'checkout should not start branch from a tree' '
        test_must_fail git checkout -b newbranch main^{tree}
 '
 
-test_expect_success 'checkout main from invalid HEAD' '
+test_expect_success REFFILES 'checkout main from invalid HEAD' '
        echo $ZERO_OID >.git/HEAD &&
        git checkout main --
 '
 
-test_expect_success 'checkout notices failure to lock HEAD' '
+test_expect_success REFFILES 'checkout notices failure to lock HEAD' '
        test_when_finished "rm -f .git/HEAD.lock" &&
        >.git/HEAD.lock &&
        test_must_fail git checkout -b other
 '
 
-test_expect_success 'create ref directory/file conflict scenario' '
+test_expect_success REFFILES 'create ref directory/file conflict scenario' '
        git update-ref refs/heads/outer/inner main &&
 
        # do not rely on symbolic-ref to get a known state,
@@ -39,26 +39,26 @@ test_expect_success 'create ref directory/file conflict scenario' '
        }
 '
 
-test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '
+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to branch)' '
        reset_to_df &&
        git checkout main
 '
 
-test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '
+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to detached)' '
        reset_to_df &&
        git checkout --detach main
 '
 
-test_expect_success 'pack refs' '
+test_expect_success REFFILES 'pack refs' '
        git pack-refs --all --prune
 '
 
-test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '
+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to branch)' '
        reset_to_df &&
        git checkout main
 '
 
-test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '
+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to detached)' '
        reset_to_df &&
        git checkout --detach main
 '
index 1f6c4ed0428a5c966c76359c1f4fa2e536f37fcf..4b6372f4c3e946c26a490698335a821cff5701f2 100755 (executable)
@@ -5,6 +5,7 @@ test_description='checkout can switch to last branch and merge base'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 9425aae6395838b3d0fd57b8cf8fe228d1db335f..fb0e13881cd8a290dd272c6dc16c3f097f49cb39 100755 (executable)
@@ -9,11 +9,12 @@ TEST_PASSES_SANITIZE_LEAK=true
 
 test_expect_success 'setup' '
        mkdir parent &&
-       (cd parent &&
-        git init &&
-        echo content >file &&
-        git add file &&
-        git commit -m base
+       (
+               cd parent &&
+               git init &&
+               echo content >file &&
+               git add file &&
+               git commit -m base
        ) &&
        git fetch parent main:origin
 '
index a5822e41af28112d44f8a2076804a5dd7d197904..747eb5563eca8f1df4374f179a08019221ba1fac 100755 (executable)
@@ -4,12 +4,6 @@ test_description='git checkout --patch'
 
 . ./lib-patch-mode.sh
 
-if ! test_have_prereq ADD_I_USE_BUILTIN && ! test_have_prereq PERL
-then
-       skip_all='skipping interactive add tests, PERL not set'
-       test_done
-fi
-
 test_expect_success 'setup' '
        mkdir dir &&
        echo parent > dir/foo &&
index 771c3c3c50e60583e3ba8dffbb62f47a7619f35b..43551cc1481c3813c3aae0b291bac2b1a17b63d7 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='checkout'
 
 TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Arguments: [!] <branch> <oid> [<checkout options>]
@@ -277,12 +278,12 @@ test_expect_success 'checkout -b to a new branch preserves mergeable changes des
 
 test_expect_success 'checkout -b rejects an invalid start point' '
        test_must_fail git checkout -b branch4 file1 2>err &&
-       test_i18ngrep "is not a commit" err
+       test_grep "is not a commit" err
 '
 
 test_expect_success 'checkout -b rejects an extra path argument' '
        test_must_fail git checkout -b branch5 branch1 file1 2>err &&
-       test_i18ngrep "Cannot update paths and switch to branch" err
+       test_grep "Cannot update paths and switch to branch" err
 '
 
 test_done
index 2c8c926b4d73a32a23380ec347e6c89c5a8e9657..c67261e2b68a9d330dc14c92783a485b0f605601 100755 (executable)
@@ -16,7 +16,7 @@ test_expect_success 'setup ambiguous refs' '
 '
 
 test_expect_success 'checkout ambiguous ref succeeds' '
-       git checkout ambiguity >stdout 2>stderr
+       git checkout ambiguity 2>stderr
 '
 
 test_expect_success 'checkout produces ambiguity warning' '
@@ -32,12 +32,12 @@ test_expect_success 'checkout chooses branch over tag' '
 '
 
 test_expect_success 'checkout reports switch to branch' '
-       test_i18ngrep "Switched to branch" stderr &&
-       test_i18ngrep ! "^HEAD is now at" stderr
+       test_grep "Switched to branch" stderr &&
+       test_grep ! "^HEAD is now at" stderr
 '
 
 test_expect_success 'checkout vague ref succeeds' '
-       git checkout vagueness >stdout 2>stderr &&
+       git checkout vagueness 2>stderr &&
        test_set_prereq VAGUENESS_SUCCESS
 '
 
@@ -54,8 +54,8 @@ test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
 '
 
 test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
-       test_i18ngrep "Switched to branch" stderr &&
-       test_i18ngrep ! "^HEAD is now at" stderr
+       test_grep "Switched to branch" stderr &&
+       test_grep ! "^HEAD is now at" stderr
 '
 
 test_done
index 2eab6474f8d0f4b3116455c95cb71cd69f78a311..8202ef8c74f639fb53a16b943fb5923ecd4e420e 100755 (executable)
@@ -17,12 +17,12 @@ check_not_detached () {
 
 PREV_HEAD_DESC='Previous HEAD position was'
 check_orphan_warning() {
-       test_i18ngrep "you are leaving $2 behind" "$1" &&
-       test_i18ngrep ! "$PREV_HEAD_DESC" "$1"
+       test_grep "you are leaving $2 behind" "$1" &&
+       test_grep ! "$PREV_HEAD_DESC" "$1"
 }
 check_no_orphan_warning() {
-       test_i18ngrep ! "you are leaving .* commit.*behind" "$1" &&
-       test_i18ngrep "$PREV_HEAD_DESC" "$1"
+       test_grep ! "you are leaving .* commit.*behind" "$1" &&
+       test_grep "$PREV_HEAD_DESC" "$1"
 }
 
 reset () {
index 713c3fa6038632bff43ef8344be2d4a6a73cb32e..ecfacf0f7f3a5fa7c03fa837891c745dec10dda6 100755 (executable)
@@ -50,10 +50,13 @@ test_expect_success 'checkout commit with dir must not remove untracked a/b' '
 
 test_expect_success SYMLINKS 'the symlink remained' '
 
-       test_when_finished "rm a/b" &&
        test -h a/b
 '
 
+test_expect_success 'cleanup after previous symlink tests' '
+       rm a/b
+'
+
 test_expect_success SYMLINKS 'checkout -f must not follow symlinks when removing entries' '
        git checkout -f start &&
        mkdir dir &&
@@ -66,4 +69,15 @@ test_expect_success SYMLINKS 'checkout -f must not follow symlinks when removing
        test_path_is_file untracked/f
 '
 
+test_expect_success 'checkout --overwrite-ignore should succeed if only ignored files in the way' '
+       git checkout -b df_conflict &&
+       test_commit contents some_dir &&
+       git checkout start &&
+       mkdir some_dir &&
+       echo autogenerated information >some_dir/ignore &&
+       echo ignore >.git/info/exclude &&
+       git checkout --overwrite-ignore df_conflict &&
+       test_path_is_file some_dir
+'
+
 test_done
index 4a1c9014567542d2cc33425e3455defe708570a8..a97416ce65474df17e4133c8e14b23736ab7fc8d 100755 (executable)
@@ -93,7 +93,7 @@ test_expect_success 'when arg matches multiple remotes, do not fallback to inter
 
        test_must_fail git checkout ambiguous_branch_and_file 2>err &&
 
-       test_i18ngrep "matched multiple (2) remote tracking branches" err &&
+       test_grep "matched multiple (2) remote tracking branches" err &&
 
        # file must not be altered
        test_cmp expect ambiguous_branch_and_file
@@ -105,12 +105,12 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'
        test_must_fail git checkout foo 2>stderr &&
        test_branch main &&
        status_uno_is_clean &&
-       test_i18ngrep "^hint: " stderr &&
+       test_grep "^hint: " stderr &&
        test_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \
                checkout foo 2>stderr &&
        test_branch main &&
        status_uno_is_clean &&
-       test_i18ngrep ! "^hint: " stderr
+       test_grep ! "^hint: " stderr
 '
 
 test_expect_success PERL 'checkout -p with multiple remotes does not print advice' '
@@ -118,7 +118,7 @@ test_expect_success PERL 'checkout -p with multiple remotes does not print advic
        test_might_fail git branch -D foo &&
 
        git checkout -p foo 2>stderr &&
-       test_i18ngrep ! "^hint: " stderr &&
+       test_grep ! "^hint: " stderr &&
        status_uno_is_clean
 '
 
@@ -305,10 +305,13 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
        test_config branch.strict.merge refs/heads/main &&
        test_config branch.loose.merge main &&
 
-       git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
+       git checkout strict >expect.raw 2>&1 &&
+       sed -e "s/strict/BRANCHNAME/g" <expect.raw >expect &&
        status_uno_is_clean &&
-       git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual &&
+       git checkout loose >actual.raw 2>&1 &&
+       sed -e "s/loose/BRANCHNAME/g" <actual.raw >actual &&
        status_uno_is_clean &&
+       grep BRANCHNAME actual &&
 
        test_cmp expect actual
 '
index 8f13341cf8ee0d82e0adeeb28449e31d104a5243..246609d54d050e5fce76730318123b30f322b75a 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='checkout --no-overlay <tree-ish> -- <pathspec>'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -25,7 +26,7 @@ test_expect_success 'checkout --no-overlay removing last file from directory' '
 
 test_expect_success 'checkout -p --overlay is disallowed' '
        test_must_fail git checkout -p --overlay HEAD 2>actual &&
-       test_i18ngrep "fatal: options .-p. and .--overlay. cannot be used together" actual
+       test_grep "fatal: options .-p. and .--overlay. cannot be used together" actual
 '
 
 test_expect_success '--no-overlay --theirs with D/F conflict deletes file' '
index 9c651aefbca44283f3ac5f018557d97fb615106c..acd55217a6231c35d17f4fbefe2ea27e30d05501 100755 (executable)
@@ -149,16 +149,16 @@ test_expect_success 'error conditions' '
        echo fileA.t >list &&
 
        test_must_fail git checkout --pathspec-from-file=list --detach 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--detach. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--detach. cannot be used together" err &&
 
        test_must_fail git checkout --pathspec-from-file=list --patch 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
        test_must_fail git checkout --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+       test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git checkout --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
+       test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
 '
 
 test_done
index dca35aa3e3e264002703932e56add4322b475fcf..98f16c72399806d2e0a5cdc0580072dcc8c65dae 100755 (executable)
@@ -5,6 +5,7 @@ test_description='tests for git branch --track'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -21,7 +22,7 @@ test_expect_success 'checkout --track -b creates a new tracking branch' '
 
 test_expect_success 'checkout --track -b rejects an extra path argument' '
        test_must_fail git checkout --track -b branch2 main one.t 2>err &&
-       test_i18ngrep "cannot be used with updating paths" err
+       test_grep "cannot be used with updating paths" err
 '
 
 test_expect_success 'checkout --track -b overrides autoSetupMerge=inherit' '
index 2d8c70b03a50e9ca082f7343d78dbcf6ad456159..be3fcdde07562c27da31e7dfe549058f3ced0ea9 100755 (executable)
@@ -37,11 +37,17 @@ prime_resolve_undo () {
        git checkout second^0 &&
        test_tick &&
        test_must_fail git merge third^0 &&
-       echo merge does not leave anything &&
        check_resolve_undo empty &&
-       echo different >fi/le &&
-       git add fi/le &&
-       echo resolving records &&
+
+       # how should the conflict be resolved?
+       case "$1" in
+       remove)
+               rm -f file/le && git rm fi/le
+               ;;
+       *) # modify
+               echo different >fi/le && git add fi/le
+               ;;
+       esac
        check_resolve_undo recorded fi/le initial:fi/le second:fi/le third:fi/le
 }
 
@@ -122,6 +128,37 @@ test_expect_success 'add records checkout -m undoes' '
 test_expect_success 'unmerge with plumbing' '
        prime_resolve_undo &&
        git update-index --unresolve fi/le &&
+       git ls-files --resolve-undo fi/le >actual &&
+       test_must_be_empty actual &&
+       git ls-files -u >actual &&
+       test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge can be done even after committing' '
+       prime_resolve_undo &&
+       git commit -m "record to nuke MERGE_HEAD" &&
+       git update-index --unresolve fi/le &&
+       git ls-files --resolve-undo fi/le >actual &&
+       test_must_be_empty actual &&
+       git ls-files -u >actual &&
+       test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge removal' '
+       prime_resolve_undo remove &&
+       git update-index --unresolve fi/le &&
+       git ls-files --resolve-undo fi/le >actual &&
+       test_must_be_empty actual &&
+       git ls-files -u >actual &&
+       test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge removal after committing' '
+       prime_resolve_undo remove &&
+       git commit -m "record to nuke MERGE_HEAD" &&
+       git update-index --unresolve fi/le &&
+       git ls-files --resolve-undo fi/le >actual &&
+       test_must_be_empty actual &&
        git ls-files -u >actual &&
        test_line_count = 3 actual
 '
@@ -191,7 +228,7 @@ test_expect_success 'rerere forget (add-add conflict)' '
        git commit -m "add differently" &&
        test_must_fail git merge fifth &&
        git rerere forget add-differently 2>actual &&
-       test_i18ngrep "no remembered" actual
+       test_grep "no remembered" actual
 '
 
 test_expect_success 'resolve-undo keeps blobs from gc' '
index 5a7caf958c346620f6daffcc78ceaa75e03afe8f..c91c4db9361133e4e790f2c0201c5f41c14eaff1 100755 (executable)
@@ -146,4 +146,35 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
        test_cmp_config "" --default "" branch.main2.merge
 '
 
+test_expect_success 'switch back when temporarily detached and checked out elsewhere ' '
+       test_when_finished "
+               git worktree remove wt1 ||:
+               git worktree remove wt2 ||:
+               git checkout - ||:
+               git branch -D shared ||:
+       " &&
+       git checkout -b shared &&
+       test_commit shared-first &&
+       HASH1=$(git rev-parse --verify HEAD) &&
+       test_commit shared-second &&
+       test_commit shared-third &&
+       HASH2=$(git rev-parse --verify HEAD) &&
+       git worktree add wt1 -f shared &&
+       git -C wt1 bisect start &&
+       git -C wt1 bisect good $HASH1 &&
+       git -C wt1 bisect bad $HASH2 &&
+       git worktree add wt2 -f shared &&
+       git -C wt2 bisect start &&
+       git -C wt2 bisect good $HASH1 &&
+       git -C wt2 bisect bad $HASH2 &&
+       # we test in both worktrees to ensure that works
+       # as expected with "first" and "next" worktrees
+       test_must_fail git -C wt1 switch shared &&
+       test_must_fail git -C wt1 switch -C shared &&
+       git -C wt1 switch --ignore-other-worktrees shared &&
+       test_must_fail git -C wt2 switch shared &&
+       test_must_fail git -C wt2 switch -C shared &&
+       git -C wt2 switch --ignore-other-worktrees shared
+'
+
 test_done
index 7c43ddf1d99714ac5744b37dfb1badb6e0c785fc..16d6348b692806ab25faf89d77a73cd62fab4d93 100755 (executable)
@@ -137,4 +137,87 @@ test_expect_success 'restore --staged invalidates cache tree for deletions' '
        test_must_fail git rev-parse HEAD:new1
 '
 
+test_expect_success 'restore --merge to unresolve' '
+       O=$(echo original | git hash-object -w --stdin) &&
+       A=$(echo ourside | git hash-object -w --stdin) &&
+       B=$(echo theirside | git hash-object -w --stdin) &&
+       {
+               echo "100644 $O 1       file" &&
+               echo "100644 $A 2       file" &&
+               echo "100644 $B 3       file"
+       } | git update-index --index-info &&
+       echo nothing >file &&
+       git restore --worktree --merge file &&
+       cat >expect <<-\EOF &&
+       <<<<<<< ours
+       ourside
+       =======
+       theirside
+       >>>>>>> theirs
+       EOF
+       test_cmp expect file
+'
+
+test_expect_success 'restore --merge to unresolve after (mistaken) resolution' '
+       O=$(echo original | git hash-object -w --stdin) &&
+       A=$(echo ourside | git hash-object -w --stdin) &&
+       B=$(echo theirside | git hash-object -w --stdin) &&
+       {
+               echo "100644 $O 1       file" &&
+               echo "100644 $A 2       file" &&
+               echo "100644 $B 3       file"
+       } | git update-index --index-info &&
+       echo nothing >file &&
+       git add file &&
+       git restore --worktree --merge file &&
+       cat >expect <<-\EOF &&
+       <<<<<<< ours
+       ourside
+       =======
+       theirside
+       >>>>>>> theirs
+       EOF
+       test_cmp expect file
+'
+
+test_expect_success 'restore --merge to unresolve after (mistaken) resolution' '
+       O=$(echo original | git hash-object -w --stdin) &&
+       A=$(echo ourside | git hash-object -w --stdin) &&
+       B=$(echo theirside | git hash-object -w --stdin) &&
+       {
+               echo "100644 $O 1       file" &&
+               echo "100644 $A 2       file" &&
+               echo "100644 $B 3       file"
+       } | git update-index --index-info &&
+       git rm -f file &&
+       git restore --worktree --merge file &&
+       cat >expect <<-\EOF &&
+       <<<<<<< ours
+       ourside
+       =======
+       theirside
+       >>>>>>> theirs
+       EOF
+       test_cmp expect file
+'
+
+test_expect_success 'restore with merge options are incompatible with certain options' '
+       for opts in \
+               "--staged --ours" \
+               "--staged --theirs" \
+               "--staged --merge" \
+               "--source=HEAD --ours" \
+               "--source=HEAD --theirs" \
+               "--source=HEAD --merge" \
+               "--staged --conflict=diff3" \
+               "--staged --worktree --ours" \
+               "--staged --worktree --theirs" \
+               "--staged --worktree --merge" \
+               "--staged --worktree --conflict=zdiff3"
+       do
+               test_must_fail git restore $opts . 2>err &&
+               grep "cannot be used" err || return
+       done
+'
+
 test_done
index c22669b39f938d901555e2c9a43d8d1f07de6da8..8198a1e5789cc963c32ecb028209a7a773144fd5 100755 (executable)
@@ -152,16 +152,16 @@ test_expect_success 'error conditions' '
        >empty_list &&
 
        test_must_fail git restore --pathspec-from-file=list --patch --source=HEAD^1 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
        test_must_fail git restore --pathspec-from-file=list --source=HEAD^1 -- fileA.t 2>err &&
-       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+       test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git restore --pathspec-file-nul --source=HEAD^1 2>err &&
-       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+       test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        test_must_fail git restore --pathspec-from-file=empty_list --source=HEAD^1 2>err &&
-       test_i18ngrep -e "you must specify path(s) to restore" err
+       test_grep -e "you must specify path(s) to restore" err
 '
 
 test_expect_success 'wildcard pathspec matches file in subdirectory' '
index b8686aabd38b5ac8a60753bff80fddd4e91a5102..0bab134d71d3e785194562779ef63085de4e6545 100755 (executable)
@@ -39,7 +39,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'index is at version 2' '
-       test "$(test-tool index-version < .git/index)" = 2
+       test "$(git update-index --show-index-version)" = 2
 '
 
 test_expect_success 'update-index --skip-worktree' '
@@ -48,7 +48,7 @@ test_expect_success 'update-index --skip-worktree' '
 '
 
 test_expect_success 'index is at version 3 after having some skip-worktree entries' '
-       test "$(test-tool index-version < .git/index)" = 3
+       test "$(git update-index --show-index-version)" = 3
 '
 
 test_expect_success 'ls-files -t' '
@@ -61,7 +61,7 @@ test_expect_success 'update-index --no-skip-worktree' '
 '
 
 test_expect_success 'index version is back to 2 when there is no skip-worktree entry' '
-       test "$(test-tool index-version < .git/index)" = 2
+       test "$(git update-index --show-index-version)" = 2
 '
 
 test_done
index d943ddf47e08f98b20a1f3365dca594815e06ea5..95c004dc5c5462b8b18cf348e5b1c44115a73042 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success 'do not switch branches with dirty file' '
        echo dirt >file &&
        git update-index --assume-unchanged file &&
        test_must_fail git checkout - 2>err &&
-       test_i18ngrep overwritten err
+       test_grep overwritten err
 '
 
 test_done
index 07e6de84e6d090eeaa58dda7176986b28b3bc18e..cc72ead79f397873b5005196a99baad6b0ba2450 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success 'update-index --nonsense fails' '
 
 test_expect_success 'update-index --nonsense dumps usage' '
        test_expect_code 129 git update-index --nonsense 2>err &&
-       test_i18ngrep "[Uu]sage: git update-index" err
+       test_grep "[Uu]sage: git update-index" err
 '
 
 test_expect_success 'update-index -h with corrupt index' '
@@ -25,7 +25,7 @@ test_expect_success 'update-index -h with corrupt index' '
                >.git/index &&
                test_expect_code 129 git update-index -h >usage 2>&1
        ) &&
-       test_i18ngrep "[Uu]sage: git update-index" broken/usage
+       test_grep "[Uu]sage: git update-index" broken/usage
 '
 
 test_expect_success '--cacheinfo complains of missing arguments' '
@@ -83,7 +83,7 @@ test_expect_success '.lock files cleaned up' '
        cd repo &&
        git config core.worktree ../../worktree &&
        # --refresh triggers late setup_work_tree,
-       # active_cache_changed is zero, rollback_lock_file fails
+       # the_index.cache_changed is zero, rollback_lock_file fails
        git update-index --refresh --verbose >out &&
        test_must_be_empty out &&
        ! test -f .git/index.lock
@@ -111,4 +111,35 @@ test_expect_success '--chmod=+x and chmod=-x in the same argument list' '
        test_cmp expect actual
 '
 
+test_expect_success '--index-version' '
+       git commit --allow-empty -m snap &&
+       git reset --hard &&
+       git rm -f -r --cached . &&
+
+       # The default index version is 2 --- update this test
+       # when you change it in the code
+       git update-index --show-index-version >actual &&
+       echo 2 >expect &&
+       test_cmp expect actual &&
+
+       # The next test wants us to be using version 2
+       git update-index --index-version 2 &&
+
+       git update-index --index-version 4 --verbose >actual &&
+       echo "index-version: was 2, set to 4" >expect &&
+       test_cmp expect actual &&
+
+       git update-index --index-version 4 --verbose >actual &&
+       echo "index-version: was 4, set to 4" >expect &&
+       test_cmp expect actual &&
+
+       git update-index --index-version 2 --verbose >actual &&
+       echo "index-version: was 4, set to 2" >expect &&
+       test_cmp expect actual &&
+
+       # non-verbose should be silent
+       git update-index --index-version 4 >actual &&
+       test_must_be_empty actual
+'
+
 test_done
index be394f1131ac191476dfa228be6fc6f35e0cecd0..c01492f33f860db2d6ae8764c94c084429abeef5 100755 (executable)
@@ -197,4 +197,15 @@ test_expect_success '"add -u non-existent" should fail' '
        ! grep "non-existent" actual
 '
 
+test_expect_success '"commit -a" implies "add -u" if index becomes empty' '
+       git rm -rf \* &&
+       git commit -m clean-slate &&
+       test_commit file1 &&
+       rm file1.t &&
+       test_tick &&
+       git commit -a -m remove &&
+       git ls-tree HEAD: >out &&
+       test_must_be_empty out
+'
+
 test_done
index ebf58db2d1827209464c94ac621f16f1693367d5..8fa44a92a277f414018d3f71731f791ec4b4f490 100755 (executable)
@@ -173,7 +173,7 @@ test_expect_success 'rename detection finds the right names' '
                git add -N third &&
 
                git status | grep -v "^?" >actual.1 &&
-               test_i18ngrep "renamed: *first -> third" actual.1 &&
+               test_grep "renamed: *first -> third" actual.1 &&
 
                git status --porcelain | grep -v "^?" >actual.2 &&
                cat >expected.2 <<-\EOF &&
@@ -213,8 +213,8 @@ test_expect_success 'double rename detection in status' '
                git add -N third &&
 
                git status | grep -v "^?" >actual.1 &&
-               test_i18ngrep "renamed: *first -> second" actual.1 &&
-               test_i18ngrep "renamed: *second -> third" actual.1 &&
+               test_grep "renamed: *first -> second" actual.1 &&
+               test_grep "renamed: *second -> third" actual.1 &&
 
                git status --porcelain | grep -v "^?" >actual.2 &&
                cat >expected.2 <<-\EOF &&
index 89424abccd1c5663b8c2a8a6c3b023831887f330..b7cf1e492c1073ee3fb4705bf8755d8b49c3d6d0 100755 (executable)
@@ -36,7 +36,7 @@ do
        '
 
        test_expect_success "complaints for ignored $i output" '
-               test_i18ngrep -e "Use -f if" err
+               test_grep -e "Use -f if" err
        '
 
        test_expect_success "complaints for ignored $i with unignored file" '
@@ -46,7 +46,7 @@ do
                test_must_be_empty out
        '
        test_expect_success "complaints for ignored $i with unignored file output" '
-               test_i18ngrep -e "Use -f if" err
+               test_grep -e "Use -f if" err
        '
 done
 
@@ -65,7 +65,7 @@ do
        test_expect_success "complaints for ignored $i in dir output" '
                (
                        cd dir &&
-                       test_i18ngrep -e "Use -f if" err
+                       test_grep -e "Use -f if" err
                )
        '
 done
@@ -85,7 +85,7 @@ do
        test_expect_success "complaints for ignored $i in sub output" '
                (
                        cd sub &&
-                       test_i18ngrep -e "Use -f if" err
+                       test_grep -e "Use -f if" err
                )
        '
 done
index d587e0b20db22423ef1a1d204076c50b769e0375..3742971105c15268a3c8c41642ff98a75be4f1bf 100755 (executable)
@@ -121,7 +121,30 @@ test_expect_success '"add" worktree creating new branch' '
 test_expect_success 'die the same branch is already checked out' '
        (
                cd here &&
-               test_must_fail git checkout newmain
+               test_must_fail git checkout newmain 2>actual &&
+               grep "already used by worktree at" actual
+       )
+'
+
+test_expect_success 'refuse to reset a branch in use elsewhere' '
+       (
+               cd here &&
+
+               # we know we are on detached HEAD but just in case ...
+               git checkout --detach HEAD &&
+               git rev-parse --verify HEAD >old.head &&
+
+               git rev-parse --verify refs/heads/newmain >old.branch &&
+               test_must_fail git checkout -B newmain 2>error &&
+               git rev-parse --verify refs/heads/newmain >new.branch &&
+               git rev-parse --verify HEAD >new.head &&
+
+               grep "already used by worktree at" error &&
+               test_cmp old.branch new.branch &&
+               test_cmp old.head new.head &&
+
+               # and we must be still on the same detached HEAD state
+               test_must_fail git symbolic-ref HEAD
        )
 '
 
@@ -298,17 +321,24 @@ test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' '
        test_must_fail git -C mish/mash symbolic-ref HEAD
 '
 
-test_expect_success '"add" -b/-B mutually exclusive' '
-       test_must_fail git worktree add -b poodle -B poodle bamboo main
-'
-
-test_expect_success '"add" -b/--detach mutually exclusive' '
-       test_must_fail git worktree add -b poodle --detach bamboo main
-'
+# Helper function to test mutually exclusive options.
+#
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_excl () {
+       local opts="$*" &&
+       test_expect_success "'worktree add' with '$opts' has mutually exclusive options" '
+               test_must_fail git worktree add $opts 2>actual &&
+               grep -E "fatal:( options)? .* cannot be used together" actual
+       '
+}
 
-test_expect_success '"add" -B/--detach mutually exclusive' '
-       test_must_fail git worktree add -B poodle --detach bamboo main
-'
+test_wt_add_excl -b poodle -B poodle bamboo main
+test_wt_add_excl -b poodle --detach bamboo main
+test_wt_add_excl -B poodle --detach bamboo main
+test_wt_add_excl --orphan --detach bamboo
+test_wt_add_excl --orphan --no-checkout bamboo
+test_wt_add_excl --orphan bamboo main
+test_wt_add_excl --orphan -b bamboo wtdir/ main
 
 test_expect_success '"add -B" fails if the branch is checked out' '
        git rev-parse newmain >before &&
@@ -326,10 +356,111 @@ test_expect_success 'add -B' '
 '
 
 test_expect_success 'add --quiet' '
+       test_when_finished "git worktree remove -f -f another-worktree" &&
        git worktree add --quiet another-worktree main 2>actual &&
        test_must_be_empty actual
 '
 
+test_expect_success 'add --quiet -b' '
+       test_when_finished "git branch -D quietnewbranch" &&
+       test_when_finished "git worktree remove -f -f another-worktree" &&
+       git worktree add --quiet -b quietnewbranch another-worktree 2>actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success '"add --orphan"' '
+       test_when_finished "git worktree remove -f -f orphandir" &&
+       git worktree add --orphan -b neworphan orphandir &&
+       echo refs/heads/neworphan >expected &&
+       git -C orphandir symbolic-ref HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '"add --orphan (no -b)"' '
+       test_when_finished "git worktree remove -f -f neworphan" &&
+       git worktree add --orphan neworphan &&
+       echo refs/heads/neworphan >expected &&
+       git -C neworphan symbolic-ref HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '"add --orphan --quiet"' '
+       test_when_finished "git worktree remove -f -f orphandir" &&
+       git worktree add --quiet --orphan -b neworphan orphandir 2>log.actual &&
+       test_must_be_empty log.actual &&
+       echo refs/heads/neworphan >expected &&
+       git -C orphandir symbolic-ref HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '"add --orphan" fails if the branch already exists' '
+       test_when_finished "git branch -D existingbranch" &&
+       git worktree add -b existingbranch orphandir main &&
+       git worktree remove orphandir &&
+       test_must_fail git worktree add --orphan -b existingbranch orphandir
+'
+
+test_expect_success '"add --orphan" with empty repository' '
+       test_when_finished "rm -rf empty_repo" &&
+       echo refs/heads/newbranch >expected &&
+       GIT_DIR="empty_repo" git init --bare &&
+       git -C empty_repo worktree add --orphan -b newbranch worktreedir &&
+       git -C empty_repo/worktreedir symbolic-ref HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '"add" worktree with orphan branch and lock' '
+       git worktree add --lock --orphan -b orphanbr orphan-with-lock &&
+       test_when_finished "git worktree unlock orphan-with-lock || :" &&
+       test -f .git/worktrees/orphan-with-lock/locked
+'
+
+test_expect_success '"add" worktree with orphan branch, lock, and reason' '
+       lock_reason="why not" &&
+       git worktree add --detach --lock --reason "$lock_reason" orphan-with-lock-reason main &&
+       test_when_finished "git worktree unlock orphan-with-lock-reason || :" &&
+       test -f .git/worktrees/orphan-with-lock-reason/locked &&
+       echo "$lock_reason" >expect &&
+       test_cmp expect .git/worktrees/orphan-with-lock-reason/locked
+'
+
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_orphan_hint () {
+       local context="$1" &&
+       local use_branch=$2 &&
+       shift 2 &&
+       local opts="$*" &&
+       test_expect_success "'worktree add' show orphan hint in bad/orphan HEAD w/ $context" '
+               test_when_finished "rm -rf repo" &&
+               git init repo &&
+               (cd repo && test_commit commit) &&
+               git -C repo switch --orphan noref &&
+               test_must_fail git -C repo worktree add $opts foobar/ 2>actual &&
+               ! grep "error: unknown switch" actual &&
+               grep "hint: If you meant to create a worktree containing a new unborn branch" actual &&
+               if [ $use_branch -eq 1 ]
+               then
+                       grep -E "^hint: +git worktree add --orphan -b [^ ]+ [^ ]+$" actual
+               else
+                       grep -E "^hint: +git worktree add --orphan [^ ]+$" actual
+               fi
+
+       '
+}
+
+test_wt_add_orphan_hint 'no opts' 0
+test_wt_add_orphan_hint '-b' 1 -b foobar_branch
+test_wt_add_orphan_hint '-B' 1 -B foobar_branch
+
+test_expect_success "'worktree add' doesn't show orphan hint in bad/orphan HEAD w/ --quiet" '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (cd repo && test_commit commit) &&
+       test_must_fail git -C repo worktree add --quiet foobar_branch foobar/ 2>actual &&
+       ! grep "error: unknown switch" actual &&
+       ! grep "hint: If you meant to create a worktree containing a new unborn branch" actual
+'
+
 test_expect_success 'local clone from linked checkout' '
        git clone --local here here-clone &&
        ( cd here-clone && git fsck )
@@ -446,6 +577,14 @@ setup_remote_repo () {
        )
 }
 
+test_expect_success '"add" <path> <remote/branch> w/ no HEAD' '
+       test_when_finished rm -rf repo_upstream repo_local foo &&
+       setup_remote_repo repo_upstream repo_local &&
+       git -C repo_local config --bool core.bare true &&
+       git -C repo_local branch -D main &&
+       git -C repo_local worktree add ./foo repo_upstream/foo
+'
+
 test_expect_success '--no-track avoids setting up tracking' '
        test_when_finished rm -rf repo_upstream repo_local foo &&
        setup_remote_repo repo_upstream repo_local &&
@@ -528,6 +667,35 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '
                test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
        )
 '
+test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '
+       test_when_finished rm -rf repo_a repo_b foo &&
+       setup_remote_repo repo_a repo_b &&
+       (
+               cd repo_b &&
+               git worktree add --quiet --guess-remote ../foo 2>actual &&
+               test_must_be_empty actual
+       ) &&
+       (
+               cd foo &&
+               test_branch_upstream foo repo_a foo &&
+               test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+       )
+'
+
+test_expect_success 'git worktree --no-guess-remote (quiet)' '
+       test_when_finished rm -rf repo_a repo_b foo &&
+       setup_remote_repo repo_a repo_b &&
+       (
+               cd repo_b &&
+               git worktree add --quiet --no-guess-remote ../foo
+       ) &&
+       (
+               cd foo &&
+               test_must_fail git config "branch.foo.remote" &&
+               test_must_fail git config "branch.foo.merge" &&
+               test_cmp_rev ! refs/remotes/repo_a/foo refs/heads/foo
+       )
+'
 
 test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
        test_when_finished rm -rf repo_a repo_b foo &&
@@ -560,6 +728,348 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '
        )
 '
 
+test_dwim_orphan () {
+       local info_text="No possible source branch, inferring '--orphan'" &&
+       local fetch_error_text="fatal: No local or remote refs exist despite at least one remote" &&
+       local orphan_hint="hint: If you meant to create a worktree containing a new unborn branch" &&
+       local invalid_ref_regex="^fatal: invalid reference: " &&
+       local bad_combo_regex="^fatal: options '[-a-z]*' and '[-a-z]*' cannot be used together" &&
+
+       local git_ns="repo" &&
+       local dashc_args="-C $git_ns" &&
+       local use_cd=0 &&
+
+       local bad_head=0 &&
+       local empty_repo=1 &&
+       local local_ref=0 &&
+       local use_quiet=0 &&
+       local remote=0 &&
+       local remote_ref=0 &&
+       local use_detach=0 &&
+       local use_new_branch=0 &&
+
+       local outcome="$1" &&
+       local outcome_text &&
+       local success &&
+       shift &&
+       local args="" &&
+       local context="" &&
+       case "$outcome" in
+       "infer")
+               success=1 &&
+               outcome_text='"add" DWIM infer --orphan'
+               ;;
+       "no_infer")
+               success=1 &&
+               outcome_text='"add" DWIM doesnt infer --orphan'
+               ;;
+       "fetch_error")
+               success=0 &&
+               outcome_text='"add" error need fetch'
+               ;;
+       "fatal_orphan_bad_combo")
+               success=0 &&
+               outcome_text='"add" error inferred "--orphan" gives illegal opts combo'
+               ;;
+       "warn_bad_head")
+               success=0 &&
+               outcome_text='"add" error, warn on bad HEAD, hint use orphan'
+               ;;
+       *)
+               echo "test_dwim_orphan(): invalid outcome: '$outcome'" >&2 &&
+               return 1
+               ;;
+       esac &&
+       while [ $# -gt 0 ]
+       do
+               case "$1" in
+               # How and from where to create the worktree
+               "-C_repo")
+                       use_cd=0 &&
+                       git_ns="repo" &&
+                       dashc_args="-C $git_ns" &&
+                       context="$context, 'git -C repo'"
+                       ;;
+               "-C_wt")
+                       use_cd=0 &&
+                       git_ns="wt" &&
+                       dashc_args="-C $git_ns" &&
+                       context="$context, 'git -C wt'"
+                       ;;
+               "cd_repo")
+                       use_cd=1 &&
+                       git_ns="repo" &&
+                       dashc_args="" &&
+                       context="$context, 'cd repo && git'"
+                       ;;
+               "cd_wt")
+                       use_cd=1 &&
+                       git_ns="wt" &&
+                       dashc_args="" &&
+                       context="$context, 'cd wt && git'"
+                       ;;
+
+               # Bypass the "pull first" warning
+               "force")
+                       args="$args --force" &&
+                       context="$context, --force"
+                       ;;
+
+               # Try to use remote refs when DWIM
+               "guess_remote")
+                       args="$args --guess-remote" &&
+                       context="$context, --guess-remote"
+                       ;;
+               "no_guess_remote")
+                       args="$args --no-guess-remote" &&
+                       context="$context, --no-guess-remote"
+                       ;;
+
+               # Whether there is at least one local branch present
+               "local_ref")
+                       empty_repo=0 &&
+                       local_ref=1 &&
+                       context="$context, >=1 local branches"
+                       ;;
+               "no_local_ref")
+                       empty_repo=0 &&
+                       context="$context, 0 local branches"
+                       ;;
+
+               # Whether the HEAD points at a valid ref (skip this opt when no refs)
+               "good_head")
+                       # requires: local_ref
+                       context="$context, valid HEAD"
+                       ;;
+               "bad_head")
+                       bad_head=1 &&
+                       context="$context, invalid (or orphan) HEAD"
+                       ;;
+
+               # Whether the code path is tested with the base add command, -b, or --detach
+               "no_-b")
+                       use_new_branch=0 &&
+                       context="$context, no --branch"
+                       ;;
+               "-b")
+                       use_new_branch=1 &&
+                       context="$context, --branch"
+                       ;;
+               "detach")
+                       use_detach=1 &&
+                       context="$context, --detach"
+                       ;;
+
+               # Whether to check that all output is suppressed (except errors)
+               # or that the output is as expected
+               "quiet")
+                       use_quiet=1 &&
+                       args="$args --quiet" &&
+                       context="$context, --quiet"
+                       ;;
+               "no_quiet")
+                       use_quiet=0 &&
+                       context="$context, no --quiet (expect output)"
+                       ;;
+
+               # Whether there is at least one remote attached to the repo
+               "remote")
+                       empty_repo=0 &&
+                       remote=1 &&
+                       context="$context, >=1 remotes"
+                       ;;
+               "no_remote")
+                       empty_repo=0 &&
+                       remote=0 &&
+                       context="$context, 0 remotes"
+                       ;;
+
+               # Whether there is at least one valid remote ref
+               "remote_ref")
+                       # requires: remote
+                       empty_repo=0 &&
+                       remote_ref=1 &&
+                       context="$context, >=1 fetched remote branches"
+                       ;;
+               "no_remote_ref")
+                       empty_repo=0 &&
+                       remote_ref=0 &&
+                       context="$context, 0 fetched remote branches"
+                       ;;
+
+               # Options or flags that become illegal when --orphan is inferred
+               "no_checkout")
+                       args="$args --no-checkout" &&
+                       context="$context, --no-checkout"
+                       ;;
+               "track")
+                       args="$args --track" &&
+                       context="$context, --track"
+                       ;;
+
+               # All other options are illegal
+               *)
+                       echo "test_dwim_orphan(): invalid arg: '$1'" >&2 &&
+                       return 1
+                       ;;
+               esac &&
+               shift
+       done &&
+       context="${context#', '}" &&
+       if [ $use_new_branch -eq 1 ]
+       then
+               args="$args -b foo"
+       elif [ $use_detach -eq 1 ]
+       then
+               args="$args --detach"
+       else
+               context="DWIM (no --branch), $context"
+       fi &&
+       if [ $empty_repo -eq 1 ]
+       then
+               context="empty repo, $context"
+       fi &&
+       args="$args ../foo" &&
+       context="${context%', '}" &&
+       test_expect_success "$outcome_text w/ $context" '
+               test_when_finished "rm -rf repo" &&
+               git init repo &&
+               if [ $local_ref -eq 1 ] && [ "$git_ns" = "repo" ]
+               then
+                       (cd repo && test_commit commit) &&
+                       if [ $bad_head -eq 1 ]
+                       then
+                               git -C repo symbolic-ref HEAD refs/heads/badbranch
+                       fi
+               elif [ $local_ref -eq 1 ] && [ "$git_ns" = "wt" ]
+               then
+                       test_when_finished "git -C repo worktree remove -f ../wt" &&
+                       git -C repo worktree add --orphan -b main ../wt &&
+                       (cd wt && test_commit commit) &&
+                       if [ $bad_head -eq 1 ]
+                       then
+                               git -C wt symbolic-ref HEAD refs/heads/badbranch
+                       fi
+               elif [ $local_ref -eq 0 ] && [ "$git_ns" = "wt" ]
+               then
+                       test_when_finished "git -C repo worktree remove -f ../wt" &&
+                       git -C repo worktree add --orphan -b orphanbranch ../wt
+               fi &&
+
+               if [ $remote -eq 1 ]
+               then
+                       test_when_finished "rm -rf upstream" &&
+                       git init upstream &&
+                       (cd upstream && test_commit commit) &&
+                       git -C upstream switch -c foo &&
+                       git -C repo remote add upstream ../upstream
+               fi &&
+
+               if [ $remote_ref -eq 1 ]
+               then
+                       git -C repo fetch
+               fi &&
+               if [ $success -eq 1 ]
+               then
+                       test_when_finished git -C repo worktree remove ../foo
+               fi &&
+               (
+                       if [ $use_cd -eq 1 ]
+                       then
+                               cd $git_ns
+                       fi &&
+                       if [ "$outcome" = "infer" ]
+                       then
+                               git $dashc_args worktree add $args 2>actual &&
+                               if [ $use_quiet -eq 1 ]
+                               then
+                                       test_must_be_empty actual
+                               else
+                                       grep "$info_text" actual
+                               fi
+                       elif [ "$outcome" = "no_infer" ]
+                       then
+                               git $dashc_args worktree add $args 2>actual &&
+                               if [ $use_quiet -eq 1 ]
+                               then
+                                       test_must_be_empty actual
+                               else
+                                       ! grep "$info_text" actual
+                               fi
+                       elif [ "$outcome" = "fetch_error" ]
+                       then
+                               test_must_fail git $dashc_args worktree add $args 2>actual &&
+                               grep "$fetch_error_text" actual
+                       elif [ "$outcome" = "fatal_orphan_bad_combo" ]
+                       then
+                               test_must_fail git $dashc_args worktree add $args 2>actual &&
+                               if [ $use_quiet -eq 1 ]
+                               then
+                                       ! grep "$info_text" actual
+                               else
+                                       grep "$info_text" actual
+                               fi &&
+                               grep "$bad_combo_regex" actual
+                       elif [ "$outcome" = "warn_bad_head" ]
+                       then
+                               test_must_fail git $dashc_args worktree add $args 2>actual &&
+                               if [ $use_quiet -eq 1 ]
+                               then
+                                       grep "$invalid_ref_regex" actual &&
+                                       ! grep "$orphan_hint" actual
+                               else
+                                       headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) &&
+                                       headcontents=$(cat "$headpath") &&
+                                       grep "HEAD points to an invalid (or orphaned) reference" actual &&
+                                       grep "HEAD path: .$headpath." actual &&
+                                       grep "HEAD contents: .$headcontents." actual &&
+                                       grep "$orphan_hint" actual &&
+                                       ! grep "$info_text" actual
+                               fi &&
+                               grep "$invalid_ref_regex" actual
+                       else
+                               # Unreachable
+                               false
+                       fi
+               ) &&
+               if [ $success -ne 1 ]
+               then
+                       test_path_is_missing foo
+               fi
+       '
+}
+
+for quiet_mode in "no_quiet" "quiet"
+do
+       for changedir_type in "cd_repo" "cd_wt" "-C_repo" "-C_wt"
+       do
+               dwim_test_args="$quiet_mode $changedir_type"
+               test_dwim_orphan 'infer' $dwim_test_args no_-b
+               test_dwim_orphan 'no_infer' $dwim_test_args no_-b local_ref good_head
+               test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref no_remote no_remote_ref no_guess_remote
+               test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref remote no_remote_ref no_guess_remote
+               test_dwim_orphan 'fetch_error' $dwim_test_args no_-b no_local_ref remote no_remote_ref guess_remote
+               test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref remote no_remote_ref guess_remote force
+               test_dwim_orphan 'no_infer' $dwim_test_args no_-b no_local_ref remote remote_ref guess_remote
+
+               test_dwim_orphan 'infer' $dwim_test_args -b
+               test_dwim_orphan 'no_infer' $dwim_test_args -b local_ref good_head
+               test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref no_remote no_remote_ref no_guess_remote
+               test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref no_guess_remote
+               test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref guess_remote
+               test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote remote_ref guess_remote
+
+               test_dwim_orphan 'warn_bad_head' $dwim_test_args no_-b local_ref bad_head
+               test_dwim_orphan 'warn_bad_head' $dwim_test_args -b local_ref bad_head
+               test_dwim_orphan 'warn_bad_head' $dwim_test_args detach local_ref bad_head
+       done
+
+       test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b no_checkout
+       test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b track
+       test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode -b no_checkout
+       test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode -b track
+done
+
 post_checkout_hook () {
        test_when_finished "rm -rf .git/hooks" &&
        mkdir .git/hooks &&
index 3d28c7f06b2c8717e31fae23d37fda0acc008a48..71aa9bcd620f8a504fe628a2d7332d8b47fd4701 100755 (executable)
@@ -5,6 +5,7 @@ test_description='prune $GIT_DIR/worktrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success initialize '
@@ -46,7 +47,7 @@ test_expect_success SANITY 'prune directories with unreadable gitdir' '
        : >.git/worktrees/def/gitdir &&
        chmod u-r .git/worktrees/def/gitdir &&
        git worktree prune --verbose 2>actual &&
-       test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual &&
+       test_grep "Removing worktrees/def: unable to read gitdir file" actual &&
        ! test -d .git/worktrees/def &&
        ! test -d .git/worktrees
 '
@@ -56,7 +57,7 @@ test_expect_success 'prune directories with invalid gitdir' '
        : >.git/worktrees/def/def &&
        : >.git/worktrees/def/gitdir &&
        git worktree prune --verbose 2>actual &&
-       test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual &&
+       test_grep "Removing worktrees/def: invalid gitdir file" actual &&
        ! test -d .git/worktrees/def &&
        ! test -d .git/worktrees
 '
@@ -66,7 +67,7 @@ test_expect_success 'prune directories with gitdir pointing to nowhere' '
        : >.git/worktrees/def/def &&
        echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir &&
        git worktree prune --verbose 2>actual &&
-       test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
+       test_grep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
        ! test -d .git/worktrees/def &&
        ! test -d .git/worktrees
 '
@@ -102,7 +103,7 @@ test_expect_success 'prune duplicate (linked/linked)' '
        sed "s/w2/w1/" .git/worktrees/w2/gitdir >.git/worktrees/w2/gitdir.new &&
        mv .git/worktrees/w2/gitdir.new .git/worktrees/w2/gitdir &&
        git worktree prune --verbose 2>actual &&
-       test_i18ngrep "duplicate entry" actual &&
+       test_grep "duplicate entry" actual &&
        test -d .git/worktrees/w1 &&
        ! test -d .git/worktrees/w2
 '
@@ -115,7 +116,7 @@ test_expect_success 'prune duplicate (main/linked)' '
        rm -fr wt &&
        mv repo wt &&
        git -C wt worktree prune --verbose 2>actual &&
-       test_i18ngrep "duplicate entry" actual &&
+       test_grep "duplicate entry" actual &&
        ! test -d .git/worktrees/wt
 '
 
index 79e0fce2d90fb9e947a76052c32e41d20519e6bd..33ea9cb21ba07c9563530b54da06753eaa993fe2 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test git worktree list'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -142,7 +143,7 @@ test_expect_success '"list" all worktrees --porcelain with prunable' '
        rm -rf prunable &&
        git worktree list --porcelain >out &&
        sed -n "/^worktree .*\/prunable$/,/^$/p" <out >only_prunable &&
-       test_i18ngrep "^prunable gitdir file points to non-existent location$" only_prunable
+       test_grep "^prunable gitdir file points to non-existent location$" only_prunable
 '
 
 test_expect_success '"list" all worktrees with prunable consistent with "prune"' '
@@ -154,8 +155,8 @@ test_expect_success '"list" all worktrees with prunable consistent with "prune"'
        grep "/prunable  *[0-9a-f].* prunable$" out &&
        ! grep "/unprunable  *[0-9a-f].* unprunable$" out &&
        git worktree prune --verbose 2>out &&
-       test_i18ngrep "^Removing worktrees/prunable" out &&
-       test_i18ngrep ! "^Removing worktrees/unprunable" out
+       test_grep "^Removing worktrees/prunable" out &&
+       test_grep ! "^Removing worktrees/unprunable" out
 '
 
 test_expect_success '"list" --verbose and --porcelain mutually exclusive' '
index 230a55e99af85f9f0b60d13df1b15372012e0f89..901342ea09b51a8e832f1109fbb737df84283aa2 100755 (executable)
@@ -202,7 +202,7 @@ test_expect_success 'proper error when worktree not found' '
        for i in noodle noodle/bork
        do
                test_must_fail git worktree lock $i 2>err &&
-               test_i18ngrep "not a working tree" err || return 1
+               test_grep "not a working tree" err || return 1
        done
 '
 
index 5c44453e1c13322e1fcf30d2facac3c2942a274e..edbf502ec57bb7e778b96ffc0ef6798b4f2f3233 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='test git worktree repair'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -24,7 +25,7 @@ test_expect_success 'worktree path not directory' '
        >notdir &&
        test_must_fail git worktree repair >out 2>err &&
        test_must_be_empty out &&
-       test_i18ngrep "not a directory" err
+       test_grep "not a directory" err
 '
 
 test_expect_success "don't clobber .git repo" '
@@ -34,7 +35,7 @@ test_expect_success "don't clobber .git repo" '
        test_create_repo repo &&
        test_must_fail git worktree repair >out 2>err &&
        test_must_be_empty out &&
-       test_i18ngrep ".git is not a file" err
+       test_grep ".git is not a file" err
 '
 
 test_corrupt_gitfile () {
@@ -46,7 +47,7 @@ test_corrupt_gitfile () {
        git -C corrupt rev-parse --absolute-git-dir >expect &&
        eval "$butcher" &&
        git -C "$repairdir" worktree repair 2>err &&
-       test_i18ngrep "$problem" err &&
+       test_grep "$problem" err &&
        git -C corrupt rev-parse --absolute-git-dir >actual &&
        test_cmp expect actual
 }
@@ -92,7 +93,7 @@ test_expect_success 'repair .git file from bare.git' '
 test_expect_success 'invalid worktree path' '
        test_must_fail git worktree repair /notvalid >out 2>err &&
        test_must_be_empty out &&
-       test_i18ngrep "not a valid path" err
+       test_grep "not a valid path" err
 '
 
 test_expect_success 'repo not found; .git not file' '
@@ -100,7 +101,7 @@ test_expect_success 'repo not found; .git not file' '
        test_create_repo not-a-worktree &&
        test_must_fail git worktree repair not-a-worktree >out 2>err &&
        test_must_be_empty out &&
-       test_i18ngrep ".git is not a file" err
+       test_grep ".git is not a file" err
 '
 
 test_expect_success 'repo not found; .git not referencing repo' '
@@ -110,7 +111,7 @@ test_expect_success 'repo not found; .git not referencing repo' '
        mv side/.newgit side/.git &&
        mkdir not-a-repo &&
        test_must_fail git worktree repair side 2>err &&
-       test_i18ngrep ".git file does not reference a repository" err
+       test_grep ".git file does not reference a repository" err
 '
 
 test_expect_success 'repo not found; .git file broken' '
@@ -120,7 +121,7 @@ test_expect_success 'repo not found; .git file broken' '
        mv orig moved &&
        test_must_fail git worktree repair moved >out 2>err &&
        test_must_be_empty out &&
-       test_i18ngrep ".git file broken" err
+       test_grep ".git file broken" err
 '
 
 test_expect_success 'repair broken gitdir' '
@@ -131,7 +132,7 @@ test_expect_success 'repair broken gitdir' '
        mv orig moved &&
        git worktree repair moved 2>err &&
        test_cmp expect .git/worktrees/orig/gitdir &&
-       test_i18ngrep "gitdir unreadable" err
+       test_grep "gitdir unreadable" err
 '
 
 test_expect_success 'repair incorrect gitdir' '
@@ -141,7 +142,7 @@ test_expect_success 'repair incorrect gitdir' '
        mv orig moved &&
        git worktree repair moved 2>err &&
        test_cmp expect .git/worktrees/orig/gitdir &&
-       test_i18ngrep "gitdir incorrect" err
+       test_grep "gitdir incorrect" err
 '
 
 test_expect_success 'repair gitdir (implicit) from linked worktree' '
@@ -151,7 +152,7 @@ test_expect_success 'repair gitdir (implicit) from linked worktree' '
        mv orig moved &&
        git -C moved worktree repair 2>err &&
        test_cmp expect .git/worktrees/orig/gitdir &&
-       test_i18ngrep "gitdir incorrect" err
+       test_grep "gitdir incorrect" err
 '
 
 test_expect_success 'unable to repair gitdir (implicit) from main worktree' '
@@ -176,8 +177,8 @@ test_expect_success 'repair multiple gitdir files' '
        git worktree repair moved1 moved2 2>err &&
        test_cmp expect1 .git/worktrees/orig1/gitdir &&
        test_cmp expect2 .git/worktrees/orig2/gitdir &&
-       test_i18ngrep "gitdir incorrect:.*orig1/gitdir$" err &&
-       test_i18ngrep "gitdir incorrect:.*orig2/gitdir$" err
+       test_grep "gitdir incorrect:.*orig1/gitdir$" err &&
+       test_grep "gitdir incorrect:.*orig2/gitdir$" err
 '
 
 test_expect_success 'repair moved main and linked worktrees' '
index 019a40df2ca99480c547910026bf97102d54a23c..f6835c91dcc49cfeb23881fe0ef7a96629bfb2e6 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success 'refuse to overwrite: checked out in worktree' '
                grep "cannot force update the branch" err &&
 
                test_must_fail git branch -D wt-$i 2>err &&
-               grep "Cannot delete branch" err || return 1
+               grep "cannot delete branch" err || return 1
        done
 '
 
@@ -58,7 +58,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in bisect' '
        git -C wt-4 bisect good wt-1 &&
 
        test_must_fail git branch -f wt-4 HEAD 2>err &&
-       grep "cannot force update the branch '\''wt-4'\'' checked out at.*wt-4" err
+       grep "cannot force update the branch '\''wt-4'\'' used by worktree at.*wt-4" err
 '
 
 test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (apply)' '
@@ -68,7 +68,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (app
        test_must_fail git -C wt-2 rebase --apply conflict-2 &&
 
        test_must_fail git branch -f wt-2 HEAD 2>err &&
-       grep "cannot force update the branch '\''wt-2'\'' checked out at.*wt-2" err
+       grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err
 '
 
 test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (merge)' '
@@ -78,7 +78,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (mer
        test_must_fail git -C wt-2 rebase conflict-2 &&
 
        test_must_fail git branch -f wt-2 HEAD 2>err &&
-       grep "cannot force update the branch '\''wt-2'\'' checked out at.*wt-2" err
+       grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err
 '
 
 test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with --update-refs' '
@@ -90,7 +90,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with
        for i in 3 4
        do
                test_must_fail git branch -f can-be-updated HEAD 2>err &&
-               grep "cannot force update the branch '\''can-be-updated'\'' checked out at.*wt-3" err ||
+               grep "cannot force update the branch '\''can-be-updated'\'' used by worktree at.*wt-3" err ||
                        return 1
        done
 '
@@ -150,7 +150,7 @@ test_expect_success 'refuse to overwrite when in error states' '
        for i in 1 2
        do
                test_must_fail git branch -f fake-$i HEAD 2>err &&
-               grep "cannot force update the branch '\''fake-$i'\'' checked out at" err ||
+               grep "cannot force update the branch '\''fake-$i'\'' used by worktree at" err ||
                        return 1
        done
 '
index a16e25c79bdde748cbb9c1d5419960e0e5f8f3f5..12e41a7b40e0ab73efa1fc30354e80996c3a91a4 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success 'ls-files with nonexistent path' '
 
 test_expect_success 'ls-files with nonsense option' '
        test_expect_code 129 git ls-files --nonsense 2>actual &&
-       test_i18ngrep "[Uu]sage: git ls-files" actual
+       test_grep "[Uu]sage: git ls-files" actual
 '
 
 test_expect_success 'ls-files -h in corrupt repository' '
@@ -32,7 +32,7 @@ test_expect_success 'ls-files -h in corrupt repository' '
                >.git/index &&
                test_expect_code 129 git ls-files -h >usage 2>&1
        ) &&
-       test_i18ngrep "[Uu]sage: git ls-files " broken/usage
+       test_grep "[Uu]sage: git ls-files " broken/usage
 '
 
 test_expect_success SYMLINKS 'ls-files with absolute paths to symlinks' '
index dd7770e85de8660061116d42aa32b8d20fe52f95..61771eec830c0688587588ee0a334282effdb1bb 100755 (executable)
@@ -296,13 +296,46 @@ test_expect_success '--recurse-submodules and relative paths' '
 
 test_expect_success '--recurse-submodules does not support --error-unmatch' '
        test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual &&
-       test_i18ngrep "does not support --error-unmatch" actual
+       test_grep "does not support --error-unmatch" actual
+'
+
+test_expect_success '--recurse-submodules parses submodule repo config' '
+       test_config -C submodule index.sparse "invalid non-boolean value" &&
+       test_must_fail git ls-files --recurse-submodules 2>err &&
+       grep "bad boolean config value" err
+'
+
+test_expect_success '--recurse-submodules parses submodule worktree config' '
+       test_config -C submodule extensions.worktreeConfig true &&
+       test_config -C submodule --worktree index.sparse "invalid non-boolean value" &&
+
+       test_must_fail git ls-files --recurse-submodules 2>err &&
+       grep "bad boolean config value" err
+'
+
+test_expect_success '--recurse-submodules submodules ignore super project worktreeConfig extension' '
+       # Enable worktree config in both super project & submodule, set an
+       # invalid config in the submodule worktree config
+       test_config extensions.worktreeConfig true &&
+       test_config -C submodule extensions.worktreeConfig true &&
+       test_config -C submodule --worktree index.sparse "invalid non-boolean value" &&
+
+       # Now, disable the worktree config in the submodule. Note that we need
+       # to manually re-enable extensions.worktreeConfig when the test is
+       # finished, otherwise the test_unconfig of index.sparse will not work.
+       test_unconfig -C submodule extensions.worktreeConfig &&
+       test_when_finished "git -C submodule config extensions.worktreeConfig true" &&
+
+       # With extensions.worktreeConfig disabled in the submodule, the invalid
+       # worktree config is not picked up.
+       git ls-files --recurse-submodules 2>err &&
+       ! grep "bad boolean config value" err
 '
 
 test_incompatible_with_recurse_submodules () {
        test_expect_success "--recurse-submodules and $1 are incompatible" "
                test_must_fail git ls-files --recurse-submodules $1 2>actual &&
-               test_i18ngrep 'unsupported mode' actual
+               test_grep 'unsupported mode' actual
        "
 }
 
index 963f3462b750b2a4fc7d009df6cf1fa1949090d6..14218b342437788fef40c539c45fb41575ef0071 100755 (executable)
@@ -18,6 +18,7 @@ This test runs git ls-files --others with the following working tree:
       git repository with a commit and an untracked file
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup: directories' '
index 580e158f9918bcaf86224ce8743894da67266096..054178703d732403ce32eda9871d8d85c47dddaa 100755 (executable)
@@ -41,6 +41,8 @@ Also for modification test, the cache and working tree have:
 We should report path0, path1, path2/file2, path3/file3, path7 and path8
 modified without reporting path9 and path10.  submod1 is also modified.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'git update-index --add to add various paths.' '
index efb7450bf1e9c14fc5563564b5b4e6913bd7c97c..6e6ea0b6f3ca256eb3903b08ca7025e3508ecb0b 100755 (executable)
@@ -38,6 +38,41 @@ test_expect_success 'git ls-files --format objectname v.s. -s' '
        test_cmp expect actual
 '
 
+test_expect_success 'git ls-files --format objecttype' '
+       git ls-files --format="%(objectname)" o1.txt o4.txt o6.txt >objectname &&
+       git cat-file --batch-check="%(objecttype)" >expect <objectname &&
+       git ls-files --format="%(objecttype)" o1.txt o4.txt o6.txt >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectsize' '
+       cat>expect <<-\EOF &&
+26
+29
+27
+26
+-
+26
+       EOF
+       git ls-files --format="%(objectsize)" >actual &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectsize:padded' '
+       cat>expect <<-\EOF &&
+     26
+     29
+     27
+     26
+      -
+     26
+       EOF
+       git ls-files --format="%(objectsize:padded)" >actual &&
+
+       test_cmp expect actual
+'
+
 test_expect_success 'git ls-files --format v.s. --eol' '
        git ls-files --eol >tmp &&
        sed -e "s/      / /g" -e "s/  */ /g" tmp >expect 2>err &&
@@ -54,6 +89,22 @@ test_expect_success 'git ls-files --format path v.s. -s' '
        test_cmp expect actual
 '
 
+test_expect_success 'git ls-files --format with relative path' '
+       cat >expect <<-\EOF &&
+       ../o1.txt
+       ../o2.txt
+       ../o3.txt
+       ../o4.txt
+       ../o5.txt
+       ../o6.txt
+       EOF
+       mkdir sub &&
+       cd sub &&
+       git ls-files --format="%(path)" ":/" >../actual &&
+       cd .. &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git ls-files --format with -m' '
        echo change >o1.txt &&
        cat >expect <<-\EOF &&
index f1f09abdd9b254eed1efa6b0d06369f3c8606cf8..3884694165525b9e4a3d8527785f40758817be53 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='fetching and pushing project with subproject'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 52f76f7b57f3ba9861bc9220602a5ca9fc45aeea..5a06732ca730f4a4becff478cc96f20b8931f6fc 100755 (executable)
@@ -8,6 +8,8 @@ test_description='git ls-files test (--with-tree).
 This test runs git ls-files --with-tree and in particular in
 a scenario known to trigger a crash with some versions of git.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -38,7 +40,7 @@ test_expect_success 'setup' '
        git commit -a -m "remove them all" &&
 
        # The bug also requires some entry before our directory so that
-       # prune_path will modify the_index.cache
+       # prune_index will modify the_repository->index.cache
 
        mkdir a_directory_that_sorts_before_sub &&
        >a_directory_that_sorts_before_sub/file &&
@@ -54,7 +56,7 @@ test_expect_success 'usage' '
 '
 
 test_expect_success 'git ls-files --with-tree should succeed from subdir' '
-       # We have to run from a sub-directory to trigger prune_path
+       # We have to run from a sub-directory to trigger prune_index
        # Then we finally get to run our --with-tree test
        (
                cd sub &&
index 5d871fde960a032bbf5523dc2fee578745d1479d..4dd42df38c25615e0833b754d1a2c3132ef0bf1d 100755 (executable)
@@ -431,4 +431,15 @@ match 1 1 1 1 'a' '[B-a]'
 match 0 1 0 1 'z' '[Z-y]'
 match 1 1 1 1 'Z' '[Z-y]'
 
+test_expect_success 'matching does not exhibit exponential behavior' '
+       {
+               test-tool wildmatch wildmatch \
+                       aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab \
+                       "*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a" &
+               pid=$!
+       } &&
+       sleep 2 &&
+       ! kill $!
+'
+
 test_done
index 217006d1bfb55df9f1e5874641226c9ad703b828..5af2dac0e4b8d5fde4d6a11dae240086473e0571 100755 (executable)
@@ -154,6 +154,14 @@ EOF
        test_output
 '
 
+test_expect_success 'ls-tree --no-full-name' '
+       git -C path0 ls-tree --no-full-name $tree a >current &&
+       cat >expected <<-EOF &&
+       040000 tree X   a
+       EOF
+       test_output
+'
+
 test_expect_success 'ls-tree --full-tree' '
        (
                cd path1/b/c &&
index 383896667b6dc9e446c157370dedd3a3aa8051d1..3adb206a93bc58aac509a1f093f9dd86cb98e5be 100755 (executable)
@@ -20,7 +20,6 @@ test_ls_tree_format () {
        format=$1 &&
        opts=$2 &&
        fmtopts=$3 &&
-       shift 2 &&
 
        test_expect_success "ls-tree '--format=<$format>' is like options '$opts $fmtopts'" '
                git ls-tree $opts -r HEAD >expect &&
@@ -36,6 +35,12 @@ test_ls_tree_format () {
        '
 }
 
+test_expect_success "ls-tree --format='%(path) %(path) %(path)' HEAD top-file" '
+       git ls-tree --format="%(path) %(path) %(path)" HEAD top-file.t >actual &&
+       echo top-file.t top-file.t top-file.t >expect &&
+       test_cmp expect actual
+'
+
 test_ls_tree_format \
        "%(objectmode) %(objecttype) %(objectname)%x09%(path)" \
        ""
index c7ec1c752087ef3f255425f3e735d6ab651f6e30..de7d3014e4fb914012db343d9efd85a2818677b4 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git branch assorted tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
@@ -24,10 +25,10 @@ test_expect_success 'prepare a trivial repository' '
 
 test_expect_success 'git branch --help should not have created a bogus branch' '
        test_might_fail git branch --man --help </dev/null >/dev/null 2>&1 &&
-       test_path_is_missing .git/refs/heads/--help
+       test_ref_missing refs/heads/--help
 '
 
-test_expect_success 'branch -h in broken repository' '
+test_expect_success REFFILES 'branch -h in broken repository' '
        mkdir broken &&
        (
                cd broken &&
@@ -35,11 +36,12 @@ test_expect_success 'branch -h in broken repository' '
                >.git/refs/heads/main &&
                test_expect_code 129 git branch -h >usage 2>&1
        ) &&
-       test_i18ngrep "[Uu]sage" broken/usage
+       test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'git branch abc should create a branch' '
-       git branch abc && test_path_is_file .git/refs/heads/abc
+       git branch abc &&
+       test_ref_exists refs/heads/abc
 '
 
 test_expect_success 'git branch abc should fail when abc exists' '
@@ -60,11 +62,13 @@ test_expect_success 'git branch --force abc should succeed when abc exists' '
 '
 
 test_expect_success 'git branch a/b/c should create a branch' '
-       git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c
+       git branch a/b/c &&
+       test_ref_exists refs/heads/a/b/c
 '
 
 test_expect_success 'git branch mb main... should create a branch' '
-       git branch mb main... && test_path_is_file .git/refs/heads/mb
+       git branch mb main... &&
+       test_ref_exists refs/heads/mb
 '
 
 test_expect_success 'git branch HEAD should fail' '
@@ -72,19 +76,19 @@ test_expect_success 'git branch HEAD should fail' '
 '
 
 cat >expect <<EOF
-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000    branch: Created from main
+$HEAD refs/heads/d/e/f@{0}: branch: Created from main
 EOF
 test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
        git -c core.logallrefupdates=false branch --create-reflog d/e/f &&
-       test_path_is_file .git/refs/heads/d/e/f &&
-       test_path_is_file .git/logs/refs/heads/d/e/f &&
-       test_cmp expect .git/logs/refs/heads/d/e/f
+       test_ref_exists refs/heads/d/e/f &&
+       git reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch -d d/e/f should delete a branch and a log' '
        git branch -d d/e/f &&
-       test_path_is_missing .git/refs/heads/d/e/f &&
+       test_ref_missing refs/heads/d/e/f &&
        test_must_fail git reflog exists refs/heads/d/e/f
 '
 
@@ -102,7 +106,7 @@ test_expect_success 'git branch l should work after branch l/m has been deleted'
 
 test_expect_success 'git branch -m dumps usage' '
        test_expect_code 128 git branch -m 2>err &&
-       test_i18ngrep "branch name required" err
+       test_grep "branch name required" err
 '
 
 test_expect_success 'git branch -m m broken_symref should work' '
@@ -199,10 +203,9 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
        test $(git rev-parse --abbrev-ref HEAD) = bam
 '
 
-test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '
-       msg="Branch: renamed refs/heads/baz to refs/heads/bam" &&
-       grep " 0\{40\}.*$msg$" .git/logs/HEAD &&
-       grep "^0\{40\}.*$msg$" .git/logs/HEAD
+test_expect_success 'git branch -M baz bam should add entries to HEAD reflog' '
+       git reflog show HEAD >actual &&
+       grep "HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam" actual
 '
 
 test_expect_success 'git branch -M should leave orphaned HEAD alone' '
@@ -211,17 +214,20 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '
                cd orphan &&
                test_commit initial &&
                git checkout --orphan lonely &&
-               grep lonely .git/HEAD &&
-               test_path_is_missing .git/refs/head/lonely &&
+               git symbolic-ref HEAD >expect &&
+               echo refs/heads/lonely >actual &&
+               test_cmp expect actual &&
+               test_ref_missing refs/head/lonely &&
                git branch -M main mistress &&
-               grep lonely .git/HEAD
+               git symbolic-ref HEAD >expect &&
+               test_cmp expect actual
        )
 '
 
 test_expect_success 'resulting reflog can be shown by log -g' '
        oid=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-       HEAD@{0} $oid $msg
+       HEAD@{0} $oid Branch: renamed refs/heads/baz to refs/heads/bam
        HEAD@{2} $oid checkout: moving from foo to baz
        EOF
        git log -g --format="%gd %H %gs" -2 HEAD >actual &&
@@ -239,15 +245,34 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
        git worktree prune
 '
 
+test_expect_success REFFILES 'git branch -M fails if updating any linked working tree fails' '
+       git worktree add -b baz bazdir1 &&
+       git worktree add -f bazdir2 baz &&
+       touch .git/worktrees/bazdir1/HEAD.lock &&
+       test_must_fail git branch -M baz bam &&
+       test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
+       git branch -M bam baz &&
+       rm .git/worktrees/bazdir1/HEAD.lock &&
+       touch .git/worktrees/bazdir2/HEAD.lock &&
+       test_must_fail git branch -M baz bam &&
+       test $(git -C bazdir1 rev-parse --abbrev-ref HEAD) = bam &&
+       rm -rf bazdir1 bazdir2 &&
+       git worktree prune
+'
+
 test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
        git checkout -b baz &&
        git worktree add -f bazdir baz &&
        (
                cd bazdir &&
                git branch -M baz bam &&
-               test $(git rev-parse --abbrev-ref HEAD) = bam
+               echo bam >expect &&
+               git rev-parse --abbrev-ref HEAD >actual &&
+               test_cmp expect actual
        ) &&
-       test $(git rev-parse --abbrev-ref HEAD) = bam &&
+       echo bam >expect &&
+       git rev-parse --abbrev-ref HEAD >actual &&
+       test_cmp expect actual &&
        rm -r bazdir &&
        git worktree prune
 '
@@ -268,6 +293,67 @@ test_expect_success 'git branch -M topic topic should work when main is checked
        git branch -M topic topic
 '
 
+test_expect_success 'git branch -M and -C fail on detached HEAD' '
+       git checkout HEAD^{} &&
+       test_when_finished git checkout - &&
+       echo "fatal: cannot rename the current branch while not on any" >expect &&
+       test_must_fail git branch -M must-fail 2>err &&
+       test_cmp expect err &&
+       echo "fatal: cannot copy the current branch while not on any" >expect &&
+       test_must_fail git branch -C must-fail 2>err &&
+       test_cmp expect err
+'
+
+test_expect_success 'git branch -m should work with orphan branches' '
+       test_when_finished git checkout - &&
+       test_when_finished git worktree remove -f wt &&
+       git worktree add wt --detach &&
+       # rename orphan in another worktreee
+       git -C wt checkout --orphan orphan-foo-wt &&
+       git branch -m orphan-foo-wt orphan-bar-wt &&
+       test orphan-bar-wt=$(git -C orphan-worktree branch --show-current) &&
+       # rename orphan in the current worktree
+       git checkout --orphan orphan-foo &&
+       git branch -m orphan-foo orphan-bar &&
+       test orphan-bar=$(git branch --show-current)
+'
+
+test_expect_success 'git branch -d on orphan HEAD (merged)' '
+       test_when_finished git checkout main &&
+       git checkout --orphan orphan &&
+       test_when_finished "rm -rf .git/objects/commit-graph*" &&
+       git commit-graph write --reachable &&
+       git branch --track to-delete main &&
+       git branch -d to-delete
+'
+
+test_expect_success 'git branch -d on orphan HEAD (merged, graph)' '
+       test_when_finished git checkout main &&
+       git checkout --orphan orphan &&
+       git branch --track to-delete main &&
+       git branch -d to-delete
+'
+
+test_expect_success 'git branch -d on orphan HEAD (unmerged)' '
+       test_when_finished git checkout main &&
+       git checkout --orphan orphan &&
+       test_when_finished "git branch -D to-delete" &&
+       git branch to-delete main &&
+       test_must_fail git branch -d to-delete 2>err &&
+       grep "not fully merged" err
+'
+
+test_expect_success 'git branch -d on orphan HEAD (unmerged, graph)' '
+       test_when_finished git checkout main &&
+       git checkout --orphan orphan &&
+       test_when_finished "git branch -D to-delete" &&
+       git branch to-delete main &&
+       test_when_finished "rm -rf .git/objects/commit-graph*" &&
+       git commit-graph write --reachable &&
+       test_must_fail git branch -d to-delete 2>err &&
+       grep "not fully merged" err
+'
+
 test_expect_success 'git branch -v -d t should work' '
        git branch t &&
        git rev-parse --verify refs/heads/t &&
@@ -433,7 +519,7 @@ EOF
 
 mv .git/config .git/config-saved
 
-test_expect_success SHA1 'git branch -m q q2 without config should succeed' '
+test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' '
        git branch -m q q2 &&
        git branch -m q2 q
 '
@@ -500,12 +586,12 @@ EOF
 
 test_expect_success 'git branch -c dumps usage' '
        test_expect_code 128 git branch -c 2>err &&
-       test_i18ngrep "branch name required" err
+       test_grep "branch name required" err
 '
 
 test_expect_success 'git branch --copy dumps usage' '
        test_expect_code 128 git branch --copy 2>err &&
-       test_i18ngrep "branch name required" err
+       test_grep "branch name required" err
 '
 
 test_expect_success 'git branch -c d e should work' '
@@ -615,7 +701,8 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'
 
 test_expect_success 'git branch -C c1 c2 should never touch HEAD' '
        msg="Branch: copied refs/heads/c1 to refs/heads/c2" &&
-       ! grep "$msg$" .git/logs/HEAD
+       git reflog HEAD >actual &&
+       ! grep "$msg$" actual
 '
 
 test_expect_success 'git branch -C main should work when main is checked out' '
@@ -718,26 +805,26 @@ test_expect_success 'deleting a symref' '
        git symbolic-ref refs/heads/symref refs/heads/target &&
        echo "Deleted branch symref (was refs/heads/target)." >expect &&
        git branch -d symref >actual &&
-       test_path_is_file .git/refs/heads/target &&
-       test_path_is_missing .git/refs/heads/symref &&
+       test_ref_exists refs/heads/target &&
+       test_ref_missing refs/heads/symref &&
        test_cmp expect actual
 '
 
 test_expect_success 'deleting a dangling symref' '
        git symbolic-ref refs/heads/dangling-symref nowhere &&
-       test_path_is_file .git/refs/heads/dangling-symref &&
+       git symbolic-ref --no-recurse refs/heads/dangling-symref &&
        echo "Deleted branch dangling-symref (was nowhere)." >expect &&
        git branch -d dangling-symref >actual &&
-       test_path_is_missing .git/refs/heads/dangling-symref &&
+       test_ref_missing refs/heads/dangling-symref &&
        test_cmp expect actual
 '
 
 test_expect_success 'deleting a self-referential symref' '
        git symbolic-ref refs/heads/self-reference refs/heads/self-reference &&
-       test_path_is_file .git/refs/heads/self-reference &&
+       test_ref_exists refs/heads/self-reference &&
        echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
        git branch -d self-reference >actual &&
-       test_path_is_missing .git/refs/heads/self-reference &&
+       test_ref_missing refs/heads/self-reference &&
        test_cmp expect actual
 '
 
@@ -745,18 +832,18 @@ test_expect_success 'renaming a symref is not allowed' '
        git symbolic-ref refs/heads/topic refs/heads/main &&
        test_must_fail git branch -m topic new-topic &&
        git symbolic-ref refs/heads/topic &&
-       test_path_is_file .git/refs/heads/main &&
-       test_path_is_missing .git/refs/heads/new-topic
+       test_ref_exists refs/heads/main &&
+       test_ref_missing refs/heads/new-topic
 '
 
-test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
+test_expect_success SYMLINKS,REFFILES 'git branch -m u v should fail when the reflog for u is a symlink' '
        git branch --create-reflog u &&
        mv .git/logs/refs/heads/u real-u &&
        ln -s real-u .git/logs/refs/heads/u &&
        test_must_fail git branch -m u v
 '
 
-test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '
+test_expect_success SYMLINKS,REFFILES 'git branch -m with symlinked .git/refs' '
        test_when_finished "rm -rf subdir" &&
        git init --bare subdir &&
 
@@ -861,7 +948,19 @@ test_expect_success 'test deleting branch without config' '
 test_expect_success 'deleting currently checked out branch fails' '
        git worktree add -b my7 my7 &&
        test_must_fail git -C my7 branch -d my7 &&
-       test_must_fail git branch -d my7 &&
+       test_must_fail git branch -d my7 2>actual &&
+       grep "^error: cannot delete branch .my7. used by worktree at " actual &&
+       rm -r my7 &&
+       git worktree prune
+'
+
+test_expect_success 'deleting in-use branch fails' '
+       git worktree add my7 &&
+       test_commit -C my7 bt7 &&
+       git -C my7 bisect start HEAD HEAD~2 &&
+       test_must_fail git -C my7 branch -d my7 &&
+       test_must_fail git branch -d my7 2>actual &&
+       grep "^error: cannot delete branch .my7. used by worktree at " actual &&
        rm -r my7 &&
        git worktree prune
 '
@@ -931,7 +1030,7 @@ test_expect_success '--set-upstream-to fails on multiple branches' '
 test_expect_success '--set-upstream-to fails on detached HEAD' '
        git checkout HEAD^{} &&
        test_when_finished git checkout - &&
-       echo "fatal: could not set upstream of HEAD to main when it does not point to any branch." >expect &&
+       echo "fatal: could not set upstream of HEAD to main when it does not point to any branch" >expect &&
        test_must_fail git branch --set-upstream-to main 2>err &&
        test_cmp expect err
 '
@@ -944,7 +1043,7 @@ test_expect_success '--set-upstream-to fails on a missing dst branch' '
 
 test_expect_success '--set-upstream-to fails on a missing src branch' '
        test_must_fail git branch --set-upstream-to does-not-exist main 2>err &&
-       test_i18ngrep "the requested upstream branch '"'"'does-not-exist'"'"' does not exist" err
+       test_grep "the requested upstream branch '"'"'does-not-exist'"'"' does not exist" err
 '
 
 test_expect_success '--set-upstream-to fails on a non-ref' '
@@ -958,7 +1057,7 @@ test_expect_success '--set-upstream-to fails on locked config' '
        >.git/config.lock &&
        git branch locked &&
        test_must_fail git branch --set-upstream-to locked 2>err &&
-       test_i18ngrep "could not lock config file .git/config" err
+       test_grep "could not lock config file .git/config" err
 '
 
 test_expect_success 'use --set-upstream-to modify HEAD' '
@@ -979,7 +1078,7 @@ test_expect_success 'use --set-upstream-to modify a particular branch' '
 '
 
 test_expect_success '--unset-upstream should fail if given a non-existent branch' '
-       echo "fatal: Branch '"'"'i-dont-exist'"'"' has no upstream information" >expect &&
+       echo "fatal: branch '"'"'i-dont-exist'"'"' has no upstream information" >expect &&
        test_must_fail git branch --unset-upstream i-dont-exist 2>err &&
        test_cmp expect err
 '
@@ -989,7 +1088,7 @@ test_expect_success '--unset-upstream should fail if config is locked' '
        git branch --set-upstream-to locked &&
        >.git/config.lock &&
        test_must_fail git branch --unset-upstream 2>err &&
-       test_i18ngrep "could not lock config file .git/config" err
+       test_grep "could not lock config file .git/config" err
 '
 
 test_expect_success 'test --unset-upstream on HEAD' '
@@ -1001,7 +1100,7 @@ test_expect_success 'test --unset-upstream on HEAD' '
        test_must_fail git config branch.main.remote &&
        test_must_fail git config branch.main.merge &&
        # fail for a branch without upstream set
-       echo "fatal: Branch '"'"'main'"'"' has no upstream information" >expect &&
+       echo "fatal: branch '"'"'main'"'"' has no upstream information" >expect &&
        test_must_fail git branch --unset-upstream 2>err &&
        test_cmp expect err
 '
@@ -1015,7 +1114,7 @@ test_expect_success '--unset-upstream should fail on multiple branches' '
 test_expect_success '--unset-upstream should fail on detached HEAD' '
        git checkout HEAD^{} &&
        test_when_finished git checkout - &&
-       echo "fatal: could not unset upstream of HEAD when it does not point to any branch." >expect &&
+       echo "fatal: could not unset upstream of HEAD when it does not point to any branch" >expect &&
        test_must_fail git branch --unset-upstream 2>err &&
        test_cmp expect err
 '
@@ -1044,14 +1143,14 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups
 
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000    branch: Created from main
+$HEAD refs/heads/g/h/i@{0}: branch: Created from main
 EOF
 test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
        git checkout -b g/h/i -l main &&
-       test_path_is_file .git/refs/heads/g/h/i &&
-       test_path_is_file .git/logs/refs/heads/g/h/i &&
-       test_cmp expect .git/logs/refs/heads/g/h/i
+       test_ref_exists refs/heads/g/h/i &&
+       git reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'checkout -b makes reflog by default' '
@@ -1383,7 +1482,7 @@ test_expect_success 'branch --delete --force removes dangling branch' '
 
 test_expect_success 'use --edit-description' '
        EDITOR=: git branch --edit-description &&
-       test_must_fail git config branch.main.description &&
+       test_expect_code 1 git config branch.main.description &&
 
        write_script editor <<-\EOF &&
                echo "New contents" >"$1"
@@ -1425,7 +1524,7 @@ test_expect_success '--list during rebase' '
        set_fake_editor &&
        git rebase -i HEAD~2 &&
        git branch --list >actual &&
-       test_i18ngrep "rebasing main" actual
+       test_grep "rebasing main" actual
 '
 
 test_expect_success '--list during rebase from detached HEAD' '
@@ -1437,7 +1536,7 @@ test_expect_success '--list during rebase from detached HEAD' '
        set_fake_editor &&
        git rebase -i HEAD~2 &&
        git branch --list >actual &&
-       test_i18ngrep "rebasing detached HEAD $oid" actual
+       test_grep "rebasing detached HEAD $oid" actual
 '
 
 test_expect_success 'tracking with unexpected .fetch refspec' '
@@ -1477,9 +1576,10 @@ test_expect_success 'tracking with unexpected .fetch refspec' '
 
 test_expect_success 'configured committerdate sort' '
        git init -b main sort &&
+       test_config -C sort branch.sort "committerdate" &&
+
        (
                cd sort &&
-               git config branch.sort committerdate &&
                test_commit initial &&
                git checkout -b a &&
                test_commit a &&
@@ -1499,9 +1599,10 @@ test_expect_success 'configured committerdate sort' '
 '
 
 test_expect_success 'option override configured sort' '
+       test_config -C sort branch.sort "committerdate" &&
+
        (
                cd sort &&
-               git config branch.sort committerdate &&
                git branch --sort=refname >actual &&
                cat >expect <<-\EOF &&
                  a
@@ -1513,10 +1614,70 @@ test_expect_success 'option override configured sort' '
        )
 '
 
+test_expect_success '--no-sort cancels config sort keys' '
+       test_config -C sort branch.sort "-refname" &&
+
+       (
+               cd sort &&
+
+               # objecttype is identical for all of them, so sort falls back on
+               # default (ascending refname)
+               git branch \
+                       --no-sort \
+                       --sort="objecttype" >actual &&
+               cat >expect <<-\EOF &&
+                 a
+               * b
+                 c
+                 main
+               EOF
+               test_cmp expect actual
+       )
+
+'
+
+test_expect_success '--no-sort cancels command line sort keys' '
+       (
+               cd sort &&
+
+               # objecttype is identical for all of them, so sort falls back on
+               # default (ascending refname)
+               git branch \
+                       --sort="-refname" \
+                       --no-sort \
+                       --sort="objecttype" >actual &&
+               cat >expect <<-\EOF &&
+                 a
+               * b
+                 c
+                 main
+               EOF
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected branches' '
+       (
+               cd sort &&
+
+               # Sort the results with `sort` for a consistent comparison
+               # against expected
+               git branch --no-sort | sort >actual &&
+               cat >expect <<-\EOF &&
+                 a
+                 c
+                 main
+               * b
+               EOF
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'invalid sort parameter in configuration' '
+       test_config -C sort branch.sort "v:notvalid" &&
+
        (
                cd sort &&
-               git config branch.sort "v:notvalid" &&
 
                # this works in the "listing" mode, so bad sort key
                # is a dying offence.
index ea7cfd1951d8b2173c8fe13f1bc307ff9e7cfa1c..6a98b2df7611ed741be197640f20e1147794aa8d 100755 (executable)
@@ -10,7 +10,7 @@ GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
 test_expect_success 'error descriptions on empty repository' '
        current=$(git branch --show-current) &&
        cat >expect <<-EOF &&
-       error: No commit on branch '\''$current'\'' yet.
+       error: no commit on branch '\''$current'\'' yet
        EOF
        test_must_fail git branch --edit-description 2>actual &&
        test_cmp expect actual &&
@@ -21,7 +21,7 @@ test_expect_success 'error descriptions on empty repository' '
 test_expect_success 'fatal descriptions on empty repository' '
        current=$(git branch --show-current) &&
        cat >expect <<-EOF &&
-       fatal: No commit on branch '\''$current'\'' yet.
+       fatal: no commit on branch '\''$current'\'' yet
        EOF
        test_must_fail git branch --set-upstream-to=non-existent 2>actual &&
        test_cmp expect actual &&
@@ -119,6 +119,22 @@ test_expect_success 'show branch --remotes' '
        test_must_be_empty actual.out
 '
 
+test_expect_success 'show-branch --sparse' '
+       test_when_finished "git checkout branch10 && git branch -D branchA" &&
+       git checkout -b branchA branch10 &&
+       git merge -s ours -m "merge 1 and 10 to make A" branch1 &&
+       git commit --allow-empty -m "another" &&
+
+       git show-branch --sparse >out &&
+       grep "merge 1 and 10 to make A" out &&
+
+       git show-branch >out &&
+       ! grep "merge 1 and 10 to make A" out &&
+
+       git show-branch --no-sparse >out &&
+       ! grep "merge 1 and 10 to make A" out
+'
+
 test_expect_success 'setup show branch --list' '
        sed "s/^> //" >expect <<-\EOF
        >   [branch1] branch1
@@ -197,9 +213,18 @@ done <<\EOF
 --reflog --current
 EOF
 
+# unnegatable options
+for opt in topo-order date-order reflog
+do
+       test_expect_success "show-branch --no-$opt (should fail)" '
+               test_must_fail git show-branch --no-$opt 2>err &&
+               grep "unknown option .no-$opt." err
+       '
+done
+
 test_expect_success 'error descriptions on non-existent branch' '
        cat >expect <<-EOF &&
-       error: No branch named '\''non-existent'\'.'
+       error: no branch named '\''non-existent'\''
        EOF
        test_must_fail git branch --edit-description non-existent 2>actual &&
        test_cmp expect actual
@@ -213,7 +238,7 @@ test_expect_success 'fatal descriptions on non-existent branch' '
        test_cmp expect actual &&
 
        cat >expect <<-EOF &&
-       fatal: No branch named '\''non-existent'\''.
+       fatal: no branch named '\''non-existent'\''
        EOF
        test_must_fail git branch -c non-existent new-branch 2>actual &&
        test_cmp expect actual &&
@@ -221,4 +246,22 @@ test_expect_success 'fatal descriptions on non-existent branch' '
        test_cmp expect actual
 '
 
+test_expect_success 'error descriptions on orphan branch' '
+       test_when_finished git worktree remove -f wt &&
+       git worktree add wt --detach &&
+       git -C wt checkout --orphan orphan-branch &&
+       test_branch_op_in_wt() {
+               test_orphan_error() {
+                       test_must_fail git $* 2>actual &&
+                       test_grep "no commit on branch .orphan-branch. yet$" actual
+               } &&
+               test_orphan_error -C wt branch $1 $2 &&                # implicit branch
+               test_orphan_error -C wt branch $1 orphan-branch $2 &&  # explicit branch
+               test_orphan_error branch $1 orphan-branch $2           # different worktree
+       } &&
+       test_branch_op_in_wt --edit-description &&
+       test_branch_op_in_wt --set-upstream-to=ne &&
+       test_branch_op_in_wt -c new-branch
+'
+
 test_done
index d34d77f89348d86518375a66e277bb118ff29c22..758963b189d8d9fc7ad50f4fa06bf431c4fe6e80 100755 (executable)
@@ -55,9 +55,17 @@ cat >expect <<'EOF'
 EOF
 test_expect_success 'git branch -r shows remote branches' '
        git branch -r >actual &&
+       test_cmp expect actual &&
+
+       git branch --remotes >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'git branch --no-remotes is rejected' '
+       test_must_fail git branch --no-remotes 2>err &&
+       grep "unknown option .no-remotes." err
+'
+
 cat >expect <<'EOF'
   branch-one
   branch-two
@@ -68,9 +76,17 @@ cat >expect <<'EOF'
 EOF
 test_expect_success 'git branch -a shows local and remote branches' '
        git branch -a >actual &&
+       test_cmp expect actual &&
+
+       git branch --all >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'git branch --no-all is rejected' '
+       test_must_fail git branch --no-all 2>err &&
+       grep "unknown option .no-all." err
+'
+
 cat >expect <<'EOF'
 two
 one
@@ -337,10 +353,48 @@ test_expect_success 'git branch --format option' '
        test_cmp expect actual
 '
 
+test_expect_success 'git branch --format with ahead-behind' '
+       cat >expect <<-\EOF &&
+       (HEAD detached from fromtag) 0 0
+       refs/heads/ambiguous 0 0
+       refs/heads/branch-one 1 0
+       refs/heads/branch-two 0 0
+       refs/heads/main 1 0
+       refs/heads/ref-to-branch 1 0
+       refs/heads/ref-to-remote 1 0
+       EOF
+       git branch --format="%(refname) %(ahead-behind:HEAD)" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git branch with --format=%(rest) must fail' '
        test_must_fail git branch --format="%(rest)" >actual
 '
 
+test_expect_success 'git branch --format --omit-empty' '
+       cat >expect <<-\EOF &&
+       Refname is (HEAD detached from fromtag)
+       Refname is refs/heads/ambiguous
+       Refname is refs/heads/branch-one
+       Refname is refs/heads/branch-two
+
+       Refname is refs/heads/ref-to-branch
+       Refname is refs/heads/ref-to-remote
+       EOF
+       git branch --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+       test_cmp expect actual &&
+       cat >expect <<-\EOF &&
+       Refname is (HEAD detached from fromtag)
+       Refname is refs/heads/ambiguous
+       Refname is refs/heads/branch-one
+       Refname is refs/heads/branch-two
+       Refname is refs/heads/ref-to-branch
+       Refname is refs/heads/ref-to-remote
+       EOF
+       git branch --omit-empty --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'worktree colors correct' '
        cat >expect <<-EOF &&
        * <GREEN>(HEAD detached from fromtag)<RESET>
index 993a6b5eff7cdc91a8199fe023e00cc700432511..594e3e43e129a8848b5f2e65538087ca978743d6 100755 (executable)
@@ -9,6 +9,7 @@ This script aims to check the behavior of those corner cases.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 expect_branch() {
@@ -57,6 +58,16 @@ test_expect_success 'create branch with pseudo-qualified name' '
        expect_branch refs/heads/refs/heads/qualified two
 '
 
+test_expect_success 'force-copy a branch to itself via @{-1} is no-op' '
+       git branch -t copiable main &&
+       git checkout copiable &&
+       git checkout - &&
+       git branch -C @{-1} copiable &&
+       git config --get-all branch.copiable.merge >actual &&
+       echo refs/heads/main >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'delete branch via @{-1}' '
        git branch previous-del &&
 
@@ -133,4 +144,28 @@ test_expect_success 'checkout does not treat remote @{upstream} as a branch' '
        expect_branch HEAD one
 '
 
+test_expect_success 'edit-description via @{-1}' '
+       git checkout -b desc-branch &&
+       git checkout -b non-desc-branch &&
+       write_script editor <<-\EOF &&
+               echo "Branch description" >"$1"
+       EOF
+       EDITOR=./editor git branch --edit-description @{-1} &&
+       test_must_fail git config branch.non-desc-branch.description &&
+       git config branch.desc-branch.description >actual &&
+       printf "Branch description\n\n" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'modify branch upstream via "@{-1}" and "@{-1}@{upstream}"' '
+       git checkout -b upstream-branch &&
+       git checkout -b upstream-other -t upstream-branch &&
+       git branch --set-upstream-to upstream-other @{-1} &&
+       git config branch.upstream-branch.merge >actual &&
+       echo "refs/heads/upstream-other" >expect &&
+       test_cmp expect actual &&
+       git branch --unset-upstream @{-1}@{upstream} &&
+       test_must_fail git config branch.upstream-other.merge
+'
+
 test_done
index 84dd0cd26d02cb3a3151f1529544ef1f70e052ae..7b05bf3961923035bba7fd26b2c389a9f4d151a0 100755 (executable)
@@ -33,6 +33,26 @@ test_expect_success 'setup' '
        u3 sha256:736c4bc
        u4 sha256:673e77d
 
+       # topic (abbrev=10)
+       t1_abbrev sha1:4de457d2c0
+       t2_abbrev sha1:fccce22f8c
+       t3_abbrev sha1:147e64ef53
+       t4_abbrev sha1:a63e992599
+       t1_abbrev sha256:b89f8b9092
+       t2_abbrev sha256:5f12aadf34
+       t3_abbrev sha256:ea8b273a6c
+       t4_abbrev sha256:14b73361fc
+
+       # unmodified (abbrev=10)
+       u1_abbrev sha1:35b9b25f76
+       u2_abbrev sha1:de345ab3de
+       u3_abbrev sha1:9af6654000
+       u4_abbrev sha1:2901f773f3
+       u1_abbrev sha256:e3731be242
+       u2_abbrev sha256:14fadf8cee
+       u3_abbrev sha256:736c4bcb44
+       u4_abbrev sha256:673e77d589
+
        # reordered
        r1 sha1:aca177a
        r2 sha1:14ad629
@@ -153,6 +173,18 @@ test_expect_success 'simple A B C (unmodified)' '
        test_cmp expect actual
 '
 
+test_expect_success 'simple A..B A..C (unmodified) with --abbrev' '
+       git range-diff --no-color --abbrev=10 main..topic main..unmodified \
+               >actual &&
+       cat >expect <<-EOF &&
+       1:  $(test_oid t1_abbrev) = 1:  $(test_oid u1_abbrev) s/5/A/
+       2:  $(test_oid t2_abbrev) = 2:  $(test_oid u2_abbrev) s/4/A/
+       3:  $(test_oid t3_abbrev) = 3:  $(test_oid u3_abbrev) s/11/B/
+       4:  $(test_oid t4_abbrev) = 4:  $(test_oid u4_abbrev) s/12/B/
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success 'A^! and A^-<n> (unmodified)' '
        git range-diff --no-color topic^! unmodified^-1 >actual &&
        cat >expect <<-EOF &&
@@ -163,7 +195,7 @@ test_expect_success 'A^! and A^-<n> (unmodified)' '
 
 test_expect_success 'A^{/..} is not mistaken for a range' '
        test_must_fail git range-diff topic^.. topic^{/..} -- 2>error &&
-       test_i18ngrep "not a commit range" error
+       test_grep "not a commit range" error
 '
 
 test_expect_success 'trivial reordering' '
@@ -505,7 +537,7 @@ do
                        main..unmodified >actual &&
                test_when_finished "rm 000?-*" &&
                test_line_count = 5 actual &&
-               test_i18ngrep "^Range-diff:$" 0000-* &&
+               test_grep "^Range-diff:$" 0000-* &&
                grep "= 1: .* s/5/A" 0000-* &&
                grep "= 2: .* s/4/A" 0000-* &&
                grep "= 3: .* s/11/B" 0000-* &&
@@ -517,7 +549,7 @@ test_expect_success 'format-patch --range-diff as commentary' '
        git format-patch --range-diff=HEAD~1 HEAD~1 >actual &&
        test_when_finished "rm 0001-*" &&
        test_line_count = 1 actual &&
-       test_i18ngrep "^Range-diff:$" 0001-* &&
+       test_grep "^Range-diff:$" 0001-* &&
        grep "> 1: .* new message" 0001-*
 '
 
@@ -525,7 +557,7 @@ test_expect_success 'format-patch --range-diff reroll-count with a non-integer'
        git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
        test_when_finished "rm v2.9-0001-*" &&
        test_line_count = 1 actual &&
-       test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+       test_grep "^Range-diff:$" v2.9-0001-* &&
        grep "> 1: .* new message" v2.9-0001-*
 '
 
@@ -533,7 +565,7 @@ test_expect_success 'format-patch --range-diff reroll-count with a integer' '
        git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
        test_when_finished "rm v2-0001-*" &&
        test_line_count = 1 actual &&
-       test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+       test_grep "^Range-diff ..* v1:$" v2-0001-* &&
        grep "> 1: .* new message" v2-0001-*
 '
 
@@ -541,7 +573,7 @@ test_expect_success 'format-patch --range-diff with v0' '
        git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual &&
        test_when_finished "rm v0-0001-*" &&
        test_line_count = 1 actual &&
-       test_i18ngrep "^Range-diff:$" v0-0001-* &&
+       test_grep "^Range-diff:$" v0-0001-* &&
        grep "> 1: .* new message" v0-0001-*
 '
 
@@ -630,6 +662,20 @@ test_expect_success 'range-diff with multiple --notes' '
        test_cmp expect actual
 '
 
+# `range-diff` should act like `log` with regards to notes
+test_expect_success 'range-diff with --notes=custom does not show default notes' '
+       git notes add -m "topic note" topic &&
+       git notes add -m "unmodified note" unmodified &&
+       git notes --ref=custom add -m "topic note" topic &&
+       git notes --ref=custom add -m "unmodified note" unmodified &&
+       test_when_finished git notes remove topic unmodified &&
+       test_when_finished git notes --ref=custom remove topic unmodified &&
+       git range-diff --notes=custom main..topic main..unmodified \
+               >actual &&
+       ! grep "## Notes ##" actual &&
+       grep "## Notes (custom) ##" actual
+'
+
 test_expect_success 'format-patch --range-diff does not compare notes by default' '
        git notes add -m "topic note" topic &&
        git notes add -m "unmodified note" unmodified &&
@@ -638,7 +684,7 @@ test_expect_success 'format-patch --range-diff does not compare notes by default
                main..unmodified >actual &&
        test_when_finished "rm 000?-*" &&
        test_line_count = 5 actual &&
-       test_i18ngrep "^Range-diff:$" 0000-* &&
+       test_grep "^Range-diff:$" 0000-* &&
        grep "= 1: .* s/5/A" 0000-* &&
        grep "= 2: .* s/4/A" 0000-* &&
        grep "= 3: .* s/11/B" 0000-* &&
@@ -647,6 +693,20 @@ test_expect_success 'format-patch --range-diff does not compare notes by default
        ! grep "note" 0000-*
 '
 
+test_expect_success 'format-patch --notes=custom --range-diff only compares custom notes' '
+       git notes add -m "topic note" topic &&
+       git notes --ref=custom add -m "topic note (custom)" topic &&
+       git notes add -m "unmodified note" unmodified &&
+       git notes --ref=custom add -m "unmodified note (custom)" unmodified &&
+       test_when_finished git notes remove topic unmodified &&
+       test_when_finished git notes --ref=custom remove topic unmodified &&
+       git format-patch --notes=custom --cover-letter --range-diff=$prev \
+               main..unmodified >actual &&
+       test_when_finished "rm 000?-*" &&
+       grep "## Notes (custom) ##" 0000-* &&
+       ! grep "## Notes ##" 0000-*
+'
+
 test_expect_success 'format-patch --range-diff with --no-notes' '
        git notes add -m "topic note" topic &&
        git notes add -m "unmodified note" unmodified &&
@@ -655,7 +715,7 @@ test_expect_success 'format-patch --range-diff with --no-notes' '
                main..unmodified >actual &&
        test_when_finished "rm 000?-*" &&
        test_line_count = 5 actual &&
-       test_i18ngrep "^Range-diff:$" 0000-* &&
+       test_grep "^Range-diff:$" 0000-* &&
        grep "= 1: .* s/5/A" 0000-* &&
        grep "= 2: .* s/4/A" 0000-* &&
        grep "= 3: .* s/11/B" 0000-* &&
@@ -672,7 +732,7 @@ test_expect_success 'format-patch --range-diff with --notes' '
                main..unmodified >actual &&
        test_when_finished "rm 000?-*" &&
        test_line_count = 5 actual &&
-       test_i18ngrep "^Range-diff:$" 0000-* &&
+       test_grep "^Range-diff:$" 0000-* &&
        grep "= 1: .* s/5/A" 0000-* &&
        grep "= 2: .* s/4/A" 0000-* &&
        grep "= 3: .* s/11/B" 0000-* &&
@@ -701,7 +761,7 @@ test_expect_success 'format-patch --range-diff with format.notes config' '
                main..unmodified >actual &&
        test_when_finished "rm 000?-*" &&
        test_line_count = 5 actual &&
-       test_i18ngrep "^Range-diff:$" 0000-* &&
+       test_grep "^Range-diff:$" 0000-* &&
        grep "= 1: .* s/5/A" 0000-* &&
        grep "= 2: .* s/4/A" 0000-* &&
        grep "= 3: .* s/11/B" 0000-* &&
@@ -732,7 +792,7 @@ test_expect_success 'format-patch --range-diff with multiple notes' '
                main..unmodified >actual &&
        test_when_finished "rm 000?-*" &&
        test_line_count = 5 actual &&
-       test_i18ngrep "^Range-diff:$" 0000-* &&
+       test_grep "^Range-diff:$" 0000-* &&
        grep "= 1: .* s/5/A" 0000-* &&
        grep "= 2: .* s/4/A" 0000-* &&
        grep "= 3: .* s/11/B" 0000-* &&
index 577f32dc71ff2ccf0f34c01b060fd82b87c566f9..7f4e98db7db24363a451fa4b1ae8412b6e1277c3 100755 (executable)
@@ -12,107 +12,145 @@ semantic is still the same.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'enable reflogs' '
        git config core.logallrefupdates true
 '
 
-test_expect_success \
-    'prepare a trivial repository' \
-    'echo Hello > A &&
-     git update-index --add A &&
-     git commit -m "Initial commit." &&
-     HEAD=$(git rev-parse --verify HEAD)'
+test_expect_success 'prepare a trivial repository' '
+       echo Hello > A &&
+       git update-index --add A &&
+       git commit -m "Initial commit." &&
+       HEAD=$(git rev-parse --verify HEAD)
+'
 
 SHA1=
 
-test_expect_success \
-    'see if git show-ref works as expected' \
-    'git branch a &&
-     SHA1=$(cat .git/refs/heads/a) &&
-     echo "$SHA1 refs/heads/a" >expect &&
-     git show-ref a >result &&
-     test_cmp expect result'
-
-test_expect_success \
-    'see if a branch still exists when packed' \
-    'git branch b &&
-     git pack-refs --all &&
-     rm -f .git/refs/heads/b &&
-     echo "$SHA1 refs/heads/b" >expect &&
-     git show-ref b >result &&
-     test_cmp expect result'
+test_expect_success 'see if git show-ref works as expected' '
+       git branch a &&
+       SHA1=$(cat .git/refs/heads/a) &&
+       echo "$SHA1 refs/heads/a" >expect &&
+       git show-ref a >result &&
+       test_cmp expect result
+'
+
+test_expect_success 'see if a branch still exists when packed' '
+       git branch b &&
+       git pack-refs --all &&
+       rm -f .git/refs/heads/b &&
+       echo "$SHA1 refs/heads/b" >expect &&
+       git show-ref b >result &&
+       test_cmp expect result
+'
 
 test_expect_success 'git branch c/d should barf if branch c exists' '
-     git branch c &&
-     git pack-refs --all &&
-     rm -f .git/refs/heads/c &&
-     test_must_fail git branch c/d
+       git branch c &&
+       git pack-refs --all &&
+       rm -f .git/refs/heads/c &&
+       test_must_fail git branch c/d
 '
 
-test_expect_success \
-    'see if a branch still exists after git pack-refs --prune' \
-    'git branch e &&
-     git pack-refs --all --prune &&
-     echo "$SHA1 refs/heads/e" >expect &&
-     git show-ref e >result &&
-     test_cmp expect result'
+test_expect_success 'see if a branch still exists after git pack-refs --prune' '
+       git branch e &&
+       git pack-refs --all --prune &&
+       echo "$SHA1 refs/heads/e" >expect &&
+       git show-ref e >result &&
+       test_cmp expect result
+'
 
 test_expect_success 'see if git pack-refs --prune remove ref files' '
-     git branch f &&
-     git pack-refs --all --prune &&
-     ! test -f .git/refs/heads/f
+       git branch f &&
+       git pack-refs --all --prune &&
+       ! test -f .git/refs/heads/f
 '
 
 test_expect_success 'see if git pack-refs --prune removes empty dirs' '
-     git branch r/s/t &&
-     git pack-refs --all --prune &&
-     ! test -e .git/refs/heads/r
+       git branch r/s/t &&
+       git pack-refs --all --prune &&
+       ! test -e .git/refs/heads/r
 '
 
-test_expect_success \
-    'git branch g should work when git branch g/h has been deleted' \
-    'git branch g/h &&
-     git pack-refs --all --prune &&
-     git branch -d g/h &&
-     git branch g &&
-     git pack-refs --all &&
-     git branch -d g'
+test_expect_success 'git branch g should work when git branch g/h has been deleted' '
+       git branch g/h &&
+       git pack-refs --all --prune &&
+       git branch -d g/h &&
+       git branch g &&
+       git pack-refs --all &&
+       git branch -d g
+'
 
 test_expect_success 'git branch i/j/k should barf if branch i exists' '
-     git branch i &&
-     git pack-refs --all --prune &&
-     test_must_fail git branch i/j/k
+       git branch i &&
+       git pack-refs --all --prune &&
+       test_must_fail git branch i/j/k
+'
+
+test_expect_success 'test git branch k after branch k/l/m and k/lm have been deleted' '
+       git branch k/l &&
+       git branch k/lm &&
+       git branch -d k/l &&
+       git branch k/l/m &&
+       git branch -d k/l/m &&
+       git branch -d k/lm &&
+       git branch k
 '
 
-test_expect_success \
-    'test git branch k after branch k/l/m and k/lm have been deleted' \
-    'git branch k/l &&
-     git branch k/lm &&
-     git branch -d k/l &&
-     git branch k/l/m &&
-     git branch -d k/l/m &&
-     git branch -d k/lm &&
-     git branch k'
-
-test_expect_success \
-    'test git branch n after some branch deletion and pruning' \
-    'git branch n/o &&
-     git branch n/op &&
-     git branch -d n/o &&
-     git branch n/o/p &&
-     git branch -d n/op &&
-     git pack-refs --all --prune &&
-     git branch -d n/o/p &&
-     git branch n'
-
-test_expect_success \
-       'see if up-to-date packed refs are preserved' \
-       'git branch q &&
-        git pack-refs --all --prune &&
-        git update-ref refs/heads/q refs/heads/q &&
-        ! test -f .git/refs/heads/q'
+test_expect_success 'test git branch n after some branch deletion and pruning' '
+       git branch n/o &&
+       git branch n/op &&
+       git branch -d n/o &&
+       git branch n/o/p &&
+       git branch -d n/op &&
+       git pack-refs --all --prune &&
+       git branch -d n/o/p &&
+       git branch n
+'
+
+test_expect_success 'test excluded refs are not packed' '
+       git branch dont_pack1 &&
+       git branch dont_pack2 &&
+       git branch pack_this &&
+       git pack-refs --all --exclude "refs/heads/dont_pack*" &&
+       test -f .git/refs/heads/dont_pack1 &&
+       test -f .git/refs/heads/dont_pack2 &&
+       ! test -f .git/refs/heads/pack_this'
+
+test_expect_success 'test --no-exclude refs clears excluded refs' '
+       git branch dont_pack3 &&
+       git branch dont_pack4 &&
+       git pack-refs --all --exclude "refs/heads/dont_pack*" --no-exclude &&
+       ! test -f .git/refs/heads/dont_pack3 &&
+       ! test -f .git/refs/heads/dont_pack4'
+
+test_expect_success 'test only included refs are packed' '
+       git branch pack_this1 &&
+       git branch pack_this2 &&
+       git tag dont_pack5 &&
+       git pack-refs --include "refs/heads/pack_this*" &&
+       test -f .git/refs/tags/dont_pack5 &&
+       ! test -f .git/refs/heads/pack_this1 &&
+       ! test -f .git/refs/heads/pack_this2'
+
+test_expect_success 'test --no-include refs clears included refs' '
+       git branch pack1 &&
+       git branch pack2 &&
+       git pack-refs --include "refs/heads/pack*" --no-include &&
+       test -f .git/refs/heads/pack1 &&
+       test -f .git/refs/heads/pack2'
+
+test_expect_success 'test --exclude takes precedence over --include' '
+       git branch dont_pack5 &&
+       git pack-refs --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
+       test -f .git/refs/heads/dont_pack5'
+
+test_expect_success 'see if up-to-date packed refs are preserved' '
+       git branch q &&
+       git pack-refs --all --prune &&
+       git update-ref refs/heads/q refs/heads/q &&
+       ! test -f .git/refs/heads/q
+'
 
 test_expect_success 'pack, prune and repack' '
        git tag foo &&
@@ -189,7 +227,7 @@ test_expect_success 'notice d/f conflict with existing directory' '
 
 test_expect_success 'existing directory reports concrete ref' '
        test_must_fail git branch foo 2>stderr &&
-       test_i18ngrep refs/heads/foo/bar/baz stderr
+       test_grep refs/heads/foo/bar/baz stderr
 '
 
 test_expect_success 'notice d/f conflict with existing ref' '
index 3288aaec7dc9362dc923b963e31ccd05005ba351..cf23c06c098756abec968d689bcbf3e58541901b 100755 (executable)
@@ -362,6 +362,7 @@ test_expect_success 'do not create empty note with -m ""' '
 '
 
 test_expect_success 'create note with combination of -m and -F' '
+       test_when_finished git notes remove HEAD &&
        cat >expect-combine_m_and_F <<-EOF &&
                foo
 
@@ -380,6 +381,41 @@ test_expect_success 'create note with combination of -m and -F' '
        test_cmp expect-combine_m_and_F actual
 '
 
+test_expect_success 'create note with combination of -m and -F and --separator' '
+       test_when_finished git notes remove HEAD &&
+       cat >expect-combine_m_and_F <<-\EOF &&
+       foo
+       -------
+       xyzzy
+       -------
+       bar
+       -------
+       zyxxy
+       -------
+       baz
+       EOF
+       echo "xyzzy" >note_a &&
+       echo "zyxxy" >note_b &&
+       git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" --separator="-------" &&
+       git notes show >actual &&
+       test_cmp expect-combine_m_and_F actual
+'
+
+test_expect_success 'create note with combination of -m and -F and --no-separator' '
+       cat >expect-combine_m_and_F <<-\EOF &&
+       foo
+       xyzzy
+       bar
+       zyxxy
+       baz
+       EOF
+       echo "xyzzy" >note_a &&
+       echo "zyxxy" >note_b &&
+       git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" --no-separator &&
+       git notes show >actual &&
+       test_cmp expect-combine_m_and_F actual
+'
+
 test_expect_success 'remove note with "git notes remove"' '
        git notes remove HEAD^ &&
        git notes remove &&
@@ -521,6 +557,112 @@ test_expect_success 'listing non-existing notes fails' '
        test_must_be_empty actual
 '
 
+test_expect_success 'append: specify a separator with an empty arg' '
+       test_when_finished git notes remove HEAD &&
+       cat >expect <<-\EOF &&
+       notes-1
+
+       notes-2
+       EOF
+
+       git notes add -m "notes-1" &&
+       git notes append --separator="" -m "notes-2" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append: specify a separator without arg' '
+       test_when_finished git notes remove HEAD &&
+       cat >expect <<-\EOF &&
+       notes-1
+
+       notes-2
+       EOF
+
+       git notes add -m "notes-1" &&
+       git notes append --separator -m "notes-2" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append: specify as --no-separator' '
+       test_when_finished git notes remove HEAD &&
+       cat >expect <<-\EOF &&
+       notes-1
+       notes-2
+       EOF
+
+       git notes add -m "notes-1" &&
+       git notes append --no-separator -m "notes-2" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator with line break' '
+       test_when_finished git notes remove HEAD &&
+       cat >expect <<-\EOF &&
+       notes-1
+       -------
+       notes-2
+       EOF
+
+       git notes add -m "notes-1" &&
+       git notes append --separator="-------$LF" -m "notes-2" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator without line break' '
+       test_when_finished git notes remove HEAD &&
+       cat >expect <<-\EOF &&
+       notes-1
+       -------
+       notes-2
+       EOF
+
+       git notes add -m "notes-1" &&
+       git notes append --separator="-------" -m "notes-2" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator with multiple messages' '
+       test_when_finished git notes remove HEAD &&
+       cat >expect <<-\EOF &&
+       notes-1
+       -------
+       notes-2
+       -------
+       notes-3
+       EOF
+
+       git notes add -m "notes-1" &&
+       git notes append --separator="-------" -m "notes-2" -m "notes-3" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append note with combination of -m and -F and --separator' '
+       test_when_finished git notes remove HEAD &&
+       cat >expect-combine_m_and_F <<-\EOF &&
+       m-notes-1
+       -------
+       f-notes-1
+       -------
+       m-notes-2
+       -------
+       f-notes-2
+       -------
+       m-notes-3
+       EOF
+
+       echo "f-notes-1" >note_a &&
+       echo "f-notes-2" >note_b &&
+       git notes append -m "m-notes-1" -F note_a -m "m-notes-2" -F note_b -m "m-notes-3" --separator="-------" &&
+       git notes show >actual &&
+       test_cmp expect-combine_m_and_F actual
+'
+
 test_expect_success 'append to existing note with "git notes append"' '
        cat >expect <<-EOF &&
                Initial set of notes
@@ -818,6 +960,33 @@ test_expect_success 'create note from blob with "git notes add -C" reuses blob i
        test_cmp blob actual
 '
 
+test_expect_success 'create note from blob with "-C", also specify "-m", "-F" and "--separator"' '
+       # 8th will be reuseed in following tests, so rollback when the test is done
+       test_when_finished "git notes remove && git notes add -C $(cat blob)" &&
+       commit=$(git rev-parse HEAD) &&
+       cat >expect <<-EOF &&
+               commit $commit
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:20:13 2005 -0700
+
+               ${indent}8th
+
+               Notes:
+               ${indent}This is a blob object
+               ${indent}-------
+               ${indent}This is created by -m
+               ${indent}-------
+               ${indent}This is created by -F
+       EOF
+
+       git notes remove &&
+       echo "This is a blob object" | git hash-object -w --stdin >blob &&
+       echo "This is created by -F" >note_a &&
+       git notes add -C $(cat blob) -m "This is created by -m" -F note_a --separator="-------" &&
+       git log -1 >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'create note from other note with "git notes add -c"' '
        test_commit 9th &&
        commit=$(git rev-parse HEAD) &&
@@ -1300,9 +1469,9 @@ test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
 
 test_expect_success 'git notes copy diagnoses too many or too few arguments' '
        test_must_fail git notes copy 2>error &&
-       test_i18ngrep "too few arguments" error &&
+       test_grep "too few arguments" error &&
        test_must_fail git notes copy one two three 2>error &&
-       test_i18ngrep "too many arguments" error
+       test_grep "too many arguments" error
 '
 
 test_expect_success 'git notes get-ref expands refs/heads/main to refs/notes/refs/heads/main' '
index 22ffe5bcb9908d914585dbc90e59194023bd9ff5..1ec1fb6715efda8a0ec546956a0deece76efefff 100755 (executable)
@@ -9,7 +9,7 @@ path_has_fanout() {
        path=$1 &&
        fanout=$2 &&
        after_last_slash=$(($(test_oid hexsz) - $fanout * 2)) &&
-       echo $path | grep -q "^\([0-9a-f]\{2\}/\)\{$fanout\}[0-9a-f]\{$after_last_slash\}$"
+       echo $path | grep -q -E "^([0-9a-f]{2}/){$fanout}[0-9a-f]{$after_last_slash}$"
 }
 
 touched_one_note_with_fanout() {
index 141d3e4ca4dc49181b31aff8a540a551ce8e4805..9bd5dbf341fd81a80dbebf0e1fafe623defecfa4 100755 (executable)
@@ -360,7 +360,12 @@ test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
 
 test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '
        git config core.notesRef refs/notes/y &&
-       test_must_fail git -c notes.mergeStrategy="foo" notes merge z &&
+       cat >expect <<-\EOF &&
+       error: unknown notes merge strategy foo
+       fatal: unable to parse '\''notes.mergeStrategy'\'' from command-line config
+       EOF
+       test_must_fail git -c notes.mergeStrategy="foo" notes merge z 2>actual &&
+       test_cmp expect actual &&
        # Verify no changes (y)
        verify_notes y y
 '
index d3d72e25fe4b5ba05527d1378841978029f4c543..597df5ebc0a582018d30c018e734d1154c364ed6 100755 (executable)
@@ -216,7 +216,7 @@ test_expect_success 'merge z into m (== y) with default ("manual") resolver => C
        git config core.notesRef refs/notes/m &&
        test_must_fail git notes merge z >output 2>&1 &&
        # Output should point to where to resolve conflicts
-       test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+       test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
        # Inspect merge conflicts
        ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
        test_cmp expect_conflicts output_conflicts &&
@@ -263,7 +263,7 @@ test_expect_success 'cannot do merge w/conflicts when previous merge is unfinish
        test -d .git/NOTES_MERGE_WORKTREE &&
        test_must_fail git notes merge z >output 2>&1 &&
        # Output should indicate what is wrong
-       test_i18ngrep -q "\\.git/NOTES_MERGE_\\* exists" output
+       test_grep -q "\\.git/NOTES_MERGE_\\* exists" output
 '
 
 # Setup non-conflicting merge between x and new notes ref w
@@ -417,7 +417,7 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol
        git config core.notesRef refs/notes/m &&
        test_must_fail git notes merge z >output 2>&1 &&
        # Output should point to where to resolve conflicts
-       test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+       test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
        # Inspect merge conflicts
        ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
        test_cmp expect_conflicts output_conflicts &&
@@ -449,7 +449,7 @@ git rev-parse refs/notes/z > pre_merge_z
 test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
        test_must_fail git notes merge z >output 2>&1 &&
        # Output should point to where to resolve conflicts
-       test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+       test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
        # Inspect merge conflicts
        ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
        test_cmp expect_conflicts output_conflicts &&
@@ -528,7 +528,7 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol
        git update-ref refs/notes/m refs/notes/y &&
        test_must_fail git notes merge z >output 2>&1 &&
        # Output should point to where to resolve conflicts
-       test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+       test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
        # Inspect merge conflicts
        ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
        test_cmp expect_conflicts output_conflicts &&
@@ -561,9 +561,9 @@ y and z notes on 4th commit
 EOF
        # Fail to finalize merge
        test_must_fail git notes merge --commit >output 2>&1 &&
-       # .git/NOTES_MERGE_* must remain
-       test -f .git/NOTES_MERGE_PARTIAL &&
-       test -f .git/NOTES_MERGE_REF &&
+       # NOTES_MERGE_* refs and .git/NOTES_MERGE_* state files must remain
+       git rev-parse --verify NOTES_MERGE_PARTIAL &&
+       git rev-parse --verify NOTES_MERGE_REF &&
        test -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&
        test -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&
        test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
@@ -573,9 +573,9 @@ EOF
        test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
        test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
        # Mention refs/notes/m, and its current and expected value in output
-       test_i18ngrep -q "refs/notes/m" output &&
-       test_i18ngrep -q "$(git rev-parse refs/notes/m)" output &&
-       test_i18ngrep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
+       test_grep -q "refs/notes/m" output &&
+       test_grep -q "$(git rev-parse refs/notes/m)" output &&
+       test_grep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
        # Verify that other notes refs has not changed (w, x, y and z)
        verify_notes w &&
        verify_notes x &&
index bff0aea550f285a6c2073efd50182cbc5fab8656..0fd33280cf91f7fe9384205df8b7570363a37076 100755 (executable)
@@ -57,7 +57,7 @@ test_expect_success 'merge z into y while mid-merge in another workdir fails' '
                cd worktree &&
                git config core.notesRef refs/notes/y &&
                test_must_fail git notes merge z 2>err &&
-               test_i18ngrep "a notes merge into refs/notes/y is already in-progress at" err
+               test_grep "a notes merge into refs/notes/y is already in-progress at" err
        ) &&
        test_must_fail git -C worktree symbolic-ref NOTES_MERGE_REF
 '
@@ -67,7 +67,7 @@ test_expect_success 'merge z into x while mid-merge on y succeeds' '
                cd worktree2 &&
                git config core.notesRef refs/notes/x &&
                test_must_fail git notes merge z >out 2>&1 &&
-               test_i18ngrep "Automatic notes merge failed" out &&
+               test_grep "Automatic notes merge failed" out &&
                grep -v "A notes merge into refs/notes/x is already in-progress in" out
        ) &&
        echo "refs/notes/x" >expect &&
diff --git a/t/t3321-notes-stripspace.sh b/t/t3321-notes-stripspace.sh
new file mode 100755 (executable)
index 0000000..088a852
--- /dev/null
@@ -0,0 +1,578 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Teng Long
+#
+
+test_description='Test commit notes with stripspace behavior'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+MULTI_LF="$LF$LF$LF"
+write_script fake_editor <<\EOF
+echo "$MSG" >"$1"
+echo "$MSG" >&2
+EOF
+GIT_EDITOR=./fake_editor
+export GIT_EDITOR
+
+test_expect_success 'setup the commit' '
+       test_commit 1st
+'
+
+test_expect_success 'add note by editor' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add  &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-m", "--stripspace" is the default behavior' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       git notes add -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+       git notes show >actual &&
+       test_cmp expect actual &&
+       git notes remove &&
+       git notes add --stripspace -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-m" and "--no-stripspace" ' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       ${LF}first-line${MULTI_LF}second-line
+       EOF
+
+       git notes add --no-stripspace \
+                     -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-m", "--stripspace" is the default behavior' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       git notes add -m "${LF}" \
+                     -m "first-line" \
+                     -m "${MULTI_LF}" \
+                     -m "second-line" \
+                     -m "${LF}" &&
+       git notes show >actual &&
+       test_cmp expect actual &&
+       git notes remove &&
+       git notes add --stripspace -m "${LF}" \
+                     -m "first-line" \
+                     -m "${MULTI_LF}" \
+                     -m "second-line" \
+                     -m "${LF}" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add notes by specifying multiple "-m" and "--no-stripspace"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line${LF}
+       EOF
+
+       git notes add --no-stripspace \
+                     -m "${LF}" \
+                     -m "first-line" \
+                     -m "${MULTI_LF}" \
+                     -m "second-line" \
+                     -m "${LF}" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-F", "--stripspace" is the default behavior' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       cat >note-file <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line
+       ${LF}
+       EOF
+
+       git notes add -F note-file &&
+       git notes show >actual &&
+       test_cmp expect actual &&
+       git notes remove &&
+       git notes add --stripspace -F note-file &&
+       git notes show >actual
+'
+
+test_expect_success 'add note by specifying single "-F" and "--no-stripspace"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line
+       ${LF}
+       EOF
+
+       cat >note-file <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line
+       ${LF}
+       EOF
+
+       git notes add --no-stripspace -F note-file &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-F", "--stripspace" is the default behavior' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       file-1-first-line
+
+       file-1-second-line
+
+       file-2-first-line
+
+       file-2-second-line
+       EOF
+
+       cat >note-file-1 <<-EOF &&
+       ${LF}
+       file-1-first-line
+       ${MULTI_LF}
+       file-1-second-line
+       ${LF}
+       EOF
+
+       cat >note-file-2 <<-EOF &&
+       ${LF}
+       file-2-first-line
+       ${MULTI_LF}
+       file-2-second-line
+       ${LF}
+       EOF
+
+       git notes add -F note-file-1 -F note-file-2 &&
+       git notes show >actual &&
+       test_cmp expect actual &&
+       git notes remove &&
+       git notes add --stripspace -F note-file-1 -F note-file-2 &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-F" with "--no-stripspace"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       ${LF}
+       file-1-first-line
+       ${MULTI_LF}
+       file-1-second-line
+       ${LF}
+
+       ${LF}
+       file-2-first-line
+       ${MULTI_LF}
+       file-2-second-line
+       ${LF}
+       EOF
+
+       cat >note-file-1 <<-EOF &&
+       ${LF}
+       file-1-first-line
+       ${MULTI_LF}
+       file-1-second-line
+       ${LF}
+       EOF
+
+       cat >note-file-2 <<-EOF &&
+       ${LF}
+       file-2-first-line
+       ${MULTI_LF}
+       file-2-second-line
+       ${LF}
+       EOF
+
+       git notes add --no-stripspace -F note-file-1 -F note-file-2 &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append note by editor' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       git notes add -m "first-line" &&
+       MSG="${MULTI_LF}second-line${LF}" git notes append  &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying single "-m"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       git notes add -m "${LF}first-line" &&
+       git notes append -m "${MULTI_LF}second-line${LF}" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying multiple "-m"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       git notes add -m "${LF}first-line" &&
+       git notes append -m "${MULTI_LF}" \
+                     -m "second-line" \
+                     -m "${LF}" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-F"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       cat >note-file <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line
+       ${LF}
+       EOF
+
+       git notes add -F note-file &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add notes by specifying multiple "-F"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       file-1-first-line
+
+       file-1-second-line
+
+       file-2-first-line
+
+       file-2-second-line
+       EOF
+
+       cat >note-file-1 <<-EOF &&
+       ${LF}
+       file-1-first-line
+       ${MULTI_LF}
+       file-1-second-line
+       ${LF}
+       EOF
+
+       cat >note-file-2 <<-EOF &&
+       ${LF}
+       file-2-first-line
+       ${MULTI_LF}
+       file-2-second-line
+       ${LF}
+       EOF
+
+       git notes add -F note-file-1 -F note-file-2 &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying single "-F"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       initial-line
+
+       first-line
+
+       second-line
+       EOF
+
+       cat >note-file <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line
+       ${LF}
+       EOF
+
+       git notes add -m "initial-line" &&
+       git notes append -F note-file &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append notes by specifying multiple "-F"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       initial-line
+
+       file-1-first-line
+
+       file-1-second-line
+
+       file-2-first-line
+
+       file-2-second-line
+       EOF
+
+       cat >note-file-1 <<-EOF &&
+       ${LF}
+       file-1-first-line
+       ${MULTI_LF}
+       file-1-second-line
+       ${LF}
+       EOF
+
+       cat >note-file-2 <<-EOF &&
+       ${LF}
+       file-2-first-line
+       ${MULTI_LF}
+       file-2-second-line
+       ${LF}
+       EOF
+
+       git notes add -m "initial-line" &&
+       git notes append -F note-file-1 -F note-file-2 &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying multiple "-F" with "--no-stripspace"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       initial-line
+       ${LF}${LF}
+       file-1-first-line
+       ${MULTI_LF}
+       file-1-second-line
+       ${LF}
+
+       ${LF}
+       file-2-first-line
+       ${MULTI_LF}
+       file-2-second-line
+       ${LF}
+       EOF
+
+       cat >note-file-1 <<-EOF &&
+       ${LF}
+       file-1-first-line
+       ${MULTI_LF}
+       file-1-second-line
+       ${LF}
+       EOF
+
+       cat >note-file-2 <<-EOF &&
+       ${LF}
+       file-2-first-line
+       ${MULTI_LF}
+       file-2-second-line
+       ${LF}
+       EOF
+
+       git notes add -m "initial-line" &&
+       git notes append --no-stripspace -F note-file-1 -F note-file-2 &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add notes with empty messages' '
+       rev=$(git rev-parse HEAD) &&
+       git notes add -m "${LF}" \
+                     -m "${MULTI_LF}" \
+                     -m "${LF}" >actual 2>&1 &&
+       test_grep "Removing note for object" actual
+'
+
+test_expect_success 'add note by specifying "-C", "--no-stripspace" is the default behavior' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line
+       ${LF}
+       EOF
+
+       cat expect | git hash-object -w --stdin >blob &&
+       git notes add -C $(cat blob) &&
+       git notes show >actual &&
+       test_cmp expect actual &&
+       git notes remove &&
+       git notes add --no-stripspace -C $(cat blob) &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
+       test_when_finished "git notes remove" &&
+       cat >data <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line
+       ${LF}
+       EOF
+
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       cat data | git hash-object -w --stdin >blob &&
+       git notes add --stripspace -C $(cat blob) &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspace all together' '
+       test_when_finished "git notes remove" &&
+       cat >data <<-EOF &&
+       ${LF}
+       first-line
+       ${MULTI_LF}
+       second-line
+       ${LF}
+       EOF
+
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+
+       third-line
+       EOF
+
+       cat data | git hash-object -w --stdin >blob &&
+       git notes add -C $(cat blob) -m "third-line" &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not stripspace all together' '
+       test_when_finished "git notes remove" &&
+       cat >data <<-EOF &&
+
+       second-line
+       EOF
+
+       cat >expect <<-EOF &&
+       first-line
+       ${LF}
+       second-line
+       EOF
+
+       cat data | git hash-object -w --stdin >blob &&
+       git notes add -m "first-line" -C $(cat blob)  &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying "-c", "--stripspace" is the default behavior' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       echo "initial-line" | git hash-object -w --stdin >blob &&
+       MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add -c $(cat blob) &&
+       git notes show >actual &&
+       test_cmp expect actual &&
+       git notes remove &&
+       MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --stripspace -c $(cat blob) &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying "-c" with "--no-stripspace"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       ${LF}first-line${MULTI_LF}second-line${LF}
+       EOF
+
+       echo "initial-line" | git hash-object -w --stdin >blob &&
+       MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace -c $(cat blob) &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'edit note by specifying "-c", "--stripspace" is the default behavior' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       first-line
+
+       second-line
+       EOF
+
+       MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit &&
+       git notes show >actual &&
+       test_cmp expect actual &&
+       git notes remove &&
+       MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit --stripspace &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'edit note by specifying "-c" with "--no-stripspace"' '
+       test_when_finished "git notes remove" &&
+       cat >expect <<-EOF &&
+       ${LF}first-line${MULTI_LF}second-line${LF}
+       EOF
+
+       MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace &&
+       git notes show >actual &&
+       test_cmp expect actual
+'
+
+test_done
index d5a8ee39fc478d3d3ab2ab71ec780efd76140d20..57f13929260e70865fbb8f31e89ba7ff197afeaa 100755 (executable)
@@ -143,8 +143,8 @@ test_expect_success 'Show verbose error when HEAD could not be detached' '
        >B &&
        test_when_finished "rm -f B" &&
        test_must_fail git rebase topic 2>output.err >output.out &&
-       test_i18ngrep "The following untracked working tree files would be overwritten by checkout:" output.err &&
-       test_i18ngrep B output.err
+       test_grep "The following untracked working tree files would be overwritten by checkout:" output.err &&
+       test_grep B output.err
 '
 
 test_expect_success 'fail when upstream arg is missing and not on branch' '
@@ -388,6 +388,20 @@ test_expect_success 'switch to branch checked out here' '
        git rebase main main
 '
 
+test_expect_success 'switch to branch checked out elsewhere fails' '
+       test_when_finished "
+               git worktree remove wt1 &&
+               git worktree remove wt2 &&
+               git branch -d shared
+       " &&
+       git worktree add wt1 -b shared &&
+       git worktree add wt2 -f shared &&
+       # we test in both worktrees to ensure that works
+       # as expected with "first" and "next" worktrees
+       test_must_fail git -C wt1 rebase shared shared &&
+       test_must_fail git -C wt2 rebase shared shared
+'
+
 test_expect_success 'switch to branch not checked out' '
        git checkout main &&
        git branch other &&
@@ -407,10 +421,10 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' '
        git checkout main &&
        git worktree add wt &&
        test_must_fail git -C wt rebase main main 2>err &&
-       test_i18ngrep "already checked out" err
+       test_grep "already used by worktree at" err
 '
 
-test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
+test_expect_success REFFILES,MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
        git checkout main &&
        mv .git/logs actual_logs &&
        cmd //c "mklink /D .git\logs ..\actual_logs" &&
index 7e46f4ca850616b695cc6ffa061ca39ac404fb7d..5c67d07ba3ecf6333d1967c341d9bfb75fc33769 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git rebase --merge test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 T="A quick brown fox
@@ -131,27 +132,6 @@ test_expect_success 'picking rebase' '
        esac
 '
 
-test_expect_success 'rebase -s funny -Xopt' '
-       test_when_finished "rm -fr test-bin funny.was.run" &&
-       mkdir test-bin &&
-       cat >test-bin/git-merge-funny <<-EOF &&
-       #!$SHELL_PATH
-       case "\$1" in --opt) ;; *) exit 2 ;; esac
-       shift &&
-       >funny.was.run &&
-       exec git merge-recursive "\$@"
-       EOF
-       chmod +x test-bin/git-merge-funny &&
-       git reset --hard &&
-       git checkout -b test-funny main^ &&
-       test_commit funny &&
-       (
-               PATH=./test-bin:$PATH &&
-               git rebase -s funny -Xopt main
-       ) &&
-       test -f funny.was.run
-'
-
 test_expect_success 'rebase --skip works with two conflicts in a row' '
        git checkout second-side  &&
        tr "[A-Z]" "[a-z]" <newfile >tmp &&
@@ -191,7 +171,7 @@ test_expect_success '--reapply-cherry-picks' '
 
        # Regular rebase fails, because the 1-11 commit is deduplicated
        test_must_fail git -C repo rebase --merge main 2> err &&
-       test_i18ngrep "error: could not apply.*add 12 in another branch" err &&
+       test_grep "error: could not apply.*add 12 in another branch" err &&
        git -C repo rebase --abort &&
 
        # With --reapply-cherry-picks, it works
index f6e48644978b63ab00ccf40cca379e0770aef0b9..a1911c4a9d60e71f99d66ef6d7f30728ab8732df 100755 (executable)
@@ -108,10 +108,10 @@ test_expect_success 'correct advice upon picking empty commit' '
        test_when_finished "git rebase --abort" &&
        test_must_fail git rebase -i --onto goodbye \
                amended-goodbye^ amended-goodbye 2>err &&
-       test_i18ngrep "previous cherry-pick is now empty" err &&
-       test_i18ngrep "git rebase --skip" err &&
+       test_grep "previous cherry-pick is now empty" err &&
+       test_grep "git rebase --skip" err &&
        test_must_fail git commit &&
-       test_i18ngrep "git rebase --skip" err
+       test_grep "git rebase --skip" err
 '
 
 test_expect_success 'correct authorship when committing empty pick' '
@@ -131,10 +131,10 @@ test_expect_success 'correct advice upon rewording empty commit' '
                test_must_fail env FAKE_LINES="reword 1" git rebase -i \
                        --onto goodbye amended-goodbye^ amended-goodbye 2>err
        ) &&
-       test_i18ngrep "previous cherry-pick is now empty" err &&
-       test_i18ngrep "git rebase --skip" err &&
+       test_grep "previous cherry-pick is now empty" err &&
+       test_grep "git rebase --skip" err &&
        test_must_fail git commit &&
-       test_i18ngrep "git rebase --skip" err
+       test_grep "git rebase --skip" err
 '
 
 test_expect_success 'correct advice upon editing empty commit' '
@@ -144,10 +144,10 @@ test_expect_success 'correct advice upon editing empty commit' '
                test_must_fail env FAKE_LINES="edit 1" git rebase -i \
                        --onto goodbye amended-goodbye^ amended-goodbye 2>err
        ) &&
-       test_i18ngrep "previous cherry-pick is now empty" err &&
-       test_i18ngrep "git rebase --skip" err &&
+       test_grep "previous cherry-pick is now empty" err &&
+       test_grep "git rebase --skip" err &&
        test_must_fail git commit &&
-       test_i18ngrep "git rebase --skip" err
+       test_grep "git rebase --skip" err
 '
 
 test_expect_success 'correct advice upon cherry-picking an empty commit during a rebase' '
@@ -157,10 +157,10 @@ test_expect_success 'correct advice upon cherry-picking an empty commit during a
                test_must_fail env FAKE_LINES="1 exec_git_cherry-pick_amended-goodbye" \
                        git rebase -i goodbye^ goodbye 2>err
        ) &&
-       test_i18ngrep "previous cherry-pick is now empty" err &&
-       test_i18ngrep "git cherry-pick --skip" err &&
+       test_grep "previous cherry-pick is now empty" err &&
+       test_grep "git cherry-pick --skip" err &&
        test_must_fail git commit 2>err &&
-       test_i18ngrep "git cherry-pick --skip" err
+       test_grep "git cherry-pick --skip" err
 '
 
 test_expect_success 'correct advice upon multi cherry-pick picking an empty commit during a rebase' '
@@ -170,10 +170,10 @@ test_expect_success 'correct advice upon multi cherry-pick picking an empty comm
                test_must_fail env FAKE_LINES="1 exec_git_cherry-pick_goodbye_amended-goodbye" \
                        git rebase -i goodbye^^ goodbye 2>err
        ) &&
-       test_i18ngrep "previous cherry-pick is now empty" err &&
-       test_i18ngrep "git cherry-pick --skip" err &&
+       test_grep "previous cherry-pick is now empty" err &&
+       test_grep "git cherry-pick --skip" err &&
        test_must_fail git commit 2>err &&
-       test_i18ngrep "git cherry-pick --skip" err
+       test_grep "git cherry-pick --skip" err
 '
 
 test_expect_success 'fixup that empties commit fails' '
index f0a0a54fba43d3a0a49b9df59c9aeb4996c2a85f..64b641002e4fdb049d7f958cdc54335f9c0f34ce 100755 (executable)
@@ -291,9 +291,9 @@ test_expect_success 'abort with error when new base cannot be checked out' '
        git rm --cached file1 &&
        git commit -m "remove file in base" &&
        test_must_fail git rebase -i primary > output 2>&1 &&
-       test_i18ngrep "The following untracked working tree files would be overwritten by checkout:" \
+       test_grep "The following untracked working tree files would be overwritten by checkout:" \
                output &&
-       test_i18ngrep "file1" output &&
+       test_grep "file1" output &&
        test_path_is_missing .git/rebase-merge &&
        rm file1 &&
        git reset --hard HEAD^
@@ -604,7 +604,8 @@ test_expect_success 'clean error after failed "exec"' '
        echo "edited again" > file7 &&
        git add file7 &&
        test_must_fail git rebase --continue 2>error &&
-       test_i18ngrep "you have staged changes in your working tree" error
+       test_grep "you have staged changes in your working tree" error &&
+       test_grep ! "could not open.*for reading" error
 '
 
 test_expect_success 'rebase a detached HEAD' '
@@ -758,7 +759,7 @@ test_expect_success 'reword' '
        git show HEAD~2 | grep "C changed"
 '
 
-test_expect_success 'no uncommited changes when rewording the todo list is reloaded' '
+test_expect_success 'no uncommitted changes when rewording and the todo list is reloaded' '
        git checkout E &&
        test_when_finished "git checkout @{-1}" &&
        (
@@ -955,7 +956,7 @@ test_expect_success 'rebase --exec works without -i ' '
        git reset --hard execute &&
        rm -rf exec_output &&
        EDITOR="echo >invoked_editor" git rebase --exec "echo a line >>exec_output"  HEAD~2 2>actual &&
-       test_i18ngrep  "Successfully rebased and updated" actual &&
+       test_grep  "Successfully rebased and updated" actual &&
        test_line_count = 2 exec_output &&
        test_path_is_missing invoked_editor
 '
@@ -963,7 +964,7 @@ test_expect_success 'rebase --exec works without -i ' '
 test_expect_success 'rebase -i --exec without <CMD>' '
        git reset --hard execute &&
        test_must_fail git rebase -i --exec 2>actual &&
-       test_i18ngrep "requires a value" actual &&
+       test_grep "requires a value" actual &&
        git checkout primary
 '
 
@@ -1244,9 +1245,9 @@ test_expect_success 'short commit ID collide' '
                test $colliding_id = "$(git rev-parse HEAD | cut -c 1-4)" &&
                grep "^pick $colliding_id " \
                        .git/rebase-merge/git-rebase-todo.tmp &&
-               grep "^pick [0-9a-f]\{$hexsz\}" \
+               grep -E "^pick [0-9a-f]{$hexsz}" \
                        .git/rebase-merge/git-rebase-todo &&
-               grep "^pick [0-9a-f]\{$hexsz\}" \
+               grep -E "^pick [0-9a-f]{$hexsz}" \
                        .git/rebase-merge/git-rebase-todo.backup &&
                git rebase --continue
        ) &&
@@ -1261,7 +1262,7 @@ test_expect_success 'respect core.abbrev' '
                set_cat_todo_editor &&
                test_must_fail git rebase -i HEAD~4 >todo-list
        ) &&
-       test 4 = $(grep -c "pick [0-9a-f]\{12,\}" todo-list)
+       test 4 = $(grep -c -E "pick [0-9a-f]{12,}" todo-list)
 '
 
 test_expect_success 'todo count' '
@@ -1272,24 +1273,38 @@ test_expect_success 'todo count' '
                test_set_editor "$(pwd)/dump-raw.sh" &&
                git rebase -i HEAD~4 >actual
        ) &&
-       test_i18ngrep "^# Rebase ..* onto ..* ([0-9]" actual
+       test_grep "^# Rebase ..* onto ..* ([0-9]" actual
 '
 
 test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
-       git checkout --force branch2 &&
+       git checkout --force A &&
        git clean -f &&
+       cat >todo <<-EOF &&
+       exec >file2
+       pick $(git rev-parse B) B
+       pick $(git rev-parse C) C
+       pick $(git rev-parse D) D
+       exec cat .git/rebase-merge/done >actual
+       EOF
        (
-               set_fake_editor &&
-               FAKE_LINES="edit 1 2" git rebase -i A
-       ) &&
-       test_cmp_rev HEAD F &&
-       test_path_is_missing file6 &&
-       >file6 &&
-       test_must_fail git rebase --continue &&
-       test_cmp_rev HEAD F &&
-       rm file6 &&
+               set_replace_editor todo &&
+               test_must_fail git rebase -i A
+       ) &&
+       test_cmp_rev HEAD B &&
+       test_cmp_rev REBASE_HEAD C &&
+       head -n3 todo >expect &&
+       test_cmp expect .git/rebase-merge/done &&
+       rm file2 &&
+       test_path_is_missing .git/rebase-merge/patch &&
+       echo changed >file1 &&
+       git add file1 &&
+       test_must_fail git rebase --continue 2>err &&
+       grep "error: you have staged changes in your working tree" err &&
+       git reset --hard HEAD &&
        git rebase --continue &&
-       test_cmp_rev HEAD I
+       test_cmp_rev HEAD D &&
+       tail -n3 todo >>expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'rebase -i commits that overwrite untracked files (squash)' '
@@ -1305,7 +1320,14 @@ test_expect_success 'rebase -i commits that overwrite untracked files (squash)'
        >file6 &&
        test_must_fail git rebase --continue &&
        test_cmp_rev HEAD F &&
+       test_cmp_rev REBASE_HEAD I &&
        rm file6 &&
+       test_path_is_missing .git/rebase-merge/patch &&
+       echo changed >file1 &&
+       git add file1 &&
+       test_must_fail git rebase --continue 2>err &&
+       grep "error: you have staged changes in your working tree" err &&
+       git reset --hard HEAD &&
        git rebase --continue &&
        test $(git cat-file commit HEAD | sed -ne \$p) = I &&
        git reset --hard original-branch2
@@ -1323,7 +1345,14 @@ test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
        >file6 &&
        test_must_fail git rebase --continue &&
        test $(git cat-file commit HEAD | sed -ne \$p) = F &&
+       test_cmp_rev REBASE_HEAD I &&
        rm file6 &&
+       test_path_is_missing .git/rebase-merge/patch &&
+       echo changed >file1 &&
+       git add file1 &&
+       test_must_fail git rebase --continue 2>err &&
+       grep "error: you have staged changes in your working tree" err &&
+       git reset --hard HEAD &&
        git rebase --continue &&
        test $(git cat-file commit HEAD | sed -ne \$p) = I
 '
@@ -1379,7 +1408,7 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = ignore' '
                FAKE_LINES="1 2 3 4" git rebase -i --root 2>actual
        ) &&
        test D = $(git cat-file commit HEAD | sed -ne \$p) &&
-       test_i18ngrep \
+       test_grep \
                "Successfully rebased and updated refs/heads/missing-commit" \
                actual
 '
@@ -1442,21 +1471,22 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = ig
                git rebase --continue 2>actual
        ) &&
        test D = $(git cat-file commit HEAD | sed -ne \$p) &&
-       test_i18ngrep \
+       test_grep \
                "Successfully rebased and updated refs/heads/missing-commit" \
                actual
 '
 
 test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = warn' '
        cat >expect <<-EOF &&
-       error: invalid line 1: badcmd $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
+       error: invalid command '\''pickled'\''
+       error: invalid line 1: pickled $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
        Warning: some commits may have been dropped accidentally.
        Dropped commits (newer to older):
         - $(git rev-list --pretty=oneline --abbrev-commit -1 primary)
         - $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
        To avoid this message, use "drop" to explicitly remove a commit.
        EOF
-       head -n4 expect >expect.2 &&
+       head -n5 expect >expect.2 &&
        tail -n1 expect >>expect.2 &&
        tail -n4 expect.2 >expect.3 &&
        test_config rebase.missingCommitsCheck warn &&
@@ -1467,7 +1497,7 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = wa
                        git rebase -i --root &&
                cp .git/rebase-merge/git-rebase-todo.backup orig &&
                FAKE_LINES="2 3 4" git rebase --edit-todo 2>actual.2 &&
-               head -n6 actual.2 >actual &&
+               head -n7 actual.2 >actual &&
                test_cmp expect actual &&
                cp orig .git/rebase-merge/git-rebase-todo &&
                FAKE_LINES="1 2 3 4" git rebase --edit-todo 2>actual.2 &&
@@ -1476,14 +1506,15 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = wa
                git rebase --continue 2>actual
        ) &&
        test D = $(git cat-file commit HEAD | sed -ne \$p) &&
-       test_i18ngrep \
+       test_grep \
                "Successfully rebased and updated refs/heads/missing-commit" \
                actual
 '
 
 test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = error' '
        cat >expect <<-EOF &&
-       error: invalid line 1: badcmd $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
+       error: invalid command '\''pickled'\''
+       error: invalid line 1: pickled $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
        Warning: some commits may have been dropped accidentally.
        Dropped commits (newer to older):
         - $(git rev-list --pretty=oneline --abbrev-commit -1 primary)
@@ -1523,7 +1554,7 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = er
                git rebase --continue 2>actual
        ) &&
        test D = $(git cat-file commit HEAD | sed -ne \$p) &&
-       test_i18ngrep \
+       test_grep \
                "Successfully rebased and updated refs/heads/missing-commit" \
                actual
 '
@@ -1583,9 +1614,9 @@ test_expect_success 'static check of bad command' '
                set_fake_editor &&
                test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
                git rebase -i --root 2>actual &&
-               test_i18ngrep "badcmd $(git rev-list --oneline -1 primary~1)" \
+               test_grep "pickled $(git rev-list --oneline -1 primary~1)" \
                                actual &&
-               test_i18ngrep "You can fix this with .git rebase --edit-todo.." \
+               test_grep "You can fix this with .git rebase --edit-todo.." \
                                actual &&
                FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo
        ) &&
@@ -1594,6 +1625,32 @@ test_expect_success 'static check of bad command' '
        test C = $(git cat-file commit HEAD^ | sed -ne \$p)
 '
 
+test_expect_success 'the first command cannot be a fixup' '
+       rebase_setup_and_clean fixup-first &&
+
+       cat >orig <<-EOF &&
+       fixup $(git log -1 --format="%h %s" B)
+       pick $(git log -1 --format="%h %s" C)
+       EOF
+
+       (
+               set_replace_editor orig &&
+               test_must_fail git rebase -i A 2>actual
+       ) &&
+       grep "cannot .fixup. without a previous commit" actual &&
+       grep "You can fix this with .git rebase --edit-todo.." actual &&
+       # verify that the todo list has not been truncated
+       grep -v "^#" .git/rebase-merge/git-rebase-todo >actual &&
+       test_cmp orig actual &&
+
+       test_must_fail git rebase --edit-todo 2>actual &&
+       grep "cannot .fixup. without a previous commit" actual &&
+       grep "You can fix this with .git rebase --edit-todo.." actual &&
+       # verify that the todo list has not been truncated
+       grep -v "^#" .git/rebase-merge/git-rebase-todo >actual &&
+       test_cmp orig actual
+'
+
 test_expect_success 'tabs and spaces are accepted in the todolist' '
        rebase_setup_and_clean indented-comment &&
        write_script add-indent.sh <<-\EOF &&
@@ -1617,8 +1674,8 @@ test_expect_success 'static check of bad SHA-1' '
                set_fake_editor &&
                test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
                        git rebase -i --root 2>actual &&
-                       test_i18ngrep "edit XXXXXXX False commit" actual &&
-                       test_i18ngrep "You can fix this with .git rebase --edit-todo.." \
+                       test_grep "edit XXXXXXX False commit" actual &&
+                       test_grep "You can fix this with .git rebase --edit-todo.." \
                                        actual &&
                FAKE_LINES="1 2 4 5 6" git rebase --edit-todo
        ) &&
@@ -1645,7 +1702,7 @@ test_expect_success 'rebase -i --gpg-sign=<key-id>' '
                FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" \
                        HEAD^ >out 2>err
        ) &&
-       test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
+       test_grep "$SQ-S\"S I Gner\"$SQ" err
 '
 
 test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' '
@@ -1656,7 +1713,7 @@ test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' '
                FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" \
                        HEAD^ >out 2>err
        ) &&
-       test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
+       test_grep "$SQ-S\"S I Gner\"$SQ" err
 '
 
 test_expect_success 'valid author header after --root swap' '
@@ -1710,7 +1767,7 @@ test_expect_success 'correct error message for partial commit after empty pick'
        ) &&
        echo x >file1 &&
        test_must_fail git commit file1 2>err &&
-       test_i18ngrep "cannot do a partial commit during a rebase." err
+       test_grep "cannot do a partial commit during a rebase." err
 '
 
 test_expect_success 'correct error message for commit --amend after empty pick' '
@@ -1723,13 +1780,13 @@ test_expect_success 'correct error message for commit --amend after empty pick'
        ) &&
        echo x>file1 &&
        test_must_fail git commit -a --amend 2>err &&
-       test_i18ngrep "middle of a rebase -- cannot amend." err
+       test_grep "middle of a rebase -- cannot amend." err
 '
 
 test_expect_success 'todo has correct onto hash' '
        GIT_SEQUENCE_EDITOR=cat git rebase -i no-conflict-branch~4 no-conflict-branch >actual &&
        onto=$(git rev-parse --short HEAD~4) &&
-       test_i18ngrep "^# Rebase ..* onto $onto" actual
+       test_grep "^# Rebase ..* onto $onto" actual
 '
 
 test_expect_success 'ORIG_HEAD is updated correctly' '
@@ -2072,6 +2129,7 @@ test_expect_success '--update-refs: --edit-todo with no update-ref lines' '
 '
 
 test_expect_success '--update-refs: check failed ref update' '
+       test_when_finished "test_might_fail git rebase --abort" &&
        git checkout -B update-refs-error no-conflict-branch &&
        git branch -f base HEAD~4 &&
        git branch -f first HEAD~3 &&
@@ -2102,7 +2160,7 @@ test_expect_success '--update-refs: check failed ref update' '
        # recorded in the update-refs file. We will force-update the
        # "second" ref, but "git branch -f" will not work because of
        # the lock in the update-refs file.
-       git rev-parse third >.git/refs/heads/second &&
+       git update-ref refs/heads/second third &&
 
        test_must_fail git rebase --continue 2>err &&
        grep "update_ref failed for ref '\''refs/heads/second'\''" err &&
@@ -2123,6 +2181,28 @@ test_expect_success '--update-refs: check failed ref update' '
        test_cmp expect err.trimmed
 '
 
+test_expect_success 'bad labels and refs rejected when parsing todo list' '
+       test_when_finished "test_might_fail git rebase --abort" &&
+       cat >todo <<-\EOF &&
+       exec >execed
+       label #
+       label :invalid
+       update-ref :bad
+       update-ref topic
+       EOF
+       rm -f execed &&
+       (
+               set_replace_editor todo &&
+               test_must_fail git rebase -i HEAD 2>err
+       ) &&
+       grep "'\''#'\'' is not a valid label" err &&
+       grep "'\'':invalid'\'' is not a valid label" err &&
+       grep "'\'':bad'\'' is not a valid refname" err &&
+       grep "update-ref requires a fully qualified refname e.g. refs/heads/topic" \
+               err &&
+       test_path_is_missing execed
+'
+
 # This must be the last test in this file
 test_expect_success '$EDITOR and friends are unchanged' '
        test_editor_unchanged
index 2524331861869d6b9ee9f80082c09d2ed089e8fe..8979bc340734f860f56c49a4380366dca1d46ee8 100755 (executable)
@@ -5,6 +5,7 @@ test_description='rebase should handle arbitrary git message'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
index d17b450e811cc0f13ab94ac76e288f782e897d64..a1d7fa7f7c6965f49cc017303aca52a658211b10 100755 (executable)
@@ -10,10 +10,16 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 test_expect_success 'setup' '
        test_commit O fileO &&
        test_commit X fileX &&
+       git branch fast-forward &&
        test_commit A fileA &&
        test_commit B fileB &&
        test_commit Y fileY &&
 
+       git checkout -b conflicts O &&
+       test_commit P &&
+       test_commit conflict-X fileX &&
+       test_commit Q &&
+
        git checkout -b topic O &&
        git cherry-pick A B &&
        test_commit Z fileZ &&
@@ -27,24 +33,24 @@ test_expect_success 'rebase -m' '
 
 test_expect_success 'rebase against main twice' '
        git rebase --apply main >out &&
-       test_i18ngrep "Current branch topic is up to date" out
+       test_grep "Current branch topic is up to date" out
 '
 
 test_expect_success 'rebase against main twice with --force' '
        git rebase --force-rebase --apply main >out &&
-       test_i18ngrep "Current branch topic is up to date, rebase forced" out
+       test_grep "Current branch topic is up to date, rebase forced" out
 '
 
 test_expect_success 'rebase against main twice from another branch' '
        git checkout topic^ &&
        git rebase --apply main topic >out &&
-       test_i18ngrep "Current branch topic is up to date" out
+       test_grep "Current branch topic is up to date" out
 '
 
 test_expect_success 'rebase fast-forward to main' '
        git checkout topic^ &&
        git rebase --apply topic >out &&
-       test_i18ngrep "Fast-forwarded HEAD to topic" out
+       test_grep "Fast-forwarded HEAD to topic" out
 '
 
 test_expect_success 'rebase --stat' '
@@ -69,64 +75,175 @@ test_expect_success 'rebase -n overrides config rebase.stat config' '
 
 test_expect_success 'rebase --onto outputs the invalid ref' '
        test_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err &&
-       test_i18ngrep "invalid-ref" err
+       test_grep "invalid-ref" err
 '
 
 test_expect_success 'error out early upon -C<n> or --whitespace=<bad>' '
        test_must_fail git rebase -Cnot-a-number HEAD 2>err &&
-       test_i18ngrep "numerical value" err &&
+       test_grep "numerical value" err &&
        test_must_fail git rebase --whitespace=bad HEAD 2>err &&
-       test_i18ngrep "Invalid whitespace option" err
+       test_grep "Invalid whitespace option" err
 '
 
-test_expect_success 'GIT_REFLOG_ACTION' '
-       git checkout start &&
-       test_commit reflog-onto &&
-       git checkout -b reflog-topic start &&
-       test_commit reflog-to-rebase &&
+write_reflog_expect () {
+       if test $mode = --apply
+       then
+               sed 's/(continue)/(pick)/'
+       else
+               cat
+       fi >expect
+}
 
-       git rebase reflog-onto &&
-       git log -g --format=%gs -3 >actual &&
-       cat >expect <<-\EOF &&
-       rebase (finish): returning to refs/heads/reflog-topic
-       rebase (pick): reflog-to-rebase
-       rebase (start): checkout reflog-onto
+test_reflog () {
+       mode=$1
+       reflog_action="$2"
+
+       test_expect_success "rebase $mode reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+       git checkout conflicts &&
+       test_when_finished "git reset --hard Q" &&
+
+       (
+               if test -n "$reflog_action"
+               then
+                       GIT_REFLOG_ACTION="$reflog_action" &&
+                       export GIT_REFLOG_ACTION
+               fi &&
+               test_must_fail git rebase $mode main &&
+               echo resolved >fileX &&
+               git add fileX &&
+               git rebase --continue
+       ) &&
+
+       git log -g --format=%gs -5 >actual &&
+       write_reflog_expect <<-EOF &&
+       ${reflog_action:-rebase} (finish): returning to refs/heads/conflicts
+       ${reflog_action:-rebase} (pick): Q
+       ${reflog_action:-rebase} (continue): conflict-X
+       ${reflog_action:-rebase} (pick): P
+       ${reflog_action:-rebase} (start): checkout main
        EOF
        test_cmp expect actual &&
 
-       git checkout -b reflog-prefix reflog-to-rebase &&
-       GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
-       git log -g --format=%gs -3 >actual &&
-       cat >expect <<-\EOF &&
-       change-the-reflog (finish): returning to refs/heads/reflog-prefix
-       change-the-reflog (pick): reflog-to-rebase
-       change-the-reflog (start): checkout reflog-onto
+       git log -g --format=%gs -1 conflicts >actual &&
+       write_reflog_expect <<-EOF &&
+       ${reflog_action:-rebase} (finish): refs/heads/conflicts onto $(git rev-parse main)
+       EOF
+       test_cmp expect actual &&
+
+       # check there is only one new entry in the branch reflog
+       test_cmp_rev conflicts@{1} Q
+       '
+
+       test_expect_success "rebase $mode fast-forward reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+       git checkout fast-forward &&
+       test_when_finished "git reset --hard X" &&
+
+       (
+               if test -n "$reflog_action"
+               then
+                       GIT_REFLOG_ACTION="$reflog_action" &&
+                       export GIT_REFLOG_ACTION
+               fi &&
+               git rebase $mode main
+       ) &&
+
+       git log -g --format=%gs -2 >actual &&
+       write_reflog_expect <<-EOF &&
+       ${reflog_action:-rebase} (finish): returning to refs/heads/fast-forward
+       ${reflog_action:-rebase} (start): checkout main
+       EOF
+       test_cmp expect actual &&
+
+       git log -g --format=%gs -1 fast-forward >actual &&
+       write_reflog_expect <<-EOF &&
+       ${reflog_action:-rebase} (finish): refs/heads/fast-forward onto $(git rev-parse main)
+       EOF
+       test_cmp expect actual &&
+
+       # check there is only one new entry in the branch reflog
+       test_cmp_rev fast-forward@{1} X
+       '
+
+       test_expect_success "rebase $mode --skip reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+       git checkout conflicts &&
+       test_when_finished "git reset --hard Q" &&
+
+       (
+               if test -n "$reflog_action"
+               then
+                       GIT_REFLOG_ACTION="$reflog_action" &&
+                       export GIT_REFLOG_ACTION
+               fi &&
+               test_must_fail git rebase $mode main &&
+               git rebase --skip
+       ) &&
+
+       git log -g --format=%gs -4 >actual &&
+       write_reflog_expect <<-EOF &&
+       ${reflog_action:-rebase} (finish): returning to refs/heads/conflicts
+       ${reflog_action:-rebase} (pick): Q
+       ${reflog_action:-rebase} (pick): P
+       ${reflog_action:-rebase} (start): checkout main
        EOF
        test_cmp expect actual
-'
+       '
 
-test_expect_success 'rebase --apply reflog' '
-       git checkout -b reflog-apply start &&
-       old_head_reflog="$(git log -g --format=%gs -1 HEAD)" &&
+       test_expect_success "rebase $mode --abort reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+       git checkout conflicts &&
+       test_when_finished "git reset --hard Q" &&
 
-       git rebase --apply Y &&
+       git log -g -1 conflicts >branch-expect &&
+       (
+               if test -n "$reflog_action"
+               then
+                       GIT_REFLOG_ACTION="$reflog_action" &&
+                       export GIT_REFLOG_ACTION
+               fi &&
+               test_must_fail git rebase $mode main &&
+               git rebase --abort
+       ) &&
 
-       git log -g --format=%gs -4 HEAD >actual &&
-       cat >expect <<-EOF &&
-       rebase finished: returning to refs/heads/reflog-apply
-       rebase: Z
-       rebase: checkout Y
-       $old_head_reflog
+       git log -g --format=%gs -3 >actual &&
+       write_reflog_expect <<-EOF &&
+       ${reflog_action:-rebase} (abort): returning to refs/heads/conflicts
+       ${reflog_action:-rebase} (pick): P
+       ${reflog_action:-rebase} (start): checkout main
        EOF
        test_cmp expect actual &&
 
-       git log -g --format=%gs -2 reflog-apply >actual &&
-       cat >expect <<-EOF &&
-       rebase finished: refs/heads/reflog-apply onto $(git rev-parse Y)
-       branch: Created from start
+       # check branch reflog is unchanged
+       git log -g -1 conflicts >branch-actual &&
+       test_cmp branch-expect branch-actual
+       '
+
+       test_expect_success "rebase $mode --abort detached HEAD reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+       git checkout Q &&
+       test_when_finished "git reset --hard Q" &&
+
+       (
+               if test -n "$reflog_action"
+               then
+                       GIT_REFLOG_ACTION="$reflog_action" &&
+                       export GIT_REFLOG_ACTION
+               fi &&
+               test_must_fail git rebase $mode main &&
+               git rebase --abort
+       ) &&
+
+       git log -g --format=%gs -3 >actual &&
+       write_reflog_expect <<-EOF &&
+       ${reflog_action:-rebase} (abort): returning to $(git rev-parse Q)
+       ${reflog_action:-rebase} (pick): P
+       ${reflog_action:-rebase} (start): checkout main
        EOF
        test_cmp expect actual
-'
+       '
+}
+
+test_reflog --merge
+test_reflog --merge my-reflog-action
+test_reflog --apply
+test_reflog --apply my-reflog-action
 
 test_expect_success 'rebase -i onto unrelated history' '
        git init unrelated &&
@@ -134,8 +251,8 @@ test_expect_success 'rebase -i onto unrelated history' '
        git -C unrelated remote add -f origin "$PWD" &&
        git -C unrelated branch --set-upstream-to=origin/main &&
        git -C unrelated -c core.editor=true rebase -i -v --stat >actual &&
-       test_i18ngrep "Changes to " actual &&
-       test_i18ngrep "5 files changed" actual
+       test_grep "Changes to " actual &&
+       test_grep "5 files changed" actual
 '
 
 test_done
index 83ffb39d9ffdfd869ecb24e5adbbe11944a73c53..acaf5558dbe2e445efe2989e137b2f9ef5140aaf 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rebase interactive environment'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 58371d8a5477f47d53384a84ad0dae14457128d9..e75b3d0e07cb13b5d8b4fd3b83b07c35eeddce49 100755 (executable)
@@ -7,6 +7,7 @@ Tests if git rebase --root --onto <newparent> can rebase the root commit.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 log_with_names () {
index 9fab0d779bb6cf32dbc0ece0a69ad2c2821a598e..e8456831e8ba1a3ff2009ff6339459f6c00055da 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git rebase with its hook(s)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index a364530d7629fc0bb47c30211935b878e1aa9805..fcc40d6fe1fd5b2a9c4f2a5e4ede15e73c247790 100755 (executable)
@@ -43,7 +43,7 @@ test_auto_fixup () {
 
        git tag $1 &&
        test_tick &&
-       git rebase $2 -i HEAD^^^ &&
+       git rebase $2 HEAD^^^ &&
        git log --oneline >actual &&
        if test -n "$no_squash"
        then
@@ -61,15 +61,24 @@ test_auto_fixup () {
 }
 
 test_expect_success 'auto fixup (option)' '
-       test_auto_fixup final-fixup-option --autosquash
+       test_auto_fixup fixup-option --autosquash &&
+       test_auto_fixup fixup-option-i "--autosquash -i"
 '
 
-test_expect_success 'auto fixup (config)' '
+test_expect_success 'auto fixup (config true)' '
        git config rebase.autosquash true &&
-       test_auto_fixup final-fixup-config-true &&
+       test_auto_fixup ! fixup-config-true &&
+       test_auto_fixup fixup-config-true-i -i &&
        test_auto_fixup ! fixup-config-true-no --no-autosquash &&
+       test_auto_fixup ! fixup-config-true-i-no "-i --no-autosquash"
+'
+
+test_expect_success 'auto fixup (config false)' '
        git config rebase.autosquash false &&
-       test_auto_fixup ! final-fixup-config-false
+       test_auto_fixup ! fixup-config-false &&
+       test_auto_fixup ! fixup-config-false-i -i &&
+       test_auto_fixup fixup-config-false-yes --autosquash &&
+       test_auto_fixup fixup-config-false-i-yes "-i --autosquash"
 '
 
 test_auto_squash () {
@@ -87,7 +96,7 @@ test_auto_squash () {
        git commit -m "squash! first" -m "extra para for first" &&
        git tag $1 &&
        test_tick &&
-       git rebase $2 -i HEAD^^^ &&
+       git rebase $2 HEAD^^^ &&
        git log --oneline >actual &&
        if test -n "$no_squash"
        then
@@ -105,15 +114,24 @@ test_auto_squash () {
 }
 
 test_expect_success 'auto squash (option)' '
-       test_auto_squash final-squash --autosquash
+       test_auto_squash squash-option --autosquash &&
+       test_auto_squash squash-option-i "--autosquash -i"
 '
 
-test_expect_success 'auto squash (config)' '
+test_expect_success 'auto squash (config true)' '
        git config rebase.autosquash true &&
-       test_auto_squash final-squash-config-true &&
+       test_auto_squash ! squash-config-true &&
+       test_auto_squash squash-config-true-i -i &&
        test_auto_squash ! squash-config-true-no --no-autosquash &&
+       test_auto_squash ! squash-config-true-i-no "-i --no-autosquash"
+'
+
+test_expect_success 'auto squash (config false)' '
        git config rebase.autosquash false &&
-       test_auto_squash ! final-squash-config-false
+       test_auto_squash ! squash-config-false &&
+       test_auto_squash ! squash-config-false-i -i &&
+       test_auto_squash squash-config-false-yes --autosquash &&
+       test_auto_squash squash-config-false-i-yes "-i --autosquash"
 '
 
 test_expect_success 'misspelled auto squash' '
index 3e04802cb003b027c0d70319465af4363877de06..f8c4ed78c9ef7ffd0fa970525bf3cab47842b55a 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git rebase --onto A...B'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-rebase.sh"
 
@@ -79,8 +80,10 @@ test_expect_success 'rebase -i --onto main...topic' '
        git reset --hard &&
        git checkout topic &&
        git reset --hard G &&
-       set_fake_editor &&
-       EXPECT_COUNT=1 git rebase -i --onto main...topic F &&
+       (
+               set_fake_editor &&
+               EXPECT_COUNT=1 git rebase -i --onto main...topic F
+       ) &&
        git rev-parse HEAD^1 >actual &&
        git rev-parse C^0 >expect &&
        test_cmp expect actual
@@ -90,20 +93,22 @@ test_expect_success 'rebase -i --onto main...' '
        git reset --hard &&
        git checkout topic &&
        git reset --hard G &&
-       set_fake_editor &&
-       EXPECT_COUNT=1 git rebase -i --onto main... F &&
+       (
+               set_fake_editor &&
+               EXPECT_COUNT=1 git rebase -i --onto main... F
+       ) &&
        git rev-parse HEAD^1 >actual &&
        git rev-parse C^0 >expect &&
        test_cmp expect actual
 '
 
-test_expect_success 'rebase -i --onto main...side' '
+test_expect_success 'rebase --onto main...side requires a single merge-base' '
        git reset --hard &&
        git checkout side &&
        git reset --hard K &&
 
-       set_fake_editor &&
-       test_must_fail git rebase -i --onto main...side J
+       test_must_fail git rebase -i --onto main...side J 2>err &&
+       grep "need exactly one merge base" err
 '
 
 test_expect_success 'rebase --keep-base --onto incompatible' '
@@ -156,8 +161,10 @@ test_expect_success 'rebase -i --keep-base main from topic' '
        git checkout topic &&
        git reset --hard G &&
 
-       set_fake_editor &&
-       EXPECT_COUNT=2 git rebase -i --keep-base main &&
+       (
+               set_fake_editor &&
+               EXPECT_COUNT=2 git rebase -i --keep-base main
+       ) &&
        git rev-parse C >base.expect &&
        git merge-base main HEAD >base.actual &&
        test_cmp base.expect base.actual &&
@@ -171,8 +178,10 @@ test_expect_success 'rebase -i --keep-base main topic from main' '
        git checkout main &&
        git branch -f topic G &&
 
-       set_fake_editor &&
-       EXPECT_COUNT=2 git rebase -i --keep-base main topic &&
+       (
+               set_fake_editor &&
+               EXPECT_COUNT=2 git rebase -i --keep-base main topic
+       ) &&
        git rev-parse C >base.expect &&
        git merge-base main HEAD >base.actual &&
        test_cmp base.expect base.actual &&
@@ -182,13 +191,39 @@ test_expect_success 'rebase -i --keep-base main topic from main' '
        test_cmp expect actual
 '
 
-test_expect_success 'rebase -i --keep-base main from side' '
+test_expect_success 'rebase --keep-base requires a single merge base' '
        git reset --hard &&
        git checkout side &&
        git reset --hard K &&
 
-       set_fake_editor &&
-       test_must_fail git rebase -i --keep-base main
+       test_must_fail git rebase -i --keep-base main 2>err &&
+       grep "need exactly one merge base with branch" err
+'
+
+test_expect_success 'rebase --keep-base keeps cherry picks' '
+       git checkout -f -B main E &&
+       git cherry-pick F &&
+       (
+               set_fake_editor &&
+               EXPECT_COUNT=2 git rebase -i --keep-base HEAD G
+       ) &&
+       test_cmp_rev HEAD G
+'
+
+test_expect_success 'rebase --keep-base --no-reapply-cherry-picks' '
+       git checkout -f -B main E &&
+       git cherry-pick F &&
+       (
+               set_fake_editor &&
+               EXPECT_COUNT=1 git rebase -i --keep-base \
+                                       --no-reapply-cherry-picks HEAD G
+       ) &&
+       test_cmp_rev HEAD^ C
+'
+
+# This must be the last test in this file
+test_expect_success '$EDITOR and friends are unchanged' '
+       test_editor_unchanged
 '
 
 test_done
index 130e2f9b553003b9972ebe1010f17abff2d5ee05..127216f7225aa412022bb0f296f716c42ce15f56 100755 (executable)
@@ -62,61 +62,39 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
        rm -fr .git/rebase-* &&
        git reset --hard commit-new-file-F2-on-topic-branch &&
        test_commit "commit-new-file-F3-on-topic-branch" F3 32 &&
-       test_when_finished "rm -fr test-bin funny.was.run" &&
+       test_when_finished "rm -fr test-bin" &&
        mkdir test-bin &&
-       cat >test-bin/git-merge-funny <<-EOF &&
-       #!$SHELL_PATH
-       case "\$1" in --opt) ;; *) exit 2 ;; esac
-       shift &&
-       >funny.was.run &&
-       exec git merge-recursive "\$@"
+
+       write_script test-bin/git-merge-funny <<-\EOF &&
+       printf "[%s]\n" $# "$1" "$2" "$3" "$5" >actual
+       shift 3 &&
+       exec git merge-recursive "$@"
        EOF
-       chmod +x test-bin/git-merge-funny &&
-       (
-               PATH=./test-bin:$PATH &&
-               test_must_fail git rebase -s funny -Xopt main topic
-       ) &&
-       test -f funny.was.run &&
-       rm funny.was.run &&
-       echo "Resolved" >F2 &&
-       git add F2 &&
-       (
-               PATH=./test-bin:$PATH &&
-               git rebase --continue
-       ) &&
-       test -f funny.was.run
-'
 
-test_expect_success 'rebase -i --continue handles merge strategy and options' '
-       rm -fr .git/rebase-* &&
-       git reset --hard commit-new-file-F2-on-topic-branch &&
-       test_commit "commit-new-file-F3-on-topic-branch-for-dash-i" F3 32 &&
-       test_when_finished "rm -fr test-bin funny.was.run funny.args" &&
-       mkdir test-bin &&
-       cat >test-bin/git-merge-funny <<-EOF &&
-       #!$SHELL_PATH
-       echo "\$@" >>funny.args
-       case "\$1" in --opt) ;; *) exit 2 ;; esac
-       case "\$2" in --foo) ;; *) exit 2 ;; esac
-       case "\$4" in --) ;; *) exit 2 ;; esac
-       shift 2 &&
-       >funny.was.run &&
-       exec git merge-recursive "\$@"
+       cat >expect <<-\EOF &&
+       [7]
+       [--option=arg with space]
+       [--op"tion\]
+       [--new
+       line ]
+       [--]
        EOF
-       chmod +x test-bin/git-merge-funny &&
+
+       rm -f actual &&
        (
                PATH=./test-bin:$PATH &&
-               test_must_fail git rebase -i -s funny -Xopt -Xfoo main topic
+               test_must_fail git rebase -s funny -X"option=arg with space" \
+                               -Xop\"tion\\ -X"new${LF}line " main topic
        ) &&
-       test -f funny.was.run &&
-       rm funny.was.run &&
+       test_cmp expect actual &&
+       rm actual &&
        echo "Resolved" >F2 &&
        git add F2 &&
        (
                PATH=./test-bin:$PATH &&
                git rebase --continue
        ) &&
-       test -f funny.was.run
+       test_cmp expect actual
 '
 
 test_expect_success 'rebase -r passes merge strategy options correctly' '
@@ -137,15 +115,23 @@ test_expect_success '--skip after failed fixup cleans commit message' '
        test_when_finished "test_might_fail git rebase --abort" &&
        git checkout -b with-conflicting-fixup &&
        test_commit wants-fixup &&
-       test_commit "fixup! wants-fixup" wants-fixup.t 1 wants-fixup-1 &&
-       test_commit "fixup! wants-fixup" wants-fixup.t 2 wants-fixup-2 &&
-       test_commit "fixup! wants-fixup" wants-fixup.t 3 wants-fixup-3 &&
+       test_commit "fixup 1" wants-fixup.t 1 wants-fixup-1 &&
+       test_commit "fixup 2" wants-fixup.t 2 wants-fixup-2 &&
+       test_commit "fixup 3" wants-fixup.t 3 wants-fixup-3 &&
        test_must_fail env FAKE_LINES="1 fixup 2 squash 4" \
                git rebase -i HEAD~4 &&
 
        : now there is a conflict, and comments in the commit message &&
-       git show HEAD >out &&
-       grep "fixup! wants-fixup" out &&
+       test_commit_message HEAD <<-\EOF &&
+       # This is a combination of 2 commits.
+       # This is the 1st commit message:
+
+       wants-fixup
+
+       # The commit message #2 will be skipped:
+
+       # fixup 1
+       EOF
 
        : skip and continue &&
        echo "cp \"\$1\" .git/copy.txt" | write_script copy-editor.sh &&
@@ -155,33 +141,49 @@ test_expect_success '--skip after failed fixup cleans commit message' '
        test_path_is_missing .git/copy.txt &&
 
        : now the comments in the commit message should have been cleaned up &&
-       git show HEAD >out &&
-       ! grep "fixup! wants-fixup" out &&
+       test_commit_message HEAD -m wants-fixup &&
 
        : now, let us ensure that "squash" is handled correctly &&
        git reset --hard wants-fixup-3 &&
-       test_must_fail env FAKE_LINES="1 squash 4 squash 2 squash 4" \
+       test_must_fail env FAKE_LINES="1 squash 2 squash 1 squash 3 squash 1" \
                git rebase -i HEAD~4 &&
 
-       : the first squash failed, but there are two more in the chain &&
+       : the second squash failed, but there are two more in the chain &&
        (test_set_editor "$PWD/copy-editor.sh" &&
         test_must_fail git rebase --skip) &&
 
        : not the final squash, no need to edit the commit message &&
        test_path_is_missing .git/copy.txt &&
 
-       : The first squash was skipped, therefore: &&
-       git show HEAD >out &&
-       test_i18ngrep "# This is a combination of 2 commits" out &&
-       test_i18ngrep "# This is the commit message #2:" out &&
+       : The first and third squashes succeeded, therefore: &&
+       test_commit_message HEAD <<-\EOF &&
+       # This is a combination of 3 commits.
+       # This is the 1st commit message:
+
+       wants-fixup
+
+       # This is the commit message #2:
+
+       fixup 1
+
+       # This is the commit message #3:
+
+       fixup 2
+       EOF
 
        (test_set_editor "$PWD/copy-editor.sh" && git rebase --skip) &&
-       git show HEAD >out &&
-       test_i18ngrep ! "# This is a combination" out &&
+       test_commit_message HEAD <<-\EOF &&
+       wants-fixup
+
+       fixup 1
+
+       fixup 2
+       EOF
 
        : Final squash failed, but there was still a squash &&
-       test_i18ngrep "# This is a combination of 2 commits" .git/copy.txt &&
-       test_i18ngrep "# This is the commit message #2:" .git/copy.txt
+       head -n1 .git/copy.txt >first-line &&
+       test_grep "# This is a combination of 3 commits" first-line &&
+       test_grep "# This is the commit message #3:" .git/copy.txt
 '
 
 test_expect_success 'setup rerere database' '
@@ -266,6 +268,24 @@ test_expect_success 'the todo command "break" works' '
        test_path_is_file execed
 '
 
+test_expect_success 'patch file is removed before break command' '
+       test_when_finished "git rebase --abort" &&
+       cat >todo <<-\EOF &&
+       pick commit-new-file-F2-on-topic-branch
+       break
+       EOF
+
+       (
+               set_replace_editor todo &&
+               test_must_fail git rebase -i --onto commit-new-file-F2 HEAD
+       ) &&
+       test_path_is_file .git/rebase-merge/patch &&
+       echo 22>F2 &&
+       git add F2 &&
+       git rebase --continue &&
+       test_path_is_missing .git/rebase-merge/patch
+'
+
 test_expect_success '--reschedule-failed-exec' '
        test_when_finished "git rebase --abort" &&
        test_must_fail git rebase -x false --reschedule-failed-exec HEAD^ &&
@@ -274,7 +294,7 @@ test_expect_success '--reschedule-failed-exec' '
        test_must_fail git -c rebase.rescheduleFailedExec=true \
                rebase -x false HEAD^ 2>err &&
        grep "^exec false" .git/rebase-merge/git-rebase-todo &&
-       test_i18ngrep "has been rescheduled" err
+       test_grep "has been rescheduled" err
 '
 
 test_expect_success 'rebase.rescheduleFailedExec only affects `rebase -i`' '
index 295040f2fe377971f0cec1eace32570840165d90..6c61f240cf9874bbdb0190739b66a1c0ed99289f 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git rebase - test patch id computation'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 scramble () {
@@ -43,15 +44,26 @@ test_expect_success 'setup: 500 lines' '
        git add newfile &&
        git commit -q -m "add small file" &&
 
-       git cherry-pick main >/dev/null 2>&1
-'
+       git cherry-pick main >/dev/null 2>&1 &&
+
+       git branch -f squashed main &&
+       git checkout -q -f squashed &&
+       git reset -q --soft HEAD~2 &&
+       git commit -q -m squashed &&
+
+       git branch -f mode main &&
+       git checkout -q -f mode &&
+       test_chmod +x file &&
+       git commit -q -a --amend &&
 
-test_expect_success 'setup attributes' '
-       echo "file binary" >.gitattributes
+       git branch -f modeother other &&
+       git checkout -q -f modeother &&
+       test_chmod +x file &&
+       git commit -q -a --amend
 '
 
 test_expect_success 'detect upstream patch' '
-       git checkout -q main &&
+       git checkout -q main^{} &&
        scramble file &&
        git add file &&
        git commit -q -m "change big file again" &&
@@ -61,14 +73,46 @@ test_expect_success 'detect upstream patch' '
        test_must_be_empty revs
 '
 
+test_expect_success 'detect upstream patch binary' '
+       echo "file binary" >.gitattributes &&
+       git checkout -q other^{} &&
+       git rebase main &&
+       git rev-list main...HEAD~ >revs &&
+       test_must_be_empty revs &&
+       test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'detect upstream patch modechange' '
+       git checkout -q modeother^{} &&
+       git rebase mode &&
+       git rev-list mode...HEAD~ >revs &&
+       test_must_be_empty revs
+'
+
 test_expect_success 'do not drop patch' '
-       git branch -f squashed main &&
-       git checkout -q -f squashed &&
-       git reset -q --soft HEAD~2 &&
-       git commit -q -m squashed &&
        git checkout -q other^{} &&
        test_must_fail git rebase squashed &&
-       git rebase --quit
+       test_when_finished "git rebase --abort"
+'
+
+test_expect_success 'do not drop patch binary' '
+       echo "file binary" >.gitattributes &&
+       git checkout -q other^{} &&
+       test_must_fail git rebase squashed &&
+       test_when_finished "git rebase --abort" &&
+       test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'do not drop patch modechange' '
+       git checkout -q modeother^{} &&
+       git rebase other &&
+       cat >expected <<-\EOF &&
+       diff --git a/file b/file
+       old mode 100644
+       new mode 100755
+       EOF
+       git diff HEAD~ >modediff &&
+       test_cmp expected modediff
 '
 
 test_done
index 6dabb05a2ad993d0d4d1f41c619f517038f7134f..b40f26250b7e3d8ce95b149589822bd57d84dbbe 100755 (executable)
@@ -25,11 +25,11 @@ test_expect_success 'setup' '
 '
 
 #
-# Rebase has lots of useful options like --whitepsace=fix, which are
-# actually all built in terms of flags to git-am.  Since neither
-# --merge nor --interactive (nor any options that imply those two) use
-# git-am, using them together will result in flags like --whitespace=fix
-# being ignored.  Make sure rebase warns the user and aborts instead.
+# Rebase has a couple options which are specific to the apply backend,
+# and several options which are specific to the merge backend.  Flags
+# from the different sets cannot work together, and we do not want to
+# just ignore one of the sets of flags.  Make sure rebase warns the
+# user and aborts instead.
 #
 
 test_rebase_am_only () {
@@ -50,6 +50,11 @@ test_rebase_am_only () {
                test_must_fail git rebase $opt --strategy-option=ours A
        "
 
+       test_expect_success "$opt incompatible with --autosquash" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --autosquash A
+       "
+
        test_expect_success "$opt incompatible with --interactive" "
                git checkout B^0 &&
                test_must_fail git rebase $opt --interactive A
@@ -60,9 +65,70 @@ test_rebase_am_only () {
                test_must_fail git rebase $opt --exec 'true' A
        "
 
+       test_expect_success "$opt incompatible with --keep-empty" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --keep-empty A
+       "
+
+       test_expect_success "$opt incompatible with --empty=..." "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --empty=ask A
+       "
+
+       test_expect_success "$opt incompatible with --no-reapply-cherry-picks" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --no-reapply-cherry-picks A
+       "
+
+       test_expect_success "$opt incompatible with --reapply-cherry-picks" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --reapply-cherry-picks A
+       "
+
+       test_expect_success "$opt incompatible with --rebase-merges" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --rebase-merges A
+       "
+
+       test_expect_success "$opt incompatible with --update-refs" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --update-refs A
+       "
+
+       test_expect_success "$opt incompatible with --root without --onto" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --root A
+       "
+
+       test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+               git checkout B^0 &&
+               test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+               grep -e --no-rebase-merges err
+       "
+
+       test_expect_success "$opt incompatible with rebase.updateRefs" "
+               git checkout B^0 &&
+               test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
+               grep -e --no-update-refs err
+       "
+
+       test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+               test_when_finished \"git reset --hard B^0\" &&
+               git checkout B^0 &&
+               git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+       "
+
+       test_expect_success "$opt okay with overridden rebase.updateRefs" "
+               test_when_finished \"git reset --hard B^0\" &&
+               git checkout B^0 &&
+               git -c rebase.updateRefs=true rebase --no-update-refs $opt A
+       "
 }
 
+# Check options which imply --apply
 test_rebase_am_only --whitespace=fix
 test_rebase_am_only -C4
+# Also check an explicit --apply
+test_rebase_am_only --apply
 
 test_done
index 4859bb8f722314df2fdb67aa51a4419009ce22ec..2fab703d615485020f0692bdc2d39748ba0f72d4 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rebase interactive with rewording'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
index 63acc1ea4dab43e5fc1f9eb26b031f49e3d9a432..a16428bdf54ab9157dfefb7cc9797e4c5dd86e1e 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rebase topology tests with merges'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
index 48b76f82325da49d521bf4603566ebcbc3653ddc..1b3e97c8755f3d8b41549e70cf67529298118182 100755 (executable)
@@ -74,9 +74,9 @@ test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
        test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-main main &&
        : first pick results in no changes &&
        git rebase --skip &&
-       verbose test "$(commit_message HEAD~2)" = "topic_4" &&
-       verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
-       verbose test "$(commit_message HEAD)" = "Empty commit"
+       test "$(commit_message HEAD~2)" = "topic_4" &&
+       test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
+       test "$(commit_message HEAD)" = "Empty commit"
 '
 
 test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
@@ -85,9 +85,9 @@ test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit'
        test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-main --root &&
        : first pick results in no changes &&
        git rebase --skip &&
-       verbose test "$(commit_message HEAD~2)" = "topic_4" &&
-       verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
-       verbose test "$(commit_message HEAD)" = "Empty commit"
+       test "$(commit_message HEAD~2)" = "topic_4" &&
+       test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
+       test "$(commit_message HEAD)" = "Empty commit"
 '
 
 test_done
index f6993b7e14d91617b337bfaefa717ec8591fb28f..e1b1e947647462f7cbc9fb2492b2cfe127056f1e 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git rebase --signoff
 This test runs git rebase --signoff and make sure that it works.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # A simple file to commit
index abd66f360213e18ad80b1c7bb2396b06d3e77b28..8e0d03969a2fec13fba015158b3380ec2ee37c42 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='rebase should reread the todo file if an exec modifies it'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
index f351701fec281aa6a88e9ee7185101639c8bbcd6..59b5d6b6f276c367862df92f847db37906715004 100755 (executable)
@@ -128,14 +128,41 @@ test_expect_success 'generate correct todo list' '
 '
 
 test_expect_success '`reset` refuses to overwrite untracked files' '
-       git checkout -b refuse-to-reset &&
+       git checkout B &&
        test_commit dont-overwrite-untracked &&
-       git checkout @{-1} &&
-       : >dont-overwrite-untracked.t &&
-       echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
+       cat >script-from-scratch <<-EOF &&
+       exec >dont-overwrite-untracked.t
+       pick $(git rev-parse B) B
+       reset refs/tags/dont-overwrite-untracked
+       pick $(git rev-parse C) C
+       exec cat .git/rebase-merge/done >actual
+       EOF
        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
-       test_must_fail git rebase -ir HEAD &&
-       git rebase --abort
+       test_must_fail git rebase -ir A &&
+       test_cmp_rev HEAD B &&
+       head -n3 script-from-scratch >expect &&
+       test_cmp expect .git/rebase-merge/done &&
+       rm dont-overwrite-untracked.t &&
+       git rebase --continue &&
+       tail -n3 script-from-scratch >>expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '`reset` rejects trees' '
+       test_when_finished "test_might_fail git rebase --abort" &&
+       test_must_fail env GIT_SEQUENCE_EDITOR="echo reset A^{tree} >" \
+               git rebase -i B C >out 2>err &&
+       grep "object .* is a tree" err &&
+       test_must_be_empty out
+'
+
+test_expect_success '`reset` only looks for labels under refs/rewritten/' '
+       test_when_finished "test_might_fail git rebase --abort" &&
+       git branch refs/rewritten/my-label A &&
+       test_must_fail env GIT_SEQUENCE_EDITOR="echo reset my-label >" \
+               git rebase -i B C >out 2>err &&
+       grep "could not resolve ${SQ}my-label${SQ}" err &&
+       test_must_be_empty out
 '
 
 test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
@@ -148,12 +175,16 @@ test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
        test_tick &&
        test_must_fail git rebase -ir HEAD &&
+       test_cmp_rev REBASE_HEAD H^0 &&
        grep "^merge -C .* G$" .git/rebase-merge/done &&
        grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
-       test_path_is_file .git/rebase-merge/patch &&
+       test_path_is_missing .git/rebase-merge/patch &&
+       echo changed >file1 &&
+       git add file1 &&
+       test_must_fail git rebase --continue 2>err &&
+       grep "error: you have staged changes in your working tree" err &&
 
        : fail because of merge conflict &&
-       rm G.t .git/rebase-merge/patch &&
        git reset --hard conflicting-G &&
        test_must_fail git rebase --continue &&
        ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
@@ -233,6 +264,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
        EOF
 '
 
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+       git checkout -b no-rebase-merges E &&
+       git rebase --rebase-merges --no-rebase-merges C &&
+       test_cmp_graph C.. <<-\EOF
+       * B
+       * D
+       o C
+       EOF
+'
+
 test_expect_success 'do not rebase cousins unless asked for' '
        git checkout -b cousins main &&
        before="$(git rev-parse --verify HEAD)" &&
@@ -251,6 +292,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
        EOF
 '
 
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+       test_config rebase.rebaseMerges rebase-cousins &&
+       git checkout -b config-rebase-cousins main &&
+       git rebase HEAD^ &&
+       test_cmp_graph HEAD^.. <<-\EOF
+       *   Merge the topic branch '\''onebranch'\''
+       |\
+       | * D
+       | * G
+       |/
+       o H
+       EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+       test_config rebase.rebaseMerges no-rebase-cousins &&
+       git checkout -b override-config-no-rebase-cousins E &&
+       git rebase --no-rebase-merges C &&
+       test_cmp_graph C.. <<-\EOF
+       * B
+       * D
+       o C
+       EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
+       test_config rebase.rebaseMerges rebase-cousins &&
+       git checkout -b override-config-rebase-cousins E &&
+       before="$(git rev-parse --verify HEAD)" &&
+       test_tick &&
+       git rebase --rebase-merges C &&
+       test_cmp_rev HEAD $before
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
        git worktree add wt &&
        cat >wt/script-from-scratch <<-\EOF &&
@@ -517,4 +592,23 @@ test_expect_success '--rebase-merges with message matched with onto label' '
        EOF
 '
 
+test_expect_success 'progress shows the correct total' '
+       git checkout -b progress H &&
+       git rebase --rebase-merges --force-rebase --verbose A 2> err &&
+       # Expecting "Rebasing (N/14)" here, no bogus total number
+       grep "^Rebasing.*/14.$" err >progress &&
+       test_line_count = 14 progress
+'
+
+test_expect_success 'truncate label names' '
+       commit=$(git commit-tree -p HEAD^ -p HEAD -m "0123456789 我 123" HEAD^{tree}) &&
+       git merge --ff-only $commit &&
+
+       done="$(git rev-parse --git-path rebase-merge/done)" &&
+       git -c rebase.maxLabelLength=14 rebase --rebase-merges -x "cp \"$done\" out" --root &&
+       grep "label 0123456789-我$" out &&
+       git -c rebase.maxLabelLength=13 rebase --rebase-merges -x "cp \"$done\" out" --root &&
+       grep "label 0123456789-$" out
+'
+
 test_done
index 1d0b15380edf3195b10e25a7d8d36f93330b95f5..0bb284d61dbfae3077c7408ba266ca596e61207d 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git rebase --fork-point test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # A---B---D---E    (main)
@@ -50,7 +51,7 @@ test_rebase () {
 
 test_rebase 'G F E D B A'
 test_rebase 'G F D B A' --onto D
-test_rebase 'G F B A' --keep-base
+test_rebase 'G F B A' --keep-base
 test_rebase 'G F C E D B A' --no-fork-point
 test_rebase 'G F C D B A' --no-fork-point --onto D
 test_rebase 'G F C B A' --no-fork-point --keep-base
@@ -83,7 +84,7 @@ test_expect_success 'git rebase --fork-point with ambigous refname' '
 
 test_expect_success '--fork-point and --root both given' '
        test_must_fail git rebase --fork-point --root 2>err &&
-       test_i18ngrep "cannot be used together" err
+       test_grep "cannot be used together" err
 '
 
 test_expect_success 'rebase.forkPoint set to false' '
index 5086e14c02207184bd7c048ee2bc2419ad4d1b34..7f1a5dd3deb10bef921654ef767a7cd53347bfd8 100755 (executable)
@@ -8,6 +8,7 @@ test_description='ensure rebase fast-forwards commits when possible'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 05df964670f49d011bf35b414177a35f812a4c4e..c8172b0852233b6f370eab47c6cc4877db54d8f7 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rebase across mode change'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index c023fefd681ba62b8296925b2051cffe365e63ac..7929e2e2e3a8fc1240af3991f85172c16dcbd2cf 100755 (executable)
@@ -14,27 +14,13 @@ to the "fixup" command that works with "fixup!", "fixup -C" works with
 "amend!" upon --autosquash.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
 EMPTY=""
 
-# test_commit_message <rev> -m <msg>
-# test_commit_message <rev> <path>
-# Verify that the commit message of <rev> matches
-# <msg> or the content of <path>.
-test_commit_message () {
-       git show --no-patch --pretty=format:%B "$1" >actual &&
-       case "$2" in
-       -m)
-               echo "$3" >expect &&
-               test_cmp expect actual ;;
-       *)
-               test_cmp "$2" actual ;;
-       esac
-}
-
 get_author () {
        rev="$1" &&
        git log -1 --pretty=format:"%an %ae %at" "$rev"
@@ -50,6 +36,7 @@ test_expect_success 'setup' '
        body
        EOF
 
+       test_commit initial &&
        test_commit A A &&
        test_commit B B &&
        get_author HEAD >expected-author &&
@@ -208,4 +195,29 @@ test_expect_success 'fixup -C works upon --autosquash with amend!' '
                actual-squash-message
 '
 
+test_expect_success 'fixup -[Cc]<commit> works' '
+       test_when_finished "test_might_fail git rebase --abort" &&
+       cat >todo <<-\EOF &&
+       pick A
+       fixup -CA1
+       pick B
+       fixup -cA2
+       EOF
+       (
+               set_replace_editor todo &&
+               FAKE_COMMIT_MESSAGE="edited and fixed up" \
+                       git rebase -i initial initial
+       ) &&
+       git log --pretty=format:%B initial.. >actual &&
+       cat >expect <<-EOF &&
+       edited and fixed up
+       $EMPTY
+       new subject
+       $EMPTY
+       new
+       body
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index b92a3ce46b8cc044e44adfa9cc807c2d2ddb4af2..c614c4f2e4ba285ba7ffc6b7b4384b69f0b9a851 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rebase behavior when on-disk files are broken'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'set up conflicting branches' '
index 0458a58b4b58ccb5a7fb5af378a5e2908126c111..78c3eac54b599ef9cded9a042f4346621798b645 100755 (executable)
@@ -16,46 +16,43 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 GIT_AUTHOR_EMAIL=bogus_email_address
 export GIT_AUTHOR_EMAIL
 
-test_expect_success \
-    'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
-    'echo First > A &&
-     git update-index --add A &&
-     test_tick &&
-     git commit -m "Add A." &&
-
-     git checkout -b my-topic-branch &&
-
-     echo Second > B &&
-     git update-index --add B &&
-     test_tick &&
-     git commit -m "Add B." &&
-
-     echo AnotherSecond > C &&
-     git update-index --add C &&
-     test_tick &&
-     git commit -m "Add C." &&
-
-     git checkout -f main &&
-     rm -f B C &&
-
-     echo Third >> A &&
-     git update-index A &&
-     test_tick &&
-     git commit -m "Modify A." &&
-
-     expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* + .*"
+test_expect_success 'prepare repository with topic branch, and check cherry finds the 2 patches from there' '
+       echo First > A &&
+       git update-index --add A &&
+       test_tick &&
+       git commit -m "Add A." &&
+
+       git checkout -b my-topic-branch &&
+
+       echo Second > B &&
+       git update-index --add B &&
+       test_tick &&
+       git commit -m "Add B." &&
+
+       echo AnotherSecond > C &&
+       git update-index --add C &&
+       test_tick &&
+       git commit -m "Add C." &&
+
+       git checkout -f main &&
+       rm -f B C &&
+
+       echo Third >> A &&
+       git update-index A &&
+       test_tick &&
+       git commit -m "Modify A." &&
+
+       expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* + .*"
 '
 
-test_expect_success \
-    'check that cherry with limit returns only the top patch'\
-    'expr "$(echo $(git cherry main my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
+test_expect_success 'check that cherry with limit returns only the top patch' '
+       expr "$(echo $(git cherry main my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
 '
 
-test_expect_success \
-    'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' \
-    'git cherry-pick my-topic-branch^0 &&
-     echo $(git cherry main my-topic-branch) &&
-     expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* - .*"
+test_expect_success 'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' '
+       git cherry-pick my-topic-branch^0 &&
+       echo $(git cherry main my-topic-branch) &&
+       expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* - .*"
 '
 
 test_expect_success 'cherry ignores whitespace' '
index 1f4cfc37449186c52a36cbe19f2631df759643e3..aeab689a98d00919e0473380fc9bf40497b32c80 100755 (executable)
@@ -1,18 +1,11 @@
 #!/bin/sh
 
-test_description='test cherry-pick and revert with renames
-
-  --
-   + rename2: renames oops to opos
-  +  rename1: renames oops to spoo
-  +  added:   adds extra line to oops
-  ++ initial: has lines in oops
-
-'
+test_description='miscellaneous basic tests for cherry-pick and revert'
 
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -50,7 +43,7 @@ test_expect_success 'cherry-pick --nonsense' '
        git diff --exit-code HEAD &&
        test_must_fail git cherry-pick --nonsense 2>msg &&
        git diff --exit-code HEAD "$pos" &&
-       test_i18ngrep "[Uu]sage:" msg
+       test_grep "[Uu]sage:" msg
 '
 
 test_expect_success 'revert --nonsense' '
@@ -59,9 +52,17 @@ test_expect_success 'revert --nonsense' '
        git diff --exit-code HEAD &&
        test_must_fail git revert --nonsense 2>msg &&
        git diff --exit-code HEAD "$pos" &&
-       test_i18ngrep "[Uu]sage:" msg
+       test_grep "[Uu]sage:" msg
 '
 
+# the following two test cherry-pick and revert with renames
+#
+# --
+#  + rename2: renames oops to opos
+# +  rename1: renames oops to spoo
+# +  added:   adds extra line to oops
+# ++ initial: has lines in oops
+
 test_expect_success 'cherry-pick after renaming branch' '
 
        git checkout rename2 &&
@@ -98,7 +99,7 @@ test_expect_success 'revert forbidden on dirty working tree' '
        echo content >extra_file &&
        git add extra_file &&
        test_must_fail git revert HEAD 2>errors &&
-       test_i18ngrep "your local changes would be overwritten by " errors
+       test_grep "your local changes would be overwritten by " errors
 
 '
 
@@ -175,6 +176,29 @@ test_expect_success 'advice from failed revert' '
        test_cmp expected actual
 '
 
+test_expect_subject () {
+       echo "$1" >expect &&
+       git log -1 --pretty=%s >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'titles of fresh reverts' '
+       test_commit --no-tag A file1 &&
+       test_commit --no-tag B file1 &&
+       git revert --no-edit HEAD &&
+       test_expect_subject "Revert \"B\"" &&
+       git revert --no-edit HEAD &&
+       test_expect_subject "Reapply \"B\"" &&
+       git revert --no-edit HEAD &&
+       test_expect_subject "Revert \"Reapply \"B\"\""
+'
+
+test_expect_success 'title of legacy double revert' '
+       test_commit --no-tag "Revert \"Revert \"B\"\"" file1 &&
+       git revert --no-edit HEAD &&
+       test_expect_subject "Revert \"Revert \"Revert \"B\"\"\""
+'
+
 test_expect_success 'identification of reverted commit (default)' '
        test_commit to-ident &&
        test_when_finished "git reset --hard to-ident" &&
index 5495eacfec11a904ba24d69439cb1b98b5a353ec..1b2c0d6aca64e05a85ffd6568be17bb5897a297e 100755 (executable)
@@ -11,6 +11,7 @@ test_description='cherry picking and reverting a merge
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 95fe4feaeee98f6432b39ee3a4817f05da5d649b..76d393dc8a307ebef13dfab5e4eb3d88a7a7cd69 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test cherry-picking (and reverting) a root commit'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 7e11bd4a4c5c41c9e326244f598bdc9bb4e4e3ba..b71bad17b853c566caac5828c19453da9c862647 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test cherry-picking with --ff option'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index f32799e04633fd831293d5b64106f9c856d3d407..c88d597b12682ccc611699f43c6451e35483fe6a 100755 (executable)
@@ -177,7 +177,7 @@ test_expect_success 'partial commit of cherry-pick fails' '
        git add foo &&
        test_must_fail git commit foo 2>err &&
 
-       test_i18ngrep "cannot do a partial commit during a cherry-pick." err
+       test_grep "cannot do a partial commit during a cherry-pick." err
 '
 
 test_expect_success 'commit --amend of cherry-pick fails' '
@@ -188,7 +188,7 @@ test_expect_success 'commit --amend of cherry-pick fails' '
        git add foo &&
        test_must_fail git commit --amend 2>err &&
 
-       test_i18ngrep "in the middle of a cherry-pick -- cannot amend." err
+       test_grep "in the middle of a cherry-pick -- cannot amend." err
 '
 
 test_expect_success 'successful final commit clears cherry-pick state' '
@@ -498,7 +498,7 @@ test_expect_success \
 test_expect_success 'failed cherry-pick does not forget -s' '
        pristine_detach initial &&
        test_must_fail git cherry-pick -s picked &&
-       test_i18ngrep -e "Signed-off-by" .git/MERGE_MSG
+       test_grep -e "Signed-off-by" .git/MERGE_MSG
 '
 
 test_expect_success 'commit after failed cherry-pick does not add duplicated -s' '
@@ -563,7 +563,7 @@ test_expect_success 'cherry-pick preserves sparse-checkout' '
        echo /unrelated >.git/info/sparse-checkout &&
        git read-tree --reset -u HEAD &&
        test_must_fail git cherry-pick -Xours picked>actual &&
-       test_i18ngrep ! "Changes not staged for commit:" actual
+       test_grep ! "Changes not staged for commit:" actual
 '
 
 test_expect_success 'cherry-pick --continue remembers --keep-redundant-commits' '
index 3b0fa66c33da5857012b56d465c5ffad77f61be4..72020a51c4375fd793af7e0129443cbf8d11d929 100755 (executable)
@@ -154,7 +154,7 @@ test_expect_success 'skip "empty" commit' '
        pristine_detach picked &&
        test_commit dummy foo d &&
        test_must_fail git cherry-pick anotherpick 2>err &&
-       test_i18ngrep "git cherry-pick --skip" err &&
+       test_grep "git cherry-pick --skip" err &&
        git cherry-pick --skip &&
        test_cmp_rev dummy HEAD
 '
@@ -314,7 +314,7 @@ test_expect_success '--abort does not unsafely change HEAD' '
        git reset --hard base &&
        test_must_fail git cherry-pick picked anotherpick &&
        git cherry-pick --abort 2>actual &&
-       test_i18ngrep "You seem to have moved HEAD" actual &&
+       test_grep "You seem to have moved HEAD" actual &&
        test_cmp_rev base HEAD
 '
 
@@ -520,7 +520,7 @@ test_expect_success '--continue asks for help after resolving patch to nil' '
        test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
        git checkout HEAD -- unrelated &&
        test_must_fail git cherry-pick --continue 2>msg &&
-       test_i18ngrep "The previous cherry-pick is now empty" msg
+       test_grep "The previous cherry-pick is now empty" msg
 '
 
 test_expect_success 'follow advice and skip nil patch' '
index 84a587daf3af0356244c58c6472e202875679200..dd5d92ef302124aeb9d6fe02642489875a9e1b22 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test cherry-pick -x and -s'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 pristine_detach () {
index 0e8afe49ed100cb3c1b89f8c33b639ab8ac1cabb..98259e2adaa9dba9fa6bbcb79e50948e69a6b9e6 100755 (executable)
@@ -276,7 +276,7 @@ test_expect_success 'Resolving by removal is not a warning-worthy event' '
        blob=$(echo blob | git hash-object -w --stdin) &&
        printf "100644 $blob %d\tblob\n" 1 2 3 | git update-index --index-info &&
        git rm blob >msg 2>&1 &&
-       test_i18ngrep ! "needs merge" msg &&
+       test_grep ! "needs merge" msg &&
        test_must_fail git ls-files -s --error-unmatch blob
 '
 
@@ -631,7 +631,7 @@ test_expect_success 'rm of a populated submodule with a .git directory migrates
        test_path_is_missing submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_file_not_empty actual &&
-       test_i18ngrep Migrating output.err
+       test_grep Migrating output.err
 '
 
 cat >expect.deepmodified <<EOF
@@ -722,7 +722,7 @@ test_expect_success "rm absorbs submodule's nested .git directory" '
        test_path_is_missing submod/subsubmod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_file_not_empty actual &&
-       test_i18ngrep Migrating output.err
+       test_grep Migrating output.err
 '
 
 test_expect_success 'checking out a commit after submodule removal needs manual updates' '
@@ -731,7 +731,7 @@ test_expect_success 'checking out a commit after submodule removal needs manual
        git submodule update &&
        git checkout -q HEAD^ &&
        git checkout -q main 2>actual &&
-       test_i18ngrep "^warning: unable to rmdir '\''submod'\'':" actual &&
+       test_grep "^warning: unable to rmdir '\''submod'\'':" actual &&
        git status -s submod >actual &&
        echo "?? submod/" >expected &&
        test_cmp expected actual &&
index a2a0c820fe38a976b963570453df17a089599f9d..7cef12981c4be0b611f70fe021d5960f1ad4e5a8 100755 (executable)
@@ -67,14 +67,14 @@ test_expect_success 'error conditions' '
        echo fileA.t >list &&
 
        test_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+       test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git rm --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+       test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        >empty_list &&
        test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
-       test_i18ngrep -e "No pathspec was given. Which files should I remove?" err
+       test_grep -e "No pathspec was given. Which files should I remove?" err
 '
 
 test_done
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
new file mode 100755 (executable)
index 0000000..3896702
--- /dev/null
@@ -0,0 +1,198 @@
+#!/bin/sh
+
+test_description='basic git replay tests'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+
+test_expect_success 'setup' '
+       test_commit A &&
+       test_commit B &&
+
+       git switch -c topic1 &&
+       test_commit C &&
+       git switch -c topic2 &&
+       test_commit D &&
+       test_commit E &&
+       git switch topic1 &&
+       test_commit F &&
+       git switch -c topic3 &&
+       test_commit G &&
+       test_commit H &&
+       git switch -c topic4 main &&
+       test_commit I &&
+       test_commit J &&
+
+       git switch -c next main &&
+       test_commit K &&
+       git merge -m "Merge topic1" topic1 &&
+       git merge -m "Merge topic2" topic2 &&
+       git merge -m "Merge topic3" topic3 &&
+       >evil &&
+       git add evil &&
+       git commit --amend &&
+       git merge -m "Merge topic4" topic4 &&
+
+       git switch main &&
+       test_commit L &&
+       test_commit M &&
+
+       git switch -c conflict B &&
+       test_commit C.conflict C.t conflict
+'
+
+test_expect_success 'setup bare' '
+       git clone --bare . bare
+'
+
+test_expect_success 'using replay to rebase two branches, one on top of other' '
+       git replay --onto main topic1..topic2 >result &&
+
+       test_line_count = 1 result &&
+
+       git log --format=%s $(cut -f 3 -d " " result) >actual &&
+       test_write_lines E D M L B A >expect &&
+       test_cmp expect actual &&
+
+       printf "update refs/heads/topic2 " >expect &&
+       printf "%s " $(cut -f 3 -d " " result) >>expect &&
+       git rev-parse topic2 >>expect &&
+
+       test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase two branches, one on top of other' '
+       git -C bare replay --onto main topic1..topic2 >result-bare &&
+       test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase with a conflict' '
+       test_expect_code 1 git replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay on bare repo to rebase with a conflict' '
+       test_expect_code 1 git -C bare replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay to perform basic cherry-pick' '
+       # The differences between this test and previous ones are:
+       #   --advance vs --onto
+       # 2nd field of result is refs/heads/main vs. refs/heads/topic2
+       # 4th field of result is hash for main instead of hash for topic2
+
+       git replay --advance main topic1..topic2 >result &&
+
+       test_line_count = 1 result &&
+
+       git log --format=%s $(cut -f 3 -d " " result) >actual &&
+       test_write_lines E D M L B A >expect &&
+       test_cmp expect actual &&
+
+       printf "update refs/heads/main " >expect &&
+       printf "%s " $(cut -f 3 -d " " result) >>expect &&
+       git rev-parse main >>expect &&
+
+       test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to perform basic cherry-pick' '
+       git -C bare replay --advance main topic1..topic2 >result-bare &&
+       test_cmp expect result-bare
+'
+
+test_expect_success 'replay on bare repo fails with both --advance and --onto' '
+       test_must_fail git -C bare replay --advance main --onto main topic1..topic2 >result-bare
+'
+
+test_expect_success 'replay fails when both --advance and --onto are omitted' '
+       test_must_fail git replay topic1..topic2 >result
+'
+
+test_expect_success 'using replay to also rebase a contained branch' '
+       git replay --contained --onto main main..topic3 >result &&
+
+       test_line_count = 2 result &&
+       cut -f 3 -d " " result >new-branch-tips &&
+
+       git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+       test_write_lines F C M L B A >expect &&
+       test_cmp expect actual &&
+
+       git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+       test_write_lines H G F C M L B A >expect &&
+       test_cmp expect actual &&
+
+       printf "update refs/heads/topic1 " >expect &&
+       printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+       git rev-parse topic1 >>expect &&
+       printf "update refs/heads/topic3 " >>expect &&
+       printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+       git rev-parse topic3 >>expect &&
+
+       test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to also rebase a contained branch' '
+       git -C bare replay --contained --onto main main..topic3 >result-bare &&
+       test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase multiple divergent branches' '
+       git replay --onto main ^topic1 topic2 topic4 >result &&
+
+       test_line_count = 2 result &&
+       cut -f 3 -d " " result >new-branch-tips &&
+
+       git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+       test_write_lines E D M L B A >expect &&
+       test_cmp expect actual &&
+
+       git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+       test_write_lines J I M L B A >expect &&
+       test_cmp expect actual &&
+
+       printf "update refs/heads/topic2 " >expect &&
+       printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+       git rev-parse topic2 >>expect &&
+       printf "update refs/heads/topic4 " >>expect &&
+       printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+       git rev-parse topic4 >>expect &&
+
+       test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase multiple divergent branches, including contained ones' '
+       git -C bare replay --contained --onto main ^main topic2 topic3 topic4 >result &&
+
+       test_line_count = 4 result &&
+       cut -f 3 -d " " result >new-branch-tips &&
+
+       >expect &&
+       for i in 2 1 3 4
+       do
+               printf "update refs/heads/topic$i " >>expect &&
+               printf "%s " $(grep topic$i result | cut -f 3 -d " ") >>expect &&
+               git -C bare rev-parse topic$i >>expect || return 1
+       done &&
+
+       test_cmp expect result &&
+
+       test_write_lines F C M L B A >expect1 &&
+       test_write_lines E D C M L B A >expect2 &&
+       test_write_lines H G F C M L B A >expect3 &&
+       test_write_lines J I M L B A >expect4 &&
+
+       for i in 1 2 3 4
+       do
+               git -C bare log --format=%s $(grep topic$i result | cut -f 3 -d " ") >actual &&
+               test_cmp expect$i actual || return 1
+       done
+'
+
+test_done
index 8689b48589c0d515b9d15b86e4e00807fd26f566..f23d39f0d52ec6f5035acfb029550babc67859da 100755 (executable)
@@ -24,17 +24,17 @@ test_mode_in_index () {
        esac
 }
 
-test_expect_success \
-    'Test of git add' \
-    'touch foo && git add foo'
+test_expect_success 'Test of git add' '
+       touch foo && git add foo
+'
 
-test_expect_success \
-    'Post-check that foo is in the index' \
-    'git ls-files foo | grep foo'
+test_expect_success 'Post-check that foo is in the index' '
+       git ls-files foo | grep foo
+'
 
-test_expect_success \
-    'Test that "git add -- -q" works' \
-    'touch -- -q && git add -- -q'
+test_expect_success 'Test that "git add -- -q" works' '
+       touch -- -q && git add -- -q
+'
 
 BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
 
@@ -106,24 +106,32 @@ test_expect_success '.gitignore test setup' '
 
 test_expect_success '.gitignore is honored' '
        git add . &&
-       ! (git ls-files | grep "\\.ig")
+       git ls-files >files &&
+       sed -n "/\\.ig/p" <files >actual &&
+       test_must_be_empty actual
 '
 
 test_expect_success 'error out when attempting to add ignored ones without -f' '
        test_must_fail git add a.?? &&
-       ! (git ls-files | grep "\\.ig")
+       git ls-files >files &&
+       sed -n "/\\.ig/p" <files >actual &&
+       test_must_be_empty actual
 '
 
 test_expect_success 'error out when attempting to add ignored ones without -f' '
        test_must_fail git add d.?? &&
-       ! (git ls-files | grep "\\.ig")
+       git ls-files >files &&
+       sed -n "/\\.ig/p" <files >actual &&
+       test_must_be_empty actual
 '
 
 test_expect_success 'error out when attempting to add ignored ones but add others' '
        touch a.if &&
        test_must_fail git add a.?? &&
-       ! (git ls-files | grep "\\.ig") &&
-       (git ls-files | grep a.if)
+       git ls-files >files &&
+       sed -n "/\\.ig/p" <files >actual &&
+       test_must_be_empty actual &&
+       grep a.if files
 '
 
 test_expect_success 'add ignored ones with -f' '
@@ -276,14 +284,14 @@ test_expect_success POSIXPERM,SANITY 'git add (add.ignore-errors = false)' '
 rm -f foo2
 
 test_expect_success POSIXPERM,SANITY '--no-ignore-errors overrides config' '
-       git config add.ignore-errors 1 &&
-       git reset --hard &&
-       date >foo1 &&
-       date >foo2 &&
-       chmod 0 foo2 &&
-       test_must_fail git add --verbose --no-ignore-errors . &&
-       ! ( git ls-files foo1 | grep foo1 ) &&
-       git config add.ignore-errors 0
+       git config add.ignore-errors 1 &&
+       git reset --hard &&
+       date >foo1 &&
+       date >foo2 &&
+       chmod 0 foo2 &&
+       test_must_fail git add --verbose --no-ignore-errors . &&
+       ! ( git ls-files foo1 | grep foo1 ) &&
+       git config add.ignore-errors 0
 '
 rm -f foo2
 
@@ -291,7 +299,7 @@ test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
        git reset --hard &&
        touch fo\[ou\]bar foobar &&
        git add '\''fo\[ou\]bar'\'' &&
-       git ls-files fo\[ou\]bar | fgrep fo\[ou\]bar &&
+       git ls-files fo\[ou\]bar | grep -F fo\[ou\]bar &&
        ! ( git ls-files foobar | grep foobar )
 '
 
@@ -430,7 +438,7 @@ test_expect_success 'git add --chmod fails with non regular files (but updates t
        test_ln_s_add foo foo3 &&
        touch foo4 &&
        test_must_fail git add --chmod=+x foo3 foo4 2>stderr &&
-       test_i18ngrep "cannot chmod +x .foo3." stderr &&
+       test_grep "cannot chmod +x .foo3." stderr &&
        test_mode_in_index 120000 foo3 &&
        test_mode_in_index 100755 foo4
 '
@@ -447,12 +455,12 @@ test_expect_success 'git add --chmod --dry-run reports error for non regular fil
        git reset --hard &&
        test_ln_s_add foo foo4 &&
        test_must_fail git add --chmod=+x --dry-run foo4 2>stderr &&
-       test_i18ngrep "cannot chmod +x .foo4." stderr
+       test_grep "cannot chmod +x .foo4." stderr
 '
 
 test_expect_success 'git add --chmod --dry-run reports error for unmatched pathspec' '
        test_must_fail git add --chmod=+x --dry-run nonexistent 2>stderr &&
-       test_i18ngrep "pathspec .nonexistent. did not match any files" stderr
+       test_grep "pathspec .nonexistent. did not match any files" stderr
 '
 
 test_expect_success 'no file status change if no pathspec is given' '
index 5841f280fb2d4c3185e0a42b9d9a894d72cc4e79..0b5339ac6ca8248582ce723e3d552a8d4513e294 100755 (executable)
@@ -7,12 +7,6 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
-if test_have_prereq !ADD_I_USE_BUILTIN,!PERL
-then
-       skip_all='skipping add -i (scripted) tests, perl not available'
-       test_done
-fi
-
 diff_cmp () {
        for x
        do
@@ -46,6 +40,21 @@ force_color () {
        )
 }
 
+test_expect_success 'warn about add.interactive.useBuiltin' '
+       cat >expect <<-\EOF &&
+       warning: the add.interactive.useBuiltin setting has been removed!
+       See its entry in '\''git help config'\'' for details.
+       No changes.
+       EOF
+
+       for v in = =true =false
+       do
+               git -c "add.interactive.useBuiltin$v" add -p >out 2>actual &&
+               test_must_be_empty out &&
+               test_cmp expect actual || return 1
+       done
+'
+
 test_expect_success 'setup (initial)' '
        echo content >file &&
        git add file &&
@@ -296,9 +305,11 @@ test_expect_success FILEMODE 'stage mode and hunk' '
        echo content >>file &&
        chmod +x file &&
        printf "y\\ny\\n" | git add -p &&
-       git diff --cached file | grep "new mode" &&
-       git diff --cached file | grep "+content" &&
-       test -z "$(git diff file)"
+       git diff --cached file >out &&
+       grep "new mode" out &&
+       grep "+content" out &&
+       git diff file >out &&
+       test_must_be_empty out
 '
 
 # end of tests disabled when filemode is not usable
@@ -324,12 +335,12 @@ test_expect_success 'different prompts for mode change/deleted' '
 test_expect_success 'correct message when there is nothing to do' '
        git reset --hard &&
        git add -p 2>err &&
-       test_i18ngrep "No changes" err &&
+       test_grep "No changes" err &&
        printf "\\0123" >binary &&
        git add binary &&
        printf "\\0abc" >binary &&
        git add -p 2>err &&
-       test_i18ngrep "Only binary files changed" err
+       test_grep "Only binary files changed" err
 '
 
 test_expect_success 'setup again' '
@@ -486,7 +497,7 @@ test_expect_success 'adding an empty file' '
 
                echo y | git checkout -p added-file -- >actual &&
                test_path_is_file empty &&
-               test_i18ngrep "Apply addition to index and worktree" actual
+               test_grep "Apply addition to index and worktree" actual
        )
 '
 
@@ -547,15 +558,7 @@ test_expect_success 'split hunk "add -p (edit)"' '
        ! grep "^+15" actual
 '
 
-test_expect_success 'setup ADD_I_USE_BUILTIN check' '
-       result=success &&
-       if ! test_have_prereq ADD_I_USE_BUILTIN
-       then
-               result=failure
-       fi
-'
-
-test_expect_$result 'split hunk "add -p (no, yes, edit)"' '
+test_expect_success 'split hunk "add -p (no, yes, edit)"' '
        test_write_lines 5 10 20 21 30 31 40 50 60 >test &&
        git reset &&
        # test sequence is s(plit), n(o), y(es), e(dit)
@@ -579,7 +582,7 @@ test_expect_success 'split hunk with incomplete line at end' '
        test_must_fail git grep --cached before
 '
 
-test_expect_$result 'edit, adding lines to the first hunk' '
+test_expect_success 'edit, adding lines to the first hunk' '
        test_write_lines 10 11 20 30 40 50 51 60 >test &&
        git reset &&
        tr _ " " >patch <<-EOF &&
@@ -731,6 +734,44 @@ test_expect_success 'colors can be overridden' '
        test_cmp expect actual
 '
 
+test_expect_success 'brackets appear without color' '
+       git reset --hard &&
+       test_when_finished "git rm -f bracket-test" &&
+       test_write_lines context old more-context >bracket-test &&
+       git add bracket-test &&
+       test_write_lines context new more-context another-one >bracket-test &&
+
+       test_write_lines quit >input &&
+       git add -i >actual <input &&
+
+       sed "s/^|//" >expect <<-\EOF &&
+       |           staged     unstaged path
+       |  1:        +3/-0        +2/-1 bracket-test
+       |
+       |*** Commands ***
+       |  1: [s]tatus    2: [u]pdate     3: [r]evert     4: [a]dd untracked
+       |  5: [p]atch     6: [d]iff       7: [q]uit       8: [h]elp
+       |What now> Bye.
+       EOF
+
+       test_cmp expect actual
+'
+
+test_expect_success 'colors can be skipped with color.ui=false' '
+       git reset --hard &&
+       test_when_finished "git rm -f color-test" &&
+       test_write_lines context old more-context >color-test &&
+       git add color-test &&
+       test_write_lines context new more-context another-one >color-test &&
+
+       test_write_lines help quit >input &&
+       force_color git \
+               -c color.ui=false \
+               add -i >actual.raw <input &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp actual.raw actual
+'
+
 test_expect_success 'colorized diffs respect diff.wsErrorHighlight' '
        git reset --hard &&
 
@@ -797,7 +838,7 @@ test_expect_success 'diff.algorithm is passed to `git diff-files`' '
        git add file &&
        echo changed >file &&
        test_must_fail git -c diff.algorithm=bogus add -p 2>err &&
-       test_i18ngrep "error: option diff-algorithm accepts " err
+       test_grep "error: option diff-algorithm accepts " err
 '
 
 test_expect_success 'patch-mode via -i prompts for files' '
@@ -1068,4 +1109,25 @@ test_expect_success 'show help from add--helper' '
        test_cmp expect actual
 '
 
+test_expect_success 'reset -p with unmerged files' '
+       test_when_finished "git checkout --force main" &&
+       test_commit one conflict &&
+       git checkout -B side HEAD^ &&
+       test_commit two conflict &&
+       test_must_fail git merge one &&
+
+       # this is a noop with only an unmerged entry
+       git reset -p &&
+
+       # add files that sort before and after unmerged entry
+       echo a >a &&
+       echo z >z &&
+       git add a z &&
+
+       # confirm that we can reset those files
+       printf "%s\n" y y | git reset -p &&
+       git diff-index --cached --diff-filter=u HEAD >staged &&
+       test_must_be_empty staged
+'
+
 test_done
index a1801a8cbd4185f80253b019eb40a2c6a378d7ab..82bfb2fd2aca9eae78af33fa67620e76458b3ac6 100755 (executable)
@@ -100,7 +100,7 @@ EOF
 
 echo "#!$SHELL_PATH" >fake-editor.sh
 cat >> fake-editor.sh <<\EOF
-egrep -v '^index' "$1" >orig-patch &&
+grep -E -v '^index' "$1" >orig-patch &&
 mv -f patch "$1"
 EOF
 
index 4e6b5177c9329b11ee9046d75e6cf5b0dbf358df..3aa59f6f639b5bfc79ab048f594cb2512755be73 100755 (executable)
@@ -138,23 +138,23 @@ test_expect_success 'error conditions' '
        >empty_list &&
 
        test_must_fail git add --pathspec-from-file=list --interactive 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
        test_must_fail git add --pathspec-from-file=list --patch 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
        test_must_fail git add --pathspec-from-file=list --edit 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--edit. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--edit. cannot be used together" err &&
 
        test_must_fail git add --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+       test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git add --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+       test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        # This case succeeds, but still prints to stderr
        git add --pathspec-from-file=empty_list 2>err &&
-       test_i18ngrep -e "Nothing specified, nothing added." err
+       test_grep -e "Nothing specified, nothing added." err
 '
 
 test_done
index e3cf0ffbe59c449b218cafedd74bd3cf07e82ff1..d3e428ff46eb5c99d98a8fe27b54cf98d6d2a936 100755 (executable)
@@ -4,6 +4,7 @@
 
 test_description='git mktag: tag object verify test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 ###########################################################
index bfab245eb32b07d9a30810f9b388dcf9c629bdf3..f27d09cfd9439ae31ec707fe738356bca88064f6 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success 'UTF-8 invalid characters refused' '
        printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
                >"$HOME/invalid" &&
        git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-       test_i18ngrep "did not conform" "$HOME"/stderr
+       test_grep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 overlong sequences rejected' '
@@ -55,7 +55,7 @@ test_expect_success 'UTF-8 overlong sequences rejected' '
        printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
                >"$HOME/invalid" &&
        git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-       test_i18ngrep "did not conform" "$HOME"/stderr
+       test_grep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 non-characters refused' '
@@ -64,7 +64,7 @@ test_expect_success 'UTF-8 non-characters refused' '
        printf "Commit message\n\nNon-character:\364\217\277\276\n" \
                >"$HOME/invalid" &&
        git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-       test_i18ngrep "did not conform" "$HOME"/stderr
+       test_grep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 non-characters refused' '
@@ -73,7 +73,7 @@ test_expect_success 'UTF-8 non-characters refused' '
        printf "Commit message\n\nNon-character:\357\267\220\n" \
                >"$HOME/invalid" &&
        git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-       test_i18ngrep "did not conform" "$HOME"/stderr
+       test_grep "did not conform" "$HOME"/stderr
 '
 
 for H in ISO8859-1 eucJP ISO-2022-JP
index 4f16a735d99e835b3b812130659ba609b1bfc284..4b37f78829819ff023db64b026fc6b8ea4061deb 100755 (executable)
@@ -298,7 +298,7 @@ test_expect_success 'am --no-utf8 (U/L)' '
 
        # commit-tree will warn that the commit message does not contain valid UTF-8
        # as mailinfo did not convert it
-       test_i18ngrep "did not conform" err &&
+       test_grep "did not conform" err &&
 
        check_encoding 2
 '
index 376cc8f4ab8429b0488ad23b0f9731c9af237124..34faeac3f1cde42633fc5a8b8b67cbf14d57edaf 100755 (executable)
@@ -395,7 +395,7 @@ test_expect_success 'stash --staged' '
 
 test_expect_success 'dont assume push with non-option args' '
        test_must_fail git stash -q drop 2>err &&
-       test_i18ngrep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
+       test_grep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
 '
 
 test_expect_success 'stash --invalid-option' '
@@ -596,7 +596,7 @@ test_expect_success 'giving too many ref arguments does not modify files' '
        for type in apply pop "branch stash-branch"
        do
                test_must_fail git stash $type stash@{0} stash@{1} 2>err &&
-               test_i18ngrep "Too many revisions" err &&
+               test_grep "Too many revisions" err &&
                test 123456789 = $(test-tool chmtime -g file2) || return 1
        done
 '
@@ -604,14 +604,14 @@ test_expect_success 'giving too many ref arguments does not modify files' '
 test_expect_success 'drop: too many arguments errors out (does nothing)' '
        git stash list >expect &&
        test_must_fail git stash drop stash@{0} stash@{1} 2>err &&
-       test_i18ngrep "Too many revisions" err &&
+       test_grep "Too many revisions" err &&
        git stash list >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'show: too many arguments errors out (does nothing)' '
        test_must_fail git stash show stash@{0} stash@{1} 2>err 1>out &&
-       test_i18ngrep "Too many revisions" err &&
+       test_grep "Too many revisions" err &&
        test_must_be_empty out
 '
 
@@ -654,7 +654,7 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '
 
 test_expect_success 'stash branch complains with no arguments' '
        test_must_fail git stash branch 2>err &&
-       test_i18ngrep "No branch name specified" err
+       test_grep "No branch name specified" err
 '
 
 test_expect_success 'stash show format defaults to --stat' '
@@ -931,6 +931,10 @@ test_expect_success 'store called with invalid commit' '
        test_must_fail git stash store foo
 '
 
+test_expect_success 'store called with non-stash commit' '
+       test_must_fail git stash store HEAD
+'
+
 test_expect_success 'store updates stash ref and reflog' '
        git stash clear &&
        git reset --hard &&
@@ -1211,19 +1215,19 @@ test_expect_success 'stash with file including $IFS character' '
 '
 
 test_expect_success 'stash with pathspec matching multiple paths' '
-       echo original >file &&
-       echo original >other-file &&
-       git commit -m "two" file other-file &&
-       echo modified >file &&
-       echo modified >other-file &&
-       git stash push -- "*file" &&
-       echo original >expect &&
-       test_cmp expect file &&
-       test_cmp expect other-file &&
-       git stash pop &&
-       echo modified >expect &&
-       test_cmp expect file &&
-       test_cmp expect other-file
+       echo original >file &&
+       echo original >other-file &&
+       git commit -m "two" file other-file &&
+       echo modified >file &&
+       echo modified >other-file &&
+       git stash push -- "*file" &&
+       echo original >expect &&
+       test_cmp expect file &&
+       test_cmp expect other-file &&
+       git stash pop &&
+       echo modified >expect &&
+       test_cmp expect file &&
+       test_cmp expect other-file
 '
 
 test_expect_success 'stash push -p with pathspec shows no changes only once' '
index 5390eec4e3b57eefca8971d86fa08b8f09c93747..1289ae3e07c635d30930067022969247b2b63f1f 100755 (executable)
@@ -404,7 +404,7 @@ test_expect_success 'stash show --include-untracked errors on duplicate files' '
        ) &&
        w_commit=$(git commit-tree -p HEAD -p "$i_commit" -p "$u_commit" -m "WIP on any-branch" "$tree") &&
        test_must_fail git stash show --include-untracked "$w_commit" 2>err &&
-       test_i18ngrep "worktree and untracked commit have duplicate entries: tracked" err
+       test_grep "worktree and untracked commit have duplicate entries: tracked" err
 '
 
 test_expect_success 'stash show --{include,only}-untracked on stashes without untracked entries' '
index dead9f18d937599427afe2c29fc177ed00f187ec..73f2dbdeb022ee71d04e9961da959f60d6eabd5c 100755 (executable)
@@ -88,13 +88,13 @@ test_expect_success 'error conditions' '
        echo fileA.t >list &&
 
        test_must_fail git stash push --pathspec-from-file=list --patch 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
        test_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+       test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git stash push --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
+       test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
 '
 
 test_done
index 4c661d4d54a779f8bc056c62e5d9e864401420c7..67fd2345affd78507a6cb714b48565ed190cb5af 100755 (executable)
@@ -12,7 +12,7 @@ create_crlf_ref () {
        cat >.crlf-orig-$branch.txt &&
        cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
        grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
-       grep 'Body' .crlf-message-$branch.txt >.crlf-body-$branch.txt || true &&
+       grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
        LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
        test_tick &&
        hash=$(git commit-tree HEAD^{tree} -p HEAD -F .crlf-message-${branch}.txt) &&
index bfcaae390f3ad95584ce9cf78b62cc81c33adf67..8d50331b8c8e431853bbdf377813ed288007961b 100755 (executable)
@@ -5,6 +5,9 @@
 
 test_description='Test built-in diff output engine.
 
+We happen to know that all diff plumbing and diff Porcelain share the
+same command line parser, so testing one should be sufficient; pick
+diff-files as a representative.
 '
 
 TEST_PASSES_SANITIZE_LEAK=true
@@ -16,9 +19,11 @@ Line 2
 line 3'
 cat path0 >path1
 chmod +x path1
+mkdir path2
+>path2/path3
 
 test_expect_success 'update-index --add two files with and without +x.' '
-       git update-index --add path0 path1
+       git update-index --add path0 path1 path2/path3
 '
 
 mv path0 path0-
@@ -91,4 +96,31 @@ test_expect_success 'git diff-files --patch --no-patch does not show the patch'
        test_must_be_empty err
 '
 
+
+# Smudge path2/path3 so that dirstat has something to show
+date >path2/path3
+
+for format in stat raw numstat shortstat summary \
+       dirstat cumulative dirstat-by-file \
+       patch-with-raw patch-with-stat compact-summary
+do
+       test_expect_success "--no-patch in 'git diff-files --no-patch --$format' is a no-op" '
+               git diff-files --no-patch "--$format" >actual &&
+               git diff-files "--$format" >expect &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "--no-patch clears all previous ones" '
+               git diff-files --$format -s -p >actual &&
+               git diff-files -p >expect &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "--no-patch in 'git diff --no-patch --$format' is a no-op" '
+               git diff --no-patch "--$format" >actual &&
+               git diff "--$format" >expect &&
+               test_cmp expect actual
+       '
+done
+
 test_done
index 3dc90470446dbb81e9c9421dbc7c21d3648d91f4..85be1367de6a3f73e1162ffc03563ec7b9a9eb5a 100755 (executable)
@@ -135,25 +135,25 @@ test_expect_success 'favour same basenames over different ones' '
        mkdir subdir &&
        git mv another-path subdir/path1 &&
        git status >out &&
-       test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+       test_grep "renamed: .*path1 -> subdir/path1" out
 '
 
 test_expect_success 'test diff.renames=true for git status' '
        git -c diff.renames=true status >out &&
-       test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+       test_grep "renamed: .*path1 -> subdir/path1" out
 '
 
 test_expect_success 'test diff.renames=false for git status' '
        git -c diff.renames=false status >out &&
-       test_i18ngrep ! "renamed: .*path1 -> subdir/path1" out &&
-       test_i18ngrep "new file: .*subdir/path1" out &&
-       test_i18ngrep "deleted: .*[^/]path1" out
+       test_grep ! "renamed: .*path1 -> subdir/path1" out &&
+       test_grep "new file: .*subdir/path1" out &&
+       test_grep "deleted: .*[^/]path1" out
 '
 
 test_expect_success 'favour same basenames even with minor differences' '
        git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
        git status >out &&
-       test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+       test_grep "renamed: .*path1 -> subdir/path1" out
 '
 
 test_expect_success 'two files with same basename and same content' '
@@ -165,7 +165,7 @@ test_expect_success 'two files with same basename and same content' '
        git commit -m 2 &&
        git mv dir other-dir &&
        git status >out &&
-       test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file" out
+       test_grep "renamed: .*dir/A/file -> other-dir/A/file" out
 '
 
 test_expect_success 'setup for many rename source candidates' '
@@ -202,9 +202,9 @@ test_expect_success 'rename pretty print with nothing in common' '
        git mv a/b/c c/b/a &&
        git commit -m "a/b/c -> c/b/a" &&
        git diff -M --summary HEAD^ HEAD >output &&
-       test_i18ngrep " a/b/c => c/b/a " output &&
+       test_grep " a/b/c => c/b/a " output &&
        git diff -M --stat HEAD^ HEAD >output &&
-       test_i18ngrep " a/b/c => c/b/a " output
+       test_grep " a/b/c => c/b/a " output
 '
 
 test_expect_success 'rename pretty print with common prefix' '
@@ -212,9 +212,9 @@ test_expect_success 'rename pretty print with common prefix' '
        git mv c/b/a c/d/e &&
        git commit -m "c/b/a -> c/d/e" &&
        git diff -M --summary HEAD^ HEAD >output &&
-       test_i18ngrep " c/{b/a => d/e} " output &&
+       test_grep " c/{b/a => d/e} " output &&
        git diff -M --stat HEAD^ HEAD >output &&
-       test_i18ngrep " c/{b/a => d/e} " output
+       test_grep " c/{b/a => d/e} " output
 '
 
 test_expect_success 'rename pretty print with common suffix' '
@@ -222,9 +222,9 @@ test_expect_success 'rename pretty print with common suffix' '
        git mv c/d/e d/e &&
        git commit -m "c/d/e -> d/e" &&
        git diff -M --summary HEAD^ HEAD >output &&
-       test_i18ngrep " {c/d => d}/e " output &&
+       test_grep " {c/d => d}/e " output &&
        git diff -M --stat HEAD^ HEAD >output &&
-       test_i18ngrep " {c/d => d}/e " output
+       test_grep " {c/d => d}/e " output
 '
 
 test_expect_success 'rename pretty print with common prefix and suffix' '
@@ -232,9 +232,9 @@ test_expect_success 'rename pretty print with common prefix and suffix' '
        git mv d/e d/f/e &&
        git commit -m "d/e -> d/f/e" &&
        git diff -M --summary HEAD^ HEAD >output &&
-       test_i18ngrep " d/{ => f}/e " output &&
+       test_grep " d/{ => f}/e " output &&
        git diff -M --stat HEAD^ HEAD >output &&
-       test_i18ngrep " d/{ => f}/e " output
+       test_grep " d/{ => f}/e " output
 '
 
 test_expect_success 'rename pretty print common prefix and suffix overlap' '
@@ -242,9 +242,9 @@ test_expect_success 'rename pretty print common prefix and suffix overlap' '
        git mv d/f/e d/f/f/e &&
        git commit -m "d/f/e d/f/f/e" &&
        git diff -M --summary HEAD^ HEAD >output &&
-       test_i18ngrep " d/f/{ => f}/e " output &&
+       test_grep " d/f/{ => f}/e " output &&
        git diff -M --stat HEAD^ HEAD >output &&
-       test_i18ngrep " d/f/{ => f}/e " output
+       test_grep " d/f/{ => f}/e " output
 '
 
 test_expect_success 'diff-tree -l0 defaults to a big rename limit, not zero' '
index ea52e5b91b75e3df741cbc12b927cacd6ee08afb..7afc883ec374e1c4b747ed80ba64575d05017502 100755 (executable)
@@ -284,132 +284,131 @@ cmp_diff_files_output () {
     test_cmp "$1" .test-tmp
 }
 
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree $tree_O $tree_A >.test-a &&
-     cmp -s .test-a .test-plain-OA'
-
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree -r $tree_O $tree_A >.test-a &&
-     cmp -s .test-a .test-recursive-OA'
-
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree $tree_O $tree_B >.test-a &&
-     cmp -s .test-a .test-plain-OB'
-
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree -r $tree_O $tree_B >.test-a &&
-     cmp -s .test-a .test-recursive-OB'
-
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree $tree_A $tree_B >.test-a &&
-     cmp -s .test-a .test-plain-AB'
-
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree -r $tree_A $tree_B >.test-a &&
-     cmp -s .test-a .test-recursive-AB'
-
-test_expect_success \
-    'diff-tree --stdin of known trees.' \
-    'echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
-     echo $tree_A $tree_B > .test-plain-ABx &&
-     cat .test-plain-AB >> .test-plain-ABx &&
-     cmp -s .test-a .test-plain-ABx'
-
-test_expect_success \
-    'diff-tree --stdin of known trees.' \
-    'echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
-     echo $tree_A $tree_B > .test-recursive-ABx &&
-     cat .test-recursive-AB >> .test-recursive-ABx &&
-     cmp -s .test-a .test-recursive-ABx'
-
-test_expect_success \
-    'diff-cache O with A in cache' \
-    'git read-tree $tree_A &&
-     git diff-index --cached $tree_O >.test-a &&
-     cmp -s .test-a .test-recursive-OA'
-
-test_expect_success \
-    'diff-cache O with B in cache' \
-    'git read-tree $tree_B &&
-     git diff-index --cached $tree_O >.test-a &&
-     cmp -s .test-a .test-recursive-OB'
-
-test_expect_success \
-    'diff-cache A with B in cache' \
-    'git read-tree $tree_B &&
-     git diff-index --cached $tree_A >.test-a &&
-     cmp -s .test-a .test-recursive-AB'
-
-test_expect_success \
-    'diff-files with O in cache and A checked out' \
-    'rm -fr Z [A-Z][A-Z] &&
-     git read-tree $tree_A &&
-     git checkout-index -f -a &&
-     git read-tree --reset $tree_O &&
-     test_must_fail git update-index --refresh -q &&
-     git diff-files >.test-a &&
-     cmp_diff_files_output .test-a .test-recursive-OA'
-
-test_expect_success \
-    'diff-files with O in cache and B checked out' \
-    'rm -fr Z [A-Z][A-Z] &&
-     git read-tree $tree_B &&
-     git checkout-index -f -a &&
-     git read-tree --reset $tree_O &&
-     test_must_fail git update-index --refresh -q &&
-     git diff-files >.test-a &&
-     cmp_diff_files_output .test-a .test-recursive-OB'
-
-test_expect_success \
-    'diff-files with A in cache and B checked out' \
-    'rm -fr Z [A-Z][A-Z] &&
-     git read-tree $tree_B &&
-     git checkout-index -f -a &&
-     git read-tree --reset $tree_A &&
-     test_must_fail git update-index --refresh -q &&
-     git diff-files >.test-a &&
-     cmp_diff_files_output .test-a .test-recursive-AB'
+test_expect_success 'diff-tree of known trees.' '
+       git diff-tree $tree_O $tree_A >.test-a &&
+       cmp -s .test-a .test-plain-OA
+'
+
+test_expect_success 'diff-tree of known trees.' '
+       git diff-tree -r $tree_O $tree_A >.test-a &&
+       cmp -s .test-a .test-recursive-OA
+'
+
+test_expect_success 'diff-tree of known trees.' '
+       git diff-tree $tree_O $tree_B >.test-a &&
+       cmp -s .test-a .test-plain-OB
+'
+
+test_expect_success 'diff-tree of known trees.' '
+       git diff-tree -r $tree_O $tree_B >.test-a &&
+       cmp -s .test-a .test-recursive-OB
+'
+
+test_expect_success 'diff-tree of known trees.' '
+       git diff-tree $tree_A $tree_B >.test-a &&
+       cmp -s .test-a .test-plain-AB
+'
+
+test_expect_success 'diff-tree of known trees.' '
+       git diff-tree -r $tree_A $tree_B >.test-a &&
+       cmp -s .test-a .test-recursive-AB
+'
+
+test_expect_success 'diff-tree --stdin of known trees.' '
+       echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
+       echo $tree_A $tree_B > .test-plain-ABx &&
+       cat .test-plain-AB >> .test-plain-ABx &&
+       cmp -s .test-a .test-plain-ABx
+'
+
+test_expect_success 'diff-tree --stdin of known trees.' '
+       echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
+       echo $tree_A $tree_B > .test-recursive-ABx &&
+       cat .test-recursive-AB >> .test-recursive-ABx &&
+       cmp -s .test-a .test-recursive-ABx
+'
+
+test_expect_success 'diff-cache O with A in cache' '
+       git read-tree $tree_A &&
+       git diff-index --cached $tree_O >.test-a &&
+       cmp -s .test-a .test-recursive-OA
+'
+
+test_expect_success 'diff-cache O with B in cache' '
+       git read-tree $tree_B &&
+       git diff-index --cached $tree_O >.test-a &&
+       cmp -s .test-a .test-recursive-OB
+'
+
+test_expect_success 'diff-cache A with B in cache' '
+       git read-tree $tree_B &&
+       git diff-index --cached $tree_A >.test-a &&
+       cmp -s .test-a .test-recursive-AB
+'
+
+test_expect_success 'diff-files with O in cache and A checked out' '
+       rm -fr Z [A-Z][A-Z] &&
+       git read-tree $tree_A &&
+       git checkout-index -f -a &&
+       git read-tree --reset $tree_O &&
+       test_must_fail git update-index --refresh -q &&
+       git diff-files >.test-a &&
+       cmp_diff_files_output .test-a .test-recursive-OA
+'
+
+test_expect_success 'diff-files with O in cache and B checked out' '
+       rm -fr Z [A-Z][A-Z] &&
+       git read-tree $tree_B &&
+       git checkout-index -f -a &&
+       git read-tree --reset $tree_O &&
+       test_must_fail git update-index --refresh -q &&
+       git diff-files >.test-a &&
+       cmp_diff_files_output .test-a .test-recursive-OB
+'
+
+test_expect_success 'diff-files with A in cache and B checked out' '
+       rm -fr Z [A-Z][A-Z] &&
+       git read-tree $tree_B &&
+       git checkout-index -f -a &&
+       git read-tree --reset $tree_A &&
+       test_must_fail git update-index --refresh -q &&
+       git diff-files >.test-a &&
+       cmp_diff_files_output .test-a .test-recursive-AB
+'
 
 ################################################################
 # Now we have established the baseline, we do not have to
 # rely on individual object ID values that much.
 
-test_expect_success \
-    'diff-tree O A == diff-tree -R A O' \
-    'git diff-tree $tree_O $tree_A >.test-a &&
-    git diff-tree -R $tree_A $tree_O >.test-b &&
-    cmp -s .test-a .test-b'
-
-test_expect_success \
-    'diff-tree -r O A == diff-tree -r -R A O' \
-    'git diff-tree -r $tree_O $tree_A >.test-a &&
-    git diff-tree -r -R $tree_A $tree_O >.test-b &&
-    cmp -s .test-a .test-b'
-
-test_expect_success \
-    'diff-tree B A == diff-tree -R A B' \
-    'git diff-tree $tree_B $tree_A >.test-a &&
-    git diff-tree -R $tree_A $tree_B >.test-b &&
-    cmp -s .test-a .test-b'
-
-test_expect_success \
-    'diff-tree -r B A == diff-tree -r -R A B' \
-    'git diff-tree -r $tree_B $tree_A >.test-a &&
-    git diff-tree -r -R $tree_A $tree_B >.test-b &&
-    cmp -s .test-a .test-b'
-
-test_expect_success \
-    'diff can read from stdin' \
-    'test_must_fail git diff --no-index -- MN - < NN |
-        grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
-    test_must_fail git diff --no-index -- MN NN |
-        grep -v "^index" >.test-b &&
-    test_cmp .test-a .test-b'
+test_expect_success 'diff-tree O A == diff-tree -R A O' '
+       git diff-tree $tree_O $tree_A >.test-a &&
+       git diff-tree -R $tree_A $tree_O >.test-b &&
+       cmp -s .test-a .test-b
+'
+
+test_expect_success 'diff-tree -r O A == diff-tree -r -R A O' '
+       git diff-tree -r $tree_O $tree_A >.test-a &&
+       git diff-tree -r -R $tree_A $tree_O >.test-b &&
+       cmp -s .test-a .test-b
+'
+
+test_expect_success 'diff-tree B A == diff-tree -R A B' '
+       git diff-tree $tree_B $tree_A >.test-a &&
+       git diff-tree -R $tree_A $tree_B >.test-b &&
+       cmp -s .test-a .test-b
+'
+
+test_expect_success 'diff-tree -r B A == diff-tree -r -R A B' '
+       git diff-tree -r $tree_B $tree_A >.test-a &&
+       git diff-tree -r -R $tree_A $tree_B >.test-b &&
+       cmp -s .test-a .test-b'
+
+test_expect_success 'diff can read from stdin' '
+       test_must_fail git diff --no-index -- MN - < NN |
+               grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
+       test_must_fail git diff --no-index -- MN NN |
+               grep -v "^index" >.test-b &&
+       test_cmp .test-a .test-b
+'
 
 test_done
index 181e9683a7955e18fcd149cd179c58c9f28d940a..ebe091828c878eb2d3ea3ab6f0f2e371e4d34329 100755 (executable)
@@ -11,20 +11,20 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
-test_expect_success \
-    'prepare reference tree' \
-    'COPYING_test_data >COPYING &&
-     echo frotz >rezrov &&
-    git update-index --add COPYING rezrov &&
-    tree=$(git write-tree) &&
-    echo $tree'
-
-test_expect_success \
-    'prepare work tree' \
-    'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
-    sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
-    rm -f COPYING &&
-    git update-index --add --remove COPYING COPYING.?'
+test_expect_success 'prepare reference tree' '
+       COPYING_test_data >COPYING &&
+       echo frotz >rezrov &&
+       git update-index --add COPYING rezrov &&
+       tree=$(git write-tree) &&
+       echo $tree
+'
+
+test_expect_success 'prepare work tree' '
+       sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+       sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+       rm -f COPYING &&
+       git update-index --add --remove COPYING COPYING.?
+'
 
 # tree has COPYING and rezrov.  work tree has COPYING.1 and COPYING.2,
 # both are slightly edited, and unchanged rezrov.  So we say you
@@ -57,14 +57,14 @@ rename to COPYING.2
 +      This file is licensed under the G.P.L v2, or a later version
 EOF
 
-test_expect_success \
-    'validate output from rename/copy detection (#1)' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#1)' '
+       compare_diff_patch current expected
+'
 
-test_expect_success \
-    'prepare work tree again' \
-    'mv COPYING.2 COPYING &&
-     git update-index --add --remove COPYING COPYING.1 COPYING.2'
+test_expect_success 'prepare work tree again' '
+       mv COPYING.2 COPYING &&
+       git update-index --add --remove COPYING COPYING.1 COPYING.2
+'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
 # both are slightly edited, and unchanged rezrov.  So we say you
@@ -95,14 +95,14 @@ copy to COPYING.1
 + However, in order to allow a migration to GPLv3 if that seems like
 EOF
 
-test_expect_success \
-    'validate output from rename/copy detection (#2)' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#2)' '
+       compare_diff_patch current expected
+'
 
-test_expect_success \
-    'prepare work tree once again' \
-    'COPYING_test_data >COPYING &&
-     git update-index --add --remove COPYING COPYING.1'
+test_expect_success 'prepare work tree once again' '
+       COPYING_test_data >COPYING &&
+       git update-index --add --remove COPYING COPYING.1
+'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
 # but COPYING is not edited.  We say you copy-and-edit COPYING.1; this
@@ -123,8 +123,8 @@ copy to COPYING.1
 + However, in order to allow a migration to GPLv3 if that seems like
 EOF
 
-test_expect_success \
-    'validate output from rename/copy detection (#3)' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#3)' '
+       compare_diff_patch current expected
+'
 
 test_done
index 8def4d4aee9d2f2b39469fd550020fedab1d0ee7..1d70d4d221b7536c430852e9014c9372743a4a2a 100755 (executable)
@@ -14,21 +14,21 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
-test_expect_success SYMLINKS \
-    'prepare reference tree' \
-    'echo xyzzy | tr -d '\\\\'012 >yomin &&
-     ln -s xyzzy frotz &&
-    git update-index --add frotz yomin &&
-    tree=$(git write-tree) &&
-    echo $tree'
+test_expect_success SYMLINKS 'prepare reference tree' '
+       echo xyzzy | tr -d '\\\\'012 >yomin &&
+       ln -s xyzzy frotz &&
+       git update-index --add frotz yomin &&
+       tree=$(git write-tree) &&
+       echo $tree
+'
 
-test_expect_success SYMLINKS \
-    'prepare work tree' \
-    'mv frotz rezrov &&
-     rm -f yomin &&
-     ln -s xyzzy nitfol &&
-     ln -s xzzzy bozbar &&
-    git update-index --add --remove frotz rezrov nitfol bozbar yomin'
+test_expect_success SYMLINKS 'prepare work tree' '
+       mv frotz rezrov &&
+       rm -f yomin &&
+       ln -s xyzzy nitfol &&
+       ln -s xzzzy bozbar &&
+       git update-index --add --remove frotz rezrov nitfol bozbar yomin
+'
 
 # tree has frotz pointing at xyzzy, and yomin that contains xyzzy to
 # confuse things.  work tree has rezrov (xyzzy) nitfol (xyzzy) and
@@ -36,9 +36,9 @@ test_expect_success SYMLINKS \
 # rezrov and nitfol are rename/copy of frotz and bozbar should be
 # a new creation.
 
-test_expect_success SYMLINKS 'setup diff output' "
-    GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current &&
-    cat >expected <<\EOF
+test_expect_success SYMLINKS 'setup diff output' '
+       GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current &&
+       cat >expected <<\EOF
 diff --git a/bozbar b/bozbar
 new file mode 120000
 --- /dev/null
@@ -62,10 +62,10 @@ deleted file mode 100644
 -xyzzy
 \ No newline at end of file
 EOF
-"
+'
 
-test_expect_success SYMLINKS \
-    'validate diff output' \
-    'compare_diff_patch current expected'
+test_expect_success SYMLINKS 'validate diff output' '
+       compare_diff_patch current expected
+'
 
 test_done
index c509143c8141e0c4c2f2696080922f339afd432c..c64d9d2f405e1e43a2247d91ca0a1a35af7fbad4 100755 (executable)
@@ -113,20 +113,20 @@ test_expect_success 'diff --no-index with binary creation' '
 '
 
 cat >expect <<EOF
- binfile  |   Bin 0 -> 1026 bytes
- textfile | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ binfilë  |   Bin 0 -> 1026 bytes
+ tëxtfilë | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
 
 test_expect_success 'diff --stat with binary files and big change count' '
-       printf "\01\00%1024d" 1 >binfile &&
-       git add binfile &&
+       printf "\01\00%1024d" 1 >binfilë &&
+       git add binfilë &&
        i=0 &&
        while test $i -lt 10000; do
                echo $i &&
                i=$(($i + 1)) || return 1
-       done >textfile &&
-       git add textfile &&
-       git diff --cached --stat binfile textfile >output &&
+       done >tëxtfilë &&
+       git add tëxtfilë &&
+       git -c core.quotepath=false diff --cached --stat binfilë tëxtfilë >output &&
        grep " | " output >actual &&
        test_cmp expect actual
 '
index dfcf3a0aaae3e2a72f6f41023b01c639a99c2f44..cb094241ec899f53a4ec65f37f0c4227281722d7 100755 (executable)
@@ -178,32 +178,29 @@ process_diffs () {
 V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g')
 while read magic cmd
 do
-       status=success
        case "$magic" in
        '' | '#'*)
                continue ;;
-       :*)
-               magic=${magic#:}
+       :noellipses)
+               magic=noellipses
                label="$magic-$cmd"
-               case "$magic" in
-               noellipses) ;;
-               failure)
-                       status=failure
-                       magic=
-                       label="$cmd" ;;
-               *)
-                       BUG "unknown magic $magic" ;;
-               esac ;;
+               ;;
+       :*)
+               BUG "unknown magic $magic"
+               ;;
        *)
-               cmd="$magic $cmd" magic=
-               label="$cmd" ;;
+               cmd="$magic $cmd"
+               magic=
+               label="$cmd"
+               ;;
        esac
+
        test=$(echo "$label" | sed -e 's|[/ ][/ ]*|_|g')
        pfx=$(printf "%04d" $test_count)
        expect="$TEST_DIRECTORY/t4013/diff.$test"
        actual="$pfx-diff.$test"
 
-       test_expect_$status "git $cmd # magic is ${magic:-(not used)}" '
+       test_expect_success "git $cmd # magic is ${magic:-(not used)}" '
                {
                        echo "$ git $cmd"
                        case "$magic" in
@@ -473,6 +470,14 @@ test_expect_success 'log --diff-merges=on matches --diff-merges=separate' '
        test_cmp expected actual
 '
 
+test_expect_success 'log --dd matches --diff-merges=1 -p' '
+       git log --diff-merges=1 -p master >result &&
+       process_diffs result >expected &&
+       git log --dd master >result &&
+       process_diffs result >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'deny wrong log.diffMerges config' '
        test_config log.diffMerges wrong-value &&
        test_expect_code 128 git log
@@ -514,7 +519,7 @@ test_expect_success 'log -S requires an argument' '
 '
 
 test_expect_success 'diff --cached on unborn branch' '
-       echo ref: refs/heads/unborn >.git/HEAD &&
+       git symbolic-ref HEAD refs/heads/unborn &&
        git diff --cached >result &&
        process_diffs result >actual &&
        process_diffs "$TEST_DIRECTORY/t4013/diff.diff_--cached" >expected &&
@@ -613,7 +618,49 @@ test_expect_success 'diff -I<regex> --stat' '
 
 test_expect_success 'diff -I<regex>: detect malformed regex' '
        test_expect_code 129 git diff --ignore-matching-lines="^[124-9" 2>error &&
-       test_i18ngrep "invalid regex given to -I: " error
+       test_grep "invalid regex given to -I: " error
+'
+
+# check_prefix <patch> <src> <dst>
+# check only lines with paths to avoid dependency on exact oid/contents
+check_prefix () {
+       grep -E '^(diff|---|\+\+\+) ' "$1" >actual.paths &&
+       cat >expect <<-EOF &&
+       diff --git $2 $3
+       --- $2
+       +++ $3
+       EOF
+       test_cmp expect actual.paths
+}
+
+test_expect_success 'diff-files does not respect diff.noprefix' '
+       git -c diff.noprefix diff-files -p >actual &&
+       check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff-files respects --no-prefix' '
+       git diff-files -p --no-prefix >actual &&
+       check_prefix actual file0 file0
+'
+
+test_expect_success 'diff respects diff.noprefix' '
+       git -c diff.noprefix diff >actual &&
+       check_prefix actual file0 file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.noprefix' '
+       git -c diff.noprefix diff --default-prefix >actual &&
+       check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.mnemonicprefix' '
+       git -c diff.mnemonicprefix diff >actual &&
+       check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.mnemonicprefix' '
+       git -c diff.mnemonicprefix diff --default-prefix >actual &&
+       check_prefix actual a/file0 b/file0
 '
 
 test_done
index ad5c02927943ab44a6fb986a646cd9b829786ef3..e37a1411ee247cc76083367870e0e0c2e16fbb84 100755 (executable)
@@ -59,6 +59,10 @@ test_expect_success setup '
        test_tick &&
        git commit -m "patchid 3" &&
 
+       git checkout -b empty main &&
+       test_tick &&
+       git commit --allow-empty -m "empty commit" &&
+
        git checkout main
 '
 
@@ -128,6 +132,12 @@ test_expect_success 'replay did not screw up the log message' '
        grep "^Side .* with .* backslash-n" actual
 '
 
+test_expect_success 'format-patch empty commit' '
+       git format-patch --stdout main..empty >empty &&
+       grep "^From " empty >from &&
+       test_line_count = 1 from
+'
+
 test_expect_success 'extra headers' '
        git config format.headers "To: R E Cipient <rcipient@example.com>
 " &&
@@ -445,13 +455,13 @@ test_expect_success 'no threading' '
 
 cat >expect.thread <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 ---
-Message-Id: <1>
+Message-ID: <1>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <0>
 EOF
@@ -460,17 +470,22 @@ test_expect_success 'thread' '
        check_threading expect.thread --thread main
 '
 
+test_expect_success '--thread overrides format.thread=deep' '
+       test_config format.thread deep &&
+       check_threading expect.thread --thread main
+'
+
 cat >expect.in-reply-to <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <1>
 References: <1>
 EOF
@@ -482,17 +497,17 @@ test_expect_success 'thread in-reply-to' '
 
 cat >expect.cover-letter <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 ---
-Message-Id: <1>
+Message-ID: <1>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <0>
 References: <0>
 EOF
@@ -503,21 +518,21 @@ test_expect_success 'thread cover-letter' '
 
 cat >expect.cl-irt <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <1>
        <0>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <0>
 References: <1>
        <0>
 ---
-Message-Id: <4>
+Message-ID: <4>
 In-Reply-To: <0>
 References: <1>
        <0>
@@ -535,13 +550,13 @@ test_expect_success 'thread explicit shallow' '
 
 cat >expect.deep <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 ---
-Message-Id: <1>
+Message-ID: <1>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <1>
 References: <0>
        <1>
@@ -553,16 +568,16 @@ test_expect_success 'thread deep' '
 
 cat >expect.deep-irt <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <1>
        <0>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <2>
 References: <1>
        <0>
@@ -576,18 +591,18 @@ test_expect_success 'thread deep in-reply-to' '
 
 cat >expect.deep-cl <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 ---
-Message-Id: <1>
+Message-ID: <1>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <1>
 References: <0>
        <1>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <2>
 References: <0>
        <1>
@@ -600,22 +615,22 @@ test_expect_success 'thread deep cover-letter' '
 
 cat >expect.deep-cl-irt <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <1>
        <0>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <2>
 References: <1>
        <0>
        <2>
 ---
-Message-Id: <4>
+Message-ID: <4>
 In-Reply-To: <3>
 References: <1>
        <0>
@@ -1358,7 +1373,27 @@ test_expect_success '--rfc' '
        Subject: [RFC PATCH 1/1] header with . in it
        EOF
        git format-patch -n -1 --stdout --rfc >patch &&
-       grep ^Subject: patch >actual &&
+       grep "^Subject:" patch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--rfc does not overwrite prefix' '
+       cat >expect <<-\EOF &&
+       Subject: [RFC PATCH foobar 1/1] header with . in it
+       EOF
+       git -c format.subjectPrefix="PATCH foobar" \
+               format-patch -n -1 --stdout --rfc >patch &&
+       grep "^Subject:" patch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--rfc is argument order independent' '
+       cat >expect <<-\EOF &&
+       Subject: [RFC PATCH foobar 1/1] header with . in it
+       EOF
+       git format-patch -n -1 --stdout --rfc \
+               --subject-prefix="PATCH foobar" >patch &&
+       grep "^Subject:" patch >actual &&
        test_cmp expect actual
 '
 
@@ -1457,7 +1492,7 @@ append_signoff()
        C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
        git format-patch --stdout --signoff $C^..$C >append_signoff.patch &&
        sed -n -e "1,/^---$/p" append_signoff.patch |
-               egrep -n "^Subject|Sign|^$"
+               grep -E -n "^Subject|Sign|^$"
 }
 
 test_expect_success 'signoff: commit with no body' '
@@ -1871,6 +1906,16 @@ body" &&
        grep "^body$" actual
 '
 
+test_expect_success 'cover letter with --cover-from-description subject (UTF-8 subject line)' '
+       test_config branch.rebuild-1.description "Café?
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description subject --encode-email-headers main >actual &&
+       grep "^Subject: \[PATCH 0/2\] =?UTF-8?q?Caf=C3=A9=3F?=$" actual &&
+       ! grep "Café" actual
+'
+
 test_expect_success 'cover letter with format.coverFromDescription = auto (short subject line)' '
        test_config branch.rebuild-1.description "config subject
 
@@ -1976,6 +2021,20 @@ test_expect_success 'cover letter using branch description (6)' '
        grep hello actual
 '
 
+test_expect_success 'cover letter with --description-file' '
+       test_when_finished "rm -f description.txt" &&
+       cat >description.txt <<-\EOF &&
+       subject from file
+
+       body from file
+       EOF
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description auto \
+               --description-file description.txt main >actual &&
+       grep "^Subject: \[PATCH 0/2\] subject from file$" actual &&
+       grep "^body from file$" actual
+'
+
 test_expect_success 'cover letter with nothing' '
        git format-patch --stdout --cover-letter >actual &&
        test_line_count = 0 actual
@@ -2274,14 +2333,32 @@ test_expect_success 'format-patch --base with --attach' '
 test_expect_success 'format-patch --attach cover-letter only is non-multipart' '
        test_when_finished "rm -fr patches" &&
        git format-patch -o patches --cover-letter --attach=mimemime --base=HEAD~ -1 &&
-       ! egrep "^--+mimemime" patches/0000*.patch &&
-       egrep "^--+mimemime$" patches/0001*.patch >output &&
+       ! grep -E "^--+mimemime" patches/0000*.patch &&
+       grep -E "^--+mimemime$" patches/0001*.patch >output &&
        test_line_count = 2 output &&
-       egrep "^--+mimemime--$" patches/0001*.patch >output &&
+       grep -E "^--+mimemime--$" patches/0001*.patch >output &&
        test_line_count = 1 output
 '
 
-test_expect_success 'format-patch --pretty=mboxrd' '
+test_expect_success 'format-patch with format.attach' '
+       test_when_finished "rm -fr patches" &&
+       separator=attachment-separator &&
+       test_config format.attach "$separator" &&
+       filename=$(git format-patch -o patches -1) &&
+       grep "^Content-Type: multipart/.*$separator" "$filename"
+'
+
+test_expect_success 'format-patch with format.attach=disabled' '
+       test_when_finished "rm -fr patches" &&
+       separator=attachment-separator &&
+       test_config_global format.attach "$separator" &&
+       test_config format.attach "" &&
+       filename=$(git format-patch -o patches -1) &&
+       # The output should not even declare content type for text/plain.
+       ! grep "^Content-Type: multipart/" "$filename"
+'
+
+test_expect_success '-c format.mboxrd format-patch' '
        sp=" " &&
        cat >msg <<-INPUT_END &&
        mboxrd should escape the body
@@ -2316,7 +2393,9 @@ test_expect_success 'format-patch --pretty=mboxrd' '
        INPUT_END
 
        C=$(git commit-tree HEAD^^{tree} -p HEAD <msg) &&
-       git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >patch &&
+       git -c format.mboxrd format-patch --stdout -1 $C~1..$C >patch &&
+       git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >compat &&
+       test_cmp patch compat &&
        git grep -h --no-index -A11 \
                "^>From could trip up a loose mbox parser" patch >actual &&
        test_cmp expect actual
@@ -2334,25 +2413,25 @@ test_expect_success 'interdiff: cover-letter' '
        --q
        EOF
        git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
-       test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
-       test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch &&
+       test_grep "^Interdiff:$" 0000-cover-letter.patch &&
+       test_grep ! "^Interdiff:$" 0001-fleep.patch &&
        sed "1,/^@@ /d; /^-- $/q" 0000-cover-letter.patch >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'interdiff: reroll-count' '
        git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
-       test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+       test_grep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
 test_expect_success 'interdiff: reroll-count with a non-integer' '
        git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
-       test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+       test_grep "^Interdiff:$" v2.2-0000-cover-letter.patch
 '
 
 test_expect_success 'interdiff: reroll-count with a integer' '
        git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
-       test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+       test_grep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
 test_expect_success 'interdiff: solo-patch' '
@@ -2361,9 +2440,25 @@ test_expect_success 'interdiff: solo-patch' '
 
        EOF
        git format-patch --interdiff=boop~2 -1 boop &&
-       test_i18ngrep "^Interdiff:$" 0001-fleep.patch &&
+       test_grep "^Interdiff:$" 0001-fleep.patch &&
        sed "1,/^  @@ /d; /^$/q" 0001-fleep.patch >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'format-patch does not respect diff.noprefix' '
+       git -c diff.noprefix format-patch -1 --stdout >actual &&
+       grep "^--- a/blorp" actual
+'
+
+test_expect_success 'format-patch respects format.noprefix' '
+       git -c format.noprefix format-patch -1 --stdout >actual &&
+       grep "^--- blorp" actual
+'
+
+test_expect_success 'format-patch --default-prefix overrides format.noprefix' '
+       git -c format.noprefix \
+               format-patch -1 --default-prefix --stdout >actual &&
+       grep "^--- a/blorp" actual
+'
+
 test_done
index f3e20dd5bba202ba5d5589e897d753672096b866..b443626afd7271a6e58ccfc45429759b9b8c00ca 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2006 Johannes E. Schindelin
-#
+# Copyright (c) 2023 Google LLC
 
 test_description='Test special whitespace in diff engine.
 
@@ -11,6 +11,43 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
+for opt_res in --patch --quiet -s --stat --shortstat --dirstat=lines \
+              --raw! --name-only! --name-status!
+do
+       opts=${opt_res%!} expect_failure=
+       test "$opts" = "$opt_res" ||
+               expect_failure="test_expect_code 1"
+
+       test_expect_success "status with $opts (different)" '
+               echo foo >x &&
+               git add x &&
+               echo bar >x &&
+               test_expect_code 1 git diff -w $opts --exit-code x
+       '
+
+       test_expect_success POSIXPERM "status with $opts (mode differs)" '
+               test_when_finished "git update-index --chmod=-x x" &&
+               echo foo >x &&
+               git add x &&
+               git update-index --chmod=+x x &&
+               test_expect_code 1 git diff -w $opts --exit-code x
+       '
+
+       test_expect_success "status with $opts (removing an empty file)" '
+               : >x &&
+               git add x &&
+               rm x &&
+               test_expect_code 1 git diff -w $opts --exit-code -- x
+       '
+
+       test_expect_success "status with $opts (different but equivalent)" '
+               echo foo >x &&
+               git add x &&
+               echo " foo" >x &&
+               $expect_failure git diff -w $opts --exit-code x
+       '
+done
+
 test_expect_success "Ray Lehtiniemi's example" '
        cat <<-\EOF >x &&
        do {
@@ -909,7 +946,7 @@ test_expect_success 'combined diff with autocrlf conversion' '
        git commit -m "the other side" x &&
        git config core.autocrlf true &&
        test_must_fail git merge one-side >actual &&
-       test_i18ngrep "Automatic merge failed" actual &&
+       test_grep "Automatic merge failed" actual &&
 
        git diff >actual.raw &&
        sed -e "1,/^@@@/d" actual.raw >actual &&
@@ -1638,7 +1675,7 @@ test_expect_success 'no effect on diff from --color-moved with --word-diff' '
        test_cmp expect actual
 '
 
-test_expect_success !SANITIZE_LEAK 'no effect on show from --color-moved with --word-diff' '
+test_expect_success 'no effect on show from --color-moved with --word-diff' '
        git show --color-moved --word-diff >actual &&
        git show --word-diff >expect &&
        test_cmp expect actual
@@ -2024,7 +2061,7 @@ test_expect_success '--color-moved rewinds for MIN_ALNUM_COUNT' '
        test_cmp expected actual
 '
 
-test_expect_success !SANITIZE_LEAK 'move detection with submodules' '
+test_expect_success 'move detection with submodules' '
        test_create_repo bananas &&
        echo ripe >bananas/recipe &&
        git -C bananas add recipe &&
@@ -2187,27 +2224,27 @@ test_expect_success 'compare whitespace delta across moved blocks' '
 
 test_expect_success 'bogus settings in move detection erroring out' '
        test_must_fail git diff --color-moved=bogus 2>err &&
-       test_i18ngrep "must be one of" err &&
-       test_i18ngrep bogus err &&
+       test_grep "must be one of" err &&
+       test_grep bogus err &&
 
        test_must_fail git -c diff.colormoved=bogus diff 2>err &&
-       test_i18ngrep "must be one of" err &&
-       test_i18ngrep "from command-line config" err &&
+       test_grep "must be one of" err &&
+       test_grep "from command-line config" err &&
 
        test_must_fail git diff --color-moved-ws=bogus 2>err &&
-       test_i18ngrep "possible values" err &&
-       test_i18ngrep bogus err &&
+       test_grep "possible values" err &&
+       test_grep bogus err &&
 
        test_must_fail git -c diff.colormovedws=bogus diff 2>err &&
-       test_i18ngrep "possible values" err &&
-       test_i18ngrep "from command-line config" err
+       test_grep "possible values" err &&
+       test_grep "from command-line config" err
 '
 
 test_expect_success 'compare whitespace delta incompatible with other space options' '
        test_must_fail git diff \
                --color-moved-ws=allow-indentation-change,ignore-all-space \
                2>err &&
-       test_i18ngrep allow-indentation-change err
+       test_grep allow-indentation-change err
 '
 
 EMPTY=''
index 5bc28ad9f042a0476d94d9e90e5b58073cc17f99..f439f469bd2bfd3942a61e104fd290d8e3461e19 100755 (executable)
@@ -138,4 +138,9 @@ test_expect_success 'check honors conflict marker length' '
        git reset --hard
 '
 
+test_expect_success 'option errors are not confused by --exit-code' '
+       test_must_fail git diff --exit-code --nonsense 2>err &&
+       grep '^usage:' err
+'
+
 test_done
index 42a2b9a13b7a5bd6cf28cfbe9bbfb9a5a3e5f3e6..e026fac1f4090330f81b5b417e1b9e930795fd96 100755 (executable)
@@ -53,15 +53,34 @@ do
                echo "*.java diff=$p" >.gitattributes &&
                test_expect_code 1 git diff --no-index \
                        A.java B.java 2>msg &&
-               test_i18ngrep ! fatal msg &&
-               test_i18ngrep ! error msg
+               test_grep ! fatal msg &&
+               test_grep ! error msg
        '
        test_expect_success "builtin $p wordRegex pattern compiles" '
                echo "*.java diff=$p" >.gitattributes &&
                test_expect_code 1 git diff --no-index --word-diff \
                        A.java B.java 2>msg &&
-               test_i18ngrep ! fatal msg &&
-               test_i18ngrep ! error msg
+               test_grep ! fatal msg &&
+               test_grep ! error msg
+       '
+
+       test_expect_success "builtin $p pattern compiles on bare repo with --attr-source" '
+               test_when_finished "rm -rf bare.git" &&
+               git checkout -B master &&
+               git add . &&
+               echo "*.java diff=notexist" >.gitattributes &&
+               git add .gitattributes &&
+               git commit -am "changing gitattributes" &&
+               git checkout -B branchA &&
+               echo "*.java diff=$p" >.gitattributes &&
+               git add .gitattributes &&
+               git commit -am "changing gitattributes" &&
+               git clone --bare --no-local . bare.git &&
+               git -C bare.git symbolic-ref HEAD refs/heads/master &&
+               test_expect_code 1 git -C bare.git --attr-source=branchA \
+                       diff --exit-code HEAD:A.java HEAD:B.java 2>msg &&
+               test_grep ! fatal msg &&
+               test_grep ! error msg
        '
 done
 
@@ -69,7 +88,7 @@ test_expect_success 'last regexp must not be negated' '
        echo "*.java diff=java" >.gitattributes &&
        test_config diff.java.funcname "!static" &&
        test_expect_code 128 git diff --no-index A.java B.java 2>msg &&
-       test_i18ngrep ": Last expression must not be negated:" msg
+       test_grep ": Last expression must not be negated:" msg
 '
 
 test_expect_success 'setup hunk header tests' '
diff --git a/t/t4018/java-class-brace-on-separate-line b/t/t4018/java-class-brace-on-separate-line
new file mode 100644 (file)
index 0000000..8795acd
--- /dev/null
@@ -0,0 +1,6 @@
+class RIGHT
+{
+    static int ONE;
+    static int TWO;
+    static int ChangeMe;
+}
diff --git a/t/t4018/java-class-space-before-type-parameters b/t/t4018/java-class-space-before-type-parameters
new file mode 100644 (file)
index 0000000..0bdef1d
--- /dev/null
@@ -0,0 +1,6 @@
+class RIGHT <TYPE, PARAMS, AFTER, SPACE> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private A ChangeMe;
+}
diff --git a/t/t4018/java-class-type-parameters b/t/t4018/java-class-type-parameters
new file mode 100644 (file)
index 0000000..579aa7a
--- /dev/null
@@ -0,0 +1,6 @@
+class RIGHT<A, B> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private A ChangeMe;
+}
diff --git a/t/t4018/java-class-type-parameters-implements b/t/t4018/java-class-type-parameters-implements
new file mode 100644 (file)
index 0000000..b8038b1
--- /dev/null
@@ -0,0 +1,6 @@
+class RIGHT<A, B> implements List<A> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private A ChangeMe;
+}
diff --git a/t/t4018/java-interface-type-parameters b/t/t4018/java-interface-type-parameters
new file mode 100644 (file)
index 0000000..a4baa1a
--- /dev/null
@@ -0,0 +1,6 @@
+interface RIGHT<A, B> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    public B foo(A ChangeMe);
+}
diff --git a/t/t4018/java-interface-type-parameters-extends b/t/t4018/java-interface-type-parameters-extends
new file mode 100644 (file)
index 0000000..31d7fb3
--- /dev/null
@@ -0,0 +1,6 @@
+interface RIGHT<A, B> extends Function<A, B> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    public B foo(A ChangeMe);
+}
diff --git a/t/t4018/java-non-sealed b/t/t4018/java-non-sealed
new file mode 100644 (file)
index 0000000..069087c
--- /dev/null
@@ -0,0 +1,8 @@
+public abstract sealed class SealedClass {
+    public static non-sealed class RIGHT extends SealedClass {
+        static int ONE;
+        static int TWO;
+        static int THREE;
+        private int ChangeMe;
+    }
+}
diff --git a/t/t4018/java-record b/t/t4018/java-record
new file mode 100644 (file)
index 0000000..97aa819
--- /dev/null
@@ -0,0 +1,6 @@
+public record RIGHT(int comp1, double comp2, String comp3) {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    static int ChangeMe;
+}
diff --git a/t/t4018/java-record-space-before-components b/t/t4018/java-record-space-before-components
new file mode 100644 (file)
index 0000000..9827f22
--- /dev/null
@@ -0,0 +1,6 @@
+public record RIGHT (String components, String after, String space) {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    static int ChangeMe;
+}
diff --git a/t/t4018/java-record-type-parameters b/t/t4018/java-record-type-parameters
new file mode 100644 (file)
index 0000000..f62a035
--- /dev/null
@@ -0,0 +1,6 @@
+public record RIGHT<A, N extends Number>(A comp1, N comp2, int comp3) {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    static int ChangeMe;
+}
diff --git a/t/t4018/java-sealed b/t/t4018/java-sealed
new file mode 100644 (file)
index 0000000..785fbc6
--- /dev/null
@@ -0,0 +1,7 @@
+public abstract sealed class Sealed { // RIGHT
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    public final class ChangeMe extends Sealed {
+    }
+}
diff --git a/t/t4018/java-sealed-permits b/t/t4018/java-sealed-permits
new file mode 100644 (file)
index 0000000..18dd489
--- /dev/null
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT permits PermittedA, PermittedB {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters b/t/t4018/java-sealed-type-parameters
new file mode 100644 (file)
index 0000000..e6530c4
--- /dev/null
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters-implements-permits b/t/t4018/java-sealed-type-parameters-implements-permits
new file mode 100644 (file)
index 0000000..bd6e6d3
--- /dev/null
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> implements List<A> permits PermittedA, PermittedB {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters-permits b/t/t4018/java-sealed-type-parameters-permits
new file mode 100644 (file)
index 0000000..25a0da6
--- /dev/null
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> permits PermittedA, PermittedB {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private int ChangeMe;
+}
index 1c89050a974c7b101056865a4b48e7f18a90d29e..6fed993ea0b34dddae94356354c889ada9f16497 100755 (executable)
@@ -24,7 +24,7 @@ test_expect_success setup '
 test_expect_success 'detect rewrite' '
 
        actual=$(git diff-files -B --summary test) &&
-       verbose expr "$actual" : " rewrite test ([0-9]*%)$"
+       expr "$actual" : " rewrite test ([0-9]*%)$"
 
 '
 
index 7cb99092938d7dd64e8ab6f4e147fc5f70c0a310..787605ce3fd076b101d1c825fca174020ae7ff46 100755 (executable)
@@ -52,8 +52,8 @@ test_expect_success setup '
 '
 
 test_expect_success 'cross renames to be detected for regular files' '
-
-       git diff-tree five six -r --name-status -B -M | sort >actual &&
+       git diff-tree five six -r --name-status -B -M >out &&
+       sort out >actual &&
        {
                echo "R100      foo     bar" &&
                echo "R100      bar     foo"
@@ -63,8 +63,8 @@ test_expect_success 'cross renames to be detected for regular files' '
 '
 
 test_expect_success 'cross renames to be detected for typechange' '
-
-       git diff-tree one two -r --name-status -B -M | sort >actual &&
+       git diff-tree one two -r --name-status -B -M >out &&
+       sort out >actual &&
        {
                echo "R100      foo     bar" &&
                echo "R100      bar     foo"
@@ -74,8 +74,8 @@ test_expect_success 'cross renames to be detected for typechange' '
 '
 
 test_expect_success 'moves and renames' '
-
-       git diff-tree three four -r --name-status -B -M | sort >actual &&
+       git diff-tree three four -r --name-status -B -M >out &&
+       sort out >actual &&
        {
                # see -B -M (#6) in t4008
                echo "C100      foo     bar" &&
index eacc6694f785acb2e6287dd4c5bce9a5e834a361..c4394a27b56236aeda3c92df03aa19f9d8720190 100755 (executable)
@@ -53,7 +53,7 @@ test_expect_success 'rewrite diff --numstat shows binary changes' '
 test_expect_success 'diff --stat counts binary rewrite as 0 lines' '
        git diff -B --stat --summary >diff &&
        grep "Bin" diff &&
-       test_i18ngrep "0 insertions.*0 deletions" diff &&
+       test_grep "0 insertions.*0 deletions" diff &&
        grep " rewrite file" diff
 '
 
index 15764ee9ac8ab7984e57473a121be595c2e9b0cc..74586f3813c6f44955a5407e0d3382febd9e4e58 100755 (executable)
@@ -69,6 +69,10 @@ test_language_driver () {
                echo "* diff='"$lang"'" >.gitattributes &&
                word_diff --color-words
        '
+       test_expect_success "diff driver '$lang' in Islandic" '
+               LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
+               word_diff --color-words
+       '
 }
 
 test_expect_success setup '
index 9a292bac70c248c9273bf29a94d5ce39f17551df..2ce26e585c98c1a3045dee4545763f160f59aa82 100755 (executable)
@@ -80,11 +80,21 @@ test_expect_success 'check combined output (1)' '
        verify_helper sidewithone
 '
 
+test_expect_success 'check combined output (1) with git diff <rev>^!' '
+       git diff sidewithone^! -- >sidewithone &&
+       verify_helper sidewithone
+'
+
 test_expect_success 'check combined output (2)' '
        git show sidesansone -- >sidesansone &&
        verify_helper sidesansone
 '
 
+test_expect_success 'check combined output (2) with git diff <rev>^!' '
+       git diff sidesansone^! -- >sidesansone &&
+       verify_helper sidesansone
+'
+
 test_expect_success 'diagnose truncated file' '
        >file &&
        git add file &&
index e70e020ae9349c378b4b922933668932e95acc8d..eec3d73dc2b475be7185d9d1dc8733bf391a868b 100755 (executable)
@@ -28,8 +28,7 @@ test_expect_success 'diff-tree --exit-code' '
 
 test_expect_success 'diff-tree -b --exit-code' '
        git diff -b --exit-code HEAD^ HEAD &&
-       git diff-tree -b -p --exit-code HEAD^ HEAD &&
-       git diff-tree -b --exit-code HEAD^ HEAD
+       git diff-tree -b -p --exit-code HEAD^ HEAD
 '
 
 test_expect_success 'diff-index --cached --exit-code' '
index 29e49d22902dd7e7566f2662174475bf35c6ebed..9f6043dabaccdcbbaecccf2b2f0248ca132c946e 100755 (executable)
@@ -34,12 +34,12 @@ test_expect_success 'setup' '
        100644 blob $(test_oid hash2)   foo
        EOF
 
-       echo "$(test_oid val1)" > foo &&
+       test_oid val1 > foo &&
        git add foo &&
        git commit -m "initial" &&
        git cat-file -p HEAD: > actual &&
        test_cmp expect_initial actual &&
-       echo "$(test_oid val2)" > foo &&
+       test_oid val2 > foo &&
        git commit -a -m "update" &&
        git cat-file -p HEAD: > actual &&
        test_cmp expect_update actual
index fab351b48a1f52b450f3911aa6edafae70015613..9b46c4c1befece1f3e231b9672291b7a538c3f6f 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='diff --relative tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -162,6 +164,35 @@ check_diff_relative_option subdir file2 true --no-relative --relative
 check_diff_relative_option . file2 false --no-relative --relative=subdir
 check_diff_relative_option . file2 true --no-relative --relative=subdir
 
+test_expect_success 'external diff with --relative' '
+       test_when_finished "git reset --hard" &&
+       echo changed >file1 &&
+       echo changed >subdir/file2 &&
+
+       write_script mydiff <<-\EOF &&
+       # hacky pretend diff; the goal here is just to make sure we got
+       # passed sensible input that we _could_ diff, without relying on
+       # the specific output of a system diff tool.
+       echo "diff a/$1 b/$1" &&
+       echo "--- a/$1" &&
+       echo "+++ b/$1" &&
+       echo "@@ -1 +0,0 @@" &&
+       sed "s/^/-/" "$2" &&
+       sed "s/^/+/" "$5"
+       EOF
+
+       cat >expect <<-\EOF &&
+       diff a/file2 b/file2
+       --- a/file2
+       +++ b/file2
+       @@ -1 +0,0 @@
+       -other content
+       +changed
+       EOF
+       GIT_EXTERNAL_DIFF=./mydiff git diff --relative=subdir >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup diff --relative unmerged' '
        test_commit zero file0 &&
        test_commit base subdir/file0 &&
index 0ae0cd3a524da312d7f1cc7b1d4b0f6ed7b2b529..ffaf69335f7b224f76ece7228435a4ce057e5c83 100755 (executable)
@@ -86,4 +86,14 @@ test_expect_success 'diff-files -3' '
        test_cmp diff-files-3.expect diff-files-3.actual
 '
 
+test_expect_success 'diff --stat' '
+       for path in $paths
+       do
+               echo " $path | Unmerged" || return 1
+       done >diff-stat.expect &&
+       echo " 0 files changed" >>diff-stat.expect &&
+       git diff --cached --stat >diff-stat.actual &&
+       test_cmp diff-stat.expect diff-stat.actual
+'
+
 test_done
index 7fec2cb9cd783f5aeff27236c8e54ddb2241e1be..7b73462d53d2c12def254e80a9d7c90e19f68794 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='diff --dirstat tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # set up two commits where the second commit has these files
@@ -941,37 +943,37 @@ test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '
        test_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
        test_debug "cat actual_error" &&
        test_must_be_empty actual_diff_dirstat &&
-       test_i18ngrep -q "future_param" actual_error &&
-       test_i18ngrep -q "\--dirstat" actual_error
+       test_grep -q "future_param" actual_error &&
+       test_grep -q "\--dirstat" actual_error
 '
 
 test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '
        test_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
        test_debug "cat actual_error" &&
        test_must_be_empty actual_diff_dirstat &&
-       test_i18ngrep -q "dummy1" actual_error &&
-       test_i18ngrep -q "2dummy" actual_error &&
-       test_i18ngrep -q "\--dirstat" actual_error
+       test_grep -q "dummy1" actual_error &&
+       test_grep -q "2dummy" actual_error &&
+       test_grep -q "\--dirstat" actual_error
 '
 
 test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '
        git -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
        test_debug "cat actual_error" &&
        test_cmp expect_diff_dirstat actual_diff_dirstat &&
-       test_i18ngrep -q "future_param" actual_error &&
-       test_i18ngrep -q "diff\\.dirstat" actual_error &&
+       test_grep -q "future_param" actual_error &&
+       test_grep -q "diff\\.dirstat" actual_error &&
 
        git -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&
        test_debug "cat actual_error" &&
        test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
-       test_i18ngrep -q "future_param" actual_error &&
-       test_i18ngrep -q "diff\\.dirstat" actual_error &&
+       test_grep -q "future_param" actual_error &&
+       test_grep -q "diff\\.dirstat" actual_error &&
 
        git -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&
        test_debug "cat actual_error" &&
        test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
-       test_i18ngrep -q "future_param" actual_error &&
-       test_i18ngrep -q "diff\\.dirstat" actual_error
+       test_grep -q "future_param" actual_error &&
+       test_grep -q "diff\\.dirstat" actual_error
 '
 
 test_expect_success '--shortstat --dirstat should output only one dirstat' '
index b5c281edaa7037097f3c8fad28c1bb1994607a20..7badd72488d664ff776e1004f620df1fbc774cdc 100755 (executable)
@@ -8,10 +8,11 @@ test_description='test --stat output of various commands'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
-# 120 character name
+# 120-character name
 name=aaaaaaaaaa
 name=$name$name$name$name$name$name$name$name$name$name$name$name
 test_expect_success 'preparation' '
@@ -48,12 +49,41 @@ log -1 --stat
 EOF
 
 cat >expect.60 <<-'EOF'
- ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
 EOF
 cat >expect.6030 <<-'EOF'
  ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
 EOF
-cat >expect2.60 <<-'EOF'
+while read verb expect cmd args
+do
+       # No width limit applied when statNameWidth is ignored
+       case "$expect" in expect72|expect.6030)
+               test_expect_success "$cmd $verb diff.statNameWidth with long name" '
+                       git -c diff.statNameWidth=30 $cmd $args >output &&
+                       grep " | " output >actual &&
+                       test_cmp $expect actual
+               ';;
+       esac
+       # Maximum width limit still applied when statNameWidth is ignored
+       case "$expect" in expect.60|expect.6030)
+               test_expect_success "$cmd --stat=width $verb diff.statNameWidth with long name" '
+                       git -c diff.statNameWidth=30 $cmd $args --stat=60 >output &&
+                       grep " | " output >actual &&
+                       test_cmp $expect actual
+               ';;
+       esac
+done <<\EOF
+ignores expect72 format-patch -1 --stdout
+ignores expect.60 format-patch -1 --stdout
+respects expect.6030 diff HEAD^ HEAD --stat
+respects expect.6030 show --stat
+respects expect.6030 log -1 --stat
+EOF
+
+cat >expect.40 <<-'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+EOF
+cat >expect2.40 <<-'EOF'
  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
 EOF
@@ -66,22 +96,22 @@ do
        test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" '
                git $cmd $args --stat=40 >output &&
                grep " | " output >actual &&
-               test_cmp $expect.60 actual
+               test_cmp $expect.40 actual
        '
 
        test_expect_success "$cmd --stat-width=width with long name" '
                git $cmd $args --stat-width=40 >output &&
                grep " | " output >actual &&
-               test_cmp $expect.60 actual
+               test_cmp $expect.40 actual
        '
 
-       test_expect_success "$cmd --stat=...,name-width with long name" '
+       test_expect_success "$cmd --stat=width,name-width with long name" '
                git $cmd $args --stat=60,30 >output &&
                grep " | " output >actual &&
                test_cmp $expect.6030 actual
        '
 
-       test_expect_success "$cmd --stat-name-width with long name" '
+       test_expect_success "$cmd --stat-name-width=width with long name" '
                git $cmd $args --stat-name-width=30 >output &&
                grep " | " output >actual &&
                test_cmp $expect.6030 actual
@@ -93,8 +123,7 @@ expect show --stat
 expect log -1 --stat
 EOF
 
-
-test_expect_success 'preparation for big change tests' '
+test_expect_success 'preparation for big-change tests' '
        >abcd &&
        git add abcd &&
        git commit -m message &&
@@ -110,7 +139,7 @@ cat >expect72 <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success "format-patch --cover-letter ignores COLUMNS (big change)" '
+test_expect_success "format-patch --cover-letter ignores COLUMNS with big change" '
        COLUMNS=200 git format-patch -1 --stdout --cover-letter >output &&
        grep " | " output >actual &&
        test_cmp expect72 actual
@@ -130,7 +159,7 @@ cat >expect200-graph <<'EOF'
 EOF
 while read verb expect cmd args
 do
-       test_expect_success "$cmd $verb COLUMNS (big change)" '
+       test_expect_success "$cmd $verb COLUMNS with big change" '
                COLUMNS=200 git $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -138,7 +167,7 @@ do
 
        case "$cmd" in diff|show) continue;; esac
 
-       test_expect_success "$cmd --graph $verb COLUMNS (big change)" '
+       test_expect_success "$cmd --graph $verb COLUMNS with big change" '
                COLUMNS=200 git $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -158,7 +187,7 @@ cat >expect40-graph <<'EOF'
 EOF
 while read verb expect cmd args
 do
-       test_expect_success "$cmd $verb not enough COLUMNS (big change)" '
+       test_expect_success "$cmd $verb not enough COLUMNS with big change" '
                COLUMNS=40 git $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -166,7 +195,7 @@ do
 
        case "$cmd" in diff|show) continue;; esac
 
-       test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" '
+       test_expect_success "$cmd --graph $verb not enough COLUMNS with big change" '
                COLUMNS=40 git $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -186,7 +215,7 @@ cat >expect40-graph <<'EOF'
 EOF
 while read verb expect cmd args
 do
-       test_expect_success "$cmd $verb statGraphWidth config" '
+       test_expect_success "$cmd $verb diff.statGraphWidth" '
                git -c diff.statGraphWidth=26 $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -194,7 +223,7 @@ do
 
        case "$cmd" in diff|show) continue;; esac
 
-       test_expect_success "$cmd --graph $verb statGraphWidth config" '
+       test_expect_success "$cmd --graph $verb diff.statGraphWidth" '
                git -c diff.statGraphWidth=26 $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -206,7 +235,6 @@ respects expect40 show --stat
 respects expect40 log -1 --stat
 EOF
 
-
 cat >expect <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++
 EOF
@@ -227,7 +255,7 @@ do
                test_cmp expect actual
        '
 
-       test_expect_success "$cmd --stat-graph-width with big change" '
+       test_expect_success "$cmd --stat-graph-width=width with big change" '
                git $cmd $args --stat-graph-width=26 >output &&
                grep " | " output >actual &&
                test_cmp expect actual
@@ -241,7 +269,7 @@ do
                test_cmp expect-graph actual
        '
 
-       test_expect_success "$cmd --stat-graph-width --graph with big change" '
+       test_expect_success "$cmd --stat-graph-width=width --graph with big change" '
                git $cmd $args --stat-graph-width=26 --graph >output &&
                grep " | " output >actual &&
                test_cmp expect-graph actual
@@ -253,7 +281,7 @@ show --stat
 log -1 --stat
 EOF
 
-test_expect_success 'preparation for long filename tests' '
+test_expect_success 'preparation for long-name tests' '
        cp abcd aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
        git add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
        git commit -m message
@@ -301,7 +329,7 @@ cat >expect200-graph <<'EOF'
 EOF
 while read verb expect cmd args
 do
-       test_expect_success "$cmd $verb COLUMNS (long filename)" '
+       test_expect_success "$cmd $verb COLUMNS with long name" '
                COLUMNS=200 git $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -309,7 +337,7 @@ do
 
        case "$cmd" in diff|show) continue;; esac
 
-       test_expect_success "$cmd --graph $verb COLUMNS (long filename)" '
+       test_expect_success "$cmd --graph $verb COLUMNS with long name" '
                COLUMNS=200 git $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -330,7 +358,7 @@ EOF
 while read verb expect cmd args
 do
        test_expect_success COLUMNS_CAN_BE_1 \
-               "$cmd $verb prefix greater than COLUMNS (big change)" '
+               "$cmd $verb prefix greater than COLUMNS with big change" '
                COLUMNS=1 git $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -339,7 +367,7 @@ do
        case "$cmd" in diff|show) continue;; esac
 
        test_expect_success COLUMNS_CAN_BE_1 \
-               "$cmd --graph $verb prefix greater than COLUMNS (big change)" '
+               "$cmd --graph $verb prefix greater than COLUMNS with big change" '
                COLUMNS=1 git $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -354,8 +382,14 @@ EOF
 cat >expect <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success 'merge --stat respects COLUMNS (big change)' '
-       git checkout -b branch HEAD^^ &&
+test_expect_success 'merge --stat respects diff.statGraphWidth with big change' '
+       git checkout -b branch1 HEAD^^ &&
+       git -c diff.statGraphWidth=26 merge --stat --no-ff main^ >output &&
+       grep " | " output >actual &&
+       test_cmp expect40 actual
+'
+test_expect_success 'merge --stat respects COLUMNS with big change' '
+       git checkout -b branch2 HEAD^^ &&
        COLUMNS=100 git merge --stat --no-ff main^ >output &&
        grep " | " output >actual &&
        test_cmp expect actual
@@ -364,7 +398,17 @@ test_expect_success 'merge --stat respects COLUMNS (big change)' '
 cat >expect <<'EOF'
  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success 'merge --stat respects COLUMNS (long filename)' '
+cat >expect.30 <<'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++
+EOF
+test_expect_success 'merge --stat respects diff.statNameWidth with long name' '
+       git switch branch1 &&
+       git -c diff.statNameWidth=30 merge --stat --no-ff main >output &&
+       grep " | " output >actual &&
+       test_cmp expect.30 actual
+'
+test_expect_success 'merge --stat respects COLUMNS with long name' '
+       git switch branch2 &&
        COLUMNS=100 git merge --stat --no-ff main >output &&
        grep " | " output >actual &&
        test_cmp expect actual
index 3feadf0e3595b2950dd6b5698ba0f25969de8b93..5ce345d309eb70425b9c04cdbfa57d1611675af8 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='diff --no-index'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -55,7 +56,7 @@ test_expect_success 'git diff --no-index executed outside repo gives correct err
                export GIT_CEILING_DIRECTORIES &&
                cd non/git &&
                test_must_fail git diff --no-index a 2>actual.err &&
-               test_i18ngrep "usage: git diff --no-index" actual.err
+               test_grep "usage: git diff --no-index" actual.err
        )
 '
 
@@ -204,4 +205,83 @@ test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not lik
        test_cmp expected actual
 '
 
+test_expect_success "diff --no-index treats '-' as stdin" '
+       cat >expect <<-EOF &&
+       diff --git a/- b/a/1
+       index $ZERO_OID..$(git hash-object --stdin <a/1) 100644
+       --- a/-
+       +++ b/a/1
+       @@ -1 +1 @@
+       -x
+       +1
+       EOF
+
+       test_write_lines x | test_expect_code 1 \
+               git -c core.abbrev=no diff --no-index -- - a/1 >actual &&
+       test_cmp expect actual &&
+
+       test_write_lines 1 | git diff --no-index -- a/1 - >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success "diff --no-index -R treats '-' as stdin" '
+       cat >expect <<-EOF &&
+       diff --git b/a/1 a/-
+       index $(git hash-object --stdin <a/1)..$ZERO_OID 100644
+       --- b/a/1
+       +++ a/-
+       @@ -1 +1 @@
+       -1
+       +x
+       EOF
+
+       test_write_lines x | test_expect_code 1 \
+               git -c core.abbrev=no diff --no-index -R -- - a/1 >actual &&
+       test_cmp expect actual &&
+
+       test_write_lines 1 | git diff --no-index -R -- a/1 - >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'diff --no-index refuses to diff stdin and a directory' '
+       test_must_fail git diff --no-index -- - a </dev/null 2>err &&
+       grep "fatal: cannot compare stdin to a directory" err
+'
+
+test_expect_success PIPE 'diff --no-index refuses to diff a named pipe and a directory' '
+       test_when_finished "rm -f pipe" &&
+       mkfifo pipe &&
+       test_must_fail git diff --no-index -- pipe a 2>err &&
+       grep "fatal: cannot compare a named pipe to a directory" err
+'
+
+test_expect_success PIPE,SYMLINKS 'diff --no-index reads from pipes' '
+       test_when_finished "rm -f old new new-link" &&
+       mkfifo old &&
+       mkfifo new &&
+       ln -s new new-link &&
+       {
+               (test_write_lines a b c >old) &
+       } &&
+       test_when_finished "kill $! || :" &&
+       {
+               (test_write_lines a x c >new) &
+       } &&
+       test_when_finished "kill $! || :" &&
+
+       cat >expect <<-EOF &&
+       diff --git a/old b/new-link
+       --- a/old
+       +++ b/new-link
+       @@ -1,3 +1,3 @@
+        a
+       -b
+       +x
+        c
+       EOF
+
+       test_expect_code 1 git diff --no-index old new-link >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 294fb5531372d597a5a551b40e66437d7599e766..05c88f8cdf01d98b61eacff08b32cb57320e7d3b 100755 (executable)
@@ -10,7 +10,7 @@ test_expect_success 'create bogus tree' '
        bogus_tree=$(
                printf "100644 fooQ$name" |
                q_to_nul |
-               git hash-object -w --stdin -t tree
+               git hash-object --literally -w --stdin -t tree
        )
 '
 
index 73048d0a52683bed2d56de3b0be77680cecaaf5a..3ea9ae99e04b9304e4dd8014eed9b01795317a31 100755 (executable)
@@ -74,13 +74,13 @@ test_expect_success 'plumbing not affected' '
 test_expect_success 'non-integer config parsing' '
        git config diff.context no &&
        test_must_fail git diff 2>output &&
-       test_i18ngrep "bad numeric config value" output
+       test_grep "bad numeric config value" output
 '
 
 test_expect_success 'negative integer config parsing' '
        git config diff.context -1 &&
        test_must_fail git diff 2>output &&
-       test_i18ngrep "bad config variable" output
+       test_grep "bad config variable" output
 '
 
 test_expect_success '-U0 is valid, so is diff.context=0' '
index 54614b814dbc858e15fd82b8bc563ab9aae5fae8..2501c89c1c91ec7ddc02f4e285aa4c10c32b98f6 100755 (executable)
@@ -29,7 +29,7 @@ make_tree () {
                make_tree_entry "$1" "$2" "$3"
                shift; shift; shift
        done |
-       git hash-object -w -t tree --stdin
+       git hash-object --literally -w -t tree --stdin
 }
 
 # this is kind of a convoluted setup, but matches
index 9aaa068ed9bc9a7853ce8331ec5d66107a50b94b..a90b46b678ca35a6af71769149bcfa7cc2317a39 100755 (executable)
@@ -24,7 +24,7 @@ test_expect_success '-G matches' '
 
 test_expect_success '-S --pickaxe-regex' '
        git diff --name-only -S0 --pickaxe-regex HEAD^ >out &&
-       verbose test 4096-zeroes.txt = "$(cat out)"
+       test 4096-zeroes.txt = "$(cat out)"
 '
 
 test_done
index 28f42a4046e08ed61fcf2f300b826c33136e9e98..7af3a08862dec8a17b113191a3dbea56b0995cd3 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='behavior of diff when reading objects in a partial clone'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'git show batches blobs' '
@@ -150,7 +151,7 @@ test_expect_success 'diff does not fetch anything if inexact rename detection is
 
        # Ensure no fetches.
        GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD &&
-       ! test_path_exists trace
+       test_path_is_missing trace
 '
 
 test_expect_success 'diff --break-rewrites fetches only if necessary, and batches blobs if it does' '
@@ -170,7 +171,7 @@ test_expect_success 'diff --break-rewrites fetches only if necessary, and batche
 
        # Ensure no fetches.
        GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD &&
-       ! test_path_exists trace &&
+       test_path_is_missing trace &&
 
        # But with --break-rewrites, ensure that there is exactly 1 negotiation
        # by checking that there is only 1 "done" line sent. ("done" marks the
index 2d650d8f1032ca7c113fa0f48c72719fb08ab245..eff63c16b064d124b59a2e4b310e3ce5e190c532 100755 (executable)
@@ -34,7 +34,7 @@ test_expect_success setup '
        echo c >c &&
        git add c &&
        git commit -m C &&
-       git tag commit-C &&
+       git tag -m commit-C commit-C &&
        git merge -m D main &&
        git tag commit-D &&
        git checkout main &&
@@ -68,27 +68,27 @@ test_expect_success 'diff with two merge bases' '
 
 test_expect_success 'diff with no merge bases' '
        test_must_fail git diff br2...br3 2>err &&
-       test_i18ngrep "fatal: br2...br3: no merge base" err
+       test_grep "fatal: br2...br3: no merge base" err
 '
 
 test_expect_success 'diff with too many symmetric differences' '
        test_must_fail git diff br1...main br2...br3 2>err &&
-       test_i18ngrep "usage" err
+       test_grep "usage" err
 '
 
 test_expect_success 'diff with symmetric difference and extraneous arg' '
        test_must_fail git diff main br1...main 2>err &&
-       test_i18ngrep "usage" err
+       test_grep "usage" err
 '
 
 test_expect_success 'diff with two ranges' '
        test_must_fail git diff main br1..main br2..br3 2>err &&
-       test_i18ngrep "usage" err
+       test_grep "usage" err
 '
 
 test_expect_success 'diff with ranges and extra arg' '
        test_must_fail git diff main br1..main commit-D 2>err &&
-       test_i18ngrep "usage" err
+       test_grep "usage" err
 '
 
 test_expect_success 'diff --merge-base with no commits' '
@@ -97,7 +97,7 @@ test_expect_success 'diff --merge-base with no commits' '
 
 test_expect_success 'diff --merge-base with three commits' '
        test_must_fail git diff --merge-base br1 br2 main 2>err &&
-       test_i18ngrep "usage" err
+       test_grep "usage" err
 '
 
 for cmd in diff-index diff
@@ -109,6 +109,13 @@ do
                test_cmp expect actual
        '
 
+       test_expect_success "$cmd --merge-base with annotated tag" '
+               git checkout main &&
+               git $cmd commit-C >expect &&
+               git $cmd --merge-base commit-C >actual &&
+               test_cmp expect actual
+       '
+
        test_expect_success "$cmd --merge-base with one commit and unstaged changes" '
                git checkout main &&
                test_when_finished git reset --hard &&
@@ -143,19 +150,19 @@ do
        test_expect_success "$cmd --merge-base with non-commit" '
                git checkout main &&
                test_must_fail git $cmd --merge-base main^{tree} 2>err &&
-               test_i18ngrep "fatal: --merge-base only works with commits" err
+               test_grep "is a tree, not a commit" err
        '
 
        test_expect_success "$cmd --merge-base with no merge bases and one commit" '
                git checkout main &&
                test_must_fail git $cmd --merge-base br3 2>err &&
-               test_i18ngrep "fatal: no merge base found" err
+               test_grep "fatal: no merge base found" err
        '
 
        test_expect_success "$cmd --merge-base with multiple merge bases and one commit" '
                git checkout main &&
                test_must_fail git $cmd --merge-base br1 2>err &&
-               test_i18ngrep "fatal: multiple merge bases found" err
+               test_grep "fatal: multiple merge bases found" err
        '
 done
 
@@ -169,28 +176,28 @@ do
 
        test_expect_success "$cmd --merge-base commit and non-commit" '
                test_must_fail git $cmd --merge-base br2 main^{tree} 2>err &&
-               test_i18ngrep "fatal: --merge-base only works with commits" err
+               test_grep "is a tree, not a commit" err
        '
 
        test_expect_success "$cmd --merge-base with no merge bases and two commits" '
                test_must_fail git $cmd --merge-base br2 br3 2>err &&
-               test_i18ngrep "fatal: no merge base found" err
+               test_grep "fatal: no merge base found" err
        '
 
        test_expect_success "$cmd --merge-base with multiple merge bases and two commits" '
                test_must_fail git $cmd --merge-base main br1 2>err &&
-               test_i18ngrep "fatal: multiple merge bases found" err
+               test_grep "fatal: multiple merge bases found" err
        '
 done
 
 test_expect_success 'diff-tree --merge-base with one commit' '
        test_must_fail git diff-tree --merge-base main 2>err &&
-       test_i18ngrep "fatal: --merge-base only works with two commits" err
+       test_grep "fatal: --merge-base only works with two commits" err
 '
 
 test_expect_success 'diff --merge-base with range' '
        test_must_fail git diff --merge-base br2..br3 2>err &&
-       test_i18ngrep "fatal: --merge-base does not work with ranges" err
+       test_grep "fatal: --merge-base does not work with ranges" err
 '
 
 test_done
index 1618a6dbc7c718677cf669b5f6fc96f2682992fb..e9a87d761d168323ada1e58ecd9df719cc7511b3 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='patching from inconvenient places'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index e95e6d4e7d6419236c1e8bb9bc519b05ea8efec3..cbef0a593fb7ccfded7b9e97735c41dda0ce1cbd 100755 (executable)
@@ -74,7 +74,7 @@ test_expect_success SYMLINKS 'symlink escape when creating new files' '
        error: affected file ${SQ}renamed-symlink/create-me${SQ} is beyond a symbolic link
        EOF
        test_cmp expected_stderr stderr &&
-       ! test_path_exists .git/create-me
+       test_path_is_missing .git/create-me
 '
 
 test_expect_success SYMLINKS 'symlink escape when modifying file' '
@@ -136,7 +136,7 @@ test_expect_success SYMLINKS '--reject removes .rej symlink if it exists' '
 
        ln -s foo file.t.rej &&
        test_must_fail git apply patch --reject 2>err &&
-       test_i18ngrep "Rejected hunk" err &&
+       test_grep "Rejected hunk" err &&
        test_path_is_missing foo &&
        test_path_is_file file.t.rej
 '
index 497b62868d4aa0f81c62178ee9b1a5a108b89c0d..697e86c0ff456028948a769f2b6ccfa66bee2dc2 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success 'apply git diff with -p2' '
 test_expect_success 'apply with too large -p' '
        cp file1.saved file1 &&
        test_must_fail git apply --stat -p3 patch.file 2>err &&
-       test_i18ngrep "removing 3 leading" err
+       test_grep "removing 3 leading" err
 '
 
 test_expect_success 'apply (-p2) traditional diff with funny filenames' '
@@ -53,7 +53,7 @@ test_expect_success 'apply (-p2) traditional diff with funny filenames' '
 test_expect_success 'apply with too large -p and fancy filename' '
        cp file1.saved file1 &&
        test_must_fail git apply --stat -p3 patch.escaped 2>err &&
-       test_i18ngrep "removing 3 leading" err
+       test_grep "removing 3 leading" err
 '
 
 test_expect_success 'apply (-p2) diff, mode change only' '
index 96965373036a6944971b28cbf5c9f42e2c9abdb2..2089d84f64577b2b0ac6fdd762cfec8bd259c96c 100755 (executable)
@@ -95,19 +95,19 @@ test_expect_success SYMLINKS 'do not follow symbolic link (same input)' '
 
        # same input creates a confusing symbolic link
        test_must_fail git apply patch 2>error-wt &&
-       test_i18ngrep "beyond a symbolic link" error-wt &&
+       test_grep "beyond a symbolic link" error-wt &&
        test_path_is_missing arch/x86_64/dir &&
        test_path_is_missing arch/i386/dir/file &&
 
        test_must_fail git apply --index patch 2>error-ix &&
-       test_i18ngrep "beyond a symbolic link" error-ix &&
+       test_grep "beyond a symbolic link" error-ix &&
        test_path_is_missing arch/x86_64/dir &&
        test_path_is_missing arch/i386/dir/file &&
        test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
        test_must_fail git ls-files --error-unmatch arch/i386/dir &&
 
        test_must_fail git apply --cached patch 2>error-ct &&
-       test_i18ngrep "beyond a symbolic link" error-ct &&
+       test_grep "beyond a symbolic link" error-ct &&
        test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
        test_must_fail git ls-files --error-unmatch arch/i386/dir &&
 
@@ -135,23 +135,23 @@ test_expect_success SYMLINKS 'do not follow symbolic link (existing)' '
        git add arch/x86_64/dir &&
 
        test_must_fail git apply add_file.patch 2>error-wt-add &&
-       test_i18ngrep "beyond a symbolic link" error-wt-add &&
+       test_grep "beyond a symbolic link" error-wt-add &&
        test_path_is_missing arch/i386/dir/file &&
 
        mkdir arch/i386/dir &&
        >arch/i386/dir/file &&
        test_must_fail git apply del_file.patch 2>error-wt-del &&
-       test_i18ngrep "beyond a symbolic link" error-wt-del &&
+       test_grep "beyond a symbolic link" error-wt-del &&
        test_path_is_file arch/i386/dir/file &&
        rm arch/i386/dir/file &&
 
        test_must_fail git apply --index add_file.patch 2>error-ix-add &&
-       test_i18ngrep "beyond a symbolic link" error-ix-add &&
+       test_grep "beyond a symbolic link" error-ix-add &&
        test_path_is_missing arch/i386/dir/file &&
        test_must_fail git ls-files --error-unmatch arch/i386/dir &&
 
        test_must_fail git apply --cached add_file.patch 2>error-ct-file &&
-       test_i18ngrep "beyond a symbolic link" error-ct-file &&
+       test_grep "beyond a symbolic link" error-ct-file &&
        test_must_fail git ls-files --error-unmatch arch/i386/dir
 '
 
index a1c7686519ebb1284d818fd3f5615853082abc01..2775bfadd8619b2a5e2440209c46c0658205ddcd 100755 (executable)
@@ -41,7 +41,8 @@ test_expect_success FILEMODE 'same mode (index only)' '
        chmod +x file &&
        git add file &&
        git apply --cached patch-0.txt &&
-       git ls-files -s file | grep "^100755"
+       git ls-files -s file >ls-files-output &&
+       test_grep "^100755" ls-files-output
 '
 
 test_expect_success FILEMODE 'mode update (no index)' '
@@ -60,19 +61,20 @@ test_expect_success FILEMODE 'mode update (with index)' '
 test_expect_success FILEMODE 'mode update (index only)' '
        git reset --hard &&
        git apply --cached patch-1.txt &&
-       git ls-files -s file | grep "^100755"
+       git ls-files -s file >ls-files-output &&
+       test_grep "^100755" ls-files-output
 '
 
 test_expect_success FILEMODE 'empty mode is rejected' '
        git reset --hard &&
        test_must_fail git apply patch-empty-mode.txt 2>err &&
-       test_i18ngrep "invalid mode" err
+       test_grep "invalid mode" err
 '
 
 test_expect_success FILEMODE 'bogus mode is rejected' '
        git reset --hard &&
        test_must_fail git apply patch-bogus-mode.txt 2>err &&
-       test_i18ngrep "invalid mode" err
+       test_grep "invalid mode" err
 '
 
 test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree files' '
index 35f1060bc8b47f3f4de129621392c0b23b8f4c18..c21ddb29466ec46b3441313ada2c36b3173067b7 100755 (executable)
@@ -32,9 +32,9 @@ EOF
 
 test_expect_success 'apply diff with inconsistent filenames in headers' '
        test_must_fail git apply bad1.patch 2>err &&
-       test_i18ngrep "inconsistent new filename" err &&
+       test_grep "inconsistent new filename" err &&
        test_must_fail git apply bad2.patch 2>err &&
-       test_i18ngrep "inconsistent old filename" err
+       test_grep "inconsistent old filename" err
 '
 
 test_expect_success 'apply diff with new filename missing from headers' '
@@ -46,7 +46,7 @@ test_expect_success 'apply diff with new filename missing from headers' '
        +1
        EOF
        test_must_fail git apply missing_new_filename.diff 2>err &&
-       test_i18ngrep "lacks filename information" err
+       test_grep "lacks filename information" err
 '
 
 test_expect_success 'apply diff with old filename missing from headers' '
@@ -58,7 +58,7 @@ test_expect_success 'apply diff with old filename missing from headers' '
        -1
        EOF
        test_must_fail git apply missing_old_filename.diff 2>err &&
-       test_i18ngrep "lacks filename information" err
+       test_grep "lacks filename information" err
 '
 
 test_done
index 6bc3fb97a754bf242f8baf1ba867aa9f7fa93d0d..d3502c6fddf5a6a69bde769b1f808c9431d1e38e 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git apply with weird postimage filenames'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4141-apply-too-large.sh b/t/t4141-apply-too-large.sh
new file mode 100755 (executable)
index 0000000..20cc120
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='git apply with too-large patch'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success EXPENSIVE 'git apply rejects patches that are too large' '
+       sz=$((1024 * 1024 * 1023)) &&
+       {
+               cat <<-\EOF &&
+               diff --git a/file b/file
+               new file mode 100644
+               --- /dev/null
+               +++ b/file
+               @@ -0,0 +1 @@
+               EOF
+               test-tool genzeros
+       } | test_copy_bytes $sz | test_must_fail git apply 2>err &&
+       grep "patch too large" err
+'
+
+test_done
index cdad4b688078adc24da922bce14e130982f4484e..3b125762694e02c9f67dc204f1e182caecead90c 100755 (executable)
@@ -103,7 +103,7 @@ test_expect_success setup '
 
        git format-patch --stdout first >patch1 &&
        {
-               echo "Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
+               echo "Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
                echo "X-Fake-Field: Line One" &&
                echo "X-Fake-Field: Line Two" &&
                echo "X-Fake-Field: Line Three" &&
@@ -345,6 +345,21 @@ test_expect_success 'am with failing applypatch-msg hook' '
        test_cmp_rev first HEAD
 '
 
+test_expect_success 'am with failing applypatch-msg hook (no verify)' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout first &&
+       test_hook applypatch-msg <<-\EOF &&
+       echo hook-message >"$1"
+       exit 1
+       EOF
+       git am --no-verify patch1 &&
+       test_path_is_missing .git/rebase-apply &&
+       git diff --exit-code second &&
+       git log -1 --format=format:%B >actual &&
+       test_cmp msg actual
+'
+
 test_expect_success 'am with pre-applypatch hook' '
        rm -fr .git/rebase-apply &&
        git reset --hard &&
@@ -374,6 +389,23 @@ test_expect_success 'am with failing pre-applypatch hook' '
        test_cmp_rev first HEAD
 '
 
+test_expect_success 'am with failing pre-applypatch hook (no verify)' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout first &&
+       touch empty-file &&
+       test_hook pre-applypatch <<-\EOF &&
+       rm empty-file
+       exit 1
+       EOF
+       git am --no-verify patch1 &&
+       test_path_is_missing .git/rebase-apply &&
+       test_path_is_file empty-file &&
+       git diff --exit-code second &&
+       git log -1 --format=format:%B >actual &&
+       test_cmp msg actual
+'
+
 test_expect_success 'am with post-applypatch hook' '
        rm -fr .git/rebase-apply &&
        git reset --hard &&
@@ -747,7 +779,7 @@ test_expect_success 'am --resolved fails if index has unmerged entries' '
        test_must_fail git am --resolved >err &&
        test_path_is_dir .git/rebase-apply &&
        test_cmp_rev second HEAD &&
-       test_i18ngrep "still have unmerged paths" err
+       test_grep "still have unmerged paths" err
 '
 
 test_expect_success 'am takes patches from a Pine mailbox' '
@@ -881,7 +913,7 @@ test_expect_success 'am newline in subject' '
        test_tick &&
        sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
        git am <patchnl >output.out 2>&1 &&
-       test_i18ngrep "^Applying: second \\\n foo$" output.out
+       test_grep "^Applying: second \\\n foo$" output.out
 '
 
 test_expect_success 'am -q is quiet' '
@@ -910,7 +942,7 @@ test_expect_success 'am --message-id really adds the message id' '
        git am --message-id patch1.eml &&
        test_path_is_missing .git/rebase-apply &&
        git cat-file commit HEAD | tail -n1 >actual &&
-       grep Message-Id patch1.eml >expected &&
+       grep Message-ID patch1.eml >expected &&
        test_cmp expected actual
 '
 
@@ -922,7 +954,7 @@ test_expect_success 'am.messageid really adds the message id' '
        git am patch1.eml &&
        test_path_is_missing .git/rebase-apply &&
        git cat-file commit HEAD | tail -n1 >actual &&
-       grep Message-Id patch1.eml >expected &&
+       grep Message-ID patch1.eml >expected &&
        test_cmp expected actual
 '
 
@@ -933,7 +965,7 @@ test_expect_success 'am --message-id -s signs off after the message id' '
        git am -s --message-id patch1.eml &&
        test_path_is_missing .git/rebase-apply &&
        git cat-file commit HEAD | tail -n2 | head -n1 >actual &&
-       grep Message-Id patch1.eml >expected &&
+       grep Message-ID patch1.eml >expected &&
        test_cmp expected actual
 '
 
@@ -1033,7 +1065,7 @@ test_expect_success 'am --patch-format=mboxrd handles mboxrd' '
        >From extra escape for reversibility
        INPUT_END
        git commit -F msg &&
-       git format-patch --pretty=mboxrd --stdout -1 >mboxrd1 &&
+       git -c format.mboxrd format-patch --stdout -1 >mboxrd1 &&
        grep "^>From could trip up a loose mbox parser" mboxrd1 &&
        git checkout -f first &&
        git am --patch-format=mboxrd mboxrd1 &&
index 5ed7e228274ed6d283f138c4fe244f9b3d14e2f3..edb38da7010d33315f02a345a076ef803d460a32 100755 (executable)
@@ -46,7 +46,7 @@ do
 
        test_expect_success "am$with3 --skip continue after failed am$with3" '
                test_must_fail git am$with3 --skip >output &&
-               test_i18ngrep "^Applying: 6$" output &&
+               test_grep "^Applying: 6$" output &&
                test_cmp file-2-expect file-2 &&
                test ! -f .git/MERGE_RR
        '
index 4c68245acad30534d7e3e1f6e980e683d8b5aafd..9f2edba1f833a7127589f9c4883b91bf99bc00b1 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test subject preservation with format-patch | am'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 make_patches() {
index b7c3861407d026e7064dbab0237bfa709859c462..4add7c775782ff9c3f20a372fa2a076c4651b85b 100755 (executable)
@@ -53,7 +53,7 @@ test_expect_success '--no-quiet overrides --quiet' '
        # Applying side1 will be quiet.
        test_must_fail git am --quiet side[123].eml >out &&
        test_path_is_dir .git/rebase-apply &&
-       test_i18ngrep ! "^Applying: " out &&
+       test_grep ! "^Applying: " out &&
        echo side1 >file &&
        git add file &&
 
index 7025cfdae539c5109df5a33a1e0ec6e7bfeeb688..fb53dddf799c9863ec1df4fe82147ead6dead15a 100755 (executable)
@@ -433,13 +433,13 @@ test_expect_success 'rerere --no-no-rerere-autoupdate' '
        git update-index --index-info <failedmerge &&
        cp file3.conflict file3 &&
        test_must_fail git rerere --no-no-rerere-autoupdate 2>err &&
-       test_i18ngrep [Uu]sage err &&
+       test_grep [Uu]sage err &&
        test_must_fail git update-index --refresh
 '
 
 test_expect_success 'rerere -h' '
        test_must_fail git rerere -h >help &&
-       test_i18ngrep [Uu]sage help
+       test_grep [Uu]sage help
 '
 
 concat_insert () {
index 3095b1b2ffee6047abb54d5fad4d6f1e6b07feaf..d7382709fc11055be003751d802f953ed56b6eab 100755 (executable)
@@ -83,6 +83,13 @@ test_expect_success 'pretty format' '
        test_cmp expect log.predictable
 '
 
+test_expect_success 'pretty format (with --date)' '
+       sed "s/SUBJECT/2005-04-07 OBJECT_NAME/" expect.template >expect &&
+       git shortlog --format="%ad %H" --date=short HEAD >log &&
+       fuzz log >log.predictable &&
+       test_cmp expect log.predictable
+'
+
 test_expect_success '--abbrev' '
        sed s/SUBJECT/OBJID/ expect.template >expect &&
        git shortlog --format="%h" --abbrev=35 HEAD >log &&
@@ -132,7 +139,7 @@ test_expect_success !MINGW 'shortlog can read --format=raw output' '
 
 test_expect_success 'shortlog from non-git directory refuses extra arguments' '
        test_must_fail env GIT_DIR=non-existing git shortlog foo 2>out &&
-       test_i18ngrep "too many arguments" out
+       test_grep "too many arguments" out
 '
 
 test_expect_success 'shortlog should add newline when input line matches wraplen' '
@@ -237,6 +244,26 @@ test_expect_success 'shortlog --group=trailer:signed-off-by' '
        test_cmp expect actual
 '
 
+test_expect_success 'shortlog --group=format' '
+       git shortlog -s --date="format:%Y" --group="format:%cN (%cd)" \
+               HEAD >actual &&
+       cat >expect <<-\EOF &&
+            4  C O Mitter (2005)
+            1  Sin Nombre (2005)
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'shortlog --group=<format> DWIM' '
+       git shortlog -s --date="format:%Y" --group="%cN (%cd)" HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'shortlog bogus --group' '
+       test_must_fail git shortlog --group=bogus HEAD 2>err &&
+       grep "unknown group type" err
+'
+
 test_expect_success 'trailer idents are split' '
        cat >expect <<-\EOF &&
             2  C O Mitter
@@ -319,6 +346,18 @@ test_expect_success 'shortlog can match multiple groups' '
        test_cmp expect actual
 '
 
+test_expect_success 'shortlog can match multiple format groups' '
+       GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" \
+               git commit --allow-empty -m "identical names" &&
+       test_tick &&
+       cat >expect <<-\EOF &&
+            2  A U Thor
+            1  C O Mitter
+       EOF
+       git shortlog -ns --group="%cn" --group="%an" -2 HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'set up option selection tests' '
        git commit --allow-empty -F - <<-\EOF
        subject
index cc15cb4ff62ab4c939b369826e5795e46f218d84..ddd205f98abf7f8d58555761eaf20006bb8433e8 100755 (executable)
@@ -187,6 +187,21 @@ test_expect_success 'git config log.follow does not die with no paths' '
        git log --
 '
 
+test_expect_success 'git log --follow rejects unsupported pathspec magic' '
+       test_must_fail git log --follow ":(top,glob,icase)ichi" 2>stderr &&
+       # check full error message; we want to be sure we mention both
+       # of the rejected types (glob,icase), but not the allowed one (top)
+       echo "fatal: pathspec magic not supported by --follow: ${SQ}glob${SQ}, ${SQ}icase${SQ}" >expect &&
+       test_cmp expect stderr
+'
+
+test_expect_success 'log.follow disabled with unsupported pathspec magic' '
+       test_config log.follow true &&
+       git log --format=%s ":(glob,icase)ichi" >actual &&
+       echo third >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git config log.follow is overridden by --no-follow' '
        test_config log.follow true &&
        git log --no-follow --pretty="format:%s" ichi >actual &&
@@ -249,6 +264,15 @@ test_expect_success 'log --grep' '
        test_cmp expect actual
 '
 
+for noop_opt in --invert-grep --all-match
+do
+       test_expect_success "log $noop_opt without --grep is a NOOP" '
+               git log >expect &&
+               git log $noop_opt >actual &&
+               test_cmp expect actual
+       '
+done
+
 cat > expect << EOF
 second
 initial
@@ -826,6 +850,21 @@ test_expect_success 'log.decorate configuration' '
 
 '
 
+test_expect_success 'parse log.excludeDecoration with no value' '
+       cp .git/config .git/config.orig &&
+       test_when_finished mv .git/config.orig .git/config &&
+
+       cat >>.git/config <<-\EOF &&
+       [log]
+               excludeDecoration
+       EOF
+       cat >expect <<-\EOF &&
+       error: missing value for '\''log.excludeDecoration'\''
+       EOF
+       git log --decorate=short 2>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'decorate-refs with glob' '
        cat >expect.decorate <<-\EOF &&
        Merge-tag-reach
@@ -1845,7 +1884,7 @@ test_expect_success '--no-graph does not unset --parents' '
 
 test_expect_success '--reverse and --graph conflict' '
        test_must_fail git log --reverse --graph 2>stderr &&
-       test_i18ngrep "cannot be used together" stderr
+       test_grep "cannot be used together" stderr
 '
 
 test_expect_success '--reverse --graph --no-graph works' '
@@ -1856,7 +1895,7 @@ test_expect_success '--reverse --graph --no-graph works' '
 
 test_expect_success '--show-linear-break and --graph conflict' '
        test_must_fail git log --show-linear-break --graph 2>stderr &&
-       test_i18ngrep "cannot be used together" stderr
+       test_grep "cannot be used together" stderr
 '
 
 test_expect_success '--show-linear-break --graph --no-graph works' '
@@ -1867,7 +1906,7 @@ test_expect_success '--show-linear-break --graph --no-graph works' '
 
 test_expect_success '--no-walk and --graph conflict' '
        test_must_fail git log --no-walk --graph 2>stderr &&
-       test_i18ngrep "cannot be used together" stderr
+       test_grep "cannot be used together" stderr
 '
 
 test_expect_success '--no-walk --graph --no-graph works' '
@@ -1878,8 +1917,8 @@ test_expect_success '--no-walk --graph --no-graph works' '
 
 test_expect_success '--walk-reflogs and --graph conflict' '
        test_must_fail git log --walk-reflogs --graph 2>stderr &&
-       (test_i18ngrep "cannot combine" stderr ||
-               test_i18ngrep "cannot be used together" stderr)
+       (test_grep "cannot combine" stderr ||
+               test_grep "cannot be used together" stderr)
 '
 
 test_expect_success '--walk-reflogs --graph --no-graph works' '
@@ -2213,7 +2252,7 @@ test_expect_success 'log on empty repo fails' '
        git init empty &&
        test_when_finished "rm -rf empty" &&
        test_must_fail git -C empty log 2>stderr &&
-       test_i18ngrep does.not.have.any.commits stderr
+       test_grep does.not.have.any.commits stderr
 '
 
 test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '
@@ -2221,16 +2260,16 @@ test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '
        test_when_finished "rm -rf empty" &&
        echo 1234abcd >empty/.git/refs/heads/main &&
        test_must_fail git -C empty log 2>stderr &&
-       test_i18ngrep broken stderr
+       test_grep broken stderr
 '
 
 test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '
        git init empty &&
-       echo "ref: refs/heads/invalid.lock" > empty/.git/HEAD &&
+       test-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&
        test_must_fail git -C empty log 2>stderr &&
-       test_i18ngrep broken stderr &&
+       test_grep broken stderr &&
        test_must_fail git -C empty log --default totally-bogus 2>stderr &&
-       test_i18ngrep broken stderr
+       test_grep broken stderr
 '
 
 test_expect_success 'log does not default to HEAD when rev input is given' '
@@ -2319,10 +2358,10 @@ test_expect_success 'log --decorate does not include things outside filter' '
 '
 
 test_expect_success 'log --end-of-options' '
-       git update-ref refs/heads/--source HEAD &&
-       git log --end-of-options --source >actual &&
-       git log >expect &&
-       test_cmp expect actual
+       git update-ref refs/heads/--source HEAD &&
+       git log --end-of-options --source >actual &&
+       git log >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'set up commits with different authors' '
index cd1cab3e54b9170d5751279bd68e02dbc3cbcad2..8a88dd7900ca8a63327bf0853403a38a2c2bc5a9 100755 (executable)
@@ -360,7 +360,7 @@ test_expect_success 'mailmap.blob might be the wrong type' '
        cp default.map .mailmap &&
 
        git -c mailmap.blob=HEAD: shortlog HEAD >actual 2>err &&
-       test_i18ngrep "mailmap is not a blob" err &&
+       test_grep "mailmap is not a blob" err &&
        test_cmp expect actual
 '
 
@@ -466,7 +466,7 @@ test_expect_success 'gitmailmap(5) example output: example #1' '
        Author Jane Doe <jane@laptop.(none)> maps to Jane Doe <jane@laptop.(none)>
        Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 
-       Author Jane D <jane@desktop.(none)> maps to Jane Doe <jane@desktop.(none)>
+       Author Jane D. <jane@desktop.(none)> maps to Jane Doe <jane@desktop.(none)>
        Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
        EOF
        git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
@@ -494,7 +494,7 @@ test_expect_success 'gitmailmap(5) example output: example #2' '
        Author Jane Doe <jane@laptop.(none)> maps to Jane Doe <jane@example.com>
        Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 
-       Author Jane D <jane@desktop.(none)> maps to Jane Doe <jane@example.com>
+       Author Jane D. <jane@desktop.(none)> maps to Jane Doe <jane@example.com>
        Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
        EOF
        git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
@@ -1022,4 +1022,69 @@ test_expect_success '--mailmap enables mailmap in cat-file for annotated tag obj
        test_cmp expect actual
 '
 
+test_expect_success 'git cat-file -s returns correct size with --use-mailmap' '
+       test_when_finished "rm .mailmap" &&
+       cat >.mailmap <<-\EOF &&
+       C O Mitter <committer@example.com> Orig <orig@example.com>
+       EOF
+       git cat-file commit HEAD >commit.out &&
+       echo $(wc -c <commit.out) >expect &&
+       git cat-file --use-mailmap commit HEAD >commit.out &&
+       echo $(wc -c <commit.out) >>expect &&
+       git cat-file -s HEAD >actual &&
+       git cat-file --use-mailmap -s HEAD >>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git cat-file -s returns correct size with --use-mailmap for tag objects' '
+       test_when_finished "rm .mailmap" &&
+       cat >.mailmap <<-\EOF &&
+       Orig <orig@example.com> C O Mitter <committer@example.com>
+       EOF
+       git tag -a -m "annotated tag" v3 &&
+       git cat-file tag v3 >tag.out &&
+       echo $(wc -c <tag.out) >expect &&
+       git cat-file --use-mailmap tag v3 >tag.out &&
+       echo $(wc -c <tag.out) >>expect &&
+       git cat-file -s v3 >actual &&
+       git cat-file --use-mailmap -s v3 >>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check returns correct size with --use-mailmap' '
+       test_when_finished "rm .mailmap" &&
+       cat >.mailmap <<-\EOF &&
+       C O Mitter <committer@example.com> Orig <orig@example.com>
+       EOF
+       git cat-file commit HEAD >commit.out &&
+       commit_size=$(wc -c <commit.out) &&
+       commit_sha=$(git rev-parse HEAD) &&
+       echo $commit_sha commit $commit_size >expect &&
+       git cat-file --use-mailmap commit HEAD >commit.out &&
+       commit_size=$(wc -c <commit.out) &&
+       echo $commit_sha commit $commit_size >>expect &&
+       echo "HEAD" >in &&
+       git cat-file --batch-check <in >actual &&
+       git cat-file --use-mailmap --batch-check <in >>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command returns correct size with --use-mailmap' '
+       test_when_finished "rm .mailmap" &&
+       cat >.mailmap <<-\EOF &&
+       C O Mitter <committer@example.com> Orig <orig@example.com>
+       EOF
+       git cat-file commit HEAD >commit.out &&
+       commit_size=$(wc -c <commit.out) &&
+       commit_sha=$(git rev-parse HEAD) &&
+       echo $commit_sha commit $commit_size >expect &&
+       git cat-file --use-mailmap commit HEAD >commit.out &&
+       commit_size=$(wc -c <commit.out) &&
+       echo $commit_sha commit $commit_size >>expect &&
+       echo "info HEAD" >in &&
+       git cat-file --batch-command <in >actual &&
+       git cat-file --use-mailmap --batch-command <in >>actual &&
+       test_cmp expect actual
+'
+
 test_done
index a730c0db9856c1d92ba840ca07f4c67064d437a1..a7fa94ce0a212e5062a6cc0356261d29af973e32 100755 (executable)
@@ -8,13 +8,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-       as="a a a a a a a a" && # eight a
-       test_write_lines $as >foo &&
-       test_write_lines $as >bar &&
+       str="ab cd ef gh ij kl mn op" &&
+       test_write_lines $str >foo &&
+       test_write_lines $str >bar &&
        git add foo bar &&
        git commit -a -m initial &&
-       test_write_lines $as b >foo &&
-       test_write_lines $as b >bar &&
+       test_write_lines $str b >foo &&
+       test_write_lines $str b >bar &&
        git commit -a -m first &&
        git checkout -b same main &&
        git commit --amend -m same-msg &&
@@ -22,8 +22,23 @@ test_expect_success 'setup' '
        echo c >foo &&
        echo c >bar &&
        git commit --amend -a -m notsame-msg &&
+       git checkout -b with_space main~ &&
+       cat >foo <<-\EOF &&
+       a  b
+       c d
+       e    f
+         g   h
+           i   j
+       k l
+       m   n
+       op
+       EOF
+       cp foo bar &&
+       git add foo bar &&
+       git commit --amend -m "with spaces" &&
        test_write_lines bar foo >bar-then-foo &&
        test_write_lines foo bar >foo-then-bar
+
 '
 
 test_expect_success 'patch-id output is well-formed' '
@@ -42,7 +57,7 @@ calc_patch_id () {
 }
 
 get_top_diff () {
-       git log -p -1 "$@" -O bar-then-foo --
+       git log -p -1 "$@" -O bar-then-foo --full-index --
 }
 
 get_patch_id () {
@@ -61,6 +76,33 @@ test_expect_success 'patch-id detects inequality' '
        get_patch_id notsame &&
        ! test_cmp patch-id_main patch-id_notsame
 '
+test_expect_success 'patch-id detects equality binary' '
+       cat >.gitattributes <<-\EOF &&
+       foo binary
+       bar binary
+       EOF
+       get_patch_id main &&
+       get_patch_id same &&
+       git log -p -1 --binary main >top-diff.output &&
+       calc_patch_id <top-diff.output main_binpatch &&
+       git log -p -1 --binary same >top-diff.output &&
+       calc_patch_id <top-diff.output same_binpatch &&
+       test_cmp patch-id_main patch-id_main_binpatch &&
+       test_cmp patch-id_same patch-id_same_binpatch &&
+       test_cmp patch-id_main patch-id_same &&
+       test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'patch-id detects inequality binary' '
+       cat >.gitattributes <<-\EOF &&
+       foo binary
+       bar binary
+       EOF
+       get_patch_id main &&
+       get_patch_id notsame &&
+       ! test_cmp patch-id_main patch-id_notsame &&
+       test_when_finished "rm .gitattributes"
+'
 
 test_expect_success 'patch-id supports git-format-patch output' '
        get_patch_id main &&
@@ -101,9 +143,21 @@ test_patch_id_file_order () {
        git format-patch -1 --stdout -O foo-then-bar >format-patch.output &&
        calc_patch_id <format-patch.output "ordered-$name" "$@" &&
        cmp_patch_id $relevant "$name" "ordered-$name"
+}
 
+test_patch_id_whitespace () {
+       relevant="$1"
+       shift
+       name="ws-${1}-$relevant"
+       shift
+       get_top_diff "main~" >top-diff.output &&
+       calc_patch_id <top-diff.output "$name" "$@" &&
+       get_top_diff "with_space" >top-diff.output &&
+       calc_patch_id <top-diff.output "ws-$name" "$@" &&
+       cmp_patch_id $relevant "$name" "ws-$name"
 }
 
+
 # combined test for options: add more tests here to make them
 # run with all options
 test_patch_id () {
@@ -119,6 +173,14 @@ test_expect_success 'file order is relevant with --unstable' '
        test_patch_id_file_order relevant --unstable --unstable
 '
 
+test_expect_success 'whitespace is relevant with --verbatim' '
+       test_patch_id_whitespace relevant --verbatim --verbatim
+'
+
+test_expect_success 'whitespace is irrelevant without --verbatim' '
+       test_patch_id_whitespace irrelevant --stable --stable
+'
+
 #Now test various option combinations.
 test_expect_success 'default is unstable' '
        test_patch_id relevant default
@@ -134,6 +196,17 @@ test_expect_success 'patchid.stable = false is unstable' '
        test_patch_id relevant patchid.stable=false
 '
 
+test_expect_success 'patchid.verbatim = true is correct and stable' '
+       test_config patchid.verbatim true &&
+       test_patch_id_whitespace relevant patchid.verbatim=true &&
+       test_patch_id irrelevant patchid.verbatim=true
+'
+
+test_expect_success 'patchid.verbatim = false is unstable' '
+       test_config patchid.verbatim false &&
+       test_patch_id relevant patchid.verbatim=false
+'
+
 test_expect_success '--unstable overrides patchid.stable = true' '
        test_config patchid.stable true &&
        test_patch_id relevant patchid.stable=true--unstable --unstable
@@ -144,6 +217,11 @@ test_expect_success '--stable overrides patchid.stable = false' '
        test_patch_id irrelevant patchid.stable=false--stable --stable
 '
 
+test_expect_success '--verbatim overrides patchid.stable = false' '
+       test_config patchid.stable false &&
+       test_patch_id_whitespace relevant stable=false--verbatim --verbatim
+'
+
 test_expect_success 'patch-id supports git-format-patch MIME output' '
        get_patch_id main &&
        git checkout same &&
@@ -198,7 +276,10 @@ test_expect_success 'patch-id handles no-nl-at-eof markers' '
        EOF
        calc_patch_id nonl <nonl &&
        calc_patch_id withnl <withnl &&
-       test_cmp patch-id_nonl patch-id_withnl
+       test_cmp patch-id_nonl patch-id_withnl &&
+       calc_patch_id nonl-inc-ws --verbatim <nonl &&
+       calc_patch_id withnl-inc-ws --verbatim <withnl &&
+       ! test_cmp patch-id_nonl-inc-ws patch-id_withnl-inc-ws
 '
 
 test_expect_success 'patch-id handles diffs with one line of before/after' '
index fedb7ca7493f650ce9def7179cf63fa11ef90d70..e3d655e6b8b5565d619298b16da010ee671667a2 100755 (executable)
@@ -156,7 +156,7 @@ test_expect_success 'NUL termination with --reflog --pretty=oneline' '
        for r in $revs
        do
                git show -s --pretty=oneline "$r" >raw &&
-               cat raw | lf_to_nul || exit 1
+               cat raw | lf_to_nul || return 1
        done >expect &&
        # the trailing NUL is already produced so we do not need to
        # output another one
@@ -576,6 +576,38 @@ test_expect_success 'clean log decoration' '
        test_cmp expected actual1
 '
 
+test_expect_success 'pretty format %decorate' '
+       git checkout -b foo &&
+       git commit --allow-empty -m "new commit" &&
+       git tag bar &&
+       git branch qux &&
+
+       echo " (HEAD -> foo, tag: bar, qux)" >expect1 &&
+       git log --format="%(decorate)" -1 >actual1 &&
+       test_cmp expect1 actual1 &&
+
+       echo "HEAD -> foo, tag: bar, qux" >expect2 &&
+       git log --format="%(decorate:prefix=,suffix=)" -1 >actual2 &&
+       test_cmp expect2 actual2 &&
+
+       echo "[ bar; qux; foo ]" >expect3 &&
+       git log --format="%(decorate:prefix=[ ,suffix= ],separator=%x3B ,tag=)" \
+               --decorate-refs=refs/ -1 >actual3 &&
+       test_cmp expect3 actual3 &&
+
+       # Try with a typo (in "separator"), in which case the placeholder should
+       # not be replaced.
+       echo "%(decorate:prefix=[ ,suffix= ],separater=; )" >expect4 &&
+       git log --format="%(decorate:prefix=[ ,suffix= ],separater=%x3B )" \
+               -1 >actual4 &&
+       test_cmp expect4 actual4 &&
+
+       echo "HEAD->foo bar qux" >expect5 &&
+       git log --format="%(decorate:prefix=,suffix=,separator= ,tag=,pointer=->)" \
+               -1 >actual5 &&
+       test_cmp expect5 actual5
+'
+
 cat >trailers <<EOF
 Signed-off-by: A U Thor <author@example.com>
 Acked-by: A U Thor <author@example.com>
@@ -924,6 +956,36 @@ test_expect_success '%S in git log --format works with other placeholders (part
        test_cmp expect actual
 '
 
+test_expect_success 'setup more commits for %S with --bisect' '
+       test_commit four &&
+       test_commit five &&
+
+       head1=$(git rev-parse --verify HEAD~0) &&
+       head2=$(git rev-parse --verify HEAD~1) &&
+       head3=$(git rev-parse --verify HEAD~2) &&
+       head4=$(git rev-parse --verify HEAD~3)
+'
+
+test_expect_success '%S with --bisect labels commits with refs/bisect/bad ref' '
+       git update-ref refs/bisect/bad-$head1 $head1 &&
+       git update-ref refs/bisect/go $head1 &&
+       git update-ref refs/bisect/bad-$head2 $head2 &&
+       git update-ref refs/bisect/b $head3 &&
+       git update-ref refs/bisect/bad-$head4 $head4 &&
+       git update-ref refs/bisect/good-$head4 $head4 &&
+
+       # We expect to see the range of commits betwee refs/bisect/good-$head4
+       # and refs/bisect/bad-$head1. The "source" ref is the nearest bisect ref
+       # from which the commit is reachable.
+       cat >expect <<-EOF &&
+       $head1 refs/bisect/bad-$head1
+       $head2 refs/bisect/bad-$head2
+       $head3 refs/bisect/bad-$head2
+       EOF
+       git log --bisect --format="%H %S" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'log --pretty=reference' '
        git log --pretty="tformat:%h (%s, %as)" >expect &&
        git log --pretty=reference >actual &&
@@ -1012,10 +1074,25 @@ test_expect_success '%(describe:tags) vs git describe --tags' '
 
 test_expect_success '%(describe:abbrev=...) vs git describe --abbrev=...' '
        test_when_finished "git tag -d tagname" &&
+
+       # Case 1: We have commits between HEAD and the most recent tag
+       #         reachable from it
+       test_commit --no-tag file &&
+       git describe --abbrev=15 >expect &&
+       git log -1 --format="%(describe:abbrev=15)" >actual &&
+       test_cmp expect actual &&
+
+       # Make sure the hash used is at least 15 digits long
+       sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
+       test 16 -le $(wc -c <hexpart) &&
+
+       # Case 2: We have a tag at HEAD, describe directly gives the
+       #         name of the tag
        git tag -a -m tagged tagname &&
        git describe --abbrev=15 >expect &&
        git log -1 --format="%(describe:abbrev=15)" >actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test tagname = $(cat actual)
 '
 
 test_expect_success 'log --pretty with space stealing' '
@@ -1094,4 +1171,31 @@ test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit mes
        test_cmp expect error
 '
 
+# pretty-formats note wide char limitations, and add tests
+test_expect_failure 'wide and decomposed characters column counting' '
+
+# from t/lib-unicode-nfc-nfd.sh hex values converted to octal
+       utf8_nfc=$(printf "\303\251") && # e acute combined.
+       utf8_nfd=$(printf "\145\314\201") && # e with a combining acute (i.e. decomposed)
+       utf8_emoji=$(printf "\360\237\221\250") &&
+
+# replacement character when requesting a wide char fits in a single display colum.
+# "half wide" alternative could be a plain ASCII dot `.`
+       utf8_vert_ell=$(printf "\342\213\256") &&
+
+# use ${xxx} here!
+       nfc10="${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}" &&
+       nfd10="${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}" &&
+       emoji5="${utf8_emoji}${utf8_emoji}${utf8_emoji}${utf8_emoji}${utf8_emoji}" &&
+# emoji5 uses 10 display columns
+
+       test_commit "abcdefghij" &&
+       test_commit --no-tag "${nfc10}" &&
+       test_commit --no-tag "${nfd10}" &&
+       test_commit --no-tag "${emoji5}" &&
+       printf "${utf8_emoji}..${utf8_emoji}${utf8_vert_ell}\n${utf8_nfd}..${utf8_nfd}${utf8_nfd}\n${utf8_nfc}..${utf8_nfc}${utf8_nfc}\na..ij\n" >expected &&
+       git log --format="%<(5,mtrunc)%s" -4 >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 33ecf82c7f98d3b9fb59422e614eb47cfe54fecb..9167b0351f221205cf2700a318b8041513e34248 100755 (executable)
@@ -16,29 +16,29 @@ Line 2
 Line 3
 '
 
-test_expect_success \
-    'add a file path0 and commit.' \
-    'git add path0 &&
-     git commit -m "Add path0"'
+test_expect_success 'add a file path0 and commit.' '
+       git add path0 &&
+       git commit -m "Add path0"
+'
 
 echo >path0 'New line 1
 New line 2
 New line 3
 '
-test_expect_success \
-    'Change path0.' \
-    'git add path0 &&
-     git commit -m "Change path0"'
+test_expect_success 'Change path0.' '
+       git add path0 &&
+       git commit -m "Change path0"
+'
 
 cat <path0 >path1
-test_expect_success \
-    'copy path0 to path1.' \
-    'git add path1 &&
-     git commit -m "Copy path1 from path0"'
+test_expect_success 'copy path0 to path1.' '
+       git add path1 &&
+       git commit -m "Copy path1 from path0"
+'
 
-test_expect_success \
-    'find the copy path0 -> path1 harder' \
-    'git log --follow --name-status --pretty="format:%s"  path1 > current'
+test_expect_success 'find the copy path0 -> path1 harder' '
+       git log --follow --name-status --pretty="format:%s"  path1 > current
+'
 
 cat >expected <<\EOF
 Copy path1 from path0
@@ -51,8 +51,8 @@ Add path0
 A      path0
 EOF
 
-test_expect_success \
-    'validate the output.' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate the output.' '
+       compare_diff_patch current expected
+'
 
 test_done
index ded33a82e2c94cd4f44c1df4c9fe93ce9bf5f148..73ea9e515503baf7776aaba04a4b6d9de7e20c92 100755 (executable)
@@ -53,35 +53,45 @@ cmp_filtered_decorations () {
 # to this test since it does not contain any decoration, hence --first-parent
 test_expect_success 'commit decorations colored correctly' '
        cat >expect <<-EOF &&
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
-${c_reset}${c_branch}main${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit}, \
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \
 ${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset} \
-On main: Changes to A.t
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
        EOF
 
        git log --first-parent --no-abbrev --decorate --oneline --color=always --all >actual &&
        cmp_filtered_decorations
 '
 
+remove_replace_refs () {
+       git for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&
+       git update-ref --stdin <in &&
+       rm in
+}
+
 test_expect_success 'test coloring with replace-objects' '
-       test_when_finished rm -rf .git/refs/replace* &&
+       test_when_finished remove_replace_refs &&
        test_commit C &&
        test_commit D &&
 
        git replace HEAD~1 HEAD~2 &&
 
        cat >expect <<-EOF &&
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
-${c_reset}${c_branch}main${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: D${c_reset}${c_commit})${c_reset} D
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: C${c_reset}${c_commit}, \
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit})${c_reset} D
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}C${c_reset}${c_commit}, \
 ${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} B
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
 EOF
 
        git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
@@ -95,18 +105,20 @@ EOF
 '
 
 test_expect_success 'test coloring with grafted commit' '
-       test_when_finished rm -rf .git/refs/replace* &&
+       test_when_finished remove_replace_refs &&
 
        git replace --graft HEAD HEAD~2 &&
 
        cat >expect <<-EOF &&
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
-${c_reset}${c_branch}main${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: D${c_reset}${c_commit}, \
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit}, \
 ${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} D
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
-       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
+       ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
        EOF
 
        git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
index 2e8f5ad7b822b2c33d1262c5576706f40de52692..806b2809d405f854f1d1f8b0d7156ce2c167e38a 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success '"git log :/" should not be ambiguous' '
 test_expect_success '"git log :/a" should be ambiguous (applied both rev and worktree)' '
        : >a &&
        test_must_fail git log :/a 2>error &&
-       test_i18ngrep ambiguous error
+       test_grep ambiguous error
 '
 
 test_expect_success '"git log :/a -- " should not be ambiguous' '
@@ -65,7 +65,7 @@ test_expect_success '"git log :/in" should not be ambiguous' '
 
 test_expect_success '"git log :" should be ambiguous' '
        test_must_fail git log : 2>error &&
-       test_i18ngrep ambiguous error
+       test_grep ambiguous error
 '
 
 test_expect_success 'git log -- :' '
@@ -104,7 +104,7 @@ test_expect_success '"git log :(exclude)sub --" must resolve as an object' '
 
 test_expect_success '"git log :(unknown-magic) complains of bogus magic' '
        test_must_fail git log ":(unknown-magic)" 2>error &&
-       test_i18ngrep pathspec.magic error
+       test_grep pathspec.magic error
 '
 
 test_expect_success 'command line pathspec parsing for "git log"' '
index 7f6bb27f141fe786811302f023366e6d15a5aa0e..64e16237335dbb37e3d250f248758017df62943b 100755 (executable)
@@ -57,10 +57,10 @@ test_expect_success setup '
 
 test_expect_success 'usage' '
        test_expect_code 129 git log -S 2>err &&
-       test_i18ngrep "switch.*requires a value" err &&
+       test_grep "switch.*requires a value" err &&
 
        test_expect_code 129 git log -G 2>err &&
-       test_i18ngrep "switch.*requires a value" err &&
+       test_grep "switch.*requires a value" err &&
 
        test_expect_code 128 git log -Gregex -Sstring 2>err &&
        grep "cannot be used together" err &&
index ac9e4d0928593cd37b4a79e27f5fe99b6e4a3faf..02d76dca284cbaf1e0a284f0f496051b2fa88f39 100755 (executable)
@@ -19,7 +19,7 @@ test_expect_success 'basic command line parsing' '
 
        # -L requires there is no pathspec
        test_must_fail git log -L1,1:b.c -- b.c 2>error &&
-       test_i18ngrep "cannot be used with pathspec" error &&
+       test_grep "cannot be used with pathspec" error &&
 
        # This would fail because --follow wants a single path, but
        # we may fail due to incompatibility between -L/--follow in
@@ -50,7 +50,7 @@ canned_test_failure () {
 test_bad_opts () {
        test_expect_success "invalid args: $1" "
                test_must_fail git log $1 2>errors &&
-               test_i18ngrep '$2' errors
+               test_grep '$2' errors
        "
 }
 
@@ -315,4 +315,26 @@ test_expect_success 'line-log with --before' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup tests for zero-width regular expressions' '
+       cat >expect <<-EOF
+       Modify func1() in file.c
+       Add func1() and func2() in file.c
+       EOF
+'
+
+test_expect_success 'zero-width regex $ matches any function name' '
+       git log --format="%s" --no-patch "-L:$:file.c" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'zero-width regex ^ matches any function name' '
+       git log --format="%s" --no-patch "-L:^:file.c" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'zero-width regex .* matches any function name' '
+       git log --format="%s" --no-patch "-L:.*:file.c" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 30a219894bb45f2655596fe02e8a7d001a35eabd..e6b59123a3725115ff2ad5eae495d114dc7df801 100755 (executable)
@@ -8,15 +8,16 @@ TEST_PASSES_SANITIZE_LEAK=true
 test_expect_success 'setup' '
        test_commit foo &&
 
-       git cat-file commit HEAD |
-       sed "/^author /s/>/>-<>/" >broken_email.commit &&
-       git hash-object -w -t commit broken_email.commit >broken_email.hash &&
+       git cat-file commit HEAD >ok.commit &&
+       sed "s/>/>-<>/" <ok.commit >broken_email.commit &&
+
+       git hash-object --literally -w -t commit broken_email.commit >broken_email.hash &&
        git update-ref refs/heads/broken_email $(cat broken_email.hash)
 '
 
 test_expect_success 'fsck notices broken commit' '
        test_must_fail git fsck 2>actual &&
-       test_i18ngrep invalid.author actual
+       test_grep invalid.author actual
 '
 
 test_expect_success 'git log with broken author email' '
@@ -43,10 +44,15 @@ test_expect_success 'git log --format with broken author email' '
        test_must_be_empty actual.err
 '
 
+test_expect_success '--until handles broken email' '
+       git rev-list --until=1980-01-01 broken_email >actual &&
+       test_must_be_empty actual
+'
+
 munge_author_date () {
        git cat-file commit "$1" >commit.orig &&
        sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
-       git hash-object -w -t commit commit.munge
+       git hash-object --literally -w -t commit commit.munge
 }
 
 test_expect_success 'unparsable dates produce sentinel value' '
@@ -86,4 +92,45 @@ test_expect_success 'absurdly far-in-future date' '
        git log -1 --format=%ad $commit
 '
 
+test_expect_success 'create commits with whitespace committer dates' '
+       # It is important that this subject line is numeric, since we want to
+       # be sure we are not confused by skipping whitespace and accidentally
+       # parsing the subject as a timestamp.
+       #
+       # Do not use munge_author_date here. Besides not hitting the committer
+       # line, it leaves the timezone intact, and we want nothing but
+       # whitespace.
+       #
+       # We will make two munged commits here. The first, ws_commit, will
+       # be purely spaces. The second contains a vertical tab, which is
+       # considered a space by strtoumax(), but not by our isspace().
+       test_commit 1234567890 &&
+       git cat-file commit HEAD >commit.orig &&
+       sed "s/>.*/>    /" <commit.orig >commit.munge &&
+       ws_commit=$(git hash-object --literally -w -t commit commit.munge) &&
+       sed "s/>.*/>   $(printf "\013")/" <commit.orig >commit.munge &&
+       vt_commit=$(git hash-object --literally -w -t commit commit.munge)
+'
+
+test_expect_success '--until treats whitespace date as sentinel' '
+       echo $ws_commit >expect &&
+       git rev-list --until=1980-01-01 $ws_commit >actual &&
+       test_cmp expect actual &&
+
+       echo $vt_commit >expect &&
+       git rev-list --until=1980-01-01 $vt_commit >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'pretty-printer handles whitespace date' '
+       # as with the %ad test above, we will show these as the empty string,
+       # not the 1970 epoch date. This is intentional; see 7d9a281941 (t4212:
+       # test bogus timestamps with git-log, 2014-02-24) for more discussion.
+       echo : >expect &&
+       git log -1 --format="%at:%ct" $ws_commit >actual &&
+       test_cmp expect actual &&
+       git log -1 --format="%at:%ct" $vt_commit >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 53a4af324495dda3738388af31dcecf31f8ee1ee..590fce95e90a8bd751daf13c0696d0a39b8f7d1b 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='log/show --expand-tabs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 HT="   "
index f70c46bbbfa2c8fc9cc5ca1b620c9a80f29e382e..79055978690962fc3b58cd754a9383a88148f0f9 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git log --graph of skewed left octopus merge.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-log-graph.sh
 
index 28d0779a8c599ee9eab9b0b31afe1a57bb558c28..b877ac723516dc710fd42c17b65877d795d874ff 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git log --graph of skewed merges'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-log-graph.sh
 
index fa9d32facfb0ddca6c001d78c1011cf0b3568f19..2ba0324a693731412242cf9603826736a3c9279a 100755 (executable)
@@ -5,6 +5,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
 
 GIT_TEST_COMMIT_GRAPH=0
 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
@@ -404,4 +405,53 @@ test_expect_success 'Bloom generation backfills empty commits' '
        )
 '
 
+corrupt_graph () {
+       graph=.git/objects/info/commit-graph &&
+       test_when_finished "rm -rf $graph" &&
+       git commit-graph write --reachable --changed-paths &&
+       corrupt_chunk_file $graph "$@"
+}
+
+check_corrupt_graph () {
+       corrupt_graph "$@" &&
+       git -c core.commitGraph=false log -- A/B/file2 >expect.out &&
+       git -c core.commitGraph=true log -- A/B/file2 >out 2>err &&
+       test_cmp expect.out out
+}
+
+test_expect_success 'Bloom reader notices too-small data chunk' '
+       check_corrupt_graph BDAT clear 00000000 &&
+       echo "warning: ignoring too-small changed-path chunk" \
+               "(4 < 12) in commit-graph file" >expect.err &&
+       test_cmp expect.err err
+'
+
+test_expect_success 'Bloom reader notices out-of-bounds filter offsets' '
+       check_corrupt_graph BIDX 12 FFFFFFFF &&
+       # use grep to avoid depending on exact chunk size
+       grep "warning: ignoring out-of-range offset (4294967295) for changed-path filter at pos 3 of .git/objects/info/commit-graph" err
+'
+
+test_expect_success 'Bloom reader notices too-small index chunk' '
+       # replace the index with a single entry, making most
+       # lookups out-of-bounds
+       check_corrupt_graph BIDX clear 00000000 &&
+       echo "warning: commit-graph changed-path index chunk" \
+               "is too small" >expect.err &&
+       test_cmp expect.err err
+'
+
+test_expect_success 'Bloom reader notices out-of-order index offsets' '
+       # we do not know any real offsets, but we can pick
+       # something plausible; we should not get to the point of
+       # actually reading from the bogus offsets anyway.
+       corrupt_graph BIDX 4 0000000c00000005 &&
+       echo "warning: ignoring decreasing changed-path index offsets" \
+               "(12 > 5) for positions 1 and 2 of .git/objects/info/commit-graph" >expect.err &&
+       git -c core.commitGraph=false log -- A/B/file2 >expect.out &&
+       git -c core.commitGraph=true log -- A/B/file2 >out 2>err &&
+       test_cmp expect.out out &&
+       test_cmp expect.err err
+'
+
 test_done
index 6e01e2629c1b158464ab29e4086fb4eeea16bbaa..613f0710e90511c6419e843eec3cb4d11aafc437 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git log with filter options limiting the output'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup test' '
index 54be7da1611212143d843d175bf48f73bb83b417..45f1d4f95e5d5af7cff901639b5ecaf541846968 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git am with corrupt input'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 make_mbox_with_nul () {
index 2369c4e17ad8d4ee6645ddc6f87b87896603dec8..92d8c8b651cbe6714d95bdaf9bf91f62fd3bbf91 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='test format=flowed support of git am'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -12,7 +13,7 @@ test_expect_success 'setup' '
 
 test_expect_success 'am with format=flowed' '
        git am <"$TEST_DIRECTORY/t4256/1/patch" 2>stderr &&
-       test_i18ngrep "warning: Patch sent with format=flowed" stderr &&
+       test_grep "warning: Patch sent with format=flowed" stderr &&
        test_cmp "$TEST_DIRECTORY/t4256/1/mailinfo.c" mailinfo.c
 '
 
index aed8f4de3d670143753bfbce1dd40c5664abd8c6..f26d7fd2dbd35ce40534a5d76f9903361f4b7904 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='am --interactive tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'set up patches to apply' '
index c62819f3d25afc78aaed385d9bd79afe30060692..1ae528ba78fb130d6a73b314502697c1b1dc81c0 100644 (file)
@@ -2,7 +2,7 @@ From: A U Thor <mail@example.com>
 To: list@example.org
 Subject: [PATCH v2] sample
 Date: Mon,  3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id@example.com>
+Message-ID: <msg-id@example.com>
 Content-Type: text/plain; charset="utf-8"
 Content-Transfer-Encoding: base64
 
index c52c8a21fae6d0593cc120485e8d6eaa8a95ccbc..9c197260d5bbf54d3ad578c27f54e278d88ab97b 100755 (executable)
@@ -86,6 +86,33 @@ EXPECTED
        test_cmp expected actual
 '
 
+test_expect_success '3-way merge with --attr-source' '
+       test_when_finished rm -rf 3-way &&
+       git init 3-way &&
+       (
+               cd 3-way &&
+               test_commit initial file1 foo &&
+               base=$(git rev-parse HEAD) &&
+               git checkout -b brancha &&
+               echo bar >>file1 &&
+               git commit -am "adding bar" &&
+               source=$(git rev-parse HEAD) &&
+               git checkout @{-1} &&
+               git checkout -b branchb &&
+               echo baz >>file1 &&
+               git commit -am "adding baz" &&
+               merge=$(git rev-parse HEAD) &&
+               git checkout -b gitattributes &&
+               test_commit "gitattributes" .gitattributes "file1 merge=union" &&
+               git checkout @{-1} &&
+               tree=$(git --attr-source=gitattributes merge-tree --write-tree \
+               --merge-base "$base" --end-of-options "$source" "$merge") &&
+               test_write_lines foo bar baz >expect &&
+               git cat-file -p "$tree:file1" >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'file change A, B (same)' '
        git reset --hard initial &&
        test_commit "change-a-b-same-A" "initial-file" "AAA" &&
@@ -334,4 +361,22 @@ test_expect_success 'turn tree to file' '
        test_cmp expect actual
 '
 
+test_expect_success 'merge-tree respects core.useReplaceRefs=false' '
+       test_commit merge-to &&
+       test_commit valid base &&
+       git reset --hard HEAD^ &&
+       test_commit malicious base &&
+
+       test_when_finished "git replace -d $(git rev-parse valid^0)" &&
+       git replace valid^0 malicious^0 &&
+
+       tree=$(git -c core.useReplaceRefs=true merge-tree --write-tree merge-to valid) &&
+       merged=$(git cat-file -p $tree:base) &&
+       test malicious = $merged &&
+
+       tree=$(git -c core.useReplaceRefs=false merge-tree --write-tree merge-to valid) &&
+       merged=$(git cat-file -p $tree:base) &&
+       test valid = $merged
+'
+
 test_done
index 013b77144bdf1a59d4577a57cace9ce0e7408742..12ac43687366d72f2fa2870c72a9d5563a05fc66 100755 (executable)
@@ -22,6 +22,7 @@ test_expect_success setup '
        git branch side1 &&
        git branch side2 &&
        git branch side3 &&
+       git branch side4 &&
 
        git checkout side1 &&
        test_write_lines 1 2 3 4 5 6 >numbers &&
@@ -46,6 +47,13 @@ test_expect_success setup '
        test_tick &&
        git commit -m rename-numbers &&
 
+       git checkout side4 &&
+       test_write_lines 0 1 2 3 4 5 >numbers &&
+       echo yo >greeting &&
+       git add numbers greeting &&
+       test_tick &&
+       git commit -m other-content-modifications &&
+
        git switch --orphan unrelated &&
        >something-else &&
        git add something-else &&
@@ -97,6 +105,21 @@ test_expect_success 'Content merge and a few conflicts' '
        test_cmp expect actual
 '
 
+test_expect_success 'Auto resolve conflicts by "ours" strategy option' '
+       git checkout side1^0 &&
+
+       # make sure merge conflict exists
+       test_must_fail git merge side4 &&
+       git merge --abort &&
+
+       git merge -X ours side4 &&
+       git rev-parse HEAD^{tree} >expected &&
+
+       git merge-tree -X ours side1 side4 >actual &&
+
+       test_cmp expected actual
+'
+
 test_expect_success 'Barf on misspelled option, with exit code other than 0 or 1' '
        # Mis-spell with single "s" instead of double "s"
        test_expect_code 129 git merge-tree --write-tree --mesages FOOBAR side1 side2 2>expect &&
@@ -141,7 +164,7 @@ test_expect_success 'test conflict notices and such' '
 #   Commit O: foo, olddir/{a,b,c}
 #   Commit A: modify foo, newdir/{a,b,c}
 #   Commit B: modify foo differently & rename foo -> olddir/bar
-#   Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
+#   Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
 
 test_expect_success 'directory rename + content conflict' '
        # Setup
@@ -653,7 +676,7 @@ test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via collidi
 #   Commit O: foo, olddir/{a,b,c}
 #   Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file
 #   Commit B: modify foo & rename foo -> olddir/bar
-#   Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
+#   Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
 
 test_expect_success 'directory rename + rename/delete + modify/delete + directory/file conflict' '
        # Setup
@@ -819,4 +842,107 @@ test_expect_success SANITY 'merge-ort fails gracefully in a read-only repository
        test_must_fail git -C read-only merge-tree side1 side2
 '
 
+test_expect_success '--stdin with both a successful and a conflicted merge' '
+       printf "side1 side3\nside1 side2" | git merge-tree --stdin >actual &&
+
+       git checkout side1^0 &&
+       git merge side3 &&
+
+       printf "1\0" >expect &&
+       git rev-parse HEAD^{tree} | lf_to_nul >>expect &&
+       printf "\0" >>expect &&
+
+       git checkout side1^0 &&
+       test_must_fail git merge side2 &&
+       sed s/HEAD/side1/ greeting >tmp &&
+       mv tmp greeting &&
+       git add -u &&
+       git mv whatever~HEAD whatever~side1 &&
+
+       printf "0\0" >>expect &&
+       git write-tree | lf_to_nul >>expect &&
+
+       cat <<-EOF | q_to_tab | lf_to_nul >>expect &&
+       100644 $(git rev-parse side1~1:greeting) 1Qgreeting
+       100644 $(git rev-parse side1:greeting) 2Qgreeting
+       100644 $(git rev-parse side2:greeting) 3Qgreeting
+       100644 $(git rev-parse side1~1:whatever) 1Qwhatever~side1
+       100644 $(git rev-parse side1:whatever) 2Qwhatever~side1
+       EOF
+
+       q_to_nul <<-EOF >>expect &&
+       Q1QgreetingQAuto-mergingQAuto-merging greeting
+       Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
+       Q1QnumbersQAuto-mergingQAuto-merging numbers
+       Q2Qwhatever~side1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
+       Q1Qwhatever~side1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1.  Version side1 of whatever~side1 left in tree.
+       EOF
+
+       printf "\0\0" >>expect &&
+
+       test_cmp expect actual
+'
+
+
+test_expect_success '--merge-base is incompatible with --stdin' '
+       test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
+
+       grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect
+'
+
+# specify merge-base as parent of branch2
+# git merge-tree --write-tree --merge-base=c2 c1 c3
+#   Commit c1: add file1
+#   Commit c2: add file2 after c1
+#   Commit c3: add file3 after c2
+#   Expected: add file3, and file2 does NOT appear
+
+test_expect_success 'specify merge-base as parent of branch2' '
+       # Setup
+       test_when_finished "rm -rf base-b2-p" &&
+       git init base-b2-p &&
+       test_commit -C base-b2-p c1 file1 &&
+       test_commit -C base-b2-p c2 file2 &&
+       test_commit -C base-b2-p c3 file3 &&
+
+       # Testing
+       TREE_OID=$(git -C base-b2-p merge-tree --write-tree --merge-base=c2 c1 c3) &&
+
+       q_to_tab <<-EOF >expect &&
+       100644 blob $(git -C base-b2-p rev-parse c1:file1)Qfile1
+       100644 blob $(git -C base-b2-p rev-parse c3:file3)Qfile3
+       EOF
+
+       git -C base-b2-p ls-tree $TREE_OID >actual &&
+       test_cmp expect actual
+'
+
+# Since the earlier tests have verified that individual merge-tree calls
+# are doing the right thing, this test case is only used to verify that
+# we can also trigger merges via --stdin, and that when we do we get
+# the same answer as running a bunch of separate merges.
+
+test_expect_success 'check the input format when --stdin is passed' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       test_commit -C repo c1 &&
+       test_commit -C repo c2 &&
+       test_commit -C repo c3 &&
+       printf "c1 c3\nc2 -- c1 c3\nc2 c3" | git -C repo merge-tree --stdin >actual &&
+
+       printf "1\0" >expect &&
+       git -C repo merge-tree --write-tree -z c1 c3 >>expect &&
+       printf "\0" >>expect &&
+
+       printf "1\0" >>expect &&
+       git -C repo merge-tree --write-tree -z --merge-base=c2 c1 c3 >>expect &&
+       printf "\0" >>expect &&
+
+       printf "1\0" >>expect &&
+       git -C repo merge-tree --write-tree -z c2 c3 >>expect &&
+       printf "\0" >>expect &&
+
+       test_cmp expect actual
+'
+
 test_done
index eaa0b22ece4ff3ce7d79faed2edf8ab829c97051..72b8d0ff02e34be0606747c850b0815be31196d4 100755 (executable)
@@ -105,6 +105,18 @@ check_added() {
        '
 }
 
+check_mtime() {
+       dir=$1
+       path_in_archive=$2
+       mtime=$3
+
+       test_expect_success " validate mtime of $path_in_archive" '
+               test-tool chmtime --get $dir/$path_in_archive >actual.mtime &&
+               echo $mtime >expect.mtime &&
+               test_cmp expect.mtime actual.mtime
+       '
+}
+
 test_expect_success 'setup' '
        test_oid_cache <<-EOF
        obj sha1:19f9c8273ec45a8938e6999cb59b3ff66739902a
@@ -112,6 +124,16 @@ test_expect_success 'setup' '
        EOF
 '
 
+test_expect_success '--list notices extra parameters' '
+       test_must_fail git archive --list blah &&
+       test_must_fail git archive --remote=. --list blah
+'
+
+test_expect_success 'end-of-options is correctly eaten' '
+       git archive --list --end-of-options &&
+       git archive --remote=. --list --end-of-options
+'
+
 test_expect_success 'populate workdir' '
        mkdir a &&
        echo simple textfile >a/a &&
@@ -173,6 +195,14 @@ test_expect_success 'git archive' '
 '
 
 check_tar b
+check_mtime b a/a 1117231200
+
+test_expect_success 'git archive --mtime' '
+       git archive --mtime=2002-02-02T02:02:02-0200 HEAD >with_mtime.tar
+'
+
+check_tar with_mtime
+check_mtime with_mtime a/a 1012622522
 
 test_expect_success 'git archive --prefix=prefix/' '
        git archive --prefix=prefix/ HEAD >with_prefix.tar
@@ -238,14 +268,6 @@ test_expect_success 'git archive --remote with configured remote' '
        test_cmp_bin b.tar b5-nick.tar
 '
 
-test_expect_success 'validate file modification time' '
-       mkdir extract &&
-       "$TAR" xf b.tar -C extract a/a &&
-       test-tool chmtime --get extract/a/a >b.mtime &&
-       echo "1117231200" >expected.mtime &&
-       test_cmp expected.mtime b.mtime
-'
-
 test_expect_success 'git get-tar-commit-id' '
        git get-tar-commit-id <b.tar >actual &&
        git rev-parse HEAD >expect &&
@@ -342,6 +364,13 @@ test_expect_success 'only enabled filters are available remotely' '
        test_cmp_bin remote.bar config.bar
 '
 
+test_expect_success 'invalid filter is reported only once' '
+       test_must_fail git -c tar.invalid.command= archive --format=invalid \
+               HEAD >out 2>err &&
+       test_must_be_empty out &&
+       test_line_count = 1 err
+'
+
 test_expect_success 'git archive --format=tgz' '
        git archive --format=tgz HEAD >j.tgz
 '
@@ -395,11 +424,11 @@ test_expect_success GZIP 'extract tgz file (external gzip)' '
 
 test_expect_success 'archive and :(glob)' '
        git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual &&
-       cat >expect <<EOF &&
-a/
-a/bin/
-a/bin/sh
-EOF
+       cat >expect <<-\EOF &&
+       a/
+       a/bin/
+       a/bin/sh
+       EOF
        test_cmp expect actual
 '
 
@@ -407,6 +436,19 @@ test_expect_success 'catch non-matching pathspec' '
        test_must_fail git archive -v HEAD -- "*.abc" >/dev/null
 '
 
+test_expect_success 'reject paths outside the current directory' '
+       test_must_fail git -C a/bin archive HEAD .. >/dev/null 2>err &&
+       grep "outside the current directory" err
+'
+
+test_expect_success 'allow pathspecs that resolve to the current directory' '
+       git -C a/bin archive -v HEAD ../bin >/dev/null 2>actual &&
+       cat >expect <<-\EOF &&
+       sh
+       EOF
+       test_cmp expect actual
+'
+
 # Pull the size and date of each entry in a tarfile using the system tar.
 #
 # We'll pull out only the year from the date; that avoids any question of
index 2f6eef5e3720452df49c4810bd9f4f63dc1167fb..eaf959d8f63f158651609a7c5b0c2eb6592b4926 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='git archive attribute tests'
 
 TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 SUBSTFORMAT='%H (%h)%n'
@@ -32,6 +33,13 @@ test_expect_success 'setup' '
        echo ignored-by-tree.d export-ignore >>.gitattributes &&
        git add ignored-by-tree ignored-by-tree.d .gitattributes &&
 
+       mkdir subdir &&
+       >subdir/included &&
+       >subdir/ignored-by-subtree &&
+       >subdir/ignored-by-tree &&
+       echo ignored-by-subtree export-ignore >subdir/.gitattributes &&
+       git add subdir &&
+
        echo ignored by worktree >ignored-by-worktree &&
        echo ignored-by-worktree export-ignore >.gitattributes &&
        git add ignored-by-worktree &&
@@ -92,6 +100,15 @@ test_expect_exists  archive-pathspec-wildcard/ignored-by-worktree
 test_expect_missing    archive-pathspec-wildcard/excluded-by-pathspec.d
 test_expect_missing    archive-pathspec-wildcard/excluded-by-pathspec.d/file
 
+test_expect_success 'git -C subdir archive' '
+       git -C subdir archive HEAD >archive-subdir.tar &&
+       extract_tar_to_dir archive-subdir
+'
+
+test_expect_exists     archive-subdir/included
+test_expect_missing    archive-subdir/ignored-by-subtree
+test_expect_missing    archive-subdir/ignored-by-tree
+
 test_expect_success 'git archive with worktree attributes' '
        git archive --worktree-attributes HEAD >worktree.tar &&
        (mkdir worktree && cd worktree && "$TAR" xf -) <worktree.tar
@@ -121,7 +138,7 @@ test_expect_success 'git archive with worktree attributes, bare' '
 '
 
 test_expect_missing    bare-worktree/ignored
-test_expect_exists     bare-worktree/ignored-by-tree
+test_expect_missing    bare-worktree/ignored-by-tree
 test_expect_exists     bare-worktree/ignored-by-worktree
 
 test_expect_success 'export-subst' '
index ae508e21623fb4af7b55c1c8afe27556b24bb232..9f2c6da80e8cd97c12eb5452e940a9022b2e3a84 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test corner cases of git-archive'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # the 10knuls.tar file is used to test for an empty git generated tar
index db11cababd310f9773dcd0a3bb7039a4df8d4f59..654d8cf3ee003ee4e8d66b6e88df06312014cb47 100755 (executable)
@@ -268,4 +268,26 @@ test_expect_success 'mailinfo warn CR in base64 encoded email' '
        test_must_be_empty quoted-cr/0002.err
 '
 
+test_expect_success 'from line with unterminated quoted string' '
+       echo "From: bob \"unterminated string smith <bob@example.com>" >in &&
+       git mailinfo /dev/null /dev/null <in >actual &&
+       cat >expect <<-\EOF &&
+       Author: bob unterminated string smith
+       Email: bob@example.com
+
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'from line with unterminated comment' '
+       echo "From: bob (unterminated comment smith <bob@example.com>" >in &&
+       git mailinfo /dev/null /dev/null <in >actual &&
+       cat >expect <<-\EOF &&
+       Author: bob (unterminated comment smith
+       Email: bob@example.com
+
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 72281779843fb270762b5352b8dc213bd3b805e1..bd71956a474531775de56ac26757484a76025676 100644 (file)
@@ -1,4 +1,4 @@
-Author: A U Thor (this is (really) a comment (honestly))
+Author: (this is (really) a "comment" (honestly)) A U Thor
 Email: somebody@example.com
 Subject: testing comments
 Date: Sun, 25 May 2008 00:38:18 -0700
index c53a192dfeac51a32000d80f4f8052eef6e44486..0b7e903b061131b30a258d30b11cd9cc22acae5d 100644 (file)
@@ -1,5 +1,5 @@
 From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
-From: "A U Thor" <somebody@example.com> (this is \(really\) a comment (honestly))
+From: (this is \(really\) a "comment" (honestly)) "A U Thor" <somebody@example.com>
 Date: Sun, 25 May 2008 00:38:18 -0700
 Subject: [PATCH] testing comments
 
index e2546ec7332b6b0483f4951906a40436da6383e0..1089382425e481e343064d401bb635407c79e5b4 100644 (file)
@@ -3,7 +3,7 @@ message:
 
 From: Nit Picker <nit.picker@example.net>
 Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
 
 Hopefully this would fix the problem stated there.
 
index 1ac68101b135f4aaf6dacd4bcc0b7586a29d6584..3402b534a6a060c87af0dd5cb6cdc12a4f703835 100644 (file)
@@ -3,7 +3,7 @@ message:
 
 From: Nit Picker <nit.picker@example.net>
 Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
 
 Hopefully this would fix the problem stated there.
 
index 376e26e9aeba074a7f9a240449af1874726e7f44..44482958ce711988d9d72a183c8f11133a896881 100644 (file)
@@ -5,4 +5,4 @@ docutils заменён на python-docutils
 python-docutils. В то время как сам rest2web не нужен.
 
 Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
-Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
index 909021bb7a8de1bf5ce619517b1facc715dfdc4d..a529d4de08863ef78bc32a15d5c90d761e1e5b28 100644 (file)
@@ -3,7 +3,7 @@ From: A U Thor <mail@example.com>
 To: list@example.org
 Subject: [PATCH v2] sample
 Date: Mon,  3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id@example.com>
+Message-ID: <msg-id@example.com>
 Content-Type: text/plain; charset="utf-8"
 Content-Transfer-Encoding: base64
 
@@ -27,7 +27,7 @@ From: A U Thor <mail@example.com>
 To: list@example.org
 Subject: [PATCH v2] sample
 Date: Mon,  3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id2@example.com>
+Message-ID: <msg-id2@example.com>
 Content-Type: text/plain; charset="utf-8"
 Content-Transfer-Encoding: base64
 
index 6d4d0e44742ed3a162b1f9c813b0ad41b6f7dc7a..4a54ee517198cf218d330bdc4cd93cd279abcf0e 100644 (file)
@@ -35,7 +35,7 @@ message:
 
 From: Nit Picker <nit.picker@example.net>
 Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
 
 Hopefully this would fix the problem stated there.
 
@@ -78,7 +78,7 @@ message:
 
 From: Nit Picker <nit.picker@example.net>
 Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
 
 Hopefully this would fix the problem stated there.
 
@@ -508,7 +508,7 @@ From bda@mnsspb.ru Wed Nov 12 17:54:41 2008
 From: Dmitriy Blinov <bda@mnsspb.ru>
 To: navy-patches@dinar.mns.mnsspb.ru
 Date: Wed, 12 Nov 2008 17:54:41 +0300
-Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
 X-Mailer: git-send-email 1.5.6.5
 MIME-Version: 1.0
 Content-Type: text/plain;
index f8a0f309e2dec0f2ab864851730446bdc7102818..d402ec18b795c7ab5bc1b53d5eaaa4467e37fdbb 100755 (executable)
@@ -208,7 +208,7 @@ test_expect_success 'unpack with OFS_DELTA' '
 '
 
 test_expect_success 'unpack with OFS_DELTA (core.fsyncmethod=batch)' '
-       check_unpack test-3-${packname_3} obj-list "$BATCH_CONFIGURATION"
+       check_unpack test-3-${packname_3} obj-list "$BATCH_CONFIGURATION"
 '
 
 test_expect_success 'compare delta flavors' '
@@ -263,97 +263,97 @@ test_expect_success 'survive missing objects/pack directory' '
        )
 '
 
-test_expect_success \
-    'verify pack' \
-    'git verify-pack   test-1-${packname_1}.idx \
-                       test-2-${packname_2}.idx \
-                       test-3-${packname_3}.idx'
-
-test_expect_success \
-    'verify pack -v' \
-    'git verify-pack -v        test-1-${packname_1}.idx \
-                       test-2-${packname_2}.idx \
-                       test-3-${packname_3}.idx'
-
-test_expect_success \
-    'verify-pack catches mismatched .idx and .pack files' \
-    'cat test-1-${packname_1}.idx >test-3.idx &&
-     cat test-2-${packname_2}.pack >test-3.pack &&
-     if git verify-pack test-3.idx
-     then false
-     else :;
-     fi'
-
-test_expect_success \
-    'verify-pack catches a corrupted pack signature' \
-    'cat test-1-${packname_1}.pack >test-3.pack &&
-     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
-     if git verify-pack test-3.idx
-     then false
-     else :;
-     fi'
-
-test_expect_success \
-    'verify-pack catches a corrupted pack version' \
-    'cat test-1-${packname_1}.pack >test-3.pack &&
-     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
-     if git verify-pack test-3.idx
-     then false
-     else :;
-     fi'
-
-test_expect_success \
-    'verify-pack catches a corrupted type/size of the 1st packed object data' \
-    'cat test-1-${packname_1}.pack >test-3.pack &&
-     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
-     if git verify-pack test-3.idx
-     then false
-     else :;
-     fi'
-
-test_expect_success \
-    'verify-pack catches a corrupted sum of the index file itself' \
-    'l=$(wc -c <test-3.idx) &&
-     l=$(expr $l - 20) &&
-     cat test-1-${packname_1}.pack >test-3.pack &&
-     printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
-     if git verify-pack test-3.pack
-     then false
-     else :;
-     fi'
-
-test_expect_success \
-    'build pack index for an existing pack' \
-    'cat test-1-${packname_1}.pack >test-3.pack &&
-     git index-pack -o tmp.idx test-3.pack &&
-     cmp tmp.idx test-1-${packname_1}.idx &&
-
-     git index-pack --promisor=message test-3.pack &&
-     cmp test-3.idx test-1-${packname_1}.idx &&
-     echo message >expect &&
-     test_cmp expect test-3.promisor &&
-
-     cat test-2-${packname_2}.pack >test-3.pack &&
-     git index-pack -o tmp.idx test-2-${packname_2}.pack &&
-     cmp tmp.idx test-2-${packname_2}.idx &&
-
-     git index-pack test-3.pack &&
-     cmp test-3.idx test-2-${packname_2}.idx &&
-
-     cat test-3-${packname_3}.pack >test-3.pack &&
-     git index-pack -o tmp.idx test-3-${packname_3}.pack &&
-     cmp tmp.idx test-3-${packname_3}.idx &&
-
-     git index-pack test-3.pack &&
-     cmp test-3.idx test-3-${packname_3}.idx &&
-
-     cat test-1-${packname_1}.pack >test-4.pack &&
-     rm -f test-4.keep &&
-     git index-pack --keep=why test-4.pack &&
-     cmp test-1-${packname_1}.idx test-4.idx &&
-     test -f test-4.keep &&
-
-     :'
+test_expect_success 'verify pack' '
+       git verify-pack test-1-${packname_1}.idx \
+               test-2-${packname_2}.idx \
+               test-3-${packname_3}.idx
+'
+
+test_expect_success 'verify pack -v' '
+       git verify-pack -v test-1-${packname_1}.idx \
+               test-2-${packname_2}.idx \
+               test-3-${packname_3}.idx
+'
+
+test_expect_success 'verify-pack catches mismatched .idx and .pack files' '
+       cat test-1-${packname_1}.idx >test-3.idx &&
+       cat test-2-${packname_2}.pack >test-3.pack &&
+       if git verify-pack test-3.idx
+       then false
+       else :;
+       fi
+'
+
+test_expect_success 'verify-pack catches a corrupted pack signature' '
+       cat test-1-${packname_1}.pack >test-3.pack &&
+       echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
+       if git verify-pack test-3.idx
+       then false
+       else :;
+       fi
+'
+
+test_expect_success 'verify-pack catches a corrupted pack version' '
+       cat test-1-${packname_1}.pack >test-3.pack &&
+       echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
+       if git verify-pack test-3.idx
+       then false
+       else :;
+       fi
+'
+
+test_expect_success 'verify-pack catches a corrupted type/size of the 1st packed object data' '
+       cat test-1-${packname_1}.pack >test-3.pack &&
+       echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
+       if git verify-pack test-3.idx
+       then false
+       else :;
+       fi
+'
+
+test_expect_success 'verify-pack catches a corrupted sum of the index file itself' '
+       l=$(wc -c <test-3.idx) &&
+       l=$(expr $l - 20) &&
+       cat test-1-${packname_1}.pack >test-3.pack &&
+       printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
+       if git verify-pack test-3.pack
+       then false
+       else :;
+       fi
+'
+
+test_expect_success 'build pack index for an existing pack' '
+       cat test-1-${packname_1}.pack >test-3.pack &&
+       git index-pack -o tmp.idx test-3.pack &&
+       cmp tmp.idx test-1-${packname_1}.idx &&
+
+       git index-pack --promisor=message test-3.pack &&
+       cmp test-3.idx test-1-${packname_1}.idx &&
+       echo message >expect &&
+       test_cmp expect test-3.promisor &&
+
+       cat test-2-${packname_2}.pack >test-3.pack &&
+       git index-pack -o tmp.idx test-2-${packname_2}.pack &&
+       cmp tmp.idx test-2-${packname_2}.idx &&
+
+       git index-pack test-3.pack &&
+       cmp test-3.idx test-2-${packname_2}.idx &&
+
+       cat test-3-${packname_3}.pack >test-3.pack &&
+       git index-pack -o tmp.idx test-3-${packname_3}.pack &&
+       cmp tmp.idx test-3-${packname_3}.idx &&
+
+       git index-pack test-3.pack &&
+       cmp test-3.idx test-3-${packname_3}.idx &&
+
+       cat test-1-${packname_1}.pack >test-4.pack &&
+       rm -f test-4.keep &&
+       git index-pack --keep=why test-4.pack &&
+       cmp test-1-${packname_1}.idx test-4.idx &&
+       test -f test-4.keep &&
+
+       :
+'
 
 test_expect_success 'unpacking with --strict' '
 
@@ -541,7 +541,7 @@ test_expect_success 'make sure index-pack detects the SHA1 collision' '
        (
                cd corrupt &&
                test_must_fail git index-pack -o ../bad.idx ../test-3.pack 2>msg &&
-               test_i18ngrep "SHA1 COLLISION FOUND" msg
+               test_grep "SHA1 COLLISION FOUND" msg
        )
 '
 
@@ -549,7 +549,7 @@ test_expect_success 'make sure index-pack detects the SHA1 collision (large blob
        (
                cd corrupt &&
                test_must_fail git -c core.bigfilethreshold=1 index-pack -o ../bad.idx ../test-3.pack 2>msg &&
-               test_i18ngrep "SHA1 COLLISION FOUND" msg
+               test_grep "SHA1 COLLISION FOUND" msg
        )
 '
 
@@ -589,141 +589,6 @@ test_expect_success 'prefetch objects' '
        test_line_count = 1 donelines
 '
 
-test_expect_success 'setup for --stdin-packs tests' '
-       git init stdin-packs &&
-       (
-               cd stdin-packs &&
-
-               test_commit A &&
-               test_commit B &&
-               test_commit C &&
-
-               for id in A B C
-               do
-                       git pack-objects .git/objects/pack/pack-$id \
-                               --incremental --revs <<-EOF || exit 1
-                       refs/tags/$id
-                       EOF
-               done &&
-
-               ls -la .git/objects/pack
-       )
-'
-
-test_expect_success '--stdin-packs with excluded packs' '
-       (
-               cd stdin-packs &&
-
-               PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
-               PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
-               PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
-
-               git pack-objects test --stdin-packs <<-EOF &&
-               $PACK_A
-               ^$PACK_B
-               $PACK_C
-               EOF
-
-               (
-                       git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
-                       git show-index <$(ls .git/objects/pack/pack-C-*.idx)
-               ) >expect.raw &&
-               git show-index <$(ls test-*.idx) >actual.raw &&
-
-               cut -d" " -f2 <expect.raw | sort >expect &&
-               cut -d" " -f2 <actual.raw | sort >actual &&
-               test_cmp expect actual
-       )
-'
-
-test_expect_success '--stdin-packs is incompatible with --filter' '
-       (
-               cd stdin-packs &&
-               test_must_fail git pack-objects --stdin-packs --stdout \
-                       --filter=blob:none </dev/null 2>err &&
-               test_i18ngrep "cannot use --filter with --stdin-packs" err
-       )
-'
-
-test_expect_success '--stdin-packs is incompatible with --revs' '
-       (
-               cd stdin-packs &&
-               test_must_fail git pack-objects --stdin-packs --revs out \
-                       </dev/null 2>err &&
-               test_i18ngrep "cannot use internal rev list with --stdin-packs" err
-       )
-'
-
-test_expect_success '--stdin-packs with loose objects' '
-       (
-               cd stdin-packs &&
-
-               PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
-               PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
-               PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
-
-               test_commit D && # loose
-
-               git pack-objects test2 --stdin-packs --unpacked <<-EOF &&
-               $PACK_A
-               ^$PACK_B
-               $PACK_C
-               EOF
-
-               (
-                       git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
-                       git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
-                       git rev-list --objects --no-object-names \
-                               refs/tags/C..refs/tags/D
-
-               ) >expect.raw &&
-               ls -la . &&
-               git show-index <$(ls test2-*.idx) >actual.raw &&
-
-               cut -d" " -f2 <expect.raw | sort >expect &&
-               cut -d" " -f2 <actual.raw | sort >actual &&
-               test_cmp expect actual
-       )
-'
-
-test_expect_success '--stdin-packs with broken links' '
-       (
-               cd stdin-packs &&
-
-               # make an unreachable object with a bogus parent
-               git cat-file -p HEAD >commit &&
-               sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit |
-               git hash-object -w -t commit --stdin >in &&
-
-               git pack-objects .git/objects/pack/pack-D <in &&
-
-               PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
-               PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
-               PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
-               PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" &&
-
-               git pack-objects test3 --stdin-packs --unpacked <<-EOF &&
-               $PACK_A
-               ^$PACK_B
-               $PACK_C
-               $PACK_D
-               EOF
-
-               (
-                       git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
-                       git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
-                       git show-index <$(ls .git/objects/pack/pack-D-*.idx) &&
-                       git rev-list --objects --no-object-names \
-                               refs/tags/C..refs/tags/D
-               ) >expect.raw &&
-               git show-index <$(ls test3-*.idx) >actual.raw &&
-
-               cut -d" " -f2 <expect.raw | sort >expect &&
-               cut -d" " -f2 <actual.raw | sort >actual &&
-               test_cmp expect actual
-       )
-'
-
 test_expect_success 'negative window clamps to 0' '
        git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr &&
        check_deltas stderr = 0
index 3ccaaeb397774da8978c5406840ec307ffe7564d..226490d60df3a61b6b9831118562c4a88bae1cc9 100755 (executable)
@@ -8,55 +8,55 @@ test_description='mmap sliding window tests'
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
-test_expect_success \
-    'setup' \
-    'rm -f .git/index* &&
-     for i in a b c
-     do
-         echo $i >$i &&
-        test-tool genrandom "$i" 32768 >>$i &&
-         git update-index --add $i || return 1
-     done &&
-     echo d >d && cat c >>d && git update-index --add d &&
-     tree=$(git write-tree) &&
-     commit1=$(git commit-tree $tree </dev/null) &&
-     git update-ref HEAD $commit1 &&
-     git repack -a -d &&
-     test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
-     pack1=$(ls .git/objects/pack/*.pack) &&
-     test -f "$pack1"'
-
-test_expect_success \
-    'verify-pack -v, defaults' \
-    'git verify-pack -v "$pack1"'
-
-test_expect_success \
-    'verify-pack -v, packedGitWindowSize == 1 page' \
-    'git config core.packedGitWindowSize 512 &&
-     git verify-pack -v "$pack1"'
-
-test_expect_success \
-    'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' \
-    'git config core.packedGitWindowSize 512 &&
-     git config core.packedGitLimit 512 &&
-     git verify-pack -v "$pack1"'
-
-test_expect_success \
-    'repack -a -d, packedGit{WindowSize,Limit} == 1 page' \
-    'git config core.packedGitWindowSize 512 &&
-     git config core.packedGitLimit 512 &&
-     commit2=$(git commit-tree $tree -p $commit1 </dev/null) &&
-     git update-ref HEAD $commit2 &&
-     git repack -a -d &&
-     test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
-     pack2=$(ls .git/objects/pack/*.pack) &&
-     test -f "$pack2" &&
-     test "$pack1" \!= "$pack2"'
-
-test_expect_success \
-    'verify-pack -v, defaults' \
-    'git config --unset core.packedGitWindowSize &&
-     git config --unset core.packedGitLimit &&
-     git verify-pack -v "$pack2"'
+test_expect_success 'setup' '
+       rm -f .git/index* &&
+       for i in a b c
+       do
+       echo $i >$i &&
+       test-tool genrandom "$i" 32768 >>$i &&
+       git update-index --add $i || return 1
+       done &&
+       echo d >d && cat c >>d && git update-index --add d &&
+       tree=$(git write-tree) &&
+       commit1=$(git commit-tree $tree </dev/null) &&
+       git update-ref HEAD $commit1 &&
+       git repack -a -d &&
+       test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
+       pack1=$(ls .git/objects/pack/*.pack) &&
+       test -f "$pack1"
+'
+
+test_expect_success 'verify-pack -v, defaults' '
+       git verify-pack -v "$pack1"
+'
+
+test_expect_success 'verify-pack -v, packedGitWindowSize == 1 page' '
+       git config core.packedGitWindowSize 512 &&
+       git verify-pack -v "$pack1"
+'
+
+test_expect_success 'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' '
+       git config core.packedGitWindowSize 512 &&
+       git config core.packedGitLimit 512 &&
+       git verify-pack -v "$pack1"
+'
+
+test_expect_success 'repack -a -d, packedGit{WindowSize,Limit} == 1 page' '
+       git config core.packedGitWindowSize 512 &&
+       git config core.packedGitLimit 512 &&
+       commit2=$(git commit-tree $tree -p $commit1 </dev/null) &&
+       git update-ref HEAD $commit2 &&
+       git repack -a -d &&
+       test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
+       pack2=$(ls .git/objects/pack/*.pack) &&
+       test -f "$pack2" &&
+       test "$pack1" \!= "$pack2"
+'
+
+test_expect_success 'verify-pack -v, defaults' '
+       git config --unset core.packedGitWindowSize &&
+       git config --unset core.packedGitLimit &&
+       git verify-pack -v "$pack2"
+'
 
 test_done
index b0095ab41d30aadda45c7dd6e812fd0a5f40ab26..d88e6f16910151009e9700dd7d20d0d12f333ddf 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='pack index with 64-bit offsets and object CRC'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -263,7 +265,7 @@ tag guten tag
 This is an invalid tag.
 EOF
 
-       tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+       tag=$(git hash-object -t tag -w --stdin --literally <wrong-tag) &&
        pack1=$(echo $tag $sha | git pack-objects tag-test) &&
        echo remove tag object &&
        thirtyeight=${tag#??} &&
@@ -280,8 +282,8 @@ test_expect_success 'index-pack --fsck-objects also warns upon missing tagger in
 test_expect_success 'index-pack -v --stdin produces progress for both phases' '
        pack=$(git pack-objects --all pack </dev/null) &&
        GIT_PROGRESS_DELAY=0 git index-pack -v --stdin <pack-$pack.pack 2>err &&
-       test_i18ngrep "Receiving objects" err &&
-       test_i18ngrep "Resolving deltas" err
+       test_grep "Receiving objects" err &&
+       test_grep "Resolving deltas" err
 '
 
 test_expect_success 'too-large packs report the breach' '
index 2926e8dfc41223cc0030685beec8f6612db25a4d..61469ef4a681200044f45f0a98f54f39dec2a4c2 100755 (executable)
@@ -59,304 +59,304 @@ do_corrupt_object() {
 
 printf '\0' > zero
 
-test_expect_success \
-    'initial setup validation' \
-    'create_test_files &&
-     create_new_pack &&
-     git prune-packed &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'create corruption in header of first object' \
-    'do_corrupt_object $blob_1 0 < zero &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_1 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and loose copy of first delta allows for partial recovery' \
-    'git prune-packed &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'create corruption in data of first object' \
-    'create_new_pack &&
-     git prune-packed &&
-     chmod +w ${pack}.pack &&
-     perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_1 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and loose copy of second object allows for partial recovery' \
-    'git prune-packed &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'create corruption in header of first delta' \
-    'create_new_pack &&
-     git prune-packed &&
-     do_corrupt_object $blob_2 0 < zero &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'create corruption in data of first delta' \
-    'create_new_pack &&
-     git prune-packed &&
-     chmod +w ${pack}.pack &&
-     perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
-    'create_new_pack &&
-     git prune-packed &&
-     do_corrupt_object $blob_2 2 < zero &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \
-    'create_new_pack --delta-base-offset &&
-     git prune-packed &&
-     do_corrupt_object $blob_2 2 < zero &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack --delta-base-offset &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' \
-    'create_new_pack --delta-base-offset &&
-     git prune-packed &&
-     printf "\001" | do_corrupt_object $blob_2 2 &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack --delta-base-offset &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and a redundant pack allows for full recovery too' \
-    'do_corrupt_object $blob_2 2 < zero &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null &&
-     mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_1 &&
-     git hash-object -t blob -w file_2 &&
-     printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
-     git prune-packed &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'corruption of delta base reference pointing to wrong object' \
-    'create_new_pack --delta-base-offset &&
-     git prune-packed &&
-     printf "\220\033" | do_corrupt_object $blob_3 2 &&
-     git cat-file blob $blob_1 >/dev/null &&
-     git cat-file blob $blob_2 >/dev/null &&
-     test_must_fail git cat-file blob $blob_3 >/dev/null'
-
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_3 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack --delta-base-offset --no-reuse-delta &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
-    'corrupting header to have too small output buffer fails unpack' \
-    'create_new_pack &&
-     git prune-packed &&
-     printf "\262\001" | do_corrupt_object $blob_1 0 &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'initial setup validation' '
+       create_test_files &&
+       create_new_pack &&
+       git prune-packed &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'create corruption in header of first object' '
+       do_corrupt_object $blob_1 0 < zero &&
+       test_must_fail git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_1 &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and loose copy of first delta allows for partial recovery' '
+       git prune-packed &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_2 &&
+       mv tmp ${pack}.idx &&
+       test_must_fail git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'create corruption in data of first object' '
+       create_new_pack &&
+       git prune-packed &&
+       chmod +w ${pack}.pack &&
+       perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
+       test_must_fail git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_1 &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and loose copy of second object allows for partial recovery' '
+       git prune-packed &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_2 &&
+       mv tmp ${pack}.idx &&
+       test_must_fail git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'create corruption in header of first delta' '
+       create_new_pack &&
+       git prune-packed &&
+       do_corrupt_object $blob_2 0 < zero &&
+       git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_2 &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+       do_repack &&
+       git prune-packed &&
+       git verify-pack ${pack}.pack &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'create corruption in data of first delta' '
+       create_new_pack &&
+       git prune-packed &&
+       chmod +w ${pack}.pack &&
+       perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
+       git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_2 &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+       do_repack &&
+       git prune-packed &&
+       git verify-pack ${pack}.pack &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' '
+       create_new_pack &&
+       git prune-packed &&
+       do_corrupt_object $blob_2 2 < zero &&
+       git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_2 &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+       do_repack &&
+       git prune-packed &&
+       git verify-pack ${pack}.pack &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' '
+       create_new_pack --delta-base-offset &&
+       git prune-packed &&
+       do_corrupt_object $blob_2 2 < zero &&
+       git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_2 &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+       do_repack --delta-base-offset &&
+       git prune-packed &&
+       git verify-pack ${pack}.pack &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' '
+       create_new_pack --delta-base-offset &&
+       git prune-packed &&
+       printf "\001" | do_corrupt_object $blob_2 2 &&
+       git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_2 &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+       do_repack --delta-base-offset &&
+       git prune-packed &&
+       git verify-pack ${pack}.pack &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and a redundant pack allows for full recovery too' '
+       do_corrupt_object $blob_2 2 < zero &&
+       git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null &&
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_1 &&
+       git hash-object -t blob -w file_2 &&
+       printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
+       git prune-packed &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corruption of delta base reference pointing to wrong object' '
+       create_new_pack --delta-base-offset &&
+       git prune-packed &&
+       printf "\220\033" | do_corrupt_object $blob_3 2 &&
+       git cat-file blob $blob_1 >/dev/null &&
+       git cat-file blob $blob_2 >/dev/null &&
+       test_must_fail git cat-file blob $blob_3 >/dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+       mv ${pack}.idx tmp &&
+       git hash-object -t blob -w file_3 &&
+       mv tmp ${pack}.idx &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+       do_repack --delta-base-offset --no-reuse-delta &&
+       git prune-packed &&
+       git verify-pack ${pack}.pack &&
+       git cat-file blob $blob_1 > /dev/null &&
+       git cat-file blob $blob_2 > /dev/null &&
+       git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corrupting header to have too small output buffer fails unpack' '
+       create_new_pack &&
+       git prune-packed &&
+       printf "\262\001" | do_corrupt_object $blob_1 0 &&
+       test_must_fail git cat-file blob $blob_1 > /dev/null &&
+       test_must_fail git cat-file blob $blob_2 > /dev/null &&
+       test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
 # \0 - empty base
 # \1 - one byte in result
 # \1 - one literal byte (X)
-test_expect_success \
-    'apply good minimal delta' \
-    'printf "\0\1\1X" > minimal_delta &&
-     test-tool delta -p /dev/null minimal_delta /dev/null'
+test_expect_success 'apply good minimal delta' '
+       printf "\0\1\1X" > minimal_delta &&
+       test-tool delta -p /dev/null minimal_delta /dev/null
+'
 
 # \0 - empty base
 # \1 - 1 byte in result
 # \2 - two literal bytes (one too many)
-test_expect_success \
-    'apply delta with too many literal bytes' \
-    'printf "\0\1\2XX" > too_big_literal &&
-     test_must_fail test-tool delta -p /dev/null too_big_literal /dev/null'
+test_expect_success 'apply delta with too many literal bytes' '
+       printf "\0\1\2XX" > too_big_literal &&
+       test_must_fail test-tool delta -p /dev/null too_big_literal /dev/null
+'
 
 # \4 - four bytes in base
 # \1 - one byte in result
 # \221 - copy, one byte offset, one byte size
 #   \0 - copy from offset 0
 #   \2 - copy two bytes (one too many)
-test_expect_success \
-    'apply delta with too many copied bytes' \
-    'printf "\4\1\221\0\2" > too_big_copy &&
-     printf base >base &&
-     test_must_fail test-tool delta -p base too_big_copy /dev/null'
+test_expect_success 'apply delta with too many copied bytes' '
+       printf "\4\1\221\0\2" > too_big_copy &&
+       printf base >base &&
+       test_must_fail test-tool delta -p base too_big_copy /dev/null
+'
 
 # \0 - empty base
 # \2 - two bytes in result
 # \2 - two literal bytes (we are short one)
-test_expect_success \
-    'apply delta with too few literal bytes' \
-    'printf "\0\2\2X" > truncated_delta &&
-     test_must_fail test-tool delta -p /dev/null truncated_delta /dev/null'
+test_expect_success 'apply delta with too few literal bytes' '
+       printf "\0\2\2X" > truncated_delta &&
+       test_must_fail test-tool delta -p /dev/null truncated_delta /dev/null
+'
 
 # \0 - empty base
 # \1 - one byte in result
 # \221 - copy, one byte offset, one byte size
 #   \0 - copy from offset 0
 #   \1 - copy one byte (we are short one)
-test_expect_success \
-    'apply delta with too few bytes in base' \
-    'printf "\0\1\221\0\1" > truncated_base &&
-     test_must_fail test-tool delta -p /dev/null truncated_base /dev/null'
+test_expect_success 'apply delta with too few bytes in base' '
+       printf "\0\1\221\0\1" > truncated_base &&
+       test_must_fail test-tool delta -p /dev/null truncated_base /dev/null
+'
 
 # \4 - four bytes in base
 # \2 - two bytes in result
@@ -366,20 +366,20 @@ test_expect_success \
 #
 # Note that the literal byte is necessary to get past the uninteresting minimum
 # delta size check.
-test_expect_success \
-    'apply delta with truncated copy parameters' \
-    'printf "\4\2\1X\221" > truncated_copy_delta &&
-     printf base >base &&
-     test_must_fail test-tool delta -p base truncated_copy_delta /dev/null'
+test_expect_success 'apply delta with truncated copy parameters' '
+       printf "\4\2\1X\221" > truncated_copy_delta &&
+       printf base >base &&
+       test_must_fail test-tool delta -p base truncated_copy_delta /dev/null
+'
 
 # \0 - empty base
 # \1 - one byte in result
 # \1 - one literal byte (X)
 # \1 - trailing garbage command
-test_expect_success \
-    'apply delta with trailing garbage literal' \
-    'printf "\0\1\1X\1" > tail_garbage_literal &&
-     test_must_fail test-tool delta -p /dev/null tail_garbage_literal /dev/null'
+test_expect_success 'apply delta with trailing garbage literal' '
+       printf "\0\1\1X\1" > tail_garbage_literal &&
+       test_must_fail test-tool delta -p /dev/null tail_garbage_literal /dev/null
+'
 
 # \4 - four bytes in base
 # \1 - one byte in result
@@ -387,19 +387,19 @@ test_expect_success \
 # \221 - copy, one byte offset, one byte size
 #   \0 - copy from offset 0
 #   \1 - copy 1 byte
-test_expect_success \
-    'apply delta with trailing garbage copy' \
-    'printf "\4\1\1X\221\0\1" > tail_garbage_copy &&
-     printf base >base &&
-     test_must_fail test-tool delta -p /dev/null tail_garbage_copy /dev/null'
+test_expect_success 'apply delta with trailing garbage copy' '
+       printf "\4\1\1X\221\0\1" > tail_garbage_copy &&
+       printf base >base &&
+       test_must_fail test-tool delta -p /dev/null tail_garbage_copy /dev/null
+'
 
 # \0 - empty base
 # \1 - one byte in result
 # \1 - one literal byte (X)
 # \0 - bogus opcode
-test_expect_success \
-    'apply delta with trailing garbage opcode' \
-    'printf "\0\1\1X\0" > tail_garbage_opcode &&
-     test_must_fail test-tool delta -p /dev/null tail_garbage_opcode /dev/null'
+test_expect_success 'apply delta with trailing garbage opcode' '
+       printf "\0\1\1X\0" > tail_garbage_opcode &&
+       test_must_fail test-tool delta -p /dev/null tail_garbage_opcode /dev/null
+'
 
 test_done
index 8ae314af585482ec240fed353613faf6b23ebb67..1f1f664871ece6dba57e6bb601ba3771f490e2eb 100755 (executable)
@@ -16,7 +16,7 @@ add_blob() {
        before=$(git count-objects | sed "s/ .*//") &&
        BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
        BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-       verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
        test_path_is_file $BLOB_FILE &&
        test-tool chmtime =+0 $BLOB_FILE
 }
@@ -29,6 +29,14 @@ test_expect_success setup '
        git gc
 '
 
+test_expect_success 'bare repo prune is quiet without $GIT_DIR/objects/pack' '
+       git clone -q --shared --template= --bare . bare.git &&
+       rmdir bare.git/objects/pack &&
+       git --git-dir=bare.git prune --no-progress 2>prune.err &&
+       test_must_be_empty prune.err &&
+       rm -r bare.git prune.err
+'
+
 test_expect_success 'prune stale packs' '
        orig_pack=$(echo .git/objects/pack/*.pack) &&
        >.git/objects/tmp_1.pack &&
@@ -43,34 +51,42 @@ test_expect_success 'prune stale packs' '
 test_expect_success 'prune --expire' '
        add_blob &&
        git prune --expire=1.hour.ago &&
-       verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
        test_path_is_file $BLOB_FILE &&
        test-tool chmtime =-86500 $BLOB_FILE &&
        git prune --expire 1.day &&
-       verbose test $before = $(git count-objects | sed "s/ .*//") &&
+       test $before = $(git count-objects | sed "s/ .*//") &&
        test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc: implicit prune --expire' '
        add_blob &&
        test-tool chmtime =-$((2*$week-30)) $BLOB_FILE &&
-       git gc &&
-       verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       git gc --no-cruft &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
        test_path_is_file $BLOB_FILE &&
        test-tool chmtime =-$((2*$week+1)) $BLOB_FILE &&
-       git gc &&
-       verbose test $before = $(git count-objects | sed "s/ .*//") &&
+       git gc --no-cruft &&
+       test $before = $(git count-objects | sed "s/ .*//") &&
        test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
-       git config gc.pruneExpire invalid &&
-       test_must_fail git gc
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       >repo/.git/config &&
+       git -C repo config gc.pruneExpire invalid &&
+       cat >expect <<-\EOF &&
+       error: Invalid gc.pruneexpire: '\''invalid'\''
+       fatal: bad config variable '\''gc.pruneexpire'\'' in file '\''.git/config'\'' at line 2
+       EOF
+       test_must_fail git -C repo gc 2>actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'gc: start with ok gc.pruneExpire' '
        git config gc.pruneExpire 2.days.ago &&
-       git gc
+       git gc --no-cruft
 '
 
 test_expect_success 'prune: prune nonsense parameters' '
@@ -121,44 +137,44 @@ test_expect_success 'gc --no-prune' '
        add_blob &&
        test-tool chmtime =-$((5001*$day)) $BLOB_FILE &&
        git config gc.pruneExpire 2.days.ago &&
-       git gc --no-prune &&
-       verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+       git gc --no-prune --no-cruft &&
+       test 1 = $(git count-objects | sed "s/ .*//") &&
        test_path_is_file $BLOB_FILE
 '
 
 test_expect_success 'gc respects gc.pruneExpire' '
        git config gc.pruneExpire 5002.days.ago &&
-       git gc &&
+       git gc --no-cruft &&
        test_path_is_file $BLOB_FILE &&
        git config gc.pruneExpire 5000.days.ago &&
-       git gc &&
+       git gc --no-cruft &&
        test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc --prune=<date>' '
        add_blob &&
        test-tool chmtime =-$((5001*$day)) $BLOB_FILE &&
-       git gc --prune=5002.days.ago &&
+       git gc --prune=5002.days.ago --no-cruft &&
        test_path_is_file $BLOB_FILE &&
-       git gc --prune=5000.days.ago &&
+       git gc --prune=5000.days.ago --no-cruft &&
        test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc --prune=never' '
        add_blob &&
-       git gc --prune=never &&
+       git gc --prune=never --no-cruft &&
        test_path_is_file $BLOB_FILE &&
-       git gc --prune=now &&
+       git gc --prune=now --no-cruft &&
        test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc respects gc.pruneExpire=never' '
        git config gc.pruneExpire never &&
        add_blob &&
-       git gc &&
+       git gc --no-cruft &&
        test_path_is_file $BLOB_FILE &&
        git config gc.pruneExpire now &&
-       git gc &&
+       git gc --no-cruft &&
        test_path_is_missing $BLOB_FILE
 '
 
@@ -176,10 +192,10 @@ test_expect_success 'gc: prune old objects after local clone' '
        git clone --no-hardlinks . aclone &&
        (
                cd aclone &&
-               verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+               test 1 = $(git count-objects | sed "s/ .*//") &&
                test_path_is_file $BLOB_FILE &&
-               git gc --prune &&
-               verbose test 0 = $(git count-objects | sed "s/ .*//") &&
+               git gc --prune --no-cruft &&
+               test 0 = $(git count-objects | sed "s/ .*//") &&
                test_path_is_missing $BLOB_FILE
        )
 '
@@ -221,7 +237,7 @@ test_expect_success 'clean pack garbage with gc' '
        >.git/objects/pack/fake2.keep &&
        >.git/objects/pack/fake2.idx &&
        >.git/objects/pack/fake3.keep &&
-       git gc &&
+       git gc --no-cruft &&
        git count-objects -v 2>stderr &&
        grep "^warning:" stderr | sort >actual &&
        cat >expected <<\EOF &&
@@ -302,10 +318,10 @@ test_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
 
 test_expect_success 'prune: handle expire option correctly' '
        test_must_fail git prune --expire 2>error &&
-       test_i18ngrep "requires a value" error &&
+       test_grep "requires a value" error &&
 
        test_must_fail git prune --expire=nyah 2>error &&
-       test_i18ngrep "malformed expiration" error &&
+       test_grep "malformed expiration" error &&
 
        git prune --no-expire
 '
@@ -334,4 +350,18 @@ test_expect_success 'old reachable-from-recent retained with bitmaps' '
        test_must_fail git cat-file -e $to_drop
 '
 
+test_expect_success 'gc.recentObjectsHook' '
+       add_blob &&
+       test-tool chmtime =-86500 $BLOB_FILE &&
+
+       write_script precious-objects <<-EOF &&
+       echo $BLOB
+       EOF
+       test_config gc.recentObjectsHook ./precious-objects &&
+
+       git prune --expire=now &&
+
+       git cat-file -p $BLOB
+'
+
 test_done
index 51973f4a512bf914ee0fb50d5472d050f241a2ed..0d50c6b4bca4c0a005ad05f262e4dfc91eb73d53 100755 (executable)
@@ -6,22 +6,23 @@
 test_description='git-pack-object with missing base
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Create A-B chain
 #
-test_expect_success \
-    'setup base' \
-    'test_write_lines a b c d e f g h i >text &&
-     echo side >side &&
-     git update-index --add text side &&
-     A=$(echo A | git commit-tree $(git write-tree)) &&
+test_expect_success 'setup base' '
+       test_write_lines a b c d e f g h i >text &&
+       echo side >side &&
+       git update-index --add text side &&
+       A=$(echo A | git commit-tree $(git write-tree)) &&
 
-     echo m >>text &&
-     git update-index text &&
-     B=$(echo B | git commit-tree $(git write-tree) -p $A) &&
-     git update-ref HEAD $B
-    '
+       echo m >>text &&
+       git update-index text &&
+       B=$(echo B | git commit-tree $(git write-tree) -p $A) &&
+       git update-ref HEAD $B
+'
 
 # Create repository with C whose parent is B.
 # Repository contains C, C^{tree}, C:text, B, B^{tree}.
@@ -29,52 +30,49 @@ test_expect_success \
 # Repository is missing A (parent of B).
 # Repository is missing A:side.
 #
-test_expect_success \
-    'setup patch_clone' \
-    'base_objects=$(pwd)/.git/objects &&
-     (mkdir patch_clone &&
-      cd patch_clone &&
-      git init &&
-      echo "$base_objects" >.git/objects/info/alternates &&
-      echo q >>text &&
-      git read-tree $B &&
-      git update-index text &&
-      git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) &&
-      rm .git/objects/info/alternates &&
+test_expect_success 'setup patch_clone' '
+       base_objects=$(pwd)/.git/objects &&
+       (mkdir patch_clone &&
+       cd patch_clone &&
+       git init &&
+       echo "$base_objects" >.git/objects/info/alternates &&
+       echo q >>text &&
+       git read-tree $B &&
+       git update-index text &&
+       git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) &&
+       rm .git/objects/info/alternates &&
 
-      git --git-dir=../.git cat-file commit $B |
-      git hash-object -t commit -w --stdin &&
+       git --git-dir=../.git cat-file commit $B |
+       git hash-object -t commit -w --stdin &&
 
-      git --git-dir=../.git cat-file tree "$B^{tree}" |
-      git hash-object -t tree -w --stdin
-     ) &&
-     C=$(git --git-dir=patch_clone/.git rev-parse HEAD)
-    '
+       git --git-dir=../.git cat-file tree "$B^{tree}" |
+       git hash-object -t tree -w --stdin
+       ) &&
+       C=$(git --git-dir=patch_clone/.git rev-parse HEAD)
+'
 
 # Clone patch_clone indirectly by cloning base and fetching.
 #
-test_expect_success \
-    'indirectly clone patch_clone' \
-    '(mkdir user_clone &&
-      cd user_clone &&
-      git init &&
-      git pull ../.git &&
-      test $(git rev-parse HEAD) = $B &&
+test_expect_success 'indirectly clone patch_clone' '
+       (mkdir user_clone &&
+        cd user_clone &&
+        git init &&
+        git pull ../.git &&
+        test $(git rev-parse HEAD) = $B &&
 
-      git pull ../patch_clone/.git &&
-      test $(git rev-parse HEAD) = $C
-     )
-    '
+        git pull ../patch_clone/.git &&
+        test $(git rev-parse HEAD) = $C
+       )
+'
 
 # Cloning the patch_clone directly should fail.
 #
-test_expect_success \
-    'clone of patch_clone is incomplete' \
-    '(mkdir user_direct &&
-      cd user_direct &&
-      git init &&
-      test_must_fail git fetch ../patch_clone/.git
-     )
-    '
+test_expect_success 'clone of patch_clone is incomplete' '
+       (mkdir user_direct &&
+        cd user_direct &&
+        git init &&
+        test_must_fail git fetch ../patch_clone/.git
+       )
+'
 
 test_done
index 6d693eef82f74a72ad4f29c399e297427c892d12..d7fd71360e17bd2368562dd7c35644442475bb60 100755 (executable)
@@ -9,6 +9,10 @@ test_description='exercise basic bitmap functionality'
 # their place.
 GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
 
+# Likewise, allow individual tests to control whether or not they use
+# the boundary-based traversal.
+sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
 objpath () {
        echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
 }
@@ -267,7 +271,7 @@ test_bitmap_cases () {
                mv -f $bitmap.tmp $bitmap &&
                git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
                test_cmp expect actual &&
-               test_i18ngrep corrupt.ewah.bitmap stderr
+               test_grep corrupt.ewah.bitmap stderr
        '
 
        test_expect_success 'truncated bitmap fails gracefully (cache)' '
@@ -280,7 +284,7 @@ test_bitmap_cases () {
                mv -f $bitmap.tmp $bitmap &&
                git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
                test_cmp expect actual &&
-               test_i18ngrep corrupted.bitmap.index stderr
+               test_grep corrupted.bitmap.index stderr
        '
 
        # Create a state of history with these properties:
@@ -404,6 +408,26 @@ test_bitmap_cases () {
                )
        '
 
+       test_expect_success 'pack.preferBitmapTips' '
+               git init repo &&
+               test_when_finished "rm -rf repo" &&
+               (
+                       cd repo &&
+                       git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+                       test_commit_bulk --message="%s" 103 &&
+
+                       cat >>.git/config <<-\EOF &&
+                       [pack]
+                               preferBitmapTips
+                       EOF
+                       cat >expect <<-\EOF &&
+                       error: missing value for '\''pack.preferbitmaptips'\''
+                       EOF
+                       git repack -adb 2>actual &&
+                       test_cmp expect actual
+               )
+       '
+
        test_expect_success 'complains about multiple pack bitmaps' '
                rm -fr repo &&
                git init repo &&
@@ -428,18 +452,26 @@ test_bitmap_cases () {
                        test_line_count = 2 packs &&
                        test_line_count = 2 bitmaps &&
 
-                       git rev-list --use-bitmap-index HEAD 2>err &&
-                       grep "ignoring extra bitmap file" err
+                       GIT_TRACE2_EVENT=$(pwd)/trace2.txt git rev-list --use-bitmap-index HEAD &&
+                       grep "opened bitmap" trace2.txt &&
+                       grep "ignoring extra bitmap" trace2.txt
                )
        '
 }
 
 test_bitmap_cases
 
+GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
+export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
+test_bitmap_cases
+
+sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
 test_expect_success 'incremental repack fails when bitmaps are requested' '
        test_commit more-1 &&
        test_must_fail git repack -d 2>err &&
-       test_i18ngrep "Incremental repacks are incompatible with bitmap" err
+       test_grep "Incremental repacks are incompatible with bitmap" err
 '
 
 test_expect_success 'incremental repack can disable bitmaps' '
@@ -447,6 +479,33 @@ test_expect_success 'incremental repack can disable bitmaps' '
        git repack -d --no-write-bitmap-index
 '
 
+test_expect_success 'boundary-based traversal is used when requested' '
+       git repack -a -d --write-bitmap-index &&
+
+       for argv in \
+               "git -c pack.useBitmapBoundaryTraversal=true" \
+               "git -c feature.experimental=true" \
+               "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 git"
+       do
+               eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \
+                       --use-bitmap-index second..other 2>perf" &&
+               grep "\"region_enter\".*\"label\":\"haves/boundary\"" perf ||
+                       return 1
+       done &&
+
+       for argv in \
+               "git -c pack.useBitmapBoundaryTraversal=false" \
+               "git -c feature.experimental=true -c pack.useBitmapBoundaryTraversal=false" \
+               "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=0 git -c pack.useBitmapBoundaryTraversal=true" \
+               "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=0 git -c feature.experimental=true"
+       do
+               eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \
+                       --use-bitmap-index second..other 2>perf" &&
+               grep "\"region_enter\".*\"label\":\"haves/classic\"" perf ||
+                       return 1
+       done
+'
+
 test_bitmap_cases "pack.writeBitmapLookupTable"
 
 test_expect_success 'verify writing bitmap lookup table when enabled' '
@@ -465,7 +524,7 @@ test_expect_success 'truncated bitmap fails gracefully (lookup table)' '
        mv -f $bitmap.tmp $bitmap &&
        git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
        test_cmp expect actual &&
-       test_i18ngrep corrupted.bitmap.index stderr
+       test_grep corrupted.bitmap.index stderr
 '
 
 test_done
index 9dae60f73e3253bbb4b44355535c8cf1ebc58be8..4fe71fe8cd21ddfb0f44821c672585219278bbda 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check bitmap operation with shallow repositories'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # We want to create a situation where the shallow, grafted
index 9d8e249ae8b7f38c4c941bd76ef904cdfe1b3d27..230cb3871223e1f28482c841093cb7a0e6873754 100755 (executable)
@@ -14,6 +14,7 @@ what currently happens. If that changes, these tests should be revisited.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'disable reflogs' '
index cc4cfaa9d3712684f57f34372a4f40d0abf11f7e..ceaa6700a27230cdea34bd65aeff2db34c53ad64 100755 (executable)
@@ -59,7 +59,7 @@ test_expect_success 'setup' '
 test_expect_success 'set up base packfile and variables' '
        # the hash of this content starts with ff, which
        # makes some later computations much simpler
-       echo $(test_oid oidfff) >file &&
+       test_oid oidfff >file &&
        git add file &&
        git commit -m base &&
        git repack -ad &&
index 73a241743aa50101c032b53566de7ba2e7a3b410..82734b9a3c4420a63b91901d165ae06f4b823ea7 100755 (executable)
@@ -63,13 +63,16 @@ TEST_PASSES_SANITIZE_LEAK=true
 # Note that the two variants of "file" must be similar enough to convince git
 # to create the delta.
 make_pack () {
-       {
-               printf '%s\n' "-$(git rev-parse $2)"
-               printf '%s dummy\n' "$(git rev-parse $1:dummy)"
-               printf '%s file\n' "$(git rev-parse $1:file)"
-       } |
-       git pack-objects --stdout |
-       git index-pack --stdin --fix-thin
+       ln1=$(git rev-parse "$2") &&
+       ln2=$(git rev-parse "$1:dummy") &&
+       ln3=$(git rev-parse "$1:file") &&
+       cat >list <<-EOF
+       -$ln1
+       $ln2 dummy
+       $ln3 file
+       EOF
+       git pack-objects --stdout <list >pack &&
+       git index-pack --stdin --fix-thin <pack
 }
 
 test_expect_success 'setup' '
index bb633c9b099ed034c275389cb8e9e9da16efe704..2ff3eef9a3b8cad7f95253c79283d3ac542802fd 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git pack-objects using object filtering'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Test blob:none filter.
@@ -24,8 +25,9 @@ parse_verify_pack_blob_oid () {
 }
 
 test_expect_success 'verify blob count in normal packfile' '
-       git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 |
-       test_parse_ls_files_stage_oids |
+       git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+               >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r1 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -51,6 +53,14 @@ test_expect_success 'verify blob:none packfile has no blobs' '
        ! grep blob verify_result
 '
 
+test_expect_success 'verify blob:none packfile without --stdout' '
+       git -C r1 pack-objects --revs --filter=blob:none mypackname >packhash <<-EOF &&
+       HEAD
+       EOF
+       git -C r1 verify-pack -v "mypackname-$(cat packhash).pack" >verify_result &&
+       ! grep blob verify_result
+'
+
 test_expect_success 'verify normal and blob:none packfiles have same commits/trees' '
        git -C r1 verify-pack -v ../all.pack >verify_result &&
        grep -E "commit|tree" verify_result |
@@ -123,8 +133,8 @@ test_expect_success 'setup r2' '
 '
 
 test_expect_success 'verify blob count in normal packfile' '
-       git -C r2 ls-files -s large.1000 large.10000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -161,8 +171,8 @@ test_expect_success 'verify blob:limit=1000' '
 '
 
 test_expect_success 'verify blob:limit=1001' '
-       git -C r2 ls-files -s large.1000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
@@ -179,8 +189,8 @@ test_expect_success 'verify blob:limit=1001' '
 '
 
 test_expect_success 'verify blob:limit=10001' '
-       git -C r2 ls-files -s large.1000 large.10000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
@@ -197,8 +207,8 @@ test_expect_success 'verify blob:limit=10001' '
 '
 
 test_expect_success 'verify blob:limit=1k' '
-       git -C r2 ls-files -s large.1000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
@@ -215,8 +225,8 @@ test_expect_success 'verify blob:limit=1k' '
 '
 
 test_expect_success 'verify explicitly specifying oversized blob in input' '
-       git -C r2 ls-files -s large.1000 large.10000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        echo HEAD >objects &&
@@ -233,8 +243,8 @@ test_expect_success 'verify explicitly specifying oversized blob in input' '
 '
 
 test_expect_success 'verify blob:limit=1m' '
-       git -C r2 ls-files -s large.1000 large.10000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
@@ -264,6 +274,44 @@ test_expect_success 'verify normal and blob:limit packfiles have same commits/tr
        test_cmp expected observed
 '
 
+test_expect_success 'verify small limit and big limit results in small limit' '
+       git -C r2 ls-files -s large.1000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
+       sort >expected &&
+
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 \
+               --filter=blob:limit=10001 >filter.pack <<-EOF &&
+       HEAD
+       EOF
+       git -C r2 index-pack ../filter.pack &&
+
+       git -C r2 verify-pack -v ../filter.pack >verify_result &&
+       grep blob verify_result |
+       parse_verify_pack_blob_oid |
+       sort >observed &&
+
+       test_cmp expected observed
+'
+
+test_expect_success 'verify big limit and small limit results in small limit' '
+       git -C r2 ls-files -s large.1000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
+       sort >expected &&
+
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 \
+               --filter=blob:limit=1001 >filter.pack <<-EOF &&
+       HEAD
+       EOF
+       git -C r2 index-pack ../filter.pack &&
+
+       git -C r2 verify-pack -v ../filter.pack >verify_result &&
+       grep blob verify_result |
+       parse_verify_pack_blob_oid |
+       sort >observed &&
+
+       test_cmp expected observed
+'
+
 # Test sparse:path=<path> filter.
 # !!!!
 # NOTE: sparse:path filter support has been dropped for security reasons,
@@ -289,8 +337,9 @@ test_expect_success 'setup r3' '
 '
 
 test_expect_success 'verify blob count in normal packfile' '
-       git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 |
-       test_parse_ls_files_stage_oids |
+       git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
+               >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r3 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -341,8 +390,9 @@ test_expect_success 'setup r4' '
 '
 
 test_expect_success 'verify blob count in normal packfile' '
-       git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 |
-       test_parse_ls_files_stage_oids |
+       git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
+               >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r4 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -359,8 +409,8 @@ test_expect_success 'verify blob count in normal packfile' '
 '
 
 test_expect_success 'verify sparse:oid=OID' '
-       git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 |
-       test_parse_ls_files_stage_oids |
+       git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r4 ls-files -s pattern >staged &&
@@ -379,8 +429,8 @@ test_expect_success 'verify sparse:oid=OID' '
 '
 
 test_expect_success 'verify sparse:oid=oid-ish' '
-       git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 |
-       test_parse_ls_files_stage_oids |
+       git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r4 pack-objects --revs --stdout --filter=sparse:oid=main:pattern >filter.pack <<-EOF &&
@@ -400,8 +450,9 @@ test_expect_success 'verify sparse:oid=oid-ish' '
 # This models previously omitted objects that we did not receive.
 
 test_expect_success 'setup r1 - delete loose blobs' '
-       git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 |
-       test_parse_ls_files_stage_oids |
+       git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+               >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        for id in `cat expected | sed "s|..|&/|"`
index 049c5fc8ead328860ef7ed38dea410f4524107c9..a2b44426609fe5c52347510e8b82c9484897dea8 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='commit graph'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
 
 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
 
@@ -24,12 +25,10 @@ test_expect_success 'usage shown with an error on unknown sub-command' '
        test_cmp expect actual
 '
 
+objdir=".git/objects"
+
 test_expect_success 'setup full repo' '
-       mkdir full &&
-       cd "$TRASH_DIRECTORY/full" &&
-       git init &&
-       git config core.commitGraph true &&
-       objdir=".git/objects"
+       git init full
 '
 
 test_expect_success POSIXPERM 'tweak umask for modebit tests' '
@@ -37,31 +36,28 @@ test_expect_success POSIXPERM 'tweak umask for modebit tests' '
 '
 
 test_expect_success 'verify graph with no graph file' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph verify
+       git -C full commit-graph verify
 '
 
 test_expect_success 'write graph with no packs' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph write --object-dir $objdir &&
-       test_path_is_missing $objdir/info/commit-graph
+       git -C full commit-graph write --object-dir $objdir &&
+       test_path_is_missing full/$objdir/info/commit-graph
 '
 
 test_expect_success 'exit with correct error on bad input to --stdin-packs' '
-       cd "$TRASH_DIRECTORY/full" &&
        echo doesnotexist >in &&
-       test_expect_code 1 git commit-graph write --stdin-packs <in 2>stderr &&
-       test_i18ngrep "error adding pack" stderr
+       test_expect_code 1 git -C full commit-graph write --stdin-packs \
+               <in 2>stderr &&
+       test_grep "error adding pack" stderr
 '
 
 test_expect_success 'create commits and repack' '
-       cd "$TRASH_DIRECTORY/full" &&
        for i in $(test_seq 3)
        do
-               test_commit $i &&
-               git branch commits/$i || return 1
+               test_commit -C full $i &&
+               git -C full branch commits/$i || return 1
        done &&
-       git repack
+       git -C full repack
 '
 
 . "$TEST_DIRECTORY"/lib-commit-graph.sh
@@ -69,117 +65,106 @@ test_expect_success 'create commits and repack' '
 graph_git_behavior 'no graph' full commits/3 commits/1
 
 test_expect_success 'exit with correct error on bad input to --stdin-commits' '
-       cd "$TRASH_DIRECTORY/full" &&
        # invalid, non-hex OID
-       echo HEAD >in &&
-       test_expect_code 1 git commit-graph write --stdin-commits <in 2>stderr &&
-       test_i18ngrep "unexpected non-hex object ID: HEAD" stderr &&
+       echo HEAD | test_expect_code 1 git -C full commit-graph write \
+               --stdin-commits 2>stderr &&
+       test_grep "unexpected non-hex object ID: HEAD" stderr &&
        # non-existent OID
-       echo $ZERO_OID >in &&
-       test_expect_code 1 git commit-graph write --stdin-commits <in 2>stderr &&
-       test_i18ngrep "invalid object" stderr &&
+       echo $ZERO_OID | test_expect_code 1 git -C full commit-graph write \
+               --stdin-commits 2>stderr &&
+       test_grep "invalid object" stderr &&
        # valid commit and tree OID
-       git rev-parse HEAD HEAD^{tree} >in &&
-       git commit-graph write --stdin-commits <in &&
-       graph_read_expect 3 generation_data
+       git -C full rev-parse HEAD HEAD^{tree} >in &&
+       git -C full commit-graph write --stdin-commits <in &&
+       graph_read_expect -C full 3 generation_data
 '
 
 test_expect_success 'write graph' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph write &&
-       test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "3" generation_data
+       git -C full commit-graph write &&
+       test_path_is_file full/$objdir/info/commit-graph &&
+       graph_read_expect -C full 3 generation_data
 '
 
 test_expect_success POSIXPERM 'write graph has correct permissions' '
-       test_path_is_file $objdir/info/commit-graph &&
+       test_path_is_file full/$objdir/info/commit-graph &&
        echo "-r--r--r--" >expect &&
-       test_modebits $objdir/info/commit-graph >actual &&
+       test_modebits full/$objdir/info/commit-graph >actual &&
        test_cmp expect actual
 '
 
 graph_git_behavior 'graph exists' full commits/3 commits/1
 
 test_expect_success 'Add more commits' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git reset --hard commits/1 &&
+       git -C full reset --hard commits/1 &&
        for i in $(test_seq 4 5)
        do
-               test_commit $i &&
-               git branch commits/$i || return 1
+               test_commit -C full $i &&
+               git -C full branch commits/$i || return 1
        done &&
-       git reset --hard commits/2 &&
+       git -C full reset --hard commits/2 &&
        for i in $(test_seq 6 7)
        do
-               test_commit $i &&
-               git branch commits/$i || return 1
+               test_commit -C full $i &&
+               git -C full branch commits/$i || return 1
        done &&
-       git reset --hard commits/2 &&
-       git merge commits/4 &&
-       git branch merge/1 &&
-       git reset --hard commits/4 &&
-       git merge commits/6 &&
-       git branch merge/2 &&
-       git reset --hard commits/3 &&
-       git merge commits/5 commits/7 &&
-       git branch merge/3 &&
-       git repack
+       git -C full reset --hard commits/2 &&
+       git -C full merge commits/4 &&
+       git -C full branch merge/1 &&
+       git -C full reset --hard commits/4 &&
+       git -C full merge commits/6 &&
+       git -C full branch merge/2 &&
+       git -C full reset --hard commits/3 &&
+       git -C full merge commits/5 commits/7 &&
+       git -C full branch merge/3 &&
+       git -C full repack
 '
 
 test_expect_success 'commit-graph write progress off for redirected stderr' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph write 2>err &&
+       git -C full commit-graph write 2>err &&
        test_must_be_empty err
 '
 
 test_expect_success 'commit-graph write force progress on for stderr' '
-       cd "$TRASH_DIRECTORY/full" &&
-       GIT_PROGRESS_DELAY=0 git commit-graph write --progress 2>err &&
+       GIT_PROGRESS_DELAY=0 git -C full commit-graph write --progress 2>err &&
        test_file_not_empty err
 '
 
 test_expect_success 'commit-graph write with the --no-progress option' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph write --no-progress 2>err &&
+       git -C full commit-graph write --no-progress 2>err &&
        test_must_be_empty err
 '
 
 test_expect_success 'commit-graph write --stdin-commits progress off for redirected stderr' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git rev-parse commits/5 >in &&
-       git commit-graph write --stdin-commits <in 2>err &&
+       git -C full rev-parse commits/5 >in &&
+       git -C full commit-graph write --stdin-commits <in 2>err &&
        test_must_be_empty err
 '
 
 test_expect_success 'commit-graph write --stdin-commits force progress on for stderr' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git rev-parse commits/5 >in &&
-       GIT_PROGRESS_DELAY=0 git commit-graph write --stdin-commits --progress <in 2>err &&
-       test_i18ngrep "Collecting commits from input" err
+       git -C full rev-parse commits/5 >in &&
+       GIT_PROGRESS_DELAY=0 git -C full commit-graph write --stdin-commits \
+               --progress <in 2>err &&
+       test_grep "Collecting commits from input" err
 '
 
 test_expect_success 'commit-graph write --stdin-commits with the --no-progress option' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git rev-parse commits/5 >in &&
-       git commit-graph write --stdin-commits --no-progress <in 2>err &&
+       git -C full rev-parse commits/5 >in &&
+       git -C full commit-graph write --stdin-commits --no-progress <in 2>err &&
        test_must_be_empty err
 '
 
 test_expect_success 'commit-graph verify progress off for redirected stderr' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph verify 2>err &&
+       git -C full commit-graph verify 2>err &&
        test_must_be_empty err
 '
 
 test_expect_success 'commit-graph verify force progress on for stderr' '
-       cd "$TRASH_DIRECTORY/full" &&
-       GIT_PROGRESS_DELAY=0 git commit-graph verify --progress 2>err &&
+       GIT_PROGRESS_DELAY=0 git -C full commit-graph verify --progress 2>err &&
        test_file_not_empty err
 '
 
 test_expect_success 'commit-graph verify with the --no-progress option' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph verify --no-progress 2>err &&
+       git -C full commit-graph verify --no-progress 2>err &&
        test_must_be_empty err
 '
 
@@ -194,10 +179,9 @@ test_expect_success 'commit-graph verify with the --no-progress option' '
 # 1
 
 test_expect_success 'write graph with merges' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph write &&
-       test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "10" "generation_data extra_edges"
+       git -C full commit-graph write &&
+       test_path_is_file full/$objdir/info/commit-graph &&
+       graph_read_expect -C full 10 "generation_data extra_edges"
 '
 
 graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
@@ -205,12 +189,11 @@ graph_git_behavior 'merge 1 vs 3' full merge/1 merge/3
 graph_git_behavior 'merge 2 vs 3' full merge/2 merge/3
 
 test_expect_success 'Add one more commit' '
-       cd "$TRASH_DIRECTORY/full" &&
-       test_commit 8 &&
-       git branch commits/8 &&
-       ls $objdir/pack | grep idx >existing-idx &&
-       git repack &&
-       ls $objdir/pack| grep idx | grep -v -f existing-idx >new-idx
+       test_commit -C full 8 &&
+       git -C full branch commits/8 &&
+       ls full/$objdir/pack | grep idx >existing-idx &&
+       git -C full repack &&
+       ls full/$objdir/pack| grep idx | grep -v -f existing-idx >new-idx
 '
 
 # Current graph structure:
@@ -229,114 +212,101 @@ graph_git_behavior 'mixed mode, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'mixed mode, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'write graph with new commit' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph write &&
-       test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "generation_data extra_edges"
+       git -C full commit-graph write &&
+       test_path_is_file full/$objdir/info/commit-graph &&
+       graph_read_expect -C full 11 "generation_data extra_edges"
 '
 
 graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'full graph, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'write graph with nothing new' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph write &&
-       test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "generation_data extra_edges"
+       git -C full commit-graph write &&
+       test_path_is_file full/$objdir/info/commit-graph &&
+       graph_read_expect -C full 11 "generation_data extra_edges"
 '
 
 graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'cleared graph, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'build graph from latest pack with closure' '
-       cd "$TRASH_DIRECTORY/full" &&
-       cat new-idx | git commit-graph write --stdin-packs &&
-       test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "9" "generation_data extra_edges"
+       git -C full commit-graph write --stdin-packs <new-idx &&
+       test_path_is_file full/$objdir/info/commit-graph &&
+       graph_read_expect -C full 9 "generation_data extra_edges"
 '
 
 graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'graph from pack, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'build graph from commits with closure' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git tag -a -m "merge" tag/merge merge/2 &&
-       git rev-parse tag/merge >commits-in &&
-       git rev-parse merge/1 >>commits-in &&
-       cat commits-in | git commit-graph write --stdin-commits &&
-       test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "6" "generation_data"
+       git -C full tag -a -m "merge" tag/merge merge/2 &&
+       git -C full rev-parse tag/merge >commits-in &&
+       git -C full rev-parse merge/1 >>commits-in &&
+       git -C full commit-graph write --stdin-commits <commits-in &&
+       test_path_is_file full/$objdir/info/commit-graph &&
+       graph_read_expect -C full 6 "generation_data"
 '
 
 graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'graph from commits, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'build graph from commits with append' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
-       test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "10" "generation_data extra_edges"
+       git -C full rev-parse merge/3 >in &&
+       git -C full commit-graph write --stdin-commits --append <in &&
+       test_path_is_file full/$objdir/info/commit-graph &&
+       graph_read_expect -C full 10 "generation_data extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'build graph using --reachable' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit-graph write --reachable &&
-       test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "generation_data extra_edges"
+       git -C full commit-graph write --reachable &&
+       test_path_is_file full/$objdir/info/commit-graph &&
+       graph_read_expect -C full 11 "generation_data extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'setup bare repo' '
-       cd "$TRASH_DIRECTORY" &&
-       git clone --bare --no-local full bare &&
-       cd bare &&
-       git config core.commitGraph true &&
-       baredir="./objects"
+       git clone --bare --no-local full bare
 '
 
 graph_git_behavior 'bare repo, commit 8 vs merge 1' bare commits/8 merge/1
 graph_git_behavior 'bare repo, commit 8 vs merge 2' bare commits/8 merge/2
 
 test_expect_success 'write graph in bare repo' '
-       cd "$TRASH_DIRECTORY/bare" &&
-       git commit-graph write &&
-       test_path_is_file $baredir/info/commit-graph &&
-       graph_read_expect "11" "generation_data extra_edges"
+       git -C bare commit-graph write &&
+       test_path_is_file bare/objects/info/commit-graph &&
+       graph_read_expect -C bare 11 "generation_data extra_edges"
 '
 
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 2' bare commits/8 merge/2
 
 test_expect_success 'perform fast-forward merge in full repo' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git checkout -b merge-5-to-8 commits/5 &&
-       git merge commits/8 &&
-       git show-ref -s merge-5-to-8 >output &&
-       git show-ref -s commits/8 >expect &&
+       git -C full checkout -b merge-5-to-8 commits/5 &&
+       git -C full merge commits/8 &&
+       git -C full show-ref -s merge-5-to-8 >output &&
+       git -C full show-ref -s commits/8 >expect &&
        test_cmp expect output
 '
 
 test_expect_success 'check that gc computes commit-graph' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git commit --allow-empty -m "blank" &&
-       git commit-graph write --reachable &&
-       cp $objdir/info/commit-graph commit-graph-before-gc &&
-       git reset --hard HEAD~1 &&
-       git config gc.writeCommitGraph true &&
-       git gc &&
-       cp $objdir/info/commit-graph commit-graph-after-gc &&
+       test_commit -C full --no-tag blank &&
+       git -C full commit-graph write --reachable &&
+       cp full/$objdir/info/commit-graph commit-graph-before-gc &&
+       git -C full reset --hard HEAD~1 &&
+       test_config -C full gc.writeCommitGraph true &&
+       git -C full gc &&
+       cp full/$objdir/info/commit-graph commit-graph-after-gc &&
        ! test_cmp_bin commit-graph-before-gc commit-graph-after-gc &&
-       git commit-graph write --reachable &&
-       test_cmp_bin commit-graph-after-gc $objdir/info/commit-graph
+       git -C full commit-graph write --reachable &&
+       test_cmp_bin commit-graph-after-gc full/$objdir/info/commit-graph
 '
 
 test_expect_success 'replace-objects invalidates commit-graph' '
-       cd "$TRASH_DIRECTORY" &&
        test_when_finished rm -rf replace &&
        git clone full replace &&
        (
@@ -359,7 +329,6 @@ test_expect_success 'replace-objects invalidates commit-graph' '
 '
 
 test_expect_success 'commit grafts invalidate commit-graph' '
-       cd "$TRASH_DIRECTORY" &&
        test_when_finished rm -rf graft &&
        git clone --template= full graft &&
        (
@@ -384,7 +353,6 @@ test_expect_success 'commit grafts invalidate commit-graph' '
 '
 
 test_expect_success 'replace-objects invalidates commit-graph' '
-       cd "$TRASH_DIRECTORY" &&
        test_when_finished rm -rf shallow &&
        git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
        (
@@ -416,35 +384,36 @@ test_expect_success 'warn on improper hash version' '
                cd sha1 &&
                mv ../cg-sha256 .git/objects/info/commit-graph &&
                git log -1 2>err &&
-               test_i18ngrep "commit-graph hash version 2 does not match version 1" err
+               test_grep "commit-graph hash version 2 does not match version 1" err
        ) &&
        (
                cd sha256 &&
                mv ../cg-sha1 .git/objects/info/commit-graph &&
                git log -1 2>err &&
-               test_i18ngrep "commit-graph hash version 1 does not match version 2" err
+               test_grep "commit-graph hash version 1 does not match version 2" err
        )
 '
 
 test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow chunk' '
-       cd "$TRASH_DIRECTORY/full" &&
        UNIX_EPOCH_ZERO="@0 +0000" &&
        FUTURE_DATE="@4147483646 +0000" &&
-       rm -f .git/objects/info/commit-graph &&
-       test_commit --date "$FUTURE_DATE" future-1 &&
-       test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
-       git commit-graph write --reachable &&
-       test_commit --date "$FUTURE_DATE" future-2 &&
-       test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
-       git commit-graph write --reachable --split=no-merge &&
-       test_commit extra &&
-       git commit-graph write --reachable --split=no-merge &&
-       git commit-graph write --reachable &&
-       graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
-       mv .git/objects/info/commit-graph commit-graph-upgraded &&
-       git commit-graph write --reachable &&
-       graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
-       test_cmp .git/objects/info/commit-graph commit-graph-upgraded
+       rm -f full/.git/objects/info/commit-graph &&
+       test_commit -C full --date "$FUTURE_DATE" future-1 &&
+       test_commit -C full --date "$UNIX_EPOCH_ZERO" old-1 &&
+       git -C full commit-graph write --reachable &&
+       test_commit -C full --date "$FUTURE_DATE" future-2 &&
+       test_commit -C full --date "$UNIX_EPOCH_ZERO" old-2 &&
+       git -C full commit-graph write --reachable --split=no-merge &&
+       test_commit -C full extra &&
+       git -C full commit-graph write --reachable --split=no-merge &&
+       git -C full commit-graph write --reachable &&
+       graph_read_expect -C full 16 \
+               "generation_data generation_data_overflow extra_edges" &&
+       mv full/.git/objects/info/commit-graph commit-graph-upgraded &&
+       git -C full commit-graph write --reachable &&
+       graph_read_expect -C full 16 \
+               "generation_data generation_data_overflow extra_edges" &&
+       test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded
 '
 
 # the verify tests below expect the commit-graph to contain
@@ -454,10 +423,11 @@ test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow ch
 # and the tests will likely break.
 
 test_expect_success 'git commit-graph verify' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git rev-parse commits/8 | git -c commitGraph.generationVersion=1 commit-graph write --stdin-commits &&
-       git commit-graph verify >output &&
-       graph_read_expect 9 extra_edges 1
+       git -C full rev-parse commits/8 >in &&
+       git -C full -c commitGraph.generationVersion=1 commit-graph write \
+               --stdin-commits <in &&
+       git -C full commit-graph verify >output &&
+       graph_read_expect -C full 9 extra_edges 1
 '
 
 NUM_COMMITS=9
@@ -481,39 +451,39 @@ GRAPH_BYTE_FANOUT2=$(($GRAPH_FANOUT_OFFSET + 4 * 255))
 GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256))
 GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8))
 GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10))
+GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
 GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS))
 GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET
 GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN))
 GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4))
 GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3))
 GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11))
+GRAPH_BYTE_COMMIT_GENERATION_LAST=$(($GRAPH_BYTE_COMMIT_GENERATION + $(($NUM_COMMITS - 1)) * $GRAPH_COMMIT_DATA_WIDTH))
 GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12))
-GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
 GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
                             $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS))
 GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
 GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
 
 corrupt_graph_setup() {
-       cd "$TRASH_DIRECTORY/full" &&
-       test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
-       cp $objdir/info/commit-graph commit-graph-backup &&
-       chmod u+w $objdir/info/commit-graph
+       test_when_finished mv commit-graph-backup full/$objdir/info/commit-graph &&
+       cp full/$objdir/info/commit-graph commit-graph-backup &&
+       chmod u+w full/$objdir/info/commit-graph
 }
 
 corrupt_graph_verify() {
        grepstr=$1
-       test_must_fail git commit-graph verify 2>test_err &&
+       test_must_fail git -C full commit-graph verify 2>test_err &&
        grep -v "^+" test_err >err &&
-       test_i18ngrep "$grepstr" err &&
+       test_grep "$grepstr" err &&
        if test "$2" != "no-copy"
        then
-               cp $objdir/info/commit-graph commit-graph-pre-write-test
+               cp full/$objdir/info/commit-graph commit-graph-pre-write-test
        fi &&
-       git status --short &&
-       GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git commit-graph write &&
-       chmod u+w $objdir/info/commit-graph &&
-       git commit-graph verify
+       git -C full status --short &&
+       GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git -C full commit-graph write &&
+       chmod u+w full/$objdir/info/commit-graph &&
+       git -C full commit-graph verify
 }
 
 # usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
@@ -527,24 +497,24 @@ corrupt_graph_and_verify() {
        data="${2:-\0}"
        grepstr=$3
        corrupt_graph_setup &&
-       orig_size=$(wc -c < $objdir/info/commit-graph) &&
+       orig_size=$(wc -c <full/$objdir/info/commit-graph) &&
        zero_pos=${4:-${orig_size}} &&
-       printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
-       dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
-       test-tool genzeros $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
+       printf "$data" | dd of="full/$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
+       dd of="full/$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
+       test-tool genzeros $(($orig_size - $zero_pos)) >>"full/$objdir/info/commit-graph" &&
        corrupt_graph_verify "$grepstr"
 
 }
 
 test_expect_success POSIXPERM,SANITY 'detect permission problem' '
        corrupt_graph_setup &&
-       chmod 000 $objdir/info/commit-graph &&
+       chmod 000 full/$objdir/info/commit-graph &&
        corrupt_graph_verify "Could not open" "no-copy"
 '
 
 test_expect_success 'detect too small' '
        corrupt_graph_setup &&
-       echo "a small graph" >$objdir/info/commit-graph &&
+       echo "a small graph" >full/$objdir/info/commit-graph &&
        corrupt_graph_verify "too small"
 '
 
@@ -570,17 +540,17 @@ test_expect_success 'detect low chunk count' '
 
 test_expect_success 'detect missing OID fanout chunk' '
        corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
-               "missing the OID Fanout chunk"
+               "commit-graph required OID fanout chunk missing or corrupted"
 '
 
 test_expect_success 'detect missing OID lookup chunk' '
        corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
-               "missing the OID Lookup chunk"
+               "commit-graph required OID lookup chunk missing or corrupted"
 '
 
 test_expect_success 'detect missing commit data chunk' '
        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
-               "missing the Commit Data chunk"
+               "commit-graph required commit data chunk missing or corrupted"
 '
 
 test_expect_success 'detect incorrect fanout' '
@@ -590,7 +560,7 @@ test_expect_success 'detect incorrect fanout' '
 
 test_expect_success 'detect incorrect fanout final value' '
        corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
-               "fanout value"
+               "OID lookup chunk is the wrong size"
 '
 
 test_expect_success 'detect incorrect OID order' '
@@ -628,11 +598,6 @@ test_expect_success 'detect incorrect generation number' '
                "generation for commit"
 '
 
-test_expect_success 'detect incorrect generation number' '
-       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \
-               "non-zero generation number"
-'
-
 test_expect_success 'detect incorrect commit date' '
        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \
                "commit date"
@@ -654,34 +619,51 @@ test_expect_success 'detect incorrect chunk count' '
                $GRAPH_CHUNK_LOOKUP_OFFSET
 '
 
+test_expect_success 'detect mixed generation numbers (non-zero to zero)' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION_LAST "\0\0\0\0" \
+               "both zero and non-zero generations"
+'
+
+test_expect_success 'detect mixed generation numbers (zero to non-zero)' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\0\0\0\0" \
+               "both zero and non-zero generations"
+'
+
 test_expect_success 'git fsck (checks commit-graph when config set to true)' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git fsck &&
+       git -C full fsck &&
        corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
                "incorrect checksum" &&
-       cp commit-graph-pre-write-test $objdir/info/commit-graph &&
-       test_must_fail git -c core.commitGraph=true fsck
+       cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+       test_must_fail git -C full -c core.commitGraph=true fsck
 '
 
 test_expect_success 'git fsck (ignores commit-graph when config set to false)' '
-       cd "$TRASH_DIRECTORY/full" &&
-       git fsck &&
+       git -C full fsck &&
        corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
                "incorrect checksum" &&
-       cp commit-graph-pre-write-test $objdir/info/commit-graph &&
-       git -c core.commitGraph=false fsck
+       cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+       git -C full -c core.commitGraph=false fsck
 '
 
 test_expect_success 'git fsck (checks commit-graph when config unset)' '
-       cd "$TRASH_DIRECTORY/full" &&
-       test_when_finished "git config core.commitGraph true" &&
+       test_when_finished "git -C full config core.commitGraph true" &&
 
-       git fsck &&
+       git -C full fsck &&
        corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
                "incorrect checksum" &&
-       test_unconfig core.commitGraph &&
-       cp commit-graph-pre-write-test $objdir/info/commit-graph &&
-       test_must_fail git fsck
+       test_unconfig -C full core.commitGraph &&
+       cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+       test_must_fail git -C full fsck
+'
+
+test_expect_success 'git fsck shows commit-graph output with --progress' '
+       git -C "$TRASH_DIRECTORY/full" fsck --progress 2>err &&
+       grep "Verifying commits in commit graph" err
+'
+
+test_expect_success 'git fsck suppresses commit-graph output with --no-progress' '
+       git -C "$TRASH_DIRECTORY/full" fsck --no-progress 2>err &&
+       ! grep "Verifying commits in commit graph" err
 '
 
 test_expect_success 'setup non-the_repository tests' '
@@ -739,7 +721,7 @@ test_expect_success 'corrupt commit-graph write (broken parent)' '
                git commit-tree -p "$broken" -m "good commit" "$empty" >good &&
                test_must_fail git commit-graph write --stdin-commits \
                        <good 2>test_err &&
-               test_i18ngrep "unable to parse commit" test_err
+               test_grep "unable to parse commit" test_err
        )
 '
 
@@ -760,7 +742,7 @@ test_expect_success 'corrupt commit-graph write (missing tree)' '
                git commit-tree -p "$broken" -m "good" "$tree" >good &&
                test_must_fail git commit-graph write --stdin-commits \
                        <good 2>test_err &&
-               test_i18ngrep "unable to parse commit" test_err
+               test_grep "unable to parse commit" test_err
        )
 '
 
@@ -782,32 +764,33 @@ test_expect_success 'corrupt commit-graph write (missing tree)' '
 #
 
 test_expect_success 'set up and verify repo with generation data overflow chunk' '
-       objdir=".git/objects" &&
        UNIX_EPOCH_ZERO="@0 +0000" &&
        FUTURE_DATE="@2147483646 +0000" &&
-       cd "$TRASH_DIRECTORY" &&
-       mkdir repo &&
-       cd repo &&
-       git init &&
-       test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
-       test_commit 2 &&
-       test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
-       git commit-graph write --reachable &&
-       graph_read_expect 3 generation_data &&
-       test_commit --date "$FUTURE_DATE" 4 &&
-       test_commit 5 &&
-       test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
-       git branch left &&
-       git reset --hard 3 &&
-       test_commit 7 &&
-       test_commit --date "$FUTURE_DATE" 8 &&
-       test_commit 9 &&
-       git branch right &&
-       git reset --hard 3 &&
-       test_merge M left right &&
-       git commit-graph write --reachable &&
-       graph_read_expect 10 "generation_data generation_data_overflow" &&
-       git commit-graph verify
+
+       git init repo &&
+       (
+               cd repo &&
+
+               test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+               test_commit 2 &&
+               test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+               git commit-graph write --reachable &&
+               graph_read_expect 3 generation_data &&
+               test_commit --date "$FUTURE_DATE" 4 &&
+               test_commit 5 &&
+               test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+               git branch left &&
+               git reset --hard 3 &&
+               test_commit 7 &&
+               test_commit --date "$FUTURE_DATE" 8 &&
+               test_commit 9 &&
+               git branch right &&
+               git reset --hard 3 &&
+               test_merge M left right &&
+               git commit-graph write --reachable &&
+               graph_read_expect 10 "generation_data generation_data_overflow" &&
+               git commit-graph verify
+       )
 '
 
 graph_git_behavior 'generation data overflow chunk repo' repo left right
@@ -839,4 +822,127 @@ test_expect_success 'overflow during generation version upgrade' '
        )
 '
 
+corrupt_chunk () {
+       graph=full/.git/objects/info/commit-graph &&
+       test_when_finished "rm -rf $graph" &&
+       git -C full commit-graph write --reachable &&
+       corrupt_chunk_file $graph "$@"
+}
+
+check_corrupt_chunk () {
+       corrupt_chunk "$@" &&
+       git -C full -c core.commitGraph=false log >expect.out &&
+       git -C full -c core.commitGraph=true log >out 2>err &&
+       test_cmp expect.out out
+}
+
+test_expect_success 'reader notices too-small oid fanout chunk' '
+       # make it big enough that the graph file is plausible,
+       # otherwise we hit an earlier check
+       check_corrupt_chunk OIDF clear $(printf "000000%02x" $(test_seq 250)) &&
+       cat >expect.err <<-\EOF &&
+       error: commit-graph oid fanout chunk is wrong size
+       error: commit-graph required OID fanout chunk missing or corrupted
+       EOF
+       test_cmp expect.err err
+'
+
+test_expect_success 'reader notices fanout/lookup table mismatch' '
+       check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
+       cat >expect.err <<-\EOF &&
+       error: commit-graph OID lookup chunk is the wrong size
+       error: commit-graph required OID lookup chunk missing or corrupted
+       EOF
+       test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds fanout' '
+       # Rather than try to corrupt a specific hash, we will just
+       # wreck them all. But we cannot just set them all to 0xFFFFFFFF or
+       # similar, as they are used for hi/lo starts in a binary search (so if
+       # they are identical, that indicates that the search should abort
+       # immediately). Instead, we will give them high values that differ by
+       # 2^24, ensuring that any that are used would cause an out-of-bounds
+       # read.
+       check_corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
+       cat >expect.err <<-\EOF &&
+       error: commit-graph fanout values out of order
+       error: commit-graph required OID fanout chunk missing or corrupted
+       EOF
+       test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small commit data chunk' '
+       check_corrupt_chunk CDAT clear 00000000 &&
+       cat >expect.err <<-\EOF &&
+       error: commit-graph commit data chunk is wrong size
+       error: commit-graph required commit data chunk missing or corrupted
+       EOF
+       test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds extra edge' '
+       check_corrupt_chunk EDGE clear &&
+       cat >expect.err <<-\EOF &&
+       error: commit-graph extra-edges pointer out of bounds
+       EOF
+       test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small generations chunk' '
+       check_corrupt_chunk GDA2 clear 00000000 &&
+       cat >expect.err <<-\EOF &&
+       error: commit-graph generations chunk is wrong size
+       EOF
+       test_cmp expect.err err
+'
+
+test_expect_success 'stale commit cannot be parsed when given directly' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               test_commit A &&
+               test_commit B &&
+               git commit-graph write --reachable &&
+
+               oid=$(git rev-parse B) &&
+               rm .git/objects/"$(test_oid_to_path "$oid")" &&
+
+               # Verify that it is possible to read the commit from the
+               # commit graph when not being paranoid, ...
+               git rev-list B &&
+               # ... but parsing the commit when double checking that
+               # it actually exists in the object database should fail.
+               test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-list -1 B
+       )
+'
+
+test_expect_success 'stale commit cannot be parsed when traversing graph' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               test_commit A &&
+               test_commit B &&
+               test_commit C &&
+               git commit-graph write --reachable &&
+
+               # Corrupt the repository by deleting the intermediate commit
+               # object. Commands should notice that this object is absent and
+               # thus that the repository is corrupt even if the commit graph
+               # exists.
+               oid=$(git rev-parse B) &&
+               rm .git/objects/"$(test_oid_to_path "$oid")" &&
+
+               # Again, we should be able to parse the commit when not
+               # being paranoid about commit graph staleness...
+               git rev-parse HEAD~2 &&
+               # ... but fail when we are paranoid.
+               test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-parse HEAD~2 2>error &&
+               grep "error: commit $oid exists in commit-graph but not in the object database" error
+       )
+'
+
 test_done
index b5f9b1092229d3d8c1879d41c5c3f02f4ad04923..dd09134db03638fd01e73d89acbac45cceaa6e46 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='multi-pack-indexes'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
 
 GIT_TEST_MULTI_PACK_INDEX=0
 objdir=.git/objects
@@ -183,6 +184,18 @@ test_expect_success 'write midx with --stdin-packs' '
 
 compare_results_with_midx "mixed mode (one pack + extra)"
 
+test_expect_success 'write with no objects and preferred pack' '
+       test_when_finished "rm -rf empty" &&
+       git init empty &&
+       test_must_fail git -C empty multi-pack-index write \
+               --stdin-packs --preferred-pack=does-not-exist </dev/null 2>err &&
+       cat >expect <<-EOF &&
+       warning: unknown preferred pack: ${SQ}does-not-exist${SQ}
+       error: no pack files to index.
+       EOF
+       test_cmp expect err
+'
+
 test_expect_success 'write progress off for redirected stderr' '
        git multi-pack-index --object-dir=$objdir write 2>err &&
        test_line_count = 0 err
@@ -267,13 +280,13 @@ test_expect_success 'warn on improper hash version' '
                cd sha1 &&
                mv ../mpi-sha256 .git/objects/pack/multi-pack-index &&
                git log -1 2>err &&
-               test_i18ngrep "multi-pack-index hash version 2 does not match version 1" err
+               test_grep "multi-pack-index hash version 2 does not match version 1" err
        ) &&
        (
                cd sha256 &&
                mv ../mpi-sha1 .git/objects/pack/multi-pack-index &&
                git log -1 2>err &&
-               test_i18ngrep "multi-pack-index hash version 1 does not match version 2" err
+               test_grep "multi-pack-index hash version 1 does not match version 2" err
        )
 '
 
@@ -374,7 +387,7 @@ corrupt_midx_and_verify() {
        printf "$DATA" | dd of="$FILE" bs=1 seek="$POS" conv=notrunc &&
        test_must_fail $COMMAND 2>test_err &&
        grep -v "^+" test_err >err &&
-       test_i18ngrep "$GREPSTR" err
+       test_grep "$GREPSTR" err
 }
 
 test_expect_success 'verify bad signature' '
@@ -426,7 +439,7 @@ test_expect_success 'verify extended chunk count' '
 
 test_expect_success 'verify missing required chunk' '
        corrupt_midx_and_verify $MIDX_BYTE_CHUNK_ID "\01" $objdir \
-               "missing required"
+               "required pack-name chunk missing"
 '
 
 test_expect_success 'verify invalid chunk offset' '
@@ -473,11 +486,23 @@ test_expect_success 'git-fsck incorrect offset' '
        git -c core.multiPackIndex=false fsck
 '
 
+test_expect_success 'git fsck shows MIDX output with --progress' '
+       git fsck --progress 2>err &&
+       grep "Verifying OID order in multi-pack-index" err &&
+       grep "Verifying object offsets" err
+'
+
+test_expect_success 'git fsck suppresses MIDX output with --no-progress' '
+       git fsck --no-progress 2>err &&
+       ! grep "Verifying OID order in multi-pack-index" err &&
+       ! grep "Verifying object offsets" err
+'
+
 test_expect_success 'corrupt MIDX is not reused' '
        corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \
                "incorrect object offset" &&
        git multi-pack-index write 2>err &&
-       test_i18ngrep checksum.mismatch err &&
+       test_grep checksum.mismatch err &&
        git multi-pack-index verify
 '
 
@@ -1007,7 +1032,7 @@ test_expect_success 'load reverse index when missing .idx, .pack' '
 
 test_expect_success 'usage shown without sub-command' '
        test_expect_code 129 git multi-pack-index 2>err &&
-       ! test_i18ngrep "unrecognized subcommand" err
+       ! test_grep "unrecognized subcommand" err
 '
 
 test_expect_success 'complains when run outside of a repository' '
@@ -1015,4 +1040,170 @@ test_expect_success 'complains when run outside of a repository' '
        grep "not a git repository" err
 '
 
+test_expect_success 'repack with delta islands' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit first &&
+               git repack &&
+               test_commit second &&
+               git repack &&
+
+               git multi-pack-index write &&
+               git -c repack.useDeltaIslands=true multi-pack-index repack
+       )
+'
+
+corrupt_chunk () {
+       midx=.git/objects/pack/multi-pack-index &&
+       test_when_finished "rm -rf $midx" &&
+       git repack -ad --write-midx &&
+       corrupt_chunk_file $midx "$@"
+}
+
+test_expect_success 'reader notices too-small oid fanout chunk' '
+       corrupt_chunk OIDF clear 00000000 &&
+       test_must_fail git log 2>err &&
+       cat >expect <<-\EOF &&
+       error: multi-pack-index OID fanout is of the wrong size
+       fatal: multi-pack-index required OID fanout chunk missing or corrupted
+       EOF
+       test_cmp expect err
+'
+
+test_expect_success 'reader notices too-small oid lookup chunk' '
+       corrupt_chunk OIDL clear 00000000 &&
+       test_must_fail git log 2>err &&
+       cat >expect <<-\EOF &&
+       error: multi-pack-index OID lookup chunk is the wrong size
+       fatal: multi-pack-index required OID lookup chunk missing or corrupted
+       EOF
+       test_cmp expect err
+'
+
+test_expect_success 'reader notices too-small pack names chunk' '
+       # There is no NUL to terminate the name here, so the
+       # chunk is too short.
+       corrupt_chunk PNAM clear 70656666 &&
+       test_must_fail git log 2>err &&
+       cat >expect <<-\EOF &&
+       fatal: multi-pack-index pack-name chunk is too short
+       EOF
+       test_cmp expect err
+'
+
+test_expect_success 'reader handles unaligned chunks' '
+       # A 9-byte PNAM means all of the subsequent chunks
+       # will no longer be 4-byte aligned, but it is still
+       # a valid one-pack chunk on its own (it is "foo.pack\0").
+       corrupt_chunk PNAM clear 666f6f2e7061636b00 &&
+       git -c core.multipackindex=false log >expect.out &&
+       git -c core.multipackindex=true log >out 2>err &&
+       test_cmp expect.out out &&
+       cat >expect.err <<-\EOF &&
+       error: chunk id 4f494446 not 4-byte aligned
+       EOF
+       test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small object offset chunk' '
+       corrupt_chunk OOFF clear 00000000 &&
+       test_must_fail git log 2>err &&
+       cat >expect <<-\EOF &&
+       error: multi-pack-index object offset chunk is the wrong size
+       fatal: multi-pack-index required object offsets chunk missing or corrupted
+       EOF
+       test_cmp expect err
+'
+
+test_expect_success 'reader bounds-checks large offset table' '
+       # re-use the objects64 dir here to cheaply get access to a midx
+       # with large offsets.
+       git init repo &&
+       test_when_finished "rm -rf repo" &&
+       (
+               cd repo &&
+               (cd ../objects64 && pwd) >.git/objects/info/alternates &&
+               git multi-pack-index --object-dir=../objects64 write &&
+               midx=../objects64/pack/multi-pack-index &&
+               corrupt_chunk_file $midx LOFF clear &&
+               # using only %(objectsize) is important here; see the commit
+               # message for more details
+               test_must_fail git cat-file --batch-all-objects \
+                       --batch-check="%(objectsize)" 2>err &&
+               cat >expect <<-\EOF &&
+               fatal: multi-pack-index large offset out of bounds
+               EOF
+               test_cmp expect err
+       )
+'
+
+test_expect_success 'reader notices too-small revindex chunk' '
+       # We only get a revindex with bitmaps (and likewise only
+       # load it when they are asked for).
+       test_config repack.writeBitmaps true &&
+       corrupt_chunk RIDX clear 00000000 &&
+       git -c core.multipackIndex=false rev-list \
+               --all --use-bitmap-index >expect.out &&
+       git -c core.multipackIndex=true rev-list \
+               --all --use-bitmap-index >out 2>err &&
+       test_cmp expect.out out &&
+       cat >expect.err <<-\EOF &&
+       error: multi-pack-index reverse-index chunk is the wrong size
+       warning: multi-pack bitmap is missing required reverse index
+       EOF
+       test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds fanout' '
+       # This is similar to the out-of-bounds fanout test in t5318. The values
+       # in adjacent entries should be large but not identical (they
+       # are used as hi/lo starts for a binary search, which would then abort
+       # immediately).
+       corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
+       test_must_fail git log 2>err &&
+       cat >expect <<-\EOF &&
+       error: oid fanout out of order: fanout[254] = fe000000 > 5c = fanout[255]
+       fatal: multi-pack-index required OID fanout chunk missing or corrupted
+       EOF
+       test_cmp expect err
+'
+
+test_expect_success 'bitmapped packs are stored via the BTMP chunk' '
+       test_when_finished "rm -fr repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               for i in 1 2 3 4 5
+               do
+                       test_commit "$i" &&
+                       git repack -d || return 1
+               done &&
+
+               find $objdir/pack -type f -name "*.idx" | xargs -n 1 basename |
+               sort >packs &&
+
+               git multi-pack-index write --stdin-packs <packs &&
+               test_must_fail test-tool read-midx --bitmap $objdir 2>err &&
+               cat >expect <<-\EOF &&
+               error: MIDX does not contain the BTMP chunk
+               EOF
+               test_cmp expect err &&
+
+               git multi-pack-index write --stdin-packs --bitmap \
+                       --preferred-pack="$(head -n1 <packs)" <packs  &&
+               test-tool read-midx --bitmap $objdir >actual &&
+               for i in $(test_seq $(wc -l <packs))
+               do
+                       sed -ne "${i}s/\.idx$/\.pack/p" packs &&
+                       echo "  bitmap_pos: $((($i - 1) * 3))" &&
+                       echo "  bitmap_nr: 3" || return 1
+               done >expect &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index 124d47603df4ae71a9a1eeee9059752855f6860b..406363381f10bcc6ee4f8040afadcc362d85fc29 100755 (executable)
@@ -134,7 +134,7 @@ test_expect_success 'island core places core objects first' '
            repack -adfi &&
        git verify-pack -v .git/objects/pack/*.pack |
        cut -d" " -f1 |
-       egrep "$root|$two" >actual &&
+       grep -E "$root|$two" >actual &&
        test_cmp expect actual
 '
 
index 669ddc645faccfc402b04044ed75676592e64356..281266f7883b35cbc4dd2c631d645d0d5b430d0c 100755 (executable)
@@ -1,7 +1,10 @@
 #!/bin/sh
 
 test_description='split commit graph'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
 
 GIT_TEST_COMMIT_GRAPH=0
 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
@@ -281,7 +284,33 @@ test_expect_success 'verify hashes along chain, even in shallow' '
                corrupt_file "$base_file" $(test_oid shallow) "\01" &&
                test_must_fail git commit-graph verify --shallow 2>test_err &&
                grep -v "^+" test_err >err &&
-               test_i18ngrep "incorrect checksum" err
+               test_grep "incorrect checksum" err
+       )
+'
+
+test_expect_success 'verify notices chain slice which is bogus (base)' '
+       git clone --no-hardlinks . verify-chain-bogus-base &&
+       (
+               cd verify-chain-bogus-base &&
+               git commit-graph verify &&
+               base_file=$graphdir/graph-$(sed -n 1p $graphdir/commit-graph-chain).graph &&
+               echo "garbage" >$base_file &&
+               test_must_fail git commit-graph verify 2>test_err &&
+               grep -v "^+" test_err >err &&
+               grep "commit-graph file is too small" err
+       )
+'
+
+test_expect_success 'verify notices chain slice which is bogus (tip)' '
+       git clone --no-hardlinks . verify-chain-bogus-tip &&
+       (
+               cd verify-chain-bogus-tip &&
+               git commit-graph verify &&
+               tip_file=$graphdir/graph-$(sed -n 2p $graphdir/commit-graph-chain).graph &&
+               echo "garbage" >$tip_file &&
+               test_must_fail git commit-graph verify 2>test_err &&
+               grep -v "^+" test_err >err &&
+               grep "commit-graph file is too small" err
        )
 '
 
@@ -291,11 +320,11 @@ test_expect_success 'verify --shallow does not check base contents' '
                cd verify-shallow &&
                git commit-graph verify &&
                base_file=$graphdir/graph-$(head -n 1 $graphdir/commit-graph-chain).graph &&
-               corrupt_file "$base_file" 1000 "\01" &&
+               corrupt_file "$base_file" 1500 "\01" &&
                git commit-graph verify --shallow &&
                test_must_fail git commit-graph verify 2>test_err &&
                grep -v "^+" test_err >err &&
-               test_i18ngrep "incorrect checksum" err
+               test_grep "incorrect checksum" err
        )
 '
 
@@ -306,24 +335,51 @@ test_expect_success 'warn on base graph chunk incorrect' '
                git commit-graph verify &&
                base_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
                corrupt_file "$base_file" $(test_oid base) "\01" &&
-               git commit-graph verify --shallow 2>test_err &&
+               test_must_fail git commit-graph verify --shallow 2>test_err &&
+               grep -v "^+" test_err >err &&
+               test_grep "commit-graph chain does not match" err
+       )
+'
+
+test_expect_success 'verify after commit-graph-chain corruption (base)' '
+       git clone --no-hardlinks . verify-chain-base &&
+       (
+               cd verify-chain-base &&
+               corrupt_file "$graphdir/commit-graph-chain" 30 "G" &&
+               test_must_fail git commit-graph verify 2>test_err &&
+               grep -v "^+" test_err >err &&
+               test_grep "invalid commit-graph chain" err &&
+               corrupt_file "$graphdir/commit-graph-chain" 30 "A" &&
+               test_must_fail git commit-graph verify 2>test_err &&
                grep -v "^+" test_err >err &&
-               test_i18ngrep "commit-graph chain does not match" err
+               test_grep "unable to find all commit-graph files" err
        )
 '
 
-test_expect_success 'verify after commit-graph-chain corruption' '
-       git clone --no-hardlinks . verify-chain &&
+test_expect_success 'verify after commit-graph-chain corruption (tip)' '
+       git clone --no-hardlinks . verify-chain-tip &&
        (
-               cd verify-chain &&
-               corrupt_file "$graphdir/commit-graph-chain" 60 "G" &&
-               git commit-graph verify 2>test_err &&
+               cd verify-chain-tip &&
+               corrupt_file "$graphdir/commit-graph-chain" 70 "G" &&
+               test_must_fail git commit-graph verify 2>test_err &&
                grep -v "^+" test_err >err &&
-               test_i18ngrep "invalid commit-graph chain" err &&
-               corrupt_file "$graphdir/commit-graph-chain" 60 "A" &&
-               git commit-graph verify 2>test_err &&
+               test_grep "invalid commit-graph chain" err &&
+               corrupt_file "$graphdir/commit-graph-chain" 70 "A" &&
+               test_must_fail git commit-graph verify 2>test_err &&
                grep -v "^+" test_err >err &&
-               test_i18ngrep "unable to find all commit-graph files" err
+               test_grep "unable to find all commit-graph files" err
+       )
+'
+
+test_expect_success 'verify notices too-short chain file' '
+       git clone --no-hardlinks . verify-chain-short &&
+       (
+               cd verify-chain-short &&
+               git commit-graph verify &&
+               echo "garbage" >$graphdir/commit-graph-chain &&
+               test_must_fail git commit-graph verify 2>test_err &&
+               grep -v "^+" test_err >err &&
+               grep "commit-graph chain file too small" err
        )
 '
 
@@ -338,10 +394,23 @@ test_expect_success 'verify across alternates' '
                test_commit extra &&
                git commit-graph write --reachable --split &&
                tip_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
-               corrupt_file "$tip_file" 100 "\01" &&
+               corrupt_file "$tip_file" 1500 "\01" &&
                test_must_fail git commit-graph verify --shallow 2>test_err &&
                grep -v "^+" test_err >err &&
-               test_i18ngrep "commit-graph has incorrect fanout value" err
+               test_grep "incorrect checksum" err
+       )
+'
+
+test_expect_success 'reader bounds-checks base-graph chunk' '
+       git clone --no-hardlinks . corrupt-base-chunk &&
+       (
+               cd corrupt-base-chunk &&
+               tip_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
+               corrupt_chunk_file "$tip_file" BASE clear 01020304 &&
+               git -c core.commitGraph=false log >expect.out &&
+               git -c core.commitGraph=true log >out 2>err &&
+               test_cmp expect.out out &&
+               grep "commit-graph base graphs chunk is too small" err
        )
 '
 
@@ -351,8 +420,9 @@ test_expect_success 'add octopus merge' '
        git branch merge/octopus &&
        git commit-graph write --reachable --split &&
        git commit-graph verify --progress 2>err &&
-       test_line_count = 3 err &&
-       test_i18ngrep ! warning err &&
+       test_line_count = 1 err &&
+       grep "Verifying commits in commit graph: 100% (18/18)" err &&
+       test_grep ! warning err &&
        test_line_count = 3 $graphdir/commit-graph-chain
 '
 
@@ -454,7 +524,7 @@ test_expect_success 'prevent regression for duplicate commits across layers' '
        git init dup &&
        git -C dup commit --allow-empty -m one &&
        git -C dup -c core.commitGraph=false commit-graph write --split=no-merge --reachable 2>err &&
-       test_i18ngrep "attempting to write a commit-graph" err &&
+       test_grep "attempting to write a commit-graph" err &&
        git -C dup commit-graph write --split=no-merge --reachable &&
        git -C dup commit --allow-empty -m two &&
        git -C dup commit-graph write --split=no-merge --reachable &&
index d042d26f2b393d52049ba3c9972e2516b03ed239..431a603ca0e61d7085f28333c6a28c120ea45828 100755 (executable)
@@ -1,17 +1,20 @@
 #!/bin/sh
 
 test_description='on-disk reverse index'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # The below tests want control over the 'pack.writeReverseIndex' setting
 # themselves to assert various combinations of it with other options.
-sane_unset GIT_TEST_WRITE_REV_INDEX
+sane_unset GIT_TEST_NO_WRITE_REV_INDEX
 
 packdir=.git/objects/pack
 
 test_expect_success 'setup' '
        test_commit base &&
 
+       test_config pack.writeReverseIndex false &&
        pack=$(git pack-objects --all $packdir/pack) &&
        rev=$packdir/pack-$pack.rev &&
 
@@ -94,6 +97,17 @@ test_expect_success 'reverse index is not generated when available on disk' '
                --batch-check="%(objectsize:disk)" <tip
 '
 
+test_expect_success 'reverse index is ignored when pack.readReverseIndex is false' '
+       test_index_pack true &&
+       test_path_is_file $rev &&
+
+       test_config pack.readReverseIndex false &&
+
+       git rev-parse HEAD >tip &&
+       GIT_TEST_REV_INDEX_DIE_ON_DISK=1 git cat-file \
+               --batch-check="%(objectsize:disk)" <tip
+'
+
 test_expect_success 'revindex in-memory vs on-disk' '
        git init repo &&
        test_when_finished "rm -fr repo" &&
@@ -117,4 +131,78 @@ test_expect_success 'revindex in-memory vs on-disk' '
                test_cmp on-disk in-core
        )
 '
+
+test_expect_success 'fsck succeeds on good rev-index' '
+       test_when_finished rm -fr repo &&
+       git init repo &&
+       (
+               cd repo &&
+
+               test_commit commit &&
+               git -c pack.writeReverseIndex=true repack -ad &&
+               git fsck 2>err &&
+               test_must_be_empty err
+       )
+'
+
+test_expect_success 'set up rev-index corruption tests' '
+       git init corrupt &&
+       (
+               cd corrupt &&
+
+               test_commit commit &&
+               git -c pack.writeReverseIndex=true repack -ad &&
+
+               revfile=$(ls .git/objects/pack/pack-*.rev) &&
+               chmod a+w $revfile &&
+               cp $revfile $revfile.bak
+       )
+'
+
+corrupt_rev_and_verify () {
+       (
+               pos="$1" &&
+               value="$2" &&
+               error="$3" &&
+
+               cd corrupt &&
+               revfile=$(ls .git/objects/pack/pack-*.rev) &&
+
+               # Reset to original rev-file.
+               cp $revfile.bak $revfile &&
+
+               printf "$value" | dd of=$revfile bs=1 seek="$pos" conv=notrunc &&
+               test_must_fail git fsck 2>err &&
+               grep "$error" err
+       )
+}
+
+test_expect_success 'fsck catches invalid checksum' '
+       revfile=$(ls corrupt/.git/objects/pack/pack-*.rev) &&
+       orig_size=$(wc -c <$revfile) &&
+       hashpos=$((orig_size - 10)) &&
+       corrupt_rev_and_verify $hashpos bogus \
+               "invalid checksum"
+'
+
+test_expect_success 'fsck catches invalid row position' '
+       corrupt_rev_and_verify 14 "\07" \
+               "invalid rev-index position"
+'
+
+test_expect_success 'fsck catches invalid header: magic number' '
+       corrupt_rev_and_verify 1 "\07" \
+               "reverse-index file .* has unknown signature"
+'
+
+test_expect_success 'fsck catches invalid header: version' '
+       corrupt_rev_and_verify 7 "\02" \
+               "reverse-index file .* has unsupported version"
+'
+
+test_expect_success 'fsck catches invalid header: hash function' '
+       corrupt_rev_and_verify 11 "\03" \
+               "reverse-index file .* has unsupported hash id"
+'
+
 test_done
index ad6eea5fa0f20a4e9a72612eee4b3c22729a0da8..70d1b58709a91199bf6d589e141897ca96204547 100755 (executable)
@@ -410,4 +410,107 @@ test_expect_success 'preferred pack change with existing MIDX bitmap' '
        )
 '
 
+test_expect_success 'tagged commits are selected for bitmapping' '
+       rm -fr repo &&
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit --annotate base &&
+               git repack -d &&
+
+               # Remove refs/heads/main which points at the commit directly,
+               # leaving only a reference to the annotated tag.
+               git branch -M main &&
+               git checkout base &&
+               git branch -d main &&
+
+               git multi-pack-index write --bitmap &&
+
+               git rev-parse HEAD >want &&
+               test-tool bitmap list-commits >actual &&
+               grep $(cat want) actual
+       )
+'
+
+corrupt_file () {
+       chmod a+w "$1" &&
+       printf "bogus" | dd of="$1" bs=1 seek="12" conv=notrunc
+}
+
+test_expect_success 'git fsck correctly identifies good and bad bitmaps' '
+       git init valid &&
+       test_when_finished rm -rf valid &&
+
+       test_commit_bulk 20 &&
+       git repack -adbf &&
+
+       # Move pack-bitmap aside so it is not deleted
+       # in next repack.
+       packbitmap=$(ls .git/objects/pack/pack-*.bitmap) &&
+       mv "$packbitmap" "$packbitmap.bak" &&
+
+       test_commit_bulk 10 &&
+       git repack -b --write-midx &&
+       midxbitmap=$(ls .git/objects/pack/multi-pack-index-*.bitmap) &&
+
+       # Copy MIDX bitmap to backup. Copy pack bitmap from backup.
+       cp "$midxbitmap" "$midxbitmap.bak" &&
+       cp "$packbitmap.bak" "$packbitmap" &&
+
+       # fsck works at first
+       git fsck 2>err &&
+       test_must_be_empty err &&
+
+       corrupt_file "$packbitmap" &&
+       test_must_fail git fsck 2>err &&
+       grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err &&
+
+       cp "$packbitmap.bak" "$packbitmap" &&
+       corrupt_file "$midxbitmap" &&
+       test_must_fail git fsck 2>err &&
+       grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err &&
+
+       corrupt_file "$packbitmap" &&
+       test_must_fail git fsck 2>err &&
+       grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err &&
+       grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err
+'
+
+test_expect_success 'corrupt MIDX with bitmap causes fallback' '
+       git init corrupt-midx-bitmap &&
+       (
+               cd corrupt-midx-bitmap &&
+
+               test_commit first &&
+               git repack -d &&
+               test_commit second &&
+               git repack -d &&
+
+               git multi-pack-index write --bitmap &&
+               checksum=$(midx_checksum $objdir) &&
+               for f in $midx $midx-$checksum.bitmap
+               do
+                       mv $f $f.bak || return 1
+               done &&
+
+               # pack everything together, invalidating the MIDX
+               git repack -ad &&
+               # then restore the now-stale MIDX
+               for f in $midx $midx-$checksum.bitmap
+               do
+                       mv $f.bak $f || return 1
+               done &&
+
+               git rev-list --count --objects --use-bitmap-index HEAD >out 2>err &&
+               # should attempt opening the broken pack twice (once
+               # from the attempt to load it via the stale bitmap, and
+               # again when attempting to load it from the stale MIDX)
+               # before falling back to the non-MIDX case
+               test 2 -eq $(grep -c "could not open pack" err) &&
+               test 6 -eq $(cat out)
+       )
+'
+
 test_done
index 093f0c067af6801e3334152fe65b7c4495830d30..fc6a242b56d88686036ff019003352ee806ef8ce 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='commit graph with 64-bit timestamps'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if ! test_have_prereq TIME_IS_64BIT || ! test_have_prereq TIME_T_IS_64BIT
@@ -10,6 +12,7 @@ then
 fi
 
 . "$TEST_DIRECTORY"/lib-commit-graph.sh
+. "$TEST_DIRECTORY/lib-chunk.sh"
 
 UNIX_EPOCH_ZERO="@0 +0000"
 FUTURE_DATE="@4147483646 +0000"
@@ -37,30 +40,48 @@ test_expect_success 'lower layers have overflow chunk' '
 graph_git_behavior 'overflow' '' HEAD~2 HEAD
 
 test_expect_success 'set up and verify repo with generation data overflow chunk' '
-       mkdir repo &&
-       cd repo &&
-       git init &&
-       test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
-       test_commit 2 &&
-       test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
-       git commit-graph write --reachable &&
-       graph_read_expect 3 generation_data &&
-       test_commit --date "$FUTURE_DATE" 4 &&
-       test_commit 5 &&
-       test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
-       git branch left &&
-       git reset --hard 3 &&
-       test_commit 7 &&
-       test_commit --date "$FUTURE_DATE" 8 &&
-       test_commit 9 &&
-       git branch right &&
-       git reset --hard 3 &&
-       test_merge M left right &&
-       git commit-graph write --reachable &&
-       graph_read_expect 10 "generation_data generation_data_overflow" &&
-       git commit-graph verify
+       git init repo &&
+       (
+               cd repo &&
+               test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+               test_commit 2 &&
+               test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+               git commit-graph write --reachable &&
+               graph_read_expect 3 generation_data &&
+               test_commit --date "$FUTURE_DATE" 4 &&
+               test_commit 5 &&
+               test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+               git branch left &&
+               git reset --hard 3 &&
+               test_commit 7 &&
+               test_commit --date "$FUTURE_DATE" 8 &&
+               test_commit 9 &&
+               git branch right &&
+               git reset --hard 3 &&
+               test_merge M left right &&
+               git commit-graph write --reachable &&
+               graph_read_expect 10 "generation_data generation_data_overflow" &&
+               git commit-graph verify
+       )
 '
 
 graph_git_behavior 'overflow 2' repo left right
 
+test_expect_success 'single commit with generation data exceeding UINT32_MAX' '
+       git init repo-uint32-max &&
+       test_commit -C repo-uint32-max --date "@4294967297 +0000" 1 &&
+       git -C repo-uint32-max commit-graph write --reachable &&
+       graph_read_expect -C repo-uint32-max 1 "generation_data" &&
+       git -C repo-uint32-max commit-graph verify
+'
+
+test_expect_success 'reader notices out-of-bounds generation overflow' '
+       graph=.git/objects/info/commit-graph &&
+       test_when_finished "rm -rf $graph" &&
+       git commit-graph write --reachable &&
+       corrupt_chunk_file $graph GDO2 clear &&
+       test_must_fail git log 2>err &&
+       grep "commit-graph overflow generation data is too small" err
+'
+
 test_done
index 303f7a5d842d36edb191bb05ec61df9a6af8d7c9..fc5fedbe9b0c4dd5b59a429a4a877c422f46e602 100755 (executable)
@@ -573,23 +573,54 @@ test_expect_success 'cruft repack with no reachable objects' '
        )
 '
 
-test_expect_success 'cruft repack ignores --max-pack-size' '
+write_blob () {
+       test-tool genrandom "$@" >in &&
+       git hash-object -w -t blob in
+}
+
+find_pack () {
+       for idx in $(ls $packdir/pack-*.idx)
+       do
+               git show-index <$idx >out &&
+               if grep -q "$1" out
+               then
+                       echo $idx
+               fi || return 1
+       done
+}
+
+test_expect_success 'cruft repack with --max-pack-size' '
        git init max-pack-size &&
        (
                cd max-pack-size &&
                test_commit base &&
+
                # two cruft objects which exceed the maximum pack size
-               test-tool genrandom foo 1048576 | git hash-object --stdin -w &&
-               test-tool genrandom bar 1048576 | git hash-object --stdin -w &&
+               foo=$(write_blob foo 1048576) &&
+               bar=$(write_blob bar 1048576) &&
+               test-tool chmtime --get -1000 \
+                       "$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
+               test-tool chmtime --get -2000 \
+                       "$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
                git repack --cruft --max-pack-size=1M &&
                find $packdir -name "*.mtimes" >cruft &&
-               test_line_count = 1 cruft &&
-               test-tool pack-mtimes "$(basename "$(cat cruft)")" >objects &&
-               test_line_count = 2 objects
+               test_line_count = 2 cruft &&
+
+               foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
+               bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
+               test-tool pack-mtimes $foo_mtimes >foo.actual &&
+               test-tool pack-mtimes $bar_mtimes >bar.actual &&
+
+               echo "$foo $(cat foo.mtime)" >foo.expect &&
+               echo "$bar $(cat bar.mtime)" >bar.expect &&
+
+               test_cmp foo.expect foo.actual &&
+               test_cmp bar.expect bar.actual &&
+               test "$foo_mtimes" != "$bar_mtimes"
        )
 '
 
-test_expect_success 'cruft repack ignores pack.packSizeLimit' '
+test_expect_success 'cruft repack with pack.packSizeLimit' '
        (
                cd max-pack-size &&
                # repack everything back together to remove the existing cruft
@@ -599,9 +630,12 @@ test_expect_success 'cruft repack ignores pack.packSizeLimit' '
                # ensure the same post condition is met when --max-pack-size
                # would otherwise be inferred from the configuration
                find $packdir -name "*.mtimes" >cruft &&
-               test_line_count = 1 cruft &&
-               test-tool pack-mtimes "$(basename "$(cat cruft)")" >objects &&
-               test_line_count = 2 objects
+               test_line_count = 2 cruft &&
+               for pack in $(cat cruft)
+               do
+                       test-tool pack-mtimes "$(basename $pack)" >objects &&
+                       test_line_count = 1 objects || return 1
+               done
        )
 '
 
@@ -739,4 +773,175 @@ test_expect_success 'cruft objects are freshend via loose' '
        )
 '
 
+test_expect_success 'gc.recentObjectsHook' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               # Create a handful of objects.
+               #
+               #   - one reachable commit, "base", designated for the reachable
+               #     pack
+               #   - one unreachable commit, "cruft.discard", which is marked
+               #     for deletion
+               #   - one unreachable commit, "cruft.old", which would be marked
+               #     for deletion, but is rescued as an extra cruft tip
+               #   - one unreachable commit, "cruft.new", which is not marked
+               #     for deletion
+               test_commit base &&
+               git branch -M main &&
+
+               git checkout --orphan discard &&
+               git rm -fr . &&
+               test_commit --no-tag cruft.discard &&
+
+               git checkout --orphan old &&
+               git rm -fr . &&
+               test_commit --no-tag cruft.old &&
+               cruft_old="$(git rev-parse HEAD)" &&
+
+               git checkout --orphan new &&
+               git rm -fr . &&
+               test_commit --no-tag cruft.new &&
+               cruft_new="$(git rev-parse HEAD)" &&
+
+               git checkout main &&
+               git branch -D discard old new &&
+               git reflog expire --all --expire=all &&
+
+               # mark cruft.old with an mtime that is many minutes
+               # older than the expiration period, and mark cruft.new
+               # with an mtime that is in the future (and thus not
+               # eligible for pruning).
+               test-tool chmtime -2000 "$objdir/$(test_oid_to_path $cruft_old)" &&
+               test-tool chmtime +1000 "$objdir/$(test_oid_to_path $cruft_new)" &&
+
+               # Write the list of cruft objects we expect to
+               # accumulate, which is comprised of everything reachable
+               # from cruft.old and cruft.new, but not cruft.discard.
+               git rev-list --objects --no-object-names \
+                       $cruft_old $cruft_new >cruft.raw &&
+               sort cruft.raw >cruft.expect &&
+
+               # Write the script to list extra tips, which are limited
+               # to cruft.old, in this case.
+               write_script extra-tips <<-EOF &&
+               echo $cruft_old
+               EOF
+               git config gc.recentObjectsHook ./extra-tips &&
+
+               git repack --cruft --cruft-expiration=now -d &&
+
+               mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+               git show-index <${mtimes%.mtimes}.idx >cruft &&
+               cut -d" " -f2 cruft | sort >cruft.actual &&
+               test_cmp cruft.expect cruft.actual &&
+
+               # Ensure that the "old" objects are removed after
+               # dropping the gc.recentObjectsHook hook.
+               git config --unset gc.recentObjectsHook &&
+               git repack --cruft --cruft-expiration=now -d &&
+
+               mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+               git show-index <${mtimes%.mtimes}.idx >cruft &&
+               cut -d" " -f2 cruft | sort >cruft.actual &&
+
+               git rev-list --objects --no-object-names $cruft_new >cruft.raw &&
+               cp cruft.expect cruft.old &&
+               sort cruft.raw >cruft.expect &&
+               test_cmp cruft.expect cruft.actual &&
+
+               # ensure objects which are no longer in the cruft pack were
+               # removed from the repository
+               for object in $(comm -13 cruft.expect cruft.old)
+               do
+                       test_must_fail git cat-file -t $object || return 1
+               done
+       )
+'
+
+test_expect_success 'multi-valued gc.recentObjectsHook' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit base &&
+               git branch -M main &&
+
+               git checkout --orphan cruft.a &&
+               git rm -fr . &&
+               test_commit --no-tag cruft.a &&
+               cruft_a="$(git rev-parse HEAD)" &&
+
+               git checkout --orphan cruft.b &&
+               git rm -fr . &&
+               test_commit --no-tag cruft.b &&
+               cruft_b="$(git rev-parse HEAD)" &&
+
+               git checkout main &&
+               git branch -D cruft.a cruft.b &&
+               git reflog expire --all --expire=all &&
+
+               echo "echo $cruft_a" | write_script extra-tips.a &&
+               echo "echo $cruft_b" | write_script extra-tips.b &&
+               echo "false" | write_script extra-tips.c &&
+
+               git rev-list --objects --no-object-names $cruft_a $cruft_b \
+                       >cruft.raw &&
+               sort cruft.raw >cruft.expect &&
+
+               # ensure that each extra cruft tip is saved by its
+               # respective hook
+               git config --add gc.recentObjectsHook ./extra-tips.a &&
+               git config --add gc.recentObjectsHook ./extra-tips.b &&
+               git repack --cruft --cruft-expiration=now -d &&
+
+               mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+               git show-index <${mtimes%.mtimes}.idx >cruft &&
+               cut -d" " -f2 cruft | sort >cruft.actual &&
+               test_cmp cruft.expect cruft.actual &&
+
+               # ensure that a dirty exit halts cruft pack generation
+               git config --add gc.recentObjectsHook ./extra-tips.c &&
+               test_must_fail git repack --cruft --cruft-expiration=now -d 2>err &&
+               grep "unable to enumerate additional recent objects" err &&
+
+               # and that the existing cruft pack is left alone
+               test_path_is_file "$mtimes"
+       )
+'
+
+test_expect_success 'additional cruft blobs via gc.recentObjectsHook' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit base &&
+
+               blob=$(echo "unreachable" | git hash-object -w --stdin) &&
+
+               # mark the unreachable blob we wrote above as having
+               # aged out of the retention period
+               test-tool chmtime -2000 "$objdir/$(test_oid_to_path $blob)" &&
+
+               # Write the script to list extra tips, which is just the
+               # extra blob as above.
+               write_script extra-tips <<-EOF &&
+               echo $blob
+               EOF
+               git config gc.recentObjectsHook ./extra-tips &&
+
+               git repack --cruft --cruft-expiration=now -d &&
+
+               mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+               git show-index <${mtimes%.mtimes}.idx >cruft &&
+               cut -d" " -f2 cruft >actual &&
+               echo $blob >expect &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index 2cc7fd7a4765b2ba8057591df7588150d453a44a..5eb28f0512d447dcea6a16fe27b0a3272514de42 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='test for no lazy fetch with the commit-graph'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup: prepare a repository with a commit' '
diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh
new file mode 100755 (executable)
index 0000000..2dcf1ee
--- /dev/null
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+test_description='pack-objects --stdin'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+packed_objects () {
+       git show-index <"$1" >tmp-object-list &&
+       cut -d' ' -f2 tmp-object-list | sort &&
+       rm tmp-object-list
+ }
+
+test_expect_success 'setup for --stdin-packs tests' '
+       git init stdin-packs &&
+       (
+               cd stdin-packs &&
+
+               test_commit A &&
+               test_commit B &&
+               test_commit C &&
+
+               for id in A B C
+               do
+                       git pack-objects .git/objects/pack/pack-$id \
+                               --incremental --revs <<-EOF || exit 1
+                       refs/tags/$id
+                       EOF
+               done &&
+
+               ls -la .git/objects/pack
+       )
+'
+
+test_expect_success '--stdin-packs with excluded packs' '
+       (
+               cd stdin-packs &&
+
+               PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+               PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+               PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+
+               git pack-objects test --stdin-packs <<-EOF &&
+               $PACK_A
+               ^$PACK_B
+               $PACK_C
+               EOF
+
+               (
+                       git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+                       git show-index <$(ls .git/objects/pack/pack-C-*.idx)
+               ) >expect.raw &&
+               git show-index <$(ls test-*.idx) >actual.raw &&
+
+               cut -d" " -f2 <expect.raw | sort >expect &&
+               cut -d" " -f2 <actual.raw | sort >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '--stdin-packs is incompatible with --filter' '
+       (
+               cd stdin-packs &&
+               test_must_fail git pack-objects --stdin-packs --stdout \
+                       --filter=blob:none </dev/null 2>err &&
+               test_grep "cannot use --filter with --stdin-packs" err
+       )
+'
+
+test_expect_success '--stdin-packs is incompatible with --revs' '
+       (
+               cd stdin-packs &&
+               test_must_fail git pack-objects --stdin-packs --revs out \
+                       </dev/null 2>err &&
+               test_grep "cannot use internal rev list with --stdin-packs" err
+       )
+'
+
+test_expect_success '--stdin-packs with loose objects' '
+       (
+               cd stdin-packs &&
+
+               PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+               PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+               PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+
+               test_commit D && # loose
+
+               git pack-objects test2 --stdin-packs --unpacked <<-EOF &&
+               $PACK_A
+               ^$PACK_B
+               $PACK_C
+               EOF
+
+               (
+                       git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+                       git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
+                       git rev-list --objects --no-object-names \
+                               refs/tags/C..refs/tags/D
+
+               ) >expect.raw &&
+               ls -la . &&
+               git show-index <$(ls test2-*.idx) >actual.raw &&
+
+               cut -d" " -f2 <expect.raw | sort >expect &&
+               cut -d" " -f2 <actual.raw | sort >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '--stdin-packs with broken links' '
+       (
+               cd stdin-packs &&
+
+               # make an unreachable object with a bogus parent
+               git cat-file -p HEAD >commit &&
+               sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit |
+               git hash-object -w -t commit --stdin >in &&
+
+               git pack-objects .git/objects/pack/pack-D <in &&
+
+               PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+               PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+               PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+               PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" &&
+
+               git pack-objects test3 --stdin-packs --unpacked <<-EOF &&
+               $PACK_A
+               ^$PACK_B
+               $PACK_C
+               $PACK_D
+               EOF
+
+               (
+                       git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+                       git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
+                       git show-index <$(ls .git/objects/pack/pack-D-*.idx) &&
+                       git rev-list --objects --no-object-names \
+                               refs/tags/C..refs/tags/D
+               ) >expect.raw &&
+               git show-index <$(ls test3-*.idx) >actual.raw &&
+
+               cut -d" " -f2 <expect.raw | sort >expect &&
+               cut -d" " -f2 <actual.raw | sort >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'pack-objects --stdin with duplicate packfile' '
+       test_when_finished "rm -fr repo" &&
+
+       git init repo &&
+       (
+               cd repo &&
+               test_commit "commit" &&
+               git repack -ad &&
+
+               {
+                       basename .git/objects/pack/pack-*.pack &&
+                       basename .git/objects/pack/pack-*.pack
+               } >packfiles &&
+
+               git pack-objects --stdin-packs generated-pack <packfiles &&
+               packed_objects .git/objects/pack/pack-*.idx >expect &&
+               packed_objects generated-pack-*.idx >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'pack-objects --stdin with same packfile excluded and included' '
+       test_when_finished "rm -fr repo" &&
+
+       git init repo &&
+       (
+               cd repo &&
+               test_commit "commit" &&
+               git repack -ad &&
+
+               {
+                       basename .git/objects/pack/pack-*.pack &&
+                       printf "^%s\n" "$(basename .git/objects/pack/pack-*.pack)"
+               } >packfiles &&
+
+               git pack-objects --stdin-packs generated-pack <packfiles &&
+               packed_objects generated-pack-*.idx >packed-objects &&
+               test_must_be_empty packed-objects
+       )
+'
+
+test_expect_success 'pack-objects --stdin with packfiles from alternate object database' '
+       test_when_finished "rm -fr shared member" &&
+
+       # Set up a shared repository with a single packfile.
+       git init shared &&
+       test_commit -C shared "shared-objects" &&
+       git -C shared repack -ad &&
+       basename shared/.git/objects/pack/pack-*.pack >packfile &&
+
+       # Set up a repository that is connected to the shared repository. This
+       # repository has no objects on its own, but we still expect to be able
+       # to pack objects from its alternate.
+       git clone --shared shared member &&
+       git -C member pack-objects --stdin-packs generated-pack <packfile &&
+       test_cmp shared/.git/objects/pack/pack-*.pack member/generated-pack-*.pack
+'
+
+test_expect_success 'pack-objects --stdin with packfiles from main and alternate object database' '
+       test_when_finished "rm -fr shared member" &&
+
+       # Set up a shared repository with a single packfile.
+       git init shared &&
+       test_commit -C shared "shared-commit" &&
+       git -C shared repack -ad &&
+
+       # Set up a repository that is connected to the shared repository. This
+       # repository has a second packfile so that we can verify that it is
+       # possible to write packs that include packfiles from different object
+       # databases.
+       git clone --shared shared member &&
+       test_commit -C member "local-commit" &&
+       git -C member repack -dl &&
+
+       {
+               basename shared/.git/objects/pack/pack-*.pack &&
+               basename member/.git/objects/pack/pack-*.pack
+       } >packfiles &&
+
+       {
+               packed_objects shared/.git/objects/pack/pack-*.idx &&
+               packed_objects member/.git/objects/pack/pack-*.idx
+       } | sort >expected-objects &&
+
+       git -C member pack-objects --stdin-packs generated-pack <packfiles &&
+       packed_objects member/generated-pack-*.idx >actual-objects &&
+       test_cmp expected-objects actual-objects
+'
+
+test_done
diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh
new file mode 100755 (executable)
index 0000000..2ba788b
--- /dev/null
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+test_description='pack-objects multi-pack reuse'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bitmap.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+test_pack_reused () {
+       test_trace2_data pack-objects pack-reused "$1"
+}
+
+test_packs_reused () {
+       test_trace2_data pack-objects packs-reused "$1"
+}
+
+
+# pack_position <object> </path/to/pack.idx
+pack_position () {
+       git show-index >objects &&
+       grep "$1" objects | cut -d" " -f1
+}
+
+test_expect_success 'preferred pack is reused for single-pack reuse' '
+       test_config pack.allowPackReuse single &&
+
+       for i in A B
+       do
+               test_commit "$i" &&
+               git repack -d || return 1
+       done &&
+
+       git multi-pack-index write --bitmap &&
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --revs --all >/dev/null &&
+
+       test_pack_reused 3 <trace2.txt &&
+       test_packs_reused 1 <trace2.txt
+'
+
+test_expect_success 'enable multi-pack reuse' '
+       git config pack.allowPackReuse multi
+'
+
+test_expect_success 'reuse all objects from subset of bitmapped packs' '
+       test_commit C &&
+       git repack -d &&
+
+       git multi-pack-index write --bitmap &&
+
+       cat >in <<-EOF &&
+       $(git rev-parse C)
+       ^$(git rev-parse A)
+       EOF
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --revs <in >/dev/null &&
+
+       test_pack_reused 6 <trace2.txt &&
+       test_packs_reused 2 <trace2.txt
+'
+
+test_expect_success 'reuse all objects from all packs' '
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --revs --all >/dev/null &&
+
+       test_pack_reused 9 <trace2.txt &&
+       test_packs_reused 3 <trace2.txt
+'
+
+test_expect_success 'reuse objects from first pack with middle gap' '
+       for i in D E F
+       do
+               test_commit "$i" || return 1
+       done &&
+
+       # Set "pack.window" to zero to ensure that we do not create any
+       # deltas, which could alter the amount of pack reuse we perform
+       # (if, for e.g., we are not sending one or more bases).
+       D="$(git -c pack.window=0 pack-objects --all --unpacked $packdir/pack)" &&
+
+       d_pos="$(pack_position $(git rev-parse D) <$packdir/pack-$D.idx)" &&
+       e_pos="$(pack_position $(git rev-parse E) <$packdir/pack-$D.idx)" &&
+       f_pos="$(pack_position $(git rev-parse F) <$packdir/pack-$D.idx)" &&
+
+       # commits F, E, and D, should appear in that order at the
+       # beginning of the pack
+       test $f_pos -lt $e_pos &&
+       test $e_pos -lt $d_pos &&
+
+       # Ensure that the pack we are constructing sorts ahead of any
+       # other packs in lexical/bitmap order by choosing it as the
+       # preferred pack.
+       git multi-pack-index write --bitmap --preferred-pack="pack-$D.idx" &&
+
+       cat >in <<-EOF &&
+       $(git rev-parse E)
+       ^$(git rev-parse D)
+       EOF
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
+
+       test_pack_reused 3 <trace2.txt &&
+       test_packs_reused 1 <trace2.txt
+'
+
+test_expect_success 'reuse objects from middle pack with middle gap' '
+       rm -fr $packdir/multi-pack-index* &&
+
+       # Ensure that the pack we are constructing sort into any
+       # position *but* the first one, by choosing a different pack as
+       # the preferred one.
+       git multi-pack-index write --bitmap --preferred-pack="pack-$A.idx" &&
+
+       cat >in <<-EOF &&
+       $(git rev-parse E)
+       ^$(git rev-parse D)
+       EOF
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
+
+       test_pack_reused 3 <trace2.txt &&
+       test_packs_reused 1 <trace2.txt
+'
+
+test_expect_success 'omit delta with uninteresting base (same pack)' '
+       git repack -adk &&
+
+       test_seq 32 >f &&
+       git add f &&
+       test_tick &&
+       git commit -m "delta" &&
+       delta="$(git rev-parse HEAD)" &&
+
+       test_seq 64 >f &&
+       test_tick &&
+       git commit -a -m "base" &&
+       base="$(git rev-parse HEAD)" &&
+
+       test_commit other &&
+
+       git repack -d &&
+
+       have_delta "$(git rev-parse $delta:f)" "$(git rev-parse $base:f)" &&
+
+       git multi-pack-index write --bitmap &&
+
+       cat >in <<-EOF &&
+       $(git rev-parse other)
+       ^$base
+       EOF
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
+
+       # We can only reuse the 3 objects corresponding to "other" from
+       # the latest pack.
+       #
+       # This is because even though we want "delta", we do not want
+       # "base", meaning that we have to inflate the delta/base-pair
+       # corresponding to the blob in commit "delta", which bypasses
+       # the pack-reuse mechanism.
+       #
+       # The remaining objects from the other pack are similarly not
+       # reused because their objects are on the uninteresting side of
+       # the query.
+       test_pack_reused 3 <trace2.txt &&
+       test_packs_reused 1 <trace2.txt
+'
+
+test_expect_success 'omit delta from uninteresting base (cross pack)' '
+       cat >in <<-EOF &&
+       $(git rev-parse $base)
+       ^$(git rev-parse $delta)
+       EOF
+
+       P="$(git pack-objects --revs $packdir/pack <in)" &&
+
+       git multi-pack-index write --bitmap --preferred-pack="pack-$P.idx" &&
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --delta-base-offset --all >/dev/null &&
+
+       packs_nr="$(find $packdir -type f -name "pack-*.pack" | wc -l)" &&
+       objects_nr="$(git rev-list --count --all --objects)" &&
+
+       test_pack_reused $(($objects_nr - 1)) <trace2.txt &&
+       test_packs_reused $packs_nr <trace2.txt
+'
+
+test_done
index 8c8af99b844be1ad571f55364891db10fc172d98..43cbcd5d497ecaef33c97032e7c5a8729fb4c01c 100755 (executable)
@@ -55,7 +55,7 @@ check_fsync_events () {
 
        cat >expect &&
        sed -n \
-               -e '/^{"event":"data",.*"category":"fsync",/ {
+               -e '/^{"event":"counter",.*"category":"fsync",/ {
                        s/.*"category":"fsync",//;
                        s/}$//;
                        p;
@@ -78,8 +78,8 @@ test_expect_success 'unpack big object in stream (core.fsyncmethod=batch)' '
                flush_count=1
        fi &&
        check_fsync_events trace2.txt <<-EOF &&
-       "key":"fsync/writeout-only","value":"6"
-       "key":"fsync/hardware-flush","value":"$flush_count"
+       "name":"writeout-only","count":6
+       "name":"hardware-flush","count":$flush_count
        EOF
 
        test_dir_is_empty dest.git/objects/pack &&
index 001b7a17ad2bb93610198ebf134ccbedc2781f00..8b8bc47dc0b9d6a8d90928c628bbe122d5d04f31 100755 (executable)
@@ -133,10 +133,8 @@ test_expect_success 'pre-receive hook that forgets to read its input' '
        EOF
        rm -f victim.git/hooks/update victim.git/hooks/post-update &&
 
-       for v in $(test_seq 100 999)
-       do
-               git branch branch_$v main || return
-       done &&
+       printf "create refs/heads/branch_%d main\n" $(test_seq 100 999) >input &&
+       git update-ref --stdin <input &&
        git push ./victim.git "+refs/heads/*:refs/heads/*"
 '
 
index 978f240cdaceb4e7593adcf89136e9de9b558aa1..cfaae547398e0e54cfc5b8f6403d2502ac826782 100755 (executable)
@@ -7,6 +7,7 @@ test_description='Test the post-checkout hook.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index cc078896673ae221754a5b65102b559b872dcfd9..51737eeafeeefa0c1b1f075081f4c5c4b95de64e 100755 (executable)
@@ -5,6 +5,7 @@ test_description='tracking branch update checks for git push'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 11f03239a0628c4a50ef13758647f6c85e35e552..1686ac13aa628584297fcfd961356ae5291c57e7 100755 (executable)
@@ -5,6 +5,7 @@ test_description='forced push to replace commit we do not have'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index dcbeb42082791ba0bb2683e9f78b545250d380fc..d6a994663383c32da26d1654527f938873589e2b 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='remote push rejects are reported by client'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 5f3ff051ca2fa3bba8beabbcb1491d5e69262f34..ad7f8c6f00202c5ec844108b14a2c1301c185223 100755 (executable)
@@ -17,6 +17,12 @@ test_expect_success 'setup' '
        git checkout A^0 &&
        test_commit E bar E &&
        test_commit F foo F &&
+       git checkout B &&
+       git merge E &&
+       git tag merge-E &&
+       test_commit G G &&
+       test_commit H H &&
+       test_commit I I &&
        git checkout main &&
 
        test_hook --setup post-rewrite <<-EOF
@@ -173,6 +179,48 @@ test_fail_interactive_rebase () {
        )
 }
 
+test_expect_success 'git rebase with failed pick' '
+       clear_hook_input &&
+       cat >todo <<-\EOF &&
+       exec >bar
+       merge -C merge-E E
+       exec >G
+       pick G
+       exec >H 2>I
+       pick H
+       fixup I
+       EOF
+
+       (
+               set_replace_editor todo &&
+               test_must_fail git rebase -i D D 2>err
+       ) &&
+       grep "would be overwritten" err &&
+       rm bar &&
+
+       test_must_fail git rebase --continue 2>err &&
+       grep "would be overwritten" err &&
+       rm G &&
+
+       test_must_fail git rebase --continue 2>err &&
+       grep "would be overwritten" err &&
+       rm H &&
+
+       test_must_fail git rebase --continue 2>err &&
+       grep "would be overwritten" err &&
+       rm I &&
+
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<-EOF &&
+       $(git rev-parse merge-E) $(git rev-parse HEAD~2)
+       $(git rev-parse G) $(git rev-parse HEAD~1)
+       $(git rev-parse H) $(git rev-parse HEAD)
+       $(git rev-parse I) $(git rev-parse HEAD)
+       EOF
+       verify_hook_input
+'
+
 test_expect_success 'git rebase -i (unchanged)' '
        git reset --hard D &&
        clear_hook_input &&
index 6dfc7b1c0da421ac143c68005a90caf6a351fb76..510fff38da9cb68c31a63ef00ece3d8f21c79942 100644 (file)
@@ -18,7 +18,7 @@ test_expect_success "proc-receive: not support push options ($PROTOCOL)" '
                HEAD:refs/for/main/topic \
                >out-$test_count 2>&1 &&
        make_user_friendly_and_stable_output <out-$test_count >actual &&
-       test_i18ngrep "fatal: the receiving end does not support push options" \
+       test_grep "fatal: the receiving end does not support push options" \
                actual &&
 
        test_cmp_refs -C "$upstream" <<-EOF
index 768880b40fbdbbb072ed8b362ea638436583e066..9435457de0cd9baff4ddac0a8363b52b0aa6006d 100644 (file)
@@ -19,7 +19,7 @@ test_expect_success "proc-receive: not support push options ($PROTOCOL/porcelain
                HEAD:refs/for/main/topic \
                >out-$test_count 2>&1 &&
        make_user_friendly_and_stable_output <out-$test_count >actual &&
-       test_i18ngrep "fatal: the receiving end does not support push options" \
+       test_grep "fatal: the receiving end does not support push options" \
                actual &&
 
        test_cmp_refs -C "$upstream" <<-EOF
index d18f2823d86e8b94c5331b1087f962469fc91f2c..1bc15a3f080d3930f4f726ef7ad2bb9de38d368e 100755 (executable)
@@ -132,13 +132,18 @@ test_expect_success 'single branch object count' '
 '
 
 test_expect_success 'single given branch clone' '
-       git clone --single-branch --branch A "file://$(pwd)/." branch-a &&
-       test_must_fail git --git-dir=branch-a/.git rev-parse origin/B
+       GIT_TRACE2_EVENT="$(pwd)/branch-a/trace2_event" \
+               git clone --single-branch --branch A "file://$(pwd)/." branch-a &&
+       test_must_fail git --git-dir=branch-a/.git rev-parse origin/B &&
+       grep \"fetch-info\".*\"haves\":0 branch-a/trace2_event &&
+       grep \"fetch-info\".*\"wants\":1 branch-a/trace2_event
 '
 
 test_expect_success 'clone shallow depth 1' '
-       git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 &&
-       test "$(git --git-dir=shallow0/.git rev-list --count HEAD)" = 1
+       GIT_TRACE2_EVENT="$(pwd)/shallow0/trace2_event" \
+               git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 &&
+       test "$(git --git-dir=shallow0/.git rev-list --count HEAD)" = 1 &&
+       grep \"fetch-info\".*\"depth\":1 shallow0/trace2_event
 '
 
 test_expect_success 'clone shallow depth 1 with fsck' '
@@ -235,7 +240,10 @@ test_expect_success 'add two more (part 2)' '
 test_expect_success 'deepening pull in shallow repo' '
        (
                cd shallow &&
-               git pull --depth 4 .. B
+               GIT_TRACE2_EVENT="$(pwd)/trace2_event" \
+                       git pull --depth 4 .. B &&
+               grep \"fetch-info\".*\"depth\":4 trace2_event &&
+               grep \"fetch-info\".*\"shallows\":2 trace2_event
        )
 '
 
@@ -306,9 +314,12 @@ test_expect_success 'fetch --depth --no-shallow' '
 test_expect_success 'turn shallow to complete repository' '
        (
                cd shallow &&
-               git fetch --unshallow &&
+               GIT_TRACE2_EVENT="$(pwd)/trace2_event" \
+                       git fetch --unshallow &&
                ! test -f .git/shallow &&
-               git fsck --full
+               git fsck --full &&
+               grep \"fetch-info\".*\"shallows\":2 trace2_event &&
+               grep \"fetch-info\".*\"depth\":2147483647 trace2_event
        )
 '
 
@@ -403,7 +414,7 @@ test_expect_success 'in_vain not triggered before first ACK' '
        test_commit -C myserver bar &&
 
        git -C myclient fetch --progress origin 2>log &&
-       test_i18ngrep "remote: Total 3 " log
+       test_grep "remote: Total 3 " log
 '
 
 test_expect_success 'in_vain resetted upon ACK' '
@@ -435,7 +446,7 @@ test_expect_success 'in_vain resetted upon ACK' '
        # the client reports that first_anotherbranch_commit is common.
        GIT_TRACE2_EVENT="$(pwd)/trace2" git -C myclient fetch --progress origin main 2>log &&
        grep \"key\":\"total_rounds\",\"value\":\"6\" trace2 &&
-       test_i18ngrep "Total 3 " log
+       test_grep "Total 3 " log
 '
 
 test_expect_success 'fetch in shallow repo unreachable shallow objects' '
@@ -459,7 +470,7 @@ test_expect_success 'fetch creating new shallow root' '
                git fetch --depth=1 --progress 2>actual &&
                # This should fetch only the empty commit, no tree or
                # blob objects
-               test_i18ngrep "remote: Total 1" actual
+               test_grep "remote: Total 1" actual
        )
 '
 
@@ -694,7 +705,7 @@ test_expect_success 'fetch-pack cannot fetch a raw sha1 that is not advertised a
        # unadvertised objects, so restrict this test to v0.
        test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C client fetch-pack ../server \
                $(git -C server rev-parse refs/heads/main^) 2>err &&
-       test_i18ngrep "Server does not allow request for unadvertised object" err
+       test_grep "Server does not allow request for unadvertised object" err
 '
 
 check_prot_path () {
@@ -826,13 +837,15 @@ test_expect_success 'clone shallow since ...' '
 '
 
 test_expect_success 'fetch shallow since ...' '
-       git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
+       GIT_TRACE2_EVENT=$(pwd)/shallow11/trace2_event \
+               git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
        git -C shallow11 log --pretty=tformat:%s origin/main >actual &&
        cat >expected <<-\EOF &&
        three
        two
        EOF
-       test_cmp expected actual
+       test_cmp expected actual &&
+       grep \"fetch-info\".*\"deepen-since\":true shallow11/trace2_event
 '
 
 test_expect_success 'clone shallow since selects no commits' '
@@ -987,13 +1000,16 @@ test_expect_success 'filtering by size' '
        test_config -C server uploadpack.allowfilter 1 &&
 
        test_create_repo client &&
-       git -C client fetch-pack --filter=blob:limit=0 ../server HEAD &&
+       GIT_TRACE2_EVENT=$(pwd)/client/trace2_event \
+               git -C client fetch-pack --filter=blob:limit=0 ../server HEAD &&
 
        # Ensure that object is not inadvertently fetched
        commit=$(git -C server rev-parse HEAD) &&
        blob=$(git hash-object server/one.t) &&
        git -C client rev-list --objects --missing=allow-any "$commit" >oids &&
-       ! grep "$blob" oids
+       ! grep "$blob" oids &&
+
+       grep \"fetch-info\".*\"filter\":\"blob:limit\" client/trace2_event
 '
 
 test_expect_success 'filtering by size has no effect if support for it is not advertised' '
@@ -1010,7 +1026,7 @@ test_expect_success 'filtering by size has no effect if support for it is not ad
        git -C client rev-list --objects --missing=allow-any "$commit" >oids &&
        grep "$blob" oids &&
 
-       test_i18ngrep "filtering not recognized by server" err
+       test_grep "filtering not recognized by server" err
 '
 
 fetch_filter_blob_limit_zero () {
index b160f8b7fb7e1f9973361cf3ed86d769d5610206..7b3ff21b984ff4fcfbd4a64a3eecceabf694e27b 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test quickfetch from local'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index ac4099ca8931930989eece26fa735e2f6c00bbc3..138e6778a477650ecbe2dc3e480c5fe83d4bb485 100755 (executable)
@@ -4,6 +4,7 @@ test_description='fetch/receive strict mode'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup and inject "corrupt or missing" object' '
@@ -138,12 +139,12 @@ This commit object intentionally broken
 EOF
 
 test_expect_success 'setup bogus commit' '
-       commit="$(git hash-object -t commit -w --stdin <bogus-commit)"
+       commit="$(git hash-object --literally -t commit -w --stdin <bogus-commit)"
 '
 
 test_expect_success 'fsck with no skipList input' '
        test_must_fail git fsck 2>err &&
-       test_i18ngrep "missingEmail" err
+       test_grep "missingEmail" err
 '
 
 test_expect_success 'setup sorted and unsorted skipLists' '
@@ -168,9 +169,9 @@ test_expect_success 'fsck with unsorted skipList' '
 test_expect_success 'fsck with invalid or bogus skipList input' '
        git -c fsck.skipList=/dev/null -c fsck.missingEmail=ignore fsck &&
        test_must_fail git -c fsck.skipList=does-not-exist -c fsck.missingEmail=ignore fsck 2>err &&
-       test_i18ngrep "could not open.*: does-not-exist" err &&
+       test_grep "could not open.*: does-not-exist" err &&
        test_must_fail git -c fsck.skipList=.git/config -c fsck.missingEmail=ignore fsck 2>err &&
-       test_i18ngrep "invalid object name: \[core\]" err
+       test_grep "invalid object name: \[core\]" err
 '
 
 test_expect_success 'fsck with other accepted skipList input (comments & empty lines)' '
@@ -179,14 +180,14 @@ test_expect_success 'fsck with other accepted skipList input (comments & empty l
        $(test_oid 001)
        EOF
        test_must_fail git -c fsck.skipList=SKIP.with-comment fsck 2>err-with-comment &&
-       test_i18ngrep "missingEmail" err-with-comment &&
+       test_grep "missingEmail" err-with-comment &&
        cat >SKIP.with-empty-line <<-EOF &&
        $(test_oid 001)
 
        $(test_oid 002)
        EOF
        test_must_fail git -c fsck.skipList=SKIP.with-empty-line fsck 2>err-with-empty-line &&
-       test_i18ngrep "missingEmail" err-with-empty-line
+       test_grep "missingEmail" err-with-empty-line
 '
 
 test_expect_success 'fsck no garbage output from comments & empty lines errors' '
@@ -197,7 +198,7 @@ test_expect_success 'fsck no garbage output from comments & empty lines errors'
 test_expect_success 'fsck with invalid abbreviated skipList input' '
        echo $commit | test_copy_bytes 20 >SKIP.abbreviated &&
        test_must_fail git -c fsck.skipList=SKIP.abbreviated fsck 2>err-abbreviated &&
-       test_i18ngrep "^fatal: invalid object name: " err-abbreviated
+       test_grep "^fatal: invalid object name: " err-abbreviated
 '
 
 test_expect_success 'fsck with exhaustive accepted skipList input (various types of comments etc.)' '
@@ -230,10 +231,10 @@ test_expect_success 'push with receive.fsck.skipList' '
        test_must_fail git push --porcelain dst bogus &&
        git --git-dir=dst/.git config receive.fsck.skipList does-not-exist &&
        test_must_fail git push --porcelain dst bogus 2>err &&
-       test_i18ngrep "could not open.*: does-not-exist" err &&
+       test_grep "could not open.*: does-not-exist" err &&
        git --git-dir=dst/.git config receive.fsck.skipList config &&
        test_must_fail git push --porcelain dst bogus 2>err &&
-       test_i18ngrep "invalid object name: \[core\]" err &&
+       test_grep "invalid object name: \[core\]" err &&
 
        git --git-dir=dst/.git config receive.fsck.skipList SKIP &&
        git push --porcelain dst bogus
@@ -259,10 +260,10 @@ test_expect_success 'fetch with fetch.fsck.skipList' '
        test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec &&
        git --git-dir=dst/.git config fetch.fsck.skipList does-not-exist &&
        test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec 2>err &&
-       test_i18ngrep "could not open.*: does-not-exist" err &&
+       test_grep "could not open.*: does-not-exist" err &&
        git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/config &&
        test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec 2>err &&
-       test_i18ngrep "invalid object name: \[core\]" err &&
+       test_grep "invalid object name: \[core\]" err &&
 
        git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/SKIP &&
        git --git-dir=dst/.git fetch "file://$(pwd)" $refspec
@@ -270,7 +271,7 @@ test_expect_success 'fetch with fetch.fsck.skipList' '
 
 test_expect_success 'fsck.<unknownmsg-id> dies' '
        test_must_fail git -c fsck.whatEver=ignore fsck 2>err &&
-       test_i18ngrep "Unhandled message id: whatever" err
+       test_grep "Unhandled message id: whatever" err
 '
 
 test_expect_success 'push with receive.fsck.missingEmail=warn' '
@@ -292,7 +293,7 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' '
                receive.fsck.missingEmail warn &&
        git push --porcelain dst bogus >act 2>&1 &&
        grep "missingEmail" act &&
-       test_i18ngrep "skipping unknown msg id.*whatever" act &&
+       test_grep "skipping unknown msg id.*whatever" act &&
        git --git-dir=dst/.git branch -D bogus &&
        git --git-dir=dst/.git config --add \
                receive.fsck.missingEmail ignore &&
@@ -320,7 +321,7 @@ test_expect_success 'fetch with fetch.fsck.missingEmail=warn' '
                fetch.fsck.missingEmail warn &&
        git --git-dir=dst/.git fetch "file://$(pwd)" $refspec >act 2>&1 &&
        grep "missingEmail" act &&
-       test_i18ngrep "Skipping unknown msg id.*whatever" act &&
+       test_grep "Skipping unknown msg id.*whatever" act &&
        rm -rf dst &&
        git init dst &&
        git --git-dir=dst/.git config fetch.fsckobjects true &&
index 43b7bcd7159c252ff7a2e68dc28fc617649bb658..7789ff12c4b8f80f8ad4385932c342c7c9ae7d00 100755 (executable)
@@ -1075,7 +1075,7 @@ test_expect_success 'remote prune to cause a dangling symref' '
                cd eight &&
                git remote prune origin
        ) >err 2>&1 &&
-       test_i18ngrep "has become dangling" err &&
+       test_grep "has become dangling" err &&
 
        : And the dangling symref will not cause other annoying errors &&
        (
@@ -1087,7 +1087,7 @@ test_expect_success 'remote prune to cause a dangling symref' '
                cd eight &&
                test_must_fail git branch nomore origin
        ) 2>err &&
-       test_i18ngrep "dangling symref" err
+       test_grep "dangling symref" err
 '
 
 test_expect_success 'show empty remote' '
@@ -1419,7 +1419,7 @@ test_expect_success 'extra args: setup' '
 test_extra_arg () {
        test_expect_success "extra args: $*" "
                test_must_fail git remote $* bogus_extra_arg 2>actual &&
-               test_i18ngrep '^usage:' actual
+               test_grep '^usage:' actual
        "
 }
 
@@ -1453,12 +1453,12 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '
                                oid=$(git rev-parse some-tag^{$type})
                        fi &&
                        test_must_fail git push origin $oid:dst 2>err &&
-                       test_i18ngrep "error: The destination you" err &&
-                       test_i18ngrep "hint: Did you mean" err &&
+                       test_grep "error: The destination you" err &&
+                       test_grep "hint: Did you mean" err &&
                        test_must_fail git -c advice.pushUnqualifiedRefName=false \
                                push origin $oid:dst 2>err &&
-                       test_i18ngrep "error: The destination you" err &&
-                       test_i18ngrep ! "hint: Did you mean" err ||
+                       test_grep "error: The destination you" err &&
+                       test_grep ! "hint: Did you mean" err ||
                        exit 1
                done
        )
@@ -1479,16 +1479,16 @@ test_expect_success 'refs/remotes/* <src> refspec and unqualified <dst> DWIM and
                git fetch --no-tags two &&
 
                test_must_fail git push origin refs/remotes/two/another:dst 2>err &&
-               test_i18ngrep "error: The destination you" err &&
+               test_grep "error: The destination you" err &&
 
                test_must_fail git push origin refs/remotes/tags-from-two/my-tag:dst-tag 2>err &&
-               test_i18ngrep "error: The destination you" err &&
+               test_grep "error: The destination you" err &&
 
                test_must_fail git push origin refs/remotes/trees-from-two/my-head-tree:dst-tree 2>err &&
-               test_i18ngrep "error: The destination you" err &&
+               test_grep "error: The destination you" err &&
 
                test_must_fail git push origin refs/remotes/blobs-from-two/my-file-blob:dst-blob 2>err &&
-               test_i18ngrep "error: The destination you" err
+               test_grep "error: The destination you" err
        )
 '
 
index 5bac03ede81b4f964a91df275e3620f9da476c46..0e176175a353571428bcf2ca505fa116564f80a3 100755 (executable)
@@ -99,4 +99,13 @@ test_expect_success 'updating remote name updates that remote' '
        ! repo_fetched two
 '
 
+test_expect_success 'updating group in parallel with a duplicate remote does not fail (fetch)' '
+       mark fetch-group-duplicate &&
+       update_repo one &&
+       git config --add remotes.duplicate one &&
+       git config --add remotes.duplicate one &&
+       git -c fetch.parallel=2 remote update duplicate &&
+       repo_fetched one
+'
+
 test_done
index e6149295b187429575aa6fe56e2e206900d30341..c6a6957c5001e9480cbd26ccba5fd26c54126ece 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check environment showed to remote side of transports'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'set up "remote" push situation' '
index c0b745e33b8a52b6c2defda1fa04ef1b8c202841..33d34d5ae9e953e29f6dba8badb16922072cd76c 100755 (executable)
@@ -169,6 +169,7 @@ test_expect_success REFFILES 'fetch --prune fails to delete branches' '
        git clone . prune-fail &&
        cd prune-fail &&
        git update-ref refs/remotes/origin/extrabranch main &&
+       git pack-refs --all &&
        : this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds  &&
        >.git/packed-refs.new &&
 
@@ -415,9 +416,9 @@ test_expect_success 'fetch uses remote ref names to describe new refs' '
        (
                cd descriptive &&
                git fetch o 2>actual &&
-               test_i18ngrep "new branch.* -> refs/crazyheads/descriptive-branch$" actual &&
-               test_i18ngrep "new tag.* -> descriptive-tag$" actual &&
-               test_i18ngrep "new ref.* -> crazy$" actual
+               test_grep "new branch.* -> refs/crazyheads/descriptive-branch$" actual &&
+               test_grep "new tag.* -> descriptive-tag$" actual &&
+               test_grep "new ref.* -> crazy$" actual
        ) &&
        git checkout main
 '
@@ -802,10 +803,19 @@ test_expect_success 'fetch.writeCommitGraph with submodules' '
                cd super-clone &&
                rm -rf .git/objects/info &&
                git -c fetch.writeCommitGraph=true fetch origin &&
-               test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain
+               test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain &&
+               git -c fetch.writeCommitGraph=true fetch --recurse-submodules origin
        )
 '
 
+# fetches from first configured url
+test_expect_success 'fetch from multiple configured URLs in single remote' '
+       git init url1 &&
+       git remote add multipleurls url1 &&
+       git remote set-url --add multipleurls url2 &&
+       git fetch multipleurls
+'
+
 # configured prune tests
 
 set_config_tristate () {
@@ -1105,63 +1115,65 @@ test_expect_success 'fetching with auto-gc does not lock up' '
                git config gc.autoPackLimit 1 &&
                git config gc.autoDetach false &&
                GIT_ASK_YESNO="$D/askyesno" git fetch --verbose >fetch.out 2>&1 &&
-               test_i18ngrep "Auto packing the repository" fetch.out &&
+               test_grep "Auto packing the repository" fetch.out &&
                ! grep "Should I try again" fetch.out
        )
 '
 
-test_expect_success 'fetch aligned output' '
-       git clone . full-output &&
-       test_commit looooooooooooong-tag &&
-       (
-               cd full-output &&
-               git -c fetch.output=full fetch origin >actual 2>&1 &&
-               grep -e "->" actual | cut -c 22- >../actual
-       ) &&
-       cat >expect <<-\EOF &&
-       main                 -> origin/main
-       looooooooooooong-tag -> looooooooooooong-tag
-       EOF
-       test_cmp expect actual
-'
+for section in fetch transfer
+do
+       test_expect_success "$section.hideRefs affects connectivity check" '
+               GIT_TRACE="$PWD"/trace git -c $section.hideRefs=refs -c \
+                       $section.hideRefs="!refs/tags/" fetch &&
+               grep "git rev-list .*--exclude-hidden=fetch" trace
+       '
+done
+
+test_expect_success 'prepare source branch' '
+       echo one >onebranch &&
+       git checkout --orphan onebranch &&
+       git rm --cached -r . &&
+       git add onebranch &&
+       git commit -m onebranch &&
+       git rev-list --objects onebranch -- >actual &&
+       # 3 objects should be created, at least ...
+       test 3 -le $(wc -l <actual)
+'
+
+validate_store_type () {
+       git -C dest count-objects -v >actual &&
+       case "$store_type" in
+       packed)
+               grep "^count: 0$" actual ;;
+       loose)
+               grep "^packs: 0$" actual ;;
+       esac || {
+               echo "store_type is $store_type"
+               cat actual
+               false
+       }
+}
 
-test_expect_success 'fetch compact output' '
-       git clone . compact &&
-       test_commit extraaa &&
-       (
-               cd compact &&
-               git -c fetch.output=compact fetch origin >actual 2>&1 &&
-               grep -e "->" actual | cut -c 22- >../actual
-       ) &&
-       cat >expect <<-\EOF &&
-       main       -> origin/*
-       extraaa    -> *
-       EOF
-       test_cmp expect actual
-'
+test_unpack_limit () {
+       store_type=$1
 
-test_expect_success '--no-show-forced-updates' '
-       mkdir forced-updates &&
-       (
-               cd forced-updates &&
-               git init &&
-               test_commit 1 &&
-               test_commit 2
-       ) &&
-       git clone forced-updates forced-update-clone &&
-       git clone forced-updates no-forced-update-clone &&
-       git -C forced-updates reset --hard HEAD~1 &&
-       (
-               cd forced-update-clone &&
-               git fetch --show-forced-updates origin 2>output &&
-               test_i18ngrep "(forced update)" output
-       ) &&
-       (
-               cd no-forced-update-clone &&
-               git fetch --no-show-forced-updates origin 2>output &&
-               test_i18ngrep ! "(forced update)" output
-       )
-'
+       case "$store_type" in
+       packed) fetch_limit=1 transfer_limit=10000 ;;
+       loose) fetch_limit=10000 transfer_limit=1 ;;
+       esac
+
+       test_expect_success "fetch trumps transfer limit" '
+               rm -fr dest &&
+               git --bare init dest &&
+               git -C dest config fetch.unpacklimit $fetch_limit &&
+               git -C dest config transfer.unpacklimit $transfer_limit &&
+               git -C dest fetch .. onebranch &&
+               validate_store_type
+       '
+}
+
+test_unpack_limit packed
+test_unpack_limit loose
 
 setup_negotiation_tip () {
        SERVER="$1"
index 20d063fb9ae72c4df77a00dd5d5cb6da65640238..5dbe107ce88f98e75341d5ba3b25ae4f8d10d35f 100755 (executable)
@@ -15,6 +15,19 @@ generate_references () {
        done
 }
 
+test_expect_success 'set up fake upload-pack' '
+       # This can be used to simulate an upload-pack that just shows the
+       # contents of the "input" file (prepared with the test-tool pkt-line
+       # helper), and does not do any negotiation (since ls-remote does not
+       # need it).
+       write_script cat-input <<-\EOF
+       # send our initial advertisement/response
+       cat input
+       # soak up the flush packet from the client
+       cat
+       EOF
+'
+
 test_expect_success 'dies when no remote found' '
        test_must_fail git ls-remote
 '
@@ -231,22 +244,25 @@ test_expect_success 'protocol v2 supports hiderefs' '
 
 test_expect_success 'ls-remote --symref' '
        git fetch origin &&
-       echo "ref: refs/heads/main      HEAD" >expect &&
+       echo "ref: refs/heads/main      HEAD" >expect.v2 &&
        generate_references \
                HEAD \
-               refs/heads/main >>expect &&
+               refs/heads/main >>expect.v2 &&
+       echo "ref: refs/remotes/origin/main     refs/remotes/origin/HEAD" >>expect.v2 &&
        oid=$(git rev-parse HEAD) &&
-       echo "$oid      refs/remotes/origin/HEAD" >>expect &&
+       echo "$oid      refs/remotes/origin/HEAD" >>expect.v2 &&
        generate_references \
                refs/remotes/origin/main \
                refs/tags/mark \
                refs/tags/mark1.1 \
                refs/tags/mark1.10 \
-               refs/tags/mark1.2 >>expect &&
-       # Protocol v2 supports sending symrefs for refs other than HEAD, so use
-       # protocol v0 here.
-       GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref >actual &&
-       test_cmp expect actual
+               refs/tags/mark1.2 >>expect.v2 &&
+       # v0 does not show non-HEAD symrefs
+       grep -v "ref: refs/remotes" <expect.v2 >expect.v0 &&
+       git -c protocol.version=0 ls-remote --symref >actual.v0 &&
+       test_cmp expect.v0 actual.v0 &&
+       git -c protocol.version=2 ls-remote --symref >actual.v2 &&
+       test_cmp expect.v2 actual.v2
 '
 
 test_expect_success 'ls-remote with filtered symref (refname)' '
@@ -255,76 +271,41 @@ test_expect_success 'ls-remote with filtered symref (refname)' '
        ref: refs/heads/main    HEAD
        $rev    HEAD
        EOF
-       # Protocol v2 supports sending symrefs for refs other than HEAD, so use
-       # protocol v0 here.
-       GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref . HEAD >actual &&
+       git ls-remote --symref . HEAD >actual &&
        test_cmp expect actual
 '
 
-test_expect_failure 'ls-remote with filtered symref (--heads)' '
+test_expect_success 'ls-remote with filtered symref (--heads)' '
        git symbolic-ref refs/heads/foo refs/tags/mark &&
-       cat >expect <<-EOF &&
+       cat >expect.v2 <<-EOF &&
        ref: refs/tags/mark     refs/heads/foo
        $rev    refs/heads/foo
        $rev    refs/heads/main
        EOF
-       # Protocol v2 supports sending symrefs for refs other than HEAD, so use
-       # protocol v0 here.
-       GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref --heads . >actual &&
-       test_cmp expect actual
+       grep -v "^ref: refs/tags/" <expect.v2 >expect.v0 &&
+       git -c protocol.version=0 ls-remote --symref --heads . >actual.v0 &&
+       test_cmp expect.v0 actual.v0 &&
+       git -c protocol.version=2 ls-remote --symref --heads . >actual.v2 &&
+       test_cmp expect.v2 actual.v2
 '
 
-test_expect_success 'ls-remote --symref omits filtered-out matches' '
-       cat >expect <<-EOF &&
-       $rev    refs/heads/foo
-       $rev    refs/heads/main
+test_expect_success 'indicate no refs in v0 standards-compliant empty remote' '
+       # Git does not produce an output like this, but it does match the
+       # standard and is produced by other implementations like JGit. So
+       # hard-code the case we care about.
+       #
+       # The actual capabilities do not matter; there are none that would
+       # change how ls-remote behaves.
+       oid=0000000000000000000000000000000000000000 &&
+       test-tool pkt-line pack >input.q <<-EOF &&
+       $oid capabilities^{}Qcaps-go-here
+       0000
        EOF
-       # Protocol v2 supports sending symrefs for refs other than HEAD, so use
-       # protocol v0 here.
-       GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref --heads . >actual &&
-       test_cmp expect actual &&
-       GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref . "refs/heads/*" >actual &&
-       test_cmp expect actual
-'
-
-test_lazy_prereq GIT_DAEMON '
-       test_bool_env GIT_TEST_GIT_DAEMON true
-'
+       q_to_nul <input.q >input &&
 
-# This test spawns a daemon, so run it only if the user would be OK with
-# testing with git-daemon.
-test_expect_success PIPE,JGIT,GIT_DAEMON 'indicate no refs in standards-compliant empty remote' '
-       test_set_port JGIT_DAEMON_PORT &&
-       JGIT_DAEMON_PID= &&
-       git init --bare empty.git &&
-       >empty.git/git-daemon-export-ok &&
-       mkfifo jgit_daemon_output &&
-       {
-               jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
-               JGIT_DAEMON_PID=$!
-       } &&
-       test_when_finished kill "$JGIT_DAEMON_PID" &&
-       {
-               read line &&
-               case $line in
-               Exporting*)
-                       ;;
-               *)
-                       echo "Expected: Exporting" &&
-                       false;;
-               esac &&
-               read line &&
-               case $line in
-               "Listening on"*)
-                       ;;
-               *)
-                       echo "Expected: Listening on" &&
-                       false;;
-               esac
-       } <jgit_daemon_output &&
        # --exit-code asks the command to exit with 2 when no
        # matching refs are found.
-       test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
+       test_expect_code 2 git ls-remote --exit-code --upload-pack=./cat-input .
 '
 
 test_expect_success 'ls-remote works outside repository' '
@@ -339,14 +320,14 @@ test_expect_success 'ls-remote works outside repository' '
 test_expect_success 'ls-remote --sort fails gracefully outside repository' '
        # Use a sort key that requires access to the referenced objects.
        nongit test_must_fail git ls-remote --sort=authordate "$TRASH_DIRECTORY" 2>err &&
-       test_i18ngrep "^fatal: not a git repository, but the field '\''authordate'\'' requires access to object data" err
+       test_grep "^fatal: not a git repository, but the field '\''authordate'\'' requires access to object data" err
 '
 
 test_expect_success 'ls-remote patterns work with all protocol versions' '
        git for-each-ref --format="%(objectname)        %(refname)" \
                refs/heads/main refs/remotes/origin/main >expect &&
-       git -c protocol.version=1 ls-remote . main >actual.v1 &&
-       test_cmp expect actual.v1 &&
+       git -c protocol.version=0 ls-remote . main >actual.v0 &&
+       test_cmp expect actual.v0 &&
        git -c protocol.version=2 ls-remote . main >actual.v2 &&
        test_cmp expect actual.v2
 '
@@ -354,10 +335,49 @@ test_expect_success 'ls-remote patterns work with all protocol versions' '
 test_expect_success 'ls-remote prefixes work with all protocol versions' '
        git for-each-ref --format="%(objectname)        %(refname)" \
                refs/heads/ refs/tags/ >expect &&
-       git -c protocol.version=1 ls-remote --heads --tags . >actual.v1 &&
-       test_cmp expect actual.v1 &&
+       git -c protocol.version=0 ls-remote --heads --tags . >actual.v0 &&
+       test_cmp expect actual.v0 &&
        git -c protocol.version=2 ls-remote --heads --tags . >actual.v2 &&
        test_cmp expect actual.v2
 '
 
+test_expect_success 'v0 clients can handle multiple symrefs' '
+       # Modern versions of Git will not return multiple symref capabilities
+       # for v0, so we have to hard-code the response. Note that we will
+       # always use both v0 and object-format=sha1 here, as the hard-coded
+       # response reflects a server that only supports those.
+       oid=1234567890123456789012345678901234567890 &&
+       symrefs="symref=refs/remotes/origin/HEAD:refs/remotes/origin/main" &&
+       symrefs="$symrefs symref=HEAD:refs/heads/main" &&
+
+       # Likewise we want to make sure our parser is not fooled by the string
+       # "symref" appearing as part of an earlier cap. But there is no way to
+       # do that via upload-pack, as arbitrary strings can appear only in a
+       # "symref" value itself (where we skip past the values as a whole)
+       # and "agent" (which always appears after "symref", so putting our
+       # parser in a confused state is less interesting).
+       caps="some other caps including a-fake-symref-cap" &&
+
+       test-tool pkt-line pack >input.q <<-EOF &&
+       $oid HEADQ$caps $symrefs
+       $oid refs/heads/main
+       $oid refs/remotes/origin/HEAD
+       $oid refs/remotes/origin/main
+       0000
+       EOF
+       q_to_nul <input.q >input &&
+
+       cat >expect <<-EOF &&
+       ref: refs/heads/main    HEAD
+       $oid    HEAD
+       $oid    refs/heads/main
+       ref: refs/remotes/origin/main   refs/remotes/origin/HEAD
+       $oid    refs/remotes/origin/HEAD
+       $oid    refs/remotes/origin/main
+       EOF
+
+       git ls-remote --symref --upload-pack=./cat-input . >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 511ba3bd454808943c73298c7c8f44a0b5bb9c7a..25772c85c5a9a89bf247cfa41cf9d8b127572cd8 100755 (executable)
@@ -24,6 +24,15 @@ setup_repository () {
        )
 }
 
+setup_test_clone () {
+       test_dir="$1" &&
+       git clone one "$test_dir" &&
+       for r in one two three
+       do
+               git -C "$test_dir" remote add "$r" "../$r" || return 1
+       done
+}
+
 test_expect_success setup '
        setup_repository one &&
        setup_repository two &&
@@ -58,6 +67,13 @@ test_expect_success 'git fetch --all' '
         test_cmp expect output)
 '
 
+test_expect_success 'git fetch --all --no-write-fetch-head' '
+       (cd test &&
+       rm -f .git/FETCH_HEAD &&
+       git fetch --all --no-write-fetch-head &&
+       test_path_is_missing .git/FETCH_HEAD)
+'
+
 test_expect_success 'git fetch --all should continue if a remote has errors' '
        (git clone one test2 &&
         cd test2 &&
@@ -193,8 +209,165 @@ test_expect_success 'parallel' '
        test_must_fail env GIT_TRACE="$PWD/trace" \
                git fetch --jobs=2 --multiple one two 2>err &&
        grep "preparing to run up to 2 tasks" trace &&
-       test_i18ngrep "could not fetch .one.*128" err &&
-       test_i18ngrep "could not fetch .two.*128" err
+       test_grep "could not fetch .one.*128" err &&
+       test_grep "could not fetch .two.*128" err
+'
+
+test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
+       (cd test &&
+        git fetch --multiple --jobs=0)
+'
+
+create_fetch_all_expect () {
+       cat >expect <<-\EOF
+         one/main
+         one/side
+         origin/HEAD -> origin/main
+         origin/main
+         origin/side
+         three/another
+         three/main
+         three/side
+         two/another
+         two/main
+         two/side
+       EOF
+}
+
+for fetch_all in true false
+do
+       test_expect_success "git fetch --all (works with fetch.all = $fetch_all)" '
+               test_dir="test_fetch_all_$fetch_all" &&
+               setup_test_clone "$test_dir" &&
+               (
+                       cd "$test_dir" &&
+                       git config fetch.all $fetch_all &&
+                       git fetch --all &&
+                       create_fetch_all_expect &&
+                       git branch -r >actual &&
+                       test_cmp expect actual
+               )
+       '
+done
+
+test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '
+       setup_test_clone test9 &&
+       (
+               cd test9 &&
+               git config fetch.all true &&
+               git fetch &&
+               git branch -r >actual &&
+               create_fetch_all_expect &&
+               test_cmp expect actual
+       )
+'
+
+create_fetch_one_expect () {
+       cat >expect <<-\EOF
+         one/main
+         one/side
+         origin/HEAD -> origin/main
+         origin/main
+         origin/side
+       EOF
+}
+
+test_expect_success 'git fetch one (explicit remote overrides fetch.all)' '
+       setup_test_clone test10 &&
+       (
+               cd test10 &&
+               git config fetch.all true &&
+               git fetch one &&
+               create_fetch_one_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+create_fetch_two_as_origin_expect () {
+       cat >expect <<-\EOF
+         origin/HEAD -> origin/main
+         origin/another
+         origin/main
+         origin/side
+       EOF
+}
+
+test_expect_success 'git config fetch.all false (fetch only default remote)' '
+       setup_test_clone test11 &&
+       (
+               cd test11 &&
+               git config fetch.all false &&
+               git remote set-url origin ../two &&
+               git fetch &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+for fetch_all in true false
+do
+       test_expect_success "git fetch --no-all (fetch only default remote with fetch.all = $fetch_all)" '
+               test_dir="test_no_all_fetch_all_$fetch_all" &&
+               setup_test_clone "$test_dir" &&
+               (
+                       cd "$test_dir" &&
+                       git config fetch.all $fetch_all &&
+                       git remote set-url origin ../two &&
+                       git fetch --no-all &&
+                       create_fetch_two_as_origin_expect &&
+                       git branch -r >actual &&
+                       test_cmp expect actual
+               )
+       '
+done
+
+test_expect_success 'git fetch --no-all (fetch only default remote without fetch.all)' '
+       setup_test_clone test12 &&
+       (
+               cd test12 &&
+               git config --unset-all fetch.all || true &&
+               git remote set-url origin ../two &&
+               git fetch --no-all &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --all --no-all (fetch only default remote)' '
+       setup_test_clone test13 &&
+       (
+               cd test13 &&
+               git remote set-url origin ../two &&
+               git fetch --all --no-all &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --no-all one (fetch only explicit remote)' '
+       setup_test_clone test14 &&
+       (
+               cd test14 &&
+               git fetch --no-all one &&
+               create_fetch_one_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --no-all --all (fetch all remotes)' '
+       setup_test_clone test15 &&
+       (
+               cd test15 &&
+               git fetch --no-all --all &&
+               create_fetch_all_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
 '
 
 test_done
index 79dc470c014a15224fef5f05e648cc178ef24910..2e7c0e1648f7aa4b879edcbe7cf15cde7583ae65 100755 (executable)
@@ -120,6 +120,17 @@ test_expect_success setup '
 
 '
 
+for cmd in push fetch
+do
+       for opt in ipv4 ipv6
+       do
+               test_expect_success "reject 'git $cmd --no-$opt'" '
+                       test_must_fail git $cmd --no-$opt 2>err &&
+                       grep "unknown option .no-$opt" err
+               '
+       done
+done
+
 test_expect_success 'fetch without wildcard' '
        mk_empty testrepo &&
        (
@@ -216,7 +227,7 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f
        GIT_TEST_PROTOCOL_VERSION=0 GIT_TRACE2_EVENT="$(pwd)/event" \
                git -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
        grep_wrote 5 event && # 2 commits, 2 trees, 1 blob
-       test_i18ngrep "push negotiation failed" err
+       test_grep "push negotiation failed" err
 '
 
 test_expect_success 'push with negotiation does not attempt to fetch submodules' '
@@ -401,6 +412,11 @@ test_expect_success 'push with ambiguity' '
 
 '
 
+test_expect_success 'push with onelevel ref' '
+       mk_test testrepo heads/main &&
+       test_must_fail git push testrepo HEAD:refs/onelevel
+'
+
 test_expect_success 'push with colon-less refspec (1)' '
 
        mk_test testrepo heads/frotz tags/frotz &&
@@ -898,6 +914,13 @@ test_expect_success 'push --delete refuses empty string' '
        test_must_fail git push testrepo --delete ""
 '
 
+test_expect_success 'push --delete onelevel refspecs' '
+       mk_test testrepo heads/main &&
+       git -C testrepo update-ref refs/onelevel refs/heads/main &&
+       git push testrepo --delete refs/onelevel &&
+       test_must_fail git -C testrepo rev-parse --verify refs/onelevel
+'
+
 test_expect_success 'warn on push to HEAD of non-bare repository' '
        mk_test testrepo heads/main &&
        (
@@ -1244,7 +1267,7 @@ test_expect_success 'fetch exact SHA1' '
                # fetching the hidden object should fail by default
                test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
                        git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
-               test_i18ngrep "Server does not allow request for unadvertised object" err &&
+               test_grep "Server does not allow request for unadvertised object" err &&
                test_must_fail git rev-parse --verify refs/heads/copy &&
 
                # the server side can allow it to succeed
@@ -1346,7 +1369,7 @@ do
                                git fetch ../testrepo/.git $SHA1_3 2>err &&
                        # ideally we would insist this be on a "remote error:"
                        # line, but it is racy; see the commit message
-                       test_i18ngrep "not our ref.*$SHA1_3\$" err
+                       test_grep "not our ref.*$SHA1_3\$" err
                )
        '
 done
@@ -1384,7 +1407,7 @@ test_expect_success 'peeled advertisements are not considered ref tips' '
        oid=$(git -C testrepo rev-parse mytag^{commit}) &&
        test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
                git fetch testrepo $oid 2>err &&
-       test_i18ngrep "Server does not allow request for unadvertised object" err
+       test_grep "Server does not allow request for unadvertised object" err
 '
 
 test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
@@ -1853,55 +1876,24 @@ test_expect_success 'refuse to push a hidden ref, and make sure do not pollute t
        test_dir_is_empty testrepo/.git/objects/pack
 '
 
-test_expect_success LIBCURL 'fetch warns or fails when using username:password' '
-       message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
-       test_must_fail git -c transfer.credentialsInUrl=allow fetch https://username:password@localhost 2>err &&
-       ! grep "$message" err &&
-
-       test_must_fail git -c transfer.credentialsInUrl=warn fetch https://username:password@localhost 2>err &&
-       grep "warning: $message" err >warnings &&
-       test_line_count = 3 warnings &&
-
-       test_must_fail git -c transfer.credentialsInUrl=die fetch https://username:password@localhost 2>err &&
-       grep "fatal: $message" err >warnings &&
-       test_line_count = 1 warnings &&
-
-       test_must_fail git -c transfer.credentialsInUrl=die fetch https://username:@localhost 2>err &&
-       grep "fatal: $message" err >warnings &&
-       test_line_count = 1 warnings
-'
-
-
-test_expect_success LIBCURL 'push warns or fails when using username:password' '
-       message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
-       test_must_fail git -c transfer.credentialsInUrl=allow push https://username:password@localhost 2>err &&
-       ! grep "$message" err &&
-
-       test_must_fail git -c transfer.credentialsInUrl=warn push https://username:password@localhost 2>err &&
-       grep "warning: $message" err >warnings &&
-       test_must_fail git -c transfer.credentialsInUrl=die push https://username:password@localhost 2>err &&
-       grep "fatal: $message" err >warnings &&
-       test_line_count = 1 warnings
-'
-
 test_expect_success 'push with config push.useBitmaps' '
        mk_test testrepo heads/main &&
        git checkout main &&
        test_unconfig push.useBitmaps &&
        GIT_TRACE2_EVENT="$PWD/default" \
-       git push testrepo main:test &&
+       git push --quiet testrepo main:test &&
        test_subcommand git pack-objects --all-progress-implied --revs --stdout \
                --thin --delta-base-offset -q <default &&
 
        test_config push.useBitmaps true &&
        GIT_TRACE2_EVENT="$PWD/true" \
-       git push testrepo main:test2 &&
+       git push --quiet testrepo main:test2 &&
        test_subcommand git pack-objects --all-progress-implied --revs --stdout \
                --thin --delta-base-offset -q <true &&
 
        test_config push.useBitmaps false &&
        GIT_TRACE2_EVENT="$PWD/false" \
-       git push testrepo main:test3 &&
+       git push --quiet testrepo main:test3 &&
        test_subcommand git pack-objects --all-progress-implied --revs --stdout \
                --thin --delta-base-offset -q --no-use-bitmap-index <false
 '
index a448e169bd029f21ec77cd89dc087c8e637578f8..6d4944a72826401d3e698ad4c1b6982c4a233eae 100755 (executable)
@@ -5,6 +5,7 @@ test_description='pushing to a mirror repository'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 D=$(pwd)
index 0b72112fb10759bb1d5e9de912ae1b2038df1d00..47534f1062d203a9b823d545740004e2002162ff 100755 (executable)
@@ -31,7 +31,7 @@ test_pull_autostash_fail () {
        echo dirty >new_file &&
        git add new_file &&
        test_must_fail git pull "$@" . copy 2>err &&
-       test_i18ngrep -E "uncommitted changes.|overwritten by merge:" err
+       test_grep -E "uncommitted changes.|overwritten by merge:" err
 }
 
 test_expect_success setup '
@@ -151,7 +151,7 @@ test_expect_success 'fail if wildcard spec does not match any refs' '
        echo file >expect &&
        test_cmp expect file &&
        test_must_fail git pull . "refs/nonexisting1/*:refs/nonexisting2/*" 2>err &&
-       test_i18ngrep "no candidates for merging" err &&
+       test_grep "no candidates for merging" err &&
        test_cmp expect file
 '
 
@@ -164,7 +164,7 @@ test_expect_success 'fail if no branches specified with non-default remote' '
        test_cmp expect file &&
        test_config branch.test.remote origin &&
        test_must_fail git pull test_remote 2>err &&
-       test_i18ngrep "specify a branch on the command line" err &&
+       test_grep "specify a branch on the command line" err &&
        test_cmp expect file
 '
 
@@ -176,7 +176,7 @@ test_expect_success 'fail if not on a branch' '
        echo file >expect &&
        test_cmp expect file &&
        test_must_fail git pull 2>err &&
-       test_i18ngrep "not currently on a branch" err &&
+       test_grep "not currently on a branch" err &&
        test_cmp expect file
 '
 
@@ -189,7 +189,7 @@ test_expect_success 'fail if no configuration for current branch' '
        echo file >expect &&
        test_cmp expect file &&
        test_must_fail git pull 2>err &&
-       test_i18ngrep "no tracking information" err &&
+       test_grep "no tracking information" err &&
        test_cmp expect file
 '
 
@@ -202,7 +202,7 @@ test_expect_success 'pull --all: fail if no configuration for current branch' '
        echo file >expect &&
        test_cmp expect file &&
        test_must_fail git pull --all 2>err &&
-       test_i18ngrep "There is no tracking information" err &&
+       test_grep "There is no tracking information" err &&
        test_cmp expect file
 '
 
@@ -214,7 +214,7 @@ test_expect_success 'fail if upstream branch does not exist' '
        echo file >expect &&
        test_cmp expect file &&
        test_must_fail git pull 2>err &&
-       test_i18ngrep "no such ref was fetched" err &&
+       test_grep "no such ref was fetched" err &&
        test_cmp expect file
 '
 
@@ -248,13 +248,13 @@ test_expect_success 'fail if the index has unresolved entries' '
        test_file_not_empty unmerged &&
        cp file expected &&
        test_must_fail git pull . second 2>err &&
-       test_i18ngrep "Pulling is not possible because you have unmerged files." err &&
+       test_grep "Pulling is not possible because you have unmerged files." err &&
        test_cmp expected file &&
        git add file &&
        git ls-files -u >unmerged &&
        test_must_be_empty unmerged &&
        test_must_fail git pull . second 2>err &&
-       test_i18ngrep "You have not concluded your merge" err &&
+       test_grep "You have not concluded your merge" err &&
        test_cmp expected file
 '
 
@@ -264,7 +264,7 @@ test_expect_success 'fast-forwards working tree if branch head is updated' '
        echo file >expect &&
        test_cmp expect file &&
        git pull . second:third 2>err &&
-       test_i18ngrep "fetch updated the current branch head" err &&
+       test_grep "fetch updated the current branch head" err &&
        echo modified >expect &&
        test_cmp expect file &&
        test_cmp_rev third second
@@ -277,7 +277,7 @@ test_expect_success 'fast-forward fails with conflicting work tree' '
        test_cmp expect file &&
        echo conflict >file &&
        test_must_fail git pull . second:third 2>err &&
-       test_i18ngrep "Cannot fast-forward your working tree" err &&
+       test_grep "Cannot fast-forward your working tree" err &&
        echo conflict >expect &&
        test_cmp expect file &&
        test_cmp_rev third second
@@ -375,7 +375,7 @@ test_expect_success '--rebase with conflicts shows advice' '
        test_tick &&
        git commit -m "Create conflict" seq.txt &&
        test_must_fail git pull --rebase . seq 2>err >out &&
-       test_i18ngrep "Resolve all conflicts manually" err
+       test_grep "Resolve all conflicts manually" err
 '
 
 test_expect_success 'failed --rebase shows advice' '
@@ -389,14 +389,14 @@ test_expect_success 'failed --rebase shows advice' '
        git checkout -f -b fails-to-rebase HEAD^ &&
        test_commit v2-without-cr file "2" file2-lf &&
        test_must_fail git pull --rebase . diverging 2>err >out &&
-       test_i18ngrep "Resolve all conflicts manually" err
+       test_grep "Resolve all conflicts manually" err
 '
 
 test_expect_success '--rebase fails with multiple branches' '
        git reset --hard before-rebase &&
        test_must_fail git pull --rebase . copy main 2>err &&
        test_cmp_rev HEAD before-rebase &&
-       test_i18ngrep "Cannot rebase onto multiple branches" err &&
+       test_grep "Cannot rebase onto multiple branches" err &&
        echo modified >expect &&
        git show HEAD:file >actual &&
        test_cmp expect actual
@@ -520,7 +520,7 @@ test_expect_success 'pull --rebase warns on --verify-signatures' '
        echo new >expect &&
        git show HEAD:file2 >actual &&
        test_cmp expect actual &&
-       test_i18ngrep "ignoring --verify-signatures for rebase" err
+       test_grep "ignoring --verify-signatures for rebase" err
 '
 
 test_expect_success 'pull --rebase does not warn on --no-verify-signatures' '
@@ -530,7 +530,7 @@ test_expect_success 'pull --rebase does not warn on --no-verify-signatures' '
        echo new >expect &&
        git show HEAD:file2 >actual &&
        test_cmp expect actual &&
-       test_i18ngrep ! "verify-signatures" err
+       test_grep ! "verify-signatures" err
 '
 
 # add a feature branch, keep-merge, that is merged into main, so the
@@ -740,7 +740,7 @@ test_expect_success 'pull --rebase fails on unborn branch with staged changes' '
                test_cmp expect actual &&
                git show :staged-file >actual &&
                test_cmp expect actual &&
-               test_i18ngrep "unborn branch with changes added to the index" err
+               test_grep "unborn branch with changes added to the index" err
        )
 '
 
index 264de29c35c11cbe09d31d57629aabaae0423b35..db00c4336b1671158f69b9711e247d9300b5cac8 100755 (executable)
@@ -5,6 +5,7 @@ test_description='pull options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -93,7 +94,7 @@ test_expect_success 'git pull --no-write-fetch-head fails' '
        (cd clonedwfh && git init &&
        test_expect_code 129 git pull --no-write-fetch-head "../parent" >out 2>err &&
        test_must_be_empty out &&
-       test_i18ngrep "no-write-fetch-head" err)
+       test_grep "no-write-fetch-head" err)
 '
 
 test_expect_success 'git pull --force' '
@@ -142,7 +143,7 @@ test_expect_success 'git pull --dry-run' '
                cd clonedry &&
                git pull --dry-run ../parent &&
                test_path_is_missing .git/FETCH_HEAD &&
-               test_path_is_missing .git/refs/heads/main &&
+               test_ref_missing refs/heads/main &&
                test_path_is_missing .git/index &&
                test_path_is_missing file
        )
@@ -156,7 +157,7 @@ test_expect_success 'git pull --all --dry-run' '
                git remote add origin ../parent &&
                git pull --all --dry-run &&
                test_path_is_missing .git/FETCH_HEAD &&
-               test_path_is_missing .git/refs/remotes/origin/main &&
+               test_ref_missing refs/remotes/origin/main &&
                test_path_is_missing .git/index &&
                test_path_is_missing file
        )
index bcff460d0a2ba2b6b9965e7bff58bc26033231ab..cc5496e28fd0bcbf32badb583272c464ec88fae8 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='pulling from symlinked subdir'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # The scenario we are building:
@@ -78,7 +79,9 @@ test_expect_success SYMLINKS 'pushing from symlinked subdir' '
                git commit -m push ./file &&
                git push
        ) &&
-       test push = $(git show HEAD:subdir/file)
+       echo push >expect &&
+       git show HEAD:subdir/file >actual &&
+       test_cmp expect actual
 '
 
 test_done
index fdb429205643b8ccb03529795f5b6f6aef63d93a..1f859ade16251d6e2c61dd84ebb14aa35bc41ca1 100755 (executable)
@@ -4,6 +4,7 @@ test_description='push with --set-upstream'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -60,12 +61,20 @@ test_expect_success 'push -u :topic_2' '
        check_config topic_2 upstream refs/heads/other2
 '
 
-test_expect_success 'push -u --all' '
+test_expect_success 'push -u --all(the same behavior with--branches)' '
        git branch all1 &&
        git branch all2 &&
        git push -u --all &&
        check_config all1 upstream refs/heads/all1 &&
-       check_config all2 upstream refs/heads/all2
+       check_config all2 upstream refs/heads/all2 &&
+       git config --get-regexp branch.all* > expect &&
+       git config --remove-section branch.all1 &&
+       git config --remove-section branch.all2 &&
+       git push -u --branches &&
+       check_config all1 upstream refs/heads/all1 &&
+       check_config all2 upstream refs/heads/all2 &&
+       git config --get-regexp branch.all* > actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'push -u HEAD' '
@@ -78,7 +87,7 @@ test_expect_success TTY 'progress messages go to tty' '
        ensure_fresh_upstream &&
 
        test_terminal git push -u upstream main >out 2>err &&
-       test_i18ngrep "Writing objects" err
+       test_grep "Writing objects" err
 '
 
 test_expect_success 'progress messages do not go to non-tty' '
@@ -86,7 +95,7 @@ test_expect_success 'progress messages do not go to non-tty' '
 
        # skip progress messages, since stderr is non-tty
        git push -u upstream main >out 2>err &&
-       test_i18ngrep ! "Writing objects" err
+       test_grep ! "Writing objects" err
 '
 
 test_expect_success 'progress messages go to non-tty (forced)' '
@@ -94,22 +103,22 @@ test_expect_success 'progress messages go to non-tty (forced)' '
 
        # force progress messages to stderr, even though it is non-tty
        git push -u --progress upstream main >out 2>err &&
-       test_i18ngrep "Writing objects" err
+       test_grep "Writing objects" err
 '
 
 test_expect_success TTY 'push -q suppresses progress' '
        ensure_fresh_upstream &&
 
        test_terminal git push -u -q upstream main >out 2>err &&
-       test_i18ngrep ! "Writing objects" err
+       test_grep ! "Writing objects" err
 '
 
 test_expect_success TTY 'push --no-progress suppresses progress' '
        ensure_fresh_upstream &&
 
        test_terminal git push -u --no-progress upstream main >out 2>err &&
-       test_i18ngrep ! "Unpacking objects" err &&
-       test_i18ngrep ! "Writing objects" err
+       test_grep ! "Unpacking objects" err &&
+       test_grep ! "Writing objects" err
 '
 
 test_expect_success TTY 'quiet push' '
index 45815f737850b61684f648af9d984ac16ea0944e..3a28f1ded5fdcce6b2a55aa6122e27489d942265 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='tagopt variable affects "git fetch" and is overridden by commandline.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 setup_clone () {
index 3c44f1961220b376a0156f020588788ab986e131..7ab220fa3135a1ecbaa8f69a5db8a9b1298f4ce1 100755 (executable)
@@ -167,6 +167,19 @@ test_expect_success "fetch --recurse-submodules recurses into submodules" '
        verify_fetch_result actual.err
 '
 
+test_expect_success "fetch --recurse-submodules honors --no-write-fetch-head" '
+       (
+               cd downstream &&
+               git submodule foreach --recursive \
+               sh -c "cd \"\$(git rev-parse --git-dir)\" && rm -f FETCH_HEAD" &&
+
+               git fetch --recurse-submodules --no-write-fetch-head &&
+
+               git submodule foreach --recursive \
+               sh -c "cd \"\$(git rev-parse --git-dir)\" && ! test -f FETCH_HEAD"
+       )
+'
+
 test_expect_success "submodule.recurse option triggers recursive fetch" '
        add_submodule_commits &&
        (
@@ -178,6 +191,7 @@ test_expect_success "submodule.recurse option triggers recursive fetch" '
 '
 
 test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" '
+       test_when_finished "rm -f trace.out" &&
        add_submodule_commits &&
        (
                cd downstream &&
@@ -705,17 +719,30 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
 
 test_expect_success 'fetching submodules respects parallel settings' '
        git config fetch.recurseSubmodules true &&
+       test_when_finished "rm -f downstream/trace.out" &&
        (
                cd downstream &&
                GIT_TRACE=$(pwd)/trace.out git fetch &&
                grep "1 tasks" trace.out &&
+               >trace.out &&
+
                GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 &&
                grep "7 tasks" trace.out &&
+               >trace.out &&
+
                git config submodule.fetchJobs 8 &&
                GIT_TRACE=$(pwd)/trace.out git fetch &&
                grep "8 tasks" trace.out &&
+               >trace.out &&
+
                GIT_TRACE=$(pwd)/trace.out git fetch --jobs 9 &&
-               grep "9 tasks" trace.out
+               grep "9 tasks" trace.out &&
+               >trace.out &&
+
+               GIT_TRACE=$(pwd)/trace.out git -c submodule.fetchJobs=0 fetch &&
+               grep "preparing to run up to [0-9]* tasks" trace.out &&
+               ! grep "up to 0 tasks" trace.out &&
+               >trace.out
        )
 '
 
@@ -744,7 +771,7 @@ test_expect_success 'fetching submodule into a broken repository' '
        git -C dst fetch --recurse-submodules &&
 
        # Break the receiving submodule
-       rm -f dst/sub/.git/HEAD &&
+       test-tool -C dst/sub ref-store main delete-refs REF_NO_DEREF msg HEAD &&
 
        # NOTE: without the fix the following tests will recurse forever!
        # They should terminate with an error.
@@ -1153,4 +1180,17 @@ test_expect_success 'fetch --all with --recurse-submodules with multiple' '
        test_line_count = 2 fetch-subs
 '
 
+test_expect_success "fetch --all with --no-recurse-submodules only fetches superproject" '
+       test_when_finished "rm -rf src_clone" &&
+
+       git clone --recurse-submodules src src_clone &&
+       (
+               cd src_clone &&
+               git remote add secondary ../src &&
+               git config submodule.recurse true &&
+               git fetch --all --no-recurse-submodules 2>../fetch-log
+       ) &&
+       ! grep "Fetching submodule" fetch-log
+'
+
 test_done
index e2770e4541f94d60dba1ae8698abf3496a6a50e9..98ece27c6a0819093cd54ce19916456ce2136dae 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test fetching of oddly-named refs'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # afterwards we will have:
index 284e20fefda861f450d79a3f849cc9ddc2afa65b..14f7eced9a0b43bf8a565e3817f2ef802389a3ba 100755 (executable)
@@ -179,7 +179,7 @@ test_expect_success 'push from/to new branch succeeds with simple if push.autoSe
 test_expect_success '"matching" fails if none match' '
        git init --bare empty &&
        test_must_fail git push empty : 2>actual &&
-       test_i18ngrep "Perhaps you should specify a branch" actual
+       test_grep "Perhaps you should specify a branch" actual
 '
 
 test_expect_success 'push ambiguously named branch with upstream, matching and simple' '
index ce85fd30ad195eecd061ad0ec734542daf2d7c59..0247137cb36474c22f6ce5ed045379fa43f1a3ea 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='detect some push errors early (before contacting remote)'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
index 7c1460eaa99865306ddfb5254584eba275e17a21..7172780d5502a9768e00ed84c65f13d618ad0dc8 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='errors in upload-pack'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 D=$(pwd)
@@ -35,8 +36,8 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' '
        printf "%04xwant %s\n00000009done\n0000" \
                $(($hexsz + 10)) $head >input &&
        test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
-       test_i18ngrep "unable to read" output.err &&
-       test_i18ngrep "pack-objects died" output.err
+       test_grep "unable to read" output.err &&
+       test_grep "pack-objects died" output.err
 '
 
 test_expect_success 'corrupt repo differently' '
index 3f58b515cee5d8f8ec72d22e363512b5a5a40c1f..f3fff557447c3e2e5c46878e7f0415c38d29e970 100755 (executable)
@@ -311,7 +311,7 @@ test_expect_success 'submodule entry pointing at a tag is error' '
        git -C work commit -m "bad commit" &&
        test_when_finished "git -C work reset --hard HEAD^" &&
        test_must_fail git -C work push --recurse-submodules=on-demand ../pub.git main 2>err &&
-       test_i18ngrep "is a tag, not a commit" err
+       test_grep "is a tag, not a commit" err
 '
 
 test_expect_success 'push fails if recurse submodules option passed as yes' '
@@ -512,6 +512,56 @@ test_expect_success 'push only unpushed submodules recursively' '
        test_cmp expected_pub actual_pub
 '
 
+setup_subsub () {
+       git init upstream &&
+       git init upstream/sub &&
+       git init upstream/sub/deepsub &&
+       test_commit -C upstream/sub/deepsub innermost &&
+       git -C upstream/sub submodule add ./deepsub deepsub &&
+       git -C upstream/sub commit -m middle &&
+       git -C upstream submodule add ./sub sub &&
+       git -C upstream commit -m outermost &&
+
+       git -c protocol.file.allow=always clone --recurse-submodules upstream downstream &&
+       git -C downstream/sub/deepsub checkout -b downstream-branch &&
+       git -C downstream/sub checkout -b downstream-branch &&
+       git -C downstream checkout -b downstream-branch
+}
+
+new_downstream_commits () {
+       test_commit -C downstream/sub/deepsub new-innermost &&
+       git -C downstream/sub add deepsub &&
+       git -C downstream/sub commit -m new-middle &&
+       git -C downstream add sub &&
+       git -C downstream commit -m new-outermost
+}
+
+test_expect_success 'push with push.recurseSubmodules=only on superproject' '
+       test_when_finished rm -rf upstream downstream &&
+       setup_subsub &&
+       new_downstream_commits &&
+       git -C downstream config push.recurseSubmodules only &&
+       git -C downstream push origin downstream-branch &&
+
+       test_must_fail git -C upstream rev-parse refs/heads/downstream-branch &&
+       git -C upstream/sub rev-parse refs/heads/downstream-branch &&
+       test_must_fail git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch
+'
+
+test_expect_success 'push with push.recurseSubmodules=only on superproject and top-level submodule' '
+       test_when_finished rm -rf upstream downstream &&
+       setup_subsub &&
+       new_downstream_commits &&
+       git -C downstream config push.recurseSubmodules only &&
+       git -C downstream/sub config push.recurseSubmodules only &&
+       git -C downstream push origin downstream-branch 2> err &&
+
+       test_must_fail git -C upstream rev-parse refs/heads/downstream-branch &&
+       git -C upstream/sub rev-parse refs/heads/downstream-branch &&
+       git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch &&
+       grep "recursing into submodule with push.recurseSubmodules=only; using on-demand instead" err
+'
+
 test_expect_success 'push propagating the remotes name to a submodule' '
        git -C work remote add origin ../pub.git &&
        git -C work remote add pub ../pub.git &&
index 7c0a148e73c9e54003ed0fbad0805fe00f31eed0..b4bc24691c8150e3b0829cb97af1911d13d02504 100755 (executable)
@@ -68,13 +68,13 @@ test_expect_success 'talking with a receiver without push certificate support' '
 test_expect_success 'push --signed fails with a receiver without push certificate support' '
        prepare_dst &&
        test_must_fail git push --signed dst noop ff +noff 2>err &&
-       test_i18ngrep "the receiving end does not support" err
+       test_grep "the receiving end does not support" err
 '
 
 test_expect_success 'push --signed=1 is accepted' '
        prepare_dst &&
        test_must_fail git push --signed=1 dst noop ff +noff 2>err &&
-       test_i18ngrep "the receiving end does not support" err
+       test_grep "the receiving end does not support" err
 '
 
 test_expect_success GPG 'no certificate for a signed push with no update' '
@@ -378,7 +378,7 @@ test_expect_success GPG 'failed atomic push does not execute GPG' '
                        --signed --atomic --porcelain \
                        dst noop ff noff >out 2>err &&
 
-       test_i18ngrep ! "gpg failed to sign" err &&
+       test_grep ! "gpg failed to sign" err &&
        cat >expect <<-EOF &&
        To dst
        =       refs/heads/noop:refs/heads/noop [up to date]
index 91f28c2f783df7391d22bd893e2488e56658515b..23bf69617007d7e97ff3641d3a5783a7ffe0352a 100755 (executable)
@@ -40,7 +40,7 @@ test_expect_success 'fetch conflict: config vs. config' '
                "+refs/heads/branch2:refs/remotes/origin/branch1" && (
                cd ccc &&
                test_must_fail git fetch origin 2>error &&
-               test_i18ngrep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
+               test_grep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
        )
 '
 
@@ -67,7 +67,7 @@ test_expect_success 'fetch conflict: arg vs. arg' '
                test_must_fail git fetch origin \
                        refs/heads/*:refs/remotes/origin/* \
                        refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
-               test_i18ngrep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
+               test_grep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
        )
 '
 
@@ -78,8 +78,8 @@ test_expect_success 'fetch conflict: criss-cross args' '
                git fetch origin \
                        refs/heads/branch1:refs/remotes/origin/branch2 \
                        refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
-               test_i18ngrep "warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2" error &&
-               test_i18ngrep "warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1" error
+               test_grep "warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2" error &&
+               test_grep "warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1" error
        )
 '
 
index fbad2d5ff5e9e3a7d23a9117b48edf0c9a2b59d8..df758e187dfdc2fc869ad5cb747d895a145d5e91 100755 (executable)
@@ -36,28 +36,6 @@ test_expect_success 'setup remote repository' '
 
 setup_askpass_helper
 
-cat >exp <<EOF
-GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
-EOF
-test_expect_success 'no empty path components' '
-       # Clear the log, so that it does not affect the "used receive-pack
-       # service" test which reads the log too.
-       test_when_finished ">\"\$HTTPD_ROOT_PATH\"/access.log" &&
-
-       # In the URL, add a trailing slash, and see if git appends yet another
-       # slash.
-       cd "$ROOT_PATH" &&
-       git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
-
-       # NEEDSWORK: If the overspecification of the expected result is reduced, we
-       # might be able to run this test in all protocol versions.
-       if test "$GIT_TEST_PROTOCOL_VERSION" = 0
-       then
-               check_access_log exp
-       fi
-'
-
 test_expect_success 'clone remote repository' '
        rm -rf test_repo_clone &&
        git clone $HTTPD_URL/smart/test_repo.git test_repo_clone &&
@@ -67,6 +45,10 @@ test_expect_success 'clone remote repository' '
 '
 
 test_expect_success 'push to remote repository (standard)' '
+       # Clear the log, so that the "used receive-pack service" test below
+       # sees just what we did here.
+       >"$HTTPD_ROOT_PATH"/access.log &&
+
        cd "$ROOT_PATH"/test_repo_clone &&
        : >path2 &&
        git add path2 &&
@@ -80,6 +62,15 @@ test_expect_success 'push to remote repository (standard)' '
         test $HEAD = $(git rev-parse --verify HEAD))
 '
 
+test_expect_success 'used receive-pack service' '
+       cat >exp <<-\EOF &&
+       GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+       POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+       EOF
+
+       check_access_log exp
+'
+
 test_expect_success 'push to remote repository (standard) with sending Accept-Language' '
        cat >exp <<-\EOF &&
        => Send header: Accept-Language: ko-KR, *;q=0.9
@@ -141,28 +132,6 @@ test_expect_success 'rejected update prints status' '
 '
 rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
 
-cat >exp <<EOF
-GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-EOF
-test_expect_success 'used receive-pack service' '
-       # NEEDSWORK: If the overspecification of the expected result is reduced, we
-       # might be able to run this test in all protocol versions.
-       if test "$GIT_TEST_PROTOCOL_VERSION" = 0
-       then
-               check_access_log exp
-       fi
-'
-
 test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
        "$ROOT_PATH"/test_repo_clone main               success
 
@@ -184,7 +153,7 @@ test_expect_success 'push fails for non-fast-forward refs unmatched by remote he
 '
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
-       test_i18ngrep "Updates were rejected because" \
+       test_grep "Updates were rejected because" \
                output
 '
 
@@ -328,7 +297,7 @@ test_expect_success TTY 'push shows progress when stderr is a tty' '
        cd "$ROOT_PATH"/test_repo_clone &&
        test_commit noisy &&
        test_terminal git push >output 2>&1 &&
-       test_i18ngrep "^Writing objects" output
+       test_grep "^Writing objects" output
 '
 
 test_expect_success TTY 'push --quiet silences status and progress' '
@@ -342,16 +311,16 @@ test_expect_success TTY 'push --no-progress silences progress but not status' '
        cd "$ROOT_PATH"/test_repo_clone &&
        test_commit no-progress &&
        test_terminal git push --no-progress >output 2>&1 &&
-       test_i18ngrep "^To http" output &&
-       test_i18ngrep ! "^Writing objects" output
+       test_grep "^To http" output &&
+       test_grep ! "^Writing objects" output
 '
 
 test_expect_success 'push --progress shows progress to non-tty' '
        cd "$ROOT_PATH"/test_repo_clone &&
        test_commit progress &&
        git push --progress >output 2>&1 &&
-       test_i18ngrep "^To http" output &&
-       test_i18ngrep "^Writing objects" output
+       test_grep "^To http" output &&
+       test_grep "^Writing objects" output
 '
 
 test_expect_success 'http push gives sane defaults to reflog' '
@@ -520,10 +489,10 @@ test_expect_success 'colorize errors/hints' '
                -c color.push=always \
                push origin origin/main^:main 2>act &&
        test_decode_color <act >decoded &&
-       test_i18ngrep "<RED>.*rejected.*<RESET>" decoded &&
-       test_i18ngrep "<RED>error: failed to push some refs" decoded &&
-       test_i18ngrep "<YELLOW>hint: " decoded &&
-       test_i18ngrep ! "^hint: " decoded
+       test_grep "<RED>.*rejected.*<RESET>" decoded &&
+       test_grep "<RED>error: failed to push some refs" decoded &&
+       test_grep "<YELLOW>hint: " decoded &&
+       test_grep ! "^hint: " decoded
 '
 
 test_expect_success 'report error server does not provide ref status' '
index 70431122a40360c265dc28627c4208dc360c000c..04b47ad84a86ab3407ba26f069d934ed7dcb5dec 100755 (executable)
@@ -117,7 +117,10 @@ test_expect_success 'atomic push fails if one branch fails' '
                test_commit five &&
                git checkout main &&
                test_commit six &&
-               test_must_fail git push --atomic --all up
+               test_must_fail git push --atomic --all up >output-all 2>&1 &&
+               # --all and --branches have the same behavior when be combined with --atomic
+               test_must_fail git push --atomic --branches up >output-branches 2>&1 &&
+               test_cmp output-all output-branches
        ) &&
        test_refs main HEAD@{7} &&
        test_refs second HEAD@{4}
index 54f54f8d2ebde39c846e1c193e93e13e8b509618..1a9e14bbccd3c572d86f5cb15afef37abd8d3d67 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test custom script in place of pack-objects'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create some history to fetch' '
index a158e7d2c011f464c90ff69e7eda1c6ae0669ece..fb13549da7f305b88da0f0bdcf3d791907e96a08 100755 (executable)
@@ -252,7 +252,7 @@ test_expect_success 'push option denied properly by http server' '
        mk_http_pair false &&
        test_commit -C test_http_clone one &&
        test_must_fail git -C test_http_clone push --push-option=asdf origin main 2>actual &&
-       test_i18ngrep "the receiving end does not support push options" actual &&
+       test_grep "the receiving end does not support push options" actual &&
        git -C test_http_clone push origin main
 '
 
index 0b0e987fdb73fcd7d8f54c393ee7deb15a4b5b8c..9fc9ba552f1db320a595dcf7b5b23c31b5c855b1 100755 (executable)
@@ -1,16 +1,34 @@
 #!/bin/sh
 
 test_description='check receive input limits'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Let's run tests with different unpack limits: 1 and 10000
 # When the limit is 1, `git receive-pack` will call `git index-pack`.
 # When the limit is 10000, `git receive-pack` will call `git unpack-objects`.
 
+validate_store_type () {
+       git -C dest count-objects -v >actual &&
+       case "$store_type" in
+       index)
+               grep "^count: 0$" actual ;;
+       unpack)
+               grep "^packs: 0$" actual ;;
+       esac || {
+               echo "store_type is $store_type"
+               cat actual
+               false;
+       }
+}
+
 test_pack_input_limit () {
-       case "$1" in
-       index) unpack_limit=1 ;;
-       unpack) unpack_limit=10000 ;;
+       store_type=$1
+
+       case "$store_type" in
+       index) unpack_limit=1 other_limit=10000 ;;
+       unpack) unpack_limit=10000 other_limit=1 ;;
        esac
 
        test_expect_success 'prepare destination repository' '
@@ -41,6 +59,19 @@ test_pack_input_limit () {
                git --git-dir=dest config receive.maxInputSize 0 &&
                git push dest HEAD
        '
+
+       test_expect_success 'prepare destination repository (once more)' '
+               rm -fr dest &&
+               git --bare init dest
+       '
+
+       test_expect_success 'receive trumps transfer' '
+               git --git-dir=dest config receive.unpacklimit "$unpack_limit" &&
+               git --git-dir=dest config transfer.unpacklimit "$other_limit" &&
+               git push dest HEAD &&
+               validate_store_type
+       '
+
 }
 
 test_expect_success "create known-size (1024 bytes) commit" '
index 1876fb34e51a09f8b3d7f6d82d174044c9a16c84..9f899b8c7d7bcc63ba5fe97c513748eeba436edb 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check quarantine of objects during push'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create picky dest repo' '
index d7cf85ffeadefcc62e051cc61f9a0fbeae58cd53..4c3b32785d580fb32846306df720629aee9b3a4b 100755 (executable)
@@ -66,11 +66,11 @@ test_expect_success 'create empty remote repository' '
        setup_post_update_server_info_hook "$HTTPD_DOCUMENT_ROOT_PATH/empty.git"
 '
 
-test_expect_success 'empty dumb HTTP repository has default hash algorithm' '
+test_expect_success 'empty dumb HTTP repository falls back to SHA1' '
        test_when_finished "rm -fr clone-empty" &&
        git clone $HTTPD_URL/dumb/empty.git clone-empty &&
        git -C clone-empty rev-parse --show-object-format >empty-format &&
-       test "$(cat empty-format)" = "$(test_oid algo)"
+       test "$(cat empty-format)" = sha1
 '
 
 setup_askpass_helper
@@ -234,7 +234,7 @@ test_expect_success 'http-fetch --packfile' '
                --index-pack-arg=--keep \
                "$HTTPD_URL"/dumb/repo_pack.git/$p >out &&
 
-       grep "^keep.[0-9a-f]\{16,\}$" out &&
+       grep -E "^keep.[0-9a-f]{16,}$" out &&
        cut -c6- out >packhash &&
 
        # Ensure that the expected files are generated
@@ -376,7 +376,7 @@ test_expect_success 'git client send an empty Accept-Language' '
 
 test_expect_success 'remote-http complains cleanly about malformed urls' '
        test_must_fail git remote-http http::/example.com/repo.git 2>stderr &&
-       test_i18ngrep "url has no scheme" stderr
+       test_grep "url has no scheme" stderr
 '
 
 # NEEDSWORK: Writing commands to git-remote-curl can race against the latter
@@ -385,7 +385,7 @@ test_expect_success 'remote-http complains cleanly about malformed urls' '
 test_expect_success 'remote-http complains cleanly about empty scheme' '
        test_must_fail ok=sigpipe git ls-remote \
                http::${HTTPD_URL#http}/dumb/repo.git 2>stderr &&
-       test_i18ngrep "url has no scheme" stderr
+       test_grep "url has no scheme" stderr
 '
 
 test_expect_success 'redirects can be forbidden/allowed' '
@@ -397,7 +397,7 @@ test_expect_success 'redirects can be forbidden/allowed' '
 
 test_expect_success 'redirects are reported to stderr' '
        # just look for a snippet of the redirected-to URL
-       test_i18ngrep /dumb/ stderr
+       test_grep /dumb/ stderr
 '
 
 test_expect_success 'non-initial redirects can be forbidden' '
@@ -466,7 +466,7 @@ test_expect_success 'can redirect through non-"info/refs?service=git-upload-pack
 
 test_expect_success 'print HTTP error when any intermediate redirect throws error' '
        test_must_fail git clone "$HTTPD_URL/redir-to/502" 2> stderr &&
-       test_i18ngrep "unable to access.*/redir-to/502" stderr
+       test_grep "unable to access.*/redir-to/502" stderr
 '
 
 test_expect_success 'fetching via http alternates works' '
index 6a38294a47671dccdc81849e1385a8c6e0310488..e069737b80b7bd906afa1963f7b818ffac8ed2ca 100755 (executable)
@@ -1,13 +1,19 @@
 #!/bin/sh
 
-test_description='test smart fetching over http via http-backend'
+: ${HTTP_PROTO:=HTTP/1.1}
+test_description="test smart fetching over http via http-backend ($HTTP_PROTO)"
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
+test "$HTTP_PROTO" = "HTTP/2" && enable_http2
 start_httpd
 
+test_expect_success HTTP2 'enable client-side http/2' '
+       git config --global http.version HTTP/2
+'
+
 test_expect_success 'setup repository' '
        git config push.default matching &&
        echo content >file &&
@@ -27,35 +33,71 @@ test_expect_success 'create http-accessible bare repository' '
 setup_askpass_helper
 
 test_expect_success 'clone http repository' '
-       cat >exp <<-\EOF &&
-       > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
-       > Accept: */*
-       > Accept-Encoding: ENCODINGS
-       > Accept-Language: ko-KR, *;q=0.9
-       > Pragma: no-cache
-       < HTTP/1.1 200 OK
-       < Pragma: no-cache
-       < Cache-Control: no-cache, max-age=0, must-revalidate
-       < Content-Type: application/x-git-upload-pack-advertisement
-       > POST /smart/repo.git/git-upload-pack HTTP/1.1
-       > Accept-Encoding: ENCODINGS
-       > Content-Type: application/x-git-upload-pack-request
-       > Accept: application/x-git-upload-pack-result
-       > Accept-Language: ko-KR, *;q=0.9
-       > Content-Length: xxx
-       < HTTP/1.1 200 OK
-       < Pragma: no-cache
-       < Cache-Control: no-cache, max-age=0, must-revalidate
-       < Content-Type: application/x-git-upload-pack-result
+       if test_have_prereq HTTP2 && test "$HTTPD_PROTO" = "https"
+       then
+               # ALPN lets us immediately use HTTP/2; likewise, POSTs with
+               # bodies can use it because they do not need to upgrade
+               INITIAL_PROTO=HTTP/2
+       else
+               # either we are not using HTTP/2, or the initial
+               # request is sent via HTTP/1.1 and asks for upgrade
+               INITIAL_PROTO=HTTP/1.1
+       fi &&
+
+       cat >exp.raw <<-EOF &&
+       > GET /smart/repo.git/info/refs?service=git-upload-pack $INITIAL_PROTO
+       > accept: */*
+       > accept-encoding: ENCODINGS
+       > accept-language: ko-KR, *;q=0.9
+       > pragma: no-cache
+       {V2} > git-protocol: version=2
+       < $HTTP_PROTO 200 OK
+       < pragma: no-cache
+       < cache-control: no-cache, max-age=0, must-revalidate
+       < content-type: application/x-git-upload-pack-advertisement
+       > POST /smart/repo.git/git-upload-pack $INITIAL_PROTO
+       > accept-encoding: ENCODINGS
+       > content-type: application/x-git-upload-pack-request
+       > accept: application/x-git-upload-pack-result
+       > accept-language: ko-KR, *;q=0.9
+       {V2} > git-protocol: version=2
+       > content-length: xxx
+       < $INITIAL_PROTO 200 OK
+       < pragma: no-cache
+       < cache-control: no-cache, max-age=0, must-revalidate
+       < content-type: application/x-git-upload-pack-result
+       {V2} > POST /smart/repo.git/git-upload-pack $INITIAL_PROTO
+       {V2} > accept-encoding: ENCODINGS
+       {V2} > content-type: application/x-git-upload-pack-request
+       {V2} > accept: application/x-git-upload-pack-result
+       {V2} > accept-language: ko-KR, *;q=0.9
+       {V2} > git-protocol: version=2
+       {V2} > content-length: xxx
+       {V2} < $INITIAL_PROTO 200 OK
+       {V2} < pragma: no-cache
+       {V2} < cache-control: no-cache, max-age=0, must-revalidate
+       {V2} < content-type: application/x-git-upload-pack-result
        EOF
 
-       GIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE="ko_KR.UTF-8" \
+       if test "$GIT_TEST_PROTOCOL_VERSION" = 0
+       then
+               sed "/^{V2}/d" <exp.raw >exp
+       else
+               sed "s/^{V2} //" <exp.raw >exp
+       fi &&
+
+       GIT_TRACE_CURL=true LANGUAGE="ko_KR.UTF-8" \
                git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
        test_cmp file clone/file &&
        tr '\''\015'\'' Q <err |
+       perl -pe '\''
+               s/(Send|Recv) header: ([A-Za-z0-9-]+):/
+               "$1 header: " . lc($2) . ":"
+               /e;
+       '\'' |
        sed -e "
                s/Q\$//
-               /^[*] /d
+               /^[^<=]/d
                /^== Info:/d
                /^=> Send header, /d
                /^=> Send header:$/d
@@ -65,6 +107,8 @@ test_expect_success 'clone http repository' '
                s/= Recv header://
                /^<= Recv data/d
                /^=> Send data/d
+               /^<= Recv SSL data/d
+               /^=> Send SSL data/d
                /^$/d
                /^< $/d
 
@@ -72,36 +116,35 @@ test_expect_success 'clone http repository' '
                        s/^/> /
                }
 
-               /^> User-Agent: /d
-               /^> Host: /d
+               /^< HTTP/ {
+                       s/200$/200 OK/
+               }
+               /^< HTTP\\/1.1 101/d
+               /^[><] connection: /d
+               /^[><] upgrade: /d
+               /^> http2-settings: /d
+
+               /^> user-agent: /d
+               /^> host: /d
                /^> POST /,$ {
                        /^> Accept: [*]\\/[*]/d
                }
-               s/^> Content-Length: .*/> Content-Length: xxx/
+               s/^> content-length: .*/> content-length: xxx/
                /^> 00..want /d
                /^> 00.*done/d
 
-               /^< Server: /d
-               /^< Expires: /d
-               /^< Date: /d
-               /^< Content-Length: /d
-               /^< Transfer-Encoding: /d
+               /^< server: /d
+               /^< expires: /d
+               /^< date: /d
+               /^< content-length: /d
+               /^< transfer-encoding: /d
        " >actual &&
 
-       # NEEDSWORK: If the overspecification of the expected result is reduced, we
-       # might be able to run this test in all protocol versions.
-       if test "$GIT_TEST_PROTOCOL_VERSION" = 0
-       then
-               sed -e "s/^> Accept-Encoding: .*/> Accept-Encoding: ENCODINGS/" \
-                               actual >actual.smudged &&
-               test_cmp exp actual.smudged &&
-
-               grep "Accept-Encoding:.*gzip" actual >actual.gzip &&
-               test_line_count = 2 actual.gzip &&
+       sed -e "s/^> accept-encoding: .*/> accept-encoding: ENCODINGS/" \
+                       actual >actual.smudged &&
+       test_cmp exp actual.smudged &&
 
-               grep "Accept-Language: ko-KR, *" actual >actual.language &&
-               test_line_count = 2 actual.language
-       fi
+       grep "accept-encoding:.*gzip" actual >actual.gzip
 '
 
 test_expect_success 'fetch changes via http' '
@@ -113,19 +156,9 @@ test_expect_success 'fetch changes via http' '
 '
 
 test_expect_success 'used upload-pack service' '
-       cat >exp <<-\EOF &&
-       GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-       POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-       GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-       POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-       EOF
-
-       # NEEDSWORK: If the overspecification of the expected result is reduced, we
-       # might be able to run this test in all protocol versions.
-       if test "$GIT_TEST_PROTOCOL_VERSION" = 0
-       then
-               check_access_log exp
-       fi
+       strip_access_log >log &&
+       grep "GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/[0-9.]* 200" log &&
+       grep "POST /smart/repo.git/git-upload-pack HTTP/[0-9.]* 200" log
 '
 
 test_expect_success 'follow redirects (301)' '
@@ -242,7 +275,7 @@ test_expect_success 'GIT_SMART_HTTP can disable smart http' '
 
 test_expect_success 'invalid Content-Type rejected' '
        test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual &&
-       test_i18ngrep "not valid:" actual
+       test_grep "not valid:" actual
 '
 
 test_expect_success 'create namespaced refs' '
@@ -274,21 +307,23 @@ test_expect_success 'cookies stored in http.cookiefile when http.savecookies set
        127.0.0.1       FALSE   /smart_cookies/ FALSE   0       othername       othervalue
        EOF
        sort >expect_cookies.txt <<-\EOF &&
-
        127.0.0.1       FALSE   /smart_cookies/ FALSE   0       othername       othervalue
+       127.0.0.1       FALSE   /smart_cookies/repo.git/        FALSE   0       name    value
        127.0.0.1       FALSE   /smart_cookies/repo.git/info/   FALSE   0       name    value
        EOF
        git config http.cookiefile cookies.txt &&
        git config http.savecookies true &&
-       git ls-remote $HTTPD_URL/smart_cookies/repo.git main &&
 
-       # NEEDSWORK: If the overspecification of the expected result is reduced, we
-       # might be able to run this test in all protocol versions.
-       if test "$GIT_TEST_PROTOCOL_VERSION" = 0
-       then
-               tail -3 cookies.txt | sort >cookies_tail.txt &&
-               test_cmp expect_cookies.txt cookies_tail.txt
-       fi
+       test_when_finished "
+               git --git-dir=\"\$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" \
+                       tag -d cookie-tag
+       " &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+               tag -m "foo" cookie-tag &&
+       git fetch $HTTPD_URL/smart_cookies/repo.git cookie-tag &&
+
+       grep "^[^#]" cookies.txt | sort >cookies_stripped.txt &&
+       test_cmp expect_cookies.txt cookies_stripped.txt
 '
 
 test_expect_success 'transfer.hiderefs works over smart-http' '
@@ -324,7 +359,9 @@ create_tags () {
 
        # now assign tags to all the dangling commits we created above
        tag=$(perl -e "print \"bla\" x 30") &&
-       sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+       sed -e "s|^:\([^ ]*\) \(.*\)$|create refs/tags/$tag-\1 \2|" <marks >input &&
+       git update-ref --stdin <input &&
+       rm input
 }
 
 test_expect_success 'create 2,000 tags in the repo' '
@@ -347,7 +384,10 @@ test_expect_success CMDLINE_LIMIT \
 test_expect_success 'large fetch-pack requests can be sent using chunked encoding' '
        GIT_TRACE_CURL=true git -c http.postbuffer=65536 \
                clone --bare "$HTTPD_URL/smart/repo.git" split.git 2>err &&
-       grep "^=> Send header: Transfer-Encoding: chunked" err
+       {
+               test_have_prereq HTTP2 ||
+               grep "^=> Send header: Transfer-Encoding: chunked" err
+       }
 '
 
 test_expect_success 'test allowreachablesha1inwant' '
@@ -520,7 +560,7 @@ test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' '
 
 test_expect_success 'server-side error detected' '
        test_must_fail git clone $HTTPD_URL/error_smart/repo.git 2>actual &&
-       test_i18ngrep "server-side error" actual
+       test_grep "server-side error" actual
 '
 
 test_expect_success 'http auth remembers successful credentials' '
@@ -573,6 +613,33 @@ test_expect_success 'client falls back from v2 to v0 to match server' '
        grep symref=HEAD:refs/heads/ trace
 '
 
+test_expect_success 'create empty http-accessible SHA-256 repository' '
+       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/sha256.git" &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/sha256.git" &&
+        git --bare init --object-format=sha256
+       )
+'
+
+test_expect_success 'clone empty SHA-256 repository with protocol v2' '
+       rm -fr sha256 &&
+       echo sha256 >expected &&
+       git -c protocol.version=2 clone "$HTTPD_URL/smart/sha256.git" &&
+       git -C sha256 rev-parse --show-object-format >actual &&
+       test_cmp actual expected &&
+       git ls-remote "$HTTPD_URL/smart/sha256.git" >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'clone empty SHA-256 repository with protocol v0' '
+       rm -fr sha256 &&
+       echo sha256 >expected &&
+       GIT_TRACE=1 GIT_TRACE_PACKET=1 git -c protocol.version=0 clone "$HTTPD_URL/smart/sha256.git" &&
+       git -C sha256 rev-parse --show-object-format >actual &&
+       test_cmp actual expected &&
+       git ls-remote "$HTTPD_URL/smart/sha256.git" >actual &&
+       test_must_be_empty actual
+'
+
 test_expect_success 'passing hostname resolution information works' '
        BOGUS_HOST=gitbogusexamplehost.invalid &&
        BOGUS_HTTPD_URL=$HTTPD_PROTO://$BOGUS_HOST:$LIB_HTTPD_PORT &&
@@ -580,4 +647,90 @@ test_expect_success 'passing hostname resolution information works' '
        git -c "http.curloptResolve=$BOGUS_HOST:$LIB_HTTPD_PORT:127.0.0.1" ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null
 '
 
+# here user%40host is the URL-encoded version of user@host,
+# which is our intentionally-odd username to catch parsing errors
+url_user=$HTTPD_URL_USER/auth/smart/repo.git
+url_userpass=$HTTPD_URL_USER_PASS/auth/smart/repo.git
+url_userblank=$HTTPD_PROTO://user%40host:@$HTTPD_DEST/auth/smart/repo.git
+message="URL .*:<redacted>@.* uses plaintext credentials"
+
+test_expect_success 'clone warns or fails when using username:password' '
+       test_when_finished "rm -rf attempt*" &&
+
+       git -c transfer.credentialsInUrl=allow \
+               clone $url_userpass attempt1 2>err &&
+       ! grep "$message" err &&
+
+       git -c transfer.credentialsInUrl=warn \
+               clone $url_userpass attempt2 2>err &&
+       grep "warning: $message" err >warnings &&
+       test_line_count -ge 1 warnings &&
+
+       test_must_fail git -c transfer.credentialsInUrl=die \
+               clone $url_userpass attempt3 2>err &&
+       grep "fatal: $message" err >warnings &&
+       test_line_count -ge 1 warnings &&
+
+       test_must_fail git -c transfer.credentialsInUrl=die \
+               clone $url_userblank attempt4 2>err &&
+       grep "fatal: $message" err >warnings &&
+       test_line_count -ge 1 warnings
+'
+
+test_expect_success 'clone does not detect username:password when it is https://username@domain:port/' '
+       test_when_finished "rm -rf attempt1" &&
+
+       # we are relying on lib-httpd for url construction, so document our
+       # assumptions
+       case "$HTTPD_URL_USER" in
+       *:[0-9]*) : ok ;;
+       *) BUG "httpd url does not have port: $HTTPD_URL_USER"
+       esac &&
+
+       git -c transfer.credentialsInUrl=warn clone $url_user attempt1 2>err &&
+       ! grep "uses plaintext credentials" err
+'
+
+test_expect_success 'fetch warns or fails when using username:password' '
+       git -c transfer.credentialsInUrl=allow fetch $url_userpass 2>err &&
+       ! grep "$message" err &&
+
+       git -c transfer.credentialsInUrl=warn fetch $url_userpass 2>err &&
+       grep "warning: $message" err >warnings &&
+       test_line_count -ge 1 warnings &&
+
+       test_must_fail git -c transfer.credentialsInUrl=die \
+               fetch $url_userpass 2>err &&
+       grep "fatal: $message" err >warnings &&
+       test_line_count -ge 1 warnings &&
+
+       test_must_fail git -c transfer.credentialsInUrl=die \
+               fetch $url_userblank 2>err &&
+       grep "fatal: $message" err >warnings &&
+       test_line_count -ge 1 warnings
+'
+
+
+test_expect_success 'push warns or fails when using username:password' '
+       git -c transfer.credentialsInUrl=allow push $url_userpass 2>err &&
+       ! grep "$message" err &&
+
+       git -c transfer.credentialsInUrl=warn push $url_userpass 2>err &&
+       grep "warning: $message" err >warnings &&
+
+       test_must_fail git -c transfer.credentialsInUrl=die \
+               push $url_userpass 2>err &&
+       grep "fatal: $message" err >warnings &&
+       test_line_count -ge 1 warnings
+'
+
+test_expect_success 'no empty path components' '
+       # In the URL, add a trailing slash, and see if git appends yet another
+       # slash.
+       git clone $HTTPD_URL/smart/repo.git/ clone-with-slash &&
+
+       strip_access_log >log &&
+       ! grep "//" log
+'
+
 test_done
index 165427d57e5cfb2c4ecd3826564bf7ea89fba629..b55a9f65e6bfb56b0461ca6b6da0ec03df2cda32 100755 (executable)
@@ -3,6 +3,22 @@
 test_description='test skipping fetch negotiator'
 . ./test-lib.sh
 
+test_expect_success 'fetch.negotiationalgorithm config' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       cat >repo/.git/config <<-\EOF &&
+       [fetch]
+       negotiationAlgorithm
+       EOF
+       cat >expect <<-\EOF &&
+       error: missing value for '\''fetch.negotiationalgorithm'\''
+       fatal: bad config variable '\''fetch.negotiationalgorithm'\'' in file '\''.git/config'\'' at line 2
+       EOF
+       test_expect_code 128 git -C repo fetch >out 2>actual &&
+       test_must_be_empty out &&
+       test_cmp expect actual
+'
+
 have_sent () {
        while test "$#" -ne 0
        do
index 2ac7b5859e7eefdea13613ba9ae1d238313833dd..06991e8e8aabc28c1a4e522324e5c3eb505cc37e 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test noop fetch negotiator'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'noop negotiator does not emit any "have"' '
index ad666a2d28a553fced4ba0aa55af43c60671222c..1ca5f745e73bd30deb9fc2dfa79baa10d63345bf 100755 (executable)
@@ -33,6 +33,15 @@ test_expect_success 'clone with path bundle' '
        test_cmp expect actual
 '
 
+test_expect_success 'clone with path bundle and non-default hash' '
+       test_when_finished "rm -rf clone-path-non-default-hash" &&
+       GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
+               clone-from clone-path-non-default-hash &&
+       git -C clone-path-non-default-hash rev-parse refs/bundles/topic >actual &&
+       git -C clone-from rev-parse topic >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'clone with file:// bundle' '
        git clone --bundle-uri="file://$(pwd)/clone-from/B.bundle" \
                clone-from clone-file &&
@@ -41,6 +50,215 @@ test_expect_success 'clone with file:// bundle' '
        test_cmp expect actual
 '
 
+# To get interesting tests for bundle lists, we need to construct a
+# somewhat-interesting commit history.
+#
+# ---------------- bundle-4
+#
+#       4
+#      / \
+# ----|---|------- bundle-3
+#     |   |
+#     |   3
+#     |   |
+# ----|---|------- bundle-2
+#     |   |
+#     2   |
+#     |   |
+# ----|---|------- bundle-1
+#      \ /
+#       1
+#       |
+# (previous commits)
+test_expect_success 'construct incremental bundle list' '
+       (
+               cd clone-from &&
+               git checkout -b base &&
+               test_commit 1 &&
+               git checkout -b left &&
+               test_commit 2 &&
+               git checkout -b right base &&
+               test_commit 3 &&
+               git checkout -b merge left &&
+               git merge right -m "4" &&
+
+               git bundle create bundle-1.bundle base &&
+               git bundle create bundle-2.bundle base..left &&
+               git bundle create bundle-3.bundle base..right &&
+               git bundle create bundle-4.bundle merge --not left right
+       )
+'
+
+test_expect_success 'clone bundle list (file, no heuristic)' '
+       cat >bundle-list <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+
+       [bundle "bundle-1"]
+               uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+       [bundle "bundle-2"]
+               uri = file://$(pwd)/clone-from/bundle-2.bundle
+
+       [bundle "bundle-3"]
+               uri = file://$(pwd)/clone-from/bundle-3.bundle
+
+       [bundle "bundle-4"]
+               uri = file://$(pwd)/clone-from/bundle-4.bundle
+       EOF
+
+       git clone --bundle-uri="file://$(pwd)/bundle-list" \
+               clone-from clone-list-file 2>err &&
+       ! grep "Repository lacks these prerequisite commits" err &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-list-file cat-file --batch-check <oids &&
+
+       git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
+       grep "refs/bundles/" refs >actual &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       refs/bundles/right
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, all mode, some failures)' '
+       cat >bundle-list <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-0"]
+               uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+       [bundle "bundle-1"]
+               uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+       [bundle "bundle-2"]
+               uri = file://$(pwd)/clone-from/bundle-2.bundle
+
+       # No bundle-3 means bundle-4 will not apply.
+
+       [bundle "bundle-4"]
+               uri = file://$(pwd)/clone-from/bundle-4.bundle
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-5"]
+               uri = file://$(pwd)/clone-from/bundle-5.bundle
+       EOF
+
+       GIT_TRACE2_PERF=1 \
+       git clone --bundle-uri="file://$(pwd)/bundle-list" \
+               clone-from clone-all-some 2>err &&
+       ! grep "Repository lacks these prerequisite commits" err &&
+       ! grep "fatal" err &&
+       grep "warning: failed to download bundle from URI" err &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-all-some cat-file --batch-check <oids &&
+
+       git -C clone-all-some for-each-ref --format="%(refname)" >refs &&
+       grep "refs/bundles/" refs >actual &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, all mode, all failures)' '
+       cat >bundle-list <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-0"]
+               uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-5"]
+               uri = file://$(pwd)/clone-from/bundle-5.bundle
+       EOF
+
+       git clone --bundle-uri="file://$(pwd)/bundle-list" \
+               clone-from clone-all-fail 2>err &&
+       ! grep "Repository lacks these prerequisite commits" err &&
+       ! grep "fatal" err &&
+       grep "warning: failed to download bundle from URI" err &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-all-fail cat-file --batch-check <oids &&
+
+       git -C clone-all-fail for-each-ref --format="%(refname)" >refs &&
+       ! grep "refs/bundles/" refs
+'
+
+test_expect_success 'clone bundle list (file, any mode)' '
+       cat >bundle-list <<-EOF &&
+       [bundle]
+               version = 1
+               mode = any
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-0"]
+               uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+       [bundle "bundle-1"]
+               uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-5"]
+               uri = file://$(pwd)/clone-from/bundle-5.bundle
+       EOF
+
+       git clone --bundle-uri="file://$(pwd)/bundle-list" \
+               clone-from clone-any-file 2>err &&
+       ! grep "Repository lacks these prerequisite commits" err &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-any-file cat-file --batch-check <oids &&
+
+       git -C clone-any-file for-each-ref --format="%(refname)" >refs &&
+       grep "refs/bundles/" refs >actual &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, any mode, all failures)' '
+       cat >bundle-list <<-EOF &&
+       [bundle]
+               version = 1
+               mode = any
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-0"]
+               uri = $HTTPD_URL/bundle-0.bundle
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-5"]
+               uri = $HTTPD_URL/bundle-5.bundle
+       EOF
+
+       git clone --bundle-uri="file://$(pwd)/bundle-list" \
+               clone-from clone-any-fail 2>err &&
+       ! grep "fatal" err &&
+       grep "warning: failed to download bundle from URI" err &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-any-fail cat-file --batch-check <oids &&
+
+       git -C clone-any-fail for-each-ref --format="%(refname)" >refs &&
+       ! grep "refs/bundles/" refs
+'
+
 #########################################################################
 # HTTP tests begin here
 
@@ -75,6 +293,783 @@ test_expect_success 'clone HTTP bundle' '
        test_config -C clone-http log.excludedecoration refs/bundle/
 '
 
+test_expect_success 'clone HTTP bundle with non-default hash' '
+       test_when_finished "rm -rf clone-http-non-default-hash" &&
+       GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="$HTTPD_URL/B.bundle" \
+               "$HTTPD_URL/smart/fetch.git" clone-http-non-default-hash &&
+       git -C clone-http-non-default-hash rev-parse refs/bundles/topic >actual &&
+       git -C clone-from rev-parse topic >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (HTTP, no heuristic)' '
+       test_when_finished rm -f trace*.txt &&
+
+       cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+
+       [bundle "bundle-1"]
+               uri = $HTTPD_URL/bundle-1.bundle
+
+       [bundle "bundle-2"]
+               uri = $HTTPD_URL/bundle-2.bundle
+
+       [bundle "bundle-3"]
+               uri = $HTTPD_URL/bundle-3.bundle
+
+       [bundle "bundle-4"]
+               uri = $HTTPD_URL/bundle-4.bundle
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+               git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+               clone-from clone-list-http  2>err &&
+       ! grep "Repository lacks these prerequisite commits" err &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-list-http cat-file --batch-check <oids &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-1.bundle
+       $HTTPD_URL/bundle-2.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-list
+       EOF
+
+       # Sort the list, since the order is not well-defined
+       # without a heuristic.
+       test_remote_https_urls <trace-clone.txt | sort >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (HTTP, any mode)' '
+       cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = any
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-0"]
+               uri = $HTTPD_URL/bundle-0.bundle
+
+       [bundle "bundle-1"]
+               uri = $HTTPD_URL/bundle-1.bundle
+
+       # Does not exist. Should be skipped.
+       [bundle "bundle-5"]
+               uri = $HTTPD_URL/bundle-5.bundle
+       EOF
+
+       git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+               clone-from clone-any-http 2>err &&
+       ! grep "fatal" err &&
+       grep "warning: failed to download bundle from URI" err &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-any-http cat-file --batch-check <oids &&
+
+       git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
+       grep "refs/bundles/" refs >actual &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       refs/bundles/right
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (http, creationToken)' '
+       test_when_finished rm -f trace*.txt &&
+
+       cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" git \
+               clone --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" clone-list-http-2 &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-list-http-2 cat-file --batch-check <oids &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+
+       test_remote_https_urls <trace-clone.txt >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone incomplete bundle list (http, creationToken)' '
+       test_when_finished rm -f trace*.txt &&
+
+       cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+       EOF
+
+       GIT_TRACE2_EVENT=$(pwd)/trace-clone.txt \
+       git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+               --single-branch --branch=base --no-tags \
+               "$HTTPD_URL/smart/fetch.git" clone-token-http &&
+
+       test_cmp_config -C clone-token-http "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+       test_cmp_config -C clone-token-http 1 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+
+       test_remote_https_urls <trace-clone.txt >actual &&
+       test_cmp expect actual &&
+
+       # We now have only one bundle ref.
+       git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       EOF
+       test_cmp expect refs &&
+
+       # Add remaining bundles, exercising the "deepening" strategy
+       # for downloading via the creationToken heurisitc.
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+               git -C clone-token-http fetch origin --no-tags \
+               refs/heads/merge:refs/heads/merge &&
+       test_cmp_config -C clone-token-http 4 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       EOF
+
+       test_remote_https_urls <trace1.txt >actual &&
+       test_cmp expect actual &&
+
+       # We now have all bundle refs.
+       git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       refs/bundles/right
+       EOF
+       test_cmp expect refs
+'
+
+test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
+       test_when_finished rm -rf fetch-http-4 trace*.txt &&
+
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+       git clone --single-branch --branch=base \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" fetch-http-4 &&
+
+       test_cmp_config -C fetch-http-4 "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+       test_cmp_config -C fetch-http-4 1 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+
+       test_remote_https_urls <trace-clone.txt >actual &&
+       test_cmp expect actual &&
+
+       # only received base ref from bundle-1
+       git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       EOF
+       test_cmp expect refs &&
+
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+       EOF
+
+       # Fetch the objects for bundle-2 _and_ bundle-3.
+       GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+               git -C fetch-http-4 fetch origin --no-tags \
+               refs/heads/left:refs/heads/left \
+               refs/heads/right:refs/heads/right &&
+       test_cmp_config -C fetch-http-4 2 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-2.bundle
+       EOF
+
+       test_remote_https_urls <trace1.txt >actual &&
+       test_cmp expect actual &&
+
+       # received left from bundle-2
+       git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       EOF
+       test_cmp expect refs &&
+
+       # No-op fetch
+       GIT_TRACE2_EVENT="$(pwd)/trace1b.txt" \
+               git -C fetch-http-4 fetch origin --no-tags \
+               refs/heads/left:refs/heads/left \
+               refs/heads/right:refs/heads/right &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       EOF
+       test_remote_https_urls <trace1b.txt >actual &&
+       test_cmp expect actual &&
+
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       # This fetch should skip bundle-3.bundle, since its objects are
+       # already local (we have the requisite commits for bundle-4.bundle).
+       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+               git -C fetch-http-4 fetch origin --no-tags \
+               refs/heads/merge:refs/heads/merge &&
+       test_cmp_config -C fetch-http-4 4 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       EOF
+
+       test_remote_https_urls <trace2.txt >actual &&
+       test_cmp expect actual &&
+
+       # received merge ref from bundle-4, but right is missing
+       # because we did not download bundle-3.
+       git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       EOF
+       test_cmp expect refs &&
+
+       # No-op fetch
+       GIT_TRACE2_EVENT="$(pwd)/trace2b.txt" \
+               git -C fetch-http-4 fetch origin &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       EOF
+       test_remote_https_urls <trace2b.txt >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'creationToken heuristic with failed downloads (clone)' '
+       test_when_finished rm -rf download-* trace*.txt &&
+
+       # Case 1: base bundle does not exist, nothing can unbundle
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = fake.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone-1.txt" \
+       git clone --single-branch --branch=base \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" download-1 &&
+
+       # Bundle failure does not set these configs.
+       test_must_fail git -C download-1 config fetch.bundleuri &&
+       test_must_fail git -C download-1 config fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       $HTTPD_URL/fake.bundle
+       EOF
+       test_remote_https_urls <trace-clone-1.txt >actual &&
+       test_cmp expect actual &&
+
+       # All bundles failed to unbundle
+       git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       test_must_be_empty refs &&
+
+       # Case 2: middle bundle does not exist, only two bundles can unbundle
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = fake.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone-2.txt" \
+       git clone --single-branch --branch=base \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" download-2 &&
+
+       # Bundle failure does not set these configs.
+       test_must_fail git -C download-2 config fetch.bundleuri &&
+       test_must_fail git -C download-2 config fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/fake.bundle
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+       test_remote_https_urls <trace-clone-2.txt >actual &&
+       test_cmp expect actual &&
+
+       # bundle-1 and bundle-3 could unbundle, but bundle-4 could not
+       git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/right
+       EOF
+       test_cmp expect refs &&
+
+       # Case 3: top bundle does not exist, rest unbundle fine.
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = fake.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone-3.txt" \
+       git clone --single-branch --branch=base \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" download-3 &&
+
+       # As long as we have continguous successful downloads,
+       # we _do_ set these configs.
+       test_cmp_config -C download-3 "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+       test_cmp_config -C download-3 3 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/fake.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+       test_remote_https_urls <trace-clone-3.txt >actual &&
+       test_cmp expect actual &&
+
+       # fake.bundle did not unbundle, but the others did.
+       git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/right
+       EOF
+       test_cmp expect refs
+'
+
+# Expand the bundle list to include other interesting shapes, specifically
+# interesting for use when fetching from a previous state.
+#
+# ---------------- bundle-7
+#       7
+#     _/|\_
+# ---/--|--\------ bundle-6
+#   5   |   6
+# --|---|---|----- bundle-4
+#   |   4   |
+#   |  / \  /
+# --|-|---|/------ bundle-3 (the client will be caught up to this point.)
+#   \ |   3
+# ---\|---|------- bundle-2
+#     2   |
+# ----|---|------- bundle-1
+#      \ /
+#       1
+#       |
+# (previous commits)
+test_expect_success 'expand incremental bundle list' '
+       (
+               cd clone-from &&
+               git checkout -b lefter left &&
+               test_commit 5 &&
+               git checkout -b righter right &&
+               test_commit 6 &&
+               git checkout -b top lefter &&
+               git merge -m "7" merge righter &&
+
+               git bundle create bundle-6.bundle lefter righter --not left right &&
+               git bundle create bundle-7.bundle top --not lefter merge righter &&
+
+               cp bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/"
+       ) &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/fetch.git" fetch origin +refs/heads/*:refs/heads/*
+'
+
+test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
+       test_when_finished rm -rf download-* trace*.txt &&
+
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+       EOF
+
+       git clone --single-branch --branch=left \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" fetch-base &&
+       test_cmp_config -C fetch-base "$HTTPD_URL/bundle-list" fetch.bundleURI &&
+       test_cmp_config -C fetch-base 3 fetch.bundleCreationToken &&
+
+       # Case 1: all bundles exist: successful unbundling of all bundles
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+
+       [bundle "bundle-6"]
+               uri = bundle-6.bundle
+               creationToken = 6
+
+       [bundle "bundle-7"]
+               uri = bundle-7.bundle
+               creationToken = 7
+       EOF
+
+       cp -r fetch-base fetch-1 &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-fetch-1.txt" \
+               git -C fetch-1 fetch origin &&
+       test_cmp_config -C fetch-1 7 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-7.bundle
+       $HTTPD_URL/bundle-6.bundle
+       $HTTPD_URL/bundle-4.bundle
+       EOF
+       test_remote_https_urls <trace-fetch-1.txt >actual &&
+       test_cmp expect actual &&
+
+       # Check which bundles have unbundled by refs
+       git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/lefter
+       refs/bundles/merge
+       refs/bundles/right
+       refs/bundles/righter
+       refs/bundles/top
+       EOF
+       test_cmp expect refs &&
+
+       # Case 2: middle bundle does not exist, only bundle-4 can unbundle
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+
+       [bundle "bundle-6"]
+               uri = fake.bundle
+               creationToken = 6
+
+       [bundle "bundle-7"]
+               uri = bundle-7.bundle
+               creationToken = 7
+       EOF
+
+       cp -r fetch-base fetch-2 &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-fetch-2.txt" \
+               git -C fetch-2 fetch origin &&
+
+       # Since bundle-7 fails to unbundle, do not update creation token.
+       test_cmp_config -C fetch-2 3 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-7.bundle
+       $HTTPD_URL/fake.bundle
+       $HTTPD_URL/bundle-4.bundle
+       EOF
+       test_remote_https_urls <trace-fetch-2.txt >actual &&
+       test_cmp expect actual &&
+
+       # Check which bundles have unbundled by refs
+       git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       refs/bundles/right
+       EOF
+       test_cmp expect refs &&
+
+       # Case 3: top bundle does not exist, rest unbundle fine.
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+
+       [bundle "bundle-6"]
+               uri = bundle-6.bundle
+               creationToken = 6
+
+       [bundle "bundle-7"]
+               uri = fake.bundle
+               creationToken = 7
+       EOF
+
+       cp -r fetch-base fetch-3 &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-fetch-3.txt" \
+               git -C fetch-3 fetch origin &&
+
+       # As long as we have continguous successful downloads,
+       # we _do_ set the maximum creation token.
+       test_cmp_config -C fetch-3 6 fetch.bundlecreationtoken &&
+
+       # NOTE: the fetch skips bundle-4 since bundle-6 successfully
+       # unbundles itself and bundle-7 failed to download.
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/fake.bundle
+       $HTTPD_URL/bundle-6.bundle
+       EOF
+       test_remote_https_urls <trace-fetch-3.txt >actual &&
+       test_cmp expect actual &&
+
+       # Check which bundles have unbundled by refs
+       git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/lefter
+       refs/bundles/right
+       refs/bundles/righter
+       EOF
+       test_cmp expect refs
+'
+
+test_expect_success 'bundles are downloaded once during fetch --all' '
+       test_when_finished rm -rf download-* trace*.txt fetch-mult &&
+
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+       EOF
+
+       git clone --single-branch --branch=left \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" fetch-mult &&
+       git -C fetch-mult remote add dup1 "$HTTPD_URL/smart/fetch.git" &&
+       git -C fetch-mult remote add dup2 "$HTTPD_URL/smart/fetch.git" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-mult.txt" \
+               git -C fetch-mult fetch --all &&
+       grep "\"child_start\".*\"git-remote-https\",\"$HTTPD_URL/bundle-list\"" \
+               trace-mult.txt >bundle-fetches &&
+       test_line_count = 1 bundle-fetches
+'
 # Do not add tests here unless they use the HTTP server, as they will
 # not run unless the HTTP dependencies exist.
 
diff --git a/t/t5559-http-fetch-smart-http2.sh b/t/t5559-http-fetch-smart-http2.sh
new file mode 100755 (executable)
index 0000000..54aa9d3
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+HTTP_PROTO=HTTP/2
+LIB_HTTPD_SSL=1
+. ./t5551-http-fetch-smart.sh
index d30cf4f5b8398e1cf98c86221dd42208d64fd014..f75068de64851faf86d7ae270c1b88bb9d08df16 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test git-http-backend-noserver'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
index 9c57d843152dd5861603316650a0f7f440711c52..e1d3b8caed07394fcdf02db4d31d4f038d3474b2 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test git-http-backend'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 
index b68ec22d3fdb0bab650b39642a61a11a6579fcb5..7ee9858a78b6fb9a473d777f280a9d450fd49830 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test git-http-backend respects CONTENT_LENGTH'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_lazy_prereq GZIP 'gzip --version'
index 718dd9b49d493ec55e2503dcb14aa5ddcc26cc6f..9babb9a375e5fb2a66f216d1968312ec094a6d93 100644 (file)
@@ -1,4 +1,4 @@
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
new file mode 100755 (executable)
index 0000000..ab8a721
--- /dev/null
@@ -0,0 +1,329 @@
+#!/bin/sh
+
+test_description='test http auth header and credential helper interop'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+enable_cgipassauth
+if ! test_have_prereq CGIPASSAUTH
+then
+       skip_all="no CGIPassAuth support"
+       test_done
+fi
+start_httpd
+
+test_expect_success 'setup_credential_helper' '
+       mkdir "$TRASH_DIRECTORY/bin" &&
+       PATH=$PATH:"$TRASH_DIRECTORY/bin" &&
+       export PATH &&
+
+       CREDENTIAL_HELPER="$TRASH_DIRECTORY/bin/git-credential-test-helper" &&
+       write_script "$CREDENTIAL_HELPER" <<-\EOF
+       cmd=$1
+       teefile=$cmd-query.cred
+       catfile=$cmd-reply.cred
+       sed -n -e "/^$/q" -e "p" >>$teefile
+       if test "$cmd" = "get"
+       then
+               cat $catfile
+       fi
+       EOF
+'
+
+set_credential_reply () {
+       cat >"$TRASH_DIRECTORY/$1-reply.cred"
+}
+
+expect_credential_query () {
+       cat >"$TRASH_DIRECTORY/$1-expect.cred" &&
+       test_cmp "$TRASH_DIRECTORY/$1-expect.cred" \
+                "$TRASH_DIRECTORY/$1-query.cred"
+}
+
+per_test_cleanup () {
+       rm -f *.cred &&
+       rm -f "$HTTPD_ROOT_PATH"/custom-auth.valid \
+             "$HTTPD_ROOT_PATH"/custom-auth.challenge
+}
+
+test_expect_success 'setup repository' '
+       test_commit foo &&
+       git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git"
+'
+
+test_expect_success 'access using basic auth' '
+       test_when_finished "per_test_cleanup" &&
+
+       set_credential_reply get <<-EOF &&
+       username=alice
+       password=secret-passwd
+       EOF
+
+       # Basic base64(alice:secret-passwd)
+       cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+       Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+       EOF
+
+       cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+       WWW-Authenticate: Basic realm="example.com"
+       EOF
+
+       test_config_global credential.helper test-helper &&
+       git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+       expect_credential_query get <<-EOF &&
+       protocol=http
+       host=$HTTPD_DEST
+       wwwauth[]=Basic realm="example.com"
+       EOF
+
+       expect_credential_query store <<-EOF
+       protocol=http
+       host=$HTTPD_DEST
+       username=alice
+       password=secret-passwd
+       EOF
+'
+
+test_expect_success 'access using basic auth invalid credentials' '
+       test_when_finished "per_test_cleanup" &&
+
+       set_credential_reply get <<-EOF &&
+       username=baduser
+       password=wrong-passwd
+       EOF
+
+       # Basic base64(alice:secret-passwd)
+       cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+       Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+       EOF
+
+       cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+       WWW-Authenticate: Basic realm="example.com"
+       EOF
+
+       test_config_global credential.helper test-helper &&
+       test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+       expect_credential_query get <<-EOF &&
+       protocol=http
+       host=$HTTPD_DEST
+       wwwauth[]=Basic realm="example.com"
+       EOF
+
+       expect_credential_query erase <<-EOF
+       protocol=http
+       host=$HTTPD_DEST
+       username=baduser
+       password=wrong-passwd
+       wwwauth[]=Basic realm="example.com"
+       EOF
+'
+
+test_expect_success 'access using basic auth with extra challenges' '
+       test_when_finished "per_test_cleanup" &&
+
+       set_credential_reply get <<-EOF &&
+       username=alice
+       password=secret-passwd
+       EOF
+
+       # Basic base64(alice:secret-passwd)
+       cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+       Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+       EOF
+
+       cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+       WWW-Authenticate: FooBar param1="value1" param2="value2"
+       WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+       WWW-Authenticate: Basic realm="example.com"
+       EOF
+
+       test_config_global credential.helper test-helper &&
+       git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+       expect_credential_query get <<-EOF &&
+       protocol=http
+       host=$HTTPD_DEST
+       wwwauth[]=FooBar param1="value1" param2="value2"
+       wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+       wwwauth[]=Basic realm="example.com"
+       EOF
+
+       expect_credential_query store <<-EOF
+       protocol=http
+       host=$HTTPD_DEST
+       username=alice
+       password=secret-passwd
+       EOF
+'
+
+test_expect_success 'access using basic auth mixed-case wwwauth header name' '
+       test_when_finished "per_test_cleanup" &&
+
+       set_credential_reply get <<-EOF &&
+       username=alice
+       password=secret-passwd
+       EOF
+
+       # Basic base64(alice:secret-passwd)
+       cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+       Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+       EOF
+
+       cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+       www-authenticate: foobar param1="value1" param2="value2"
+       WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
+       WwW-aUtHeNtIcAtE: baSiC realm="example.com"
+       EOF
+
+       test_config_global credential.helper test-helper &&
+       git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+       expect_credential_query get <<-EOF &&
+       protocol=http
+       host=$HTTPD_DEST
+       wwwauth[]=foobar param1="value1" param2="value2"
+       wwwauth[]=BEARER authorize_uri="id.example.com" p=1 q=0
+       wwwauth[]=baSiC realm="example.com"
+       EOF
+
+       expect_credential_query store <<-EOF
+       protocol=http
+       host=$HTTPD_DEST
+       username=alice
+       password=secret-passwd
+       EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header continuations' '
+       test_when_finished "per_test_cleanup" &&
+
+       set_credential_reply get <<-EOF &&
+       username=alice
+       password=secret-passwd
+       EOF
+
+       # Basic base64(alice:secret-passwd)
+       cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+       Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+       EOF
+
+       # Note that leading and trailing whitespace is important to correctly
+       # simulate a continuation/folded header.
+       cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+       WWW-Authenticate: FooBar param1="value1"
+        param2="value2"
+       WWW-Authenticate: Bearer authorize_uri="id.example.com"
+        p=1
+        q=0
+       WWW-Authenticate: Basic realm="example.com"
+       EOF
+
+       test_config_global credential.helper test-helper &&
+       git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+       expect_credential_query get <<-EOF &&
+       protocol=http
+       host=$HTTPD_DEST
+       wwwauth[]=FooBar param1="value1" param2="value2"
+       wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+       wwwauth[]=Basic realm="example.com"
+       EOF
+
+       expect_credential_query store <<-EOF
+       protocol=http
+       host=$HTTPD_DEST
+       username=alice
+       password=secret-passwd
+       EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header empty continuations' '
+       test_when_finished "per_test_cleanup" &&
+
+       set_credential_reply get <<-EOF &&
+       username=alice
+       password=secret-passwd
+       EOF
+
+       # Basic base64(alice:secret-passwd)
+       cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+       Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+       EOF
+
+       CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+       # Note that leading and trailing whitespace is important to correctly
+       # simulate a continuation/folded header.
+       printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+       printf " \r\n" >>"$CHALLENGE" &&
+       printf " param2=\"value2\"\r\n" >>"$CHALLENGE" &&
+       printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
+       printf " p=1\r\n" >>"$CHALLENGE" &&
+       printf " \r\n" >>"$CHALLENGE" &&
+       printf " q=0\r\n" >>"$CHALLENGE" &&
+       printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
+
+       test_config_global credential.helper test-helper &&
+       git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+       expect_credential_query get <<-EOF &&
+       protocol=http
+       host=$HTTPD_DEST
+       wwwauth[]=FooBar param1="value1" param2="value2"
+       wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+       wwwauth[]=Basic realm="example.com"
+       EOF
+
+       expect_credential_query store <<-EOF
+       protocol=http
+       host=$HTTPD_DEST
+       username=alice
+       password=secret-passwd
+       EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header mixed line-endings' '
+       test_when_finished "per_test_cleanup" &&
+
+       set_credential_reply get <<-EOF &&
+       username=alice
+       password=secret-passwd
+       EOF
+
+       # Basic base64(alice:secret-passwd)
+       cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+       Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+       EOF
+
+       CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+       # Note that leading and trailing whitespace is important to correctly
+       # simulate a continuation/folded header.
+       printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+       printf " \r\n" >>"$CHALLENGE" &&
+       printf "\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
+       printf "WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
+
+       test_config_global credential.helper test-helper &&
+       git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+       expect_credential_query get <<-EOF &&
+       protocol=http
+       host=$HTTPD_DEST
+       wwwauth[]=FooBar param1="value1" param2="value2"
+       wwwauth[]=Basic realm="example.com"
+       EOF
+
+       expect_credential_query store <<-EOF
+       protocol=http
+       host=$HTTPD_DEST
+       username=alice
+       password=secret-passwd
+       EOF
+'
+
+test_done
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
new file mode 100755 (executable)
index 0000000..9da5134
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description="test fetching through http proxy"
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+LIB_HTTPD_PROXY=1
+start_httpd
+
+test_expect_success 'setup repository' '
+       test_commit foo &&
+       git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git"
+'
+
+setup_askpass_helper
+
+# sanity check that our test setup is correctly using proxy
+test_expect_success 'proxy requires password' '
+       test_config_global http.proxy $HTTPD_DEST &&
+       test_must_fail git clone $HTTPD_URL/smart/repo.git 2>err &&
+       grep "error.*407" err
+'
+
+test_expect_success 'clone through proxy with auth' '
+       test_when_finished "rm -rf clone" &&
+       test_config_global http.proxy http://proxuser:proxpass@$HTTPD_DEST &&
+       GIT_TRACE_CURL=$PWD/trace git clone $HTTPD_URL/smart/repo.git clone &&
+       grep -i "Proxy-Authorization: Basic <redacted>" trace
+'
+
+test_expect_success 'clone can prompt for proxy password' '
+       test_when_finished "rm -rf clone" &&
+       test_config_global http.proxy http://proxuser@$HTTPD_DEST &&
+       set_askpass nobody proxpass &&
+       GIT_TRACE_CURL=$PWD/trace git clone $HTTPD_URL/smart/repo.git clone &&
+       expect_askpass pass proxuser
+'
+
+test_done
index 1131503b760c48cbb6f6b0d10a5027d76ec40db5..f9a9bf950328e805ffb27c743b8d85f61510eec8 100755 (executable)
@@ -10,9 +10,9 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 start_git_daemon
 
 check_verbose_connect () {
-       test_i18ngrep -F "Looking up 127.0.0.1 ..." stderr &&
-       test_i18ngrep -F "Connecting to 127.0.0.1 (port " stderr &&
-       test_i18ngrep -F "done." stderr
+       test_grep -F "Looking up 127.0.0.1 ..." stderr &&
+       test_grep -F "Connecting to 127.0.0.1 (port " stderr &&
+       test_grep -F "done." stderr
 }
 
 test_expect_success 'setup repository' '
@@ -108,7 +108,7 @@ test_expect_success 'fetch notices corrupt idx' '
 
 test_expect_success 'client refuses to ask for repo with newline' '
        test_must_fail git clone "$GIT_DAEMON_URL/repo$LF.git" dst 2>stderr &&
-       test_i18ngrep newline.is.forbidden stderr
+       test_grep newline.is.forbidden stderr
 '
 
 test_remote_error()
@@ -148,7 +148,7 @@ test_remote_error()
        fi
 
        test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output &&
-       test_i18ngrep "fatal: remote error: $msg: /$repo" output &&
+       test_grep "fatal: remote error: $msg: /$repo" output &&
        ret=$?
        chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git"
        (exit $ret)
index a11b20e378223ea30242e70d5befcfeed889a2ed..448134c4bf72bd5f3a4c2287e59f6f2a1f3f074b 100755 (executable)
@@ -4,6 +4,7 @@ test_description='check pre-push hooks'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 09097eff3f46033a26c9b60c94bb8cb79f3fad2c..51744521f789b654ffbcee73afba47a0ecbe7e35 100755 (executable)
@@ -121,7 +121,7 @@ test_expect_success "fetch.recurseSubmodules option triggers recursive fetch (bu
        sub_oid=$(git -C child rev-parse HEAD) &&
        git -C super/sub cat-file -e $sub_oid &&
        # Check that the submodule worktree did not update
-       ! test_path_is_file super/sub/merge_strategy_5.t
+       test_path_is_missing super/sub/merge_strategy_5.t
 '
 
 test_expect_success "fetch.recurseSubmodules takes precedence over submodule.recurse" '
@@ -134,7 +134,7 @@ test_expect_success "fetch.recurseSubmodules takes precedence over submodule.rec
        sub_oid=$(git -C child rev-parse HEAD) &&
        git -C super/sub cat-file -e $sub_oid &&
        # Check that the submodule worktree did not update
-       ! test_path_is_file super/sub/merge_strategy_6.t
+       test_path_is_missing super/sub/merge_strategy_6.t
 '
 
 test_expect_success 'pull --rebase --recurse-submodules (remote superproject submodule changes, local submodule changes)' '
@@ -177,7 +177,7 @@ test_expect_success 'pull --rebase --recurse-submodules fails if both sides reco
        # submodule itself, but the merge strategy in submodules
        # does not support rebase:
        test_must_fail git -C super pull --rebase --recurse-submodules 2>err &&
-       test_i18ngrep "locally recorded submodule modifications" err
+       test_grep "locally recorded submodule modifications" err
 '
 
 test_expect_success 'pull --rebase --recurse-submodules (no submodule changes, no fork-point)' '
index a53dd8550d0b8cbe40f49ece0381fb554ba87062..ab05f38a99823968a6657ac2579c404560118044 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='pull signature verification tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
@@ -45,46 +47,46 @@ test_expect_success GPG 'create repositories with signed commits' '
 test_expect_success GPG 'pull unsigned commit with --verify-signatures' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git pull --ff-only --verify-signatures unsigned 2>pullerror &&
-       test_i18ngrep "does not have a GPG signature" pullerror
+       test_grep "does not have a GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with bad signature with --verify-signatures' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git pull --ff-only --verify-signatures bad 2>pullerror &&
-       test_i18ngrep "has a bad GPG signature" pullerror
+       test_grep "has a bad GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
-       test_i18ngrep "has an untrusted GPG signature" pullerror
+       test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=ultimate' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config gpg.minTrustLevel ultimate &&
        test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
-       test_i18ngrep "has an untrusted GPG signature" pullerror
+       test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=marginal' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config gpg.minTrustLevel marginal &&
        test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
-       test_i18ngrep "has an untrusted GPG signature" pullerror
+       test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=undefined' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config gpg.minTrustLevel undefined &&
        git pull --ff-only --verify-signatures untrusted >pulloutput &&
-       test_i18ngrep "has a good GPG signature" pulloutput
+       test_grep "has a good GPG signature" pulloutput
 '
 
 test_expect_success GPG 'pull signed commit with --verify-signatures' '
        test_when_finished "git reset --hard && git checkout initial" &&
        git pull --verify-signatures signed >pulloutput &&
-       test_i18ngrep "has a good GPG signature" pulloutput
+       test_grep "has a good GPG signature" pulloutput
 '
 
 test_expect_success GPG 'pull commit with bad signature without verification' '
@@ -104,7 +106,7 @@ test_expect_success GPG 'pull unsigned commit into unborn branch' '
        git init empty-repo &&
        test_must_fail \
                git -C empty-repo pull --verify-signatures ..  2>pullerror &&
-       test_i18ngrep "does not have a GPG signature" pullerror
+       test_grep "does not have a GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with bad signature and --verify-signatures' '
@@ -112,7 +114,7 @@ test_expect_success GPG 'pull commit into unborn branch with bad signature and -
        git init empty-repo &&
        test_must_fail \
                git -C empty-repo pull --ff-only --verify-signatures ../bad 2>pullerror &&
-       test_i18ngrep "has a bad GPG signature" pullerror
+       test_grep "has a bad GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures' '
@@ -120,7 +122,7 @@ test_expect_success GPG 'pull commit into unborn branch with untrusted signature
        git init empty-repo &&
        test_must_fail \
                git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
-       test_i18ngrep "has an untrusted GPG signature" pullerror
+       test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=ultimate' '
@@ -129,7 +131,7 @@ test_expect_success GPG 'pull commit into unborn branch with untrusted signature
        test_config_global gpg.minTrustLevel ultimate &&
        test_must_fail \
                git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
-       test_i18ngrep "has an untrusted GPG signature" pullerror
+       test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=marginal' '
@@ -138,7 +140,7 @@ test_expect_success GPG 'pull commit into unborn branch with untrusted signature
        test_config_global gpg.minTrustLevel marginal &&
        test_must_fail \
                git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
-       test_i18ngrep "has an untrusted GPG signature" pullerror
+       test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=undefined' '
@@ -146,7 +148,7 @@ test_expect_success GPG 'pull commit into unborn branch with untrusted signature
        git init empty-repo &&
        test_config_global gpg.minTrustLevel undefined &&
        git -C empty-repo pull --ff-only --verify-signatures ../untrusted >pulloutput &&
-       test_i18ngrep "has a good GPG signature" pulloutput
+       test_grep "has a good GPG signature" pulloutput
 '
 
 test_done
diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh
new file mode 100755 (executable)
index 0000000..5883839
--- /dev/null
@@ -0,0 +1,304 @@
+#!/bin/sh
+
+test_description='git fetch output format'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'fetch with invalid output format configuration' '
+       test_when_finished "rm -rf clone" &&
+       git clone . clone &&
+
+       test_must_fail git -C clone -c fetch.output fetch origin 2>actual.err &&
+       cat >expect <<-EOF &&
+       error: missing value for ${SQ}fetch.output${SQ}
+       fatal: unable to parse ${SQ}fetch.output${SQ} from command-line config
+       EOF
+       test_cmp expect actual.err &&
+
+       test_must_fail git -C clone -c fetch.output= fetch origin 2>actual.err &&
+       cat >expect <<-EOF &&
+       fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}${SQ}
+       EOF
+       test_cmp expect actual.err &&
+
+       test_must_fail git -C clone -c fetch.output=garbage fetch origin 2>actual.err &&
+       cat >expect <<-EOF &&
+       fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}garbage${SQ}
+       EOF
+       test_cmp expect actual.err
+'
+
+test_expect_success 'fetch aligned output' '
+       git clone . full-output &&
+       test_commit looooooooooooong-tag &&
+       (
+               cd full-output &&
+               git -c fetch.output=full fetch origin >actual 2>&1 &&
+               grep -e "->" actual | cut -c 22- >../actual
+       ) &&
+       cat >expect <<-\EOF &&
+       main                 -> origin/main
+       looooooooooooong-tag -> looooooooooooong-tag
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'fetch compact output' '
+       git clone . compact &&
+       test_commit extraaa &&
+       (
+               cd compact &&
+               git -c fetch.output=compact fetch origin >actual 2>&1 &&
+               grep -e "->" actual | cut -c 22- >../actual
+       ) &&
+       cat >expect <<-\EOF &&
+       main       -> origin/*
+       extraaa    -> *
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'setup for fetch porcelain output' '
+       # Set up a bunch of references that we can use to demonstrate different
+       # kinds of flag symbols in the output format.
+       test_commit commit-for-porcelain-output &&
+       MAIN_OLD=$(git rev-parse HEAD) &&
+       git branch "fast-forward" &&
+       git branch "deleted-branch" &&
+       git checkout -b force-updated &&
+       test_commit --no-tag force-update-old &&
+       FORCE_UPDATED_OLD=$(git rev-parse HEAD) &&
+       git checkout main &&
+
+       # Backup to preseed.git
+       git clone --mirror . preseed.git &&
+
+       # Continue changing our local references.
+       git branch new-branch &&
+       git branch -d deleted-branch &&
+       git checkout fast-forward &&
+       test_commit --no-tag fast-forward-new &&
+       FAST_FORWARD_NEW=$(git rev-parse HEAD) &&
+       git checkout force-updated &&
+       git reset --hard HEAD~ &&
+       test_commit --no-tag force-update-new &&
+       FORCE_UPDATED_NEW=$(git rev-parse HEAD)
+'
+
+for opt in "" "--atomic"
+do
+       test_expect_success "fetch porcelain output ${opt:+(atomic)}" '
+               test_when_finished "rm -rf porcelain" &&
+
+               # Clone and pre-seed the repositories. We fetch references into two
+               # namespaces so that we can test that rejected and force-updated
+               # references are reported properly.
+               refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" &&
+               git clone preseed.git porcelain &&
+               git -C porcelain fetch origin $opt $refspecs &&
+
+               cat >expect <<-EOF &&
+               - $MAIN_OLD $ZERO_OID refs/forced/deleted-branch
+               - $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch
+                 $MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward
+               ! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated
+               * $ZERO_OID $MAIN_OLD refs/unforced/new-branch
+                 $MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward
+               + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated
+               * $ZERO_OID $MAIN_OLD refs/forced/new-branch
+                 $MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward
+               + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated
+               * $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch
+               EOF
+
+               # Change the URL of the repository to fetch different references.
+               git -C porcelain remote set-url origin .. &&
+
+               # Execute a dry-run fetch first. We do this to assert that the dry-run
+               # and non-dry-run fetches produces the same output. Execution of the
+               # fetch is expected to fail as we have a rejected reference update.
+               test_must_fail git -C porcelain fetch $opt \
+                       --porcelain --dry-run --prune origin $refspecs >actual &&
+               test_cmp expect actual &&
+
+               # And now we perform a non-dry-run fetch.
+               test_must_fail git -C porcelain fetch $opt \
+                       --porcelain --prune origin $refspecs >actual 2>stderr &&
+               test_cmp expect actual &&
+               test_must_be_empty stderr
+       '
+done
+
+test_expect_success 'fetch porcelain with multiple remotes' '
+       test_when_finished "rm -rf porcelain" &&
+
+       git switch --create multiple-remotes &&
+       git clone . porcelain &&
+       git -C porcelain remote add second-remote "$PWD" &&
+       git -C porcelain fetch second-remote &&
+
+       test_commit --no-tag multi-commit &&
+       old_commit=$(git rev-parse HEAD~) &&
+       new_commit=$(git rev-parse HEAD) &&
+
+       cat >expect <<-EOF &&
+         $old_commit $new_commit refs/remotes/origin/multiple-remotes
+         $old_commit $new_commit refs/remotes/second-remote/multiple-remotes
+       EOF
+
+       git -C porcelain fetch --porcelain --all >actual 2>stderr &&
+       test_cmp expect actual &&
+       test_must_be_empty stderr
+'
+
+test_expect_success 'fetch porcelain refuses to work with submodules' '
+       test_when_finished "rm -rf porcelain" &&
+
+       cat >expect <<-EOF &&
+       fatal: options ${SQ}--porcelain${SQ} and ${SQ}--recurse-submodules${SQ} cannot be used together
+       EOF
+
+       git init porcelain &&
+       test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=yes 2>stderr &&
+       test_cmp expect stderr &&
+
+       test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=on-demand 2>stderr &&
+       test_cmp expect stderr
+'
+
+test_expect_success 'fetch porcelain overrides fetch.output config' '
+       test_when_finished "rm -rf porcelain" &&
+
+       git switch --create config-override &&
+       git clone . porcelain &&
+       test_commit new-commit &&
+       old_commit=$(git rev-parse HEAD~) &&
+       new_commit=$(git rev-parse HEAD) &&
+
+       cat >expect <<-EOF &&
+         $old_commit $new_commit refs/remotes/origin/config-override
+       * $ZERO_OID $new_commit refs/tags/new-commit
+       EOF
+
+       git -C porcelain -c fetch.output=compact fetch --porcelain >stdout 2>stderr &&
+       test_must_be_empty stderr &&
+       test_cmp expect stdout
+'
+
+test_expect_success 'fetch --no-porcelain overrides previous --porcelain' '
+       test_when_finished "rm -rf no-porcelain" &&
+
+       git switch --create no-porcelain &&
+       git clone . no-porcelain &&
+       test_commit --no-tag no-porcelain &&
+       old_commit=$(git rev-parse --short HEAD~) &&
+       new_commit=$(git rev-parse --short HEAD) &&
+
+       cat >expect <<-EOF &&
+       From $(test-tool path-utils real_path .)/.
+          $old_commit..$new_commit  no-porcelain -> origin/no-porcelain
+       EOF
+
+       git -C no-porcelain fetch --porcelain --no-porcelain >stdout 2>stderr &&
+       test_cmp expect stderr &&
+       test_must_be_empty stdout
+'
+
+test_expect_success 'fetch output with HEAD' '
+       test_when_finished "rm -rf head" &&
+       git clone . head &&
+
+       git -C head fetch --dry-run origin HEAD >actual.out 2>actual.err &&
+       cat >expect <<-EOF &&
+       From $(test-tool path-utils real_path .)/.
+        * branch            HEAD       -> FETCH_HEAD
+       EOF
+       test_must_be_empty actual.out &&
+       test_cmp expect actual.err &&
+
+       git -C head fetch origin HEAD >actual.out 2>actual.err &&
+       test_must_be_empty actual.out &&
+       test_cmp expect actual.err &&
+
+       git -C head fetch --dry-run origin HEAD:foo >actual.out 2>actual.err &&
+       cat >expect <<-EOF &&
+       From $(test-tool path-utils real_path .)/.
+        * [new ref]         HEAD       -> foo
+       EOF
+       test_must_be_empty actual.out &&
+       test_cmp expect actual.err &&
+
+       git -C head fetch origin HEAD:foo >actual.out 2>actual.err &&
+       test_must_be_empty actual.out &&
+       test_cmp expect actual.err
+'
+
+test_expect_success 'fetch porcelain output with HEAD' '
+       test_when_finished "rm -rf head" &&
+       git clone . head &&
+       COMMIT_ID=$(git rev-parse HEAD) &&
+
+       git -C head fetch --porcelain --dry-run origin HEAD >actual &&
+       cat >expect <<-EOF &&
+       * $ZERO_OID $COMMIT_ID FETCH_HEAD
+       EOF
+       test_cmp expect actual &&
+
+       git -C head fetch --porcelain origin HEAD >actual &&
+       test_cmp expect actual &&
+
+       git -C head fetch --porcelain --dry-run origin HEAD:foo >actual &&
+       cat >expect <<-EOF &&
+       * $ZERO_OID $COMMIT_ID refs/heads/foo
+       EOF
+       test_cmp expect actual &&
+
+       git -C head fetch --porcelain origin HEAD:foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fetch output with object ID' '
+       test_when_finished "rm -rf object-id" &&
+       git clone . object-id &&
+       commit=$(git rev-parse HEAD) &&
+
+       git -C object-id fetch --dry-run origin $commit:object-id >actual.out 2>actual.err &&
+       cat >expect <<-EOF &&
+       From $(test-tool path-utils real_path .)/.
+        * [new ref]         $commit -> object-id
+       EOF
+       test_must_be_empty actual.out &&
+       test_cmp expect actual.err &&
+
+       git -C object-id fetch origin $commit:object-id >actual.out 2>actual.err &&
+       test_must_be_empty actual.out &&
+       test_cmp expect actual.err
+'
+
+test_expect_success '--no-show-forced-updates' '
+       mkdir forced-updates &&
+       (
+               cd forced-updates &&
+               git init &&
+               test_commit 1 &&
+               test_commit 2
+       ) &&
+       git clone forced-updates forced-update-clone &&
+       git clone forced-updates no-forced-update-clone &&
+       git -C forced-updates reset --hard HEAD~1 &&
+       (
+               cd forced-update-clone &&
+               git fetch --show-forced-updates origin 2>output &&
+               test_grep "(forced update)" output
+       ) &&
+       (
+               cd no-forced-update-clone &&
+               git fetch --no-show-forced-updates origin 2>output &&
+               test_grep ! "(forced update)" output
+       )
+'
+
+test_done
index cd7604fff93a55ff9bb9e4bcae905c4386f0374a..d7537a162b21fe09ac48cab3ac3d937c188ec081 100755 (executable)
@@ -75,7 +75,7 @@ test_expect_success push '
 test_expect_success MINGW 'remote nick cannot contain backslashes' '
        BACKSLASHED="$(winpwd | tr / \\\\)" &&
        git ls-remote "$BACKSLASHED" 2>err &&
-       test_i18ngrep ! "unable to access" err
+       test_grep ! "unable to access" err
 '
 
 test_expect_success 'unc alternates' '
diff --git a/t/t5583-push-branches.sh b/t/t5583-push-branches.sh
new file mode 100755 (executable)
index 0000000..320f49c
--- /dev/null
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+test_description='check the consisitency of behavior of --all and --branches'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+delete_refs() {
+       dir=$1
+       shift
+       rm -rf deletes
+       for arg in $*
+       do
+               echo "delete ${arg}" >>deletes
+       done
+       git -C $dir update-ref --stdin < deletes
+}
+
+test_expect_success 'setup bare remote' '
+       git init --bare remote-1 &&
+       git -C remote-1 config gc.auto 0 &&
+       test_commit one &&
+       git push remote-1 HEAD
+'
+
+test_expect_success 'setup different types of references' '
+       cat >refs <<-EOF &&
+       update refs/heads/branch-1 HEAD
+       update refs/heads/branch-2 HEAD
+       EOF
+
+       git tag -a -m "annotated" annotated-1 HEAD &&
+       git tag -a -m "annotated" annotated-2 HEAD &&
+       git update-ref --stdin < refs
+'
+
+test_expect_success '--all and --branches have the same behavior' '
+       test_when_finished "delete_refs remote-1 \
+                          refs/heads/branch-1 \
+                          refs/heads/branch-2" &&
+       git push remote-1 --all &&
+       commit=$(git rev-parse HEAD) &&
+       cat >expect <<-EOF &&
+       $commit refs/heads/branch-1
+       $commit refs/heads/branch-2
+       $commit refs/heads/main
+       EOF
+
+       git -C remote-1 show-ref --heads >actual.all &&
+       delete_refs remote-1 refs/heads/branch-1 refs/heads/branch-2 &&
+       git push remote-1 --branches &&
+       git -C remote-1 show-ref --heads >actual.branches &&
+       test_cmp actual.all actual.branches &&
+       test_cmp expect actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with refspecs' '
+       test_must_fail git push remote-1 --all main >actual.all 2>&1 &&
+       test_must_fail git push remote-1 --branches main >actual.branches 2>&1 &&
+       test_cmp actual.all actual.branches &&
+       grep "be combined with refspecs" actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with --mirror' '
+       test_must_fail git push remote-1 --all --mirror >actual.all 2>&1 &&
+       test_must_fail git push remote-1 --branches --mirror >actual.branches 2>&1 &&
+       test_cmp actual.all actual.branches &&
+       grep "cannot be used together" actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with --tags' '
+       test_must_fail git push remote-1 --all --tags >actual.all 2>&1 &&
+       test_must_fail git push remote-1 --branches --tags >actual.branches 2>&1 &&
+       test_cmp actual.all actual.branches &&
+       grep "cannot be used together" actual.all
+'
+
+
+test_expect_success '--all or --branches can not be combined with --delete' '
+       test_must_fail git push remote-1 --all --delete >actual.all 2>&1 &&
+       test_must_fail git push remote-1 --branches --delete >actual.branches 2>&1 &&
+       test_cmp actual.all actual.branches &&
+       grep "cannot be used together" actual.all
+'
+
+test_expect_success '--all or --branches combines with --follow-tags have same behavior' '
+       test_when_finished "delete_refs remote-1 \
+                          refs/heads/branch-1 \
+                          refs/heads/branch-2 \
+                          refs/tags/annotated-1 \
+                          refs/tags/annotated-2" &&
+       git push remote-1 --all --follow-tags &&
+       git -C remote-1 show-ref > actual.all &&
+       cat >expect <<-EOF &&
+       $commit refs/heads/branch-1
+       $commit refs/heads/branch-2
+       $commit refs/heads/main
+       $(git rev-parse annotated-1) refs/tags/annotated-1
+       $(git rev-parse annotated-2) refs/tags/annotated-2
+       EOF
+
+       delete_refs remote-1 \
+                   refs/heads/branch-1 \
+                   refs/heads/branch-2 \
+                   refs/tags/annotated-1 \
+                   refs/tags/annotated-2 &&
+       git push remote-1 --branches --follow-tags &&
+       git -C remote-1 show-ref >actual.branches &&
+       test_cmp actual.all actual.branches &&
+       test_cmp expect actual.all
+'
+
+test_done
index 45f0803ed4dccd528df61060dfb4c82ff4c6fc34..fb1b9c686db24a30f9dbd2f6f38f29bfc4624676 100755 (executable)
@@ -71,29 +71,6 @@ test_expect_success 'clone respects GIT_WORK_TREE' '
 
 '
 
-test_expect_success LIBCURL 'clone warns or fails when using username:password' '
-       message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
-       test_must_fail git -c transfer.credentialsInUrl=allow clone https://username:password@localhost attempt1 2>err &&
-       ! grep "$message" err &&
-
-       test_must_fail git -c transfer.credentialsInUrl=warn clone https://username:password@localhost attempt2 2>err &&
-       grep "warning: $message" err >warnings &&
-       test_line_count = 2 warnings &&
-
-       test_must_fail git -c transfer.credentialsInUrl=die clone https://username:password@localhost attempt3 2>err &&
-       grep "fatal: $message" err >warnings &&
-       test_line_count = 1 warnings &&
-
-       test_must_fail git -c transfer.credentialsInUrl=die clone https://username:@localhost attempt3 2>err &&
-       grep "fatal: $message" err >warnings &&
-       test_line_count = 1 warnings
-'
-
-test_expect_success LIBCURL 'clone does not detect username:password when it is https://username@domain:port/' '
-       test_must_fail git -c transfer.credentialsInUrl=warn clone https://username@localhost:8080 attempt3 2>err &&
-       ! grep "uses plaintext credentials" err
-'
-
 test_expect_success 'clone from hooks' '
 
        test_create_repo r0 &&
@@ -180,6 +157,23 @@ test_expect_success 'clone --mirror does not repeat tags' '
 
 '
 
+test_expect_success 'clone with files ref format' '
+       test_when_finished "rm -rf ref-storage" &&
+       git clone --ref-format=files --mirror src ref-storage &&
+       echo files >expect &&
+       git -C ref-storage rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone with garbage ref format' '
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err &&
+       test_cmp expect err &&
+       test_path_is_missing ref-storage
+'
+
 test_expect_success 'clone to destination with trailing /' '
 
        git clone src target-1/ &&
@@ -653,7 +647,7 @@ test_expect_success 'clone on case-insensitive fs' '
 test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
        grep X icasefs/warning &&
        grep x icasefs/warning &&
-       test_i18ngrep "the following paths have collided" icasefs/warning
+       test_grep "the following paths have collided" icasefs/warning
 '
 
 test_expect_success 'clone with GIT_DEFAULT_HASH' '
@@ -719,7 +713,7 @@ test_expect_success 'partial clone: warn if server does not support object filte
 
        git clone --filter=blob:limit=0 "file://$(pwd)/server" client 2> err &&
 
-       test_i18ngrep "filtering not recognized by server" err
+       test_grep "filtering not recognized by server" err
 '
 
 test_expect_success 'batch missing blob request during checkout' '
@@ -790,11 +784,116 @@ test_expect_success 'reject cloning shallow repository using HTTP' '
        test_when_finished "rm -rf repo" &&
        git clone --bare --no-local --depth=1 src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
        test_must_fail git -c protocol.version=2 clone --reject-shallow $HTTPD_URL/smart/repo.git repo 2>err &&
-       test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+       test_grep -e "source repository is shallow, reject to clone." err &&
 
        git clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo
 '
 
+test_expect_success 'auto-discover bundle URI from HTTP clone' '
+       test_when_finished rm -rf trace.txt repo2 "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" &&
+       git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/everything.bundle" --all &&
+       git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" &&
+
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+               uploadpack.advertiseBundleURIs true &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+               bundle.version 1 &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+               bundle.mode all &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+               bundle.everything.uri "$HTTPD_URL/everything.bundle" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+               git -c protocol.version=2 \
+                   -c transfer.bundleURI=true clone \
+               $HTTPD_URL/smart/repo2.git repo2 &&
+       cat >pattern <<-EOF &&
+       "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\]
+       EOF
+       grep -f pattern trace.txt
+'
+
+test_expect_success 'auto-discover multiple bundles from HTTP clone' '
+       test_when_finished rm -rf trace.txt repo3 "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" &&
+
+       test_commit -C src new &&
+       git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/new.bundle" HEAD~1..HEAD &&
+       git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" &&
+
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               uploadpack.advertiseBundleURIs true &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               bundle.version 1 &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               bundle.mode all &&
+
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               bundle.everything.uri "$HTTPD_URL/everything.bundle" &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               bundle.new.uri "$HTTPD_URL/new.bundle" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+               git -c protocol.version=2 \
+                   -c transfer.bundleURI=true clone \
+               $HTTPD_URL/smart/repo3.git repo3 &&
+
+       # We should fetch _both_ bundles
+       cat >pattern <<-EOF &&
+       "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\]
+       EOF
+       grep -f pattern trace.txt &&
+       cat >pattern <<-EOF &&
+       "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/new.bundle"\]
+       EOF
+       grep -f pattern trace.txt
+'
+
+test_expect_success 'auto-discover multiple bundles from HTTP clone: creationToken heuristic' '
+       test_when_finished rm -rf "$HTTPD_DOCUMENT_ROOT_PATH/repo4.git" &&
+       test_when_finished rm -rf clone-heuristic trace*.txt &&
+
+       test_commit -C src newest &&
+       git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/newest.bundle" HEAD~1..HEAD &&
+       git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo4.git" &&
+
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/repo4.git/config" <<-EOF &&
+       [uploadPack]
+               advertiseBundleURIs = true
+
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "everything"]
+               uri = $HTTPD_URL/everything.bundle
+               creationtoken = 1
+
+       [bundle "new"]
+               uri = $HTTPD_URL/new.bundle
+               creationtoken = 2
+
+       [bundle "newest"]
+               uri = $HTTPD_URL/newest.bundle
+               creationtoken = 3
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+               git -c protocol.version=2 \
+                   -c transfer.bundleURI=true clone \
+               "$HTTPD_URL/smart/repo4.git" clone-heuristic &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/newest.bundle
+       $HTTPD_URL/new.bundle
+       $HTTPD_URL/everything.bundle
+       EOF
+
+       # We should fetch all bundles in the expected order.
+       test_remote_https_urls <trace-clone.txt >actual &&
+       test_cmp expect actual
+'
+
 # DO NOT add non-httpd-specific tests here, because the last part of this
 # test script is only executed when httpd is available and enabled.
 
index 7ccebb40c33825561618fce95afc2e96b0349f6f..9b32db8478ab77ff118a1df382a97d1f4064c2a8 100755 (executable)
@@ -7,6 +7,7 @@ test_description='test clone --reference'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 base_dir=$(pwd)
@@ -316,7 +317,7 @@ test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at obje
        for option in --local --no-hardlinks --dissociate
        do
                test_must_fail git clone $option T T$option 2>err || return 1 &&
-               test_i18ngrep "symlink.*exists" err || return 1
+               test_grep "symlink.*exists" err || return 1
        done &&
 
        # But `--shared` clones should still work, even when specifying
@@ -357,7 +358,7 @@ test_expect_success SYMLINKS 'clone repo with symlinked objects directory' '
        test_must_fail git clone --local malicious clone 2>err &&
 
        test_path_is_missing clone &&
-       grep "failed to start iterator over" err
+       grep "is a symlink, refusing to clone with --local" err
 '
 
 test_done
index 38b850c10ef8cddeb790ed276209bb5019439eae..a3055869bc7bc182e2e4af736b06785ee28bed85 100755 (executable)
@@ -15,8 +15,12 @@ test_expect_success 'preparing origin repository' '
        : >file && git add . && git commit -m1 &&
        git clone --bare . a.git &&
        git clone --bare . x &&
-       test "$(cd a.git && git config --bool core.bare)" = true &&
-       test "$(cd x && git config --bool core.bare)" = true &&
+       echo true >expect &&
+       git -C a.git config --bool core.bare >actual &&
+       test_cmp expect actual &&
+       echo true >expect &&
+       git -C x config --bool core.bare >actual &&
+       test_cmp expect actual &&
        git bundle create b1.bundle --all &&
        git bundle create b2.bundle main &&
        mkdir dir &&
@@ -29,7 +33,9 @@ test_expect_success 'preparing origin repository' '
 test_expect_success 'local clone without .git suffix' '
        git clone -l -s a b &&
        (cd b &&
-       test "$(git config --bool core.bare)" = false &&
+       echo false >expect &&
+       git config --bool core.bare >actual &&
+       test_cmp expect actual &&
        git fetch)
 '
 
@@ -59,11 +65,11 @@ test_expect_success 'Even without -l, local will make a hardlink' '
 '
 
 test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
-       echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
+       git -C a.git symbolic-ref HEAD refs/heads/nonexistent &&
        git clone a d &&
        (cd d &&
        git fetch &&
-       test ! -e .git/refs/remotes/origin/HEAD)
+       test_ref_missing refs/remotes/origin/HEAD)
 '
 
 test_expect_success 'bundle clone without .bundle suffix' '
@@ -151,7 +157,7 @@ test_expect_success 'cloning locally respects "-u" for fetching refs' '
        test_must_fail git clone --bare -u false a should_not_work.git
 '
 
-test_expect_success 'local clone from repo with corrupt refs fails gracefully' '
+test_expect_success REFFILES 'local clone from repo with corrupt refs fails gracefully' '
        git init corrupt &&
        test_commit -C corrupt one &&
        echo a >corrupt/.git/refs/heads/topic &&
index cf221e92c4d97e852f1bdefbf61f6caded87aff2..a400bcca622f464f13fe4f0a57c4d44ec8dd827b 100755 (executable)
@@ -4,6 +4,7 @@ test_description='basic clone options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -38,7 +39,7 @@ test_expect_success 'clone -o' '
 test_expect_success 'rejects invalid -o/--origin' '
 
        test_must_fail git clone -o "bad...name" parent clone-bad-name 2>err &&
-       test_i18ngrep "'\''bad...name'\'' is not a valid remote name" err
+       test_grep "'\''bad...name'\'' is not a valid remote name" err
 
 '
 
@@ -55,7 +56,7 @@ test_expect_success 'disallows --bare with --separate-git-dir' '
 
        test_must_fail git clone --bare --separate-git-dir dot-git-destiation parent clone-bare-sgd 2>err &&
        test_debug "cat err" &&
-       test_i18ngrep -e "options .--bare. and .--separate-git-dir. cannot be used together" err
+       test_grep -e "options .--bare. and .--separate-git-dir. cannot be used together" err
 
 '
 
@@ -63,14 +64,14 @@ test_expect_success 'disallows --bundle-uri with shallow options' '
        for option in --depth=1 --shallow-since=01-01-2000 --shallow-exclude=HEAD
        do
                test_must_fail git clone --bundle-uri=bundle $option from to 2>err &&
-               grep "bundle-uri is incompatible" err || return 1
+               grep "bundle-uri.* cannot be used together" err || return 1
        done
 '
 
 test_expect_success 'reject cloning shallow repository' '
        test_when_finished "rm -rf repo" &&
        test_must_fail git clone --reject-shallow shallow-repo out 2>err &&
-       test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+       test_grep -e "source repository is shallow, reject to clone." err &&
 
        git clone --no-reject-shallow shallow-repo repo
 '
@@ -78,7 +79,7 @@ test_expect_success 'reject cloning shallow repository' '
 test_expect_success 'reject cloning non-local shallow repository' '
        test_when_finished "rm -rf repo" &&
        test_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&
-       test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+       test_grep -e "source repository is shallow, reject to clone." err &&
 
        git clone --no-reject-shallow --no-local shallow-repo repo
 '
@@ -119,6 +120,16 @@ test_expect_success 'prefers -c config over --template config' '
 
 '
 
+test_expect_failure 'prefers --template config even for core.bare' '
+
+       template="$TRASH_DIRECTORY/template-with-bare-config" &&
+       mkdir "$template" &&
+       git config --file "$template/config" core.bare true &&
+       git clone "--template=$template" parent clone-bare-config &&
+       test "$(git -C clone-bare-config config --local core.bare)" = "true" &&
+       test_path_is_file clone-bare-config/HEAD
+'
+
 test_expect_success 'prefers config "clone.defaultRemoteName" over default' '
 
        test_config_global clone.defaultRemoteName from_config &&
@@ -138,7 +149,7 @@ test_expect_success 'redirected clone does not show progress' '
 
        git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
        ! grep % err &&
-       test_i18ngrep ! "Checking connectivity" err
+       test_grep ! "Checking connectivity" err
 
 '
 
index 51705aa86a172edfcdf9f41d9640f93c8667ebb9..0d1e92d9963554323a0e9a3891e43eeb263b1aff 100755 (executable)
@@ -24,7 +24,7 @@ test_expect_success 'setup' '
 test_expect_success '"verify" needs a worktree' '
        git bundle create tip.bundle -1 main &&
        nongit test_must_fail git bundle verify ../tip.bundle 2>err &&
-       test_i18ngrep "need a repository" err
+       test_grep "need a repository" err
 '
 
 test_expect_success 'annotated tags can be excluded by rev-list options' '
@@ -166,7 +166,7 @@ test_expect_success 'git bundle v3 rejects unknown capabilities' '
        @unknown=silly
        EOF
        test_must_fail git bundle verify new 2>output &&
-       test_i18ngrep "unknown capability .unknown=silly." output
+       test_grep "unknown capability .unknown=silly." output
 '
 
 test_done
index a7ec21eda5aabf16144f79a7f348e6f6be9b7507..022ed3d87c37159f758b349f6604b781a29b9327 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test cloning a repository with detached HEAD'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 head_is_detached() {
index 4b3877216ee4649a0a31986962740e0f37b5ad85..298d4befab84f2c4476c46562167a0e0a09b3981 100755 (executable)
@@ -4,6 +4,7 @@ test_description='tests for git clone -c key=value'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'clone -c sets config in cloned repo' '
@@ -102,7 +103,7 @@ test_expect_success 'set up shallow repository' '
 test_expect_success 'clone.rejectshallow=true should reject cloning shallow repo' '
        test_when_finished "rm -rf out" &&
        test_must_fail git -c clone.rejectshallow=true clone --no-local shallow-repo out 2>err &&
-       test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+       test_grep -e "source repository is shallow, reject to clone." err &&
 
        git -c clone.rejectshallow=false clone --no-local shallow-repo out
 '
@@ -110,7 +111,7 @@ test_expect_success 'clone.rejectshallow=true should reject cloning shallow repo
 test_expect_success 'option --[no-]reject-shallow override clone.rejectshallow config' '
        test_when_finished "rm -rf out" &&
        test_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local shallow-repo out 2>err &&
-       test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+       test_grep -e "source repository is shallow, reject to clone." err &&
 
        git -c clone.rejectshallow=true clone --no-reject-shallow --no-local shallow-repo out
 '
index 895f46bb9118bbf15d32f594ce79f0e638d25b6a..7708cbafa982ef3a5579f8bde075fcad40e29e40 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='test transitive info/alternate entries'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'preparing first repository' '
index 0c85ef834ab90e6fddec2ec510929df1d62acb1d..c2a2bb453eeabcc8725bffae2c537ec99b8a7d87 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test shallow cloning of repos with submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 pwd=$(pwd)
index 037941b95d2019f3c65b1b7fa39c03bfc343cd33..2da7291e37997a8b967788ee65c628ecf343b082 100755 (executable)
@@ -257,8 +257,8 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 works with submod
        test_commit -C submodule mycommit &&
 
        test_create_repo src_with_sub &&
-       test_config -C src_with_sub uploadpack.allowfilter 1 &&
-       test_config -C src_with_sub uploadpack.allowanysha1inwant 1 &&
+       git -C src_with_sub config uploadpack.allowfilter 1 &&
+       git -C src_with_sub config uploadpack.allowanysha1inwant 1 &&
 
        test_config_global protocol.file.allow always &&
 
@@ -270,6 +270,12 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 works with submod
        test_when_finished rm -rf dst
 '
 
+test_expect_success 'lazily fetched .gitmodules works' '
+       git clone --filter="blob:none" --no-checkout "file://$(pwd)/src_with_sub" dst &&
+       git -C dst fetch &&
+       test_when_finished rm -rf dst
+'
+
 test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack --fsck-objects' '
        git init src &&
        test_commit -C src x &&
@@ -347,14 +353,14 @@ test_expect_success 'upload-pack complains of bogus filter config' '
        test_must_fail git \
                -c uploadpackfilter.tree.maxdepth \
                upload-pack . >/dev/null 2>err &&
-       test_i18ngrep "unable to parse.*tree.maxdepth" err
+       test_grep "unable to parse.*tree.maxdepth" err
 '
 
 test_expect_success 'upload-pack fails banned object filters' '
        test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
        test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
                "file://$(pwd)/srv.bare" pc3 2>err &&
-       test_i18ngrep "filter '\''blob:none'\'' not supported" err
+       test_grep "filter '\''blob:none'\'' not supported" err
 '
 
 test_expect_success 'upload-pack fails banned combine object filters' '
@@ -364,14 +370,14 @@ test_expect_success 'upload-pack fails banned combine object filters' '
        test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
        test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
                --filter=blob:none "file://$(pwd)/srv.bare" pc3 2>err &&
-       test_i18ngrep "filter '\''blob:none'\'' not supported" err
+       test_grep "filter '\''blob:none'\'' not supported" err
 '
 
 test_expect_success 'upload-pack fails banned object filters with fallback' '
        test_config -C srv.bare uploadpackfilter.allow false &&
        test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
                "file://$(pwd)/srv.bare" pc3 2>err &&
-       test_i18ngrep "filter '\''blob:none'\'' not supported" err
+       test_grep "filter '\''blob:none'\'' not supported" err
 '
 
 test_expect_success 'upload-pack limits tree depth filters' '
@@ -380,7 +386,7 @@ test_expect_success 'upload-pack limits tree depth filters' '
        test_config -C srv.bare uploadpackfilter.tree.maxDepth 0 &&
        test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
                "file://$(pwd)/srv.bare" pc3 2>err &&
-       test_i18ngrep "tree filter allows max depth 0, but got 1" err &&
+       test_grep "tree filter allows max depth 0, but got 1" err &&
 
        git clone --no-checkout --filter=tree:0 "file://$(pwd)/srv.bare" pc4 &&
 
@@ -388,7 +394,7 @@ test_expect_success 'upload-pack limits tree depth filters' '
        git clone --no-checkout --filter=tree:5 "file://$(pwd)/srv.bare" pc5 &&
        test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:6 \
                "file://$(pwd)/srv.bare" pc6 2>err &&
-       test_i18ngrep "tree filter allows max depth 5, but got 6" err
+       test_grep "tree filter allows max depth 5, but got 6" err
 '
 
 test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
@@ -453,11 +459,11 @@ test_expect_success 'partial clone with unresolvable sparse filter fails cleanly
        test_must_fail git clone --no-local --bare \
                                 --filter=sparse:oid=main:no-such-name \
                                 sparse-src dst.git 2>err &&
-       test_i18ngrep "unable to access sparse blob in .main:no-such-name" err &&
+       test_grep "unable to access sparse blob in .main:no-such-name" err &&
        test_must_fail git clone --no-local --bare \
                                 --filter=sparse:oid=main \
                                 sparse-src dst.git 2>err &&
-       test_i18ngrep "unable to parse sparse filter data in" err
+       test_grep "unable to parse sparse filter data in" err
 '
 
 setup_triangle () {
@@ -487,8 +493,8 @@ setup_triangle () {
        TREE_HASH=$(git -C server rev-parse HEAD~1^{tree}) &&
        git -C promisor-remote fetch --keep "file://$(pwd)/server" "$TREE_HASH" &&
        git -C promisor-remote count-objects -v >object-count &&
-       test_i18ngrep "count: 0" object-count &&
-       test_i18ngrep "in-pack: 2" object-count &&
+       test_grep "count: 0" object-count &&
+       test_grep "in-pack: 2" object-count &&
 
        # Set it as the promisor remote of client. Thus, whenever
        # the client lazy fetches, the lazy fetch will succeed only if it is
@@ -644,6 +650,49 @@ test_expect_success 'repack does not loosen promisor objects' '
        grep "loosen_unused_packed_objects/loosened:0" trace
 '
 
+test_expect_success 'lazy-fetch in submodule succeeds' '
+       # setup
+       test_config_global protocol.file.allow always &&
+
+       test_when_finished "rm -rf src-sub" &&
+       git init src-sub &&
+       git -C src-sub config uploadpack.allowfilter 1 &&
+       git -C src-sub config uploadpack.allowanysha1inwant 1 &&
+
+       # This blob must be missing in the subsequent commit.
+       echo foo >src-sub/file &&
+       git -C src-sub add file &&
+       git -C src-sub commit -m "submodule one" &&
+       SUB_ONE=$(git -C src-sub rev-parse HEAD) &&
+
+       echo bar >src-sub/file &&
+       git -C src-sub add file &&
+       git -C src-sub commit -m "submodule two" &&
+       SUB_TWO=$(git -C src-sub rev-parse HEAD) &&
+
+       test_when_finished "rm -rf src-super" &&
+       git init src-super &&
+       git -C src-super config uploadpack.allowfilter 1 &&
+       git -C src-super config uploadpack.allowanysha1inwant 1 &&
+       git -C src-super submodule add ../src-sub src-sub &&
+
+       git -C src-super/src-sub checkout $SUB_ONE &&
+       git -C src-super add src-sub &&
+       git -C src-super commit -m "superproject one" &&
+
+       git -C src-super/src-sub checkout $SUB_TWO &&
+       git -C src-super add src-sub &&
+       git -C src-super commit -m "superproject two" &&
+
+       # the fetch
+       test_when_finished "rm -rf client" &&
+       git clone --filter=blob:none --also-filter-submodules \
+               --recurse-submodules "file://$(pwd)/src-super" client &&
+
+       # Trigger lazy-fetch from the superproject
+       git -C client restore --recurse-submodules --source=HEAD^ :/
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
@@ -699,7 +748,7 @@ test_expect_success 'upon cloning, check that all refs point to objects' '
        test_must_fail git -c protocol.version=2 clone \
                --filter=blob:none $HTTPD_URL/one_time_perl/server repo 2>err &&
 
-       test_i18ngrep "did not send all necessary objects" err &&
+       test_grep "did not send all necessary objects" err &&
 
        # Ensure that the one-time-perl script was used.
        ! test -e "$HTTPD_ROOT_PATH/one-time-perl"
index 688433824934d89354669d920acd1fb06a5da725..5a4d7936a72aac48fde876076393956bb83d42e9 100755 (executable)
@@ -5,6 +5,7 @@ test_description='Test cloning repos with submodules using remote-tracking branc
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 pwd=$(pwd)
index 3353216f09e52450a28fd10d68df9478dcfc129b..f905db0a3fdf406dd564114713cb0236fda42ce6 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test handling of --alternate-refs traversal'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Avoid test_commit because we want a specific and known set of refs:
index 6c8d4c6cf1c8e0f98e2a6cccfec798c19b1954a0..a73b4d4ff6a0d26e925ae7f6a0e8e34a1e138836 100755 (executable)
@@ -244,15 +244,28 @@ test_expect_success 'push with ssh:// using protocol v1' '
        grep "push< version 1" log
 '
 
+test_expect_success 'clone propagates object-format from empty repo' '
+       test_when_finished "rm -fr src256 dst256" &&
+
+       echo sha256 >expect &&
+       git init --object-format=sha256 src256 &&
+       git clone --no-local src256 dst256 &&
+       git -C dst256 rev-parse --show-object-format >actual &&
+
+       test_cmp expect actual
+'
+
 # Test protocol v1 with 'http://' transport
 #
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
-test_expect_success 'create repo to be served by http:// transport' '
+test_expect_success 'create repos to be served by http:// transport' '
        git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
        git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" config http.receivepack true &&
-       test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one
+       test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one &&
+       git init --object-format=sha256 "$HTTPD_DOCUMENT_ROOT_PATH/sha256" &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/sha256" config http.receivepack true
 '
 
 test_expect_success 'clone with http:// using protocol v1' '
@@ -269,6 +282,20 @@ test_expect_success 'clone with http:// using protocol v1' '
        grep "git< version 1" log
 '
 
+test_expect_success 'clone with http:// using protocol v1 with empty SHA-256 repo' '
+       GIT_TRACE_PACKET=1 GIT_TRACE_CURL=1 git -c protocol.version=1 \
+               clone "$HTTPD_URL/smart/sha256" sha256 2>log &&
+
+       echo sha256 >expect &&
+       git -C sha256 rev-parse --show-object-format >actual &&
+       test_cmp expect actual &&
+
+       # Client requested to use protocol v1
+       grep "Git-Protocol: version=1" log &&
+       # Server responded using protocol v1
+       grep "git< version 1" log
+'
+
 test_expect_success 'fetch with http:// using protocol v1' '
        test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
 
index 1896f671cb37f916bc09e50f0f5a45df81da1327..3591bc2417119c75181cc1884ea9e48a7a87646a 100755 (executable)
@@ -13,7 +13,7 @@ test_expect_success 'test capability advertisement' '
        wrong_algo sha1:sha256
        wrong_algo sha256:sha1
        EOF
-       cat >expect <<-EOF &&
+       cat >expect.base <<-EOF &&
        version 2
        agent=git/$(git version | cut -d" " -f3)
        ls-refs=unborn
@@ -21,8 +21,11 @@ test_expect_success 'test capability advertisement' '
        server-option
        object-format=$(test_oid algo)
        object-info
+       EOF
+       cat >expect.trailer <<-EOF &&
        0000
        EOF
+       cat expect.base expect.trailer >expect &&
 
        GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
                --advertise-capabilities >out &&
@@ -49,7 +52,7 @@ test_expect_success 'request invalid capability' '
        0000
        EOF
        test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
-       test_i18ngrep "unknown capability" err
+       test_grep "unknown capability" err
 '
 
 test_expect_success 'request with no command' '
@@ -59,7 +62,7 @@ test_expect_success 'request with no command' '
        0000
        EOF
        test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
-       test_i18ngrep "no command requested" err
+       test_grep "no command requested" err
 '
 
 test_expect_success 'request invalid command' '
@@ -70,7 +73,7 @@ test_expect_success 'request invalid command' '
        0000
        EOF
        test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
-       test_i18ngrep "invalid command" err
+       test_grep "invalid command" err
 '
 
 test_expect_success 'request capability as command' '
@@ -112,7 +115,7 @@ test_expect_success 'wrong object-format' '
        0000
        EOF
        test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
-       test_i18ngrep "mismatched object format" err
+       test_grep "mismatched object format" err
 '
 
 # Test the basics of ls-refs
@@ -342,4 +345,39 @@ test_expect_success 'basics of object-info' '
        test_cmp expect actual
 '
 
+test_expect_success 'test capability advertisement with uploadpack.advertiseBundleURIs' '
+       test_config uploadpack.advertiseBundleURIs true &&
+
+       cat >expect.extra <<-EOF &&
+       bundle-uri
+       EOF
+       cat expect.base \
+           expect.extra \
+           expect.trailer >expect &&
+
+       GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+               --advertise-capabilities >out &&
+       test-tool pkt-line unpack <out >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'basics of bundle-uri: dies if not enabled' '
+       test-tool pkt-line pack >in <<-EOF &&
+       command=bundle-uri
+       0000
+       EOF
+
+       cat >err.expect <<-\EOF &&
+       fatal: invalid command '"'"'bundle-uri'"'"'
+       EOF
+
+       cat >expect <<-\EOF &&
+       ERR serve: invalid command '"'"'bundle-uri'"'"'
+       EOF
+
+       test_must_fail test-tool serve-v2 --stateless-rpc <in >out 2>err.actual &&
+       test_cmp err.expect err.actual &&
+       test_must_be_empty out
+'
+
 test_done
index 5d42a355a8b81b0f2087eeb8302eb7859c04553c..6ef4971845fb4407977e98cb62b3468abfca5838 100755 (executable)
@@ -189,8 +189,8 @@ test_expect_success 'warn if using server-option with ls-remote with legacy prot
        test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
                ls-remote -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
 
-       test_i18ngrep "see protocol.version in" err &&
-       test_i18ngrep "server options require protocol version 2 or later" err
+       test_grep "see protocol.version in" err &&
+       test_grep "server options require protocol version 2 or later" err
 '
 
 test_expect_success 'clone with file:// using protocol v2' '
@@ -221,7 +221,9 @@ test_expect_success 'clone of empty repo propagates name of default branch' '
        GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone "file://$(pwd)/file_empty_parent" file_empty_child &&
-       grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+       echo refs/heads/mydefaultbranch >expect &&
+       git -C file_empty_child symbolic-ref HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '...but not if explicitly forbidden by config' '
@@ -234,7 +236,9 @@ test_expect_success '...but not if explicitly forbidden by config' '
        GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone "file://$(pwd)/file_empty_parent" file_empty_child &&
-       ! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+       echo refs/heads/main >expect &&
+       git -C file_empty_child symbolic-ref HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'bare clone propagates empty default branch' '
@@ -247,7 +251,9 @@ test_expect_success 'bare clone propagates empty default branch' '
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone --bare \
                "file://$(pwd)/file_empty_parent" file_empty_child.git &&
-       grep "refs/heads/mydefaultbranch" file_empty_child.git/HEAD
+       echo "refs/heads/mydefaultbranch" >expect &&
+       git -C file_empty_child.git symbolic-ref HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
@@ -265,10 +271,23 @@ test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone "file://$(pwd)/file_unborn_parent" \
                file_unborn_child 2>stderr &&
-       grep "refs/heads/mydefaultbranch" file_unborn_child/.git/HEAD &&
+       echo "refs/heads/mydefaultbranch" >expect &&
+       git -C file_unborn_child symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
        grep "warning: remote HEAD refers to nonexistent ref" stderr
 '
 
+test_expect_success 'clone propagates object-format from empty repo' '
+       test_when_finished "rm -fr src256 dst256" &&
+
+       echo sha256 >expect &&
+       git init --object-format=sha256 src256 &&
+       git clone src256 dst256 &&
+       git -C dst256 rev-parse --show-object-format >actual &&
+
+       test_cmp expect actual
+'
+
 test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '
        test_when_finished "rm -rf file_unborn_parent file_unborn_child.git" &&
 
@@ -284,7 +303,9 @@ test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone --bare "file://$(pwd)/file_unborn_parent" \
                file_unborn_child.git 2>stderr &&
-       grep "refs/heads/mydefaultbranch" file_unborn_child.git/HEAD &&
+       echo "refs/heads/mydefaultbranch" >expect &&
+       git -C file_unborn_child.git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
        ! grep "warning:" stderr
 '
 
@@ -304,7 +325,9 @@ test_expect_success 'defaulted HEAD uses remote branch if available' '
        git -c init.defaultBranch=branchwithstuff -c protocol.version=2 \
                clone "file://$(pwd)/file_unborn_parent" \
                file_unborn_child 2>stderr &&
-       grep "refs/heads/branchwithstuff" file_unborn_child/.git/HEAD &&
+       echo "refs/heads/branchwithstuff" >expect &&
+       git -C file_unborn_child symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
        test_path_is_file file_unborn_child/stuff.t &&
        ! grep "warning:" stderr
 '
@@ -366,8 +389,8 @@ test_expect_success 'warn if using server-option with fetch with legacy protocol
        test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C temp_child -c protocol.version=0 \
                fetch -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
 
-       test_i18ngrep "see protocol.version in" err &&
-       test_i18ngrep "server options require protocol version 2 or later" err
+       test_grep "see protocol.version in" err &&
+       test_grep "server options require protocol version 2 or later" err
 '
 
 test_expect_success 'server-options are sent when cloning' '
@@ -388,8 +411,8 @@ test_expect_success 'warn if using server-option with clone with legacy protocol
                clone --server-option=hello --server-option=world \
                "file://$(pwd)/file_parent" myclone 2>err &&
 
-       test_i18ngrep "see protocol.version in" err &&
-       test_i18ngrep "server options require protocol version 2 or later" err
+       test_grep "see protocol.version in" err &&
+       test_grep "server options require protocol version 2 or later" err
 '
 
 test_expect_success 'upload-pack respects config using protocol v2' '
@@ -484,7 +507,7 @@ test_expect_success 'partial clone warns if filter is not advertised' '
        git -C server config uploadpack.allowfilter 0 &&
        git -c protocol.version=2 \
                clone --filter=blob:none "file://$(pwd)/server" client 2>err &&
-       test_i18ngrep "filtering not recognized by server, ignoring" err
+       test_grep "filtering not recognized by server, ignoring" err
 '
 
 test_expect_success 'even with handcrafted request, filter does not work if not advertised' '
@@ -725,7 +748,34 @@ test_expect_success 'file:// --negotiate-only with protocol v0' '
                --negotiate-only \
                --negotiation-tip=$(git -C client rev-parse HEAD) \
                origin 2>err &&
-       test_i18ngrep "negotiate-only requires protocol v2" err
+       test_grep "negotiate-only requires protocol v2" err
+'
+
+test_expect_success 'push with custom path does not request v2' '
+       rm -f env.trace &&
+       git -C client push \
+               --receive-pack="env >../env.trace; git-receive-pack" \
+               origin HEAD:refs/heads/custom-push-test &&
+       test_path_is_file env.trace &&
+       ! grep ^GIT_PROTOCOL env.trace
+'
+
+test_expect_success 'fetch with custom path does request v2' '
+       rm -f env.trace &&
+       git -C client fetch \
+               --upload-pack="env >../env.trace; git-upload-pack" \
+               origin HEAD &&
+       grep ^GIT_PROTOCOL=version=2 env.trace
+'
+
+test_expect_success 'archive with custom path does not request v2' '
+       rm -f env.trace &&
+       git -C client archive \
+               --exec="env >../env.trace; git-upload-archive" \
+               --remote=origin \
+               HEAD >/dev/null &&
+       test_path_is_file env.trace &&
+       ! grep ^GIT_PROTOCOL env.trace
 '
 
 # Test protocol v2 with 'http://' transport
@@ -771,7 +821,7 @@ test_expect_success 'clone repository with http:// using protocol v2 with incomp
        # Server responded using protocol v2
        grep "git< version 2" log &&
        # Client reported appropriate failure
-       test_i18ngrep "bytes of length header were received" err
+       test_grep "bytes of length header were received" err
 '
 
 test_expect_success 'clone repository with http:// using protocol v2 with incomplete pktline body' '
@@ -788,7 +838,7 @@ test_expect_success 'clone repository with http:// using protocol v2 with incomp
        # Server responded using protocol v2
        grep "git< version 2" log &&
        # Client reported appropriate failure
-       test_i18ngrep "bytes of body are still expected" err
+       test_grep "bytes of body are still expected" err
 '
 
 test_expect_success 'clone with http:// using protocol v2 and invalid parameters' '
@@ -935,7 +985,7 @@ test_expect_success 'when server sends "ready", expect DELIM' '
 
        test_must_fail git -C http_child -c protocol.version=2 \
                fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
-       test_i18ngrep "expected packfile to be sent after .ready." err
+       test_grep "expected packfile to be sent after .ready." err
 '
 
 test_expect_success 'when server does not send "ready", expect FLUSH' '
@@ -963,7 +1013,7 @@ test_expect_success 'when server does not send "ready", expect FLUSH' '
                fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
        grep "fetch< .*acknowledgments" log &&
        ! grep "fetch< .*ready" log &&
-       test_i18ngrep "expected no other sections to be sent after no .ready." err
+       test_grep "expected no other sections to be sent after no .ready." err
 '
 
 configure_exclusion () {
@@ -1001,7 +1051,7 @@ test_expect_success 'part of packfile response provided as URI' '
        do
                git verify-pack --object-format=$(test_oid algo) --verbose $idx >out &&
                {
-                       grep "^[0-9a-f]\{16,\} " out || :
+                       grep -E "^[0-9a-f]{16,} " out || :
                } >out.objectlist &&
                if test_line_count = 1 out.objectlist
                then
@@ -1073,7 +1123,7 @@ test_expect_success 'fetching with valid packfile URI but invalid hash fails' '
                git -c protocol.version=2 \
                -c fetch.uriprotocols=http,https \
                clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
-       test_i18ngrep "pack downloaded from.*does not match expected hash" err
+       test_grep "pack downloaded from.*does not match expected hash" err
 '
 
 test_expect_success 'packfile-uri with transfer.fsckobjects' '
@@ -1114,7 +1164,7 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object'
 
        This commit object intentionally broken
        EOF
-       BOGUS=$(git -C "$P" hash-object -t commit -w --stdin <bogus-commit) &&
+       BOGUS=$(git -C "$P" hash-object -t commit -w --stdin --literally <bogus-commit) &&
        git -C "$P" branch bogus-branch "$BOGUS" &&
 
        echo my-blob >"$P/my-blob" &&
@@ -1127,7 +1177,7 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object'
        test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
                -c fetch.uriprotocols=http,https \
                clone "$HTTPD_URL/smart/http_parent" http_child 2>error &&
-       test_i18ngrep "invalid author/committer line - missing email" error
+       test_grep "invalid author/committer line - missing email" error
 '
 
 test_expect_success 'packfile-uri with transfer.fsckobjects succeeds when .gitmodules is separate from tree' '
@@ -1175,7 +1225,7 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails when .gitmodul
        test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
                -c fetch.uriprotocols=http,https \
                clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
-       test_i18ngrep "disallowed submodule name" err
+       test_grep "disallowed submodule name" err
 '
 
 test_expect_success 'packfile-uri path redacted in trace' '
@@ -1258,7 +1308,7 @@ test_expect_success 'http:// --negotiate-only without wait-for-done support' '
                --negotiate-only \
                --negotiation-tip=$(git -C client rev-parse HEAD) \
                origin 2>err &&
-       test_i18ngrep "server does not support wait-for-done" err
+       test_grep "server does not support wait-for-done" err
 '
 
 test_expect_success 'http:// --negotiate-only with protocol v0' '
@@ -1272,7 +1322,7 @@ test_expect_success 'http:// --negotiate-only with protocol v0' '
                --negotiate-only \
                --negotiation-tip=$(git -C client rev-parse HEAD) \
                origin 2>err &&
-       test_i18ngrep "negotiate-only requires protocol v2" err
+       test_grep "negotiate-only requires protocol v2" err
 '
 
 # DO NOT add non-httpd-specific tests here, because the last part of this
index df74f80061c564b7f69961f0f3d665b1afca460f..191097171bcbd4d16f107430af4ee4bb4c14493a 100755 (executable)
@@ -484,7 +484,7 @@ test_expect_success 'server is initially ahead - no ref in want' '
        cp -r "$LOCAL_PRISTINE" local &&
        inconsistency main $(test_oid numeric) &&
        test_must_fail git -C local fetch 2>err &&
-       test_i18ngrep "fatal: remote error: upload-pack: not our ref" err
+       test_grep "fatal: remote error: upload-pack: not our ref" err
 '
 
 test_expect_success 'server is initially ahead - ref in want' '
@@ -530,7 +530,7 @@ test_expect_success 'server loses a ref - ref in want' '
        echo "s/main/rain/" >"$HTTPD_ROOT_PATH/one-time-perl" &&
        test_must_fail git -C local fetch 2>err &&
 
-       test_i18ngrep "fatal: remote error: unknown ref refs/heads/rain" err
+       test_grep "fatal: remote error: unknown ref refs/heads/rain" err
 '
 
 # DO NOT add non-httpd-specific tests here, because the last part of this
index ae1a00afb09e2f38b272602a9c39d51f0fa72110..11be64fc0381134ffe30aa83480d4557c89f6660 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'extra delim packet in v2 ls-refs args' '
        } >input &&
        test_must_fail env GIT_PROTOCOL=version=2 \
                git upload-pack . <input 2>err &&
-       test_i18ngrep "expected flush after ls-refs arguments" err
+       test_grep "expected flush after ls-refs arguments" err
 '
 
 test_expect_success 'extra delim packet in v2 fetch args' '
@@ -31,7 +31,7 @@ test_expect_success 'extra delim packet in v2 fetch args' '
        } >input &&
        test_must_fail env GIT_PROTOCOL=version=2 \
                git upload-pack . <input 2>err &&
-       test_i18ngrep "expected flush after fetch arguments" err
+       test_grep "expected flush after fetch arguments" err
 '
 
 test_expect_success 'bogus symref in v0 capabilities' '
index ed38c76c29059d3f5363116db1b8231c31b0dc5a..b8a722ec27e73aeeb259e938417017607f64b5ee 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='session ID in capabilities'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 REPO="$(pwd)/repo"
diff --git a/t/t5730-protocol-v2-bundle-uri-file.sh b/t/t5730-protocol-v2-bundle-uri-file.sh
new file mode 100755 (executable)
index 0000000..37bdb72
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'file://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'file://' transport
+#
+BUNDLE_URI_PROTOCOL=file
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
diff --git a/t/t5731-protocol-v2-bundle-uri-git.sh b/t/t5731-protocol-v2-bundle-uri-git.sh
new file mode 100755 (executable)
index 0000000..8add1b3
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'git://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'git://' transport
+#
+BUNDLE_URI_PROTOCOL=git
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
diff --git a/t/t5732-protocol-v2-bundle-uri-http.sh b/t/t5732-protocol-v2-bundle-uri-http.sh
new file mode 100755 (executable)
index 0000000..129daa0
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'http://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'http://' transport
+#
+BUNDLE_URI_PROTOCOL=http
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
diff --git a/t/t5750-bundle-uri-parse.sh b/t/t5750-bundle-uri-parse.sh
new file mode 100755 (executable)
index 0000000..81bdf58
--- /dev/null
@@ -0,0 +1,290 @@
+#!/bin/sh
+
+test_description="Test bundle-uri bundle_uri_parse_line()"
+
+TEST_NO_CREATE_REPO=1
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'bundle_uri_parse_line() just URIs' '
+       cat >in <<-\EOF &&
+       bundle.one.uri=http://example.com/bundle.bdl
+       bundle.two.uri=https://example.com/bundle.bdl
+       bundle.three.uri=file:///usr/share/git/bundle.bdl
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = http://example.com/bundle.bdl
+       [bundle "two"]
+               uri = https://example.com/bundle.bdl
+       [bundle "three"]
+               uri = file:///usr/share/git/bundle.bdl
+       EOF
+
+       test-tool bundle-uri parse-key-values in >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line(): relative URIs' '
+       cat >in <<-\EOF &&
+       bundle.one.uri=bundle.bdl
+       bundle.two.uri=../bundle.bdl
+       bundle.three.uri=sub/dir/bundle.bdl
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = <uri>/bundle.bdl
+       [bundle "two"]
+               uri = bundle.bdl
+       [bundle "three"]
+               uri = <uri>/sub/dir/bundle.bdl
+       EOF
+
+       test-tool bundle-uri parse-key-values in >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line(): relative URIs and parent paths' '
+       cat >in <<-\EOF &&
+       bundle.one.uri=bundle.bdl
+       bundle.two.uri=../bundle.bdl
+       bundle.three.uri=../../bundle.bdl
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = <uri>/bundle.bdl
+       [bundle "two"]
+               uri = bundle.bdl
+       [bundle "three"]
+               uri = <uri>/../bundle.bdl
+       EOF
+
+       # TODO: We would prefer if parsing a bundle list would not cause
+       # a die() and instead would give a warning and allow the rest of
+       # a Git command to continue. This test_must_fail is necessary for
+       # now until the interface for relative_url() allows for reporting
+       # an error instead of die()ing.
+       test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+       grep "fatal: cannot strip one component off url" err
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty key or value' '
+       cat >in <<-\EOF &&
+       =bogus-value
+       bogus-key=
+       EOF
+
+       cat >err.expect <<-EOF &&
+       error: bundle-uri: line has empty key or value
+       error: bad line: '\''=bogus-value'\''
+       error: bundle-uri: line has empty key or value
+       error: bad line: '\''bogus-key='\''
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       EOF
+
+       test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+       test_cmp err.expect err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty lines' '
+       cat >in <<-\EOF &&
+       bundle.one.uri=http://example.com/bundle.bdl
+
+       bundle.two.uri=https://example.com/bundle.bdl
+
+       bundle.three.uri=file:///usr/share/git/bundle.bdl
+       EOF
+
+       cat >err.expect <<-\EOF &&
+       error: bundle-uri: got an empty line
+       error: bad line: '\'''\''
+       error: bundle-uri: got an empty line
+       error: bad line: '\'''\''
+       EOF
+
+       # We fail, but try to continue parsing regardless
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = http://example.com/bundle.bdl
+       [bundle "two"]
+               uri = https://example.com/bundle.bdl
+       [bundle "three"]
+               uri = file:///usr/share/git/bundle.bdl
+       EOF
+
+       test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+       test_cmp err.expect err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: duplicate lines' '
+       cat >in <<-\EOF &&
+       bundle.one.uri=http://example.com/bundle.bdl
+       bundle.two.uri=https://example.com/bundle.bdl
+       bundle.one.uri=https://example.com/bundle-2.bdl
+       bundle.three.uri=file:///usr/share/git/bundle.bdl
+       EOF
+
+       cat >err.expect <<-\EOF &&
+       error: bad line: '\''bundle.one.uri=https://example.com/bundle-2.bdl'\''
+       EOF
+
+       # We fail, but try to continue parsing regardless
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = http://example.com/bundle.bdl
+       [bundle "two"]
+               uri = https://example.com/bundle.bdl
+       [bundle "three"]
+               uri = file:///usr/share/git/bundle.bdl
+       EOF
+
+       test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+       test_cmp err.expect err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format: just URIs' '
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = http://example.com/bundle.bdl
+       [bundle "two"]
+               uri = https://example.com/bundle.bdl
+       [bundle "three"]
+               uri = file:///usr/share/git/bundle.bdl
+       EOF
+
+       test-tool bundle-uri parse-config expect >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format: relative URIs' '
+       cat >in <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = bundle.bdl
+       [bundle "two"]
+               uri = ../bundle.bdl
+       [bundle "three"]
+               uri = sub/dir/bundle.bdl
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = <uri>/bundle.bdl
+       [bundle "two"]
+               uri = bundle.bdl
+       [bundle "three"]
+               uri = <uri>/sub/dir/bundle.bdl
+       EOF
+
+       test-tool bundle-uri parse-config in >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format edge cases: empty key or value' '
+       cat >in1 <<-\EOF &&
+       = bogus-value
+       EOF
+
+       cat >err1 <<-EOF &&
+       error: bad config line 1 in file in1
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       EOF
+
+       test_must_fail test-tool bundle-uri parse-config in1 >actual 2>err &&
+       test_cmp err1 err &&
+       test_cmp_config_output expect actual &&
+
+       cat >in2 <<-\EOF &&
+       bogus-key =
+       EOF
+
+       cat >err2 <<-EOF &&
+       error: bad config line 1 in file in2
+       EOF
+
+       test_must_fail test-tool bundle-uri parse-config in2 >actual 2>err &&
+       test_cmp err2 err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format: creationToken heuristic' '
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+       [bundle "one"]
+               uri = http://example.com/bundle.bdl
+               creationToken = 123456
+       [bundle "two"]
+               uri = https://example.com/bundle.bdl
+               creationToken = 12345678901234567890
+       [bundle "three"]
+               uri = file:///usr/share/git/bundle.bdl
+               creationToken = 1
+       EOF
+
+       test-tool bundle-uri parse-config expect >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format edge cases: creationToken heuristic' '
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+       [bundle "one"]
+               uri = http://example.com/bundle.bdl
+               creationToken = bogus
+       EOF
+
+       test-tool bundle-uri parse-config expect >actual 2>err &&
+       grep "could not parse bundle list key creationToken with value '\''bogus'\''" err
+'
+
+test_done
index d386076dbd3f8db7611ab843efcbddca362fa06c..4e0a77f9859b392b06edd9f3696e804271d6f225 100755 (executable)
@@ -137,7 +137,7 @@ test_expect_success 'forced push' '
 test_expect_success 'cloning without refspec' '
        GIT_REMOTE_TESTGIT_NOREFSPEC=1 \
        git clone "testgit::${PWD}/server" local2 2>error &&
-       test_i18ngrep "this remote helper should implement refspec capability" error &&
+       test_grep "this remote helper should implement refspec capability" error &&
        compare_refs local2 HEAD server HEAD
 '
 
@@ -145,7 +145,7 @@ test_expect_success 'pulling without refspecs' '
        (cd local2 &&
        git reset --hard &&
        GIT_REMOTE_TESTGIT_NOREFSPEC=1 git pull 2>../error) &&
-       test_i18ngrep "this remote helper should implement refspec capability" error &&
+       test_grep "this remote helper should implement refspec capability" error &&
        compare_refs local2 HEAD server HEAD
 '
 
@@ -157,7 +157,7 @@ test_expect_success 'pushing without refspecs' '
        GIT_REMOTE_TESTGIT_NOREFSPEC=1 &&
        export GIT_REMOTE_TESTGIT_NOREFSPEC &&
        test_must_fail git push 2>../error) &&
-       test_i18ngrep "remote-helper doesn.t support push; refspec needed" error
+       test_grep "remote-helper doesn.t support push; refspec needed" error
 '
 
 test_expect_success 'pulling without marks' '
@@ -256,7 +256,7 @@ clean_mark () {
 test_expect_success 'proper failure checks for fetching' '
        (cd local &&
        test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error &&
-       test_i18ngrep -q "error while running fast-import" error
+       test_grep -q "error while running fast-import" error
        )
 '
 
index c1ef99b85c293e8a8bfc0872e3511fe8b7029213..862610256fb082a9ed62547cc51dc0a3cb754c48 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test disabling of local paths in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-proto-disable.sh"
 
index 8ac6b2a1d0a286cac120c751e5e3038e465dc074..ed773e7432694b7ccc05a4d09084f238c9ff4b42 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test disabling of git-over-tcp in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-proto-disable.sh"
 . "$TEST_DIRECTORY/lib-git-daemon.sh"
index d8da5f58d16a149647ec35876117b1874bff61f7..769c717e88b83de123a33e9c9bc4b52d192a780a 100755 (executable)
@@ -20,7 +20,7 @@ test_expect_success 'http(s) transport respects GIT_ALLOW_PROTOCOL' '
        test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
                           GIT_SMART_HTTP=0 \
                git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
-       test_i18ngrep -E "(ftp.*disabled|your curl version is too old)" stderr
+       test_grep -E "(ftp.*disabled|your curl version is too old)" stderr
 '
 
 test_expect_success 'curl limits redirects' '
index 3f084ee306517b2bc991365f6f15b76627e1d125..2e975dc70ec2c8cdb8b92cada469439a99c1d544 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test disabling of git-over-ssh in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-proto-disable.sh"
 
index 12def7bcbf91949a90066b04cb0b9fddeb3f656d..6289a2e8b03890ded5863f3472e469c28cb3034a 100755 (executable)
@@ -169,4 +169,17 @@ test_expect_success 'rev-list --count --objects' '
        test_line_count = $count actual
 '
 
+test_expect_success 'rev-list --unpacked' '
+       git repack -ad &&
+       test_commit unpacked &&
+
+       git rev-list --objects --no-object-names unpacked^.. >expect.raw &&
+       sort expect.raw >expect &&
+
+       git rev-list --all --objects --unpacked --no-object-names >actual.raw &&
+       sort actual.raw >actual &&
+
+       test_cmp expect actual
+'
+
 test_done
index 16635ecc33e5e67ab56985a5e4a071492ed22cc9..73a2465aa0eca662e69f1efd49f285786f5bd9c1 100755 (executable)
@@ -118,10 +118,10 @@ done
 
 test_expect_success 'show advice that grafts are deprecated' '
        git show HEAD 2>err &&
-       test_i18ngrep "git replace" err &&
+       test_grep "git replace" err &&
        test_config advice.graftFileDeprecated false &&
        git show HEAD 2>err &&
-       test_i18ngrep ! "git replace" err
+       test_grep ! "git replace" err
 '
 
 test_done
index 1f7d7dd20c1cf129c8a98ba59694f75e8e0de911..5cf2cee74dbcf3250491642983942905618eca92 100755 (executable)
@@ -326,19 +326,16 @@ a2
 c3
 EOF
 
-#
-# this test fails on --topo-order - a fix is required
-#
-#test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
-#l5
-#l4
-#l3
-#a4
-#c3
-#b4
-#a3
-#a2
-#EOF
+test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
+l5
+l4
+l3
+a4
+c3
+b4
+a3
+a2
+EOF
 
 test_output_expect_success 'one specified head reachable from another a4, c3, --topo-order' "list_duplicates git rev-list --topo-order a4 c3" <<EOF
 EOF
index 0729f800c3c95790946d68296240799c2dbbb5ad..ee0306aeec0b6e60fc9b62e7efbc5b659fbc1c72 100755 (executable)
@@ -18,20 +18,34 @@ test_expect_success 'no options' '
 '
 
 test_expect_success '--max-count' '
+       test_must_fail git rev-list --max-count=1q HEAD 2>error &&
+       grep "not an integer" error &&
+
        test_stdout_line_count = 0 git rev-list HEAD --max-count=0 &&
        test_stdout_line_count = 3 git rev-list HEAD --max-count=3 &&
        test_stdout_line_count = 5 git rev-list HEAD --max-count=5 &&
-       test_stdout_line_count = 5 git rev-list HEAD --max-count=10
+       test_stdout_line_count = 5 git rev-list HEAD --max-count=10 &&
+       test_stdout_line_count = 5 git rev-list HEAD --max-count=-1
 '
 
 test_expect_success '--max-count all forms' '
+       test_must_fail git rev-list -1q HEAD 2>error &&
+       grep "not an integer" error &&
+       test_must_fail git rev-list --1 HEAD &&
+       test_must_fail git rev-list -n 1q HEAD 2>error &&
+       grep "not an integer" error &&
+
        test_stdout_line_count = 1 git rev-list HEAD --max-count=1 &&
        test_stdout_line_count = 1 git rev-list HEAD -1 &&
        test_stdout_line_count = 1 git rev-list HEAD -n1 &&
-       test_stdout_line_count = 1 git rev-list HEAD -n 1
+       test_stdout_line_count = 1 git rev-list HEAD -n 1 &&
+       test_stdout_line_count = 5 git rev-list HEAD -n -1
 '
 
 test_expect_success '--skip' '
+       test_must_fail git rev-list --skip 1q HEAD 2>error &&
+       grep "not an integer" error &&
+
        test_stdout_line_count = 5 git rev-list HEAD --skip=0 &&
        test_stdout_line_count = 2 git rev-list HEAD --skip=3 &&
        test_stdout_line_count = 0 git rev-list HEAD --skip=5 &&
index 41d0ca00b1c025beaadedf195586fb81db835bbb..573eb97a0f7f06e45d3358e65be8b55816ea47b3 100755 (executable)
@@ -493,7 +493,7 @@ test_expect_success 'empty email' '
        test_tick &&
        C=$(GIT_AUTHOR_EMAIL= git commit-tree HEAD^{tree} </dev/null) &&
        A=$(git show --pretty=format:%an,%ae,%ad%n -s $C) &&
-       verbose test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
+       test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
 '
 
 test_expect_success 'del LF before empty (1)' '
index 5a67bbc760fdc8c2b53dc7721a144e033bf8e934..91db8fafe83e3883923b71e36bbd8bc11bfde1f6 100755 (executable)
@@ -5,6 +5,7 @@ test_description='ancestor culling and limiting by parent number'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_revlist () {
@@ -62,6 +63,17 @@ test_expect_success 'setup roots, merges and octopuses' '
        git checkout main
 '
 
+test_expect_success 'parse --max-parents & --min-parents' '
+       test_must_fail git rev-list --max-parents=1q HEAD 2>error &&
+       grep "not an integer" error &&
+
+       test_must_fail git rev-list --min-parents=1q HEAD 2>error &&
+       grep "not an integer" error &&
+
+       git rev-list --max-parents=1 --min-parents=1 HEAD &&
+       git rev-list --max-parents=-1 --min-parents=-1 HEAD
+'
+
 test_expect_success 'rev-list roots' '
 
        check_revlist "--max-parents=0" one five
index bad02cf5b83dbc014c23cc8ac66135e0cf095138..b2e422cf0f7eaa43411fdb6f2bb769ee7294a322 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rev-list should notice bad commits'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Note:
index c9bedd29cba4aac6b7db1aa8f1698fc5eac11b15..16b8bd1d090eae0449e409cd8313ffd45f769208 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='--all includes detached HEADs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
index 05162512a09d422a9592a112a1d613d34be4f5ed..4821b90e7479ad8f4878ab4432ce0e9d2ce47de5 100755 (executable)
@@ -48,7 +48,9 @@ test_expect_success setup '
                        git add file-$i &&
                        test_tick &&
                        git commit -m side-$i || exit
-               done
+               done &&
+
+               git update-ref refs/heads/-dashed-branch HEAD
        )
 '
 
@@ -60,6 +62,13 @@ check side-1 ^side-7 -- file-2
 check side-3 ^side-4 -- file-3
 check side-3 ^side-2
 check side-3 ^side-2 -- file-1
+check --all
+check --all --not --branches
+check --glob=refs/heads
+check --glob=refs/heads --
+check --glob=refs/heads -- file-1
+check --end-of-options -dashed-branch
+check --all --not refs/heads/main
 
 test_expect_success 'not only --stdin' '
        cat >expect <<-EOF &&
@@ -78,4 +87,65 @@ test_expect_success 'not only --stdin' '
        test_cmp expect actual
 '
 
+test_expect_success 'pseudo-opt with missing value' '
+       cat >input <<-EOF &&
+       --glob
+       refs/heads
+       EOF
+
+       cat >expect <<-EOF &&
+       fatal: Option ${SQ}--glob${SQ} requires a value
+       EOF
+
+       test_must_fail git rev-list --stdin <input 2>error &&
+       test_cmp expect error
+'
+
+test_expect_success 'pseudo-opt with invalid value' '
+       cat >input <<-EOF &&
+       --no-walk=garbage
+       EOF
+
+       cat >expect <<-EOF &&
+       error: invalid argument to --no-walk
+       fatal: invalid option ${SQ}--no-walk=garbage${SQ} in --stdin mode
+       EOF
+
+       test_must_fail git rev-list --stdin <input 2>error &&
+       test_cmp expect error
+'
+
+test_expect_success 'unknown option without --end-of-options' '
+       cat >input <<-EOF &&
+       -dashed-branch
+       EOF
+
+       cat >expect <<-EOF &&
+       fatal: invalid option ${SQ}-dashed-branch${SQ} in --stdin mode
+       EOF
+
+       test_must_fail git rev-list --stdin <input 2>error &&
+       test_cmp expect error
+'
+
+test_expect_success '--not on command line does not influence revisions read via --stdin' '
+       cat >input <<-EOF &&
+       refs/heads/main
+       EOF
+       git rev-list refs/heads/main >expect &&
+
+       git rev-list refs/heads/main --not --stdin <input >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--not via stdin does not influence revisions from command line' '
+       cat >input <<-EOF &&
+       --not
+       EOF
+       git rev-list refs/heads/main >expect &&
+
+       git rev-list refs/heads/main --stdin refs/heads/main <input >actual &&
+       test_cmp expect actual
+'
+
 test_done
index e1abc5c2b32f57526c277e6ac62fa1b45f2f46bd..3b181f771c25a3d8a074f7d0472bf1f0422f582b 100755 (executable)
@@ -187,6 +187,44 @@ test_expect_success 'rev-parse --exclude=ref with --remotes=glob' '
        compare rev-parse "--exclude=upstream/x --remotes=upstream/*" "upstream/one upstream/two"
 '
 
+for section in fetch receive uploadpack
+do
+       test_expect_success "rev-parse --exclude-hidden=$section with --all" '
+               compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags" "--exclude-hidden=$section --all"
+       '
+
+       test_expect_success "rev-parse --exclude-hidden=$section with --all" '
+               compare "-c transfer.hideRefs=refs/heads/subspace/ rev-parse" "--exclude=refs/heads/subspace/* --all" "--exclude-hidden=$section --all"
+       '
+
+       test_expect_success "rev-parse --exclude-hidden=$section with --glob" '
+               compare "-c transfer.hideRefs=refs/heads/subspace/ rev-parse" "--exclude=refs/heads/subspace/* --glob=refs/heads/*" "--exclude-hidden=$section --glob=refs/heads/*"
+       '
+
+       test_expect_success "rev-parse --exclude-hidden=$section can be passed once per pseudo-ref" '
+               compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags --branches --tags" "--exclude-hidden=$section --all --exclude-hidden=$section --all"
+       '
+
+       test_expect_success "rev-parse --exclude-hidden=$section can only be passed once per pseudo-ref" '
+               echo "fatal: --exclude-hidden= passed more than once" >expected &&
+               test_must_fail git rev-parse --exclude-hidden=$section --exclude-hidden=$section 2>err &&
+               test_cmp expected err
+       '
+
+       for pseudoopt in branches tags remotes
+       do
+               test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt" '
+                       test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt 2>err &&
+                       test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
+               '
+
+               test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt=pattern" '
+                       test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
+                       test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
+               '
+       done
+done
+
 test_expect_success 'rev-list --exclude=glob with --branches=glob' '
        compare rev-list "--exclude=subspace-* --branches=sub*" "subspace/one subspace/two"
 '
index 833205125abf5a2673ae0cbede62653fbfd151c8..3e6bcbf30cdaf0e623ae73858a4f98838e03255c 100755 (executable)
@@ -10,6 +10,14 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bundle.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+for cmd in create verify list-heads unbundle
+do
+       test_expect_success "usage: git bundle $cmd needs an argument" '
+               test_expect_code 129 git bundle $cmd
+       '
+done
 
 # Create a commit or tag and set the variable with the object ID.
 test_commit_setvar () {
@@ -559,4 +567,88 @@ test_expect_success 'cloning from filtered bundle has useful error' '
        grep "cannot clone from filtered bundle" err
 '
 
+test_expect_success 'verify catches unreachable, broken prerequisites' '
+       test_when_finished rm -rf clone-from clone-to &&
+       git init clone-from &&
+       (
+               cd clone-from &&
+               git checkout -b base &&
+               test_commit A &&
+               git checkout -b tip &&
+               git commit --allow-empty -m "will drop by shallow" &&
+               git commit --allow-empty -m "will keep by shallow" &&
+               git commit --allow-empty -m "for bundle, not clone" &&
+               git bundle create tip.bundle tip~1..tip &&
+               git reset --hard HEAD~1 &&
+               git checkout base
+       ) &&
+       BAD_OID=$(git -C clone-from rev-parse tip~1) &&
+       TIP_OID=$(git -C clone-from rev-parse tip) &&
+       git clone --depth=1 --no-single-branch \
+               "file://$(pwd)/clone-from" clone-to &&
+       (
+               cd clone-to &&
+
+               # Set up broken history by removing shallow markers
+               git update-ref -d refs/remotes/origin/tip &&
+               rm .git/shallow &&
+
+               # Verify should fail
+               test_must_fail git bundle verify \
+                       ../clone-from/tip.bundle 2>err &&
+               grep "some prerequisite commits .* are not connected" err &&
+               test_line_count = 1 err &&
+
+               # Unbundling should fail
+               test_must_fail git bundle unbundle \
+                       ../clone-from/tip.bundle 2>err &&
+               grep "some prerequisite commits .* are not connected" err &&
+               test_line_count = 1 err
+       )
+'
+
+test_expect_success 'bundle progress includes write phase' '
+       GIT_PROGRESS_DELAY=0 \
+               git bundle create --progress out.bundle --all 2>err &&
+       grep 'Writing' err
+'
+
+test_expect_success TTY 'create --quiet disables all bundle progress' '
+       test_terminal env GIT_PROGRESS_DELAY=0 \
+               git bundle create --quiet out.bundle --all 2>err &&
+       test_must_be_empty err
+'
+
+test_expect_success 'bundle progress with --no-quiet' '
+       GIT_PROGRESS_DELAY=0 \
+               git bundle create --no-quiet out.bundle --all 2>err &&
+       grep "%" err
+'
+
+test_expect_success 'read bundle over stdin' '
+       git bundle create some.bundle HEAD &&
+
+       git bundle verify - <some.bundle 2>err &&
+       grep "<stdin> is okay" err &&
+
+       git bundle list-heads some.bundle >expect &&
+       git bundle list-heads - <some.bundle >actual &&
+       test_cmp expect actual &&
+
+       git bundle unbundle some.bundle >expect &&
+       git bundle unbundle - <some.bundle >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'send a bundle to standard output' '
+       git bundle create - --all HEAD >bundle-one &&
+       mkdir -p down &&
+       git -C down bundle create - --all HEAD >bundle-two &&
+       git bundle verify bundle-one &&
+       git bundle verify bundle-two &&
+       git ls-remote bundle-one >expect &&
+       git ls-remote bundle-two >actual &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
new file mode 100755 (executable)
index 0000000..51df021
--- /dev/null
@@ -0,0 +1,164 @@
+#!/bin/sh
+
+test_description='git rev-list --exclude-hidden test'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit_bulk --id=commit --ref=refs/heads/branch 1 &&
+       COMMIT=$(git rev-parse refs/heads/branch) &&
+       test_commit_bulk --id=tag --ref=refs/tags/lightweight 1 &&
+       TAG=$(git rev-parse refs/tags/lightweight) &&
+       test_commit_bulk --id=hidden --ref=refs/hidden/commit 1 &&
+       HIDDEN=$(git rev-parse refs/hidden/commit) &&
+       test_commit_bulk --id=namespace --ref=refs/namespaces/namespace/refs/namespaced/commit 1 &&
+       NAMESPACE=$(git rev-parse refs/namespaces/namespace/refs/namespaced/commit)
+'
+
+test_expect_success 'invalid section' '
+       echo "fatal: unsupported section for hidden refs: unsupported" >expected &&
+       test_must_fail git rev-list --exclude-hidden=unsupported 2>err &&
+       test_cmp expected err
+'
+
+for section in fetch receive uploadpack
+do
+       test_expect_success "$section: passed multiple times" '
+               echo "fatal: --exclude-hidden= passed more than once" >expected &&
+               test_must_fail git rev-list --exclude-hidden=$section --exclude-hidden=$section 2>err &&
+               test_cmp expected err
+       '
+
+       test_expect_success "$section: without hiddenRefs" '
+               git rev-list --exclude-hidden=$section --all >out &&
+               cat >expected <<-EOF &&
+               $NAMESPACE
+               $HIDDEN
+               $TAG
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: hidden via transfer.hideRefs" '
+               git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+               cat >expected <<-EOF &&
+               $NAMESPACE
+               $TAG
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: hidden via $section.hideRefs" '
+               git -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+               cat >expected <<-EOF &&
+               $NAMESPACE
+               $TAG
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: respects both transfer.hideRefs and $section.hideRefs" '
+               git -c transfer.hideRefs=refs/tags/ -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+               cat >expected <<-EOF &&
+               $NAMESPACE
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: negation without hidden refs marks everything as uninteresting" '
+               git rev-list --all --exclude-hidden=$section --not --all >out &&
+               test_must_be_empty out
+       '
+
+       test_expect_success "$section: negation with hidden refs marks them as interesting" '
+               git -c transfer.hideRefs=refs/hidden/ rev-list --all --exclude-hidden=$section --not --all >out &&
+               cat >expected <<-EOF &&
+               $HIDDEN
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: hidden refs and excludes work together" '
+               git -c transfer.hideRefs=refs/hidden/ rev-list --exclude=refs/tags/* --exclude-hidden=$section --all >out &&
+               cat >expected <<-EOF &&
+               $NAMESPACE
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: excluded hidden refs get reset" '
+               git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=$section --all --all >out &&
+               cat >expected <<-EOF &&
+               $NAMESPACE
+               $HIDDEN
+               $TAG
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: excluded hidden refs can be used with multiple pseudo-refs" '
+               git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=$section --all --exclude-hidden=$section --all >out &&
+               test_must_be_empty out
+       '
+
+       test_expect_success "$section: works with --glob" '
+               git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --glob=refs/h* >out &&
+               cat >expected <<-EOF &&
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: operates on stripped refs by default" '
+               GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaced/ rev-list --exclude-hidden=$section --all >out &&
+               cat >expected <<-EOF &&
+               $HIDDEN
+               $TAG
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: does not hide namespace by default" '
+               GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaces/namespace/ rev-list --exclude-hidden=$section --all >out &&
+               cat >expected <<-EOF &&
+               $NAMESPACE
+               $HIDDEN
+               $TAG
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       test_expect_success "$section: can operate on unstripped refs" '
+               GIT_NAMESPACE=namespace git -c transfer.hideRefs=^refs/namespaces/namespace/ rev-list --exclude-hidden=$section --all >out &&
+               cat >expected <<-EOF &&
+               $HIDDEN
+               $TAG
+               $COMMIT
+               EOF
+               test_cmp expected out
+       '
+
+       for pseudoopt in remotes branches tags
+       do
+               test_expect_success "$section: fails with --$pseudoopt" '
+                       test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt 2>err &&
+                       test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
+               '
+
+               test_expect_success "$section: fails with --$pseudoopt=pattern" '
+                       test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
+                       test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
+               '
+       done
+done
+
+test_done
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh
new file mode 100755 (executable)
index 0000000..2116727
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='handling of missing objects in rev-list'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# We setup the repository with two commits, this way HEAD is always
+# available and we can hide commit 1.
+test_expect_success 'create repository and alternate directory' '
+       test_commit 1 &&
+       test_commit 2 &&
+       test_commit 3
+'
+
+# We manually corrupt the repository, which means that the commit-graph may
+# contain references to already-deleted objects. We thus need to enable
+# commit-graph paranoia to not returned these deleted commits from the graph.
+GIT_COMMIT_GRAPH_PARANOIA=true
+export GIT_COMMIT_GRAPH_PARANOIA
+
+for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+       test_expect_success "rev-list --missing=error fails with missing object $obj" '
+               oid="$(git rev-parse $obj)" &&
+               path=".git/objects/$(test_oid_to_path $oid)" &&
+
+               mv "$path" "$path.hidden" &&
+               test_when_finished "mv $path.hidden $path" &&
+
+               test_must_fail git rev-list --missing=error --objects \
+                       --no-object-names HEAD
+       '
+done
+
+for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+       for action in "allow-any" "print"
+       do
+               test_expect_success "rev-list --missing=$action with missing $obj" '
+                       oid="$(git rev-parse $obj)" &&
+                       path=".git/objects/$(test_oid_to_path $oid)" &&
+
+                       # Before the object is made missing, we use rev-list to
+                       # get the expected oids.
+                       git rev-list --objects --no-object-names \
+                               HEAD ^$obj >expect.raw &&
+
+                       # Blobs are shared by all commits, so evethough a commit/tree
+                       # might be skipped, its blob must be accounted for.
+                       if [ $obj != "HEAD:1.t" ]; then
+                               echo $(git rev-parse HEAD:1.t) >>expect.raw &&
+                               echo $(git rev-parse HEAD:2.t) >>expect.raw
+                       fi &&
+
+                       mv "$path" "$path.hidden" &&
+                       test_when_finished "mv $path.hidden $path" &&
+
+                       git rev-list --missing=$action --objects --no-object-names \
+                               HEAD >actual.raw &&
+
+                       # When the action is to print, we should also add the missing
+                       # oid to the expect list.
+                       case $action in
+                       allow-any)
+                               ;;
+                       print)
+                               grep ?$oid actual.raw &&
+                               echo ?$oid >>expect.raw
+                               ;;
+                       esac &&
+
+                       sort actual.raw >actual &&
+                       sort expect.raw >expect &&
+                       test_cmp expect actual
+               '
+       done
+done
+
+test_done
index 83931d482fb23b0b774310a49e8dcd6ad7eac4b9..561080bf240d6d5de7b557c6332995846f60e56e 100755 (executable)
@@ -34,6 +34,36 @@ HASH2=
 HASH3=
 HASH4=
 
+test_bisect_usage () {
+       local code="$1" &&
+       shift &&
+       cat >expect &&
+       test_expect_code $code "$@" >out 2>actual &&
+       test_must_be_empty out &&
+       test_cmp expect actual
+}
+
+test_expect_success 'bisect usage' "
+       test_bisect_usage 1 git bisect reset extra1 extra2 <<-\EOF &&
+       error: 'git bisect reset' requires either no argument or a commit
+       EOF
+       test_bisect_usage 1 git bisect terms extra1 extra2 <<-\EOF &&
+       error: 'git bisect terms' requires 0 or 1 argument
+       EOF
+       test_bisect_usage 1 git bisect next extra1 <<-\EOF &&
+       error: 'git bisect next' requires 0 arguments
+       EOF
+       test_bisect_usage 1 git bisect log extra1 <<-\EOF &&
+       error: We are not bisecting.
+       EOF
+       test_bisect_usage 1 git bisect replay <<-\EOF &&
+       error: no logfile given
+       EOF
+       test_bisect_usage 1 git bisect run <<-\EOF
+       error: 'git bisect run' failed: no command provided.
+       EOF
+"
+
 test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
      add_line_into_file "1: Hello World" hello &&
      HASH1=$(git rev-parse --verify HEAD) &&
@@ -92,6 +122,29 @@ test_expect_success 'bisect start without -- takes unknown arg as pathspec' '
        grep bar ".git/BISECT_NAMES"
 '
 
+test_expect_success 'bisect reset: back in a branch checked out also elsewhere' '
+       echo "shared" > branch.expect &&
+       test_bisect_reset() {
+               git -C $1 bisect start &&
+               git -C $1 bisect good $HASH1 &&
+               git -C $1 bisect bad $HASH3 &&
+               git -C $1 bisect reset &&
+               git -C $1 branch --show-current > branch.output &&
+               cmp branch.expect branch.output
+       } &&
+       test_when_finished "
+               git worktree remove wt1 &&
+               git worktree remove wt2 &&
+               git branch -d shared
+       " &&
+       git worktree add wt1 -b shared &&
+       git worktree add wt2 -f shared &&
+       # we test in both worktrees to ensure that works
+       # as expected with "first" and "next" worktrees
+       test_bisect_reset wt1 &&
+       test_bisect_reset wt2
+'
+
 test_expect_success 'bisect reset: back in the main branch' '
        git bisect reset &&
        echo "* main" > branch.expect &&
@@ -117,6 +170,12 @@ test_expect_success 'bisect reset when not bisecting' '
        cmp branch.expect branch.output
 '
 
+test_expect_success 'bisect reset cleans up even when not bisecting' '
+       echo garbage >.git/BISECT_LOG &&
+       git bisect reset &&
+       test_path_is_missing .git/BISECT_LOG
+'
+
 test_expect_success 'bisect reset removes packed refs' '
        git bisect reset &&
        git bisect start &&
@@ -167,7 +226,7 @@ test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if
        cp .git/BISECT_START saved &&
        test_must_fail git bisect start $HASH4 foo -- &&
        git branch > branch.output &&
-       test_i18ngrep "* (no branch, bisect started on other)" branch.output > /dev/null &&
+       test_grep "* (no branch, bisect started on other)" branch.output > /dev/null &&
        test_cmp saved .git/BISECT_START
 '
 test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
@@ -252,6 +311,124 @@ test_expect_success 'bisect skip: with commit both bad and skipped' '
        grep $HASH4 my_bisect_log.txt
 '
 
+test_bisect_run_args () {
+       test_when_finished "rm -f run.sh actual" &&
+       >actual &&
+       cat >expect.args &&
+       cat <&6 >expect.out &&
+       cat <&7 >expect.err &&
+       write_script run.sh <<-\EOF &&
+       while test $# != 0
+       do
+               echo "<$1>" &&
+               shift
+       done >actual.args
+       EOF
+
+       test_when_finished "git bisect reset" &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       git bisect run ./run.sh $@ >actual.out.raw 2>actual.err &&
+       # Prune just the log output
+       sed -n \
+               -e '/^Author:/d' \
+               -e '/^Date:/d' \
+               -e '/^$/d' \
+               -e '/^commit /d' \
+               -e '/^ /d' \
+               -e 'p' \
+               <actual.out.raw >actual.out &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err &&
+       test_cmp expect.args actual.args
+}
+
+test_expect_success 'git bisect run: args, stdout and stderr with no arguments' "
+       test_bisect_run_args <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+       EOF_ARGS
+       running './run.sh'
+       $HASH4 is the first bad commit
+       bisect found first bad commit
+       EOF_OUT
+       EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--" argument' "
+       test_bisect_run_args -- <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+       <-->
+       EOF_ARGS
+       running './run.sh' '--'
+       $HASH4 is the first bad commit
+       bisect found first bad commit
+       EOF_OUT
+       EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--log foo --no-log bar" arguments' "
+       test_bisect_run_args --log foo --no-log bar <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+       <--log>
+       <foo>
+       <--no-log>
+       <bar>
+       EOF_ARGS
+       running './run.sh' '--log' 'foo' '--no-log' 'bar'
+       $HASH4 is the first bad commit
+       bisect found first bad commit
+       EOF_OUT
+       EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--bisect-start" argument' "
+       test_bisect_run_args --bisect-start <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+       <--bisect-start>
+       EOF_ARGS
+       running './run.sh' '--bisect-start'
+       $HASH4 is the first bad commit
+       bisect found first bad commit
+       EOF_OUT
+       EOF_ERR
+"
+
+test_expect_success 'git bisect run: negative exit code' "
+       write_script fail.sh <<-'EOF' &&
+       exit 255
+       EOF
+       cat <<-'EOF' >expect &&
+       bisect run failed: exit code -1 from './fail.sh' is < 0 or >= 128
+       EOF
+       test_when_finished 'git bisect reset' &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       ! git bisect run ./fail.sh 2>err &&
+       sed -En 's/.*(bisect.*code) (-?[0-9]+) (from.*)/\1 -1 \3/p' err >actual &&
+       test_cmp expect actual
+"
+
+test_expect_success 'git bisect run: unable to verify on good' "
+       write_script fail.sh <<-'EOF' &&
+       head=\$(git rev-parse --verify HEAD)
+       good=\$(git rev-parse --verify $HASH1)
+       if test "\$head" = "\$good"
+       then
+               exit 255
+       else
+               exit 127
+       fi
+       EOF
+       cat <<-'EOF' >expect &&
+       unable to verify './fail.sh' on good revision
+       EOF
+       test_when_finished 'git bisect reset' &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       ! git bisect run ./fail.sh 2>err &&
+       sed -n 's/.*\(unable to verify.*\)/\1/p' err >actual &&
+       test_cmp expect actual
+"
+
 # We want to automatically find the commit that
 # added "Another" into hello.
 test_expect_success '"git bisect run" simple case' '
@@ -266,6 +443,16 @@ test_expect_success '"git bisect run" simple case' '
        git bisect reset
 '
 
+# We want to make sure no arguments has been eaten
+test_expect_success '"git bisect run" simple case' '
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       git bisect run printf "%s %s\n" reset --bisect-skip >my_bisect_log.txt &&
+       grep -e "reset --bisect-skip" my_bisect_log.txt &&
+       git bisect reset
+'
+
 # We want to automatically find the commit that
 # added "Ciao" into hello.
 test_expect_success '"git bisect run" with more complex "git bisect start"' '
@@ -407,7 +594,7 @@ test_expect_success 'bisect starting with a detached HEAD' '
 test_expect_success 'bisect errors out if bad and good are mistaken' '
        git bisect reset &&
        test_must_fail git bisect start $HASH2 $HASH4 2> rev_list_error &&
-       test_i18ngrep "mistook good and bad" rev_list_error &&
+       test_grep "mistook good and bad" rev_list_error &&
        git bisect reset
 '
 
@@ -449,7 +636,7 @@ test_expect_success 'side branch creation' '
 
 test_expect_success 'good merge base when good and bad are siblings' '
        git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
-       test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+       test_grep "merge base must be tested" my_bisect_log.txt &&
        grep $HASH4 my_bisect_log.txt &&
        git bisect good > my_bisect_log.txt &&
        ! grep "merge base must be tested" my_bisect_log.txt &&
@@ -458,7 +645,7 @@ test_expect_success 'good merge base when good and bad are siblings' '
 '
 test_expect_success 'skipped merge base when good and bad are siblings' '
        git bisect start "$SIDE_HASH7" "$HASH7" > my_bisect_log.txt &&
-       test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+       test_grep "merge base must be tested" my_bisect_log.txt &&
        grep $HASH4 my_bisect_log.txt &&
        git bisect skip > my_bisect_log.txt 2>&1 &&
        grep "warning" my_bisect_log.txt &&
@@ -468,11 +655,11 @@ test_expect_success 'skipped merge base when good and bad are siblings' '
 
 test_expect_success 'bad merge base when good and bad are siblings' '
        git bisect start "$HASH7" HEAD > my_bisect_log.txt &&
-       test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+       test_grep "merge base must be tested" my_bisect_log.txt &&
        grep $HASH4 my_bisect_log.txt &&
        test_must_fail git bisect bad > my_bisect_log.txt 2>&1 &&
-       test_i18ngrep "merge base $HASH4 is bad" my_bisect_log.txt &&
-       test_i18ngrep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
+       test_grep "merge base $HASH4 is bad" my_bisect_log.txt &&
+       test_grep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
        git bisect reset
 '
 
@@ -523,9 +710,9 @@ test_expect_success '"git bisect run --first-parent" simple case' '
 
 test_expect_success 'good merge bases when good and bad are siblings' '
        git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
-       test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+       test_grep "merge base must be tested" my_bisect_log.txt &&
        git bisect good > my_bisect_log2.txt &&
-       test_i18ngrep "merge base must be tested" my_bisect_log2.txt &&
+       test_grep "merge base must be tested" my_bisect_log2.txt &&
        {
                {
                        grep "$SIDE_HASH5" my_bisect_log.txt &&
@@ -540,14 +727,14 @@ test_expect_success 'good merge bases when good and bad are siblings' '
 
 test_expect_success 'optimized merge base checks' '
        git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
-       test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+       test_grep "merge base must be tested" my_bisect_log.txt &&
        grep "$HASH4" my_bisect_log.txt &&
        git bisect good > my_bisect_log2.txt &&
        test -f ".git/BISECT_ANCESTORS_OK" &&
        test "$HASH6" = $(git rev-parse --verify HEAD) &&
        git bisect bad &&
        git bisect good "$A_HASH" > my_bisect_log4.txt &&
-       test_i18ngrep "merge base must be tested" my_bisect_log4.txt &&
+       test_grep "merge base must be tested" my_bisect_log4.txt &&
        test_path_is_missing ".git/BISECT_ANCESTORS_OK"
 '
 
@@ -625,7 +812,7 @@ test_expect_success 'skipping away from skipped commit' '
 
 test_expect_success 'erroring out when using bad path arguments' '
        test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
-       test_i18ngrep "bad path arguments" error.txt
+       test_grep "bad path arguments" error.txt
 '
 
 test_expect_success 'test bisection on bare repo - --no-checkout specified' '
@@ -900,6 +1087,16 @@ test_expect_success 'bisect start with one term1 and term2' '
        git bisect reset
 '
 
+test_expect_success 'bogus command does not start bisect' '
+       git bisect reset &&
+       test_must_fail git bisect --bisect-terms 1 2 2>out &&
+       ! grep "You need to start" out &&
+       test_must_fail git bisect --bisect-terms 2>out &&
+       ! grep "You need to start" out &&
+       grep "git bisect.*visualize" out &&
+       git bisect reset
+'
+
 test_expect_success 'bisect replay with term1 and term2' '
        git bisect replay log_to_replay.txt >bisect_result &&
        grep "$HASH2 is the first term1 commit" bisect_result &&
@@ -985,12 +1182,11 @@ test_expect_success 'git bisect reset cleans bisection state properly' '
        git bisect bad $HASH4 &&
        git bisect reset &&
        test -z "$(git for-each-ref "refs/bisect/*")" &&
-       test_path_is_missing ".git/BISECT_EXPECTED_REV" &&
+       test_ref_missing BISECT_EXPECTED_REV &&
        test_path_is_missing ".git/BISECT_ANCESTORS_OK" &&
        test_path_is_missing ".git/BISECT_LOG" &&
        test_path_is_missing ".git/BISECT_RUN" &&
        test_path_is_missing ".git/BISECT_TERMS" &&
-       test_path_is_missing ".git/head-name" &&
        test_path_is_missing ".git/BISECT_HEAD" &&
        test_path_is_missing ".git/BISECT_START"
 '
@@ -1053,4 +1249,14 @@ test_expect_success 'bisect state output with bad commit' '
        grep -F "waiting for good commit(s), bad commit known" output
 '
 
+test_expect_success 'verify correct error message' '
+       git bisect reset &&
+       git bisect start $HASH4 $HASH1 &&
+       write_script test_script.sh <<-\EOF &&
+       rm .git/BISECT*
+       EOF
+       test_must_fail git bisect run ./test_script.sh 2>error &&
+       grep "git bisect good.*exited with error code" error
+'
+
 test_done
index a313849406ddb4b7f104ee06eabc9068d5551425..acc281c116815e2fbec71ca6c78fc90ed6280f73 100755 (executable)
@@ -5,6 +5,7 @@ test_description='remote tracking stats'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 advance () {
@@ -82,13 +83,13 @@ test_expect_success 'checkout (diverged from upstream)' '
        (
                cd test && git checkout b1
        ) >actual &&
-       test_i18ngrep "have 1 and 1 different" actual
+       test_grep "have 1 and 1 different" actual
 '
 
 test_expect_success 'checkout with local tracked branch' '
        git checkout main &&
        git checkout follower >actual &&
-       test_i18ngrep "is ahead of" actual
+       test_grep "is ahead of" actual
 '
 
 test_expect_success 'checkout (upstream is gone)' '
@@ -96,14 +97,14 @@ test_expect_success 'checkout (upstream is gone)' '
                cd test &&
                git checkout b5
        ) >actual &&
-       test_i18ngrep "is based on .*, but the upstream is gone." actual
+       test_grep "is based on .*, but the upstream is gone." actual
 '
 
 test_expect_success 'checkout (up-to-date with upstream)' '
        (
                cd test && git checkout b6
        ) >actual &&
-       test_i18ngrep "Your branch is up to date with .origin/main" actual
+       test_grep "Your branch is up to date with .origin/main" actual
 '
 
 test_expect_success 'status (diverged from upstream)' '
@@ -113,7 +114,7 @@ test_expect_success 'status (diverged from upstream)' '
                # reports nothing to commit
                test_must_fail git commit --dry-run
        ) >actual &&
-       test_i18ngrep "have 1 and 1 different" actual
+       test_grep "have 1 and 1 different" actual
 '
 
 test_expect_success 'status (upstream is gone)' '
@@ -123,7 +124,7 @@ test_expect_success 'status (upstream is gone)' '
                # reports nothing to commit
                test_must_fail git commit --dry-run
        ) >actual &&
-       test_i18ngrep "is based on .*, but the upstream is gone." actual
+       test_grep "is based on .*, but the upstream is gone." actual
 '
 
 test_expect_success 'status (up-to-date with upstream)' '
@@ -133,7 +134,7 @@ test_expect_success 'status (up-to-date with upstream)' '
                # reports nothing to commit
                test_must_fail git commit --dry-run
        ) >actual &&
-       test_i18ngrep "Your branch is up to date with .origin/main" actual
+       test_grep "Your branch is up to date with .origin/main" actual
 '
 
 cat >expect <<\EOF
@@ -252,7 +253,7 @@ test_expect_success 'fail to track lightweight tags' '
        git checkout main &&
        git tag light &&
        test_must_fail git branch --track lighttrack light >actual &&
-       test_i18ngrep ! "set up to track" actual &&
+       test_grep ! "set up to track" actual &&
        test_must_fail git checkout lighttrack
 '
 
@@ -260,7 +261,7 @@ test_expect_success 'fail to track annotated tags' '
        git checkout main &&
        git tag -m heavy heavy &&
        test_must_fail git branch --track heavytrack heavy >actual &&
-       test_i18ngrep ! "set up to track" actual &&
+       test_grep ! "set up to track" actual &&
        test_must_fail git checkout heavytrack
 '
 
index 2500acc2ef80f9679105adfa73a805d06e14180c..c6e9b33e44edf4d2d7ecefe688ca6df5fc82d112 100755 (executable)
@@ -44,7 +44,7 @@ commit_peeling_shows_parents ()
        _parent_number=$(( $_parent_number + 1 ))
     done &&
     test_must_fail git rev-parse --verify $_commit^$_parent_number 2>err &&
-    test_i18ngrep "Needed a single revision" err
+    test_grep "Needed a single revision" err
 }
 
 commit_has_parents ()
@@ -62,59 +62,59 @@ HASH6=
 HASH7=
 
 test_expect_success 'set up buggy branch' '
-     echo "line 1" >>hello &&
-     echo "line 2" >>hello &&
-     echo "line 3" >>hello &&
-     echo "line 4" >>hello &&
-     add_and_commit_file hello "4 lines" &&
-     HASH1=$(git rev-parse --verify HEAD) &&
-     echo "line BUG" >>hello &&
-     echo "line 6" >>hello &&
-     echo "line 7" >>hello &&
-     echo "line 8" >>hello &&
-     add_and_commit_file hello "4 more lines with a BUG" &&
-     HASH2=$(git rev-parse --verify HEAD) &&
-     echo "line 9" >>hello &&
-     echo "line 10" >>hello &&
-     add_and_commit_file hello "2 more lines" &&
-     HASH3=$(git rev-parse --verify HEAD) &&
-     echo "line 11" >>hello &&
-     add_and_commit_file hello "1 more line" &&
-     HASH4=$(git rev-parse --verify HEAD) &&
-     sed -e "s/BUG/5/" hello >hello.new &&
-     mv hello.new hello &&
-     add_and_commit_file hello "BUG fixed" &&
-     HASH5=$(git rev-parse --verify HEAD) &&
-     echo "line 12" >>hello &&
-     echo "line 13" >>hello &&
-     add_and_commit_file hello "2 more lines" &&
-     HASH6=$(git rev-parse --verify HEAD) &&
-     echo "line 14" >>hello &&
-     echo "line 15" >>hello &&
-     echo "line 16" >>hello &&
-     add_and_commit_file hello "again 3 more lines" &&
-     HASH7=$(git rev-parse --verify HEAD)
+       echo "line 1" >>hello &&
+       echo "line 2" >>hello &&
+       echo "line 3" >>hello &&
+       echo "line 4" >>hello &&
+       add_and_commit_file hello "4 lines" &&
+       HASH1=$(git rev-parse --verify HEAD) &&
+       echo "line BUG" >>hello &&
+       echo "line 6" >>hello &&
+       echo "line 7" >>hello &&
+       echo "line 8" >>hello &&
+       add_and_commit_file hello "4 more lines with a BUG" &&
+       HASH2=$(git rev-parse --verify HEAD) &&
+       echo "line 9" >>hello &&
+       echo "line 10" >>hello &&
+       add_and_commit_file hello "2 more lines" &&
+       HASH3=$(git rev-parse --verify HEAD) &&
+       echo "line 11" >>hello &&
+       add_and_commit_file hello "1 more line" &&
+       HASH4=$(git rev-parse --verify HEAD) &&
+       sed -e "s/BUG/5/" hello >hello.new &&
+       mv hello.new hello &&
+       add_and_commit_file hello "BUG fixed" &&
+       HASH5=$(git rev-parse --verify HEAD) &&
+       echo "line 12" >>hello &&
+       echo "line 13" >>hello &&
+       add_and_commit_file hello "2 more lines" &&
+       HASH6=$(git rev-parse --verify HEAD) &&
+       echo "line 14" >>hello &&
+       echo "line 15" >>hello &&
+       echo "line 16" >>hello &&
+       add_and_commit_file hello "again 3 more lines" &&
+       HASH7=$(git rev-parse --verify HEAD)
 '
 
 test_expect_success 'replace the author' '
-     git cat-file commit $HASH2 | grep "author A U Thor" &&
-     R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
-     git cat-file commit $R | grep "author O Thor" &&
-     git update-ref refs/replace/$HASH2 $R &&
-     git show HEAD~5 | grep "O Thor" &&
-     git show $HASH2 | grep "O Thor"
+       git cat-file commit $HASH2 | grep "author A U Thor" &&
+       R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+       git cat-file commit $R | grep "author O Thor" &&
+       git update-ref refs/replace/$HASH2 $R &&
+       git show HEAD~5 | grep "O Thor" &&
+       git show $HASH2 | grep "O Thor"
 '
 
 test_expect_success 'test --no-replace-objects option' '
-     git cat-file commit $HASH2 | grep "author O Thor" &&
-     git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
-     git show $HASH2 | grep "O Thor" &&
-     git --no-replace-objects show $HASH2 | grep "A U Thor"
+       git cat-file commit $HASH2 | grep "author O Thor" &&
+       git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
+       git show $HASH2 | grep "O Thor" &&
+       git --no-replace-objects show $HASH2 | grep "A U Thor"
 '
 
 test_expect_success 'test GIT_NO_REPLACE_OBJECTS env variable' '
-     GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
-     GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
+       GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
+       GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
 '
 
 test_expect_success 'test core.usereplacerefs config option' '
@@ -132,64 +132,64 @@ tagger T A Gger <> 0 +0000
 EOF
 
 test_expect_success 'tag replaced commit' '
-     git update-ref refs/tags/mytag $(git mktag <tag.sig)
+       git update-ref refs/tags/mytag $(git mktag <tag.sig)
 '
 
 test_expect_success '"git fsck" works' '
-     git fsck main >fsck_main.out &&
-     test_i18ngrep "dangling commit $R" fsck_main.out &&
-     test_i18ngrep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_main.out &&
-     test -z "$(git fsck)"
+       git fsck main >fsck_main.out &&
+       test_grep "dangling commit $R" fsck_main.out &&
+       test_grep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_main.out &&
+       test -z "$(git fsck)"
 '
 
 test_expect_success 'repack, clone and fetch work' '
-     git repack -a -d &&
-     git clone --no-hardlinks . clone_dir &&
-     (
-         cd clone_dir &&
-         git show HEAD~5 | grep "A U Thor" &&
-         git show $HASH2 | grep "A U Thor" &&
-         git cat-file commit $R &&
-         git repack -a -d &&
-         test_must_fail git cat-file commit $R &&
-         git fetch ../ "refs/replace/*:refs/replace/*" &&
-         git show HEAD~5 | grep "O Thor" &&
-         git show $HASH2 | grep "O Thor" &&
-         git cat-file commit $R
-     )
+       git repack -a -d &&
+       git clone --no-hardlinks . clone_dir &&
+       (
+               cd clone_dir &&
+               git show HEAD~5 | grep "A U Thor" &&
+               git show $HASH2 | grep "A U Thor" &&
+               git cat-file commit $R &&
+               git repack -a -d &&
+               test_must_fail git cat-file commit $R &&
+               git fetch ../ "refs/replace/*:refs/replace/*" &&
+               git show HEAD~5 | grep "O Thor" &&
+               git show $HASH2 | grep "O Thor" &&
+               git cat-file commit $R
+       )
 '
 
 test_expect_success '"git replace" listing and deleting' '
-     test "$HASH2" = "$(git replace -l)" &&
-     test "$HASH2" = "$(git replace)" &&
-     aa=${HASH2%??????????????????????????????????????} &&
-     test "$HASH2" = "$(git replace --list "$aa*")" &&
-     test_must_fail git replace -d $R &&
-     test_must_fail git replace --delete &&
-     test_must_fail git replace -l -d $HASH2 &&
-     git replace -d $HASH2 &&
-     git show $HASH2 | grep "A U Thor" &&
-     test -z "$(git replace -l)"
+       test "$HASH2" = "$(git replace -l)" &&
+       test "$HASH2" = "$(git replace)" &&
+       aa=${HASH2%??????????????????????????????????????} &&
+       test "$HASH2" = "$(git replace --list "$aa*")" &&
+       test_must_fail git replace -d $R &&
+       test_must_fail git replace --delete &&
+       test_must_fail git replace -l -d $HASH2 &&
+       git replace -d $HASH2 &&
+       git show $HASH2 | grep "A U Thor" &&
+       test -z "$(git replace -l)"
 '
 
 test_expect_success '"git replace" replacing' '
-     git replace $HASH2 $R &&
-     git show $HASH2 | grep "O Thor" &&
-     test_must_fail git replace $HASH2 $R &&
-     git replace -f $HASH2 $R &&
-     test_must_fail git replace -f &&
-     test "$HASH2" = "$(git replace)"
+       git replace $HASH2 $R &&
+       git show $HASH2 | grep "O Thor" &&
+       test_must_fail git replace $HASH2 $R &&
+       git replace -f $HASH2 $R &&
+       test_must_fail git replace -f &&
+       test "$HASH2" = "$(git replace)"
 '
 
 test_expect_success '"git replace" resolves sha1' '
-     SHORTHASH2=$(git rev-parse --short=8 $HASH2) &&
-     git replace -d $SHORTHASH2 &&
-     git replace $SHORTHASH2 $R &&
-     git show $HASH2 | grep "O Thor" &&
-     test_must_fail git replace $HASH2 $R &&
-     git replace -f $HASH2 $R &&
-     test_must_fail git replace --force &&
-     test "$HASH2" = "$(git replace)"
+       SHORTHASH2=$(git rev-parse --short=8 $HASH2) &&
+       git replace -d $SHORTHASH2 &&
+       git replace $SHORTHASH2 $R &&
+       git show $HASH2 | grep "O Thor" &&
+       test_must_fail git replace $HASH2 $R &&
+       git replace -f $HASH2 $R &&
+       test_must_fail git replace --force &&
+       test "$HASH2" = "$(git replace)"
 '
 
 # This creates a side branch where the bug in H2
@@ -207,79 +207,79 @@ test_expect_success '"git replace" resolves sha1' '
 # Then we replace H6 with P6.
 #
 test_expect_success 'create parallel branch without the bug' '
-     git replace -d $HASH2 &&
-     git show $HASH2 | grep "A U Thor" &&
-     git checkout $HASH1 &&
-     git cherry-pick $HASH2 &&
-     git show $HASH5 | git apply &&
-     git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
-     PARA2=$(git rev-parse --verify HEAD) &&
-     git cherry-pick $HASH3 &&
-     PARA3=$(git rev-parse --verify HEAD) &&
-     git cherry-pick $HASH4 &&
-     PARA4=$(git rev-parse --verify HEAD) &&
-     git cherry-pick $HASH6 &&
-     PARA6=$(git rev-parse --verify HEAD) &&
-     git replace $HASH6 $PARA6 &&
-     git checkout main &&
-     cur=$(git rev-parse --verify HEAD) &&
-     test "$cur" = "$HASH7" &&
-     git log --pretty=oneline | grep $PARA2 &&
-     git remote add cloned ./clone_dir
+       git replace -d $HASH2 &&
+       git show $HASH2 | grep "A U Thor" &&
+       git checkout $HASH1 &&
+       git cherry-pick $HASH2 &&
+       git show $HASH5 | git apply &&
+       git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
+       PARA2=$(git rev-parse --verify HEAD) &&
+       git cherry-pick $HASH3 &&
+       PARA3=$(git rev-parse --verify HEAD) &&
+       git cherry-pick $HASH4 &&
+       PARA4=$(git rev-parse --verify HEAD) &&
+       git cherry-pick $HASH6 &&
+       PARA6=$(git rev-parse --verify HEAD) &&
+       git replace $HASH6 $PARA6 &&
+       git checkout main &&
+       cur=$(git rev-parse --verify HEAD) &&
+       test "$cur" = "$HASH7" &&
+       git log --pretty=oneline | grep $PARA2 &&
+       git remote add cloned ./clone_dir
 '
 
 test_expect_success 'push to cloned repo' '
-     git push cloned $HASH6^:refs/heads/parallel &&
-     (
-         cd clone_dir &&
-         git checkout parallel &&
-         git log --pretty=oneline | grep $PARA2
-     )
+       git push cloned $HASH6^:refs/heads/parallel &&
+       (
+               cd clone_dir &&
+               git checkout parallel &&
+               git log --pretty=oneline | grep $PARA2
+       )
 '
 
 test_expect_success 'push branch with replacement' '
-     git cat-file commit $PARA3 | grep "author A U Thor" &&
-     S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
-     git cat-file commit $S | grep "author O Thor" &&
-     git replace $PARA3 $S &&
-     git show $HASH6~2 | grep "O Thor" &&
-     git show $PARA3 | grep "O Thor" &&
-     git push cloned $HASH6^:refs/heads/parallel2 &&
-     (
-         cd clone_dir &&
-         git checkout parallel2 &&
-         git log --pretty=oneline | grep $PARA3 &&
-         git show $PARA3 | grep "A U Thor"
-     )
+       git cat-file commit $PARA3 | grep "author A U Thor" &&
+       S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+       git cat-file commit $S | grep "author O Thor" &&
+       git replace $PARA3 $S &&
+       git show $HASH6~2 | grep "O Thor" &&
+       git show $PARA3 | grep "O Thor" &&
+       git push cloned $HASH6^:refs/heads/parallel2 &&
+       (
+               cd clone_dir &&
+               git checkout parallel2 &&
+               git log --pretty=oneline | grep $PARA3 &&
+               git show $PARA3 | grep "A U Thor"
+       )
 '
 
 test_expect_success 'fetch branch with replacement' '
-     git branch tofetch $HASH6 &&
-     (
-         cd clone_dir &&
-         git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
-         git log --pretty=oneline parallel3 >output.txt &&
-         ! grep $PARA3 output.txt &&
-         git show $PARA3 >para3.txt &&
-         grep "A U Thor" para3.txt &&
-         git fetch origin "refs/replace/*:refs/replace/*" &&
-         git log --pretty=oneline parallel3 >output.txt &&
-         grep $PARA3 output.txt &&
-         git show $PARA3 >para3.txt &&
-         grep "O Thor" para3.txt
-     )
+       git branch tofetch $HASH6 &&
+       (
+               cd clone_dir &&
+               git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
+               git log --pretty=oneline parallel3 >output.txt &&
+               ! grep $PARA3 output.txt &&
+               git show $PARA3 >para3.txt &&
+               grep "A U Thor" para3.txt &&
+               git fetch origin "refs/replace/*:refs/replace/*" &&
+               git log --pretty=oneline parallel3 >output.txt &&
+               grep $PARA3 output.txt &&
+               git show $PARA3 >para3.txt &&
+               grep "O Thor" para3.txt
+       )
 '
 
 test_expect_success 'bisect and replacements' '
-     git bisect start $HASH7 $HASH1 &&
-     test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
-     git bisect reset &&
-     GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
-     test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
-     git bisect reset &&
-     git --no-replace-objects bisect start $HASH7 $HASH1 &&
-     test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
-     git bisect reset
+       git bisect start $HASH7 $HASH1 &&
+       test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
+       git bisect reset &&
+       GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
+       test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+       git bisect reset &&
+       git --no-replace-objects bisect start $HASH7 $HASH1 &&
+       test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+       git bisect reset
 '
 
 test_expect_success 'index-pack and replacements' '
@@ -490,9 +490,9 @@ test_expect_success '--convert-graft-file' '
                $(git rev-parse HEAD^^ HEAD^ HEAD^^ HEAD^2) \
                >.git/info/grafts &&
        git status 2>stderr &&
-       test_i18ngrep "hint:.*grafts is deprecated" stderr &&
+       test_grep "hint:.*grafts is deprecated" stderr &&
        git replace --convert-graft-file 2>stderr &&
-       test_i18ngrep ! "hint:.*grafts is deprecated" stderr &&
+       test_grep ! "hint:.*grafts is deprecated" stderr &&
        test_path_is_missing .git/info/grafts &&
 
        : verify that the history is now "grafted" &&
@@ -503,8 +503,8 @@ test_expect_success '--convert-graft-file' '
        test_when_finished "rm -f .git/info/grafts" &&
        echo $EMPTY_BLOB $EMPTY_TREE >.git/info/grafts &&
        test_must_fail git replace --convert-graft-file 2>err &&
-       test_i18ngrep "$EMPTY_BLOB $EMPTY_TREE" err &&
-       test_i18ngrep "$EMPTY_BLOB $EMPTY_TREE" .git/info/grafts
+       test_grep "$EMPTY_BLOB $EMPTY_TREE" err &&
+       test_grep "$EMPTY_BLOB $EMPTY_TREE" .git/info/grafts
 '
 
 test_done
index ed449abe5520e59ddb0ad27def5711007f620650..1a8b64cce18239284a28260734a0348f95a2d7b6 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic git merge-index / git-merge-one-file tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup diverging branches' '
index 4a9a4436e219c856095784324e4b097b3402ba29..5d28507efc687b12af0de9762668ec87fd03a0fc 100755 (executable)
@@ -28,7 +28,7 @@ test_expect_success 'TODO (should fail!): traverse unexpected non-blob entry (lo
 
 test_expect_success 'traverse unexpected non-blob entry (seen)' '
        test_must_fail git rev-list --objects $tree $broken_tree >output 2>&1 &&
-       test_i18ngrep "is not a blob" output
+       test_grep "is not a blob" output
 '
 
 test_expect_success 'setup unexpected non-tree entry' '
@@ -42,7 +42,7 @@ test_expect_success 'traverse unexpected non-tree entry (lone)' '
 
 test_expect_success 'traverse unexpected non-tree entry (seen)' '
        test_must_fail git rev-list --objects $blob $broken_tree >output 2>&1 &&
-       test_i18ngrep "is not a tree" output
+       test_grep "is not a tree" output
 '
 
 test_expect_success 'setup unexpected non-commit parent' '
@@ -54,13 +54,13 @@ test_expect_success 'setup unexpected non-commit parent' '
 
 test_expect_success 'traverse unexpected non-commit parent (lone)' '
        test_must_fail git rev-list --objects $broken_commit >output 2>&1 &&
-       test_i18ngrep "not a commit" output
+       test_grep "not a commit" output
 '
 
 test_expect_success 'traverse unexpected non-commit parent (seen)' '
        test_must_fail git rev-list --objects $blob $broken_commit \
                >output 2>&1 &&
-       test_i18ngrep "not a commit" output
+       test_grep "not a commit" output
 '
 
 test_expect_success 'setup unexpected non-tree root' '
@@ -76,7 +76,7 @@ test_expect_success 'traverse unexpected non-tree root (lone)' '
 test_expect_success 'traverse unexpected non-tree root (seen)' '
        test_must_fail git rev-list --objects $blob $broken_commit \
                >output 2>&1 &&
-       test_i18ngrep "not a tree" output
+       test_grep "not a tree" output
 '
 
 test_expect_success 'setup unexpected non-commit tag' '
@@ -93,7 +93,7 @@ test_expect_success 'traverse unexpected non-commit tag (lone)' '
 
 test_expect_success 'traverse unexpected non-commit tag (seen)' '
        test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
-       test_i18ngrep "not a commit" output
+       test_grep "not a commit" output
 '
 
 test_expect_success 'setup unexpected non-tree tag' '
@@ -110,7 +110,7 @@ test_expect_success 'traverse unexpected non-tree tag (lone)' '
 
 test_expect_success 'traverse unexpected non-tree tag (seen)' '
        test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
-       test_i18ngrep "not a tree" output
+       test_grep "not a tree" output
 '
 
 test_expect_success 'setup unexpected non-blob tag' '
@@ -121,13 +121,13 @@ test_expect_success 'setup unexpected non-blob tag' '
        tag=$(git hash-object -w --literally -t tag broken-tag)
 '
 
-test_expect_success 'TODO (should fail!): traverse unexpected non-blob tag (lone)' '
-       git rev-list --objects $tag
+test_expect_success 'traverse unexpected non-blob tag (lone)' '
+       test_must_fail git rev-list --objects $tag
 '
 
 test_expect_success 'traverse unexpected non-blob tag (seen)' '
        test_must_fail git rev-list --objects $commit $tag >output 2>&1 &&
-       test_i18ngrep "not a blob" output
+       test_grep "not a blob" output
 '
 
 test_done
index 8d9d6604f052f6645e8760d3890f1ed29384fd49..52822b9461a18c89faa35d7dc0be6a1e4690da18 100755 (executable)
@@ -457,7 +457,7 @@ expect_invalid_filter_spec () {
        test_must_fail git -C r3 rev-list --objects --filter="$spec" HEAD \
                >actual 2>actual_stderr &&
        test_must_be_empty actual &&
-       test_i18ngrep "$err" actual_stderr
+       test_grep "$err" actual_stderr
 }
 
 test_expect_success 'combine:... while URL-encoding things that should not be' '
index 4d8e09167e78d078a6c14e7d51e3a9cb0aa7bf89..459f0d741225522276b84a9a65caaa2e2a985bde 100755 (executable)
@@ -4,6 +4,8 @@ test_description='rev-list combining bitmaps and filters'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bitmap.sh
 
+TEST_PASSES_SANITIZE_LEAK=true
+
 test_expect_success 'set up bitmapped repo' '
        # one commit will have bitmaps, the other will not
        test_commit one &&
@@ -141,4 +143,17 @@ test_expect_success 'combine filter with --filter-provided-objects' '
        done <objects
 '
 
+test_expect_success 'bitmap traversal with --unpacked' '
+       git repack -adb &&
+       test_commit unpacked &&
+
+       git rev-list --objects --no-object-names unpacked^.. >expect.raw &&
+       sort expect.raw >expect &&
+
+       git rev-list --use-bitmap-index --objects --all --unpacked >actual.raw &&
+       sort actual.raw >actual &&
+
+       test_cmp expect actual
+'
+
 test_done
index d59111dedec8020a138296928649eaff4de45694..c0cfda62fa72e3581cdf596d19821c0a7941caf9 100755 (executable)
@@ -48,6 +48,13 @@ check_du HEAD
 check_du --objects HEAD
 check_du --objects HEAD^..HEAD
 
+test_expect_success 'setup for --unpacked tests' '
+       git repack -adb &&
+       test_commit unpacked
+'
+
+check_du --all --objects --unpacked
+
 # As mentioned above, don't use hardcode sizes as actual size, but use the
 # output from git cat-file.
 test_expect_success 'rev-list --disk-usage=human' '
index 9a35e783a757b6fe9800ce475e65e73b880708c7..e78315d23d7d32c6db1f9405aa8788862bf37cd5 100755 (executable)
@@ -85,6 +85,7 @@ check_describe e-1-gHASH --tags HEAD^^
 check_describe c-2-gHASH --tags HEAD^^2
 check_describe B --tags HEAD^^2^
 check_describe e --tags HEAD^^^
+check_describe e --tags --exact-match HEAD^^^
 
 check_describe heads/main --all HEAD
 check_describe tags/c-6-gHASH --all HEAD^
@@ -96,6 +97,13 @@ check_describe A-3-gHASH --long HEAD^^2
 check_describe c-7-gHASH --tags
 check_describe e-3-gHASH --first-parent --tags
 
+check_describe c-7-gHASH --tags --no-exact-match HEAD
+check_describe e-3-gHASH --first-parent --tags --no-exact-match HEAD
+
+test_expect_success '--exact-match failure' '
+       test_must_fail git describe --exact-match HEAD 2>err
+'
+
 test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
        echo "A^0" >expect &&
        git checkout A &&
@@ -384,7 +392,7 @@ test_expect_success 'describe directly tagged blob' '
 test_expect_success 'describe tag object' '
        git tag test-blob-1 -a -m msg unique-file:file &&
        test_must_fail git describe test-blob-1 2>actual &&
-       test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
+       test_grep "fatal: test-blob-1 is neither a commit nor blob" actual
 '
 
 test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
@@ -657,4 +665,10 @@ test_expect_success 'setup: describe commits with disjoint bases 2' '
 
 check_describe -C disjoint2 "B-3-gHASH" HEAD
 
+test_expect_success 'setup misleading taggerdates' '
+       GIT_COMMITTER_DATE="2006-12-12 12:31" git tag -a -m "another tag" newer-tag-older-commit unique-file~1
+'
+
+check_describe newer-tag-older-commit~1 --contains unique-file~2
+
 test_done
index cada952f9aee35fe795af5cd99af222f1a8b599f..9fdafeb1e907f4381f2b1e74c6c00771d188d136 100755 (executable)
@@ -293,11 +293,7 @@ test_expect_success 'add with all negative' '
        test_cmp expect actual
 '
 
-test_lazy_prereq ADD_I_USE_BUILTIN_OR_PERL '
-       test_have_prereq ADD_I_USE_BUILTIN || test_have_prereq PERL
-'
-
-test_expect_success ADD_I_USE_BUILTIN_OR_PERL 'add -p with all negative' '
+test_expect_success 'add -p with all negative' '
        H=$(git rev-parse HEAD) &&
        git reset --hard $H &&
        git clean -f &&
index 3214d9db97d9bcedf86de0cfed611517cfbf58f5..16ce4cfcc6420477b2c86718f2e68834b843de61 100755 (executable)
@@ -27,7 +27,7 @@ test_expect_success 'error message for path inside submodule' '
 
 test_expect_success 'error message for path inside submodule from within submodule' '
        test_must_fail git -C sub add . 2>actual &&
-       test_i18ngrep "in unpopulated submodule" actual
+       test_grep "in unpopulated submodule" actual
 '
 
 test_done
index 457cc167c774a0e4551cfc98c4f9aec6a17e1416..120dcd74a51434303b8bd7e71d10a9cb120bf574 100755 (executable)
@@ -64,11 +64,24 @@ test_expect_success 'setup .gitattributes' '
        fileSetLabel label
        fileValue label=foo
        fileWrongLabel label☺
+       newFileA* labelA
+       newFileB* labelB
        EOF
-       git add .gitattributes &&
+       echo fileSetLabel label1 >sub/.gitattributes &&
+       git add .gitattributes sub/.gitattributes &&
        git commit -m "add attributes"
 '
 
+test_expect_success 'setup .gitignore' '
+       cat <<-\EOF >.gitignore &&
+       actual
+       expect
+       pathspec_file
+       EOF
+       git add .gitignore &&
+       git commit -m "add gitignore"
+'
+
 test_expect_success 'check specific set attr' '
        cat <<-\EOF >expect &&
        fileSetLabel
@@ -78,7 +91,17 @@ test_expect_success 'check specific set attr' '
        test_cmp expect actual
 '
 
-test_expect_success 'check specific set attr (2)' '
+test_expect_success 'check set attr with pathspec pattern' '
+       echo sub/fileSetLabel >expect &&
+
+       git ls-files ":(attr:label)sub" >actual &&
+       test_cmp expect actual &&
+
+       git ls-files ":(attr:label)sub/" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check specific set attr in tree-ish' '
        cat <<-\EOF >expect &&
        HEAD:fileSetLabel
        HEAD:sub/fileSetLabel
@@ -87,6 +110,16 @@ test_expect_success 'check specific set attr (2)' '
        test_cmp expect actual
 '
 
+test_expect_success 'check specific set attr with pathspec pattern in tree-ish' '
+       echo HEAD:sub/fileSetLabel >expect &&
+
+       git grep -l content HEAD ":(attr:label)sub" >actual &&
+       test_cmp expect actual &&
+
+       git grep -l content HEAD ":(attr:label)sub/" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'check specific unset attr' '
        cat <<-\EOF >expect &&
        fileUnsetLabel
@@ -129,6 +162,7 @@ test_expect_success 'check specific value attr (2)' '
 test_expect_success 'check unspecified attr' '
        cat <<-\EOF >expect &&
        .gitattributes
+       .gitignore
        fileA
        fileAB
        fileAC
@@ -137,6 +171,7 @@ test_expect_success 'check unspecified attr' '
        fileC
        fileNoLabel
        fileWrongLabel
+       sub/.gitattributes
        sub/fileA
        sub/fileAB
        sub/fileAC
@@ -153,6 +188,7 @@ test_expect_success 'check unspecified attr' '
 test_expect_success 'check unspecified attr (2)' '
        cat <<-\EOF >expect &&
        HEAD:.gitattributes
+       HEAD:.gitignore
        HEAD:fileA
        HEAD:fileAB
        HEAD:fileAC
@@ -161,6 +197,7 @@ test_expect_success 'check unspecified attr (2)' '
        HEAD:fileC
        HEAD:fileNoLabel
        HEAD:fileWrongLabel
+       HEAD:sub/.gitattributes
        HEAD:sub/fileA
        HEAD:sub/fileAB
        HEAD:sub/fileAC
@@ -177,9 +214,11 @@ test_expect_success 'check unspecified attr (2)' '
 test_expect_success 'check multiple unspecified attr' '
        cat <<-\EOF >expect &&
        .gitattributes
+       .gitignore
        fileC
        fileNoLabel
        fileWrongLabel
+       sub/.gitattributes
        sub/fileC
        sub/fileNoLabel
        sub/fileWrongLabel
@@ -212,17 +251,100 @@ test_expect_success 'check label excluding other labels' '
 
 test_expect_success 'fail on multiple attr specifiers in one pathspec item' '
        test_must_fail git ls-files . ":(attr:labelB,attr:labelC)" 2>actual &&
-       test_i18ngrep "Only one" actual
+       test_grep "Only one" actual
 '
 
-test_expect_success 'fail if attr magic is used places not implemented' '
+test_expect_success 'fail if attr magic is used in places not implemented' '
        # The main purpose of this test is to check that we actually fail
        # when you attempt to use attr magic in commands that do not implement
-       # attr magic. This test does not advocate git-add to stay that way,
-       # though, but git-add is convenient as it has its own internal pathspec
-       # parsing.
-       test_must_fail git add ":(attr:labelB)" 2>actual &&
-       test_i18ngrep "magic not supported" actual
+       # attr magic. This test does not advocate check-ignore to stay that way.
+       # When you teach the command to grok the pathspec, you need to find
+       # another command to replace it for the test.
+       test_must_fail git check-ignore ":(attr:labelB)" 2>actual &&
+       test_grep "magic not supported" actual
+'
+
+test_expect_success 'check that attr magic works for git stash push' '
+       cat <<-\EOF >expect &&
+       A       sub/newFileA-foo
+       EOF
+       >sub/newFileA-foo &&
+       >sub/newFileB-foo &&
+       git stash push --include-untracked -- ":(exclude,attr:labelB)" &&
+       git stash show --include-untracked --name-status >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add --all' '
+       cat <<-\EOF >expect &&
+       sub/newFileA-foo
+       EOF
+       >sub/newFileA-foo &&
+       >sub/newFileB-foo &&
+       git add --all ":(exclude,attr:labelB)" &&
+       git diff --name-only --cached >actual &&
+       git restore -W -S . &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add -u' '
+       cat <<-\EOF >expect &&
+       sub/fileA
+       EOF
+       >sub/newFileA-foo &&
+       >sub/newFileB-foo &&
+       >sub/fileA &&
+       >sub/fileB &&
+       git add -u ":(exclude,attr:labelB)" &&
+       git diff --name-only --cached  >actual &&
+       git restore -S -W . && rm sub/new* &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add <path>' '
+       cat <<-\EOF >expect &&
+       fileA
+       fileB
+       sub/fileA
+       EOF
+       >fileA &&
+       >fileB &&
+       >sub/fileA &&
+       >sub/fileB &&
+       git add ":(exclude,attr:labelB)sub/*" &&
+       git diff --name-only --cached >actual &&
+       git restore -S -W . &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git -add .' '
+       cat <<-\EOF >expect &&
+       sub/fileA
+       EOF
+       >fileA &&
+       >fileB &&
+       >sub/fileA &&
+       >sub/fileB &&
+       cd sub &&
+       git add . ":(exclude,attr:labelB)" &&
+       cd .. &&
+       git diff --name-only --cached >actual &&
+       git restore -S -W . &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add --pathspec-from-file' '
+       cat <<-\EOF >pathspec_file &&
+       :(exclude,attr:labelB)
+       EOF
+       cat <<-\EOF >expect &&
+       sub/newFileA-foo
+       EOF
+       >sub/newFileA-foo &&
+       >sub/newFileB-foo &&
+       git add --all --pathspec-from-file=pathspec_file &&
+       git diff --name-only --cached >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'abort on giving invalid label on the command line' '
@@ -245,12 +367,57 @@ test_expect_success 'check attribute list' '
 
 test_expect_success 'backslash cannot be the last character' '
        test_must_fail git ls-files ":(attr:label=foo\\ labelA=bar)" 2>actual &&
-       test_i18ngrep "not allowed as last character in attr value" actual
+       test_grep "not allowed as last character in attr value" actual
 '
 
 test_expect_success 'backslash cannot be used as a value' '
        test_must_fail git ls-files ":(attr:label=f\\\oo)" 2>actual &&
-       test_i18ngrep "for value matching" actual
+       test_grep "for value matching" actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (1)' '
+       git ls-files ":(attr:label1)" >actual &&
+       test_write_lines "sub/fileSetLabel" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (2)' '
+       git ls-files ":(attr:label1)sub" >actual &&
+       test_write_lines "sub/fileSetLabel" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
+       git ls-files ":(attr:label1)sub/" >actual &&
+       test_write_lines "sub/fileSetLabel" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success POSIXPERM 'pathspec with builtin_objectmode attr can be used' '
+       >mode_exec_file_1 &&
+
+       git status -s ":(attr:builtin_objectmode=100644)mode_exec_*" >actual &&
+       echo ?? mode_exec_file_1 >expect &&
+       test_cmp expect actual &&
+
+       git add mode_exec_file_1 &&
+       chmod +x mode_exec_file_1 &&
+       git status -s ":(attr:builtin_objectmode=100755)mode_exec_*" >actual &&
+       echo AM mode_exec_file_1 >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success POSIXPERM 'builtin_objectmode attr can be excluded' '
+       >mode_1_regular &&
+       >mode_1_exec  &&
+       chmod +x mode_1_exec &&
+       git status -s ":(exclude,attr:builtin_objectmode=100644)" "mode_1_*" >actual &&
+       echo ?? mode_1_exec >expect &&
+       test_cmp expect actual &&
+
+       git status -s ":(exclude,attr:builtin_objectmode=100755)" "mode_1_*" >actual &&
+       echo ?? mode_1_regular >expect &&
+       test_cmp expect actual
 '
 
 test_done
index ae8b5379e24d52c559e0d02da6f95fdbc40874c8..2db37a6596953532423b6c1518fee199a1f5c3de 100755 (executable)
@@ -15,11 +15,11 @@ test_expect_success 'log and ls-files in a bare repository' '
                cd bare &&
                test_must_fail git log -- .. >out 2>err &&
                test_must_be_empty out &&
-               test_i18ngrep "outside repository" err &&
+               test_grep "outside repository" err &&
 
                test_must_fail git ls-files -- .. >out 2>err &&
                test_must_be_empty out &&
-               test_i18ngrep "outside repository" err
+               test_grep "outside repository" err
        )
 '
 
@@ -28,11 +28,11 @@ test_expect_success 'log and ls-files in .git directory' '
                cd .git &&
                test_must_fail git log -- .. >out 2>err &&
                test_must_be_empty out &&
-               test_i18ngrep "outside repository" err &&
+               test_grep "outside repository" err &&
 
                test_must_fail git ls-files -- .. >out 2>err &&
                test_must_be_empty out &&
-               test_i18ngrep "outside repository" err
+               test_grep "outside repository" err
        )
 '
 
index dcaab7265f5c75a8be444db3e2d1766732192f8d..843a7fe14313b52d1bb3267d9b1f82152f3ccb96 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='for-each-ref test'
 
 . ./test-lib.sh
+GNUPGHOME_NOT_USED=$GNUPGHOME
 . "$TEST_DIRECTORY"/lib-gpg.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -19,11 +20,19 @@ setdate_and_increment () {
     export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
+test_object_file_size () {
+       oid=$(git rev-parse "$1")
+       path=".git/objects/$(test_oid_to_path $oid)"
+       test_file_size "$path"
+}
+
 test_expect_success setup '
-       test_oid_cache <<-EOF &&
-       disklen sha1:138
-       disklen sha256:154
+       # setup .mailmap
+       cat >.mailmap <<-EOF &&
+       A Thor <athor@example.com> A U Thor <author@example.com>
+       C Mitter <cmitter@example.com> C O Mitter <committer@example.com>
        EOF
+
        setdate_and_increment &&
        echo "Using $datestamp" > one &&
        git add one &&
@@ -40,25 +49,29 @@ test_expect_success setup '
        git config push.default current
 '
 
-test_atom() {
+test_atom () {
        case "$1" in
                head) ref=refs/heads/main ;;
                 tag) ref=refs/tags/testtag ;;
                 sym) ref=refs/heads/sym ;;
                   *) ref=$1 ;;
        esac
+       format=$2
+       test_do=test_expect_${4:-success}
+
        printf '%s\n' "$3" >expected
-       test_expect_${4:-success} $PREREQ "basic atom: $1 $2" "
-               git for-each-ref --format='%($2)' $ref >actual &&
+       $test_do $PREREQ "basic atom: $ref $format" '
+               git for-each-ref --format="%($format)" "$ref" >actual &&
                sanitize_pgp <actual >actual.clean &&
                test_cmp expected actual.clean
-       "
+       '
+
        # Automatically test "contents:size" atom after testing "contents"
-       if test "$2" = "contents"
+       if test "$format" = "contents"
        then
                # for commit leg, $3 is changed there
                expect=$(printf '%s' "$3" | wc -c)
-               test_expect_${4:-success} $PREREQ "basic atom: $1 contents:size" '
+               $test_do $PREREQ "basic atom: $ref contents:size" '
                        type=$(git cat-file -t "$ref") &&
                        case $type in
                        tag)
@@ -82,7 +95,6 @@ test_atom() {
 }
 
 hexlen=$(test_oid hexsz)
-disklen=$(test_oid disklen)
 
 test_atom head refname refs/heads/main
 test_atom head refname: refs/heads/main
@@ -117,7 +129,7 @@ test_atom head push:strip=1 remotes/myfork/main
 test_atom head push:strip=-1 main
 test_atom head objecttype commit
 test_atom head objectsize $((131 + hexlen))
-test_atom head objectsize:disk $disklen
+test_atom head objectsize:disk $(test_object_file_size refs/heads/main)
 test_atom head deltabase $ZERO_OID
 test_atom head objectname $(git rev-parse refs/heads/main)
 test_atom head objectname:short $(git rev-parse --short refs/heads/main)
@@ -140,15 +152,31 @@ test_atom head '*objectname' ''
 test_atom head '*objecttype' ''
 test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'
 test_atom head authorname 'A U Thor'
+test_atom head authorname:mailmap 'A Thor'
 test_atom head authoremail '<author@example.com>'
 test_atom head authoremail:trim 'author@example.com'
 test_atom head authoremail:localpart 'author'
+test_atom head authoremail:trim,localpart 'author'
+test_atom head authoremail:mailmap '<athor@example.com>'
+test_atom head authoremail:mailmap,trim 'athor@example.com'
+test_atom head authoremail:trim,mailmap 'athor@example.com'
+test_atom head authoremail:mailmap,localpart 'athor'
+test_atom head authoremail:localpart,mailmap 'athor'
+test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'
 test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'
 test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'
 test_atom head committername 'C O Mitter'
+test_atom head committername:mailmap 'C Mitter'
 test_atom head committeremail '<committer@example.com>'
 test_atom head committeremail:trim 'committer@example.com'
 test_atom head committeremail:localpart 'committer'
+test_atom head committeremail:localpart,trim 'committer'
+test_atom head committeremail:mailmap '<cmitter@example.com>'
+test_atom head committeremail:mailmap,trim 'cmitter@example.com'
+test_atom head committeremail:trim,mailmap 'cmitter@example.com'
+test_atom head committeremail:mailmap,localpart 'cmitter'
+test_atom head committeremail:localpart,mailmap 'cmitter'
+test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'
 test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'
 test_atom head tag ''
 test_atom head tagger ''
@@ -175,8 +203,8 @@ test_atom tag upstream ''
 test_atom tag push ''
 test_atom tag objecttype tag
 test_atom tag objectsize $((114 + hexlen))
-test_atom tag objectsize:disk $disklen
-test_atom tag '*objectsize:disk' $disklen
+test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)
+test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)
 test_atom tag deltabase $ZERO_OID
 test_atom tag '*deltabase' $ZERO_OID
 test_atom tag objectname $(git rev-parse refs/tags/testtag)
@@ -198,22 +226,46 @@ test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})
 test_atom tag '*objecttype' 'commit'
 test_atom tag author ''
 test_atom tag authorname ''
+test_atom tag authorname:mailmap ''
 test_atom tag authoremail ''
 test_atom tag authoremail:trim ''
 test_atom tag authoremail:localpart ''
+test_atom tag authoremail:trim,localpart ''
+test_atom tag authoremail:mailmap ''
+test_atom tag authoremail:mailmap,trim ''
+test_atom tag authoremail:trim,mailmap ''
+test_atom tag authoremail:mailmap,localpart ''
+test_atom tag authoremail:localpart,mailmap ''
+test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''
 test_atom tag authordate ''
 test_atom tag committer ''
 test_atom tag committername ''
+test_atom tag committername:mailmap ''
 test_atom tag committeremail ''
 test_atom tag committeremail:trim ''
 test_atom tag committeremail:localpart ''
+test_atom tag committeremail:localpart,trim ''
+test_atom tag committeremail:mailmap ''
+test_atom tag committeremail:mailmap,trim ''
+test_atom tag committeremail:trim,mailmap ''
+test_atom tag committeremail:mailmap,localpart ''
+test_atom tag committeremail:localpart,mailmap ''
+test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''
 test_atom tag committerdate ''
 test_atom tag tag 'testtag'
 test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'
 test_atom tag taggername 'C O Mitter'
+test_atom tag taggername:mailmap 'C Mitter'
 test_atom tag taggeremail '<committer@example.com>'
 test_atom tag taggeremail:trim 'committer@example.com'
 test_atom tag taggeremail:localpart 'committer'
+test_atom tag taggeremail:trim,localpart 'committer'
+test_atom tag taggeremail:mailmap '<cmitter@example.com>'
+test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'
+test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'
+test_atom tag taggeremail:mailmap,localpart 'cmitter'
+test_atom tag taggeremail:localpart,mailmap 'cmitter'
+test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'
 test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'
 test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'
 test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'
@@ -266,6 +318,66 @@ test_expect_success 'arguments to %(objectname:short=) must be positive integers
        test_must_fail git for-each-ref --format="%(objectname:short=foo)"
 '
 
+test_bad_atom () {
+       case "$1" in
+       head) ref=refs/heads/main ;;
+        tag) ref=refs/tags/testtag ;;
+        sym) ref=refs/heads/sym ;;
+          *) ref=$1 ;;
+       esac
+       format=$2
+       test_do=test_expect_${4:-success}
+
+       printf '%s\n' "$3" >expect
+       $test_do $PREREQ "err basic atom: $ref $format" '
+               test_must_fail git for-each-ref \
+                       --format="%($format)" "$ref" 2>error &&
+               test_cmp expect error
+       '
+}
+
+test_bad_atom head 'authoremail:foo' \
+       'fatal: unrecognized %(authoremail) argument: foo'
+
+test_bad_atom head 'authoremail:mailmap,trim,bar' \
+       'fatal: unrecognized %(authoremail) argument: bar'
+
+test_bad_atom head 'authoremail:trim,' \
+       'fatal: unrecognized %(authoremail) argument: '
+
+test_bad_atom head 'authoremail:mailmaptrim' \
+       'fatal: unrecognized %(authoremail) argument: trim'
+
+test_bad_atom head 'committeremail: ' \
+       'fatal: unrecognized %(committeremail) argument:  '
+
+test_bad_atom head 'committeremail: trim,foo' \
+       'fatal: unrecognized %(committeremail) argument:  trim,foo'
+
+test_bad_atom head 'committeremail:mailmap,localpart ' \
+       'fatal: unrecognized %(committeremail) argument:  '
+
+test_bad_atom head 'committeremail:trim_localpart' \
+       'fatal: unrecognized %(committeremail) argument: _localpart'
+
+test_bad_atom head 'committeremail:localpart,,,trim' \
+       'fatal: unrecognized %(committeremail) argument: ,,trim'
+
+test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \
+       'fatal: unrecognized %(taggeremail) argument:  foo '
+
+test_bad_atom tag 'taggeremail:trim,localpart,' \
+       'fatal: unrecognized %(taggeremail) argument: '
+
+test_bad_atom tag 'taggeremail:mailmap;localpart trim' \
+       'fatal: unrecognized %(taggeremail) argument: ;localpart trim'
+
+test_bad_atom tag 'taggeremail:localpart trim' \
+       'fatal: unrecognized %(taggeremail) argument:  trim'
+
+test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \
+       'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'
+
 test_date () {
        f=$1 &&
        committer_date=$2 &&
@@ -447,6 +559,41 @@ test_expect_success 'exercise glob patterns with prefixes' '
        test_cmp expected actual
 '
 
+cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with prefix exclusions' '
+       for tag in foo/one foo/two foo/three bar baz
+       do
+               git tag "$tag" || return 1
+       done &&
+       test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+       git for-each-ref --format="%(refname)" \
+               refs/tags/ --exclude=refs/tags/foo >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/foo/one
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with pattern exclusions' '
+       for tag in foo/one foo/two foo/three bar baz
+       do
+               git tag "$tag" || return 1
+       done &&
+       test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+       git for-each-ref --format="%(refname)" \
+               refs/tags/ --exclude="refs/tags/foo/t*" >actual &&
+       test_cmp expected actual
+'
+
 cat >expected <<\EOF
 'refs/heads/main'
 'refs/remotes/origin/main'
@@ -561,6 +708,144 @@ test_expect_success 'color.ui=always does not override tty check' '
        test_cmp expected.bare actual
 '
 
+test_expect_success 'setup for describe atom tests' '
+       git init -b master describe-repo &&
+       (
+               cd describe-repo &&
+
+               test_commit --no-tag one &&
+               git tag tagone &&
+
+               test_commit --no-tag two &&
+               git tag -a -m "tag two" tagtwo
+       )
+'
+
+test_expect_success 'describe atom vs git describe' '
+       (
+               cd describe-repo &&
+
+               git for-each-ref --format="%(objectname)" \
+                       refs/tags/ >obj &&
+               while read hash
+               do
+                       if desc=$(git describe $hash)
+                       then
+                               : >expect-contains-good
+                       else
+                               : >expect-contains-bad
+                       fi &&
+                       echo "$hash $desc" || return 1
+               done <obj >expect &&
+               test_path_exists expect-contains-good &&
+               test_path_exists expect-contains-bad &&
+
+               git for-each-ref --format="%(objectname) %(describe)" \
+                       refs/tags/ >actual 2>err &&
+               test_cmp expect actual &&
+               test_must_be_empty err
+       )
+'
+
+test_expect_success 'describe:tags vs describe --tags' '
+       (
+               cd describe-repo &&
+               git describe --tags >expect &&
+               git for-each-ref --format="%(describe:tags)" \
+                               refs/heads/master >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '
+       (
+               cd describe-repo &&
+
+               # Case 1: We have commits between HEAD and the most
+               #         recent tag reachable from it
+               test_commit --no-tag file &&
+               git describe --abbrev=14 >expect &&
+               git for-each-ref --format="%(describe:abbrev=14)" \
+                       refs/heads/master >actual &&
+               test_cmp expect actual &&
+
+               # Make sure the hash used is atleast 14 digits long
+               sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
+               test 15 -le $(wc -c <hexpart) &&
+
+               # Case 2: We have a tag at HEAD, describe directly gives
+               #         the name of the tag
+               git tag -a -m tagged tagname &&
+               git describe --abbrev=14 >expect &&
+               git for-each-ref --format="%(describe:abbrev=14)" \
+                       refs/heads/master >actual &&
+               test_cmp expect actual &&
+               test tagname = $(cat actual)
+       )
+'
+
+test_expect_success 'describe:match=... vs describe --match ...' '
+       (
+               cd describe-repo &&
+               git tag -a -m "tag foo" tag-foo &&
+               git describe --match "*-foo" >expect &&
+               git for-each-ref --format="%(describe:match="*-foo")" \
+                       refs/heads/master >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'describe:exclude:... vs describe --exclude ...' '
+       (
+               cd describe-repo &&
+               git tag -a -m "tag bar" tag-bar &&
+               git describe --exclude "*-bar" >expect &&
+               git for-each-ref --format="%(describe:exclude="*-bar")" \
+                       refs/heads/master >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'deref with describe atom' '
+       (
+               cd describe-repo &&
+               cat >expect <<-\EOF &&
+
+               tagname
+               tagname
+               tagname
+
+               tagtwo
+               EOF
+               git for-each-ref --format="%(*describe)" >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'err on bad describe atom arg' '
+       (
+               cd describe-repo &&
+
+               # The bad arg is the only arg passed to describe atom
+               cat >expect <<-\EOF &&
+               fatal: unrecognized %(describe) argument: baz
+               EOF
+               test_must_fail git for-each-ref --format="%(describe:baz)" \
+                       refs/heads/master 2>actual &&
+               test_cmp expect actual &&
+
+               # The bad arg is in the middle of the option string
+               # passed to the describe atom
+               cat >expect <<-\EOF &&
+               fatal: unrecognized %(describe) argument: qux=1,abbrev=14
+               EOF
+               test_must_fail git for-each-ref \
+                       --format="%(describe:tags,qux=1,abbrev=14)" \
+                       ref/heads/master 2>actual &&
+               test_cmp expect actual
+       )
+'
+
 cat >expected <<\EOF
 heads/main
 tags/main
@@ -606,7 +891,7 @@ test_expect_success 'create tag without tagger' '
        git tag -a -m "Broken tag" taggerless &&
        git tag -f taggerless $(git cat-file tag taggerless |
                sed -e "/^tagger /d" |
-               git hash-object --stdin -w -t tag)
+               git hash-object --literally --stdin -w -t tag)
 '
 
 test_atom refs/tags/taggerless type 'commit'
@@ -843,16 +1128,16 @@ test_expect_success 'Verify sorts with raw' '
 test_expect_success 'Verify sorts with raw:size' '
        cat >expected <<-EOF &&
        refs/myblobs/blob8
-       refs/myblobs/first
        refs/myblobs/blob7
-       refs/heads/main
        refs/myblobs/blob4
        refs/myblobs/blob1
        refs/myblobs/blob2
        refs/myblobs/blob3
        refs/myblobs/blob5
        refs/myblobs/blob6
+       refs/myblobs/first
        refs/mytrees/first
+       refs/heads/main
        EOF
        git for-each-ref --format="%(refname)" --sort=raw:size \
                refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
@@ -964,6 +1249,17 @@ test_expect_success 'for-each-ref --format compare with cat-file --batch' '
        test_cmp expected actual
 '
 
+test_expect_success 'verify sorts with contents:size' '
+       cat >expect <<-\EOF &&
+       refs/heads/main
+       refs/heads/newtag
+       refs/heads/ambiguous
+       EOF
+       git for-each-ref --format="%(refname)" \
+               --sort=contents:size refs/heads/ >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'set up multiple-sort tags' '
        for when in 100000 200000
        do
@@ -1039,6 +1335,27 @@ test_expect_success '--no-sort cancels the previous sort keys' '
        test_cmp expected actual
 '
 
+test_expect_success '--no-sort without subsequent --sort prints expected refs' '
+       cat >expected <<-\EOF &&
+       refs/tags/multi-ref1-100000-user1
+       refs/tags/multi-ref1-100000-user2
+       refs/tags/multi-ref1-200000-user1
+       refs/tags/multi-ref1-200000-user2
+       refs/tags/multi-ref2-100000-user1
+       refs/tags/multi-ref2-100000-user2
+       refs/tags/multi-ref2-200000-user1
+       refs/tags/multi-ref2-200000-user2
+       EOF
+
+       # Sort the results with `sort` for a consistent comparison against
+       # expected
+       git for-each-ref \
+               --format="%(refname)" \
+               --no-sort \
+               "refs/tags/multi-*" | sort >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
        test_when_finished "git checkout main" &&
        git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
@@ -1242,6 +1559,24 @@ test_expect_success 'basic atom: rest must fail' '
        test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
 '
 
+test_expect_success 'HEAD atom does not take arguments' '
+       test_must_fail git for-each-ref --format="%(HEAD:foo)" 2>err &&
+       echo "fatal: %(HEAD) does not take arguments" >expect &&
+       test_cmp expect err
+'
+
+test_expect_success 'subject atom rejects unknown arguments' '
+       test_must_fail git for-each-ref --format="%(subject:foo)" 2>err &&
+       echo "fatal: unrecognized %(subject) argument: foo" >expect &&
+       test_cmp expect err
+'
+
+test_expect_success 'refname atom rejects unknown arguments' '
+       test_must_fail git for-each-ref --format="%(refname:foo)" 2>err &&
+       echo "fatal: unrecognized %(refname) argument: foo" >expect &&
+       test_cmp expect err
+'
+
 test_expect_success 'trailer parsing not fooled by --- line' '
        git commit --allow-empty -F - <<-\EOF &&
        this is the subject
@@ -1356,6 +1691,14 @@ test_expect_success 'for-each-ref --ignore-case ignores case' '
        test_cmp expect actual
 '
 
+test_expect_success 'for-each-ref --omit-empty works' '
+       git for-each-ref --format="%(refname)" >actual &&
+       test_line_count -gt 1 actual &&
+       git for-each-ref --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual &&
+       echo refs/heads/main >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '
        # name refs numerically to avoid case-insensitive filesystem conflicts
        nr=0 &&
@@ -1406,4 +1749,304 @@ test_expect_success 'for-each-ref reports broken tags' '
                refs/tags/broken-tag-*
 '
 
+test_expect_success 'set up tag with signature and no blank lines' '
+       git tag -F - fake-sig-no-blanks <<-\EOF
+       this is the subject
+       -----BEGIN PGP SIGNATURE-----
+       not a real signature, but we just care about the
+       subject/body parsing. It is important here that
+       there are no blank lines in the signature.
+       -----END PGP SIGNATURE-----
+       EOF
+'
+
+test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'
+test_atom refs/tags/fake-sig-no-blanks contents:body ''
+test_atom refs/tags/fake-sig-no-blanks contents:signature "$sig"
+
+test_expect_success 'set up tag with CRLF signature' '
+       append_cr <<-\EOF |
+       this is the subject
+       -----BEGIN PGP SIGNATURE-----
+
+       not a real signature, but we just care about
+       the subject/body parsing. It is important here
+       that there is a blank line separating this
+       from the signature header.
+       -----END PGP SIGNATURE-----
+       EOF
+       git tag -F - --cleanup=verbatim fake-sig-crlf
+'
+
+test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'
+test_atom refs/tags/fake-sig-crlf contents:body ''
+
+# CRLF is retained in the signature, so we have to pass our expected value
+# through append_cr. But test_atom requires a shell string, which means command
+# substitution, and the shell will strip trailing newlines from the output of
+# the substitution. Hack around it by adding and then removing a dummy line.
+sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
+sig_crlf=${sig_crlf%dummy}
+test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
+
+test_expect_success 'git for-each-ref --stdin: empty' '
+       >in &&
+       git for-each-ref --format="%(refname)" --stdin <in >actual &&
+       git for-each-ref --format="%(refname)" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref --stdin: fails if extra args' '
+       >in &&
+       test_must_fail git for-each-ref --format="%(refname)" \
+               --stdin refs/heads/extra <in 2>err &&
+       grep "unknown arguments supplied with --stdin" err
+'
+
+test_expect_success 'git for-each-ref --stdin: matches' '
+       cat >in <<-EOF &&
+       refs/tags/multi*
+       refs/heads/amb*
+       EOF
+
+       cat >expect <<-EOF &&
+       refs/heads/ambiguous
+       refs/tags/multi-ref1-100000-user1
+       refs/tags/multi-ref1-100000-user2
+       refs/tags/multi-ref1-200000-user1
+       refs/tags/multi-ref1-200000-user2
+       refs/tags/multi-ref2-100000-user1
+       refs/tags/multi-ref2-100000-user2
+       refs/tags/multi-ref2-200000-user1
+       refs/tags/multi-ref2-200000-user2
+       refs/tags/multiline
+       EOF
+
+       git for-each-ref --format="%(refname)" --stdin <in >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref with non-existing refs' '
+       cat >in <<-EOF &&
+       refs/heads/this-ref-does-not-exist
+       refs/tags/bogus
+       EOF
+
+       git for-each-ref --format="%(refname)" --stdin <in >actual &&
+       test_must_be_empty actual &&
+
+       xargs git for-each-ref --format="%(refname)" <in >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'git for-each-ref with nested tags' '
+       git tag -am "Normal tag" nested/base HEAD &&
+       git tag -am "Nested tag" nested/nest1 refs/tags/nested/base &&
+       git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 &&
+
+       head_oid="$(git rev-parse HEAD)" &&
+       base_tag_oid="$(git rev-parse refs/tags/nested/base)" &&
+       nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" &&
+       nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" &&
+
+       cat >expect <<-EOF &&
+       refs/tags/nested/base $base_tag_oid tag $head_oid commit
+       refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit
+       refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit
+       EOF
+
+       git for-each-ref \
+               --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \
+               refs/tags/nested/ >actual &&
+       test_cmp expect actual
+'
+
+GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+
+test_expect_success GPG 'setup for signature atom using gpg' '
+       git checkout -b signed &&
+
+       test_when_finished "test_unconfig commit.gpgSign" &&
+
+       echo "1" >file &&
+       git add file &&
+       test_tick &&
+       git commit -S -m "file: 1" &&
+       git tag first-signed &&
+
+       echo "2" >file &&
+       test_tick &&
+       git commit -a -m "file: 2" &&
+       git tag second-unsigned &&
+
+       git config commit.gpgSign 1 &&
+       echo "3" >file &&
+       test_tick &&
+       git commit -a --no-gpg-sign -m "file: 3" &&
+       git tag third-unsigned &&
+
+       test_tick &&
+       git rebase -f HEAD^^ && git tag second-signed HEAD^ &&
+       git tag third-signed &&
+
+       echo "4" >file &&
+       test_tick &&
+       git commit -a -SB7227189 -m "file: 4" &&
+       git tag fourth-signed &&
+
+       echo "5" >file &&
+       test_tick &&
+       git commit -a --no-gpg-sign -m "file: 5" &&
+       git tag fifth-unsigned &&
+
+       echo "6" >file &&
+       test_tick &&
+       git commit -a --no-gpg-sign -m "file: 6" &&
+
+       test_tick &&
+       git rebase -f HEAD^^ &&
+       git tag fifth-signed HEAD^ &&
+       git tag sixth-signed &&
+
+       echo "7" >file &&
+       test_tick &&
+       git commit -a --no-gpg-sign -m "file: 7" &&
+       git tag seventh-unsigned
+'
+
+test_expect_success GPGSSH 'setup for signature atom using ssh' '
+       test_when_finished "test_unconfig gpg.format user.signingkey" &&
+
+       test_config gpg.format ssh &&
+       test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+       echo "8" >file &&
+       test_tick &&
+       git add file &&
+       git commit -S -m "file: 8" &&
+       git tag eighth-signed-ssh
+'
+
+test_expect_success GPG2 'bare signature atom' '
+       git verify-commit first-signed 2>expect &&
+       echo  >>expect &&
+       git for-each-ref refs/tags/first-signed \
+               --format="%(signature)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show good signature with custom format' '
+       git verify-commit first-signed &&
+       cat >expect <<-\EOF &&
+       G
+       13B6F51ECDDE430D
+       C O Mitter <committer@example.com>
+       73D758744BE721698EC54E8713B6F51ECDDE430D
+       73D758744BE721698EC54E8713B6F51ECDDE430D
+       EOF
+       git for-each-ref refs/tags/first-signed \
+               --format="$GRADE_FORMAT" >actual &&
+       test_cmp expect actual
+'
+test_expect_success GPGSSH 'show good signature with custom format
+                           with ssh' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+       cat >expect.tmpl <<-\EOF &&
+       G
+       FINGERPRINT
+       principal with number 1
+       FINGERPRINT
+
+       EOF
+       sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+       git for-each-ref refs/tags/eighth-signed-ssh \
+               --format="$GRADE_FORMAT" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'signature atom with grade option and bad signature' '
+       git cat-file commit third-signed >raw &&
+       sed -e "s/^file: 3/file: 3 forged/" raw >forged1 &&
+       FORGED1=$(git hash-object -w -t commit forged1) &&
+       git update-ref refs/tags/third-signed "$FORGED1" &&
+       test_must_fail git verify-commit "$FORGED1" &&
+
+       cat >expect <<-\EOF &&
+       B
+       13B6F51ECDDE430D
+       C O Mitter <committer@example.com>
+
+
+       EOF
+       git for-each-ref refs/tags/third-signed \
+               --format="$GRADE_FORMAT" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with custom format' '
+       cat >expect <<-\EOF &&
+       U
+       65A0EEA02E30CAD7
+       Eris Discordia <discord@example.net>
+       F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+       D4BE22311AD3131E5EDA29A461092E85B7227189
+       EOF
+       git for-each-ref refs/tags/fourth-signed \
+               --format="$GRADE_FORMAT" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with undefined trust level' '
+       cat >expect <<-\EOF &&
+       undefined
+       65A0EEA02E30CAD7
+       Eris Discordia <discord@example.net>
+       F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+       D4BE22311AD3131E5EDA29A461092E85B7227189
+       EOF
+       git for-each-ref refs/tags/fourth-signed \
+               --format="$TRUSTLEVEL_FORMAT" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with ultimate trust level' '
+       cat >expect <<-\EOF &&
+       ultimate
+       13B6F51ECDDE430D
+       C O Mitter <committer@example.com>
+       73D758744BE721698EC54E8713B6F51ECDDE430D
+       73D758744BE721698EC54E8713B6F51ECDDE430D
+       EOF
+       git for-each-ref refs/tags/sixth-signed \
+               --format="$TRUSTLEVEL_FORMAT" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show unknown signature with custom format' '
+       cat >expect <<-\EOF &&
+       E
+       13B6F51ECDDE430D
+
+
+
+       EOF
+       GNUPGHOME="$GNUPGHOME_NOT_USED" git for-each-ref \
+               refs/tags/sixth-signed --format="$GRADE_FORMAT" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show lack of signature with custom format' '
+       cat >expect <<-\EOF &&
+       N
+
+
+
+
+       EOF
+       git for-each-ref refs/tags/seventh-unsigned \
+               --format="$GRADE_FORMAT" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 40edf9dab53402db9b5fad6dd060768537f9ba9a..83b8a19d94176dcf66d2aca1d8a4b9519ee020fe 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='for-each-ref errors for broken refs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 ZEROS=$ZERO_OID
@@ -14,7 +15,7 @@ test_expect_success setup '
        git for-each-ref --format="%(objectname) %(refname)" >brief-list
 '
 
-test_expect_success 'Broken refs are reported correctly' '
+test_expect_success REFFILES 'Broken refs are reported correctly' '
        r=refs/heads/bogus &&
        : >.git/$r &&
        test_when_finished "rm -f .git/$r" &&
@@ -24,7 +25,7 @@ test_expect_success 'Broken refs are reported correctly' '
        test_cmp broken-err err
 '
 
-test_expect_success 'NULL_SHA1 refs are reported correctly' '
+test_expect_success REFFILES 'NULL_SHA1 refs are reported correctly' '
        r=refs/heads/zeros &&
        echo $ZEROS >.git/$r &&
        test_when_finished "rm -f .git/$r" &&
@@ -38,19 +39,32 @@ test_expect_success 'NULL_SHA1 refs are reported correctly' '
 '
 
 test_expect_success 'Missing objects are reported correctly' '
-       r=refs/heads/missing &&
-       echo $MISSING >.git/$r &&
-       test_when_finished "rm -f .git/$r" &&
-       echo "fatal: missing object $MISSING for $r" >missing-err &&
+       test_when_finished "git update-ref -d refs/heads/missing" &&
+       test-tool ref-store main update-ref msg refs/heads/missing "$MISSING" "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
+       echo "fatal: missing object $MISSING for refs/heads/missing" >missing-err &&
        test_must_fail git for-each-ref 2>err &&
        test_cmp missing-err err &&
        (
                cat brief-list &&
-               echo "$MISSING $r"
+               echo "$MISSING refs/heads/missing"
        ) | sort -k 2 >missing-brief-expected &&
        git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
        test_cmp missing-brief-expected brief-out &&
        test_must_be_empty brief-err
 '
 
+test_expect_success 'ahead-behind requires an argument' '
+       test_must_fail git for-each-ref \
+               --format="%(ahead-behind)" 2>err &&
+       echo "fatal: expected format: %(ahead-behind:<committish>)" >expect &&
+       test_cmp expect err
+'
+
+test_expect_success 'missing ahead-behind base' '
+       test_must_fail git for-each-ref \
+               --format="%(ahead-behind:refs/heads/missing)" 2>err &&
+       echo "fatal: failed to find '\''refs/heads/missing'\''" >expect &&
+       test_cmp expect err
+'
+
 test_done
index 1ce5f490e99d374e039d29e708ee98dfb8988eab..82f3d1ea0f25ed60900b09a454e3c98965db574f 100755 (executable)
@@ -45,6 +45,8 @@ test_expect_success 'check signed tags with --points-at' '
        sed -e "s/Z$//" >expect <<-\EOF &&
        refs/heads/side Z
        refs/tags/annotated-tag four
+       refs/tags/doubly-annotated-tag four
+       refs/tags/doubly-signed-tag four
        refs/tags/four Z
        refs/tags/signed-tag four
        EOF
index 9d5e992878fad10f20c404a0dd676261345b2973..1962310408b12209aef235b9d6e09b8049de5f60 100755 (executable)
@@ -8,6 +8,8 @@
 
 
 test_description='Test criss-cross merge'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'prepare repository' '
index 772238e582c6f11b81c12a519f81bbe48cecf635..2738b50c2a9e01aab9e3248e666ddb91be9c3bcf 100755 (executable)
@@ -311,13 +311,13 @@ test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
        git checkout -q renamed-file-has-no-conflicts^0 &&
        test_must_fail git merge --strategy=recursive dir-in-way >output &&
 
-       test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
-       test_i18ngrep "Auto-merging dir" output &&
+       test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+       test_grep "Auto-merging dir" output &&
        if test "$GIT_TEST_MERGE_ALGORITHM" = ort
        then
-               test_i18ngrep "moving it to dir~HEAD instead" output
+               test_grep "moving it to dir~HEAD instead" output
        else
-               test_i18ngrep "Adding as dir~HEAD instead" output
+               test_grep "Adding as dir~HEAD instead" output
        fi &&
 
        test_stdout_line_count = 3 git ls-files -u &&
@@ -338,13 +338,13 @@ test_expect_success 'Same as previous, but merged other way' '
        test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors &&
 
        ! grep "error: refusing to lose untracked file at" errors &&
-       test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
-       test_i18ngrep "Auto-merging dir" output &&
+       test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+       test_grep "Auto-merging dir" output &&
        if test "$GIT_TEST_MERGE_ALGORITHM" = ort
        then
-               test_i18ngrep "moving it to dir~renamed-file-has-no-conflicts instead" output
+               test_grep "moving it to dir~renamed-file-has-no-conflicts instead" output
        else
-               test_i18ngrep "Adding as dir~renamed-file-has-no-conflicts instead" output
+               test_grep "Adding as dir~renamed-file-has-no-conflicts instead" output
        fi &&
 
        test_stdout_line_count = 3 git ls-files -u &&
index 1a7082323dddfce66b2d419efe60c87314c5f8e1..fb872c5a1136fc523dacdf6f9778b37441905980 100755 (executable)
@@ -56,7 +56,67 @@ test_expect_success 'setup' '
        deduxit me super semitas jusitiae,
        EOF
 
-       printf "propter nomen suum." >>new4.txt
+       printf "propter nomen suum." >>new4.txt &&
+
+       cat >base.c <<-\EOF &&
+       int f(int x, int y)
+       {
+               if (x == 0)
+               {
+                       return y;
+               }
+               return x;
+       }
+
+       int g(size_t u)
+       {
+               while (u < 30)
+               {
+                       u++;
+               }
+               return u;
+       }
+       EOF
+
+       cat >ours.c <<-\EOF &&
+       int g(size_t u)
+       {
+               while (u < 30)
+               {
+                       u++;
+               }
+               return u;
+       }
+
+       int h(int x, int y, int z)
+       {
+               if (z == 0)
+               {
+                       return x;
+               }
+               return y;
+       }
+       EOF
+
+       cat >theirs.c <<-\EOF
+       int f(int x, int y)
+       {
+               if (x == 0)
+               {
+                       return y;
+               }
+               return x;
+       }
+
+       int g(size_t u)
+       {
+               while (u > 34)
+               {
+                       u--;
+               }
+               return u;
+       }
+       EOF
 '
 
 test_expect_success 'merge with no changes' '
@@ -65,11 +125,30 @@ test_expect_success 'merge with no changes' '
        test_cmp test.txt orig.txt
 '
 
+test_expect_success 'merge with no changes with --object-id' '
+       git add orig.txt &&
+       git merge-file -p --object-id :orig.txt :orig.txt :orig.txt >actual &&
+       test_cmp actual orig.txt
+'
+
 test_expect_success "merge without conflict" '
        cp new1.txt test.txt &&
        git merge-file test.txt orig.txt new2.txt
 '
 
+test_expect_success 'merge without conflict with --object-id' '
+       git add orig.txt new2.txt &&
+       git merge-file --object-id :orig.txt :orig.txt :new2.txt >actual &&
+       git rev-parse :new2.txt >expected &&
+       test_cmp actual expected
+'
+
+test_expect_success 'can accept object ID with --object-id' '
+       git merge-file --object-id $(test_oid empty_blob) $(test_oid empty_blob) :new2.txt >actual &&
+       git rev-parse :new2.txt >expected &&
+       test_cmp actual expected
+'
+
 test_expect_success 'works in subdirectory' '
        mkdir dir &&
        cp new1.txt dir/a.txt &&
@@ -138,6 +217,31 @@ test_expect_success "expected conflict markers" '
        test_cmp expect.txt test.txt
 '
 
+test_expect_success "merge with conflicts with --object-id" '
+       git add backup.txt orig.txt new3.txt &&
+       test_must_fail git merge-file -p --object-id :backup.txt :orig.txt :new3.txt >actual &&
+       sed -e "s/<< test.txt/<< :backup.txt/" \
+           -e "s/>> new3.txt/>> :new3.txt/" \
+           expect.txt >expect &&
+       test_cmp expect actual &&
+       test_must_fail git merge-file --object-id :backup.txt :orig.txt :new3.txt >oid &&
+       git cat-file blob "$(cat oid)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success "merge with conflicts with --object-id with labels" '
+       git add backup.txt orig.txt new3.txt &&
+       test_must_fail git merge-file -p --object-id \
+               -L test.txt -L orig.txt -L new3.txt \
+               :backup.txt :orig.txt :new3.txt >actual &&
+       test_cmp expect.txt actual &&
+       test_must_fail git merge-file --object-id \
+               -L test.txt -L orig.txt -L new3.txt \
+               :backup.txt :orig.txt :new3.txt >oid &&
+       git cat-file blob "$(cat oid)" >actual &&
+       test_cmp expect.txt actual
+'
+
 test_expect_success "merge conflicting with --ours" '
        cp backup.txt test.txt &&
 
@@ -256,6 +360,14 @@ test_expect_success 'binary files cannot be merged' '
        grep "Cannot merge binary files" merge.err
 '
 
+test_expect_success 'binary files cannot be merged with --object-id' '
+       cp "$TEST_DIRECTORY"/test-binary-1.png . &&
+       git add orig.txt new1.txt test-binary-1.png &&
+       test_must_fail git merge-file --object-id \
+               :orig.txt :test-binary-1.png :new1.txt 2> merge.err &&
+       grep "Cannot merge binary files" merge.err
+'
+
 test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
        sed -e "s/deerit.\$/deerit;/" -e "s/me;\$/me./" <new5.txt >new6.txt &&
        sed -e "s/deerit.\$/deerit,/" -e "s/me;\$/me,/" <new5.txt >new7.txt &&
@@ -389,4 +501,72 @@ test_expect_success 'conflict sections match existing line endings' '
        test $(tr "\015" Q <nolf.txt | grep "^[<=>].*Q$" | wc -l) = 0
 '
 
+test_expect_success '--object-id fails without repository' '
+       empty="$(test_oid empty_blob)" &&
+       nongit test_must_fail git merge-file --object-id $empty $empty $empty 2>err &&
+       grep "not a git repository" err
+'
+
+test_expect_success 'merging C files with "myers" diff algorithm creates some spurious conflicts' '
+       cat >expect.c <<-\EOF &&
+       int g(size_t u)
+       {
+               while (u < 30)
+               {
+                       u++;
+               }
+               return u;
+       }
+
+       int h(int x, int y, int z)
+       {
+       <<<<<<< ours.c
+               if (z == 0)
+       ||||||| base.c
+               while (u < 30)
+       =======
+               while (u > 34)
+       >>>>>>> theirs.c
+               {
+       <<<<<<< ours.c
+                       return x;
+       ||||||| base.c
+                       u++;
+       =======
+                       u--;
+       >>>>>>> theirs.c
+               }
+               return y;
+       }
+       EOF
+
+       test_must_fail git merge-file -p --diff3 --diff-algorithm myers ours.c base.c theirs.c >myers_output.c &&
+       test_cmp expect.c myers_output.c
+'
+
+test_expect_success 'merging C files with "histogram" diff algorithm avoids some spurious conflicts' '
+       cat >expect.c <<-\EOF &&
+       int g(size_t u)
+       {
+               while (u > 34)
+               {
+                       u--;
+               }
+               return u;
+       }
+
+       int h(int x, int y, int z)
+       {
+               if (z == 0)
+               {
+                       return x;
+               }
+               return y;
+       }
+       EOF
+
+       git merge-file -p --diff3 --diff-algorithm histogram ours.c base.c theirs.c >histogram_output.c &&
+       test_cmp expect.c histogram_output.c
+'
+
 test_done
index 8650a88c40a2a58182ee39f36cdc59315ae1a35c..72f8c1722ff76d332529450b4115d7afb39ed5ec 100755 (executable)
@@ -8,6 +8,7 @@ test_description='per path merge controlled by merge attribute'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -55,6 +56,12 @@ test_expect_success setup '
        ) >"$ours+"
        cat "$ours+" >"$ours"
        rm -f "$ours+"
+
+       if test -f ./please-abort
+       then
+               echo >>./please-abort killing myself
+               kill -9 $$
+       fi
        exit "$exit"
        EOF
        chmod +x ./custom-merge
@@ -161,6 +168,24 @@ test_expect_success 'custom merge backend' '
        rm -f $o $a $b
 '
 
+test_expect_success !WINDOWS 'custom merge driver that is killed with a signal' '
+       test_when_finished "rm -f output please-abort" &&
+
+       git reset --hard anchor &&
+       git config --replace-all \
+       merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
+       git config --replace-all \
+       merge.custom.name "custom merge driver for testing" &&
+
+       >./please-abort &&
+       echo "* merge=custom" >.gitattributes &&
+       test_must_fail git merge main 2>err &&
+       grep "^error: failed to execute internal merge" err &&
+       git ls-files -u >output &&
+       git diff --name-only HEAD >>output &&
+       test_must_be_empty output
+'
+
 test_expect_success 'up-to-date merge without common ancestor' '
        git init repo1 &&
        git init repo2 &&
index e8a28717cece3248c2d9996e797dcfdb9af128eb..0753fc95f45efb642543f9f23191d3430d4d6cde 100755 (executable)
@@ -5,6 +5,7 @@ test_description='ask merge-recursive to merge binary files'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 2655e295f5ae4536eaf86dcf95e8062f61cc2948..ae00492c768217a46d73fde7cfbc330dcc502616 100755 (executable)
@@ -4,6 +4,7 @@ test_description='merging when a directory was replaced with a symlink'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create a commit where dir a/b changed to symlink' '
index 17b54d625d0e468047377c378faae25c50633c9b..5f414abc89267d3f3729366d67f8b7d6aec52c37 100755 (executable)
@@ -5,6 +5,7 @@ test_description='recursive merge corner cases involving criss-cross merges'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-merge.sh
 
index 5413e5dd9d68c36b95ecf9ccf5ca9e438ff0413b..711b709e755268ab1d02986c8aaa8257c27b41ee 100755 (executable)
@@ -155,7 +155,7 @@ test_setup_repo () {
 #   Commit A:
 #     (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
 #      -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
-#      both both Makefiles and jumps)
+#      both Makefiles and jumps)
 #              general/{jump1_A, jump2_A}
 #              basename/subdir/{numbers_A, sequence_A, values_A}
 #              folder/subdir/{a,b,c,d,Makefile_TOP_A}
@@ -343,7 +343,7 @@ test_expect_merge_algorithm failure success 'Objects downloaded when a directory
 #   Commit A:
 #     (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
 #      -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
-#      both both Makefiles and jumps)
+#      both Makefiles and jumps)
 #              general/{jump1_A, jump2_A}
 #              basename/subdir/{numbers_A, sequence_A, values_A}
 #              folder/subdir/{a,b,c,d,Makefile_TOP_A}
index 346253c7c88ee58ebf6735b6c06ae69f18ce5861..80d7b5eabaf02e555cab3c4e37342a9eb2788f5f 100755 (executable)
@@ -476,7 +476,7 @@ test_expect_success 'handle rename-with-content-merge vs. add' '
                git checkout A^0 &&
 
                test_must_fail git merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (.*/add)" out &&
+               test_grep "CONFLICT (.*/add)" out &&
 
                git ls-files -s >out &&
                test_line_count = 2 out &&
@@ -522,7 +522,7 @@ test_expect_success 'handle rename-with-content-merge vs. add, merge other way'
                git checkout B^0 &&
 
                test_must_fail git merge -s recursive A^0 >out &&
-               test_i18ngrep "CONFLICT (.*/add)" out &&
+               test_grep "CONFLICT (.*/add)" out &&
 
                git ls-files -s >out &&
                test_line_count = 2 out &&
@@ -602,7 +602,7 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' '
                git checkout B^0 &&
 
                test_must_fail git merge -s recursive C^0 >out &&
-               test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
+               test_grep "CONFLICT (\(.*\)/\1)" out &&
 
                git ls-files -s >out &&
                test_line_count = 2 out &&
@@ -914,8 +914,8 @@ test_expect_merge_algorithm failure success 'rad-check: rename/add/delete confli
                # be flexible in the type of console output message(s) reported
                # for this particular case; we will be more stringent about the
                # contents of the index and working directory.
-               test_i18ngrep "CONFLICT (.*/add)" out &&
-               test_i18ngrep "CONFLICT (rename.*/delete)" out &&
+               test_grep "CONFLICT (.*/add)" out &&
+               test_grep "CONFLICT (rename.*/delete)" out &&
                test_must_be_empty err &&
 
                git ls-files -s >file_count &&
@@ -988,8 +988,8 @@ test_expect_merge_algorithm failure success 'rrdd-check: rename/rename(2to1)/del
                # be flexible in the type of console output message(s) reported
                # for this particular case; we will be more stringent about the
                # contents of the index and working directory.
-               test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
-               test_i18ngrep "CONFLICT (rename.*delete)" out &&
+               test_grep "CONFLICT (\(.*\)/\1)" out &&
+               test_grep "CONFLICT (rename.*delete)" out &&
                test_must_be_empty err &&
 
                git ls-files -s >file_count &&
@@ -1068,7 +1068,7 @@ test_expect_merge_algorithm failure success 'mod6-check: chains of rename/rename
 
                test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep "CONFLICT (rename/rename)" out &&
+               test_grep "CONFLICT (rename/rename)" out &&
                test_must_be_empty err &&
 
                git ls-files -s >file_count &&
@@ -1159,7 +1159,6 @@ test_conflicts_with_adds_and_renames() {
        #   4) There should not be any three~* files in the working
        #      tree
        test_setup_collision_conflict () {
-       #test_expect_success "setup simple $sideL/$sideR conflict" '
                git init simple_${sideL}_${sideR} &&
                (
                        cd simple_${sideL}_${sideR} &&
@@ -1236,7 +1235,6 @@ test_conflicts_with_adds_and_renames() {
                        fi &&
                        test_tick && git commit -m R
                )
-       #'
        }
 
        test_expect_success "check simple $sideL/$sideR conflict" '
index a4941878fe2a693910cca1f226277747706a5017..88d1cf2cde9dabbba74daf6debdc82b11ba12ec1 100755 (executable)
@@ -276,7 +276,7 @@ test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict'
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
+               test_grep "CONFLICT (\(.*\)/\1)" out &&
 
                git ls-files -s >out &&
                test_line_count = 8 out &&
@@ -515,7 +515,7 @@ test_expect_success '2a: Directory split into two on one side, with equal number
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT.*directory rename split" out &&
+               test_grep "CONFLICT.*directory rename split" out &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -591,7 +591,7 @@ test_expect_success '2b: Directory split into two on one side, with equal number
                git rev-parse >expect \
                         O:z/b  O:z/c  B:x/d &&
                test_cmp expect actual &&
-               test_i18ngrep ! "CONFLICT.*directory rename split" out
+               test_grep ! "CONFLICT.*directory rename split" out
        )
 '
 
@@ -726,8 +726,8 @@ test_expect_success '3b: Avoid implicit rename if involved as source on current
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
-               test_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&
+               test_grep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
+               test_grep ! CONFLICT.*rename/rename.*y/d out &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
@@ -938,7 +938,7 @@ test_expect_success '5a: Merge directories, other side adds files to original an
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT.*implicit dir rename" out &&
+               test_grep "CONFLICT.*implicit dir rename" out &&
 
                git ls-files -s >out &&
                test_line_count = 6 out &&
@@ -1013,7 +1013,7 @@ test_expect_success '5b: Rename/delete in order to get add/add/add conflict' '
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (add/add).* y/d" out &&
+               test_grep "CONFLICT (add/add).* y/d" out &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
@@ -1094,8 +1094,8 @@ test_expect_success '5c: Transitive rename would cause rename/rename/rename/add/
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
-               test_i18ngrep "CONFLICT (add/add).* y/d" out &&
+               test_grep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
+               test_grep "CONFLICT (add/add).* y/d" out &&
 
                git ls-files -s >out &&
                test_line_count = 9 out &&
@@ -1179,7 +1179,7 @@ test_expect_success '5d: Directory/file/file conflict due to directory rename' '
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
+               test_grep "CONFLICT (file/directory).*y/d" out &&
 
                git ls-files -s >out &&
                test_line_count = 6 out &&
@@ -1278,7 +1278,7 @@ test_expect_success '6a: Tricky rename/delete' '
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&
+               test_grep "CONFLICT (rename/delete).*z/c.*y/c" out &&
 
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
@@ -1740,8 +1740,8 @@ test_expect_success '7a: rename-dir vs. rename-dir (NOT split evenly) PLUS add-o
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
-               test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
+               test_grep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
+               test_grep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
 
                git ls-files -s >out &&
                test_line_count = 7 out &&
@@ -1813,7 +1813,7 @@ test_expect_success '7b: rename/rename(2to1), but only due to transitive rename'
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
+               test_grep "CONFLICT (\(.*\)/\1)" out &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
@@ -1900,7 +1900,7 @@ test_expect_success '7c: rename/rename(1to...2or3); transitive rename may add co
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
+               test_grep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
@@ -1965,7 +1965,7 @@ test_expect_success '7d: transitive rename involved in rename/delete; how is it
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
+               test_grep "CONFLICT (rename/delete).*x/d.*y/d" out &&
 
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
@@ -2071,7 +2071,7 @@ test_expect_success '7e: transitive rename in rename/delete AND dirs in the way'
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
+               test_grep "CONFLICT (rename/delete).*x/d.*y/d" out &&
 
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
@@ -2330,7 +2330,7 @@ test_expect_success '8c: modify/delete or rename+modify/delete' '
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "CONFLICT (modify/delete).* z/d" out &&
+               test_grep "CONFLICT (modify/delete).* z/d" out &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
@@ -2491,8 +2491,8 @@ test_expect_success '8e: Both sides rename, one side adds to original directory'
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
-               test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
-               test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
+               test_grep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
+               test_grep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
 
                git ls-files -s >out &&
                test_line_count = 7 out &&
@@ -2741,7 +2741,7 @@ test_expect_success '9c: Doubly transitive rename?' '
                git checkout A^0 &&
 
                git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&
+               test_grep "WARNING: Avoiding applying x -> z rename to x/f" out &&
 
                git ls-files -s >out &&
                test_line_count = 6 out &&
@@ -2830,10 +2830,10 @@ test_expect_success '9d: N-way transitive rename?' '
                git checkout A^0 &&
 
                git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-               test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&
-               test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&
-               test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&
-               test_i18ngrep "WARNING: Avoiding applying w -> v rename to w/c" out &&
+               test_grep "WARNING: Avoiding applying z -> y rename to z/t" out &&
+               test_grep "WARNING: Avoiding applying y -> x rename to y/a" out &&
+               test_grep "WARNING: Avoiding applying x -> w rename to x/b" out &&
+               test_grep "WARNING: Avoiding applying w -> v rename to w/c" out &&
 
                git ls-files -s >out &&
                test_line_count = 7 out &&
@@ -3215,7 +3215,7 @@ test_expect_success '10a: Overwrite untracked with normal rename/delete' '
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_path_is_missing .git/MERGE_HEAD &&
-               test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&
+               test_grep "The following untracked working tree files would be overwritten by merge" err &&
 
                git ls-files -s >out &&
                test_line_count = 1 out &&
@@ -3287,7 +3287,7 @@ test_expect_success '10b: Overwrite untracked with dir rename + delete' '
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+                       test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
 
                        git ls-files -s >out &&
                        test_line_count = 1 out &&
@@ -3296,8 +3296,8 @@ test_expect_success '10b: Overwrite untracked with dir rename + delete' '
                        git ls-files -o >out &&
                        test_line_count = 5 out
                else
-                       test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
-                       test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
+                       test_grep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
+                       test_grep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 3 out &&
@@ -3377,7 +3377,7 @@ test_expect_success '10c1: Overwrite untracked with dir rename/rename(1to2)' '
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+                       test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
 
                        git ls-files -s >out &&
                        test_line_count = 4 out &&
@@ -3386,8 +3386,8 @@ test_expect_success '10c1: Overwrite untracked with dir rename/rename(1to2)' '
                        git ls-files -o >out &&
                        test_line_count = 3 out
                else
-                       test_i18ngrep "CONFLICT (rename/rename)" out &&
-                       test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
+                       test_grep "CONFLICT (rename/rename)" out &&
+                       test_grep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 6 out &&
@@ -3428,7 +3428,7 @@ test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), oth
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+                       test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
 
                        git ls-files -s >out &&
                        test_line_count = 4 out &&
@@ -3437,8 +3437,8 @@ test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), oth
                        git ls-files -o >out &&
                        test_line_count = 3 out
                else
-                       test_i18ngrep "CONFLICT (rename/rename)" out &&
-                       test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
+                       test_grep "CONFLICT (rename/rename)" out &&
+                       test_grep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 6 out &&
@@ -3517,7 +3517,7 @@ test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+                       test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
 
                        git ls-files -s >out &&
                        test_line_count = 6 out &&
@@ -3526,8 +3526,8 @@ test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
                        git ls-files -o >out &&
                        test_line_count = 3 out
                else
-                       test_i18ngrep "CONFLICT (rename/rename)" out &&
-                       test_i18ngrep "Refusing to lose untracked file at y/wham" out &&
+                       test_grep "CONFLICT (rename/rename)" out &&
+                       test_grep "Refusing to lose untracked file at y/wham" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 6 out &&
@@ -3606,7 +3606,7 @@ test_expect_merge_algorithm failure success '10e: Does git complain about untrac
                echo random >z/c &&
 
                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
-               test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&
+               test_grep ! "following untracked working tree files would be overwritten by merge" err &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -3690,9 +3690,9 @@ test_expect_success '11a: Avoid losing dirty contents with simple rename' '
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+                       test_grep "error: Your local changes to the following files would be overwritten by merge" err
                else
-                       test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+                       test_grep "Refusing to lose dirty file at z/c" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 2 out &&
@@ -3770,10 +3770,10 @@ test_expect_success '11b: Avoid losing dirty file involved in directory rename'
                then
                        test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+                       test_grep "error: Your local changes to the following files would be overwritten by merge" err
                else
                        git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
-                       test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+                       test_grep "Refusing to lose dirty file at z/c" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 3 out &&
@@ -3853,9 +3853,9 @@ test_expect_success '11c: Avoid losing not-uptodate with rename + D/F conflict'
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+                       test_grep "error: Your local changes to the following files would be overwritten by merge" err
                else
-                       test_i18ngrep "following files would be overwritten by merge" err
+                       test_grep "following files would be overwritten by merge" err
                fi &&
 
                grep -q stuff y/c &&
@@ -3927,9 +3927,9 @@ test_expect_success '11d: Avoid losing not-uptodate with rename + D/F conflict'
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+                       test_grep "error: Your local changes to the following files would be overwritten by merge" err
                else
-                       test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+                       test_grep "Refusing to lose dirty file at z/c" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 4 out &&
@@ -4013,10 +4013,10 @@ test_expect_success '11e: Avoid deleting not-uptodate with dir rename/rename(1to
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+                       test_grep "error: Your local changes to the following files would be overwritten by merge" err
                else
-                       test_i18ngrep "CONFLICT (rename/rename)" out &&
-                       test_i18ngrep "Refusing to lose dirty file at y/c" out &&
+                       test_grep "CONFLICT (rename/rename)" out &&
+                       test_grep "Refusing to lose dirty file at y/c" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 7 out &&
@@ -4102,10 +4102,10 @@ test_expect_success '11f: Avoid deleting not-uptodate with dir rename/rename(2to
                if test "$GIT_TEST_MERGE_ALGORITHM" = ort
                then
                        test_path_is_missing .git/MERGE_HEAD &&
-                       test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+                       test_grep "error: Your local changes to the following files would be overwritten by merge" err
                else
-                       test_i18ngrep "CONFLICT (rename/rename)" out &&
-                       test_i18ngrep "Refusing to lose dirty file at y/wham" out &&
+                       test_grep "CONFLICT (rename/rename)" out &&
+                       test_grep "Refusing to lose dirty file at y/wham" out &&
 
                        git ls-files -s >out &&
                        test_line_count = 4 out &&
@@ -5304,6 +5304,62 @@ test_expect_merge_algorithm failure success '12l (A into B): Rename into each ot
        )
 '
 
+# Testcase 12m, Directory rename, plus change of parent dir to symlink
+#   Commit O:  dir/subdir/file
+#   Commit A:  renamed-dir/subdir/file
+#   Commit B:  dir/subdir
+#   In words:
+#     A: dir/subdir/ -> renamed-dir/subdir
+#     B: delete dir/subdir/file, add dir/subdir as symlink
+#
+#   Expected: CONFLICT (rename/delete): renamed-dir/subdir/file,
+#             CONFLICT (file location): renamed-dir/subdir vs. dir/subdir
+#             CONFLICT (directory/file): renamed-dir/subdir symlink has
+#                                        renamed-dir/subdir in the way
+
+test_setup_12m () {
+       git init 12m &&
+       (
+               cd 12m &&
+
+               mkdir -p dir/subdir &&
+               echo 1 >dir/subdir/file &&
+               git add . &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git switch A &&
+               git mv dir/ renamed-dir/ &&
+               git add . &&
+               git commit -m "A" &&
+
+               git switch B &&
+               git rm dir/subdir/file &&
+               mkdir dir &&
+               ln -s /dev/null dir/subdir &&
+               git add . &&
+               git commit -m "B"
+       )
+}
+
+test_expect_merge_algorithm failure success '12m: Change parent of renamed-dir to symlink on other side' '
+       test_setup_12m &&
+       (
+               cd 12m &&
+
+               git checkout -q A^0 &&
+
+               test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
+
+               test_stdout_line_count = 3 git ls-files -s &&
+               test_stdout_line_count = 2 ls -1 renamed-dir &&
+               test_path_is_missing dir
+       )
+'
+
 ###########################################################################
 # SECTION 13: Checking informational and conflict messages
 #
@@ -5361,8 +5417,8 @@ test_expect_success '13a(conflict): messages for newly added files' '
 
                test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
-               test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
+               test_grep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
+               test_grep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
 
                git ls-files >paths &&
                ! grep z/ paths &&
@@ -5385,8 +5441,8 @@ test_expect_success '13a(info): messages for newly added files' '
 
                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
-               test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&
+               test_grep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
+               test_grep Path.updated:.*z/d.added.in.B^0.*y/d out &&
 
                git ls-files >paths &&
                ! grep z/ paths &&
@@ -5451,8 +5507,8 @@ test_expect_success '13b(conflict): messages for transitive rename with conflict
 
                test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
-               test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+               test_grep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+               test_grep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
 
                git ls-files >paths &&
                ! grep z/ paths &&
@@ -5473,8 +5529,8 @@ test_expect_success '13b(info): messages for transitive rename with conflicted c
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
-               test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+               test_grep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+               test_grep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
 
                git ls-files >paths &&
                ! grep z/ paths &&
@@ -5537,7 +5593,7 @@ test_expect_success '13c(conflict): messages for rename/rename(1to1) via transit
 
                test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+               test_grep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
 
                git ls-files >paths &&
                ! grep z/ paths &&
@@ -5558,7 +5614,7 @@ test_expect_success '13c(info): messages for rename/rename(1to1) via transitive
 
                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+               test_grep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
 
                git ls-files >paths &&
                ! grep z/ paths &&
@@ -5626,8 +5682,8 @@ test_expect_success '13d(conflict): messages for rename/rename(1to1) via dual tr
 
                test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
-               test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
+               test_grep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
+               test_grep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
 
                git ls-files >paths &&
                ! grep b/ paths &&
@@ -5650,8 +5706,8 @@ test_expect_success '13d(info): messages for rename/rename(1to1) via dual transi
 
                git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
-               test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
+               test_grep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
+               test_grep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
 
                git ls-files >paths &&
                ! grep b/ paths &&
@@ -5765,9 +5821,9 @@ test_expect_success '13e: directory rename detection in recursive case' '
 
                git -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err &&
 
-               test_i18ngrep ! CONFLICT out &&
-               test_i18ngrep ! BUG: err &&
-               test_i18ngrep ! core.dumped err &&
+               test_grep ! CONFLICT out &&
+               test_grep ! BUG: err &&
+               test_grep ! core.dumped err &&
                test_must_be_empty err &&
 
                git ls-files >paths &&
index a61f20c22fe62031da12af45bd8ca427782043bf..7677c5f08d0e8fb66dd243c05e9a9b892b6114df 100755 (executable)
@@ -178,7 +178,7 @@ test_expect_success 'merge-recursive, when index==head but head!=HEAD' '
        test_when_finished "git clean -fd" &&  # Do not leave untracked around
        # Merge B & F, with B as "head"
        git merge-recursive A -- B F > out &&
-       test_i18ngrep "Already up to date" out
+       test_grep "Already up to date" out
 '
 
 test_expect_success 'recursive, when file has staged changes not matching HEAD nor what a merge would give' '
@@ -194,7 +194,7 @@ test_expect_success 'recursive, when file has staged changes not matching HEAD n
        test_must_fail git merge -s recursive E^0 2>err &&
        git rev-parse --verify :subdir/a >actual &&
        test_cmp expect actual &&
-       test_i18ngrep "changes to the following files would be overwritten" err
+       test_grep "changes to the following files would be overwritten" err
 '
 
 test_expect_success 'recursive, when file has staged changes matching what a merge would give' '
@@ -210,7 +210,7 @@ test_expect_success 'recursive, when file has staged changes matching what a mer
        test_must_fail git merge -s recursive E^0 2>err &&
        git rev-parse --verify :subdir/a >actual &&
        test_cmp expect actual &&
-       test_i18ngrep "changes to the following files would be overwritten" err
+       test_grep "changes to the following files would be overwritten" err
 '
 
 test_expect_success 'octopus, unrelated file touched' '
index 93cd2869b12897b5246a3a977f0cdaf1b2911663..b95b064311b6762da86c6239a93651edd9dede78 100755 (executable)
@@ -21,8 +21,8 @@ test_expect_success 'rename/delete' '
        git commit -m "delete" &&
 
        test_must_fail git merge --strategy=recursive rename >output &&
-       test_i18ngrep "CONFLICT (rename/delete): A.* renamed .*to B.* in rename" output &&
-       test_i18ngrep "CONFLICT (rename/delete): A.*deleted in HEAD." output
+       test_grep "CONFLICT (rename/delete): A.* renamed .*to B.* in rename" output &&
+       test_grep "CONFLICT (rename/delete): A.*deleted in HEAD." output
 '
 
 test_done
index 2bb8e7f09bb5897fa670aaf808e84ac7bc3516d3..b059475ed033440bad42a059341b63176314e642 100755 (executable)
@@ -375,45 +375,33 @@ test_expect_success '2c: Modify b & add c VS rename b->c' '
                export GIT_MERGE_VERBOSITY &&
                test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-               test_i18ngrep "CONFLICT (.*/add):" out &&
+               test_grep "CONFLICT (.*/add):" out &&
                test_must_be_empty err &&
 
-               # Make sure c WAS updated
+               git ls-files -s >index_files &&
+               test_line_count = 2 index_files &&
+
+               # Ensure b was removed
+               test_path_is_missing b &&
+
+               # Make sure c WAS updated...
                test-tool chmtime --get c >new-mtime &&
-               test $(cat old-mtime) -lt $(cat new-mtime)
-
-               # FIXME: rename/add conflicts are horribly broken right now;
-               # when I get back to my patch series fixing it and
-               # rename/rename(2to1) conflicts to bring them in line with
-               # how add/add conflicts behave, then checks like the below
-               # could be added.  But that patch series is waiting until
-               # the rename-directory-detection series lands, which this
-               # is part of.  And in the mean time, I do not want to further
-               # enforce broken behavior.  So for now, the main test is the
-               # one above that err is an empty file.
-
-               #git ls-files -s >index_files &&
-               #test_line_count = 2 index_files &&
-
-               #git rev-parse >actual :2:c :3:c &&
-               #git rev-parse >expect A:b  A:c  &&
-               #test_cmp expect actual &&
-
-               #git cat-file -p A:b >>merged &&
-               #git cat-file -p A:c >>merge-me &&
-               #>empty &&
-               #test_must_fail git merge-file \
-               #       -L "Temporary merge branch 1" \
-               #       -L "" \
-               #       -L "Temporary merge branch 2" \
-               #       merged empty merge-me &&
-               #sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
-
-               #git hash-object c               >actual &&
-               #git hash-object merged-internal >expect &&
-               #test_cmp expect actual &&
-
-               #test_path_is_missing b
+               test $(cat old-mtime) -lt $(cat new-mtime) &&
+
+               # ...and has correct index entries and working tree contents
+               git rev-parse >actual :2:c :3:c &&
+               git rev-parse >expect A:c  A:b  &&
+               test_cmp expect actual &&
+
+               git cat-file -p A:b >>merge-me &&
+               git cat-file -p A:c >>merged &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       merged empty merge-me &&
+               test_cmp merged c
        )
 '
 
index d02fa16614e0622585a7732942ee9d5b515e6afa..0f39ed0d08a34230c7c42f307992c20cc8b06d02 100755 (executable)
@@ -71,8 +71,9 @@ test_expect_success 'caching renames does not preclude finding new ones' '
 
                git switch upstream &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream~1..topic
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                git ls-files >tracked-files &&
                test_line_count = 2 tracked-files &&
@@ -140,8 +141,9 @@ test_expect_success 'cherry-pick both a commit and its immediate revert' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream~1..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 1 calls
@@ -199,8 +201,9 @@ test_expect_success 'rename same file identically, then reintroduce it' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream~1..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                git ls-files >tracked &&
                test_line_count = 2 tracked &&
@@ -276,8 +279,9 @@ test_expect_success 'rename same file identically, then add file to old dir' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream~1..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                git ls-files >tracked &&
                test_line_count = 4 tracked &&
@@ -353,10 +357,7 @@ test_expect_success 'cached dir rename does not prevent noticing later conflict'
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test_must_fail test-tool fast-rebase --onto HEAD upstream~1 topic >output &&
-               #git cherry-pick upstream..topic &&
-
-               grep CONFLICT..rename/rename output &&
+               test_must_fail git replay --onto HEAD upstream~1..topic >output &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 2 calls
@@ -455,8 +456,9 @@ test_expect_success 'dir rename unneeded, then add new file to old dir' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 2 calls &&
@@ -521,8 +523,9 @@ test_expect_success 'dir rename unneeded, then rename existing file into old dir
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 3 calls &&
@@ -623,8 +626,9 @@ test_expect_success 'caching renames only on upstream side, part 1' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 1 calls &&
@@ -681,8 +685,9 @@ test_expect_success 'caching renames only on upstream side, part 2' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 2 calls &&
index 07067bb347955b146b9654b353e9db2b8b2cd323..ca15e6dd6da94bc1bac8286e9e36a196cde7a797 100755 (executable)
@@ -308,13 +308,13 @@ test_expect_success 'fail if the index has unresolved entries' '
 
        test_must_fail git merge "$c5" &&
        test_must_fail git merge "$c5" 2> out &&
-       test_i18ngrep "not possible because you have unmerged files" out &&
+       test_grep "not possible because you have unmerged files" out &&
        git add -u &&
        test_must_fail git merge "$c5" 2> out &&
-       test_i18ngrep "You have not concluded your merge" out &&
+       test_grep "You have not concluded your merge" out &&
        rm -f .git/MERGE_HEAD &&
        test_must_fail git merge "$c5" 2> out &&
-       test_i18ngrep "Your local changes to the following files would be overwritten by merge:" out
+       test_grep "Your local changes to the following files would be overwritten by merge:" out
 '
 
 test_expect_success 'merge-recursive remove conflict' '
@@ -713,7 +713,7 @@ test_expect_success 'merge-recursive remembers the names of all base trees' '
        test_must_fail git -c merge.verbosity=5 merge-recursive $(cat trees) -- $c1 $c3 >out &&
 
        # ...but make sure it fails in the expected way
-       test_i18ngrep CONFLICT.*rename/rename out &&
+       test_grep CONFLICT.*rename/rename out &&
 
        # merge-recursive prints in reverse order, but we do not care
        sort <trees >expect &&
index b16031465f34b20600cadd51c2f620ec64ae0e54..ed7866d3e955a3d093b6428b750f4d8d7ec1ae63 100755 (executable)
@@ -5,6 +5,7 @@ test_description='"git merge" top-level frontend'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 t3033_reset () {
@@ -151,7 +152,7 @@ test_expect_success 'refuse two-project merge by default, quit before --autostas
        echo change >>one.t &&
        git diff >expect &&
        test_must_fail git merge --autostash five 2>err &&
-       test_i18ngrep ! "stash" err &&
+       test_grep ! "stash" err &&
        git diff >actual &&
        test_cmp expect actual
 '
@@ -169,7 +170,7 @@ test_expect_success 'two-project merge with --allow-unrelated-histories with --a
        echo change >>one.t &&
        git diff one.t >expect &&
        git merge --allow-unrelated-histories --autostash five 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        git diff one.t >actual &&
        test_cmp expect actual
 '
index fde4aa3cd1ab66a13c61234155da0c2bacb851a9..78628fb248ab6143ef83d11e819f15c30b52f278 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='merge with sparse files'
 
 TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # test_file $filename $content
index c0b7bd7c3fe55303d62caed5f4c02368d2e3fd3c..4f4376421e7da28e22f58258061bf58f3a8bc0c8 100755 (executable)
@@ -104,12 +104,12 @@ test_expect_success 'will not overwrite unstaged changes in renamed file' '
        if test "$GIT_TEST_MERGE_ALGORITHM" = ort
        then
                test_must_fail git merge c1a >out 2>err &&
-               test_i18ngrep "would be overwritten by merge" err &&
+               test_grep "would be overwritten by merge" err &&
                test_cmp important other.c &&
                test_path_is_missing .git/MERGE_HEAD
        else
                test_must_fail git merge c1a >out &&
-               test_i18ngrep "Refusing to lose dirty file at other.c" out &&
+               test_grep "Refusing to lose dirty file at other.c" out &&
                test_path_is_file other.c~HEAD &&
                test $(git hash-object other.c~HEAD) = $(git rev-parse c1a:c1.c) &&
                test_cmp important other.c
index c9a86f2e947e4a2943438cc5adcaaeab39a465d9..70650521b042b29b41ddf7b0ee794095063e457f 100755 (executable)
@@ -8,6 +8,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
 export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-merge.sh
 
@@ -479,7 +480,7 @@ test_expect_merge_algorithm failure success !FAIL_PREREQS 'directory/submodule c
                # We do not want files within the submodule to prevent the
                # merge from starting; we should not be writing to such paths
                # anyway.
-               test_i18ngrep ! "refusing to lose untracked file at" err
+               test_grep ! "refusing to lose untracked file at" err
        )
 '
 
index 52cf0c87690be83e758b97c12b0ab00fa77613a2..0cbec57cdabc48b7cd952a6fbf5458391b44a645 100755 (executable)
@@ -5,6 +5,7 @@ test_description='unpack-trees error messages'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
index cd6c53360d206bef95cac3af0188c5eb3052e76c..18fe1c25e6a04b75d2fcdf9ac0c60da04c618ba8 100755 (executable)
@@ -41,7 +41,7 @@ test_expect_success 'gc does not leave behind pid file' '
 
 test_expect_success 'gc --gobbledegook' '
        test_expect_code 129 git gc --nonsense 2>err &&
-       test_i18ngrep "[Uu]sage: git gc" err
+       test_grep "[Uu]sage: git gc" err
 '
 
 test_expect_success 'gc -h with invalid configuration' '
@@ -52,7 +52,7 @@ test_expect_success 'gc -h with invalid configuration' '
                echo "[gc] pruneexpire = CORRUPT" >>.git/config &&
                test_expect_code 129 git gc -h >usage 2>&1
        ) &&
-       test_i18ngrep "[Uu]sage" broken/usage
+       test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'gc is not aborted due to a stale symref' '
@@ -155,7 +155,7 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
        test_commit "$(test_oid obj4)" &&
 
        git gc --auto 2>err &&
-       test_i18ngrep ! "^warning:" err &&
+       test_grep ! "^warning:" err &&
        ls .git/objects/pack/pack-*.pack | sort >post_packs &&
        comm -1 -3 existing_packs post_packs >new &&
        comm -2 -3 existing_packs post_packs >del &&
@@ -166,15 +166,15 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
 test_expect_success 'gc --no-quiet' '
        GIT_PROGRESS_DELAY=0 git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
        test_must_be_empty stdout &&
-       test_i18ngrep "Computing commit graph generation numbers" stderr
+       test_grep "Computing commit graph generation numbers" stderr
 '
 
 test_expect_success TTY 'with TTY: gc --no-quiet' '
        test_terminal env GIT_PROGRESS_DELAY=0 \
                git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
        test_must_be_empty stdout &&
-       test_i18ngrep "Enumerating objects" stderr &&
-       test_i18ngrep "Computing commit graph generation numbers" stderr
+       test_grep "Enumerating objects" stderr &&
+       test_grep "Computing commit graph generation numbers" stderr
 '
 
 test_expect_success 'gc --quiet' '
@@ -202,6 +202,158 @@ test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "e
        grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
 '
 
+test_expect_success 'gc.repackFilter launches repack with a filter' '
+       git clone --no-local --bare . bare.git &&
+
+       git -C bare.git -c gc.cruftPacks=false gc &&
+       test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+
+       GIT_TRACE=$(pwd)/trace.out git -C bare.git -c gc.repackFilter=blob:none \
+               -c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
+       test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
+       grep -E "^trace: (built-in|exec|run_command): git repack .* --filter=blob:none ?.*" trace.out
+'
+
+test_expect_success 'gc.repackFilterTo store filtered out objects' '
+       test_when_finished "rm -rf bare.git filtered.git" &&
+
+       git init --bare filtered.git &&
+       git -C bare.git -c gc.repackFilter=blob:none \
+               -c gc.repackFilterTo=../filtered.git/objects/pack/pack \
+               -c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
+
+       test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+       test_stdout_line_count = 1 ls filtered.git/objects/pack/*.pack
+'
+
+prepare_cruft_history () {
+       test_commit base &&
+
+       test_commit --no-tag foo &&
+       test_commit --no-tag bar &&
+       git reset HEAD^^
+}
+
+assert_no_cruft_packs () {
+       find .git/objects/pack -name "*.mtimes" >mtimes &&
+       test_must_be_empty mtimes
+}
+
+for argv in \
+       "gc" \
+       "-c gc.cruftPacks=true gc" \
+       "-c gc.cruftPacks=false gc --cruft"
+do
+       test_expect_success "git $argv generates a cruft pack" '
+               test_when_finished "rm -fr repo" &&
+               git init repo &&
+               (
+                       cd repo &&
+
+                       prepare_cruft_history &&
+                       git $argv &&
+
+                       find .git/objects/pack -name "*.mtimes" >mtimes &&
+                       sed -e 's/\.mtimes$/\.pack/g' mtimes >packs &&
+
+                       test_file_not_empty packs &&
+                       while read pack
+                       do
+                               test_path_is_file "$pack" || return 1
+                       done <packs
+               )
+       '
+done
+
+for argv in \
+       "gc --no-cruft" \
+       "-c gc.cruftPacks=false gc" \
+       "-c gc.cruftPacks=true gc --no-cruft"
+do
+       test_expect_success "git $argv does not generate a cruft pack" '
+               test_when_finished "rm -fr repo" &&
+               git init repo &&
+               (
+                       cd repo &&
+
+                       prepare_cruft_history &&
+                       git $argv &&
+
+                       assert_no_cruft_packs
+               )
+       '
+done
+
+test_expect_success '--keep-largest-pack ignores cruft packs' '
+       test_when_finished "rm -fr repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               # Generate a pack for reachable objects (of which there
+               # are 3), and one for unreachable objects (of which
+               # there are 6).
+               prepare_cruft_history &&
+               git gc --cruft &&
+
+               mtimes="$(find .git/objects/pack -type f -name "pack-*.mtimes")" &&
+               sz="$(test_file_size "${mtimes%.mtimes}.pack")" &&
+
+               # Ensure that the cruft pack gets removed (due to
+               # `--prune=now`) despite it being the largest pack.
+               git -c gc.bigPackThreshold=$sz gc --cruft --prune=now &&
+
+               assert_no_cruft_packs
+       )
+'
+
+test_expect_success 'gc.bigPackThreshold ignores cruft packs' '
+       test_when_finished "rm -fr repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               # Generate a pack for reachable objects (of which there
+               # are 3), and one for unreachable objects (of which
+               # there are 6).
+               prepare_cruft_history &&
+               git gc --cruft &&
+
+               # Ensure that the cruft pack gets removed (due to
+               # `--prune=now`) despite it being the largest pack.
+               git gc --cruft --prune=now --keep-largest-pack &&
+
+               assert_no_cruft_packs
+       )
+'
+
+cruft_max_size_opts="git repack -d -l --cruft --cruft-expiration=2.weeks.ago"
+
+test_expect_success 'setup for --max-cruft-size tests' '
+       git init cruft--max-size &&
+       (
+               cd cruft--max-size &&
+               prepare_cruft_history
+       )
+'
+
+test_expect_success '--max-cruft-size sets appropriate repack options' '
+       GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size \
+               gc --cruft --max-cruft-size=1M &&
+       test_subcommand $cruft_max_size_opts --max-cruft-size=1048576 <trace2.txt
+'
+
+test_expect_success 'gc.maxCruftSize sets appropriate repack options' '
+       GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
+               git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft &&
+       test_subcommand $cruft_max_size_opts --max-cruft-size=2097152 <trace2.txt &&
+
+       GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
+               git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft \
+               --max-cruft-size=3M &&
+       test_subcommand $cruft_max_size_opts --max-cruft-size=3145728 <trace2.txt
+'
+
 run_and_wait_for_auto_gc () {
        # We read stdout from gc for the side effect of waiting until the
        # background gc process exits, closing its fd 9.  Furthermore, the
@@ -220,7 +372,7 @@ test_expect_success 'background auto gc does not run if gc.log is present and re
        test_config gc.autodetach true &&
        echo fleem >.git/gc.log &&
        git gc --auto 2>err &&
-       test_i18ngrep "^warning:" err &&
+       test_grep "^warning:" err &&
        test_config gc.logexpiry 5.days &&
        test-tool chmtime =-345600 .git/gc.log &&
        git gc --auto &&
index 10662456aeef411141e60245391c50dad2545bfe..4521508b83a6560e6d025dca1750838f083fdc89 100755 (executable)
@@ -28,6 +28,7 @@ test_description='check pruning of dependent objects'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # We care about reachability, so we do not want to use
@@ -100,7 +101,7 @@ do
        '
 
        test_expect_success "simultaneous gc ($title)" '
-               git gc --prune=12.hours.ago
+               git gc --no-cruft --prune=12.hours.ago
        '
 
        test_expect_success "finish writing out commit ($title)" '
@@ -130,7 +131,7 @@ do
        '
 
        test_expect_success "simultaneous gc ($title)" '
-               git gc --prune=12.hours.ago
+               git gc --no-cruft --prune=12.hours.ago
        '
 
        # tree should have been refreshed by write-tree
@@ -150,8 +151,8 @@ test_expect_success 'do not complain about existing broken links (commit)' '
        some message
        EOF
        commit=$(git hash-object -t commit -w broken-commit) &&
-       git gc -q 2>stderr &&
-       verbose git cat-file -e $commit &&
+       git gc --no-cruft -q 2>stderr &&
+       git cat-file -e $commit &&
        test_must_be_empty stderr
 '
 
@@ -160,7 +161,7 @@ test_expect_success 'do not complain about existing broken links (tree)' '
        100644 blob $(test_oid 003)     foo
        EOF
        tree=$(git mktree --missing <broken-tree) &&
-       git gc -q 2>stderr &&
+       git gc --no-cruft -q 2>stderr &&
        git cat-file -e $tree &&
        test_must_be_empty stderr
 '
@@ -175,7 +176,7 @@ test_expect_success 'do not complain about existing broken links (tag)' '
        this is a broken tag
        EOF
        tag=$(git hash-object -t tag -w broken-tag) &&
-       git gc -q 2>stderr &&
+       git gc --no-cruft -q 2>stderr &&
        git cat-file -e $tag &&
        test_must_be_empty stderr
 '
index 338a9c46a24b02caf471da4afb1cff14a75b6f2f..b330945f497840bc723c7dba4ea237babf09b0d4 100755 (executable)
@@ -443,4 +443,173 @@ test_expect_success 'get_reachable_subset:none' '
        test_all_modes get_reachable_subset
 '
 
+test_expect_success 'for-each-ref ahead-behind:linear' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-1-3
+       refs/heads/commit-1-5
+       refs/heads/commit-1-8
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-1-1 0 8
+       refs/heads/commit-1-3 0 6
+       refs/heads/commit-1-5 0 4
+       refs/heads/commit-1-8 0 1
+       EOF
+       run_all_modes git for-each-ref \
+               --format="%(refname) %(ahead-behind:commit-1-9)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:all' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-2-4
+       refs/heads/commit-4-2
+       refs/heads/commit-4-4
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-1-1 0 24
+       refs/heads/commit-2-4 0 17
+       refs/heads/commit-4-2 0 17
+       refs/heads/commit-4-4 0 9
+       EOF
+       run_all_modes git for-each-ref \
+               --format="%(refname) %(ahead-behind:commit-5-5)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-5-3
+       refs/heads/commit-4-8
+       refs/heads/commit-9-9
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-1-1 0 53
+       refs/heads/commit-4-8 8 30
+       refs/heads/commit-5-3 0 39
+       refs/heads/commit-9-9 27 0
+       EOF
+       run_all_modes git for-each-ref \
+               --format="%(refname) %(ahead-behind:commit-9-6)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some, multibase' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-5-3
+       refs/heads/commit-7-8
+       refs/heads/commit-4-8
+       refs/heads/commit-9-9
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-1-1 0 53 0 53
+       refs/heads/commit-4-8 8 30 0 22
+       refs/heads/commit-5-3 0 39 0 39
+       refs/heads/commit-7-8 14 12 8 6
+       refs/heads/commit-9-9 27 0 27 0
+       EOF
+       run_all_modes git for-each-ref \
+               --format="%(refname) %(ahead-behind:commit-9-6) %(ahead-behind:commit-6-9)" \
+               --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:none' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-7-5
+       refs/heads/commit-4-8
+       refs/heads/commit-9-9
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-4-8 16 16
+       refs/heads/commit-7-5 7 4
+       refs/heads/commit-9-9 49 0
+       EOF
+       run_all_modes git for-each-ref \
+               --format="%(refname) %(ahead-behind:commit-8-4)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:linear' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-1-3
+       refs/heads/commit-1-5
+       refs/heads/commit-1-8
+       refs/heads/commit-2-1
+       refs/heads/commit-5-1
+       refs/heads/commit-9-1
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-1-3
+       refs/heads/commit-1-5
+       refs/heads/commit-1-8
+       EOF
+       run_all_modes git for-each-ref --merged=commit-1-9 \
+               --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:all' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-2-4
+       refs/heads/commit-4-2
+       refs/heads/commit-4-4
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-2-4
+       refs/heads/commit-4-2
+       refs/heads/commit-4-4
+       EOF
+       run_all_modes git for-each-ref --merged=commit-5-5 \
+               --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-5-3
+       refs/heads/commit-4-8
+       refs/heads/commit-9-9
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-5-3
+       EOF
+       run_all_modes git for-each-ref --merged=commit-9-6 \
+               --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:some, multibase' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-5-3
+       refs/heads/commit-7-8
+       refs/heads/commit-4-8
+       refs/heads/commit-9-9
+       EOF
+       cat >expect <<-\EOF &&
+       refs/heads/commit-1-1
+       refs/heads/commit-4-8
+       refs/heads/commit-5-3
+       EOF
+       run_all_modes git for-each-ref \
+               --merged=commit-5-8 \
+               --merged=commit-8-5 \
+               --format="%(refname)" \
+               --stdin
+'
+
+test_expect_success 'for-each-ref merged:none' '
+       cat >input <<-\EOF &&
+       refs/heads/commit-7-5
+       refs/heads/commit-4-8
+       refs/heads/commit-9-9
+       EOF
+       >expect &&
+       run_all_modes git for-each-ref --merged=commit-8-4 \
+               --format="%(refname)" --stdin
+'
+
 test_done
diff --git a/t/t6700-tree-depth.sh b/t/t6700-tree-depth.sh
new file mode 100755 (executable)
index 0000000..9e70a7c
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='handling of deep trees in various commands'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# We'll test against two depths here: a small one that will let us check the
+# behavior of the config setting easily, and a large one that should be
+# forbidden by default. Testing the default depth will let us know whether our
+# default is enough to prevent segfaults on systems that run the tests.
+small_depth=50
+big_depth=4100
+
+small_ok="-c core.maxtreedepth=$small_depth"
+small_no="-c core.maxtreedepth=$((small_depth-1))"
+
+# usage: mkdeep <name> <depth>
+#   Create a tag <name> containing a file whose path has depth <depth>.
+#
+# We'll use fast-import here for two reasons:
+#
+#   1. It's faster than creating $big_depth tree objects.
+#
+#   2. As we tighten tree limits, it's more likely to allow large sizes
+#      than trying to stuff a deep path into the index.
+mkdeep () {
+       {
+               echo "commit refs/tags/$1" &&
+               echo "committer foo <foo@example.com> 1234 -0000" &&
+               echo "data <<EOF" &&
+               echo "the commit message" &&
+               echo "EOF" &&
+
+               printf 'M 100644 inline ' &&
+               i=0 &&
+               while test $i -lt $2
+               do
+                       printf 'a/'
+                       i=$((i+1))
+               done &&
+               echo "file" &&
+
+               echo "data <<EOF" &&
+               echo "the file contents" &&
+               echo "EOF" &&
+               echo
+       } | git fast-import
+}
+
+test_expect_success 'create small tree' '
+       mkdeep small $small_depth
+'
+
+test_expect_success 'create big tree' '
+       mkdeep big $big_depth
+'
+
+test_expect_success 'limit recursion of git-archive' '
+       git $small_ok archive small >/dev/null &&
+       test_must_fail git $small_no archive small >/dev/null
+'
+
+test_expect_success 'default limit for git-archive fails gracefully' '
+       test_must_fail git archive big >/dev/null
+'
+
+test_expect_success 'limit recursion of ls-tree -r' '
+       git $small_ok ls-tree -r small &&
+       test_must_fail git $small_no ls-tree -r small
+'
+
+test_expect_success 'default limit for ls-tree fails gracefully' '
+       test_must_fail git ls-tree -r big >/dev/null
+'
+
+test_expect_success 'limit recursion of rev-list --objects' '
+       git $small_ok rev-list --objects small >/dev/null &&
+       test_must_fail git $small_no rev-list --objects small >/dev/null
+'
+
+test_expect_success 'default limit for rev-list fails gracefully' '
+       test_must_fail git rev-list --objects big >/dev/null
+'
+
+test_expect_success 'limit recursion of diff-tree -r' '
+       git $small_ok diff-tree -r $EMPTY_TREE small &&
+       test_must_fail git $small_no diff-tree -r $EMPTY_TREE small
+'
+
+test_expect_success 'default limit for diff-tree fails gracefully' '
+       test_must_fail git diff-tree -r $EMPTY_TREE big
+'
+
+test_done
index 8c37bceb336344f9b10878faf9cabf933dcb8ea2..879a6dce601110ac4bf8c59a51b32654e8726dcf 100755 (executable)
@@ -4,6 +4,10 @@ test_description='git mv in subdirs'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff-data.sh
 
+index_at_path () {
+       git ls-files --format='%(objectmode) %(objectname) %(stage)' "$@"
+}
+
 test_expect_success 'mv -f refreshes updated index entry' '
        echo test >bar &&
        git add bar &&
@@ -60,8 +64,8 @@ test_expect_success 'checking the commit' '
 
 test_expect_success 'mv --dry-run does not move file' '
        git mv -n path0/COPYING MOVED &&
-       test -f path0/COPYING &&
-       test ! -f MOVED
+       test_path_is_file path0/COPYING &&
+       test_path_is_missing MOVED
 '
 
 test_expect_success 'checking -k on non-existing file' '
@@ -71,25 +75,25 @@ test_expect_success 'checking -k on non-existing file' '
 test_expect_success 'checking -k on untracked file' '
        >untracked1 &&
        git mv -k untracked1 path0 &&
-       test -f untracked1 &&
-       test ! -f path0/untracked1
+       test_path_is_file untracked1 &&
+       test_path_is_missing path0/untracked1
 '
 
 test_expect_success 'checking -k on multiple untracked files' '
        >untracked2 &&
        git mv -k untracked1 untracked2 path0 &&
-       test -f untracked1 &&
-       test -f untracked2 &&
-       test ! -f path0/untracked1 &&
-       test ! -f path0/untracked2
+       test_path_is_file untracked1 &&
+       test_path_is_file untracked2 &&
+       test_path_is_missing path0/untracked1 &&
+       test_path_is_missing path0/untracked2
 '
 
 test_expect_success 'checking -f on untracked file with existing target' '
        >path0/untracked1 &&
        test_must_fail git mv -f untracked1 path0 &&
-       test ! -f .git/index.lock &&
-       test -f untracked1 &&
-       test -f path0/untracked1
+       test_path_is_missing .git/index.lock &&
+       test_path_is_file untracked1 &&
+       test_path_is_file path0/untracked1
 '
 
 # clean up the mess in case bad things happen
@@ -170,6 +174,13 @@ test_expect_success 'do not move directory over existing directory' '
        test_must_fail git mv path2 path0
 '
 
+test_expect_success 'rename directory to non-existing directory' '
+       mkdir dir-a &&
+       >dir-a/f &&
+       git add dir-a &&
+       git mv dir-a non-existing-dir
+'
+
 test_expect_success 'move into "."' '
        git mv path1/path2/ .
 '
@@ -187,7 +198,8 @@ test_expect_success "Michael Cassar's test case" '
        git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
 
        T=$(git write-tree) &&
-       git ls-tree -r $T | verbose grep partA/outline.txt
+       git ls-tree -r $T >out &&
+       grep partA/outline.txt out
 '
 
 rm -fr papers partA path?
@@ -215,8 +227,8 @@ test_expect_success 'absolute pathname' '
                git add sub/file &&
 
                git mv sub "$(pwd)/in" &&
-               ! test -d sub &&
-               test -d in &&
+               test_path_is_missing sub &&
+               test_path_is_dir in &&
                git ls-files --error-unmatch in/file
        )
 '
@@ -234,8 +246,8 @@ test_expect_success 'absolute pathname outside should fail' '
                git add sub/file &&
 
                test_must_fail git mv sub "$out/out" &&
-               test -d sub &&
-               ! test -d ../in &&
+               test_path_is_dir sub &&
+               test_path_is_missing ../in &&
                git ls-files --error-unmatch sub/file
        )
 '
@@ -260,12 +272,12 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
        git init &&
        echo 1 >dirty &&
        git add dirty &&
-       entry="$(git ls-files --stage dirty | cut -f 1)" &&
+       entry="$(index_at_path dirty)" &&
        git mv dirty dirty2 &&
-       test "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" &&
+       test "$entry" = "$(index_at_path dirty2)" &&
        echo 2 >dirty2 &&
        git mv dirty2 dirty &&
-       test "$entry" = "$(git ls-files --stage dirty | cut -f 1)"
+       test "$entry" = "$(index_at_path dirty)"
 '
 
 rm -f dirty dirty2
@@ -284,7 +296,7 @@ test_expect_success 'git mv error on conflicted file' '
        EOF
 
        test_must_fail git mv conflict newname 2>actual &&
-       test_i18ngrep "conflicted" actual
+       test_grep "conflicted" actual
 '
 
 test_expect_success 'git mv should overwrite symlink to a file' '
@@ -295,8 +307,8 @@ test_expect_success 'git mv should overwrite symlink to a file' '
        git add moved &&
        test_must_fail git mv moved symlink &&
        git mv -f moved symlink &&
-       ! test -e moved &&
-       test -f symlink &&
+       test_path_is_missing moved &&
+       test_path_is_file symlink &&
        test "$(cat symlink)" = 1 &&
        git update-index --refresh &&
        git diff-files --quiet
@@ -312,13 +324,13 @@ test_expect_success 'git mv should overwrite file with a symlink' '
        git add moved &&
        test_must_fail git mv symlink moved &&
        git mv -f symlink moved &&
-       ! test -e symlink &&
+       test_path_is_missing symlink &&
        git update-index --refresh &&
        git diff-files --quiet
 '
 
 test_expect_success SYMLINKS 'check moved symlink' '
-       test -h moved
+       test_path_is_symlink moved
 '
 
 rm -f moved symlink
@@ -342,7 +354,7 @@ test_expect_success 'git mv cannot move a submodule in a file' '
 '
 
 test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
-       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       entry="$(index_at_path sub)" &&
        git rm .gitmodules &&
        (
                cd sub &&
@@ -352,8 +364,8 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm
        ) &&
        mkdir mod &&
        git mv sub mod/sub &&
-       ! test -e sub &&
-       test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+       test_path_is_missing sub &&
+       test "$entry" = "$(index_at_path mod/sub)" &&
        git -C mod/sub status &&
        git update-index --refresh &&
        git diff-files --quiet
@@ -363,7 +375,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
        rm -rf mod &&
        git reset --hard &&
        git submodule update &&
-       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       entry="$(index_at_path sub)" &&
        (
                cd sub &&
                rm -f .git &&
@@ -372,8 +384,8 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
        ) &&
        mkdir mod &&
        git mv sub mod/sub &&
-       ! test -e sub &&
-       test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+       test_path_is_missing sub &&
+       test "$entry" = "$(index_at_path mod/sub)" &&
        git -C mod/sub status &&
        echo mod/sub >expected &&
        git config -f .gitmodules submodule.sub.path >actual &&
@@ -386,11 +398,11 @@ test_expect_success 'git mv moves a submodule with gitfile' '
        rm -rf mod &&
        git reset --hard &&
        git submodule update &&
-       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       entry="$(index_at_path sub)" &&
        mkdir mod &&
        git -C mod mv ../sub/ . &&
-       ! test -e sub &&
-       test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+       test_path_is_missing sub &&
+       test "$entry" = "$(index_at_path mod/sub)" &&
        git -C mod/sub status &&
        echo mod/sub >expected &&
        git config -f .gitmodules submodule.sub.path >actual &&
@@ -404,12 +416,12 @@ test_expect_success 'mv does not complain when no .gitmodules file is found' '
        git reset --hard &&
        git submodule update &&
        git rm .gitmodules &&
-       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       entry="$(index_at_path sub)" &&
        mkdir mod &&
        git mv sub mod/sub 2>actual.err &&
        test_must_be_empty actual.err &&
-       ! test -e sub &&
-       test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+       test_path_is_missing sub &&
+       test "$entry" = "$(index_at_path mod/sub)" &&
        git -C mod/sub status &&
        git update-index --refresh &&
        git diff-files --quiet
@@ -420,17 +432,17 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta
        git reset --hard &&
        git submodule update &&
        git config -f .gitmodules foo.bar true &&
-       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       entry="$(index_at_path sub)" &&
        mkdir mod &&
        test_must_fail git mv sub mod/sub 2>actual.err &&
-       test -s actual.err &&
-       test -e sub &&
+       test_file_not_empty actual.err &&
+       test_path_exists sub &&
        git diff-files --quiet -- sub &&
        git add .gitmodules &&
        git mv sub mod/sub 2>actual.err &&
        test_must_be_empty actual.err &&
-       ! test -e sub &&
-       test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+       test_path_is_missing sub &&
+       test "$entry" = "$(index_at_path mod/sub)" &&
        git -C mod/sub status &&
        git update-index --refresh &&
        git diff-files --quiet
@@ -442,13 +454,13 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule
        git submodule update &&
        git config -f .gitmodules --remove-section submodule.sub &&
        git add .gitmodules &&
-       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       entry="$(index_at_path sub)" &&
        echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
        mkdir mod &&
        git mv sub mod/sub 2>actual.err &&
        test_cmp expect.err actual.err &&
-       ! test -e sub &&
-       test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+       test_path_is_missing sub &&
+       test "$entry" = "$(index_at_path mod/sub)" &&
        git -C mod/sub status &&
        git update-index --refresh &&
        git diff-files --quiet
@@ -460,7 +472,7 @@ test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
        git submodule update &&
        mkdir mod &&
        git mv -n sub mod/sub 2>actual.err &&
-       test -f sub/.git &&
+       test_path_is_file sub/.git &&
        git diff-index --exit-code HEAD &&
        git update-index --refresh &&
        git diff-files --quiet -- sub .gitmodules
@@ -470,14 +482,14 @@ test_expect_success 'checking out a commit before submodule moved needs manual u
        git mv sub sub2 &&
        git commit -m "moved sub to sub2" &&
        git checkout -q HEAD^ 2>actual &&
-       test_i18ngrep "^warning: unable to rmdir '\''sub2'\'':" actual &&
+       test_grep "^warning: unable to rmdir '\''sub2'\'':" actual &&
        git status -s sub2 >actual &&
        echo "?? sub2/" >expected &&
        test_cmp expected actual &&
-       ! test -f sub/.git &&
-       test -f sub2/.git &&
+       test_path_is_missing sub/.git &&
+       test_path_is_file sub2/.git &&
        git submodule update &&
-       test -f sub/.git &&
+       test_path_is_file sub/.git &&
        rm -rf sub2 &&
        git diff-index --exit-code HEAD &&
        git update-index --refresh &&
index e18a218952385984944eaaab369af051873d0363..f6aebe92ff9d94b20670f7436c57f8107bb3404a 100755 (executable)
@@ -49,7 +49,7 @@ test_expect_success 'result is really identical' '
 test_expect_success 'rewrite bare repository identically' '
        (git config core.bare true && cd .git &&
         git filter-branch branch > filter-output 2>&1 &&
-       ! fgrep fatal filter-output)
+       ! grep fatal filter-output)
 '
 git config core.bare false
 test_expect_success 'result is really identical' '
@@ -506,7 +506,7 @@ test_expect_success 'rewrite repository including refs that point at non-commit
        git tag -a -m "tag to a tree" treetag $new_tree &&
        git reset --hard HEAD &&
        git filter-branch -f -- --all >filter-output 2>&1 &&
-       ! fgrep fatal filter-output
+       ! grep fatal filter-output
 '
 
 test_expect_success 'filter-branch handles ref deletion' '
index 9aa1660651b8a96397ce2a83cb3d107c8b7d43dc..b41a47eb943a03b1588bdc87802b0645944ce2ec 100755 (executable)
@@ -792,6 +792,34 @@ test_expect_success 'annotations for blobs are empty' '
        test_cmp expect actual
 '
 
+# Run this before doing any signing, so the test has the same results
+# regardless of the GPG prereq.
+test_expect_success 'git tag --format with ahead-behind' '
+       test_when_finished git reset --hard tag-one-line &&
+       git commit --allow-empty -m "left" &&
+       git tag -a -m left tag-left &&
+       git reset --hard HEAD~1 &&
+       git commit --allow-empty -m "right" &&
+       git tag -a -m left tag-right &&
+
+       # Use " !" at the end to demonstrate whitespace
+       # around empty ahead-behind token for tag-blob.
+       cat >expect <<-EOF &&
+       refs/tags/tag-blob  !
+       refs/tags/tag-left 1 1 !
+       refs/tags/tag-lines 0 1 !
+       refs/tags/tag-one-line 0 1 !
+       refs/tags/tag-right 0 0 !
+       refs/tags/tag-zero-lines 0 1 !
+       EOF
+       git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
+       grep "refs/tags/tag" actual >actual.focus &&
+       test_cmp expect actual.focus &&
+
+       # Error reported for tags that point to non-commits.
+       grep "error: object [0-9a-f]* is a blob, not a commit" err
+'
+
 # trying to verify annotated non-signed tags:
 
 test_expect_success GPG \
@@ -1834,6 +1862,51 @@ test_expect_success 'option override configured sort' '
        test_cmp expect actual
 '
 
+test_expect_success '--no-sort cancels config sort keys' '
+       test_config tag.sort "-refname" &&
+
+       # objecttype is identical for all of them, so sort falls back on
+       # default (ascending refname)
+       git tag -l \
+               --no-sort \
+               --sort="objecttype" \
+               "foo*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.3
+       foo1.6
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success '--no-sort cancels command line sort keys' '
+       # objecttype is identical for all of them, so sort falls back on
+       # default (ascending refname)
+       git tag -l \
+               --sort="-refname" \
+               --no-sort \
+               --sort="objecttype" \
+               "foo*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.3
+       foo1.6
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected tags' '
+       # Sort the results with `sort` for a consistent comparison against
+       # expected
+       git tag -l --no-sort "foo*" | sort >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.3
+       foo1.6
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success 'invalid sort parameter on command line' '
        test_must_fail git tag -l --sort=notvalid "foo*" >actual
 '
@@ -1843,6 +1916,23 @@ test_expect_success 'invalid sort parameter in configuratoin' '
        test_must_fail git tag -l "foo*"
 '
 
+test_expect_success 'version sort handles empty value for versionsort.{prereleaseSuffix,suffix}' '
+       cp .git/config .git/config.orig &&
+       test_when_finished mv .git/config.orig .git/config &&
+
+       cat >>.git/config <<-\EOF &&
+       [versionsort]
+               prereleaseSuffix
+               suffix
+       EOF
+       cat >expect <<-\EOF &&
+       error: missing value for '\''versionsort.suffix'\''
+       error: missing value for '\''versionsort.prereleasesuffix'\''
+       EOF
+       git tag -l --sort=version:refname 2>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'version sort with prerelease reordering' '
        test_config versionsort.prereleaseSuffix -rc &&
        git tag foo1.6-rc1 &&
@@ -2001,6 +2091,22 @@ test_expect_success '--format should list tags as per format given' '
        test_cmp expect actual
 '
 
+test_expect_success '--format --omit-empty works' '
+       cat >expect <<-\EOF &&
+       refname : refs/tags/v1.0
+
+       refname : refs/tags/v1.1.3
+       EOF
+       git tag -l --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+       test_cmp expect actual &&
+       cat >expect <<-\EOF &&
+       refname : refs/tags/v1.0
+       refname : refs/tags/v1.1.3
+       EOF
+       git tag -l --omit-empty --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git tag -l with --format="%(rest)" must fail' '
        test_must_fail git tag -l --format="%(rest)" "v1*"
 '
@@ -2127,4 +2233,23 @@ test_expect_success 'Does --[no-]contains stop at commits? Yes!' '
        test_cmp expected actual
 '
 
+test_expect_success 'If tag is created then tag message file is unlinked' '
+       test_when_finished "git tag -d foo" &&
+       write_script fakeeditor <<-\EOF &&
+       echo Message >.git/TAG_EDITMSG
+       EOF
+       GIT_EDITOR=./fakeeditor git tag -a foo &&
+       test_path_is_missing .git/TAG_EDITMSG
+'
+
+test_expect_success 'If tag cannot be created then tag message file is not unlinked' '
+       test_when_finished "git tag -d foo/bar && rm .git/TAG_EDITMSG" &&
+       write_script fakeeditor <<-\EOF &&
+       echo Message >.git/TAG_EDITMSG
+       EOF
+       git tag foo/bar &&
+       test_must_fail env GIT_EDITOR=./fakeeditor git tag -a foo &&
+       test_path_exists .git/TAG_EDITMSG
+'
+
 test_done
index 10faa645157ea4521e370abdfd470c84a24f6051..6f526c37c2776e6288a1abe5860b6a9efed25183 100755 (executable)
@@ -115,7 +115,7 @@ test_expect_success GPGSM 'verify and show signatures x509 with high minTrustLev
 
 test_expect_success GPG 'detect fudged signature' '
        git cat-file tag seventh-signed >raw &&
-       sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
+       sed -e "/^tag / s/seventh/7th-forged/" raw >forged1 &&
        git hash-object -w -t tag forged1 >forged1.tag &&
        test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
        grep "BAD signature from" actual1 &&
index 1cb36b9ab83cdba01a554a94c982c968cd572f8b..20913b371344264a6dc9c84b58c3b6de3b46e226 100755 (executable)
@@ -125,7 +125,7 @@ test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag failes with tag date ou
 test_expect_success GPGSSH 'detect fudged ssh signature' '
        test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
        git cat-file tag seventh-signed >raw &&
-       sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
+       sed -e "/^tag / s/seventh/7th-forged/" raw >forged1 &&
        git hash-object -w -t tag forged1 >forged1.tag &&
        test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
        grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
@@ -200,4 +200,14 @@ test_expect_success GPGSSH 'verifying a forged tag with --format should fail sil
        test_must_be_empty actual-forged
 '
 
+test_expect_success GPGSSH 'rev-list --format=%G' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git rev-list -1 --format="%G? %H" sixth-signed >actual &&
+       cat >expect <<-EOF &&
+       commit $(git rev-parse sixth-signed^0)
+       G $(git rev-parse sixth-signed^0)
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 638bb04e217600387fdf3f46fb8ba1caacba0f94..89cf98b30c8f96335180bccfe7fe07b7156febc9 100755 (executable)
@@ -10,57 +10,57 @@ TEST_PASSES_SANITIZE_LEAK=true
 . "$TEST_DIRECTORY"/lib-diff-data.sh
 
 test_expect_success 'creating initial files' '
-     mkdir path0 &&
-     COPYING_test_data >path0/COPYING &&
-     git add path0/COPYING &&
-     git commit -m add -a
+       mkdir path0 &&
+       COPYING_test_data >path0/COPYING &&
+       git add path0/COPYING &&
+       git commit -m add -a
 '
 
 test_expect_success 'creating second files' '
-     mkdir path1 &&
-     mkdir path1/path2 &&
-     COPYING_test_data >path1/path2/COPYING &&
-     COPYING_test_data >path1/COPYING &&
-     COPYING_test_data >COPYING &&
-     COPYING_test_data >path0/COPYING-TOO &&
-     git add path1/path2/COPYING &&
-     git add path1/COPYING &&
-     git add COPYING &&
-     git add path0/COPYING-TOO &&
-     git commit -m change -a
+       mkdir path1 &&
+       mkdir path1/path2 &&
+       COPYING_test_data >path1/path2/COPYING &&
+       COPYING_test_data >path1/COPYING &&
+       COPYING_test_data >COPYING &&
+       COPYING_test_data >path0/COPYING-TOO &&
+       git add path1/path2/COPYING &&
+       git add path1/COPYING &&
+       git add COPYING &&
+       git add path0/COPYING-TOO &&
+       git commit -m change -a
 '
 
 test_expect_success 'resetting tree HEAD^' '
-     git reset --hard HEAD^
+       git reset --hard HEAD^
 '
 
 test_expect_success 'checking initial files exist after rewind' '
-     test -d path0 &&
-     test -f path0/COPYING
+       test -d path0 &&
+       test -f path0/COPYING
 '
 
 test_expect_success 'checking lack of path1/path2/COPYING' '
-    ! test -f path1/path2/COPYING
+       ! test -f path1/path2/COPYING
 '
 
 test_expect_success 'checking lack of path1/COPYING' '
-    ! test -f path1/COPYING
+       ! test -f path1/COPYING
 '
 
 test_expect_success 'checking lack of COPYING' '
-     ! test -f COPYING
+       ! test -f COPYING
 '
 
 test_expect_success 'checking checking lack of path1/COPYING-TOO' '
-     ! test -f path0/COPYING-TOO
+       ! test -f path0/COPYING-TOO
 '
 
 test_expect_success 'checking lack of path1/path2' '
-     ! test -d path1/path2
+       ! test -d path1/path2
 '
 
 test_expect_success 'checking lack of path1' '
-     ! test -d path1
+       ! test -d path1
 '
 
 test_done
index 22477f3a312e4a44b0c3f99ee356d29487ed5399..62d9f846ce86c54745eb87619d14fefc005d6fe3 100755 (executable)
@@ -71,6 +71,16 @@ check_changes () {
        done | test_cmp .cat_expect -
 }
 
+# no negated form for various type of resets
+for opt in soft mixed hard merge keep
+do
+       test_expect_success "no 'git reset --no-$opt'" '
+               test_when_finished "rm -f err" &&
+               test_must_fail git reset --no-$opt 2>err &&
+               grep "error: unknown option .no-$opt." err
+       '
+done
+
 test_expect_success 'reset --hard message' '
        hex=$(git log -1 --format="%h") &&
        git reset --hard >.actual &&
@@ -606,4 +616,12 @@ test_expect_success 'reset --mixed sets up work tree' '
        test_must_be_empty actual
 '
 
+test_expect_success 'reset handles --end-of-options' '
+       git update-ref refs/heads/--foo HEAD^ &&
+       git log -1 --format=%s refs/heads/--foo >expect &&
+       git reset --hard --end-of-options --foo &&
+       git log -1 --format=%s HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_done
index a60153f9f32318c5ee31d2b16ec506a7a59ddd6d..18bbd9975ebfda330d0727c600bd0cec4b8b5181 100755 (executable)
@@ -63,7 +63,7 @@ test_expect_success '"mixed" reset is not allowed in bare' '
        test_must_fail git reset --mixed HEAD^
 '
 
-test_expect_success !SANITIZE_LEAK '"soft" reset is allowed in bare' '
+test_expect_success '"soft" reset is allowed in bare' '
        git reset --soft HEAD^ &&
        git show --pretty=format:%s >out &&
        echo one >expect &&
index fc2a6cf5c7a49a7971e23213d4d324799c2f92c9..05079c7246482cb5a50bf1579914ef810e309cd7 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git reset --patch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-patch-mode.sh
 
 test_expect_success PERL 'setup' '
@@ -28,21 +30,21 @@ test_expect_success PERL 'git reset -p' '
        test_write_lines n y | git reset -p >output &&
        verify_state dir/foo work head &&
        verify_saved_state bar &&
-       test_i18ngrep "Unstage" output
+       test_grep "Unstage" output
 '
 
 test_expect_success PERL 'git reset -p HEAD^' '
        test_write_lines n y | git reset -p HEAD^ >output &&
        verify_state dir/foo work parent &&
        verify_saved_state bar &&
-       test_i18ngrep "Apply" output
+       test_grep "Apply" output
 '
 
 test_expect_success PERL 'git reset -p HEAD^^{tree}' '
        test_write_lines n y | git reset -p HEAD^^{tree} >output &&
        verify_state dir/foo work parent &&
        verify_saved_state bar &&
-       test_i18ngrep "Apply" output
+       test_grep "Apply" output
 '
 
 test_expect_success PERL 'git reset -p HEAD^:dir/foo (blob fails)' '
index ecb85c3b823275efc68fb64643a4304a3cbf28b2..d20e5709f91cd7b1a0107bf4ee1df6809001bd30 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git reset should work on unborn branch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -40,7 +42,7 @@ test_expect_success PERL 'reset -p' '
 
        git ls-files >actual &&
        test_must_be_empty actual &&
-       test_i18ngrep "Unstage" output
+       test_grep "Unstage" output
 '
 
 test_expect_success 'reset --soft is a no-op' '
index 523efbecde1dff517ffebf3c04cfcf74ea280f15..020db201d57c3c38e75a0e2b9a38c2b3455a9309 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='reset --pathspec-from-file'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_tick
@@ -160,19 +161,19 @@ test_expect_success 'error conditions' '
        git rm fileA.t &&
 
        test_must_fail git reset --pathspec-from-file=list --patch 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
        test_must_fail git reset --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+       test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git reset --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+       test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        test_must_fail git reset --soft --pathspec-from-file=list 2>err &&
-       test_i18ngrep -e "fatal: Cannot do soft reset with paths" err &&
+       test_grep -e "fatal: Cannot do soft reset with paths" err &&
 
        test_must_fail git reset --hard --pathspec-from-file=list 2>err &&
-       test_i18ngrep -e "fatal: Cannot do hard reset with paths" err
+       test_grep -e "fatal: Cannot do hard reset with paths" err
 '
 
 test_done
index eb881be95b615f53867dbe8f56c6b0f9cc7a9d3c..7ee180f81da4f9e0ae312f73a87ab9775ef18e1d 100755 (executable)
@@ -9,17 +9,17 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-    printf "line %d\n" 1 2 3 >file1 &&
-    cat file1 >file2 &&
-    git add file1 file2 &&
-    test_tick &&
-    git commit -m "Initial commit" &&
-    git tag initial &&
-    echo line 4 >>file1 &&
-    cat file1 >file2 &&
-    test_tick &&
-    git commit -m "add line 4 to file1" file1 &&
-    git tag second
+       printf "line %d\n" 1 2 3 >file1 &&
+       cat file1 >file2 &&
+       git add file1 file2 &&
+       test_tick &&
+       git commit -m "Initial commit" &&
+       git tag initial &&
+       echo line 4 >>file1 &&
+       cat file1 >file2 &&
+       test_tick &&
+       git commit -m "add line 4 to file1" file1 &&
+       git tag second
 '
 
 # The next test will test the following:
@@ -29,19 +29,19 @@ test_expect_success setup '
 # file1:     C       C     C    D     --merge  D       D     D
 # file2:     C       D     D    D     --merge  C       D     D
 test_expect_success 'reset --merge is ok with changes in file it does not touch' '
-    git reset --merge HEAD^ &&
-    ! grep 4 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff --cached)"
+       git reset --merge HEAD^ &&
+       ! grep 4 file1 &&
+       grep 4 file2 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+       test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --merge is ok when switching back' '
-    git reset --merge second &&
-    grep 4 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+       git reset --merge second &&
+       grep 4 file1 &&
+       grep 4 file2 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+       test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -51,21 +51,21 @@ test_expect_success 'reset --merge is ok when switching back' '
 # file1:     C       C     C    D     --keep   D       D     D
 # file2:     C       D     D    D     --keep   C       D     D
 test_expect_success 'reset --keep is ok with changes in file it does not touch' '
-    git reset --hard second &&
-    cat file1 >file2 &&
-    git reset --keep HEAD^ &&
-    ! grep 4 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff --cached)"
+       git reset --hard second &&
+       cat file1 >file2 &&
+       git reset --keep HEAD^ &&
+       ! grep 4 file1 &&
+       grep 4 file2 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+       test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --keep is ok when switching back' '
-    git reset --keep second &&
-    grep 4 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+       git reset --keep second &&
+       grep 4 file1 &&
+       grep 4 file2 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+       test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -75,28 +75,28 @@ test_expect_success 'reset --keep is ok when switching back' '
 # file1:     B       B     C    D     --merge  D       D     D
 # file2:     C       D     D    D     --merge  C       D     D
 test_expect_success 'reset --merge discards changes added to index (1)' '
-    git reset --hard second &&
-    cat file1 >file2 &&
-    echo "line 5" >> file1 &&
-    git add file1 &&
-    git reset --merge HEAD^ &&
-    ! grep 4 file1 &&
-    ! grep 5 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff --cached)"
+       git reset --hard second &&
+       cat file1 >file2 &&
+       echo "line 5" >> file1 &&
+       git add file1 &&
+       git reset --merge HEAD^ &&
+       ! grep 4 file1 &&
+       ! grep 5 file1 &&
+       grep 4 file2 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+       test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --merge is ok again when switching back (1)' '
-    git reset --hard initial &&
-    echo "line 5" >> file2 &&
-    git add file2 &&
-    git reset --merge second &&
-    ! grep 4 file2 &&
-    ! grep 5 file1 &&
-    grep 4 file1 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+       git reset --hard initial &&
+       echo "line 5" >> file2 &&
+       git add file2 &&
+       git reset --merge second &&
+       ! grep 4 file2 &&
+       ! grep 5 file1 &&
+       grep 4 file1 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+       test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -105,10 +105,10 @@ test_expect_success 'reset --merge is ok again when switching back (1)' '
 #           ----------------------------------------------------
 # file1:     B       B     C    D     --keep   (disallowed)
 test_expect_success 'reset --keep fails with changes in index in files it touches' '
-    git reset --hard second &&
-    echo "line 5" >> file1 &&
-    git add file1 &&
-    test_must_fail git reset --keep HEAD^
+       git reset --hard second &&
+       echo "line 5" >> file1 &&
+       git add file1 &&
+       test_must_fail git reset --keep HEAD^
 '
 
 # The next test will test the following:
@@ -118,23 +118,23 @@ test_expect_success 'reset --keep fails with changes in index in files it touche
 # file1:     C       C     C    D     --merge  D       D     D
 # file2:     C       C     D    D     --merge  D       D     D
 test_expect_success 'reset --merge discards changes added to index (2)' '
-    git reset --hard second &&
-    echo "line 4" >> file2 &&
-    git add file2 &&
-    git reset --merge HEAD^ &&
-    ! grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff)" &&
-    test -z "$(git diff --cached)"
+       git reset --hard second &&
+       echo "line 4" >> file2 &&
+       git add file2 &&
+       git reset --merge HEAD^ &&
+       ! grep 4 file2 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+       test -z "$(git diff)" &&
+       test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --merge is ok again when switching back (2)' '
-    git reset --hard initial &&
-    git reset --merge second &&
-    ! grep 4 file2 &&
-    grep 4 file1 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+       git reset --hard initial &&
+       git reset --merge second &&
+       ! grep 4 file2 &&
+       grep 4 file1 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+       test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -144,21 +144,21 @@ test_expect_success 'reset --merge is ok again when switching back (2)' '
 # file1:     C       C     C    D     --keep   D       D     D
 # file2:     C       C     D    D     --keep   C       D     D
 test_expect_success 'reset --keep keeps changes it does not touch' '
-    git reset --hard second &&
-    echo "line 4" >> file2 &&
-    git add file2 &&
-    git reset --keep HEAD^ &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff --cached)"
+       git reset --hard second &&
+       echo "line 4" >> file2 &&
+       git add file2 &&
+       git reset --keep HEAD^ &&
+       grep 4 file2 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+       test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --keep keeps changes when switching back' '
-    git reset --keep second &&
-    grep 4 file2 &&
-    grep 4 file1 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+       git reset --keep second &&
+       grep 4 file2 &&
+       grep 4 file1 &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+       test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -167,14 +167,14 @@ test_expect_success 'reset --keep keeps changes when switching back' '
 #           ----------------------------------------------------
 # file1:     A       B     B    C     --merge  (disallowed)
 test_expect_success 'reset --merge fails with changes in file it touches' '
-    git reset --hard second &&
-    echo "line 5" >> file1 &&
-    test_tick &&
-    git commit -m "add line 5" file1 &&
-    sed -e "s/line 1/changed line 1/" <file1 >file3 &&
-    mv file3 file1 &&
-    test_must_fail git reset --merge HEAD^ 2>err.log &&
-    grep file1 err.log | grep "not uptodate"
+       git reset --hard second &&
+       echo "line 5" >> file1 &&
+       test_tick &&
+       git commit -m "add line 5" file1 &&
+       sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+       mv file3 file1 &&
+       test_must_fail git reset --merge HEAD^ 2>err.log &&
+       grep file1 err.log | grep "not uptodate"
 '
 
 # The next test will test the following:
@@ -183,36 +183,36 @@ test_expect_success 'reset --merge fails with changes in file it touches' '
 #           ----------------------------------------------------
 # file1:     A       B     B    C     --keep   (disallowed)
 test_expect_success 'reset --keep fails with changes in file it touches' '
-    git reset --hard second &&
-    echo "line 5" >> file1 &&
-    test_tick &&
-    git commit -m "add line 5" file1 &&
-    sed -e "s/line 1/changed line 1/" <file1 >file3 &&
-    mv file3 file1 &&
-    test_must_fail git reset --keep HEAD^ 2>err.log &&
-    grep file1 err.log | grep "not uptodate"
+       git reset --hard second &&
+       echo "line 5" >> file1 &&
+       test_tick &&
+       git commit -m "add line 5" file1 &&
+       sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+       mv file3 file1 &&
+       test_must_fail git reset --keep HEAD^ 2>err.log &&
+       grep file1 err.log | grep "not uptodate"
 '
 
 test_expect_success 'setup 3 different branches' '
-    git reset --hard second &&
-    git branch branch1 &&
-    git branch branch2 &&
-    git branch branch3 &&
-    git checkout branch1 &&
-    echo "line 5 in branch1" >> file1 &&
-    test_tick &&
-    git commit -a -m "change in branch1" &&
-    git checkout branch2 &&
-    echo "line 5 in branch2" >> file1 &&
-    test_tick &&
-    git commit -a -m "change in branch2" &&
-    git tag third &&
-    git checkout branch3 &&
-    echo a new file >file3 &&
-    rm -f file1 &&
-    git add file3 &&
-    test_tick &&
-    git commit -a -m "change in branch3"
+       git reset --hard second &&
+       git branch branch1 &&
+       git branch branch2 &&
+       git branch branch3 &&
+       git checkout branch1 &&
+       echo "line 5 in branch1" >> file1 &&
+       test_tick &&
+       git commit -a -m "change in branch1" &&
+       git checkout branch2 &&
+       echo "line 5 in branch2" >> file1 &&
+       test_tick &&
+       git commit -a -m "change in branch2" &&
+       git tag third &&
+       git checkout branch3 &&
+       echo a new file >file3 &&
+       rm -f file1 &&
+       git add file3 &&
+       test_tick &&
+       git commit -a -m "change in branch3"
 '
 
 # The next test will test the following:
@@ -221,12 +221,12 @@ test_expect_success 'setup 3 different branches' '
 #           ----------------------------------------------------
 # file1:     X       U     B    C     --merge  C       C     C
 test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
-    git checkout third &&
-    test_must_fail git merge branch1 &&
-    git reset --merge HEAD^ &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)" &&
-    test -z "$(git diff)"
+       git checkout third &&
+       test_must_fail git merge branch1 &&
+       git reset --merge HEAD^ &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+       test -z "$(git diff --cached)" &&
+       test -z "$(git diff)"
 '
 
 # The next test will test the following:
@@ -235,10 +235,10 @@ test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
 #           ----------------------------------------------------
 # file1:     X       U     B    C     --keep   (disallowed)
 test_expect_success '"reset --keep HEAD^" fails with pending merge' '
-    git reset --hard third &&
-    test_must_fail git merge branch1 &&
-    test_must_fail git reset --keep HEAD^ 2>err.log &&
-    test_i18ngrep "middle of a merge" err.log
+       git reset --hard third &&
+       test_must_fail git merge branch1 &&
+       test_must_fail git reset --keep HEAD^ 2>err.log &&
+       test_grep "middle of a merge" err.log
 '
 
 # The next test will test the following:
@@ -247,12 +247,12 @@ test_expect_success '"reset --keep HEAD^" fails with pending merge' '
 #           ----------------------------------------------------
 # file1:     X       U     B    B     --merge  B       B     B
 test_expect_success '"reset --merge HEAD" is ok with pending merge' '
-    git reset --hard third &&
-    test_must_fail git merge branch1 &&
-    git reset --merge HEAD &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
-    test -z "$(git diff --cached)" &&
-    test -z "$(git diff)"
+       git reset --hard third &&
+       test_must_fail git merge branch1 &&
+       git reset --merge HEAD &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
+       test -z "$(git diff --cached)" &&
+       test -z "$(git diff)"
 '
 
 # The next test will test the following:
@@ -261,36 +261,36 @@ test_expect_success '"reset --merge HEAD" is ok with pending merge' '
 #           ----------------------------------------------------
 # file1:     X       U     B    B     --keep   (disallowed)
 test_expect_success '"reset --keep HEAD" fails with pending merge' '
-    git reset --hard third &&
-    test_must_fail git merge branch1 &&
-    test_must_fail git reset --keep HEAD 2>err.log &&
-    test_i18ngrep "middle of a merge" err.log
+       git reset --hard third &&
+       test_must_fail git merge branch1 &&
+       test_must_fail git reset --keep HEAD 2>err.log &&
+       test_grep "middle of a merge" err.log
 '
 
 test_expect_success '--merge is ok with added/deleted merge' '
-    git reset --hard third &&
-    rm -f file2 &&
-    test_must_fail git merge branch3 &&
-    ! test -f file2 &&
-    test -f file3 &&
-    git diff --exit-code file3 &&
-    git diff --exit-code branch3 file3 &&
-    git reset --merge HEAD &&
-    ! test -f file3 &&
-    ! test -f file2 &&
-    git diff --exit-code --cached
+       git reset --hard third &&
+       rm -f file2 &&
+       test_must_fail git merge branch3 &&
+       ! test -f file2 &&
+       test -f file3 &&
+       git diff --exit-code file3 &&
+       git diff --exit-code branch3 file3 &&
+       git reset --merge HEAD &&
+       ! test -f file3 &&
+       ! test -f file2 &&
+       git diff --exit-code --cached
 '
 
 test_expect_success '--keep fails with added/deleted merge' '
-    git reset --hard third &&
-    rm -f file2 &&
-    test_must_fail git merge branch3 &&
-    ! test -f file2 &&
-    test -f file3 &&
-    git diff --exit-code file3 &&
-    git diff --exit-code branch3 file3 &&
-    test_must_fail git reset --keep HEAD 2>err.log &&
-    test_i18ngrep "middle of a merge" err.log
+       git reset --hard third &&
+       rm -f file2 &&
+       test_must_fail git merge branch3 &&
+       ! test -f file2 &&
+       test -f file3 &&
+       git diff --exit-code file3 &&
+       git diff --exit-code branch3 file3 &&
+       test_must_fail git reset --keep HEAD 2>err.log &&
+       test_grep "middle of a merge" err.log
 '
 
 test_done
index 78f25c1c7ead9820ed647711e4749d7e9d444c52..01b7c3503ca882cd292f2eef252a977ac5dcc183 100755 (executable)
@@ -10,9 +10,9 @@ TEST_PASSES_SANITIZE_LEAK=true
 
 
 test_expect_success 'creating initial commits' '
-    test_commit E file1 &&
-    test_commit D file1 &&
-    test_commit C file1
+       test_commit E file1 &&
+       test_commit D file1 &&
+       test_commit C file1
 '
 
 while read W1 I1 H1 T opt W2 I2 H2
@@ -74,13 +74,13 @@ B C C C keep   B C C
 EOF
 
 test_expect_success 'setting up branches to test with unmerged entries' '
-    git reset --hard C &&
-    git branch branch1 &&
-    git branch branch2 &&
-    git checkout branch1 &&
-    test_commit B1 file1 &&
-    git checkout branch2 &&
-    test_commit B file1
+       git reset --hard C &&
+       git branch branch1 &&
+       git branch branch2 &&
+       git checkout branch1 &&
+       test_commit B1 file1 &&
+       git checkout branch2 &&
+       test_commit B file1
 '
 
 while read W1 I1 H1 T opt W2 I2 H2
index 61ad47b0c18d231c60327acda04348895947809c..10cc6c46051e95e8e8d52fae23396574f319b802 100755 (executable)
@@ -217,7 +217,7 @@ test_expect_success 'switch to another branch while carrying a deletion' '
        git rm two &&
 
        test_must_fail git checkout simple 2>errs &&
-       test_i18ngrep overwritten errs &&
+       test_grep overwritten errs &&
 
        test_must_fail git read-tree --quiet -m -u HEAD simple 2>errs &&
        test_must_be_empty errs
@@ -229,7 +229,7 @@ test_expect_success 'checkout to detach HEAD (with advice declined)' '
        git checkout -f renamer &&
        git clean -f &&
        git checkout renamer^ 2>messages &&
-       test_i18ngrep "HEAD is now at $rev" messages &&
+       test_grep "HEAD is now at $rev" messages &&
        test_line_count = 1 messages &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/main) &&
@@ -372,75 +372,75 @@ test_expect_success 'checkout specific path while in subdirectory' '
 '
 
 test_expect_success 'checkout w/--track sets up tracking' '
-    git config branch.autosetupmerge false &&
-    git checkout main &&
-    git checkout --track -b track1 &&
-    test "$(git config branch.track1.remote)" &&
-    test "$(git config branch.track1.merge)"
+       git config branch.autosetupmerge false &&
+       git checkout main &&
+       git checkout --track -b track1 &&
+       test "$(git config branch.track1.remote)" &&
+       test "$(git config branch.track1.merge)"
 '
 
 test_expect_success 'checkout w/autosetupmerge=always sets up tracking' '
-    test_when_finished git config branch.autosetupmerge false &&
-    git config branch.autosetupmerge always &&
-    git checkout main &&
-    git checkout -b track2 &&
-    test "$(git config branch.track2.remote)" &&
-    test "$(git config branch.track2.merge)"
+       test_when_finished git config branch.autosetupmerge false &&
+       git config branch.autosetupmerge always &&
+       git checkout main &&
+       git checkout -b track2 &&
+       test "$(git config branch.track2.remote)" &&
+       test "$(git config branch.track2.merge)"
 '
 
 test_expect_success 'checkout w/--track from non-branch HEAD fails' '
-    git checkout main^0 &&
-    test_must_fail git symbolic-ref HEAD &&
-    test_must_fail git checkout --track -b track &&
-    test_must_fail git rev-parse --verify track &&
-    test_must_fail git symbolic-ref HEAD &&
-    test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
+       git checkout main^0 &&
+       test_must_fail git symbolic-ref HEAD &&
+       test_must_fail git checkout --track -b track &&
+       test_must_fail git rev-parse --verify track &&
+       test_must_fail git symbolic-ref HEAD &&
+       test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
 '
 
 test_expect_success 'checkout w/--track from tag fails' '
-    git checkout main^0 &&
-    test_must_fail git symbolic-ref HEAD &&
-    test_must_fail git checkout --track -b track frotz &&
-    test_must_fail git rev-parse --verify track &&
-    test_must_fail git symbolic-ref HEAD &&
-    test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
+       git checkout main^0 &&
+       test_must_fail git symbolic-ref HEAD &&
+       test_must_fail git checkout --track -b track frotz &&
+       test_must_fail git rev-parse --verify track &&
+       test_must_fail git symbolic-ref HEAD &&
+       test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
 '
 
 test_expect_success 'detach a symbolic link HEAD' '
-    git checkout main &&
-    git config --bool core.prefersymlinkrefs yes &&
-    git checkout side &&
-    git checkout main &&
-    it=$(git symbolic-ref HEAD) &&
-    test "z$it" = zrefs/heads/main &&
-    here=$(git rev-parse --verify refs/heads/main) &&
-    git checkout side^ &&
-    test "z$(git rev-parse --verify refs/heads/main)" = "z$here"
+       git checkout main &&
+       git config --bool core.prefersymlinkrefs yes &&
+       git checkout side &&
+       git checkout main &&
+       it=$(git symbolic-ref HEAD) &&
+       test "z$it" = zrefs/heads/main &&
+       here=$(git rev-parse --verify refs/heads/main) &&
+       git checkout side^ &&
+       test "z$(git rev-parse --verify refs/heads/main)" = "z$here"
 '
 
 test_expect_success 'checkout with --track fakes a sensible -b <name>' '
-    git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
-    git update-ref refs/remotes/origin/koala/bear renamer &&
+       git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
+       git update-ref refs/remotes/origin/koala/bear renamer &&
 
-    git checkout --track origin/koala/bear &&
-    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+       git checkout --track origin/koala/bear &&
+       test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 
-    git checkout main && git branch -D koala/bear &&
+       git checkout main && git branch -D koala/bear &&
 
-    git checkout --track refs/remotes/origin/koala/bear &&
-    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+       git checkout --track refs/remotes/origin/koala/bear &&
+       test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 
-    git checkout main && git branch -D koala/bear &&
+       git checkout main && git branch -D koala/bear &&
 
-    git checkout --track remotes/origin/koala/bear &&
-    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
+       git checkout --track remotes/origin/koala/bear &&
+       test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
 '
 
 test_expect_success 'checkout with --track, but without -b, fails with too short tracked name' '
-    test_must_fail git checkout --track renamer
+       test_must_fail git checkout --track renamer
 '
 
 setup_conflicting_index () {
@@ -497,6 +497,11 @@ test_expect_success 'checkout unmerged stage' '
        test ztheirside = "z$(cat file)"
 '
 
+test_expect_success 'checkout path with --merge from tree-ish is a no-no' '
+       setup_conflicting_index &&
+       test_must_fail git checkout -m HEAD -- file
+'
+
 test_expect_success 'checkout with --merge' '
        setup_conflicting_index &&
        echo "none of the above" >sample &&
@@ -517,6 +522,48 @@ test_expect_success 'checkout with --merge' '
        test_cmp merged file
 '
 
+test_expect_success 'checkout -m works after (mistaken) resolution' '
+       setup_conflicting_index &&
+       echo "none of the above" >sample &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       # resolve to something
+       git add file &&
+       git checkout --merge -- fild file filf &&
+       {
+               echo "<<<<<<< ours" &&
+               echo ourside &&
+               echo "=======" &&
+               echo theirside &&
+               echo ">>>>>>> theirs"
+       } >merged &&
+       test_cmp expect fild &&
+       test_cmp expect filf &&
+       test_cmp merged file
+'
+
+test_expect_success 'checkout -m works after (mistaken) resolution to remove' '
+       setup_conflicting_index &&
+       echo "none of the above" >sample &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       # resolve to remove
+       git rm file &&
+       git checkout --merge -- fild file filf &&
+       {
+               echo "<<<<<<< ours" &&
+               echo ourside &&
+               echo "=======" &&
+               echo theirside &&
+               echo ">>>>>>> theirs"
+       } >merged &&
+       test_cmp expect fild &&
+       test_cmp expect filf &&
+       test_cmp merged file
+'
+
 test_expect_success 'checkout with --merge, in diff3 -m style' '
        git config merge.conflictstyle diff3 &&
        setup_conflicting_index &&
index c975eb54d234d77b2f13df2a96acd4cbefb93865..611b3dd3aedb44c9b5d4657f3058c72a60258cb0 100755 (executable)
@@ -120,7 +120,7 @@ test_expect_success 'git clean with relative prefix' '
                grep part3 |
                sed -n -e "s|^Would remove ||p"
        ) &&
-       verbose test "$would_clean" = ../src/part3.c
+       test "$would_clean" = ../src/part3.c
 '
 
 test_expect_success 'git clean with absolute path' '
@@ -133,7 +133,7 @@ test_expect_success 'git clean with absolute path' '
                grep part3 |
                sed -n -e "s|^Would remove ||p"
        ) &&
-       verbose test "$would_clean" = ../src/part3.c
+       test "$would_clean" = ../src/part3.c
 '
 
 test_expect_success 'git clean with out of work tree relative path' '
@@ -517,8 +517,12 @@ test_expect_success 'nested (empty) git should be kept' '
        git init empty_repo &&
        mkdir to_clean &&
        >to_clean/should_clean.this &&
+       # Note that we put the expect file in the .git directory so that it
+       # does not get cleaned.
+       find empty_repo | sort >.git/expect &&
        git clean -f -d &&
-       test_path_is_file empty_repo/.git/HEAD &&
+       find empty_repo | sort >actual &&
+       test_cmp .git/expect actual &&
        test_path_is_missing to_clean
 '
 
@@ -559,10 +563,10 @@ test_expect_success 'giving path in nested git work tree will NOT remove it' '
                mkdir -p bar/baz &&
                test_commit msg bar/baz/hello.world
        ) &&
+       find repo | sort >expect &&
        git clean -f -d repo/bar/baz &&
-       test_path_is_file repo/.git/HEAD &&
-       test_path_is_dir repo/bar/ &&
-       test_path_is_file repo/bar/baz/hello.world
+       find repo | sort >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'giving path to nested .git will not remove it' '
@@ -573,10 +577,10 @@ test_expect_success 'giving path to nested .git will not remove it' '
                git init &&
                test_commit msg hello.world
        ) &&
+       find repo | sort >expect &&
        git clean -f -d repo/.git &&
-       test_path_is_file repo/.git/HEAD &&
-       test_path_is_dir repo/.git/refs &&
-       test_path_is_dir repo/.git/objects &&
+       find repo | sort >actual &&
+       test_cmp expect actual &&
        test_path_is_dir untracked/
 '
 
@@ -588,9 +592,10 @@ test_expect_success 'giving path to nested .git/ will NOT remove contents' '
                git init &&
                test_commit msg hello.world
        ) &&
+       find repo | sort >expect &&
        git clean -f -d repo/.git/ &&
-       test_path_is_dir repo/.git &&
-       test_path_is_file repo/.git/HEAD &&
+       find repo | sort >actual &&
+       test_cmp expect actual &&
        test_path_is_dir untracked/
 '
 
@@ -735,7 +740,7 @@ test_expect_success MINGW 'handle clean & core.longpaths = false nicely' '
        test_must_fail git clean -xdf 2>.git/err &&
        # grepping for a strerror string is unportable but it is OK here with
        # MINGW prereq
-       test_i18ngrep "too long" .git/err
+       test_grep "too long" .git/err
 '
 
 test_expect_success 'clean untracked paths by pathspec' '
index a07e8b86de2014fd24bcb4692a744f147ef07787..d82a3210a1db48288c54ce3c06d430546da70f48 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git clean -i basic tests'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
index a989aafaf57aa8710ff7889318abb8d3c9cadd51..00c1f1aab1304c127a5dccdaeff62d7213b7a9e5 100755 (executable)
@@ -60,7 +60,7 @@ test_expect_success 'submodule init aborts on missing .gitmodules file' '
        git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
        # missing the .gitmodules file here
        test_must_fail git submodule init 2>actual &&
-       test_i18ngrep "No url found for submodule path" actual
+       test_grep "No url found for submodule path" actual
 '
 
 test_expect_success 'submodule update aborts on missing .gitmodules file' '
@@ -68,7 +68,7 @@ test_expect_success 'submodule update aborts on missing .gitmodules file' '
        git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
        # missing the .gitmodules file here
        git submodule update sub 2>actual &&
-       test_i18ngrep "Submodule path .sub. not initialized" actual
+       test_grep "Submodule path .sub. not initialized" actual
 '
 
 test_expect_success 'submodule update aborts on missing gitmodules url' '
@@ -100,7 +100,7 @@ test_expect_success 'status should ignore inner git repo when not added' '
        ) &&
        test_must_fail git submodule status inner 2>output.err &&
        rm -fr inner &&
-       test_i18ngrep "^error: .*did not match any file(s) known to git" output.err
+       test_grep "^error: .*did not match any file(s) known to git" output.err
 '
 
 test_expect_success 'setup - repository in init subdirectory' '
@@ -196,7 +196,7 @@ test_expect_success 'redirected submodule add does not show progress' '
        git -C addtest submodule add "file://$submodurl/parent" submod-redirected \
                2>err &&
        ! grep % err &&
-       test_i18ngrep ! "Checking connectivity" err
+       test_grep ! "Checking connectivity" err
 '
 
 test_expect_success 'redirected submodule add --progress does show progress' '
@@ -263,7 +263,7 @@ test_expect_success 'submodule add relays add --dry-run stderr' '
                cd addtest &&
                : >.git/index.lock &&
                ! git submodule add "$submodurl" sub-while-locked 2>output.err &&
-               test_i18ngrep "^fatal: .*index\.lock" output.err &&
+               test_grep "^fatal: .*index\.lock" output.err &&
                test_path_is_missing sub-while-locked
        )
 '
@@ -405,7 +405,7 @@ test_expect_success 'submodule add in subdirectory with relative path should fai
                cd addtest/sub &&
                test_must_fail git submodule add ../../ submod3 2>../../output.err
        ) &&
-       test_i18ngrep toplevel output.err
+       test_grep toplevel output.err
 '
 
 test_expect_success 'setup - add an example entry to .gitmodules' '
@@ -486,7 +486,7 @@ test_expect_success 'status should still be "missing" after initializing' '
 
 test_failure_with_unknown_submodule () {
        test_must_fail git submodule $1 no-such-submodule 2>output.err &&
-       test_i18ngrep "^error: .*no-such-submodule" output.err
+       test_grep "^error: .*no-such-submodule" output.err
 }
 
 test_expect_success 'init should fail with unknown submodule' '
@@ -579,6 +579,16 @@ test_expect_success 'status should be "modified" after submodule commit' '
        grep "^+$rev2" list
 '
 
+test_expect_success '"submodule --cached" command forms should be identical' '
+       git submodule status --cached >expect &&
+
+       git submodule --cached >actual &&
+       test_cmp expect actual &&
+
+       git submodule --cached status >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'the --cached sha1 should be rev1' '
        git submodule --cached status >list &&
        grep "^+$rev1" list
@@ -634,7 +644,7 @@ test_expect_success 'update --init' '
        test_must_fail git config submodule.example.url &&
 
        git submodule update init 2> update.out &&
-       test_i18ngrep "not initialized" update.out &&
+       test_grep "not initialized" update.out &&
        test_must_fail git rev-parse --resolve-git-dir init/.git &&
 
        git submodule update --init init &&
@@ -651,7 +661,7 @@ test_expect_success 'update --init from subdirectory' '
        (
                cd sub &&
                git submodule update ../init 2>update.out &&
-               test_i18ngrep "not initialized" update.out &&
+               test_grep "not initialized" update.out &&
                test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
 
                git submodule update --init ../init
@@ -1111,7 +1121,7 @@ test_expect_success 'submodule deinit from subdirectory' '
                cd sub &&
                git submodule deinit ../init >../output
        ) &&
-       test_i18ngrep "\\.\\./init" output &&
+       test_grep "\\.\\./init" output &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
        test -n "$(git config --get-regexp "submodule\.example2\.")" &&
        test -f example2/.git &&
@@ -1126,8 +1136,8 @@ test_expect_success 'submodule deinit . deinits all initialized submodules' '
        git submodule deinit . >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
        test -z "$(git config --get-regexp "submodule\.example2\.")" &&
-       test_i18ngrep "Cleared directory .init" actual &&
-       test_i18ngrep "Cleared directory .example2" actual &&
+       test_grep "Cleared directory .init" actual &&
+       test_grep "Cleared directory .example2" actual &&
        rmdir init example2
 '
 
@@ -1139,8 +1149,8 @@ test_expect_success 'submodule deinit --all deinits all initialized submodules'
        git submodule deinit --all >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
        test -z "$(git config --get-regexp "submodule\.example2\.")" &&
-       test_i18ngrep "Cleared directory .init" actual &&
-       test_i18ngrep "Cleared directory .example2" actual &&
+       test_grep "Cleared directory .init" actual &&
+       test_grep "Cleared directory .example2" actual &&
        rmdir init example2
 '
 
@@ -1150,8 +1160,8 @@ test_expect_success 'submodule deinit deinits a submodule when its work tree is
        git submodule deinit init example2 >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
        test -z "$(git config --get-regexp "submodule\.example2\.")" &&
-       test_i18ngrep ! "Cleared directory .init" actual &&
-       test_i18ngrep "Cleared directory .example2" actual &&
+       test_grep ! "Cleared directory .init" actual &&
+       test_grep "Cleared directory .example2" actual &&
        rmdir init
 '
 
@@ -1163,7 +1173,7 @@ test_expect_success 'submodule deinit fails when the submodule contains modifica
        test -f example2/.git &&
        git submodule deinit -f init >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
-       test_i18ngrep "Cleared directory .init" actual &&
+       test_grep "Cleared directory .init" actual &&
        rmdir init
 '
 
@@ -1175,7 +1185,7 @@ test_expect_success 'submodule deinit fails when the submodule contains untracke
        test -f example2/.git &&
        git submodule deinit -f init >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
-       test_i18ngrep "Cleared directory .init" actual &&
+       test_grep "Cleared directory .init" actual &&
        rmdir init
 '
 
@@ -1190,30 +1200,30 @@ test_expect_success 'submodule deinit fails when the submodule HEAD does not mat
        test -f example2/.git &&
        git submodule deinit -f init >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
-       test_i18ngrep "Cleared directory .init" actual &&
+       test_grep "Cleared directory .init" actual &&
        rmdir init
 '
 
 test_expect_success 'submodule deinit is silent when used on an uninitialized submodule' '
        git submodule update --init &&
        git submodule deinit init >actual &&
-       test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
-       test_i18ngrep "Cleared directory .init" actual &&
+       test_grep "Submodule .example. (.*) unregistered for path .init" actual &&
+       test_grep "Cleared directory .init" actual &&
        git submodule deinit init >actual &&
-       test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-       test_i18ngrep "Cleared directory .init" actual &&
+       test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+       test_grep "Cleared directory .init" actual &&
        git submodule deinit . >actual &&
-       test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-       test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
-       test_i18ngrep "Cleared directory .init" actual &&
+       test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+       test_grep "Submodule .example2. (.*) unregistered for path .example2" actual &&
+       test_grep "Cleared directory .init" actual &&
        git submodule deinit . >actual &&
-       test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-       test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
-       test_i18ngrep "Cleared directory .init" actual &&
+       test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+       test_grep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+       test_grep "Cleared directory .init" actual &&
        git submodule deinit --all >actual &&
-       test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-       test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
-       test_i18ngrep "Cleared directory .init" actual &&
+       test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+       test_grep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+       test_grep "Cleared directory .init" actual &&
        rmdir init example2
 '
 
@@ -1341,6 +1351,22 @@ test_expect_success 'clone active submodule without submodule url set' '
        )
 '
 
+test_expect_success 'update submodules without url set in .gitconfig' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       git clone file://"$pwd"/multisuper multisuper_clone &&
+
+       git -C multisuper_clone submodule init &&
+       for s in sub0 sub1 sub2 sub3
+       do
+               key=submodule.$s.url &&
+               git -C multisuper_clone config --local --unset $key &&
+               git -C multisuper_clone config --file .gitmodules --unset $key || return 1
+       done &&
+
+       test_must_fail git -C multisuper_clone submodule update 2>err &&
+       grep "cannot clone submodule .sub[0-3]. without a URL" err
+'
+
 test_expect_success 'clone --recurse-submodules with a pathspec works' '
        test_when_finished "rm -rf multisuper_clone" &&
        cat >expected <<-\EOF &&
index ebeca12a71115f60d10fa3ffa4725b1c7080f6f0..2b3c363078bc06219c8b5c16b02f331b447f5102 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Test rebasing, stashing, etc. with submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -55,12 +56,15 @@ chmod a+x fake-editor.sh
 
 test_expect_success 'interactive rebase with a dirty submodule' '
 
-       test submodule = $(git diff --name-only) &&
+       echo submodule >expect &&
+       git diff --name-only >actual &&
+       test_cmp expect actual &&
        HEAD=$(git rev-parse HEAD) &&
        GIT_EDITOR="\"$(pwd)/fake-editor.sh\"" EDITOR_TEXT="pick $HEAD" \
                git rebase -i HEAD^ &&
-       test submodule = $(git diff --name-only)
-
+       echo submodule >expect &&
+       git diff --name-only >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'rebase with dirty file and submodule fails' '
@@ -82,11 +86,19 @@ test_expect_success 'stash with a dirty submodule' '
        CURRENT=$(cd submodule && git rev-parse HEAD) &&
        git stash &&
        test new != $(cat file) &&
-       test submodule = $(git diff --name-only) &&
-       test $CURRENT = $(cd submodule && git rev-parse HEAD) &&
+       echo submodule >expect &&
+       git diff --name-only >actual &&
+       test_cmp expect actual &&
+
+       echo "$CURRENT" >expect &&
+       git -C submodule rev-parse HEAD >actual &&
+       test_cmp expect actual &&
+
        git stash apply &&
        test new = $(cat file) &&
-       test $CURRENT = $(cd submodule && git rev-parse HEAD)
+       echo "$CURRENT" >expect &&
+       git -C submodule rev-parse HEAD >actual &&
+       test_cmp expect actual
 
 '
 
index ea92ef52a5eb9c593039614a6e42fa308a68b852..19b6135d11744031c8777c3bd66c752d6ab9ba75 100755 (executable)
@@ -11,6 +11,7 @@ These tests exercise the "git submodule sync" subcommand.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -162,7 +163,7 @@ test_expect_success '"git submodule sync" should update submodule URLs - subdire
                cd sub &&
                git submodule sync >../../output
        ) &&
-       test_i18ngrep "\\.\\./submodule" output &&
+       test_grep "\\.\\./submodule" output &&
        test -d "$(
                cd super-clone/submodule &&
                git config remote.origin.url
@@ -193,7 +194,7 @@ test_expect_success '"git submodule sync --recursive" should update all submodul
                cd sub &&
                git submodule sync --recursive >../../output
        ) &&
-       test_i18ngrep "\\.\\./submodule/sub-submodule" output &&
+       test_grep "\\.\\./submodule/sub-submodule" output &&
        test -d "$(
                cd super-clone/submodule &&
                git config remote.origin.url
index f094e3d7f3642de61a69b3d9131e31707654539c..8491b8c58b97f0597a5fcc27448b3d99abdbe9c9 100755 (executable)
@@ -945,7 +945,7 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re
        git clone super_update_r super_update_r2 &&
        (cd super_update_r2 &&
         git submodule update --init --recursive >actual &&
-        test_i18ngrep "Submodule path .submodule/subsubmodule.: checked out" actual &&
+        test_grep "Submodule path .submodule/subsubmodule.: checked out" actual &&
         (cd submodule/subsubmodule &&
          git log > ../../expected
         ) &&
@@ -1025,7 +1025,7 @@ test_expect_success 'submodule update clone shallow submodule outside of depth'
                # unadvertised objects, so restrict this test to v0.
                test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
                        git submodule update --init --depth=1 2>actual &&
-               test_i18ngrep "Direct fetching of that commit failed." actual &&
+               test_grep "Direct fetching of that commit failed." actual &&
                git -C ../submodule config uploadpack.allowReachableSHA1InWant true &&
                git submodule update --init --depth=1 >actual &&
                git -C submodule log --oneline >out &&
@@ -1039,7 +1039,7 @@ test_expect_success 'submodule update --recursive drops module name before recur
          git checkout HEAD^
         ) &&
         git submodule update --recursive deeper/submodule >actual &&
-        test_i18ngrep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual
+        test_grep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual
        )
 '
 
@@ -1179,4 +1179,27 @@ test_expect_success 'submodule update --recursive skip submodules with strategy=
        test_cmp expect.err actual.err
 '
 
+add_submodule_commit_and_validate () {
+       HASH=$(git rev-parse HEAD) &&
+       git update-index --add --cacheinfo 160000,$HASH,sub &&
+       git commit -m "create submodule" &&
+       echo "160000 commit $HASH       sub" >expect &&
+       git ls-tree HEAD -- sub >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'commit with staged submodule change' '
+       add_submodule_commit_and_validate
+'
+
+test_expect_success 'commit with staged submodule change with ignoreSubmodules dirty' '
+       test_config diff.ignoreSubmodules dirty &&
+       add_submodule_commit_and_validate
+'
+
+test_expect_success 'commit with staged submodule change with ignoreSubmodules all' '
+       test_config diff.ignoreSubmodules all &&
+       add_submodule_commit_and_validate
+'
+
 test_done
index 59bd150166793000ed60e70fc9e570519f53f055..8d7b234beb8b09b2f6dbe459aad56315612925b8 100755 (executable)
@@ -154,6 +154,11 @@ test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
        )
 '
 
+test_expect_success 'usage: foreach -- --not-an-option' '
+       test_expect_code 1 git submodule foreach -- --not-an-option &&
+       test_expect_code 1 git -C clone2 submodule foreach -- --not-an-option
+'
+
 test_expect_success 'use "foreach --recursive" to checkout all submodules' '
        (
                cd clone2 &&
index 374ed481e9c64b9813a11b57c80f7b6f22c791e1..574a6fc526ea0c22ed2ca9df925854235632bb42 100755 (executable)
@@ -13,6 +13,7 @@ TEST_NO_CREATE_REPO=1
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index c583c4e373ad0de2a0b51080010da995646dff3a..31271f8e0a61f7b071c770f9efad0e235920f37f 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success 'configuration parsing with error' '
        (
                cd repo &&
                test_must_fail test-tool submodule-config "" s 2>actual &&
-               test_i18ngrep "bad config" actual
+               test_grep "bad config" actual
        )
 '
 
@@ -101,7 +101,7 @@ test_expect_success 'error in history of one submodule config lets continue, std
                                >actual \
                                2>actual_stderr &&
                test_cmp expect_error actual &&
-               test_i18ngrep "submodule-blob $sha1:.gitmodules" actual_stderr >/dev/null
+               test_grep "submodule-blob $sha1:.gitmodules" actual_stderr >/dev/null
        )
 '
 
@@ -137,44 +137,44 @@ test_expect_success 'error in history in fetchrecursesubmodule lets continue' '
        )
 '
 
-test_expect_success 'reading submodules config from the working tree with "submodule--helper config"' '
+test_expect_success 'reading submodules config from the working tree' '
        (cd super &&
                echo "../submodule" >expect &&
-               git submodule--helper config submodule.submodule.url >actual &&
+               test-tool submodule config-list submodule.submodule.url >actual &&
                test_cmp expect actual
        )
 '
 
-test_expect_success 'unsetting submodules config from the working tree with "submodule--helper config --unset"' '
+test_expect_success 'unsetting submodules config from the working tree' '
        (cd super &&
-               git submodule--helper config --unset submodule.submodule.url &&
-               git submodule--helper config submodule.submodule.url >actual &&
+               test-tool submodule config-unset submodule.submodule.url &&
+               test-tool submodule config-list submodule.submodule.url >actual &&
                test_must_be_empty actual
        )
 '
 
 
-test_expect_success 'writing submodules config with "submodule--helper config"' '
+test_expect_success 'writing submodules config' '
        (cd super &&
                echo "new_url" >expect &&
-               git submodule--helper config submodule.submodule.url "new_url" &&
-               git submodule--helper config submodule.submodule.url >actual &&
+               test-tool submodule config-set submodule.submodule.url "new_url" &&
+               test-tool submodule config-list submodule.submodule.url >actual &&
                test_cmp expect actual
        )
 '
 
-test_expect_success 'overwriting unstaged submodules config with "submodule--helper config"' '
+test_expect_success 'overwriting unstaged submodules config' '
        test_when_finished "git -C super checkout .gitmodules" &&
        (cd super &&
                echo "newer_url" >expect &&
-               git submodule--helper config submodule.submodule.url "newer_url" &&
-               git submodule--helper config submodule.submodule.url >actual &&
+               test-tool submodule config-set submodule.submodule.url "newer_url" &&
+               test-tool submodule config-list submodule.submodule.url >actual &&
                test_cmp expect actual
        )
 '
 
 test_expect_success 'writeable .gitmodules when it is in the working tree' '
-       git -C super submodule--helper config --check-writeable
+       test-tool -C super submodule config-writeable
 '
 
 test_expect_success 'writeable .gitmodules when it is nowhere in the repository' '
@@ -183,7 +183,7 @@ test_expect_success 'writeable .gitmodules when it is nowhere in the repository'
        (cd super &&
                git rm .gitmodules &&
                git commit -m "remove .gitmodules from the current branch" &&
-               git submodule--helper config --check-writeable
+               test-tool submodule config-writeable
        )
 '
 
@@ -191,7 +191,7 @@ test_expect_success 'non-writeable .gitmodules when it is in the index but not i
        test_when_finished "git -C super checkout .gitmodules" &&
        (cd super &&
                rm -f .gitmodules &&
-               test_must_fail git submodule--helper config --check-writeable
+               test_must_fail test-tool submodule config-writeable
        )
 '
 
@@ -200,7 +200,7 @@ test_expect_success 'non-writeable .gitmodules when it is in the current branch
        test_when_finished "git -C super reset --hard $ORIG" &&
        (cd super &&
                git rm .gitmodules &&
-               test_must_fail git submodule--helper config --check-writeable
+               test_must_fail test-tool submodule config-writeable
        )
 '
 
@@ -208,11 +208,11 @@ test_expect_success 'reading submodules config from the index when .gitmodules i
        ORIG=$(git -C super rev-parse HEAD) &&
        test_when_finished "git -C super reset --hard $ORIG" &&
        (cd super &&
-               git submodule--helper config submodule.submodule.url "staged_url" &&
+               test-tool submodule config-set submodule.submodule.url "staged_url" &&
                git add .gitmodules &&
                rm -f .gitmodules &&
                echo "staged_url" >expect &&
-               git submodule--helper config submodule.submodule.url >actual &&
+               test-tool submodule config-list submodule.submodule.url >actual &&
                test_cmp expect actual
        )
 '
@@ -223,7 +223,7 @@ test_expect_success 'reading submodules config from the current branch when .git
        (cd super &&
                git rm .gitmodules &&
                echo "../submodule" >expect &&
-               git submodule--helper config submodule.submodule.url >actual &&
+               test-tool submodule config-list submodule.submodule.url >actual &&
                test_cmp expect actual
        )
 '
index 2859695c6d208341f5ba1de01e1cfe4131388a2b..f77832185765585e2bda1677f8cbbe13841127f7 100755 (executable)
@@ -10,6 +10,7 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
+       cwd="$(pwd)" &&
        git init sub1 &&
        test_commit -C sub1 first &&
        git submodule add ./sub1 &&
@@ -18,13 +19,21 @@ test_expect_success 'setup a real submodule' '
 '
 
 test_expect_success 'absorb the git dir' '
+       >expect &&
+       >actual &&
        >expect.1 &&
        >expect.2 &&
        >actual.1 &&
        >actual.2 &&
        git status >expect.1 &&
        git -C sub1 rev-parse HEAD >expect.2 &&
-       git submodule absorbgitdirs &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''sub1'\'' from
+       '\''$cwd/sub1/.git'\'' to
+       '\''$cwd/.git/modules/sub1'\''
+       EOF
+       git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual &&
        git fsck &&
        test -f sub1/.git &&
        test -d .git/modules/sub1 &&
@@ -37,7 +46,8 @@ test_expect_success 'absorb the git dir' '
 test_expect_success 'absorbing does not fail for deinitialized submodules' '
        test_when_finished "git submodule update --init" &&
        git submodule deinit --all &&
-       git submodule absorbgitdirs &&
+       git submodule absorbgitdirs 2>err &&
+       test_must_be_empty err &&
        test -d .git/modules/sub1 &&
        test -d sub1 &&
        ! test -e sub1/.git
@@ -56,7 +66,13 @@ test_expect_success 'setup nested submodule' '
 test_expect_success 'absorb the git dir in a nested submodule' '
        git status >expect.1 &&
        git -C sub1/nested rev-parse HEAD >expect.2 &&
-       git submodule absorbgitdirs &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''sub1/nested'\'' from
+       '\''$cwd/sub1/nested/.git'\'' to
+       '\''$cwd/.git/modules/sub1/modules/nested'\''
+       EOF
+       git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual &&
        test -f sub1/nested/.git &&
        test -d .git/modules/sub1/modules/nested &&
        git status >actual.1 &&
@@ -87,7 +103,13 @@ test_expect_success 're-setup nested submodule' '
 test_expect_success 'absorb the git dir in a nested submodule' '
        git status >expect.1 &&
        git -C sub1/nested rev-parse HEAD >expect.2 &&
-       git submodule absorbgitdirs &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''sub1'\'' from
+       '\''$cwd/sub1/.git'\'' to
+       '\''$cwd/.git/modules/sub1'\''
+       EOF
+       git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual &&
        test -f sub1/.git &&
        test -f sub1/nested/.git &&
        test -d .git/modules/sub1/modules/nested &&
@@ -97,6 +119,27 @@ test_expect_success 'absorb the git dir in a nested submodule' '
        test_cmp expect.2 actual.2
 '
 
+test_expect_success 'absorb the git dir outside of primary worktree' '
+       test_when_finished "rm -rf repo-bare.git" &&
+       git clone --bare . repo-bare.git &&
+       test_when_finished "rm -rf repo-wt" &&
+       git -C repo-bare.git worktree add ../repo-wt &&
+
+       test_when_finished "rm -f .gitconfig" &&
+       test_config_global protocol.file.allow always &&
+       git -C repo-wt submodule update --init &&
+       git init repo-wt/sub2 &&
+       test_commit -C repo-wt/sub2 A &&
+       git -C repo-wt submodule add ./sub2 sub2 &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''sub2'\'' from
+       '\''$cwd/repo-wt/sub2/.git'\'' to
+       '\''$cwd/repo-bare.git/worktrees/repo-wt/modules/sub2'\''
+       EOF
+       git -C repo-wt submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup a gitlink with missing .gitmodules entry' '
        git init sub2 &&
        test_commit -C sub2 first &&
@@ -107,7 +150,11 @@ test_expect_success 'setup a gitlink with missing .gitmodules entry' '
 test_expect_success 'absorbing the git dir fails for incomplete submodules' '
        git status >expect.1 &&
        git -C sub2 rev-parse HEAD >expect.2 &&
-       test_must_fail git submodule absorbgitdirs &&
+       cat >expect <<-\EOF &&
+       fatal: could not lookup name for submodule '\''sub2'\''
+       EOF
+       test_must_fail git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual &&
        git -C sub2 fsck &&
        test -d sub2/.git &&
        git status >actual &&
@@ -127,8 +174,11 @@ test_expect_success 'setup a submodule with multiple worktrees' '
 '
 
 test_expect_success 'absorbing fails for a submodule with multiple worktrees' '
-       test_must_fail git submodule absorbgitdirs sub3 2>error &&
-       test_i18ngrep "not supported" error
+       cat >expect <<-\EOF &&
+       fatal: could not lookup name for submodule '\''sub2'\''
+       EOF
+       test_must_fail git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual
 '
 
 test_done
index 7cdc26376490e80274c4f699ddd0b00c25e4d9cd..887d181b72ec05ab68f619de3948d667179dcb68 100755 (executable)
@@ -51,6 +51,22 @@ test_expect_success 'is-active works with submodule.<name>.active config' '
        test-tool -C super submodule is-active sub1
 '
 
+test_expect_success 'is-active handles submodule.active config missing a value' '
+       cp super/.git/config super/.git/config.orig &&
+       test_when_finished mv super/.git/config.orig super/.git/config &&
+
+       cat >>super/.git/config <<-\EOF &&
+       [submodule]
+               active
+       EOF
+
+       cat >expect <<-\EOF &&
+       error: missing value for '\''submodule.active'\''
+       EOF
+       test-tool -C super submodule is-active sub1 2>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'is-active works with basic submodule.active config' '
        test_when_finished "git -C super config submodule.sub1.URL ../sub" &&
        test_when_finished "git -C super config --unset-all submodule.active" &&
index 101afff30f67683ec03e92e4a89cead6badac521..24f30e3bf9c6440eca92838b42ae330a856f3fb6 100755 (executable)
@@ -13,13 +13,13 @@ test_expect_success 'create embedded repository' '
 test_expect_success 'git-add on embedded repository warns' '
        test_when_finished "git rm --cached -f embed" &&
        git add embed 2>stderr &&
-       test_i18ngrep warning stderr
+       test_grep warning stderr
 '
 
 test_expect_success '--no-warn-embedded-repo suppresses warning' '
        test_when_finished "git rm --cached -f embed" &&
        git add --no-warn-embedded-repo embed 2>stderr &&
-       test_i18ngrep ! warning stderr
+       test_grep ! warning stderr
 '
 
 test_expect_success 'no warning when updating entry' '
@@ -27,14 +27,14 @@ test_expect_success 'no warning when updating entry' '
        git add embed &&
        git -C embed commit --allow-empty -m two &&
        git add embed 2>stderr &&
-       test_i18ngrep ! warning stderr
+       test_grep ! warning stderr
 '
 
 test_expect_success 'submodule add does not warn' '
        test_when_finished "git rm -rf submodule .gitmodules" &&
        git -c protocol.file.allow=always \
                submodule add ./embed submodule 2>stderr &&
-       test_i18ngrep ! warning stderr
+       test_grep ! warning stderr
 '
 
 test_done
index 3ebd98598144c95165cc75870e6d586407add1c8..2ab566e71787ab6f1f77eb97b6b476c5f3fbacbc 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check handling of disallowed .gitmodule urls'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -39,7 +41,7 @@ test_expect_success 'remove ./ protection from .gitmodules url' '
 test_expect_success 'clone rejects unprotected dash' '
        test_when_finished "rm -rf dst" &&
        test_must_fail git clone --recurse-submodules . dst 2>err &&
-       test_i18ngrep ignoring err
+       test_grep ignoring err
 '
 
 test_expect_success 'fsck rejects unprotected dash' '
@@ -61,7 +63,7 @@ test_expect_success 'trailing backslash is handled correctly' '
        mv .new .gitmodules &&
        git commit -am "Add testmodule" &&
        test_must_fail git clone --verbose --recurse-submodules . dolly 2>err &&
-       test_i18ngrep ! "unknown option" err
+       test_grep ! "unknown option" err
 '
 
 test_expect_success 'fsck rejects missing URL scheme' '
index 2f4b25dfd7e3860e47c0acdd3a9eeabff7f5504e..5e3051da8bb362fe01b80f5bc20ef0d736886a22 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success 'create submodule with dash in path' '
 test_expect_success 'clone rejects unprotected dash' '
        test_when_finished "rm -rf dst" &&
        git clone --recurse-submodules . dst 2>err &&
-       test_i18ngrep ignoring err
+       test_grep ignoring err
 '
 
 test_expect_success 'fsck rejects unprotected dash' '
@@ -46,7 +46,7 @@ test_expect_success MINGW 'submodule paths disallows trailing spaces' '
        git -C super update-ref refs/heads/main $commit &&
 
        test_must_fail git clone --recurse-submodules super dst 2>err &&
-       test_i18ngrep "sub " err
+       test_grep "sub " err
 '
 
 test_done
index d5874200fdcc44c3812f0b702fe4f8c3c2a6dbdc..dde11ecce806c43272bdc59c6eb73a7c6ce241ad 100755 (executable)
@@ -50,12 +50,12 @@ test_expect_success 'sparse checkout setup which hides .gitmodules' '
 
 test_expect_success 'reading gitmodules config file when it is not checked out' '
        echo "../submodule" >expect &&
-       git -C super submodule--helper config submodule.submodule.url >actual &&
+       test-tool -C super submodule config-list submodule.submodule.url >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'not writing gitmodules config file when it is not checked out' '
-       test_must_fail git -C super submodule--helper config submodule.submodule.url newurl &&
+       test_must_fail test-tool -C super submodule config-set submodule.submodule.url newurl &&
        test_path_is_missing super/.gitmodules
 '
 
index 232065504cbfdcba60d3f5f69ffb4536b5ae99e8..a5d1bc5c54ae095f79bf9b3ee420ba004bb18a88 100755 (executable)
@@ -11,6 +11,10 @@ as expected.
 
 TEST_PASSES_SANITIZE_LEAK=true
 TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -27,26 +31,28 @@ test_expect_success 'submodule config cache setup' '
                git checkout -b topic &&
                echo b >a &&
                git add . &&
-               git commit -mb
+               git commit -mb &&
+               git checkout main
        ) &&
        mkdir super &&
        (cd super &&
                git init &&
                git submodule add ../submodule &&
-               git commit -m "add submodule"
+               git submodule add --name thename ../submodule thepath &&
+               git commit -m "add submodules"
        )
 '
 
 test_expect_success 'ensure submodule branch is unset' '
        (cd super &&
-               ! grep branch .gitmodules
+               test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch
        )
 '
 
 test_expect_success 'test submodule set-branch --branch' '
        (cd super &&
                git submodule set-branch --branch topic submodule &&
-               grep "branch = topic" .gitmodules &&
+               test_cmp_config topic -f .gitmodules submodule.submodule.branch &&
                git submodule update --remote &&
                cat <<-\EOF >expect &&
                b
@@ -57,13 +63,12 @@ test_expect_success 'test submodule set-branch --branch' '
 '
 
 test_expect_success 'test submodule set-branch --default' '
-       test_commit -C submodule c &&
        (cd super &&
                git submodule set-branch --default submodule &&
-               ! grep branch .gitmodules &&
+               test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch &&
                git submodule update --remote &&
                cat <<-\EOF >expect &&
-               c
+               a
                EOF
                git -C submodule show -s --pretty=%s >actual &&
                test_cmp expect actual
@@ -71,10 +76,9 @@ test_expect_success 'test submodule set-branch --default' '
 '
 
 test_expect_success 'test submodule set-branch -b' '
-       test_commit -C submodule b &&
        (cd super &&
                git submodule set-branch -b topic submodule &&
-               grep "branch = topic" .gitmodules &&
+               test_cmp_config topic -f .gitmodules submodule.submodule.branch &&
                git submodule update --remote &&
                cat <<-\EOF >expect &&
                b
@@ -85,17 +89,43 @@ test_expect_success 'test submodule set-branch -b' '
 '
 
 test_expect_success 'test submodule set-branch -d' '
-       test_commit -C submodule d &&
        (cd super &&
                git submodule set-branch -d submodule &&
-               ! grep branch .gitmodules &&
+               test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch &&
                git submodule update --remote &&
                cat <<-\EOF >expect &&
-               d
+               a
                EOF
                git -C submodule show -s --pretty=%s >actual &&
                test_cmp expect actual
        )
 '
 
+test_expect_success 'test submodule set-branch --branch with named submodule' '
+       (cd super &&
+               git submodule set-branch --branch topic thepath &&
+               test_cmp_config topic -f .gitmodules submodule.thename.branch &&
+               test_cmp_config "" -f .gitmodules --default "" submodule.thepath.branch &&
+               git submodule update --remote &&
+               cat <<-\EOF >expect &&
+               b
+               EOF
+               git -C thepath show -s --pretty=%s >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'test submodule set-branch --default with named submodule' '
+       (cd super &&
+               git submodule set-branch --default thepath &&
+               test_cmp_config "" -f .gitmodules --default "" submodule.thename.branch &&
+               git submodule update --remote &&
+               cat <<-\EOF >expect &&
+               a
+               EOF
+               git -C thepath show -s --pretty=%s >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index d6bf62b3ac670fc0be8167150cdf79cbd9000a45..bf7f15ee7973958970dfe0d1b34cea9d25e635a9 100755 (executable)
@@ -25,17 +25,26 @@ test_expect_success 'submodule config cache setup' '
                git add file &&
                git commit -ma
        ) &&
+       mkdir namedsubmodule &&
+       (
+               cd namedsubmodule &&
+               git init &&
+               echo 1 >file &&
+               git add file &&
+               git commit -m1
+       ) &&
        mkdir super &&
        (
                cd super &&
                git init &&
                git submodule add ../submodule &&
-               git commit -m "add submodule"
+               git submodule add --name thename ../namedsubmodule thepath &&
+               git commit -m "add submodules"
        )
 '
 
 test_expect_success 'test submodule set-url' '
-       # add a commit and move the submodule (change the url)
+       # add commits and move the submodules (change the urls)
        (
                cd submodule &&
                echo b >>file &&
@@ -44,15 +53,28 @@ test_expect_success 'test submodule set-url' '
        ) &&
        mv submodule newsubmodule &&
 
+       (
+               cd namedsubmodule &&
+               echo 2 >>file &&
+               git add file &&
+               git commit -m2
+       ) &&
+       mv namedsubmodule newnamedsubmodule &&
+
        git -C newsubmodule show >expect &&
+       git -C newnamedsubmodule show >>expect &&
        (
                cd super &&
                test_must_fail git submodule update --remote &&
                git submodule set-url submodule ../newsubmodule &&
-               grep -F "url = ../newsubmodule" .gitmodules &&
+               test_cmp_config ../newsubmodule -f .gitmodules submodule.submodule.url &&
+               git submodule set-url thepath ../newnamedsubmodule &&
+               test_cmp_config ../newnamedsubmodule -f .gitmodules submodule.thename.url &&
+               test_cmp_config "" -f .gitmodules --default "" submodule.thepath.url &&
                git submodule update --remote
        ) &&
        git -C super/submodule show >actual &&
+       git -C super/thepath show >>actual &&
        test_cmp expect actual
 '
 
diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
new file mode 100755 (executable)
index 0000000..ab946ec
--- /dev/null
@@ -0,0 +1,170 @@
+#!/bin/sh
+
+test_description='submodule --cached, --quiet etc. output'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-t3100.sh
+
+setup_sub () {
+       local d="$1" &&
+       shift &&
+       git $@ clone . "$d" &&
+       git $@ submodule add ./"$d"
+}
+
+normalize_status () {
+       sed -e 's/-g[0-9a-f]*/-gHASH/'
+}
+
+test_expect_success 'setup' '
+       test_commit A &&
+       test_commit B &&
+       setup_sub S  &&
+       setup_sub S.D &&
+       setup_sub S.C &&
+       setup_sub S.C.D &&
+       setup_sub X &&
+       git add S* &&
+       test_commit C &&
+
+       # recursive in X/
+       git -C X pull &&
+       GIT_ALLOW_PROTOCOL=file git -C X submodule update --init &&
+
+       # dirty
+       for d in S.D X/S.D
+       do
+               echo dirty >"$d"/A.t || return 1
+       done &&
+
+       # commit (for --cached)
+       for d in S.C* X/S.C*
+       do
+               git -C "$d" reset --hard A || return 1
+       done &&
+
+       # dirty
+       for d in S*.D X/S*.D
+       do
+               echo dirty >"$d/C2.t" || return 1
+       done &&
+
+       for ref in A B C
+       do
+               # Not different with SHA-1 and SHA-256, just (ab)using
+               # test_oid_cache as a variable bag to avoid using
+               # $(git rev-parse ...).
+               oid=$(git rev-parse $ref) &&
+               test_oid_cache <<-EOF || return 1
+               $ref sha1:$oid
+               $ref sha256:$oid
+               EOF
+       done
+'
+
+for opts in "" "status"
+do
+       test_expect_success "git submodule $opts" '
+               sed -e "s/^>//" >expect <<-EOF &&
+               > $(test_oid B) S (B)
+               >+$(test_oid A) S.C (A)
+               >+$(test_oid A) S.C.D (A)
+               > $(test_oid B) S.D (B)
+               >+$(test_oid C) X (C)
+               EOF
+               git submodule $opts >actual.raw &&
+               normalize_status <actual.raw >actual &&
+               test_cmp expect actual
+       '
+done
+
+for opts in \
+       "status --recursive"
+do
+       test_expect_success "git submodule $opts" '
+               sed -e "s/^>//" >expect <<-EOF &&
+               > $(test_oid B) S (B)
+               >+$(test_oid A) S.C (A)
+               >+$(test_oid A) S.C.D (A)
+               > $(test_oid B) S.D (B)
+               >+$(test_oid C) X (C)
+               > $(test_oid B) X/S (B)
+               >+$(test_oid A) X/S.C (A)
+               >+$(test_oid A) X/S.C.D (A)
+               > $(test_oid B) X/S.D (B)
+               > $(test_oid B) X/X (B)
+               EOF
+               git submodule $opts >actual.raw &&
+               normalize_status <actual.raw >actual &&
+               test_cmp expect actual
+       '
+done
+
+for opts in \
+       "--quiet" \
+       "--quiet status" \
+       "status --quiet"
+do
+       test_expect_success "git submodule $opts" '
+               git submodule $opts >out &&
+               test_must_be_empty out
+       '
+done
+
+for opts in \
+       "--cached" \
+       "--cached status" \
+       "status --cached"
+do
+       test_expect_success "git submodule $opts" '
+               sed -e "s/^>//" >expect <<-EOF &&
+               > $(test_oid B) S (B)
+               >+$(test_oid B) S.C (B)
+               >+$(test_oid B) S.C.D (B)
+               > $(test_oid B) S.D (B)
+               >+$(test_oid B) X (B)
+               EOF
+               git submodule $opts >actual.raw &&
+               normalize_status <actual.raw >actual &&
+               test_cmp expect actual
+       '
+done
+
+for opts in \
+       "--cached --quiet" \
+       "--cached --quiet status" \
+       "--cached status --quiet" \
+       "--quiet status --cached" \
+       "status --cached --quiet"
+do
+       test_expect_success "git submodule $opts" '
+               git submodule $opts >out &&
+               test_must_be_empty out
+       '
+done
+
+for opts in \
+       "status --cached --recursive" \
+       "--cached status --recursive"
+do
+       test_expect_success "git submodule $opts" '
+               sed -e "s/^>//" >expect <<-EOF &&
+               > $(test_oid B) S (B)
+               >+$(test_oid B) S.C (B)
+               >+$(test_oid B) S.C.D (B)
+               > $(test_oid B) S.D (B)
+               >+$(test_oid B) X (B)
+               > $(test_oid B) X/S (B)
+               >+$(test_oid B) X/S.C (B)
+               >+$(test_oid B) X/S.C.D (B)
+               > $(test_oid B) X/S.D (B)
+               > $(test_oid B) X/X (B)
+               EOF
+               git submodule $opts >actual.raw &&
+               normalize_status <actual.raw >actual &&
+               test_cmp expect actual
+       '
+done
+
+test_done
index ba1f569bcbbdc613c768bd34e7de6e560b809c9d..35a31acd4d7e26bc3bf8f42853d80593488c9227 100755 (executable)
@@ -12,6 +12,8 @@ Such as:
 
   - symlinked .gitmodules, etc
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-pack.sh
 
@@ -236,7 +238,7 @@ test_expect_success 'fsck detects non-blob .gitmodules' '
                git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree &&
 
                test_must_fail git fsck 2>output &&
-               test_i18ngrep gitmodulesBlob output
+               test_grep gitmodulesBlob output
        )
 '
 
@@ -250,8 +252,8 @@ test_expect_success 'fsck detects corrupt .gitmodules' '
                git commit -m "broken gitmodules" &&
 
                git fsck 2>output &&
-               test_i18ngrep gitmodulesParse output &&
-               test_i18ngrep ! "bad config" output
+               test_grep gitmodulesParse output &&
+               test_grep ! "bad config" output
        )
 '
 
@@ -273,7 +275,7 @@ test_expect_success WINDOWS 'prevent git~1 squatting on Windows' '
                hash="$(echo x | git hash-object -w --stdin)" &&
                test_must_fail git update-index --add \
                        --cacheinfo 160000,$rev,d\\a 2>err &&
-               test_i18ngrep "Invalid path" err &&
+               test_grep "Invalid path" err &&
                git -c core.protectNTFS=false update-index --add \
                        --cacheinfo 100644,$modules,.gitmodules \
                        --cacheinfo 160000,$rev,c \
@@ -287,7 +289,7 @@ test_expect_success WINDOWS 'prevent git~1 squatting on Windows' '
        then
                test_must_fail git -c core.protectNTFS=false \
                        clone --recurse-submodules squatting squatting-clone 2>err &&
-               test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
+               test_grep -e "directory not empty" -e "not an empty directory" err &&
                ! grep gitdir squatting-clone/d/a/git~2
        fi
 '
@@ -312,7 +314,7 @@ test_expect_success 'git dirs of sibling submodules must not be nested' '
                git commit -m nested
        ) &&
        test_must_fail git clone --recurse-submodules nested clone 2>err &&
-       test_i18ngrep "is inside git dir" err
+       test_grep "is inside git dir" err
 '
 
 test_done
index 5fcaa0b4f2aa5e7b533f0dc0beb5426f2997645a..4dca8d97a772d63566a9ea19121d8019cafb766c 100755 (executable)
@@ -555,7 +555,7 @@ test_expect_success 'commit without staging files fails and displays hints' '
        git commit -m initial &&
        echo "changes" >>file &&
        test_must_fail git commit -m update >actual &&
-       test_i18ngrep "no changes added to commit (use \"git add\" and/or \"git commit -a\")" actual
+       test_grep "no changes added to commit (use \"git add\" and/or \"git commit -a\")" actual
 '
 
 test_done
index fb5417d5e7e044f5bfbbe1b4ef8f6b42857c4e73..3d8500a52e50c004544c6df62cf123b8aeb62a99 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success 'initial status' '
        echo bongo bongo >file &&
        git add file &&
        git status >actual &&
-       test_i18ngrep "No commits yet" actual
+       test_grep "No commits yet" actual
 '
 
 test_expect_success 'fail initial amend' '
@@ -141,7 +141,7 @@ test_expect_success 'template "emptyness" check does not kick in with -F' '
 test_expect_success 'template "emptyness" check' '
        git checkout HEAD file && echo >>file && git add file &&
        test_must_fail git commit -t file 2>err &&
-       test_i18ngrep "did not edit" err
+       test_grep "did not edit" err
 '
 
 test_expect_success 'setup: commit message from file' '
@@ -671,7 +671,7 @@ test_expect_success 'commit a file whose name is a dash' '
        git add ./- &&
        test_tick &&
        git commit -m "add dash" >output </dev/null &&
-       test_i18ngrep " changed, 5 insertions" output
+       test_grep " changed, 5 insertions" output
 '
 
 test_expect_success '--only works on to-be-born branch' '
index 38a532d81ccd060d7c42e1a3f413bdfeccf4715b..61c8e810ccede4618594570ea3b747f5c51e6d1c 100755 (executable)
@@ -466,6 +466,25 @@ test_expect_success 'commit --trailer with -c and command' '
        test_cmp expected actual
 '
 
+test_expect_success 'commit --trailer not confused by --- separator' '
+       cat >msg <<-\EOF &&
+       subject
+
+       body with dashes
+       ---
+       in it
+       EOF
+       git commit --allow-empty --trailer="my-trailer: value" -F msg &&
+       {
+               cat msg &&
+               echo &&
+               echo "my-trailer: value"
+       } >expected &&
+       git cat-file commit HEAD >commit.msg &&
+       sed -e "1,/^\$/d" commit.msg >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
        >negative &&
@@ -687,14 +706,14 @@ test_expect_success 'cleanup commit message (whitespace config, -m)' '
 test_expect_success 'message shows author when it is not equal to committer' '
        echo >>negative &&
        git commit -e -m "sample" -a &&
-       test_i18ngrep \
+       test_grep \
          "^# Author: *A U Thor <author@example.com>\$" \
          .git/COMMIT_EDITMSG
 '
 
 test_expect_success 'message shows date when it is explicitly set' '
        git commit --allow-empty -e -m foo --date="2010-01-02T03:04:05" &&
-       test_i18ngrep \
+       test_grep \
          "^# Date: *Sat Jan 2 03:04:05 2010 +0000" \
          .git/COMMIT_EDITMSG
 '
@@ -709,7 +728,7 @@ test_expect_success AUTOIDENT 'message shows committer when it is automatic' '
        ) &&
        # the ident is calculated from the system, so we cannot
        # check the actual value, only that it is there
-       test_i18ngrep "^# Committer: " .git/COMMIT_EDITMSG
+       test_grep "^# Committer: " .git/COMMIT_EDITMSG
 '
 
 write_script .git/FAKE_EDITOR <<EOF
@@ -841,9 +860,9 @@ try_commit () {
        GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&
        case "$use_template" in
        '')
-               test_i18ngrep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
+               test_grep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
        *)
-               test_i18ngrep "^## Custom template" .git/COMMIT_EDITMSG ;;
+               test_grep "^## Custom template" .git/COMMIT_EDITMSG ;;
        esac
 }
 
@@ -851,53 +870,53 @@ try_commit_status_combo () {
 
        test_expect_success 'commit' '
                try_commit "" &&
-               test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --status' '
                try_commit --status &&
-               test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --no-status' '
                try_commit --no-status &&
-               test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit with commit.status = yes' '
                test_config commit.status yes &&
                try_commit "" &&
-               test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit with commit.status = no' '
                test_config commit.status no &&
                try_commit "" &&
-               test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --status with commit.status = yes' '
                test_config commit.status yes &&
                try_commit --status &&
-               test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --no-status with commit.status = yes' '
                test_config commit.status yes &&
                try_commit --no-status &&
-               test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --status with commit.status = no' '
                test_config commit.status no &&
                try_commit --status &&
-               test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --no-status with commit.status = no' '
                test_config commit.status no &&
                try_commit --no-status &&
-               test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+               test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
 }
@@ -911,13 +930,13 @@ try_commit_status_combo
 test_expect_success 'commit --status with custom comment character' '
        test_config core.commentchar ";" &&
        try_commit --status &&
-       test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+       test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
 '
 
 test_expect_success 'switch core.commentchar' '
        test_commit "#foo" foo &&
        GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend &&
-       test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+       test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
 '
 
 test_expect_success 'switch core.commentchar but out of options' '
index a39de8c112674f070f8dc19745e27980b7b52c9c..d1255228d5ffaa2b7b7333000e3e39468ee94bb1 100755 (executable)
@@ -5,6 +5,7 @@ test_description='commit-msg hook'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'with no hook' '
@@ -101,7 +102,9 @@ test_expect_success 'setup: commit-msg hook that always fails' '
 '
 
 commit_msg_is () {
-       test "$(git log --pretty=format:%s%b -1)" = "$1"
+       printf "%s" "$1" >expect &&
+       git log --pretty=format:%s%b -1 >actual &&
+       test_cmp expect actual
 }
 
 test_expect_success 'with failing hook' '
index d050091345b3929dc8e578409d45069b932c9733..46566d529e8725fb3b26e7664f2f8d44c2458a1c 100755 (executable)
@@ -37,19 +37,19 @@ test_expect_success 'setup' '
 
 test_expect_success 'status clean' '
        git status >output &&
-       test_i18ngrep "nothing to commit" output
+       test_grep "nothing to commit" output
 '
 
 test_expect_success 'commit --dry-run -a clean' '
        test_must_fail git commit --dry-run -a >output &&
-       test_i18ngrep "nothing to commit" output
+       test_grep "nothing to commit" output
 '
 
 test_expect_success 'status with modified file in submodule' '
        (cd sub && git reset --hard) &&
        echo "changed" >sub/foo &&
        git status >output &&
-       test_i18ngrep "modified:   sub (modified content)" output
+       test_grep "modified:   sub (modified content)" output
 '
 
 test_expect_success 'status with modified file in submodule (porcelain)' '
@@ -73,7 +73,7 @@ test_expect_success 'status with modified file in submodule (short)' '
 test_expect_success 'status with added file in submodule' '
        (cd sub && git reset --hard && echo >foo && git add foo) &&
        git status >output &&
-       test_i18ngrep "modified:   sub (modified content)" output
+       test_grep "modified:   sub (modified content)" output
 '
 
 test_expect_success 'status with added file in submodule (porcelain)' '
@@ -96,12 +96,12 @@ test_expect_success 'status with untracked file in submodule' '
        (cd sub && git reset --hard) &&
        echo "content" >sub/new-file &&
        git status >output &&
-       test_i18ngrep "modified:   sub (untracked content)" output
+       test_grep "modified:   sub (untracked content)" output
 '
 
 test_expect_success 'status -uno with untracked file in submodule' '
        git status -uno >output &&
-       test_i18ngrep "^nothing to commit" output
+       test_grep "^nothing to commit" output
 '
 
 test_expect_success 'status with untracked file in submodule (porcelain)' '
@@ -122,7 +122,7 @@ test_expect_success 'status with added and untracked file in submodule' '
        (cd sub && git reset --hard && echo >foo && git add foo) &&
        echo "content" >sub/new-file &&
        git status >output &&
-       test_i18ngrep "modified:   sub (modified content, untracked content)" output
+       test_grep "modified:   sub (modified content, untracked content)" output
 '
 
 test_expect_success 'status with added and untracked file in submodule (porcelain)' '
@@ -140,7 +140,7 @@ test_expect_success 'status with modified file in modified submodule' '
        (cd sub && echo "next change" >foo && git commit -m "next change" foo) &&
        echo "changed" >sub/foo &&
        git status >output &&
-       test_i18ngrep "modified:   sub (new commits, modified content)" output
+       test_grep "modified:   sub (new commits, modified content)" output
 '
 
 test_expect_success 'status with modified file in modified submodule (porcelain)' '
@@ -155,7 +155,7 @@ test_expect_success 'status with modified file in modified submodule (porcelain)
 test_expect_success 'status with added file in modified submodule' '
        (cd sub && git reset --hard && echo >foo && git add foo) &&
        git status >output &&
-       test_i18ngrep "modified:   sub (new commits, modified content)" output
+       test_grep "modified:   sub (new commits, modified content)" output
 '
 
 test_expect_success 'status with added file in modified submodule (porcelain)' '
@@ -170,7 +170,7 @@ test_expect_success 'status with untracked file in modified submodule' '
        (cd sub && git reset --hard) &&
        echo "content" >sub/new-file &&
        git status >output &&
-       test_i18ngrep "modified:   sub (new commits, untracked content)" output
+       test_grep "modified:   sub (new commits, untracked content)" output
 '
 
 test_expect_success 'status with untracked file in modified submodule (porcelain)' '
@@ -184,7 +184,7 @@ test_expect_success 'status with added and untracked file in modified submodule'
        (cd sub && git reset --hard && echo >foo && git add foo) &&
        echo "content" >sub/new-file &&
        git status >output &&
-       test_i18ngrep "modified:   sub (new commits, modified content, untracked content)" output
+       test_grep "modified:   sub (new commits, modified content, untracked content)" output
 '
 
 test_expect_success 'status with added and untracked file in modified submodule (porcelain)' '
@@ -209,7 +209,7 @@ test_expect_success 'setup .git file for sub' '
 test_expect_success 'status with added file in modified submodule with .git file' '
        (cd sub && git reset --hard && echo >foo && git add foo) &&
        git status >output &&
-       test_i18ngrep "modified:   sub (new commits, modified content)" output
+       test_grep "modified:   sub (new commits, modified content)" output
 '
 
 test_expect_success 'status with a lot of untracked files in the submodule' '
@@ -234,12 +234,12 @@ test_expect_success 'rm submodule contents' '
 
 test_expect_success 'status clean (empty submodule dir)' '
        git status >output &&
-       test_i18ngrep "nothing to commit" output
+       test_grep "nothing to commit" output
 '
 
 test_expect_success 'status -a clean (empty submodule dir)' '
        test_must_fail git commit --dry-run -a >output &&
-       test_i18ngrep "nothing to commit" output
+       test_grep "nothing to commit" output
 '
 
 cat >status_expect <<\EOF
index 916470c48bd69cb245eaf0e9045cd3f1519b7ba6..c3281b192e49ce130d3babc4f9fda39acd3d69b8 100755 (executable)
@@ -89,7 +89,7 @@ test_expect_success 'submodule log is stripped out too with -v' '
                export GIT_EDITOR &&
                test_must_fail git commit -a -v 2>err
        ) &&
-       test_i18ngrep "Aborting commit due to empty commit message." err
+       test_grep "Aborting commit due to empty commit message." err
 '
 
 test_expect_success 'verbose diff is stripped out with set core.commentChar' '
@@ -98,7 +98,7 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' '
                export GIT_EDITOR &&
                test_must_fail git -c core.commentchar=";" commit -a -v 2>err
        ) &&
-       test_i18ngrep "Aborting commit due to empty commit message." err
+       test_grep "Aborting commit due to empty commit message." err
 '
 
 test_expect_success 'status does not verbose without --verbose' '
index 2b7ef6c41a455423c0c62087a440e51bc4c72486..a3c18a4fc2764aa669556fe56961d254727bdab5 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='git status'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -18,7 +19,7 @@ test_expect_success 'status -h in broken repository' '
                echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
                test_expect_code 129 git status -h >usage 2>&1
        ) &&
-       test_i18ngrep "[Uu]sage" broken/usage
+       test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'commit -h in broken repository' '
@@ -30,7 +31,7 @@ test_expect_success 'commit -h in broken repository' '
                echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
                test_expect_code 129 git commit -h >usage 2>&1
        ) &&
-       test_i18ngrep "[Uu]sage" broken/usage
+       test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'create upstream branch' '
@@ -71,7 +72,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'status (1)' '
-       test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+       test_grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
 '
 
 strip_comments () {
@@ -91,7 +92,7 @@ test_expect_success 'status --column' '
 # On branch main
 # Your branch and '\''upstream'\'' have diverged,
 # and have 1 and 2 different commits each, respectively.
-#   (use "git pull" to merge the remote branch into yours)
+#   (use "git pull" if you want to integrate the remote branch with yours)
 #
 # Changes to be committed:
 #   (use "git restore --staged <file>..." to unstage)
@@ -122,7 +123,7 @@ cat >expect <<\EOF
 # On branch main
 # Your branch and 'upstream' have diverged,
 # and have 1 and 2 different commits each, respectively.
-#   (use "git pull" to merge the remote branch into yours)
+#   (use "git pull" if you want to integrate the remote branch with yours)
 #
 # Changes to be committed:
 #   (use "git restore --staged <file>..." to unstage)
@@ -269,7 +270,7 @@ test_expect_success 'status with gitignore' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -334,7 +335,7 @@ test_expect_success 'status with gitignore (nothing untracked)' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -404,7 +405,7 @@ test_expect_success 'status -uno' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -466,7 +467,7 @@ test_expect_success 'status -unormal' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -521,7 +522,7 @@ test_expect_success 'status -uall' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -581,7 +582,7 @@ test_expect_success 'status with relative paths' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -649,7 +650,7 @@ test_expect_success TTY 'status with color.ui' '
 On branch <GREEN>main<RESET>
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -772,7 +773,7 @@ test_expect_success 'status without relative paths' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -846,7 +847,6 @@ test_expect_success 'dry-run of partial commit excluding new file in index' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -900,7 +900,7 @@ test_expect_success 'status submodule summary is disabled by default' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -957,7 +957,7 @@ test_expect_success 'status submodule summary' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1012,11 +1012,11 @@ test_expect_success 'status -s submodule summary' '
 '
 
 test_expect_success 'status submodule summary (clean submodule): commit' '
-       cat >expect <<EOF &&
+       cat >expect-status <<EOF &&
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
@@ -1032,12 +1032,13 @@ Untracked files:
 
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
+       sed "/git pull/d" expect-status > expect-commit &&
        git commit -m "commit submodule" &&
        git config status.submodulesummary 10 &&
        test_must_fail git commit --dry-run >output &&
-       test_cmp expect output &&
+       test_cmp expect-commit output &&
        git status >output &&
-       test_cmp expect output
+       test_cmp expect-status output
 '
 
 cat >expect <<EOF
@@ -1064,7 +1065,6 @@ test_expect_success 'commit --dry-run submodule summary (--amend)' '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
   (use "git restore --source=HEAD^1 --staged <file>..." to unstage)
@@ -1116,7 +1116,7 @@ test_expect_success '--ignore-submodules=untracked suppresses submodules with un
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1225,7 +1225,7 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodules w
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1282,7 +1282,7 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodule su
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1363,7 +1363,7 @@ cat > expect << EOF
 ; On branch main
 ; Your branch and 'upstream' have diverged,
 ; and have 2 and 2 different commits each, respectively.
-;   (use "git pull" to merge the remote branch into yours)
+;   (use "git pull" if you want to integrate the remote branch with yours)
 ;
 ; Changes to be committed:
 ;   (use "git restore --staged <file>..." to unstage)
@@ -1411,7 +1411,7 @@ test_expect_success "--ignore-submodules=all suppresses submodule summary" '
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
@@ -1437,7 +1437,7 @@ test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summar
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1519,8 +1519,8 @@ test_expect_success '"status.branch=true" weaker than "--no-branch"' '
 '
 
 test_expect_success '"status.branch=true" weaker than "--porcelain"' '
-       git -c status.branch=true status --porcelain >actual &&
-       test_cmp expected_nobranch actual
+       git -c status.branch=true status --porcelain >actual &&
+       test_cmp expected_nobranch actual
 '
 
 test_expect_success '"status.branch=false" same as "--no-branch"' '
@@ -1542,12 +1542,12 @@ test_expect_success 'git commit will commit a staged but ignored submodule' '
        git config --add -f .gitmodules submodule.subname.path sm &&
        git config --add submodule.subname.ignore all &&
        git status -s --ignore-submodules=dirty >output &&
-       test_i18ngrep "^M. sm" output &&
+       test_grep "^M. sm" output &&
        GIT_EDITOR="echo hello >>\"\$1\"" &&
        export GIT_EDITOR &&
        git commit -uno &&
        git status -s --ignore-submodules=dirty >output &&
-       test_i18ngrep ! "^M. sm" output
+       test_grep ! "^M. sm" output
 '
 
 test_expect_success 'git commit --dry-run will show a staged but ignored submodule' '
@@ -1557,7 +1557,6 @@ test_expect_success 'git commit --dry-run will show a staged but ignored submodu
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1573,13 +1572,13 @@ EOF
        git commit -uno --dry-run >output &&
        test_cmp expect output &&
        git status -s --ignore-submodules=dirty >output &&
-       test_i18ngrep "^M. sm" output
+       test_grep "^M. sm" output
 '
 
 test_expect_success 'git commit -m will commit a staged but ignored submodule' '
        git commit -uno -m message &&
        git status -s --ignore-submodules=dirty >output &&
-       test_i18ngrep ! "^M. sm" output &&
+       test_grep ! "^M. sm" output &&
        git config --remove-section submodule.subname &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1592,7 +1591,7 @@ test_expect_success 'show stash info with "--show-stash"' '
        git stash &&
        git status >expected_default &&
        git status --show-stash >expected_with_stash &&
-       test_i18ngrep "^Your stash currently has 1 entry$" expected_with_stash
+       test_grep "^Your stash currently has 1 entry$" expected_with_stash
 '
 
 test_expect_success 'no stash info with "--show-stash --no-show-stash"' '
@@ -1619,14 +1618,14 @@ test_expect_success 'no additional info if no stash entries' '
 test_expect_success '"No commits yet" should be noted in status output' '
        git checkout --orphan empty-branch-1 &&
        git status >output &&
-       test_i18ngrep "No commits yet" output
+       test_grep "No commits yet" output
 '
 
 test_expect_success '"No commits yet" should not be noted in status output' '
        git checkout --orphan empty-branch-2 &&
        test_commit test-commit-1 &&
        git status >output &&
-       test_i18ngrep ! "No commits yet" output
+       test_grep ! "No commits yet" output
 '
 
 test_expect_success '"Initial commit" should be noted in commit template' '
@@ -1634,7 +1633,7 @@ test_expect_success '"Initial commit" should be noted in commit template' '
        touch to_be_committed_1 &&
        git add to_be_committed_1 &&
        git commit --dry-run >output &&
-       test_i18ngrep "Initial commit" output
+       test_grep "Initial commit" output
 '
 
 test_expect_success '"Initial commit" should not be noted in commit template' '
@@ -1643,7 +1642,7 @@ test_expect_success '"Initial commit" should not be noted in commit template' '
        touch to_be_committed_2 &&
        git add to_be_committed_2 &&
        git commit --dry-run >output &&
-       test_i18ngrep ! "Initial commit" output
+       test_grep ! "Initial commit" output
 '
 
 test_expect_success '--no-optional-locks prevents index update' '
@@ -1676,4 +1675,90 @@ test_expect_success 'racy timestamps will be fixed for dirty worktree' '
        ! test_is_magic_mtime .git/index
 '
 
+test_expect_success 'setup slow status advice' '
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main git init slowstatus &&
+       (
+               cd slowstatus &&
+               cat >.gitignore <<-\EOF &&
+               /actual
+               /expected
+               /out
+               EOF
+               git add .gitignore &&
+               git commit -m "Add .gitignore" &&
+               git config advice.statusuoption true
+       )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache and fsmonitor are unset' '
+       (
+               cd slowstatus &&
+               git config core.untrackedCache false &&
+               git config core.fsmonitor false &&
+               GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+               cat >expected <<-\EOF &&
+               On branch main
+
+               It took 3.25 seconds to enumerate untracked files.
+               See '\''git help status'\'' for information on how to improve this.
+
+               nothing to commit, working tree clean
+               EOF
+               test_cmp expected actual
+       )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache true, but not fsmonitor' '
+       (
+               cd slowstatus &&
+               git config core.untrackedCache true &&
+               git config core.fsmonitor false &&
+               GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+               cat >expected <<-\EOF &&
+               On branch main
+
+               It took 3.25 seconds to enumerate untracked files.
+               See '\''git help status'\'' for information on how to improve this.
+
+               nothing to commit, working tree clean
+               EOF
+               test_cmp expected actual
+       )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache true, and fsmonitor' '
+       (
+               cd slowstatus &&
+               git config core.untrackedCache true &&
+               git config core.fsmonitor true &&
+               GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+               cat >expected <<-\EOF &&
+               On branch main
+
+               It took 3.25 seconds to enumerate untracked files,
+               but the results were cached, and subsequent runs may be faster.
+               See '\''git help status'\'' for information on how to improve this.
+
+               nothing to commit, working tree clean
+               EOF
+               test_cmp expected actual
+       )
+'
+
+test_expect_success EXPENSIVE 'status does not re-read unchanged 4 or 8 GiB file' '
+       (
+               mkdir large-file &&
+               cd large-file &&
+               # Files are 2 GiB, 4 GiB, and 8 GiB sparse files.
+               test-tool truncate file-a 0x080000000 &&
+               test-tool truncate file-b 0x100000000 &&
+               test-tool truncate file-c 0x200000000 &&
+               # This will be slow.
+               git add file-a file-b file-c &&
+               git commit -m "add large files" &&
+               git diff-index HEAD file-a file-b file-c >actual &&
+               test_must_be_empty actual
+       )
+'
+
 test_done
index 21c668f75ed7345430f88f65b7e9072a873b9c42..fd8c8f8f0bccf81eed7711e178912753ac200d72 100755 (executable)
@@ -99,20 +99,20 @@ test_expect_success '--amend option with empty author' '
        echo "Empty author test" >>foo &&
        test_tick &&
        test_must_fail git commit -a -m "empty author" --amend 2>err &&
-       test_i18ngrep "empty ident" err
+       test_grep "empty ident" err
 '
 
 test_expect_success '--amend option with missing author' '
        git cat-file commit Initial >tmp &&
        sed "s/author [^<]* </author </" tmp >malformed &&
-       sha=$(git hash-object -t commit -w malformed) &&
+       sha=$(git hash-object --literally -t commit -w malformed) &&
        test_when_finished "remove_object $sha" &&
        git checkout $sha &&
        test_when_finished "git checkout Initial" &&
        echo "Missing author test" >>foo &&
        test_tick &&
        test_must_fail git commit -a -m "malformed author" --amend 2>err &&
-       test_i18ngrep "empty ident" err
+       test_grep "empty ident" err
 '
 
 test_expect_success '--reset-author makes the commit ours even with --amend option' '
index 8593b7e3cb8d9aa0c4badeef4273ed406cecc9ca..0d2dd29fe6a12c20ffbbe169afabd4c31bc6545c 100755 (executable)
@@ -202,7 +202,7 @@ test_expect_success GPG 'detect fudged signature with NUL' '
        git cat-file commit seventh-signed >raw &&
        cat raw >forged2 &&
        echo Qwik | tr "Q" "\000" >>forged2 &&
-       git hash-object -w -t commit forged2 >forged2.commit &&
+       git hash-object --literally -w -t commit forged2 >forged2.commit &&
        test_must_fail git verify-commit $(cat forged2.commit) &&
        git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
        grep "BAD signature from" actual2 &&
@@ -218,87 +218,101 @@ test_expect_success GPG 'amending already signed commit' '
        ! grep "BAD signature from" actual
 '
 
+test_expect_success GPG2 'bare signature' '
+       git verify-commit fifth-signed 2>expect &&
+       echo >>expect &&
+       git log -1 --format="%GG" fifth-signed >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success GPG 'show good signature with custom format' '
        cat >expect <<-\EOF &&
        G
+       ultimate
        13B6F51ECDDE430D
        C O Mitter <committer@example.com>
        73D758744BE721698EC54E8713B6F51ECDDE430D
        73D758744BE721698EC54E8713B6F51ECDDE430D
        EOF
-       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+       git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
        test_cmp expect actual
 '
 
 test_expect_success GPG 'show bad signature with custom format' '
        cat >expect <<-\EOF &&
        B
+       undefined
        13B6F51ECDDE430D
        C O Mitter <committer@example.com>
 
 
        EOF
-       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
+       git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
        test_cmp expect actual
 '
 
 test_expect_success GPG 'show untrusted signature with custom format' '
        cat >expect <<-\EOF &&
        U
+       undefined
        65A0EEA02E30CAD7
        Eris Discordia <discord@example.net>
        F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
        D4BE22311AD3131E5EDA29A461092E85B7227189
        EOF
-       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+       git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
        test_cmp expect actual
 '
 
 test_expect_success GPG 'show untrusted signature with undefined trust level' '
        cat >expect <<-\EOF &&
+       U
        undefined
        65A0EEA02E30CAD7
        Eris Discordia <discord@example.net>
        F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
        D4BE22311AD3131E5EDA29A461092E85B7227189
        EOF
-       git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+       git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
        test_cmp expect actual
 '
 
 test_expect_success GPG 'show untrusted signature with ultimate trust level' '
        cat >expect <<-\EOF &&
+       G
        ultimate
        13B6F51ECDDE430D
        C O Mitter <committer@example.com>
        73D758744BE721698EC54E8713B6F51ECDDE430D
        73D758744BE721698EC54E8713B6F51ECDDE430D
        EOF
-       git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+       git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
        test_cmp expect actual
 '
 
 test_expect_success GPG 'show unknown signature with custom format' '
        cat >expect <<-\EOF &&
        E
+       undefined
        65A0EEA02E30CAD7
 
 
 
        EOF
-       GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+       GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
        test_cmp expect actual
 '
 
 test_expect_success GPG 'show lack of signature with custom format' '
        cat >expect <<-\EOF &&
        N
+       undefined
 
 
 
 
        EOF
-       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
+       git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
        test_cmp expect actual
 '
 
@@ -387,4 +401,48 @@ test_expect_success GPG 'verify-commit verifies multiply signed commits' '
        ! grep "BAD signature from" actual
 '
 
+test_expect_success 'custom `gpg.program`' '
+       write_script fake-gpg <<-\EOF &&
+       args="$*"
+
+       # skip uninteresting options
+       while case "$1" in
+       --status-fd=*|--keyid-format=*) ;; # skip
+       *) break;;
+       esac; do shift; done
+
+       case "$1" in
+       -bsau)
+               test -z "$LET_GPG_PROGRAM_FAIL" || {
+                       echo "zOMG signing failed!" >&2
+                       exit 1
+               }
+               cat >sign.file
+               echo "[GNUPG:] SIG_CREATED $args" >&2
+               echo "-----BEGIN PGP MESSAGE-----"
+               echo "$args"
+               echo "-----END PGP MESSAGE-----"
+               ;;
+       --verify)
+               cat "$2" >verify.file
+               exit 0
+               ;;
+       *)
+               echo "Unhandled args: $*" >&2
+               exit 1
+               ;;
+       esac
+       EOF
+
+       test_config gpg.program "$(pwd)/fake-gpg" &&
+       git commit -S --allow-empty -m signed-commit &&
+       test_path_exists sign.file &&
+       git show --show-signature &&
+       test_path_exists verify.file &&
+
+       test_must_fail env LET_GPG_PROGRAM_FAIL=1 \
+       git commit -S --allow-empty -m must-fail 2>err &&
+       grep zOMG err
+'
+
 test_done
index 2f16d5787edfb16956312a3be70fb9da1ffadc46..802f8f704c62eb11192bcc82ecd35f0ae53b5a6a 100755 (executable)
@@ -692,6 +692,34 @@ EOF
 '
 
 
+test_expect_success 'status when bisecting while rebasing' '
+       git reset --hard main &&
+       test_when_finished "git rebase --abort" &&
+       ONTO=$(git rev-parse --short HEAD^) &&
+       FAKE_LINES="break" git rebase -i HEAD^ &&
+       test_when_finished "git checkout -" &&
+       git checkout -b bisect_while_rebasing &&
+       test_when_finished "git bisect reset" &&
+       git bisect start &&
+       cat >expected <<EOF &&
+On branch bisect_while_rebasing
+Last command done (1 command done):
+   break
+No commands remaining.
+You are currently editing a commit while rebasing branch '\''bisect'\'' on '\''$ONTO'\''.
+  (use "git commit --amend" to amend the current commit)
+  (use "git rebase --continue" once you are satisfied with your changes)
+
+You are currently bisecting, started from branch '\''bisect_while_rebasing'\''.
+  (use "git bisect reset" to get back to the original branch)
+
+nothing to commit (use -u to show untracked files)
+EOF
+       git status --untracked-files=no >actual &&
+       test_cmp expected actual
+'
+
+
 test_expect_success 'status when rebase --apply conflicts with statushints disabled' '
        git reset --hard main &&
        git checkout -b statushints_disabled &&
@@ -774,6 +802,28 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'status when cherry-picking multiple commits' '
+       git reset --hard cherry_branch &&
+       test_when_finished "git cherry-pick --abort" &&
+       test_must_fail git cherry-pick cherry_branch_second one_cherry &&
+       TO_CHERRY_PICK=$(git rev-parse --short CHERRY_PICK_HEAD) &&
+       cat >expected <<EOF &&
+On branch cherry_branch
+You are currently cherry-picking commit $TO_CHERRY_PICK.
+  (fix conflicts and run "git cherry-pick --continue")
+  (use "git cherry-pick --skip" to skip this patch)
+  (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+Unmerged paths:
+  (use "git add <file>..." to mark resolution)
+       both modified:   main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+       git status --untracked-files=no >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'status when cherry-picking after committing conflict resolution' '
        git reset --hard cherry_branch &&
        test_when_finished "git cherry-pick --abort" &&
index 97f10905d23fd3077aa9dd253fa079eb9c5be73d..832aff0616736025cb78d2b35e3823594b543b1d 100755 (executable)
@@ -489,7 +489,7 @@ test_expect_success 'multiline field treated as atomic for neighbor check' '
 '
 
 test_expect_success 'with config setup' '
-       git config trailer.ack.key "Acked-by: " &&
+       test_config trailer.ack.key "Acked-by: " &&
        cat >expected <<-\EOF &&
 
                Acked-by: Peff
@@ -503,8 +503,8 @@ test_expect_success 'with config setup' '
 '
 
 test_expect_success 'with config setup and ":=" as separators' '
-       git config trailer.separators ":=" &&
-       git config trailer.ack.key "Acked-by= " &&
+       test_config trailer.separators ":=" &&
+       test_config trailer.ack.key "Acked-by= " &&
        cat >expected <<-\EOF &&
 
                Acked-by= Peff
@@ -518,7 +518,7 @@ test_expect_success 'with config setup and ":=" as separators' '
 '
 
 test_expect_success 'with config setup and "%" as separators' '
-       git config trailer.separators "%" &&
+       test_config trailer.separators "%" &&
        cat >expected <<-\EOF &&
 
                bug% 42
@@ -532,6 +532,7 @@ test_expect_success 'with config setup and "%" as separators' '
 '
 
 test_expect_success 'with "%" as separators and a message with trailers' '
+       test_config trailer.separators "%" &&
        cat >special_message <<-\EOF &&
                Special Message
 
@@ -553,8 +554,8 @@ test_expect_success 'with "%" as separators and a message with trailers' '
 '
 
 test_expect_success 'with config setup and ":=#" as separators' '
-       git config trailer.separators ":=#" &&
-       git config trailer.bug.key "Bug #" &&
+       test_config trailer.separators ":=#" &&
+       test_config trailer.bug.key "Bug #" &&
        cat >expected <<-\EOF &&
 
                Bug #42
@@ -581,6 +582,8 @@ test_expect_success 'with basic patch' '
 '
 
 test_expect_success 'with commit complex message as argument' '
+       test_config trailer.separators ":=" &&
+       test_config trailer.ack.key "Acked-by= " &&
        cat complex_message_body complex_message_trailers >complex_message &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
@@ -594,6 +597,8 @@ test_expect_success 'with commit complex message as argument' '
 '
 
 test_expect_success 'with 2 files arguments' '
+       test_config trailer.separators ":=" &&
+       test_config trailer.ack.key "Acked-by= " &&
        cat basic_message >>expected &&
        echo >>expected &&
        cat basic_patch >>expected &&
@@ -677,6 +682,9 @@ test_expect_success 'with message that has an old style conflict block' '
 '
 
 test_expect_success 'with commit complex message and trailer args' '
+       test_config trailer.separators ":=#" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -692,6 +700,9 @@ test_expect_success 'with commit complex message and trailer args' '
 '
 
 test_expect_success 'with complex patch, args and --trim-empty' '
+       test_config trailer.separators ":=#" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
        cat complex_message >complex_patch &&
        cat basic_patch >>complex_patch &&
        cat complex_message_body >expected &&
@@ -746,7 +757,10 @@ test_expect_success POSIXPERM,SANITY "in-place editing doesn't clobber original
 '
 
 test_expect_success 'using "where = before"' '
-       git config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -762,7 +776,9 @@ test_expect_success 'using "where = before"' '
 '
 
 test_expect_success 'overriding configuration with "--where after"' '
-       git config trailer.ack.where "before" &&
+       test_config trailer.separators ":=" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "before" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -776,7 +792,12 @@ test_expect_success 'overriding configuration with "--where after"' '
        test_cmp expected actual
 '
 
-test_expect_success 'using "where = before" with "--no-where"' '
+test_expect_success 'using "--where after" with "--no-where"' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "before" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -791,8 +812,59 @@ test_expect_success 'using "where = before" with "--no-where"' '
        test_cmp expected actual
 '
 
+# Check whether using "--no-where" clears out only the "--where after", such
+# that we still use the configuration in trailer.where (which is different from
+# the hardcoded default (in WHERE_END) assuming the absence of .gitconfig).
+# Here, the "start" setting of trailer.where is respected, so the new "Acked-by"
+# and "Bug" trailers are placed at the beginning, and not at the end which is
+# the harcoded default.
+test_expect_success 'using "--where after" with "--no-where" defaults to configuration' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.separators ":=#" &&
+       test_config trailer.where "start" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Bug #42
+               Acked-by= Peff
+               Fixes: Z
+               Acked-by= Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+       EOF
+       git interpret-trailers --where after --no-where --trailer "ack: Peff" \
+               --trailer "bug: 42" complex_message >actual &&
+       test_cmp expected actual
+'
+
+# The "--where after" will only get respected for the trailer that came
+# immediately after it. For the next trailer (Bug #42), we default to using the
+# hardcoded WHERE_END because we don't have any "trailer.where" or
+# "trailer.bug.where" configured.
+test_expect_success 'using "--no-where" defaults to harcoded default if nothing configured' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.separators ":=#" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Fixes: Z
+               Acked-by= Z
+               Acked-by= Peff
+               Reviewed-by: Z
+               Signed-off-by: Z
+               Bug #42
+       EOF
+       git interpret-trailers --where after --trailer "ack: Peff" --no-where \
+               --trailer "bug: 42" complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'using "where = after"' '
-       git config trailer.ack.where "after" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -808,8 +880,11 @@ test_expect_success 'using "where = after"' '
 '
 
 test_expect_success 'using "where = end"' '
-       git config trailer.review.key "Reviewed-by" &&
-       git config trailer.review.where "end" &&
+       test_config trailer.review.key "Reviewed-by" &&
+       test_config trailer.review.where "end" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -827,8 +902,11 @@ test_expect_success 'using "where = end"' '
 '
 
 test_expect_success 'using "where = start"' '
-       git config trailer.review.key "Reviewed-by" &&
-       git config trailer.review.where "start" &&
+       test_config trailer.review.key "Reviewed-by" &&
+       test_config trailer.review.where "start" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Reviewed-by: Johannes
@@ -846,8 +924,13 @@ test_expect_success 'using "where = start"' '
 '
 
 test_expect_success 'using "where = before" for a token in the middle of the message' '
-       git config trailer.review.key "Reviewed-by:" &&
-       git config trailer.review.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.review.where "before" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -864,6 +947,12 @@ test_expect_success 'using "where = before" for a token in the middle of the mes
 '
 
 test_expect_success 'using "where = before" and --trim-empty' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        cat >>expected <<-\EOF &&
                Bug #46
@@ -878,6 +967,13 @@ test_expect_success 'using "where = before" and --trim-empty' '
 '
 
 test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.review.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -896,7 +992,13 @@ test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
 '
 
 test_expect_success 'default "ifExists" is now "addIfDifferent"' '
-       git config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -914,8 +1016,14 @@ test_expect_success 'default "ifExists" is now "addIfDifferent"' '
 '
 
 test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
-       git config trailer.ack.ifExists "addIfDifferent" &&
-       git config trailer.ack.where "end" &&
+       test_config trailer.ack.ifExists "addIfDifferent" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "end" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -932,8 +1040,14 @@ test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
 '
 
 test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
-       git config trailer.ack.ifExists "addIfDifferent" &&
-       git config trailer.ack.where "before" &&
+       test_config trailer.ack.ifExists "addIfDifferent" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "before" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -950,8 +1064,14 @@ test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end"' '
-       git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
-       git config trailer.ack.where "end" &&
+       test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "end" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -973,8 +1093,14 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor"  with "where = after"' '
-       git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
-       git config trailer.ack.where "after" &&
+       test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -995,7 +1121,11 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor"  with "where = af
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' '
-       git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        cat >>expected <<-\EOF &&
                Bug #42
@@ -1011,8 +1141,14 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty'
 '
 
 test_expect_success 'using "ifExists = add" with "where = end"' '
-       git config trailer.ack.ifExists "add" &&
-       git config trailer.ack.where "end" &&
+       test_config trailer.ack.ifExists "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "end" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1036,8 +1172,14 @@ test_expect_success 'using "ifExists = add" with "where = end"' '
 '
 
 test_expect_success 'using "ifExists = add" with "where = after"' '
-       git config trailer.ack.ifExists "add" &&
-       git config trailer.ack.where "after" &&
+       test_config trailer.ack.ifExists "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1058,8 +1200,15 @@ test_expect_success 'using "ifExists = add" with "where = after"' '
 '
 
 test_expect_success 'overriding configuration with "--if-exists replace"' '
-       git config trailer.fix.key "Fixes: " &&
-       git config trailer.fix.ifExists "add" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.review.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1074,9 +1223,66 @@ test_expect_success 'overriding configuration with "--if-exists replace"' '
        test_cmp expected actual
 '
 
+# "trailer.ifexists" is set to "doNothing", so using "--no-if-exists" defaults
+# to this "doNothing" behavior. So the "Fixes: 53" trailer does not get added.
+test_expect_success 'using "--if-exists replace" with "--no-if-exists" defaults to configuration' '
+       test_config trailer.ifexists "doNothing" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Fixes: Z
+               Acked-by: Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+       EOF
+       git interpret-trailers --if-exists replace --no-if-exists --trailer "Fixes: 53" \
+               <complex_message >actual &&
+       test_cmp expected actual
+'
+
+# No "ifexists" configuration is set, so using "--no-if-exists" makes it default
+# to addIfDifferentNeighbor. Because we do have a different neighbor "Fixes: 53"
+# (because it got added by overriding with "--if-exists replace" earlier in the
+# arguments list), we add "Signed-off-by: addme".
+test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured' '
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Acked-by: Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+               Fixes: 53
+               Signed-off-by: addme
+       EOF
+       git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \
+               --trailer "Signed-off-by: addme" <complex_message >actual &&
+       test_cmp expected actual
+'
+
+# The second "Fixes: 53" trailer is discarded, because the "--no-if-exists" here
+# makes us default to addIfDifferentNeighbor, and we already added the "Fixes:
+# 53" trailer earlier in the argument list.
+test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured (no addition)' '
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Acked-by: Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+               Fixes: 53
+       EOF
+       git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \
+               --trailer "Fixes: 53" <complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'using "ifExists = replace"' '
-       git config trailer.fix.key "Fixes: " &&
-       git config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1095,7 +1301,16 @@ test_expect_success 'using "ifExists = replace"' '
 '
 
 test_expect_success 'using "ifExists = replace" with "where = after"' '
-       git config trailer.fix.where "after" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.where "after" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1114,7 +1329,15 @@ test_expect_success 'using "ifExists = replace" with "where = after"' '
 '
 
 test_expect_success 'using "ifExists = doNothing"' '
-       git config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1133,8 +1356,17 @@ test_expect_success 'using "ifExists = doNothing"' '
 '
 
 test_expect_success 'the default is "ifMissing = add"' '
-       git config trailer.cc.key "Cc: " &&
-       git config trailer.cc.where "before" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.cc.key "Cc: " &&
+       test_config trailer.cc.where "before" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1154,7 +1386,14 @@ test_expect_success 'the default is "ifMissing = add"' '
 '
 
 test_expect_success 'overriding configuration with "--if-missing doNothing"' '
-       git config trailer.ifmissing "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.ifmissing "add" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1173,7 +1412,13 @@ test_expect_success 'overriding configuration with "--if-missing doNothing"' '
 '
 
 test_expect_success 'when default "ifMissing" is "doNothing"' '
-       git config trailer.ifmissing "doNothing" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.ifmissing "doNothing" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1187,14 +1432,21 @@ test_expect_success 'when default "ifMissing" is "doNothing"' '
                --trailer "cc=Linus" --trailer "ack: Junio" \
                --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
                <complex_message >actual &&
-       test_cmp expected actual &&
-       git config trailer.ifmissing "add"
+       test_cmp expected actual
 '
 
 test_expect_success 'using "ifMissing = add" with "where = end"' '
-       git config trailer.cc.key "Cc: " &&
-       git config trailer.cc.where "end" &&
-       git config trailer.cc.ifMissing "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.cc.key "Cc: " &&
+       test_config trailer.cc.ifMissing "add" &&
+       test_config trailer.cc.where "end" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1214,9 +1466,17 @@ test_expect_success 'using "ifMissing = add" with "where = end"' '
 '
 
 test_expect_success 'using "ifMissing = add" with "where = before"' '
-       git config trailer.cc.key "Cc: " &&
-       git config trailer.cc.where "before" &&
-       git config trailer.cc.ifMissing "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.cc.key "Cc: " &&
+       test_config trailer.cc.ifMissing "add" &&
+       test_config trailer.cc.where "before" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Cc: Linus
@@ -1236,7 +1496,15 @@ test_expect_success 'using "ifMissing = add" with "where = before"' '
 '
 
 test_expect_success 'using "ifMissing = doNothing"' '
-       git config trailer.cc.ifMissing "doNothing" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.cc.ifMissing "doNothing" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1254,9 +1522,50 @@ test_expect_success 'using "ifMissing = doNothing"' '
        test_cmp expected actual
 '
 
+# Ignore the "IgnoredTrailer" because of "--if-missing doNothing", but also
+# ignore the "StillIgnoredTrailer" because we set "trailer.ifMissing" to
+# "doNothing" in configuration.
+test_expect_success 'using "--no-if-missing" defaults to configuration' '
+       test_config trailer.ifMissing "doNothing" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+                       Fixes: Z
+                       Acked-by: Z
+                       Reviewed-by: Z
+                       Signed-off-by: Z
+       EOF
+       git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \
+                       --trailer "StillIgnoredTrailer: ignoreme" <complex_message >actual &&
+       test_cmp expected actual
+'
+
+# Add the "AddedTrailer" because the "--no-if-missing" clears the "--if-missing
+# doNothing" from earlier in the argument list.
+test_expect_success 'using "--no-if-missing" defaults to hardcoded default if nothing configured' '
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+                       Fixes: Z
+                       Acked-by: Z
+                       Reviewed-by: Z
+                       Signed-off-by: Z
+                       AddedTrailer: addme
+       EOF
+       git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \
+                       --trailer "AddedTrailer: addme" <complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'default "where" is now "after"' '
        git config trailer.where "after" &&
-       git config --unset trailer.ack.where &&
+       test_config trailer.ack.ifExists "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1280,10 +1589,15 @@ test_expect_success 'default "where" is now "after"' '
 '
 
 test_expect_success 'with simple command' '
-       git config trailer.sign.key "Signed-off-by: " &&
-       git config trailer.sign.where "after" &&
-       git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
-       git config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.sign.where "after" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1298,8 +1612,14 @@ test_expect_success 'with simple command' '
 '
 
 test_expect_success 'with command using committer information' '
-       git config trailer.sign.ifExists "addIfDifferent" &&
-       git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.sign.ifExists "addIfDifferent" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1314,10 +1634,15 @@ test_expect_success 'with command using committer information' '
 '
 
 test_expect_success 'with command using author information' '
-       git config trailer.sign.key "Signed-off-by: " &&
-       git config trailer.sign.where "after" &&
-       git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
-       git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.sign.where "after" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1338,12 +1663,19 @@ test_expect_success 'setup a commit' '
 '
 
 test_expect_success 'cmd takes precedence over command' '
-       test_when_finished "git config --unset trailer.fix.cmd" &&
-       git config trailer.fix.ifExists "replace" &&
-       git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
-       --abbrev-commit --abbrev=14 \"\$1\" || true" &&
-       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
                --abbrev-commit --abbrev=14 \$ARG" &&
+       test_config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+       --abbrev-commit --abbrev=14 \"\$1\" || true" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.where "after" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
        cat complex_message_body >expected2 &&
        sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
@@ -1359,8 +1691,16 @@ test_expect_success 'cmd takes precedence over command' '
 '
 
 test_expect_success 'with command using $ARG' '
-       git config trailer.fix.ifExists "replace" &&
-       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.where "after" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-EOF &&
@@ -1376,8 +1716,16 @@ test_expect_success 'with command using $ARG' '
 '
 
 test_expect_success 'with failing command using $ARG' '
-       git config trailer.fix.ifExists "replace" &&
-       git config trailer.fix.command "false \$ARG" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.command "false \$ARG" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.where "after" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-EOF &&
                Fixes: Z
@@ -1392,7 +1740,9 @@ test_expect_success 'with failing command using $ARG' '
 '
 
 test_expect_success 'with empty tokens' '
-       git config --unset trailer.fix.command &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.ifexists "addIfDifferent" &&
        cat >expected <<-EOF &&
 
                Signed-off-by: A U Thor <author@example.com>
@@ -1403,7 +1753,8 @@ test_expect_success 'with empty tokens' '
 '
 
 test_expect_success 'with command but no key' '
-       git config --unset trailer.sign.key &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.ifexists "addIfDifferent" &&
        cat >expected <<-EOF &&
 
                sign: A U Thor <author@example.com>
@@ -1414,7 +1765,9 @@ test_expect_success 'with command but no key' '
 '
 
 test_expect_success 'with no command and no key' '
-       git config --unset trailer.review.key &&
+       test_config trailer.review.where "before" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.ifexists "addIfDifferent" &&
        cat >expected <<-EOF &&
 
                review: Junio
@@ -1426,6 +1779,8 @@ test_expect_success 'with no command and no key' '
 '
 
 test_expect_success 'with cut line' '
+       test_config trailer.review.where "before" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
        cat >expected <<-\EOF &&
                my subject
 
@@ -1443,7 +1798,8 @@ test_expect_success 'with cut line' '
 '
 
 test_expect_success 'only trailers' '
-       git config trailer.sign.command "echo config-value" &&
+       test_config trailer.sign.command "echo config-value" &&
+       test_config trailer.ifexists "addIfDifferent" &&
        cat >expected <<-\EOF &&
                existing: existing-value
                sign: config-value
@@ -1462,7 +1818,7 @@ test_expect_success 'only trailers' '
 '
 
 test_expect_success 'only-trailers omits non-trailer in middle of block' '
-       git config trailer.sign.command "echo config-value" &&
+       test_config trailer.sign.command "echo config-value" &&
        cat >expected <<-\EOF &&
                Signed-off-by: nobody <nobody@nowhere>
                Signed-off-by: somebody <somebody@somewhere>
@@ -1482,7 +1838,7 @@ test_expect_success 'only-trailers omits non-trailer in middle of block' '
 '
 
 test_expect_success 'only input' '
-       git config trailer.sign.command "echo config-value" &&
+       test_config trailer.sign.command "echo config-value" &&
        cat >expected <<-\EOF &&
                existing: existing-value
        EOF
index f2ce14e9071c0a3b071f53186c2d311103883443..bb95f09810b704b552352d90b6ec2a046c7d0fc1 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git commit races'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'race to create orphan commit' '
@@ -10,7 +12,8 @@ test_expect_success 'race to create orphan commit' '
        test_must_fail env EDITOR=./hare-editor git commit --allow-empty -m tortoise -e &&
        git show -s --pretty=format:%s >subject &&
        grep hare subject &&
-       test -z "$(git show -s --pretty=format:%P)"
+       git show -s --pretty=format:%P >out &&
+       test_must_be_empty out
 '
 
 test_expect_success 'race to create non-orphan commit' '
index 163ae8046850e729ef5e329f6589faf5381e9cdc..efc6496e2b27f3ec74431d625e64e52c384e0383 100755 (executable)
@@ -9,6 +9,7 @@ test_description='per-repo forced setting of email address'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a likely user.useConfigOnly use case' '
index fffdb6ff2e751d2441e8f805c475a044bfdd0920..b37de0af49ff400e89b86271dd9b63b894dad56e 100755 (executable)
@@ -15,15 +15,24 @@ test_expect_success 'empty name and missing email' '
                sane_unset GIT_AUTHOR_EMAIL &&
                GIT_AUTHOR_NAME= &&
                test_must_fail git commit --allow-empty -m foo 2>err &&
-               test_i18ngrep ! "(null)" err
+               test_grep ! "(null)" err
        )
 '
 
 test_expect_success 'commit rejects all-crud name' '
-       test_must_fail env GIT_AUTHOR_NAME=" .;<>" \
+       test_must_fail env GIT_AUTHOR_NAME=" ,;<>" \
                git commit --allow-empty -m foo
 '
 
+test_expect_success 'commit does not strip trailing dot' '
+       author_name="Pat Doe Jr." &&
+       env GIT_AUTHOR_NAME="$author_name" \
+               git commit --allow-empty -m foo &&
+       git log -1 --format=%an >actual &&
+       echo "$author_name" >expected &&
+       test_cmp actual expected
+'
+
 # We must test the actual error message here, as an unwanted
 # auto-detection could fail for other reasons.
 test_expect_success 'empty configured name does not auto-detect' '
@@ -31,8 +40,8 @@ test_expect_success 'empty configured name does not auto-detect' '
                sane_unset GIT_AUTHOR_NAME &&
                test_must_fail \
                        git -c user.name= commit --allow-empty -m foo 2>err &&
-               test_i18ngrep "empty ident name" err &&
-               test_i18ngrep "Author identity unknown" err
+               test_grep "empty ident name" err &&
+               test_grep "Author identity unknown" err
        )
 '
 
@@ -41,8 +50,8 @@ test_expect_success 'empty configured name does not auto-detect for committer' '
                sane_unset GIT_COMMITTER_NAME &&
                test_must_fail \
                        git -c user.name= commit --allow-empty -m foo 2>err &&
-               test_i18ngrep "empty ident name" err &&
-               test_i18ngrep "Committer identity unknown" err
+               test_grep "empty ident name" err &&
+               test_grep "Committer identity unknown" err
        )
 '
 
index 8348e3ae7db70d9c6a65606c3b400c485a3a8719..7ee69ecdd4aa2c15dbde3ba0a976d986bff17fb3 100755 (executable)
@@ -322,14 +322,14 @@ do
                        rm -f marker &&
                        git status >actual &&
                        test_path_is_file marker &&
-                       test_i18ngrep ! "Changes not staged for commit:" actual &&
+                       test_grep ! "Changes not staged for commit:" actual &&
                        if test $uc_val = true
                        then
-                               test_i18ngrep ! "Untracked files:" actual
+                               test_grep ! "Untracked files:" actual
                        fi &&
                        if test $uc_val = false
                        then
-                               test_i18ngrep "Untracked files:" actual
+                               test_grep "Untracked files:" actual
                        fi &&
                        rm -f marker
                '
index dc57526e6f1f7af741a4cd6c1ce4800563be5c37..3b63c34a309de5e13a0c52c365d02289c6aa87f3 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='ignored hook warning'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -12,27 +13,27 @@ test_expect_success setup '
 
 test_expect_success 'no warning if hook is not ignored' '
        git commit --allow-empty -m "more" 2>message &&
-       test_i18ngrep ! -e "hook was ignored" message
+       test_grep ! -e "hook was ignored" message
 '
 
 test_expect_success POSIXPERM 'warning if hook is ignored' '
        test_hook --disable pre-commit &&
        git commit --allow-empty -m "even more" 2>message &&
-       test_i18ngrep -e "hook was ignored" message
+       test_grep -e "hook was ignored" message
 '
 
 test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '
        test_config advice.ignoredHook false &&
        test_hook --disable pre-commit &&
        git commit --allow-empty -m "even more" 2>message &&
-       test_i18ngrep ! -e "hook was ignored" message
+       test_grep ! -e "hook was ignored" message
 '
 
 test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '
        test_hook --remove pre-commit &&
        test_unconfig advice.ignoredHook &&
        git commit --allow-empty -m "even more" 2>message &&
-       test_i18ngrep ! -e "hook was ignored" message
+       test_grep ! -e "hook was ignored" message
 '
 
 test_done
index 22bf5c7e5dc108fabfc165e948733d6221855785..a9210d3a3a92213728a32599defe664e1def0ffe 100755 (executable)
@@ -21,81 +21,81 @@ test_expect_success 'setup' '
 
 test_expect_success 'status no-options' '
        git status >actual &&
-       test_i18ngrep "renamed:" actual
+       test_grep "renamed:" actual
 '
 
 test_expect_success 'status --no-renames' '
        git status --no-renames >actual &&
-       test_i18ngrep "deleted:" actual &&
-       test_i18ngrep "new file:" actual
+       test_grep "deleted:" actual &&
+       test_grep "new file:" actual
 '
 
 test_expect_success 'status.renames inherits from diff.renames false' '
        git -c diff.renames=false status >actual &&
-       test_i18ngrep "deleted:" actual &&
-       test_i18ngrep "new file:" actual
+       test_grep "deleted:" actual &&
+       test_grep "new file:" actual
 '
 
 test_expect_success 'status.renames inherits from diff.renames true' '
        git -c diff.renames=true status >actual &&
-       test_i18ngrep "renamed:" actual
+       test_grep "renamed:" actual
 '
 
 test_expect_success 'status.renames overrides diff.renames false' '
        git -c diff.renames=true -c status.renames=false status >actual &&
-       test_i18ngrep "deleted:" actual &&
-       test_i18ngrep "new file:" actual
+       test_grep "deleted:" actual &&
+       test_grep "new file:" actual
 '
 
 test_expect_success 'status.renames overrides from diff.renames true' '
        git -c diff.renames=false -c status.renames=true status >actual &&
-       test_i18ngrep "renamed:" actual
+       test_grep "renamed:" actual
 '
 
 test_expect_success 'status status.renames=false' '
        git -c status.renames=false status >actual &&
-       test_i18ngrep "deleted:" actual &&
-       test_i18ngrep "new file:" actual
+       test_grep "deleted:" actual &&
+       test_grep "new file:" actual
 '
 
 test_expect_success 'status status.renames=true' '
        git -c status.renames=true status >actual &&
-       test_i18ngrep "renamed:" actual
+       test_grep "renamed:" actual
 '
 
 test_expect_success 'commit honors status.renames=false' '
        git -c status.renames=false commit --dry-run >actual &&
-       test_i18ngrep "deleted:" actual &&
-       test_i18ngrep "new file:" actual
+       test_grep "deleted:" actual &&
+       test_grep "new file:" actual
 '
 
 test_expect_success 'commit honors status.renames=true' '
        git -c status.renames=true commit --dry-run >actual &&
-       test_i18ngrep "renamed:" actual
+       test_grep "renamed:" actual
 '
 
 test_expect_success 'status config overridden' '
        git -c status.renames=true status --no-renames >actual &&
-       test_i18ngrep "deleted:" actual &&
-       test_i18ngrep "new file:" actual
+       test_grep "deleted:" actual &&
+       test_grep "new file:" actual
 '
 
 test_expect_success 'status score=100%' '
        git status -M=100% >actual &&
-       test_i18ngrep "deleted:" actual &&
-       test_i18ngrep "new file:" actual &&
+       test_grep "deleted:" actual &&
+       test_grep "new file:" actual &&
 
        git status --find-renames=100% >actual &&
-       test_i18ngrep "deleted:" actual &&
-       test_i18ngrep "new file:" actual
+       test_grep "deleted:" actual &&
+       test_grep "new file:" actual
 '
 
 test_expect_success 'status score=01%' '
        git status -M=01% >actual &&
-       test_i18ngrep "renamed:" actual &&
+       test_grep "renamed:" actual &&
 
        git status --find-renames=01% >actual &&
-       test_i18ngrep "renamed:" actual
+       test_grep "renamed:" actual
 '
 
 test_expect_success 'copies not overridden by find-renames' '
@@ -103,12 +103,12 @@ test_expect_success 'copies not overridden by find-renames' '
        git add copy &&
 
        git -c status.renames=copies status -M=01% >actual &&
-       test_i18ngrep "copied:" actual &&
-       test_i18ngrep "renamed:" actual &&
+       test_grep "copied:" actual &&
+       test_grep "renamed:" actual &&
 
        git -c status.renames=copies status --find-renames=01% >actual &&
-       test_i18ngrep "copied:" actual &&
-       test_i18ngrep "renamed:" actual
+       test_grep "copied:" actual &&
+       test_grep "renamed:" actual
 '
 
 test_done
index ad011bb9f158034171c04e76722b183eece45576..c97c550021e837a6fd6e59f260352673d92e85da 100755 (executable)
@@ -141,25 +141,25 @@ test_expect_success 'error conditions' '
        >empty_list &&
 
        test_must_fail git commit --pathspec-from-file=list --interactive -m "Commit" 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
        test_must_fail git commit --pathspec-from-file=list --patch -m "Commit" 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
        test_must_fail git commit --pathspec-from-file=list --all -m "Commit" 2>err &&
-       test_i18ngrep -e "options .--pathspec-from-file. and .-a. cannot be used together" err &&
+       test_grep -e "options .--pathspec-from-file. and .-a. cannot be used together" err &&
 
        test_must_fail git commit --pathspec-from-file=list -m "Commit" -- fileA.t 2>err &&
-       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+       test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git commit --pathspec-file-nul -m "Commit" 2>err &&
-       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+       test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        test_must_fail git commit --pathspec-from-file=empty_list --include -m "Commit" 2>err &&
-       test_i18ngrep -e "No paths with --include/--only does not make sense." err &&
+       test_grep -e "No paths with --include/--only does not make sense." err &&
 
        test_must_fail git commit --pathspec-from-file=empty_list --only -m "Commit" 2>err &&
-       test_i18ngrep -e "No paths with --include/--only does not make sense." err
+       test_grep -e "No paths with --include/--only does not make sense." err
 '
 
 test_done
index d419085379ce6c38fce7baf68160ce0faf2898be..78503158fd699d2df75e8fbbbaf164a129f2578b 100755 (executable)
@@ -809,6 +809,11 @@ my_match_and_clean () {
                status --porcelain=v2 >actual.without &&
        test_cmp actual.with actual.without &&
 
+       git -C super --no-optional-locks diff-index --name-status HEAD >actual.with &&
+       git -C super --no-optional-locks -c core.fsmonitor=false \
+               diff-index --name-status HEAD >actual.without &&
+       test_cmp actual.with actual.without &&
+
        git -C super/dir_1/dir_2/sub reset --hard &&
        git -C super/dir_1/dir_2/sub clean -d -f
 }
@@ -866,27 +871,9 @@ test_expect_success 'submodule always visited' '
 # the submodule, and someone does a `git submodule absorbgitdirs`
 # in the super, Git will recursively invoke `git submodule--helper`
 # to do the work and this may try to read the index.  This will
-# try to start the daemon in the submodule *and* pass (either
-# directly or via inheritance) the `--super-prefix` arg to the
-# `git fsmonitor--daemon start` command inside the submodule.
-# This causes a warning because fsmonitor--daemon does take that
-# global arg (see the table in git.c)
-#
-# This causes a warning when trying to start the daemon that is
-# somewhat confusing.  It does not seem to hurt anything because
-# the fsmonitor code maps the query failure into a trivial response
-# and does the work anyway.
-#
-# It would be nice to silence the warning, however.
+# try to start the daemon in the submodule.
 
-have_t2_error_event () {
-       log=$1
-       msg="fsmonitor--daemon doesnQt support --super-prefix" &&
-
-       tr '\047' Q <$1 | grep -e "$msg"
-}
-
-test_expect_success "stray submodule super-prefix warning" '
+test_expect_success "submodule absorbgitdirs implicitly starts daemon" '
        test_when_finished "rm -rf super; \
                            rm -rf sub;   \
                            rm super-sub.trace" &&
@@ -904,21 +891,31 @@ test_expect_success "stray submodule super-prefix warning" '
 
        test_path_is_dir super/dir_1/dir_2/sub/.git &&
 
+       cwd="$(cd super && pwd)" &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''dir_1/dir_2/sub'\'' from
+       '\''$cwd/dir_1/dir_2/sub/.git'\'' to
+       '\''$cwd/.git/modules/dir_1/dir_2/sub'\''
+       EOF
        GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
-               git -C super submodule absorbgitdirs &&
+               git -C super submodule absorbgitdirs >out 2>actual &&
+       test_cmp expect actual &&
+       test_must_be_empty out &&
 
-       ! have_t2_error_event super-sub.trace
+       # Confirm that the trace2 log contains a record of the
+       # daemon starting.
+       test_subcommand git fsmonitor--daemon start <super-sub.trace
 '
 
 # On a case-insensitive file system, confirm that the daemon
 # notices when the .git directory is moved/renamed/deleted
-# regardless of how it is spelled in the the FS event.
+# regardless of how it is spelled in the FS event.
 # That is, does the FS event receive the spelling of the
 # operation or does it receive the spelling preserved with
 # the file/directory.
 #
 test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
-#      test_when_finished "stop_daemon_delete_repo test_insensitive" &&
+       test_when_finished "stop_daemon_delete_repo test_insensitive" &&
 
        git init test_insensitive &&
 
@@ -930,8 +927,8 @@ test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
        test_path_is_dir test_insensitive/.git &&
        test_path_is_dir test_insensitive/.GIT &&
 
-       # Rename .git using an alternate spelling to verify that that
-       # daemon detects it and automatically shuts down.
+       # Rename .git using an alternate spelling to verify that
+       # the daemon detects it and automatically shuts down.
        mv test_insensitive/.GIT test_insensitive/.FOO &&
 
        # See [1] above.
@@ -943,9 +940,9 @@ test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
        # directories and files that we touched.  We may or may not get a
        # trailing slash on modified directories.
        #
-       egrep "^event: abc/?$"       ./insensitive.trace &&
-       egrep "^event: abc/def/?$"   ./insensitive.trace &&
-       egrep "^event: abc/def/xyz$" ./insensitive.trace
+       grep -E "^event: abc/?$"       ./insensitive.trace &&
+       grep -E "^event: abc/def/?$"   ./insensitive.trace &&
+       grep -E "^event: abc/def/xyz$" ./insensitive.trace
 '
 
 # The variable "unicode_debug" is defined in the following library
@@ -987,20 +984,57 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
        then
                # We should have seen NFC event from OS.
                # We should not have synthesized an NFD event.
-               egrep    "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
-               egrep -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
+               grep -E    "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
+               grep -E -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
        else
                # We should have seen NFD event from OS.
                # We should have synthesized an NFC event.
-               egrep "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
-               egrep "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
+               grep -E "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
+               grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
        fi &&
 
        # We assume UNICODE_NFD_PRESERVED.
        # We should have seen explicit NFD from OS.
        # We should have synthesized an NFC event.
-       egrep "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
-       egrep "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
+       grep -E "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
+       grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
+'
+
+test_expect_success 'split-index and FSMonitor work well together' '
+       git init split-index &&
+       test_when_finished "git -C \"$PWD/split-index\" \
+               fsmonitor--daemon stop" &&
+       (
+               cd split-index &&
+               git config core.splitIndex true &&
+               # force split-index in most cases
+               git config splitIndex.maxPercentChange 99 &&
+               git config core.fsmonitor true &&
+
+               # Create the following commit topology:
+               #
+               # *   merge three
+               # |\
+               # | * three
+               # * | merge two
+               # |\|
+               # | * two
+               # * | one
+               # |/
+               # * 5a5efd7 initial
+
+               test_commit initial &&
+               test_commit two &&
+               test_commit three &&
+               git reset --hard initial &&
+               test_commit one &&
+               test_tick &&
+               git merge two &&
+               test_tick &&
+               git merge three &&
+
+               git rebase --force-rebase -r one
+       )
 '
 
 test_done
index f47e99517983163e0bbb3fce9c35800270fe4e28..065f78063629cbfad15e9360836544da34a16484 100755 (executable)
@@ -270,7 +270,7 @@ test_expect_success GPGSSH 'detect fudged signature with NUL' '
        git cat-file commit seventh-signed >raw &&
        cat raw >forged2 &&
        echo Qwik | tr "Q" "\000" >>forged2 &&
-       git hash-object -w -t commit forged2 >forged2.commit &&
+       git hash-object --literally -w -t commit forged2 >forged2.commit &&
        test_must_fail git verify-commit $(cat forged2.commit) &&
        git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
        grep "${GPGSSH_BAD_SIGNATURE}" actual2 &&
index 7c3f6ed99431839abc42533d520cd342f25dafd1..e5ff073099a983d0da7205d3a3325af28caf8a9b 100755 (executable)
@@ -105,7 +105,7 @@ verify_mergeheads () {
        test_write_lines "$@" >mergehead.expected &&
        while read sha1 rest
        do
-               git rev-parse $sha1
+               git rev-parse $sha1 || return 1
        done <.git/MERGE_HEAD >mergehead.actual &&
        test_cmp mergehead.expected mergehead.actual
 }
@@ -175,7 +175,7 @@ test_expect_success 'merge -h with invalid index' '
                >.git/index &&
                test_expect_code 129 git merge -h 2>usage
        ) &&
-       test_i18ngrep "[Uu]sage: git merge" broken/usage
+       test_grep "[Uu]sage: git merge" broken/usage
 '
 
 test_expect_success 'reject non-strategy with a git-merge-foo name' '
@@ -639,41 +639,41 @@ test_expect_success 'merge log message' '
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
-       git reset --hard c1 &&
-       test_tick &&
-       git merge c0 c2 c0 c1 &&
-       verify_merge file result.1-5 &&
-       verify_parents $c1 $c2
+       git reset --hard c1 &&
+       test_tick &&
+       git merge c0 c2 c0 c1 &&
+       verify_merge file result.1-5 &&
+       verify_parents $c1 $c2
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
-       git reset --hard c1 &&
-       test_tick &&
-       git merge c0 c2 c0 c1 &&
-       verify_merge file result.1-5 &&
-       verify_parents $c1 $c2
+       git reset --hard c1 &&
+       test_tick &&
+       git merge c0 c2 c0 c1 &&
+       verify_merge file result.1-5 &&
+       verify_parents $c1 $c2
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c1 and c2' '
-       git reset --hard c1 &&
-       test_tick &&
-       git merge c1 c2 &&
-       verify_merge file result.1-5 &&
-       verify_parents $c1 $c2
+       git reset --hard c1 &&
+       test_tick &&
+       git merge c1 c2 &&
+       verify_merge file result.1-5 &&
+       verify_parents $c1 $c2
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge fast-forward in a dirty tree' '
-       git reset --hard c0 &&
-       mv file file1 &&
-       cat file1 >file &&
-       rm -f file1 &&
-       git merge c2
+       git reset --hard c0 &&
+       mv file file1 &&
+       cat file1 >file &&
+       rm -f file1 &&
+       git merge c2
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
@@ -681,7 +681,7 @@ test_debug 'git log --graph --decorate --oneline --all'
 test_expect_success 'in-index merge' '
        git reset --hard c0 &&
        git merge --no-ff -s resolve c1 >out &&
-       test_i18ngrep "Wonderful." out &&
+       test_grep "Wonderful." out &&
        verify_parents $c0 $c1
 '
 
@@ -697,7 +697,7 @@ test_expect_success 'merge with --autostash' '
        git reset --hard c1 &&
        git merge-file file file.orig file.9 &&
        git merge --autostash c2 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        git show HEAD:file >merge-result &&
        test_cmp result.1-5 merge-result &&
        test_cmp result.1-5-9 file
@@ -708,7 +708,7 @@ test_expect_success 'merge with merge.autoStash' '
        git reset --hard c1 &&
        git merge-file file file.orig file.9 &&
        git merge c2 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        git show HEAD:file >merge-result &&
        test_cmp result.1-5 merge-result &&
        test_cmp result.1-5-9 file
@@ -718,7 +718,7 @@ test_expect_success 'fast-forward merge with --autostash' '
        git reset --hard c0 &&
        git merge-file file file.orig file.5 &&
        git merge --autostash c1 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        test_cmp result.1-5 file
 '
 
@@ -728,7 +728,7 @@ test_expect_success 'failed fast-forward merge with --autostash' '
        cp file.5 other &&
        test_when_finished "rm other" &&
        test_must_fail git merge --autostash c1 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        test_cmp file.5 file
 '
 
@@ -736,7 +736,7 @@ test_expect_success 'octopus merge with --autostash' '
        git reset --hard c1 &&
        git merge-file file file.orig file.3 &&
        git merge --autostash c2 c3 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        git show HEAD:file >merge-result &&
        test_cmp result.1-5-9 merge-result &&
        test_cmp result.1-3-5-9 file
@@ -746,7 +746,7 @@ test_expect_success 'failed merge (exit 2) with --autostash' '
        git reset --hard c1 &&
        git merge-file file file.orig file.5 &&
        test_must_fail git merge -s recursive --autostash c2 c3 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        test_cmp result.1-5 file
 '
 
@@ -755,7 +755,7 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'
        cp file.1 file &&
        test_must_fail git merge --autostash c7 &&
        git merge --abort 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        test_cmp file.1 file
 '
 
@@ -767,7 +767,7 @@ test_expect_success 'completed merge (git commit) with --no-commit and --autosta
        git stash show -p MERGE_AUTOSTASH >actual &&
        test_cmp expect actual &&
        git commit 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        git show HEAD:file >merge-result &&
        test_cmp result.1-5 merge-result &&
        test_cmp result.1-5-9 file
@@ -781,7 +781,7 @@ test_expect_success 'completed merge (git merge --continue) with --no-commit and
        git stash show -p MERGE_AUTOSTASH >actual &&
        test_cmp expect actual &&
        git merge --continue 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        git show HEAD:file >merge-result &&
        test_cmp result.1-5 merge-result &&
        test_cmp result.1-5-9 file
@@ -795,7 +795,7 @@ test_expect_success 'aborted merge (merge --abort) with --no-commit and --autost
        git stash show -p MERGE_AUTOSTASH >actual &&
        test_cmp expect actual &&
        git merge --abort 2>err &&
-       test_i18ngrep "Applied autostash." err &&
+       test_grep "Applied autostash." err &&
        git diff >actual &&
        test_cmp expect actual
 '
@@ -808,7 +808,7 @@ test_expect_success 'aborted merge (reset --hard) with --no-commit and --autosta
        git stash show -p MERGE_AUTOSTASH >actual &&
        test_cmp expect actual &&
        git reset --hard 2>err &&
-       test_i18ngrep "Autostash exists; creating a new stash entry." err &&
+       test_grep "Autostash exists; creating a new stash entry." err &&
        git diff --exit-code
 '
 
@@ -821,7 +821,7 @@ test_expect_success 'quit merge with --no-commit and --autostash' '
        test_cmp expect actual &&
        git diff HEAD >expect &&
        git merge --quit 2>err &&
-       test_i18ngrep "Autostash exists; creating a new stash entry." err &&
+       test_grep "Autostash exists; creating a new stash entry." err &&
        git diff HEAD >actual &&
        test_cmp expect actual
 '
@@ -832,7 +832,7 @@ test_expect_success 'merge with conflicted --autostash changes' '
        git diff >expect &&
        test_when_finished "test_might_fail git stash drop" &&
        git merge --autostash c3 2>err &&
-       test_i18ngrep "Applying autostash resulted in conflicts." err &&
+       test_grep "Applying autostash resulted in conflicts." err &&
        git show HEAD:file >merge-result &&
        test_cmp result.1-9 merge-result &&
        git stash show -p >actual &&
index bd238d89b0cb07ed6a92707dcfd80ac277b75524..a94387a75f2f488ba6b48ddf4658822d08fc7908 100755 (executable)
@@ -30,117 +30,117 @@ test_expect_success 'setup' '
 test_expect_success 'pull.rebase not set, ff possible' '
        git reset --hard c0 &&
        git pull . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=true' '
        git reset --hard c0 &&
        test_config pull.ff true &&
        git pull . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=false' '
        git reset --hard c0 &&
        test_config pull.ff false &&
        git pull . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=only' '
        git reset --hard c0 &&
        test_config pull.ff only &&
        git pull . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --rebase given' '
        git reset --hard c0 &&
        git pull --rebase . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-rebase given' '
        git reset --hard c0 &&
        git pull --no-rebase . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff given' '
        git reset --hard c0 &&
        git pull --ff . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-ff given' '
        git reset --hard c0 &&
        git pull --no-ff . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff-only given' '
        git reset --hard c0 &&
        git pull --ff-only . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set (not-fast-forward)' '
        git reset --hard c2 &&
        test_must_fail git -c color.advice=always pull . c1 2>err &&
        test_decode_color <err >decoded &&
-       test_i18ngrep "<YELLOW>hint: " decoded &&
-       test_i18ngrep "You have divergent branches" decoded
+       test_grep "<YELLOW>hint: " decoded &&
+       test_grep "You have divergent branches" decoded
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=true (not-fast-forward)' '
        git reset --hard c2 &&
        test_config pull.ff true &&
        git pull . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=false (not-fast-forward)' '
        git reset --hard c2 &&
        test_config pull.ff false &&
        git pull . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=only (not-fast-forward)' '
        git reset --hard c2 &&
        test_config pull.ff only &&
        test_must_fail git pull . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '
        git reset --hard c2 &&
        git pull --rebase . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-rebase given (not-fast-forward)' '
        git reset --hard c2 &&
        git pull --no-rebase . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff given (not-fast-forward)' '
        git reset --hard c2 &&
        git pull --ff . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-ff given (not-fast-forward)' '
        git reset --hard c2 &&
        git pull --no-ff . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' '
        git reset --hard c2 &&
        test_must_fail git pull --ff-only . c1 2>err &&
-       test_i18ngrep ! "You have divergent branches" err
+       test_grep ! "You have divergent branches" err
 '
 
 test_does_rebase () {
@@ -202,7 +202,7 @@ test_falls_back_to_full_merge () {
 test_attempts_fast_forward () {
        git reset --hard c2 &&
        test_must_fail git "$@" . c1 2>err &&
-       test_i18ngrep "Not possible to fast-forward, aborting" err
+       test_grep "Not possible to fast-forward, aborting" err
 }
 
 #
@@ -328,34 +328,34 @@ test_expect_success 'pull.rebase=false and --ff, ff not possible' '
 test_expect_success 'Multiple heads warns about inability to fast forward' '
        git reset --hard c1 &&
        test_must_fail git pull . c2 c3 2>err &&
-       test_i18ngrep "You have divergent branches" err
+       test_grep "You have divergent branches" err
 '
 
 test_expect_success 'Multiple can never be fast forwarded' '
        git reset --hard c0 &&
        test_must_fail git -c pull.ff=only pull . c1 c2 c3 2>err &&
-       test_i18ngrep ! "You have divergent branches" err &&
+       test_grep ! "You have divergent branches" err &&
        # In addition to calling out "cannot fast-forward", we very much
        # want the "multiple branches" piece to be called out to users.
-       test_i18ngrep "Cannot fast-forward to multiple branches" err
+       test_grep "Cannot fast-forward to multiple branches" err
 '
 
 test_expect_success 'Cannot rebase with multiple heads' '
        git reset --hard c0 &&
        test_must_fail git -c pull.rebase=true pull . c1 c2 c3 2>err &&
-       test_i18ngrep ! "You have divergent branches" err &&
-       test_i18ngrep "Cannot rebase onto multiple branches." err
+       test_grep ! "You have divergent branches" err &&
+       test_grep "Cannot rebase onto multiple branches." err
 '
 
 test_expect_success 'merge c1 with c2' '
        git reset --hard c1 &&
-       test -f c0.c &&
-       test -f c1.c &&
-       test ! -f c2.c &&
-       test ! -f c3.c &&
+       test_path_is_file c0.c &&
+       test_path_is_file c1.c &&
+       test_path_is_missing c2.c &&
+       test_path_is_missing c3.c &&
        git merge c2 &&
-       test -f c1.c &&
-       test -f c2.c
+       test_path_is_file c1.c &&
+       test_path_is_file c2.c
 '
 
 test_expect_success 'fast-forward pull succeeds with "true" in pull.ff' '
@@ -411,8 +411,8 @@ test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '
        git reset --hard c1 &&
        git config pull.twohead ours &&
        git merge c2 &&
-       test -f c1.c &&
-       ! test -f c2.c
+       test_path_is_file c1.c &&
+       test_path_is_missing c2.c
 '
 
 test_expect_success 'merge c1 with c2 and c3 (recursive in pull.octopus)' '
@@ -431,10 +431,10 @@ test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octo
        test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
        test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
        git diff --exit-code &&
-       test -f c0.c &&
-       test -f c1.c &&
-       test -f c2.c &&
-       test -f c3.c
+       test_path_is_file c0.c &&
+       test_path_is_file c1.c &&
+       test_path_is_file c2.c &&
+       test_path_is_file c3.c
 '
 
 conflict_count()
index ff085b086cc38f36a180e22ab02bbea12a29cc0c..3669d33bd5a15fc19fbfd2cbea14507c8e273642 100755 (executable)
@@ -4,6 +4,7 @@ test_description='git merge
 
 Testing octopus merge with more than 25 refs.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 4887ca705b330e8cbf6f25595cbcadfdfd67f9f2..0e85b21ec82cc64e7d1e519c269a9bdcb189e01b 100755 (executable)
@@ -4,6 +4,7 @@ test_description='git merge
 
 Testing octopus merge when reducing parents to independent branches.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # 0 - 1
index 5d56c3854647b2f2bc2fdf4221b1f76f5d0b0629..62d935d31c2e1dc9cbca3779407b6b86bc5ce4c5 100755 (executable)
@@ -4,6 +4,7 @@ test_description='git merge
 
 Testing the resolve strategy.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 89a62ac53b3d6012d13b3b1ce0dfa97d494060f2..9001674f2ea2201836613e4909d9442062f609f4 100755 (executable)
@@ -4,6 +4,7 @@ test_description="Test that merge state is as expected after failed merge"
 
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'Ensure we restore original state if no merge strategy handles it' '
index 0b908ab2e7128052a0513e740b54ba308b9feadc..2179938c437e47552eccb7af85fa3e4669ccc1bf 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test auto-generated merge messages'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_oneline() {
index 8cc64729adb20640f17de4aa9ba891a9ead7edde..22b3a85b3e960e4d40024179b1fbaef3355d304d 100755 (executable)
@@ -33,7 +33,7 @@ test_expect_success 'setup' '
                git add foo &&
                git commit -m "Add foo"
        ) &&
-       git submodule add git://example.com/submod submod &&
+       git submodule add file:///dev/null submod &&
        git add file1 "spaced name" file1[1-4] subdir/file3 .gitmodules submod &&
        git commit -m "add initial versions" &&
 
@@ -614,7 +614,7 @@ test_expect_success 'submodule in subdirectory' '
                )
        ) &&
        test_when_finished "rm -rf subdir/subdir_module" &&
-       git submodule add git://example.com/subsubmodule subdir/subdir_module &&
+       git submodule add file:///dev/null subdir/subdir_module &&
        git add subdir/subdir_module &&
        git commit -m "add submodule in subdirectory" &&
 
@@ -860,4 +860,42 @@ test_expect_success 'mergetool hideResolved' '
        git commit -m "test resolved with mergetool"
 '
 
+test_expect_success 'mergetool with guiDefault' '
+       test_config merge.guitool myguitool &&
+       test_config mergetool.myguitool.cmd "(printf \"gui \" && cat \"\$REMOTE\") >\"\$MERGED\"" &&
+       test_config mergetool.myguitool.trustExitCode true &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
+       git submodule update -N &&
+       test_must_fail git merge main &&
+
+       test_config mergetool.guiDefault auto &&
+       DISPLAY=SOMETHING && export DISPLAY &&
+       yes "" | git mergetool both &&
+       yes "" | git mergetool file1 file1 &&
+
+       DISPLAY= && export DISPLAY &&
+       yes "" | git mergetool file2 "spaced name" &&
+
+       test_config mergetool.guiDefault true &&
+       yes "" | git mergetool subdir/file3 &&
+
+       yes "d" | git mergetool file11 &&
+       yes "d" | git mergetool file12 &&
+       yes "l" | git mergetool submod &&
+
+       echo "gui main updated" >expect &&
+       test_cmp expect file1 &&
+
+       echo "main new" >expect &&
+       test_cmp expect file2 &&
+
+       echo "gui main new sub" >expect &&
+       test_cmp expect subdir/file3 &&
+
+       echo "branch1 submodule" >expect &&
+       test_cmp expect submod/bar &&
+       git commit -m "branch1 resolved with mergetool"
+'
+
 test_done
index c0e9425115f8041d615e454018150cd300eb47d3..d6975ca48df3d4710940a4b5a040c0598b0d0628 100755 (executable)
@@ -50,7 +50,7 @@ pre_merge_head="$(git rev-parse HEAD)"
 
 test_expect_success 'fails without MERGE_HEAD (unstarted merge)' '
        test_must_fail git merge --abort 2>output &&
-       test_i18ngrep MERGE_HEAD output
+       test_grep MERGE_HEAD output
 '
 
 test_expect_success 'fails without MERGE_HEAD (unstarted merge): .git/MERGE_HEAD sanity' '
@@ -64,7 +64,7 @@ test_expect_success 'fails without MERGE_HEAD (completed merge)' '
        # Merge successfully completed
        post_merge_head="$(git rev-parse HEAD)" &&
        test_must_fail git merge --abort 2>output &&
-       test_i18ngrep MERGE_HEAD output
+       test_grep MERGE_HEAD output
 '
 
 test_expect_success 'fails without MERGE_HEAD (completed merge): .git/MERGE_HEAD sanity' '
index 61330f71b1749c92a79153a3fce4f1834bfed248..84ddb568517cee76f519f6faaac3e3a89e3e68ad 100755 (executable)
@@ -4,6 +4,7 @@ test_description='merge signature verification tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
@@ -40,54 +41,54 @@ test_expect_success GPG 'create signed commits' '
 test_expect_success GPG 'merge unsigned commit with verification' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror &&
-       test_i18ngrep "does not have a GPG signature" mergeerror
+       test_grep "does not have a GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge unsigned commit with merge.verifySignatures=true' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config merge.verifySignatures true &&
        test_must_fail git merge --ff-only side-unsigned 2>mergeerror &&
-       test_i18ngrep "does not have a GPG signature" mergeerror
+       test_grep "does not have a GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with bad signature with verification' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror &&
-       test_i18ngrep "has a bad GPG signature" mergeerror
+       test_grep "has a bad GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=true' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config merge.verifySignatures true &&
        test_must_fail git merge --ff-only $(cat forged.commit) 2>mergeerror &&
-       test_i18ngrep "has a bad GPG signature" mergeerror
+       test_grep "has a bad GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with verification' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
-       test_i18ngrep "has an untrusted GPG signature" mergeerror
+       test_grep "has an untrusted GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with verification and high minTrustLevel' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config gpg.minTrustLevel marginal &&
        test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
-       test_i18ngrep "has an untrusted GPG signature" mergeerror
+       test_grep "has an untrusted GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with verification and low minTrustLevel' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config gpg.minTrustLevel undefined &&
        git merge --ff-only --verify-signatures side-untrusted >mergeoutput &&
-       test_i18ngrep "has a good GPG signature" mergeoutput
+       test_grep "has a good GPG signature" mergeoutput
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config merge.verifySignatures true &&
        test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
-       test_i18ngrep "has an untrusted GPG signature" mergeerror
+       test_grep "has an untrusted GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true and minTrustLevel' '
@@ -95,20 +96,20 @@ test_expect_success GPG 'merge commit with untrusted signature with merge.verify
        test_config merge.verifySignatures true &&
        test_config gpg.minTrustLevel marginal &&
        test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
-       test_i18ngrep "has an untrusted GPG signature" mergeerror
+       test_grep "has an untrusted GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge signed commit with verification' '
        test_when_finished "git reset --hard && git checkout initial" &&
        git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput &&
-       test_i18ngrep "has a good GPG signature" mergeoutput
+       test_grep "has a good GPG signature" mergeoutput
 '
 
 test_expect_success GPG 'merge signed commit with merge.verifySignatures=true' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config merge.verifySignatures true &&
        git merge --verbose --ff-only side-signed >mergeoutput &&
-       test_i18ngrep "has a good GPG signature" mergeoutput
+       test_grep "has a good GPG signature" mergeoutput
 '
 
 test_expect_success GPG 'merge commit with bad signature without verification' '
@@ -132,7 +133,7 @@ test_expect_success GPG 'merge unsigned commit into unborn branch' '
        test_when_finished "git checkout initial" &&
        git checkout --orphan unborn &&
        test_must_fail git merge --verify-signatures side-unsigned 2>mergeerror &&
-       test_i18ngrep "does not have a GPG signature" mergeerror
+       test_grep "does not have a GPG signature" mergeerror
 '
 
 test_done
index fee258d4f0c2c0d4b230891eeb93d522d27a4979..cf96a35e8e7b20975628cce6a5882c18c32fbeaa 100755 (executable)
@@ -8,6 +8,7 @@ This test runs git merge --signoff and makes sure that it works.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Setup test files
index ca45c4cd2c1a8a422e396fd4d29dee8c49c0c10e..94f9f4a1dac5621fbd340b6425d2f2ea7f042016 100755 (executable)
@@ -10,6 +10,10 @@ test_description='git repack works correctly'
 commit_and_pack () {
        test_commit "$@" 1>&2 &&
        incrpackid=$(git pack-objects --all --unpacked --incremental .git/objects/pack/pack </dev/null) &&
+       # Remove any loose object(s) created by test_commit, since they have
+       # already been packed. Leaving these around can create subtly different
+       # packs with `pack-objects`'s `--unpacked` option.
+       git prune-packed 1>&2 &&
        echo pack-${incrpackid}.pack
 }
 
@@ -90,6 +94,39 @@ test_expect_success 'loose objects in alternate ODB are not repacked' '
        test_has_duplicate_object false
 '
 
+test_expect_success SYMLINKS '--local keeps packs when alternate is objectdir ' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       test_commit -C repo A &&
+       (
+               cd repo &&
+               git repack -a &&
+               ls .git/objects/pack/*.pack >../expect &&
+               ln -s objects .git/alt_objects &&
+               echo "$(pwd)/.git/alt_objects" >.git/objects/info/alternates &&
+               git repack -a -d -l &&
+               ls .git/objects/pack/*.pack >../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success '--local disables writing bitmaps when connected to alternate ODB' '
+       test_when_finished "rm -rf shared member" &&
+
+       git init shared &&
+       git clone --shared shared member &&
+       (
+               cd member &&
+               test_commit "object" &&
+               GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adl --write-bitmap-index 2>err &&
+               cat >expect <<-EOF &&
+               warning: disabling bitmap writing, as some objects are not being packed
+               EOF
+               test_cmp expect err &&
+               test_path_is_missing .git/objects/pack-*.bitmap
+       )
+'
+
 test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
        mkdir alt_objects/pack &&
        mv .git/objects/pack/* alt_objects/pack &&
@@ -176,6 +213,8 @@ test_expect_success 'repack --keep-pack' '
        test_create_repo keep-pack &&
        (
                cd keep-pack &&
+               # avoid producing different packs due to delta/base choices
+               git config pack.window 0 &&
                P1=$(commit_and_pack 1) &&
                P2=$(commit_and_pack 2) &&
                P3=$(commit_and_pack 3) &&
@@ -187,10 +226,61 @@ test_expect_success 'repack --keep-pack' '
                grep -q $P1 new-counts &&
                grep -q $P4 new-counts &&
                test_line_count = 3 new-counts &&
+               git fsck &&
+
+               P5=$(commit_and_pack --no-tag 5) &&
+               git reset --hard HEAD^ &&
+               git reflog expire --all --expire=all &&
+               rm -f ".git/objects/pack/${P5%.pack}.idx" &&
+               rm -f ".git/objects/info/commit-graph" &&
+               for from in $(find .git/objects/pack -type f -name "${P5%.pack}.*")
+               do
+                       to="$(dirname "$from")/.tmp-1234-$(basename "$from")" &&
+                       mv "$from" "$to" || return 1
+               done &&
+
+               # A .idx file without a .pack should not stop us from
+               # repacking what we can.
+               touch .git/objects/pack/pack-does-not-exist.idx &&
+
+               git repack --cruft -d --keep-pack $P1 --keep-pack $P4 &&
+
+               ls .git/objects/pack/*.pack >newer-counts &&
+               test_cmp new-counts newer-counts &&
                git fsck
        )
 '
 
+test_expect_success 'repacking fails when missing .pack actually means missing objects' '
+       test_create_repo idx-without-pack &&
+       (
+               cd idx-without-pack &&
+
+               # Avoid producing different packs due to delta/base choices
+               git config pack.window 0 &&
+               P1=$(commit_and_pack 1) &&
+               P2=$(commit_and_pack 2) &&
+               P3=$(commit_and_pack 3) &&
+               P4=$(commit_and_pack 4) &&
+               ls .git/objects/pack/*.pack >old-counts &&
+               test_line_count = 4 old-counts &&
+
+               # Remove one .pack file
+               rm .git/objects/pack/$P2 &&
+
+               ls .git/objects/pack/*.pack >before-pack-dir &&
+
+               test_must_fail git fsck &&
+               test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git repack --cruft -d 2>err &&
+               grep "bad object" err &&
+
+               # Before failing, the repack did not modify the
+               # pack directory.
+               ls .git/objects/pack/*.pack >after-pack-dir &&
+               test_cmp before-pack-dir after-pack-dir
+       )
+'
+
 test_expect_success 'bitmaps are created by default in bare repos' '
        git clone --bare .git bare.git &&
        rm -f bare.git/objects/pack/*.bitmap &&
@@ -237,6 +327,203 @@ test_expect_success 'auto-bitmaps do not complain if unavailable' '
        test_must_be_empty actual
 '
 
+test_expect_success 'repacking with a filter works' '
+       git -C bare.git repack -a -d &&
+       test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+       git -C bare.git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
+       test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
+       commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) &&
+       blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) &&
+       test "$commit_pack" != "$blob_pack" &&
+       tree_pack=$(test-tool -C bare.git find-pack -c 1 HEAD^{tree}) &&
+       test "$tree_pack" = "$commit_pack" &&
+       blob_pack2=$(test-tool -C bare.git find-pack -c 1 HEAD:file2) &&
+       test "$blob_pack2" = "$blob_pack"
+'
+
+test_expect_success '--filter fails with --write-bitmap-index' '
+       test_must_fail \
+               env GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+               git -C bare.git repack -a -d --write-bitmap-index --filter=blob:none
+'
+
+test_expect_success 'repacking with two filters works' '
+       git init two-filters &&
+       (
+               cd two-filters &&
+               mkdir subdir &&
+               test_commit foo &&
+               test_commit subdir_bar subdir/bar &&
+               test_commit subdir_baz subdir/baz
+       ) &&
+       git clone --no-local --bare two-filters two-filters.git &&
+       (
+               cd two-filters.git &&
+               test_stdout_line_count = 1 ls objects/pack/*.pack &&
+               git -c repack.writebitmaps=false repack -a -d \
+                       --filter=blob:none --filter=tree:1 &&
+               test_stdout_line_count = 2 ls objects/pack/*.pack &&
+               commit_pack=$(test-tool find-pack -c 1 HEAD) &&
+               blob_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               root_tree_pack=$(test-tool find-pack -c 1 HEAD^{tree}) &&
+               subdir_tree_hash=$(git ls-tree --object-only HEAD -- subdir) &&
+               subdir_tree_pack=$(test-tool find-pack -c 1 "$subdir_tree_hash") &&
+
+               # Root tree and subdir tree are not in the same packfiles
+               test "$commit_pack" != "$blob_pack" &&
+               test "$commit_pack" = "$root_tree_pack" &&
+               test "$blob_pack" = "$subdir_tree_pack"
+       )
+'
+
+prepare_for_keep_packs () {
+       git init keep-packs &&
+       (
+               cd keep-packs &&
+               test_commit foo &&
+               test_commit bar
+       ) &&
+       git clone --no-local --bare keep-packs keep-packs.git &&
+       (
+               cd keep-packs.git &&
+
+               # Create two packs
+               # The first pack will contain all of the objects except one blob
+               git rev-list --objects --all >objs &&
+               grep -v "bar.t" objs | git pack-objects pack &&
+               # The second pack will contain the excluded object and be kept
+               packid=$(grep "bar.t" objs | git pack-objects pack) &&
+               >pack-$packid.keep &&
+
+               # Replace the existing pack with the 2 new ones
+               rm -f objects/pack/pack* &&
+               mv pack-* objects/pack/
+       )
+}
+
+test_expect_success '--filter works with .keep packs' '
+       prepare_for_keep_packs &&
+       (
+               cd keep-packs.git &&
+
+               foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+               head_pack=$(test-tool find-pack -c 1 HEAD) &&
+
+               test "$foo_pack" != "$bar_pack" &&
+               test "$foo_pack" = "$head_pack" &&
+
+               git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
+
+               foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               bar_pack_1=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+               head_pack_1=$(test-tool find-pack -c 1 HEAD) &&
+
+               # Object bar is still only in the old .keep pack
+               test "$foo_pack_1" != "$foo_pack" &&
+               test "$bar_pack_1" = "$bar_pack" &&
+               test "$head_pack_1" != "$head_pack" &&
+
+               test "$foo_pack_1" != "$bar_pack_1" &&
+               test "$foo_pack_1" != "$head_pack_1" &&
+               test "$bar_pack_1" != "$head_pack_1"
+       )
+'
+
+test_expect_success '--filter works with --pack-kept-objects and .keep packs' '
+       rm -rf keep-packs keep-packs.git &&
+       prepare_for_keep_packs &&
+       (
+               cd keep-packs.git &&
+
+               foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+               head_pack=$(test-tool find-pack -c 1 HEAD) &&
+
+               test "$foo_pack" != "$bar_pack" &&
+               test "$foo_pack" = "$head_pack" &&
+
+               git -c repack.writebitmaps=false repack -a -d --filter=blob:none \
+                       --pack-kept-objects &&
+
+               foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               test-tool find-pack -c 2 HEAD:bar.t >bar_pack_1 &&
+               head_pack_1=$(test-tool find-pack -c 1 HEAD) &&
+
+               test "$foo_pack_1" != "$foo_pack" &&
+               test "$foo_pack_1" != "$bar_pack" &&
+               test "$head_pack_1" != "$head_pack" &&
+
+               # Object bar is in both the old .keep pack and the new
+               # pack that contained the filtered out objects
+               grep "$bar_pack" bar_pack_1 &&
+               grep "$foo_pack_1" bar_pack_1 &&
+               test "$foo_pack_1" != "$head_pack_1"
+       )
+'
+
+test_expect_success '--filter-to stores filtered out objects' '
+       git -C bare.git repack -a -d &&
+       test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+
+       git init --bare filtered.git &&
+       git -C bare.git -c repack.writebitmaps=false repack -a -d \
+               --filter=blob:none \
+               --filter-to=../filtered.git/objects/pack/pack &&
+       test_stdout_line_count = 1 ls bare.git/objects/pack/pack-*.pack &&
+       test_stdout_line_count = 1 ls filtered.git/objects/pack/pack-*.pack &&
+
+       commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) &&
+       blob_pack=$(test-tool -C bare.git find-pack -c 0 HEAD:file1) &&
+       blob_hash=$(git -C bare.git rev-parse HEAD:file1) &&
+       test -n "$blob_hash" &&
+       blob_pack=$(test-tool -C filtered.git find-pack -c 1 $blob_hash) &&
+
+       echo $(pwd)/filtered.git/objects >bare.git/objects/info/alternates &&
+       blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) &&
+       blob_content=$(git -C bare.git show $blob_hash) &&
+       test "$blob_content" = "content1"
+'
+
+test_expect_success '--filter works with --max-pack-size' '
+       rm -rf filtered.git &&
+       git init --bare filtered.git &&
+       git init max-pack-size &&
+       (
+               cd max-pack-size &&
+               test_commit base &&
+               # two blobs which exceed the maximum pack size
+               test-tool genrandom foo 1048576 >foo &&
+               git hash-object -w foo &&
+               test-tool genrandom bar 1048576 >bar &&
+               git hash-object -w bar &&
+               git add foo bar &&
+               git commit -m "adding foo and bar"
+       ) &&
+       git clone --no-local --bare max-pack-size max-pack-size.git &&
+       (
+               cd max-pack-size.git &&
+               git -c repack.writebitmaps=false repack -a -d --filter=blob:none \
+                       --max-pack-size=1M \
+                       --filter-to=../filtered.git/objects/pack/pack &&
+               echo $(cd .. && pwd)/filtered.git/objects >objects/info/alternates &&
+
+               # Check that the 3 blobs are in different packfiles in filtered.git
+               test_stdout_line_count = 3 ls ../filtered.git/objects/pack/pack-*.pack &&
+               test_stdout_line_count = 1 ls objects/pack/pack-*.pack &&
+               foo_pack=$(test-tool find-pack -c 1 HEAD:foo) &&
+               bar_pack=$(test-tool find-pack -c 1 HEAD:bar) &&
+               base_pack=$(test-tool find-pack -c 1 HEAD:base.t) &&
+               test "$foo_pack" != "$bar_pack" &&
+               test "$foo_pack" != "$base_pack" &&
+               test "$bar_pack" != "$base_pack" &&
+               for pack in "$foo_pack" "$bar_pack" "$base_pack"
+               do
+                       case "$foo_pack" in */filtered.git/objects/pack/*) true ;; *) return 1 ;; esac
+               done
+       )
+'
+
 objdir=.git/objects
 midx=$objdir/pack/multi-pack-index
 
@@ -426,12 +713,73 @@ test_expect_success '--write-midx -b packs non-kept objects' '
        )
 '
 
+test_expect_success '--write-midx removes stale pack-based bitmaps' '
+       rm -fr repo &&
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+               test_commit base &&
+               GIT_TEST_MULTI_PACK_INDEX=0 git repack -Ab &&
+
+               pack_bitmap=$(ls $objdir/pack/pack-*.bitmap) &&
+               test_path_is_file "$pack_bitmap" &&
+
+               test_commit tip &&
+               GIT_TEST_MULTI_PACK_INDEX=0 git repack -bm &&
+
+               test_path_is_file $midx &&
+               test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+               test_path_is_missing $pack_bitmap
+       )
+'
+
+test_expect_success '--write-midx with --pack-kept-objects' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit one &&
+               test_commit two &&
+
+               one="$(echo "one" | git pack-objects --revs $objdir/pack/pack)" &&
+               two="$(echo "one..two" | git pack-objects --revs $objdir/pack/pack)" &&
+
+               keep="$objdir/pack/pack-$one.keep" &&
+               touch "$keep" &&
+
+               git repack --write-midx --write-bitmap-index --geometric=2 -d \
+                       --pack-kept-objects &&
+
+               test_path_is_file $keep &&
+               test_path_is_file $midx &&
+               test_path_is_file $midx-$(midx_checksum $objdir).bitmap
+       )
+'
+
 test_expect_success TTY '--quiet disables progress' '
        test_terminal env GIT_PROGRESS_DELAY=0 \
                git -C midx repack -ad --quiet --write-midx 2>stderr &&
        test_must_be_empty stderr
 '
 
+test_expect_success 'clean up .tmp-* packs on error' '
+       test_must_fail ok=sigpipe git \
+               -c repack.cruftwindow=bogus \
+               repack -ad --cruft &&
+       find $objdir/pack -name '.tmp-*' >tmpfiles &&
+       test_must_be_empty tmpfiles
+'
+
+test_expect_success 'repack -ad cleans up old .tmp-* packs' '
+       git rev-parse HEAD >input &&
+       git pack-objects $objdir/pack/.tmp-1234 <input &&
+       git repack -ad &&
+       find $objdir/pack -name '.tmp-*' >tmpfiles &&
+       test_must_be_empty tmpfiles
+'
+
 test_expect_success 'setup for update-server-info' '
        git init update-server-info &&
        test_commit -C update-server-info message
index 937f89ee8c8aa0afd4db98fe4bbe9b63b333d77a..fe6c3e77a3c9add78dadc421be9b0b00b0f6bcd9 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git repack works correctly'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 fsha1=
@@ -35,7 +36,7 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
        git repack -A -d -l &&
        # verify objects are packed in repository
        test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
-                  egrep "^($fsha1|$csha1|$tsha1) " |
+                  grep -E "^($fsha1|$csha1|$tsha1) " |
                   sort | uniq | wc -l) &&
        git show $fsha1 &&
        git show $csha1 &&
@@ -49,7 +50,7 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
        git repack -A -d -l &&
        # verify objects are retained unpacked
        test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
-                  egrep "^($fsha1|$csha1|$tsha1) " |
+                  grep -E "^($fsha1|$csha1|$tsha1) " |
                   sort | uniq | wc -l) &&
        git show $fsha1 &&
        git show $csha1 &&
@@ -112,6 +113,48 @@ test_expect_success 'do not bother loosening old objects' '
        test_must_fail git cat-file -p $obj2
 '
 
+test_expect_success 'gc.recentObjectsHook' '
+       obj1=$(echo one | git hash-object -w --stdin) &&
+       obj2=$(echo two | git hash-object -w --stdin) &&
+       obj3=$(echo three | git hash-object -w --stdin) &&
+       pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+       pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+       pack3=$(echo $obj3 | git pack-objects .git/objects/pack/pack) &&
+       git prune-packed &&
+
+       git cat-file -p $obj1 &&
+       git cat-file -p $obj2 &&
+       git cat-file -p $obj3 &&
+
+       # make an unreachable annotated tag object to ensure we rescue objects
+       # which are reachable from non-pruned unreachable objects
+       obj2_tag="$(git mktag <<-EOF
+       object $obj2
+       type blob
+       tag obj2-tag
+       tagger T A Gger <tagger@example.com> 1234567890 -0000
+       EOF
+       )" &&
+
+       obj2_tag_pack="$(echo $obj2_tag | git pack-objects .git/objects/pack/pack)" &&
+       git prune-packed &&
+
+       write_script precious-objects <<-EOF &&
+       echo $obj2_tag
+       EOF
+       git config gc.recentObjectsHook ./precious-objects &&
+
+       test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+       test-tool chmtime =-86400 .git/objects/pack/pack-$pack3.pack &&
+       test-tool chmtime =-86400 .git/objects/pack/pack-$obj2_tag_pack.pack &&
+       git repack -A -d --unpack-unreachable=1.hour.ago &&
+
+       git cat-file -p $obj1 &&
+       git cat-file -p $obj2 &&
+       git cat-file -p $obj2_tag &&
+       test_must_fail git cat-file -p $obj3
+'
+
 test_expect_success 'keep packed objects found only in index' '
        echo my-unique-content >file &&
        git add file &&
index da87f8b2d8822cd5f844cfb00bcdaf5c52117f79..9fc1626fbfde8989348537903a266eb6cae8600d 100755 (executable)
@@ -10,6 +10,12 @@ objdir=.git/objects
 packdir=$objdir/pack
 midx=$objdir/pack/multi-pack-index
 
+packed_objects () {
+       git show-index <"$1" >tmp-object-list &&
+       cut -d' ' -f2 tmp-object-list | sort &&
+       rm tmp-object-list
+ }
+
 test_expect_success '--geometric with no packs' '
        git init geometric &&
        test_when_finished "rm -fr geometric" &&
@@ -17,7 +23,7 @@ test_expect_success '--geometric with no packs' '
                cd geometric &&
 
                git repack --write-midx --geometric 2 >out &&
-               test_i18ngrep "Nothing new to pack" out
+               test_grep "Nothing new to pack" out
        )
 '
 
@@ -32,7 +38,7 @@ test_expect_success '--geometric with one pack' '
 
                git repack --geometric 2 >out &&
 
-               test_i18ngrep "Nothing new to pack" out
+               test_grep "Nothing new to pack" out
        )
 '
 
@@ -176,8 +182,12 @@ test_expect_success '--geometric ignores kept packs' '
                # be repacked, too.
                git repack --geometric 2 -d --pack-kept-objects &&
 
+               # After repacking, two packs remain: one new one (containing the
+               # objects in both the .keep and non-kept pack), and the .keep
+               # pack (since `--pack-kept-objects -d` does not actually delete
+               # the kept pack).
                find $objdir/pack -name "*.pack" >after &&
-               test_line_count = 1 after
+               test_line_count = 2 after
        )
 '
 
@@ -277,4 +287,162 @@ test_expect_success '--geometric with pack.packSizeLimit' '
        )
 '
 
+test_expect_success '--geometric --write-midx with packfiles in main and alternate ODB' '
+       test_when_finished "rm -fr shared member" &&
+
+       # Create a shared repository that will serve as the alternate object
+       # database for the member linked to it. It has got some objects on its
+       # own that are packed into a single packfile.
+       git init shared &&
+       test_commit -C shared common-object &&
+       git -C shared repack -ad &&
+
+       # We create member so that its alternates file points to the shared
+       # repository. We then create a commit in it so that git-repack(1) has
+       # something to repack.
+       # of the shared object database.
+       git clone --shared shared member &&
+       test_commit -C member unique-object &&
+       git -C member repack --geometric=2 --write-midx 2>err &&
+       test_must_be_empty err &&
+
+       # We should see that a new packfile was generated.
+       find shared/.git/objects/pack -type f -name "*.pack" >packs &&
+       test_line_count = 1 packs &&
+
+       # We should also see a multi-pack-index. This multi-pack-index should
+       # never refer to any packfiles in the alternate object database.
+       test_path_is_file member/.git/objects/pack/multi-pack-index &&
+       test-tool read-midx member/.git/objects >packs.midx &&
+       grep "^pack-.*\.idx$" packs.midx | sort >actual &&
+       basename member/.git/objects/pack/pack-*.idx >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--geometric --with-midx with no local objects' '
+       test_when_finished "rm -fr shared member" &&
+
+       # Create a repository with a single packfile that acts as alternate
+       # object database.
+       git init shared &&
+       test_commit -C shared "shared-objects" &&
+       git -C shared repack -ad &&
+
+       # Create a second repository linked to the first one and perform a
+       # geometric repack on it.
+       git clone --shared shared member &&
+       git -C member repack --geometric 2 --write-midx 2>err &&
+       test_must_be_empty err &&
+
+       # Assert that we wrote neither a new packfile nor a multi-pack-index.
+       # We should not have a packfile because the single packfile in the
+       # alternate object database does not invalidate the geometric sequence.
+       # And we should not have a multi-pack-index because these only index
+       # local packfiles, and there are none.
+       test_dir_is_empty member/$packdir
+'
+
+test_expect_success '--geometric with same pack in main and alternate ODB' '
+       test_when_finished "rm -fr shared member" &&
+
+       # Create a repository with a single packfile that acts as alternate
+       # object database.
+       git init shared &&
+       test_commit -C shared "shared-objects" &&
+       git -C shared repack -ad &&
+
+       # We create the member repository as an exact copy so that it has the
+       # same packfile.
+       cp -r shared member &&
+       test-tool path-utils real_path shared/.git/objects >member/.git/objects/info/alternates &&
+       find shared/.git/objects -type f >expected-files &&
+
+       # Verify that we can repack objects as expected without observing any
+       # error. Having the same packfile in both ODBs used to cause an error
+       # in git-pack-objects(1).
+       git -C member repack --geometric 2 2>err &&
+       test_must_be_empty err &&
+       # Nothing should have changed.
+       find shared/.git/objects -type f >actual-files &&
+       test_cmp expected-files actual-files
+'
+
+test_expect_success '--geometric -l with non-intact geometric sequence across ODBs' '
+       test_when_finished "rm -fr shared member" &&
+
+       git init shared &&
+       test_commit_bulk -C shared --start=1 1 &&
+
+       git clone --shared shared member &&
+       test_commit_bulk -C member --start=2 1 &&
+
+       # Verify that our assumptions actually hold: both generated packfiles
+       # should have three objects and should be non-equal.
+       packed_objects shared/.git/objects/pack/pack-*.idx >shared-objects &&
+       packed_objects member/.git/objects/pack/pack-*.idx >member-objects &&
+       test_line_count = 3 shared-objects &&
+       test_line_count = 3 member-objects &&
+       ! test_cmp shared-objects member-objects &&
+
+       # Perform the geometric repack. With `-l`, we should only see the local
+       # packfile and thus arrive at the conclusion that the geometric
+       # sequence is intact. We thus expect no changes.
+       #
+       # Note that we are tweaking mtimes of the packfiles so that we can
+       # verify they did not change. This is done in order to detect the case
+       # where we do repack objects, but the resulting packfile is the same.
+       test-tool chmtime --verbose =0 member/.git/objects/pack/* >expected-member-packs &&
+       git -C member repack --geometric=2 -l -d &&
+       test-tool chmtime --verbose member/.git/objects/pack/* >actual-member-packs &&
+       test_cmp expected-member-packs actual-member-packs &&
+
+       {
+               packed_objects shared/.git/objects/pack/pack-*.idx &&
+               packed_objects member/.git/objects/pack/pack-*.idx
+       } | sort >expected-objects &&
+
+       # On the other hand, when doing a non-local geometric repack we should
+       # see both packfiles and thus repack them. We expect that the shared
+       # object database was not changed.
+       test-tool chmtime --verbose =0 shared/.git/objects/pack/* >expected-shared-packs &&
+       git -C member repack --geometric=2 -d &&
+       test-tool chmtime --verbose shared/.git/objects/pack/* >actual-shared-packs &&
+       test_cmp expected-shared-packs actual-shared-packs &&
+
+       # Furthermore, we expect that the member repository now has a single
+       # packfile that contains the combined shared and non-shared objects.
+       ls member/.git/objects/pack/pack-*.idx >actual &&
+       test_line_count = 1 actual &&
+       packed_objects member/.git/objects/pack/pack-*.idx >actual-objects &&
+       test_line_count = 6 actual-objects &&
+       test_cmp expected-objects actual-objects
+'
+
+test_expect_success '--geometric -l disables writing bitmaps with non-local packfiles' '
+       test_when_finished "rm -fr shared member" &&
+
+       git init shared &&
+       test_commit_bulk -C shared --start=1 1 &&
+
+       git clone --shared shared member &&
+       test_commit_bulk -C member --start=2 1 &&
+
+       # When performing a geometric repack with `-l` while connected to an
+       # alternate object database that has a packfile we do not have full
+       # coverage of objects. As a result, we expect that writing the bitmap
+       # will be disabled.
+       git -C member repack -l --geometric=2 --write-midx --write-bitmap-index 2>err &&
+       cat >expect <<-EOF &&
+       warning: disabling bitmap writing, as some objects are not being packed
+       EOF
+       test_cmp expect err &&
+       test_path_is_missing member/.git/objects/pack/multi-pack-index-*.bitmap &&
+
+       # On the other hand, when we repack without `-l`, we should see that
+       # the bitmap gets created.
+       git -C member repack --geometric=2 --write-midx --write-bitmap-index 2>err &&
+       test_must_be_empty err &&
+       test_path_is_file member/.git/objects/pack/multi-pack-index-*.bitmap
+'
+
 test_done
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
new file mode 100755 (executable)
index 0000000..be3735d
--- /dev/null
@@ -0,0 +1,414 @@
+#!/bin/sh
+
+test_description='git repack works correctly'
+
+. ./test-lib.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+test_expect_success '--expire-to stores pruned objects (now)' '
+       git init expire-to-now &&
+       (
+               cd expire-to-now &&
+
+               git branch -M main &&
+
+               test_commit base &&
+
+               git checkout -b cruft &&
+               test_commit --no-tag cruft &&
+
+               git rev-list --objects --no-object-names main..cruft >moved.raw &&
+               sort moved.raw >moved.want &&
+
+               git rev-list --all --objects --no-object-names >expect.raw &&
+               sort expect.raw >expect &&
+
+               git checkout main &&
+               git branch -D cruft &&
+               git reflog expire --all --expire=all &&
+
+               git init --bare expired.git &&
+               git repack -d \
+                       --cruft --cruft-expiration="now" \
+                       --expire-to="expired.git/objects/pack/pack" &&
+
+               expired="$(ls expired.git/objects/pack/pack-*.idx)" &&
+               test_path_is_file "${expired%.idx}.mtimes" &&
+
+               # Since the `--cruft-expiration` is "now", the effective
+               # behavior is to move _all_ unreachable objects out to
+               # the location in `--expire-to`.
+               git show-index <$expired >expired.raw &&
+               cut -d" " -f2 expired.raw | sort >expired.objects &&
+               git rev-list --all --objects --no-object-names \
+                       >remaining.objects &&
+
+               # ...in other words, the combined contents of this
+               # repository and expired.git should be the same as the
+               # set of objects we started with.
+               cat expired.objects remaining.objects | sort >actual &&
+               test_cmp expect actual &&
+
+               # The "moved" objects (i.e., those in expired.git)
+               # should be the same as the cruft objects which were
+               # expired in the previous step.
+               test_cmp moved.want expired.objects
+       )
+'
+
+test_expect_success '--expire-to stores pruned objects (5.minutes.ago)' '
+       git init expire-to-5.minutes.ago &&
+       (
+               cd expire-to-5.minutes.ago &&
+
+               git branch -M main &&
+
+               test_commit base &&
+
+               # Create two classes of unreachable objects, one which
+               # is older than 5 minutes (stale), and another which is
+               # newer (recent).
+               for kind in stale recent
+               do
+                       git checkout -b $kind main &&
+                       test_commit --no-tag $kind || return 1
+               done &&
+
+               git rev-list --objects --no-object-names main..stale >in &&
+               stale="$(git pack-objects $objdir/pack/pack <in)" &&
+               mtime="$(test-tool chmtime --get =-600 $objdir/pack/pack-$stale.pack)" &&
+
+               # expect holds the set of objects we expect to find in
+               # this repository after repacking
+               git rev-list --objects --no-object-names recent >expect.raw &&
+               sort expect.raw >expect &&
+
+               # moved.want holds the set of objects we expect to find
+               # in expired.git
+               git rev-list --objects --no-object-names main..stale >out &&
+               sort out >moved.want &&
+
+               git checkout main &&
+               git branch -D stale recent &&
+               git reflog expire --all --expire=all &&
+               git prune-packed &&
+
+               git init --bare expired.git &&
+               git repack -d \
+                       --cruft --cruft-expiration=5.minutes.ago \
+                       --expire-to="expired.git/objects/pack/pack" &&
+
+               # Some of the remaining objects in this repository are
+               # unreachable, so use `cat-file --batch-all-objects`
+               # instead of `rev-list` to get their names
+               git cat-file --batch-all-objects --batch-check="%(objectname)" \
+                       >remaining.objects &&
+               sort remaining.objects >actual &&
+               test_cmp expect actual &&
+
+               (
+                       cd expired.git &&
+
+                       expired="$(ls objects/pack/pack-*.mtimes)" &&
+                       test-tool pack-mtimes $(basename $expired) >out &&
+                       cut -d" " -f1 out | sort >../moved.got &&
+
+                       # Ensure that there are as many objects with the
+                       # expected mtime as were moved to expired.git.
+                       #
+                       # In other words, ensure that the recorded
+                       # mtimes of any moved objects was written
+                       # correctly.
+                       grep " $mtime$" out >matching &&
+                       test_line_count = $(wc -l <../moved.want) matching
+               ) &&
+               test_cmp moved.want moved.got
+       )
+'
+
+generate_random_blob() {
+       test-tool genrandom "$@" >blob &&
+       git hash-object -w -t blob blob &&
+       rm blob
+}
+
+pack_random_blob () {
+       generate_random_blob "$@" &&
+       git repack -d -q >/dev/null
+}
+
+generate_cruft_pack () {
+       pack_random_blob "$@" >/dev/null &&
+
+       ls $packdir/pack-*.pack | xargs -n 1 basename >in &&
+       pack="$(git pack-objects --cruft $packdir/pack <in)" &&
+       git prune-packed &&
+
+       echo "$packdir/pack-$pack.mtimes"
+}
+
+test_expect_success '--max-cruft-size creates new packs when above threshold' '
+       git init max-cruft-size-large &&
+       (
+               cd max-cruft-size-large &&
+               test_commit base &&
+
+               foo="$(pack_random_blob foo $((1*1024*1024)))" &&
+               git repack --cruft -d &&
+               cruft_foo="$(ls $packdir/pack-*.mtimes)" &&
+
+               bar="$(pack_random_blob bar $((1*1024*1024)))" &&
+               git repack --cruft -d --max-cruft-size=1M &&
+               cruft_bar="$(ls $packdir/pack-*.mtimes | grep -v $cruft_foo)" &&
+
+               test-tool pack-mtimes $(basename "$cruft_foo") >foo.objects &&
+               test-tool pack-mtimes $(basename "$cruft_bar") >bar.objects &&
+
+               grep "^$foo" foo.objects &&
+               test_line_count = 1 foo.objects &&
+               grep "^$bar" bar.objects &&
+               test_line_count = 1 bar.objects
+       )
+'
+
+test_expect_success '--max-cruft-size combines existing packs when below threshold' '
+       git init max-cruft-size-small &&
+       (
+               cd max-cruft-size-small &&
+               test_commit base &&
+
+               foo="$(pack_random_blob foo $((1*1024*1024)))" &&
+               git repack --cruft -d &&
+
+               bar="$(pack_random_blob bar $((1*1024*1024)))" &&
+               git repack --cruft -d --max-cruft-size=10M &&
+
+               cruft=$(ls $packdir/pack-*.mtimes) &&
+               test-tool pack-mtimes $(basename "$cruft") >cruft.objects &&
+
+               grep "^$foo" cruft.objects &&
+               grep "^$bar" cruft.objects &&
+               test_line_count = 2 cruft.objects
+       )
+'
+
+test_expect_success '--max-cruft-size combines smaller packs first' '
+       git init max-cruft-size-consume-small &&
+       (
+               cd max-cruft-size-consume-small &&
+
+               test_commit base &&
+               git repack -ad &&
+
+               cruft_foo="$(generate_cruft_pack foo 524288)" &&    # 0.5 MiB
+               cruft_bar="$(generate_cruft_pack bar 524288)" &&    # 0.5 MiB
+               cruft_baz="$(generate_cruft_pack baz 1048576)" &&   # 1.0 MiB
+               cruft_quux="$(generate_cruft_pack quux 1572864)" && # 1.5 MiB
+
+               test-tool pack-mtimes "$(basename $cruft_foo)" >expect.raw &&
+               test-tool pack-mtimes "$(basename $cruft_bar)" >>expect.raw &&
+               sort expect.raw >expect.objects &&
+
+               # repacking with `--max-cruft-size=2M` should combine
+               # both 0.5 MiB packs together, instead of, say, one of
+               # the 0.5 MiB packs with the 1.0 MiB pack
+               ls $packdir/pack-*.mtimes | sort >cruft.before &&
+               git repack -d --cruft --max-cruft-size=2M &&
+               ls $packdir/pack-*.mtimes | sort >cruft.after &&
+
+               comm -13 cruft.before cruft.after >cruft.new &&
+               comm -23 cruft.before cruft.after >cruft.removed &&
+
+               test_line_count = 1 cruft.new &&
+               test_line_count = 2 cruft.removed &&
+
+               # the two smaller packs should be rolled up first
+               printf "%s\n" $cruft_foo $cruft_bar | sort >expect.removed &&
+               test_cmp expect.removed cruft.removed &&
+
+               # ...and contain the set of objects rolled up
+               test-tool pack-mtimes "$(basename $(cat cruft.new))" >actual.raw &&
+               sort actual.raw >actual.objects &&
+
+               test_cmp expect.objects actual.objects
+       )
+'
+
+test_expect_success 'setup --max-cruft-size with freshened objects' '
+       git init max-cruft-size-freshen &&
+       (
+               cd max-cruft-size-freshen &&
+
+               test_commit base &&
+               git repack -ad &&
+
+               foo="$(generate_random_blob foo 64)" &&
+               test-tool chmtime --get -10000 \
+                       "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
+
+               git repack --cruft -d &&
+
+               cruft="$(ls $packdir/pack-*.mtimes)" &&
+               test-tool pack-mtimes "$(basename $cruft)" >actual &&
+               echo "$foo $(cat foo.mtime)" >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '--max-cruft-size with freshened objects (loose)' '
+       (
+               cd max-cruft-size-freshen &&
+
+               # regenerate the object, setting its mtime to be more recent
+               foo="$(generate_random_blob foo 64)" &&
+               test-tool chmtime --get -100 \
+                       "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
+
+               git repack --cruft -d &&
+
+               cruft="$(ls $packdir/pack-*.mtimes)" &&
+               test-tool pack-mtimes "$(basename $cruft)" >actual &&
+               echo "$foo $(cat foo.mtime)" >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '--max-cruft-size with freshened objects (packed)' '
+       (
+               cd max-cruft-size-freshen &&
+
+               # regenerate the object and store it in a packfile,
+               # setting its mtime to be more recent
+               #
+               # store it alongside another cruft object so that we
+               # do not create an identical copy of the existing
+               # cruft pack (which contains $foo).
+               foo="$(generate_random_blob foo 64)" &&
+               bar="$(generate_random_blob bar 64)" &&
+               foo_pack="$(printf "%s\n" $foo $bar | git pack-objects $packdir/pack)" &&
+               git prune-packed &&
+
+               test-tool chmtime --get -10 \
+                       "$packdir/pack-$foo_pack.pack" >foo.mtime &&
+
+               git repack --cruft -d &&
+
+               cruft="$(ls $packdir/pack-*.mtimes)" &&
+               test-tool pack-mtimes "$(basename $cruft)" >actual &&
+               echo "$foo $(cat foo.mtime)" >expect.raw &&
+               echo "$bar $(cat foo.mtime)" >>expect.raw &&
+               sort expect.raw >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '--max-cruft-size with pruning' '
+       git init max-cruft-size-prune &&
+       (
+               cd max-cruft-size-prune &&
+
+               test_commit base &&
+               foo="$(generate_random_blob foo $((1024*1024)))" &&
+               bar="$(generate_random_blob bar $((1024*1024)))" &&
+               baz="$(generate_random_blob baz $((1024*1024)))" &&
+
+               test-tool chmtime -10000 "$objdir/$(test_oid_to_path "$foo")" &&
+
+               git repack -d --cruft --max-cruft-size=1M &&
+
+               # backdate the mtimes of all cruft packs to validate
+               # that they were rewritten as a result of pruning
+               ls $packdir/pack-*.mtimes | sort >cruft.before &&
+               for cruft in $(cat cruft.before)
+               do
+                       mtime="$(test-tool chmtime --get -10000 "$cruft")" &&
+                       echo $cruft $mtime >>mtimes || return 1
+               done &&
+
+               # repack (and prune) with a --max-cruft-size to ensure
+               # that we appropriately split the resulting set of packs
+               git repack -d --cruft --max-cruft-size=1M \
+                       --cruft-expiration=10.seconds.ago &&
+               ls $packdir/pack-*.mtimes | sort >cruft.after &&
+
+               for cruft in $(cat cruft.after)
+               do
+                       old_mtime="$(grep $cruft mtimes | cut -d" " -f2)" &&
+                       new_mtime="$(test-tool chmtime --get $cruft)" &&
+                       test $old_mtime -lt $new_mtime || return 1
+               done &&
+
+               test_line_count = 3 cruft.before &&
+               test_line_count = 2 cruft.after &&
+               test_must_fail git cat-file -e $foo &&
+               git cat-file -e $bar &&
+               git cat-file -e $baz
+       )
+'
+
+test_expect_success '--max-cruft-size ignores non-local packs' '
+       repo="max-cruft-size-non-local" &&
+       git init $repo &&
+       (
+               cd $repo &&
+               test_commit base &&
+               generate_random_blob foo 64 &&
+               git repack --cruft -d
+       ) &&
+
+       git clone --reference=$repo $repo $repo-alt &&
+       (
+               cd $repo-alt &&
+
+               test_commit other &&
+               generate_random_blob bar 64 &&
+
+               # ensure that we do not attempt to pick up packs from
+               # the non-alternated repository, which would result in a
+               # crash
+               git repack --cruft --max-cruft-size=1M -d
+       )
+'
+
+test_expect_success 'reachable packs are preferred over cruft ones' '
+       repo="cruft-preferred-packs" &&
+       git init "$repo" &&
+       (
+               cd "$repo" &&
+
+               # This test needs to exercise careful control over when a MIDX
+               # is and is not written. Unset the corresponding TEST variable
+               # accordingly.
+               sane_unset GIT_TEST_MULTI_PACK_INDEX &&
+
+               test_commit base &&
+               test_commit --no-tag cruft &&
+
+               non_cruft="$(echo base | git pack-objects --revs $packdir/pack)" &&
+               # Write a cruft pack which both (a) sorts ahead of the non-cruft
+               # pack in lexical order, and (b) has an older mtime to appease
+               # the MIDX preferred pack selection routine.
+               cruft="$(echo pack-$non_cruft.pack | git pack-objects --cruft $packdir/pack-A)" &&
+               test-tool chmtime -1000 $packdir/pack-A-$cruft.pack &&
+
+               test_commit other &&
+               git repack -d &&
+
+               git repack --geometric 2 -d --write-midx --write-bitmap-index &&
+
+               # After repacking, there are two packs left: one reachable one
+               # (which is the result of combining both of the existing two
+               # non-cruft packs), and one cruft pack.
+               find .git/objects/pack -type f -name "*.pack" >packs &&
+               test_line_count = 2 packs &&
+
+               # Make sure that the pack we just wrote is marked as preferred,
+               # not the cruft one.
+               pack="$(test-tool read-midx --preferred-pack $objdir)" &&
+               test_path_is_missing "$packdir/$(basename "$pack" ".idx").mtimes"
+       )
+'
+
+test_done
index 24297e26ca028bd0b36aa29803a51c6ddde73f0c..6a36be1e63c2bb84871e521349917be30ec6b3e3 100755 (executable)
@@ -28,14 +28,14 @@ prompt_given ()
 
 test_expect_success 'basic usage requires no repo' '
        test_expect_code 129 git difftool -h >output &&
-       test_i18ngrep ^usage: output &&
+       test_grep ^usage: output &&
        # create a ceiling directory to prevent Git from finding a repo
        mkdir -p not/repo &&
        test_when_finished rm -r not &&
        test_expect_code 129 \
        env GIT_CEILING_DIRECTORIES="$(pwd)/not" \
        git -C not/repo difftool -h >output &&
-       test_i18ngrep ^usage: output
+       test_grep ^usage: output
 '
 
 # Create a file on main and change it on branch
@@ -155,6 +155,58 @@ test_expect_success 'difftool honors --gui' '
        test_cmp expect actual
 '
 
+test_expect_success 'difftool with guiDefault auto selects gui tool when there is DISPLAY' '
+       difftool_test_setup &&
+       test_config merge.tool bogus-tool &&
+       test_config diff.tool bogus-tool &&
+       test_config diff.guitool test-tool &&
+       test_config difftool.guiDefault auto &&
+       DISPLAY=SOMETHING && export DISPLAY &&
+
+       echo branch >expect &&
+       git difftool --no-prompt branch >actual &&
+       test_cmp expect actual
+'
+test_expect_success 'difftool with guiDefault auto selects regular tool when no DISPLAY' '
+       difftool_test_setup &&
+       test_config diff.guitool bogus-tool &&
+       test_config diff.tool test-tool &&
+       test_config difftool.guiDefault Auto &&
+       DISPLAY= && export DISPLAY &&
+
+       echo branch >expect &&
+       git difftool --no-prompt branch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'difftool with guiDefault true selects gui tool' '
+       difftool_test_setup &&
+       test_config diff.tool bogus-tool &&
+       test_config diff.guitool test-tool &&
+       test_config difftool.guiDefault true &&
+
+       DISPLAY= && export DISPLAY &&
+       echo branch >expect &&
+       git difftool --no-prompt branch >actual &&
+       test_cmp expect actual &&
+
+       DISPLAY=Something && export DISPLAY &&
+       echo branch >expect &&
+       git difftool --no-prompt branch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'difftool --no-gui trumps config guiDefault' '
+       difftool_test_setup &&
+       test_config diff.guitool bogus-tool &&
+       test_config diff.tool test-tool &&
+       test_config difftool.guiDefault true &&
+
+       echo branch >expect &&
+       git difftool --no-prompt --no-gui branch >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'difftool --gui last setting wins' '
        difftool_test_setup &&
        : >expect &&
index 0f937990a062e30b5f11baa296c88472019ac338..875dcfd98f3a0eb5e47b7f32b15da43b27a50577 100755 (executable)
@@ -18,6 +18,9 @@ test_invalid_grep_expression() {
        '
 }
 
+LC_ALL=en_US.UTF-8 test-tool regex '^.$' '¿' &&
+  test_set_prereq MB_REGEX
+
 cat >hello.c <<EOF
 #include <assert.h>
 #include <stdio.h>
@@ -88,6 +91,10 @@ test_expect_success setup '
                echo unusual >"\"unusual\" pathname" &&
                echo unusual >"t/nested \"unusual\" pathname"
        fi &&
+       if test_have_prereq MB_REGEX
+       then
+               echo "¿" >reverse-question-mark
+       fi &&
        git add . &&
        test_tick &&
        git commit -m initial
@@ -569,6 +576,14 @@ do
        '
 done
 
+test_expect_success MB_REGEX 'grep exactly one char in single-char multibyte file' '
+       LC_ALL=en_US.UTF-8 git grep "^.$" reverse-question-mark
+'
+
+test_expect_success MB_REGEX 'grep two chars in single-char multibyte file' '
+       LC_ALL=en_US.UTF-8 test_expect_code 1 git grep ".." reverse-question-mark
+'
+
 cat >expected <<EOF
 file
 EOF
@@ -793,6 +808,19 @@ test_expect_success 'grep -f, ignore empty lines, read patterns from stdin' '
        test_cmp expected actual
 '
 
+test_expect_success 'grep -f, use cwd relative file' '
+       test_when_finished "git rm -f sub/dir/file" &&
+       mkdir -p sub/dir &&
+       echo hit >sub/dir/file &&
+       git add sub/dir/file &&
+       echo hit >sub/dir/pattern &&
+       echo miss >pattern &&
+       (
+               cd sub/dir && git grep -f pattern file
+       ) &&
+       git -C sub/dir grep -f pattern file
+'
+
 cat >expected <<EOF
 y:y yy
 --
@@ -986,7 +1014,9 @@ test_expect_success 'log --committer does not search in timestamp' '
 test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&
-       test "$(git grep test)" = "t/t:test" &&
+       echo "t/t:test" >expect &&
+       git grep test >actual &&
+       test_cmp expect actual &&
        git update-index --no-assume-unchanged t/t &&
        git checkout t/t
 '
@@ -1217,6 +1247,33 @@ test_expect_success 'outside of git repository with fallbackToNoIndex' '
        )
 '
 
+test_expect_success 'no repository with path outside $cwd' '
+       test_when_finished rm -fr non &&
+       rm -fr non &&
+       mkdir -p non/git/sub non/tig &&
+       (
+               GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+               test_expect_code 128 git grep --no-index search .. 2>error &&
+               grep "is outside the directory tree" error
+       ) &&
+       (
+               GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+               test_expect_code 128 git grep --no-index search ../tig 2>error &&
+               grep "is outside the directory tree" error
+       ) &&
+       (
+               GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+               test_expect_code 128 git grep --no-index search ../non 2>error &&
+               grep "no such path in the working tree" error
+       )
+'
+
 test_expect_success 'inside git repository but with --no-index' '
        rm -fr is &&
        mkdir -p is/git/sub &&
@@ -1369,7 +1426,7 @@ test_expect_success 'grep --no-index pattern -- path' '
 
 test_expect_success 'grep --no-index complains of revs' '
        test_must_fail git grep --no-index o main -- 2>err &&
-       test_i18ngrep "cannot be used with revs" err
+       test_grep "cannot be used with revs" err
 '
 
 test_expect_success 'grep --no-index prefers paths to revs' '
@@ -1382,7 +1439,7 @@ test_expect_success 'grep --no-index prefers paths to revs' '
 
 test_expect_success 'grep --no-index does not "diagnose" revs' '
        test_must_fail git grep --no-index o :1:hello.c 2>err &&
-       test_i18ngrep ! -i "did you mean" err
+       test_grep ! -i "did you mean" err
 '
 
 cat >expected <<EOF
index 1dd07141a7df9f9666ac01a611c6c6a81ddc6424..fe38d88a1a6ab151cb318429ec64c1c49ff24fb3 100755 (executable)
@@ -63,7 +63,7 @@ test_expect_success SIMPLEPAGER 'git grep -O' '
 
 test_expect_success 'git grep -O --cached' '
        test_must_fail git grep --cached -O GREP_PATTERN >out 2>msg &&
-       test_i18ngrep open-files-in-pager msg
+       test_grep open-files-in-pager msg
 '
 
 test_expect_success 'git grep -O --no-index' '
index 8143817b19e7962460387b58b7c807e0c995090e..167fe661504a2b609583750884bb40a902408e04 100755 (executable)
@@ -348,7 +348,7 @@ test_incompatible_with_recurse_submodules ()
 {
        test_expect_success "--recurse-submodules and $1 are incompatible" "
                test_must_fail git grep -e. --recurse-submodules $1 2>actual &&
-               test_i18ngrep 'not supported with --recurse-submodules' actual
+               test_grep 'not supported with --recurse-submodules' actual
        "
 }
 
@@ -594,4 +594,44 @@ test_expect_success 'grep partially-cloned submodule' '
        )
 '
 
+test_expect_success 'check scope of core.useReplaceRefs' '
+       git init base &&
+       git init base/sub &&
+
+       echo A >base/a &&
+       echo B >base/b &&
+       echo C >base/sub/c &&
+       echo D >base/sub/d &&
+
+       git -C base/sub add c d &&
+       git -C base/sub commit -m "Add files" &&
+
+       git -C base submodule add ./sub &&
+       git -C base add a b sub &&
+       git -C base commit -m "Add files and submodule" &&
+
+       A=$(git -C base rev-parse HEAD:a) &&
+       B=$(git -C base rev-parse HEAD:b) &&
+       C=$(git -C base/sub rev-parse HEAD:c) &&
+       D=$(git -C base/sub rev-parse HEAD:d) &&
+
+       git -C base replace $A $B &&
+       git -C base/sub replace $C $D &&
+
+       test_must_fail git -C base grep --cached --recurse-submodules A &&
+       test_must_fail git -C base grep --cached --recurse-submodules C &&
+
+       git -C base config core.useReplaceRefs false &&
+       git -C base grep --recurse-submodules A &&
+       test_must_fail git -C base grep --cached --recurse-submodules C &&
+
+       git -C base/sub config core.useReplaceRefs false &&
+       git -C base grep --cached --recurse-submodules A &&
+       git -C base grep --cached --recurse-submodules C &&
+
+       git -C base config --unset core.useReplaceRefs &&
+       test_must_fail git -C base grep --cached --recurse-submodules A &&
+       git -C base grep --cached --recurse-submodules C
+'
+
 test_done
index fdb2355649e31a8153f3ff60fe3ffbb531a2a7aa..4353be5adb7b0605e5bc3db9b1b29ecf0251fbf2 100755 (executable)
@@ -26,7 +26,7 @@ nul_match_internal () {
                        >stderr &&
                        printf '$pattern' | q_to_nul >f &&
                        test_must_fail env LC_ALL=\"$lc_all\" git grep $extra_flags -f f $flags a 2>stderr &&
-                       test_i18ngrep ! 'This is only supported with -P under PCRE v2' stderr
+                       test_grep ! 'This is only supported with -P under PCRE v2' stderr
                "
        elif test "$matches" = P
        then
@@ -34,7 +34,7 @@ nul_match_internal () {
                        >stderr &&
                        printf '$pattern' | q_to_nul >f &&
                        test_must_fail env LC_ALL=\"$lc_all\" git grep -f f $flags a 2>stderr &&
-                       test_i18ngrep 'This is only supported with -P under PCRE v2' stderr
+                       test_grep 'This is only supported with -P under PCRE v2' stderr
                "
        else
                test_expect_success "PANIC: Test framework error. Unknown matches value $matches" 'false'
index 2724a44fe3ef240aa09077f770706ed022a99b7e..00d29871e659ccb34ef7204017ab07dbefa83c84 100755 (executable)
@@ -33,13 +33,13 @@ test_systemd_analyze_verify () {
 
 test_expect_success 'help text' '
        test_expect_code 129 git maintenance -h >actual &&
-       test_i18ngrep "usage: git maintenance <subcommand>" actual &&
+       test_grep "usage: git maintenance <subcommand>" actual &&
        test_expect_code 129 git maintenance barf 2>err &&
-       test_i18ngrep "unknown subcommand: \`barf'\''" err &&
-       test_i18ngrep "usage: git maintenance" err &&
+       test_grep "unknown subcommand: \`barf'\''" err &&
+       test_grep "usage: git maintenance" err &&
        test_expect_code 129 git maintenance 2>err &&
-       test_i18ngrep "error: need a subcommand" err &&
-       test_i18ngrep "usage: git maintenance" err
+       test_grep "error: need a subcommand" err &&
+       test_grep "usage: git maintenance" err
 '
 
 test_expect_success 'run [--auto|--quiet]' '
@@ -131,12 +131,12 @@ test_expect_success 'commit-graph auto condition' '
 
 test_expect_success 'run --task=bogus' '
        test_must_fail git maintenance run --task=bogus 2>err &&
-       test_i18ngrep "is not a valid task" err
+       test_grep "is not a valid task" err
 '
 
 test_expect_success 'run --task duplicate' '
        test_must_fail git maintenance run --task=gc --task=gc 2>err &&
-       test_i18ngrep "cannot be selected multiple times" err
+       test_grep "cannot be selected multiple times" err
 '
 
 test_expect_success 'run --task=prefetch with no remotes' '
@@ -157,7 +157,8 @@ test_expect_success 'prefetch multiple remotes' '
        fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
        test_subcommand git fetch remote1 $fetchargs <run-prefetch.txt &&
        test_subcommand git fetch remote2 $fetchargs <run-prefetch.txt &&
-       test_path_is_missing .git/refs/remotes &&
+       git for-each-ref refs/remotes >actual &&
+       test_must_be_empty actual &&
        git log prefetch/remotes/remote1/one &&
        git log prefetch/remotes/remote2/two &&
        git fetch --all &&
@@ -377,12 +378,12 @@ test_expect_success 'pack-refs task' '
 
 test_expect_success '--auto and --schedule incompatible' '
        test_must_fail git maintenance run --auto --schedule=daily 2>err &&
-       test_i18ngrep "at most one" err
+       test_grep "at most one" err
 '
 
 test_expect_success 'invalid --schedule value' '
        test_must_fail git maintenance run --schedule=annually 2>err &&
-       test_i18ngrep "unrecognized --schedule" err
+       test_grep "unrecognized --schedule" err
 '
 
 test_expect_success '--schedule inheritance weekly -> daily -> hourly' '
@@ -480,6 +481,11 @@ test_expect_success 'maintenance.strategy inheritance' '
 
 test_expect_success 'register and unregister' '
        test_when_finished git config --global --unset-all maintenance.repo &&
+
+       test_must_fail git maintenance unregister 2>err &&
+       grep "is not registered" err &&
+       git maintenance unregister --force &&
+
        git config --global --add maintenance.repo /existing1 &&
        git config --global --add maintenance.repo /existing2 &&
        git config --global --get-all maintenance.repo >before &&
@@ -493,7 +499,68 @@ test_expect_success 'register and unregister' '
 
        git maintenance unregister &&
        git config --global --get-all maintenance.repo >actual &&
-       test_cmp before actual
+       test_cmp before actual &&
+
+       git config --file ./other --add maintenance.repo /existing1 &&
+       git config --file ./other --add maintenance.repo /existing2 &&
+       git config --file ./other --get-all maintenance.repo >before &&
+
+       git maintenance register --config-file ./other &&
+       test_cmp_config false maintenance.auto &&
+       git config --file ./other --get-all maintenance.repo >between &&
+       cp before expect &&
+       pwd >>expect &&
+       test_cmp expect between &&
+
+       git maintenance unregister --config-file ./other &&
+       git config --file ./other --get-all maintenance.repo >actual &&
+       test_cmp before actual &&
+
+       test_must_fail git maintenance unregister 2>err &&
+       grep "is not registered" err &&
+       git maintenance unregister --force &&
+
+       test_must_fail git maintenance unregister --config-file ./other 2>err &&
+       grep "is not registered" err &&
+       git maintenance unregister --config-file ./other --force
+'
+
+test_expect_success 'register with no value for maintenance.repo' '
+       cp .git/config .git/config.orig &&
+       test_when_finished mv .git/config.orig .git/config &&
+
+       cat >>.git/config <<-\EOF &&
+       [maintenance]
+               repo
+       EOF
+       cat >expect <<-\EOF &&
+       error: missing value for '\''maintenance.repo'\''
+       EOF
+       git maintenance register 2>actual &&
+       test_cmp expect actual &&
+       git config maintenance.repo
+'
+
+test_expect_success 'unregister with no value for maintenance.repo' '
+       cp .git/config .git/config.orig &&
+       test_when_finished mv .git/config.orig .git/config &&
+
+       cat >>.git/config <<-\EOF &&
+       [maintenance]
+               repo
+       EOF
+       cat >expect <<-\EOF &&
+       error: missing value for '\''maintenance.repo'\''
+       EOF
+       test_expect_code 128 git maintenance unregister 2>actual.raw &&
+       grep ^error actual.raw >actual &&
+       test_cmp expect actual &&
+       git config maintenance.repo &&
+
+       git maintenance unregister --force 2>actual.raw &&
+       grep ^error actual.raw >actual &&
+       test_cmp expect actual &&
+       git config maintenance.repo
 '
 
 test_expect_success !MINGW 'register and unregister with regex metacharacters' '
@@ -510,15 +577,15 @@ test_expect_success !MINGW 'register and unregister with regex metacharacters' '
 
 test_expect_success 'start --scheduler=<scheduler>' '
        test_expect_code 129 git maintenance start --scheduler=foo 2>err &&
-       test_i18ngrep "unrecognized --scheduler argument" err &&
+       test_grep "unrecognized --scheduler argument" err &&
 
        test_expect_code 129 git maintenance start --no-scheduler 2>err &&
-       test_i18ngrep "unknown option" err &&
+       test_grep "unknown option" err &&
 
        test_expect_code 128 \
                env GIT_TEST_MAINT_SCHEDULER="launchctl:true,schtasks:true" \
                git maintenance start --scheduler=crontab 2>err &&
-       test_i18ngrep "fatal: crontab scheduler is not available" err
+       test_grep "fatal: crontab scheduler is not available" err
 '
 
 test_expect_success 'start from empty cron table' '
@@ -678,7 +745,15 @@ test_expect_success 'start and stop Linux/systemd maintenance' '
        # start registers the repo
        git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
 
-       test_systemd_analyze_verify "systemd/user/git-maintenance@.service" &&
+       for schedule in hourly daily weekly
+       do
+               test_path_is_file "systemd/user/git-maintenance@$schedule.timer" || return 1
+       done &&
+       test_path_is_file "systemd/user/git-maintenance@.service" &&
+
+       test_systemd_analyze_verify "systemd/user/git-maintenance@hourly.service" &&
+       test_systemd_analyze_verify "systemd/user/git-maintenance@daily.service" &&
+       test_systemd_analyze_verify "systemd/user/git-maintenance@weekly.service" &&
 
        printf -- "--user enable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
        test_cmp expect args &&
@@ -689,7 +764,10 @@ test_expect_success 'start and stop Linux/systemd maintenance' '
        # stop does not unregister the repo
        git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
 
-       test_path_is_missing "systemd/user/git-maintenance@.timer" &&
+       for schedule in hourly daily weekly
+       do
+               test_path_is_missing "systemd/user/git-maintenance@$schedule.timer" || return 1
+       done &&
        test_path_is_missing "systemd/user/git-maintenance@.service" &&
 
        printf -- "--user disable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
@@ -772,4 +850,17 @@ test_expect_success 'register and unregister bare repo' '
        )
 '
 
+test_expect_success 'failed schedule prevents config change' '
+       git init --bare failcase &&
+
+       for scheduler in crontab launchctl schtasks systemctl
+       do
+               GIT_TEST_MAINT_SCHEDULER="$scheduler:false" &&
+               export GIT_TEST_MAINT_SCHEDULER &&
+               test_must_fail \
+                       git -C failcase maintenance start &&
+               test_must_fail git -C failcase config maintenance.auto || return 1
+       done
+'
+
 test_done
index d751d48b7dae6a3e007d5eb19f92cd41eb445065..731265541acf9fb0ed4dcc216b662daa897f814d 100755 (executable)
@@ -201,13 +201,13 @@ committer David Reiss <dreiss@facebook.com> 1234567890 +0000
 
 some message
 EOF
-  COMMIT=$(git hash-object -t commit -w badcommit) &&
+  COMMIT=$(git hash-object --literally -t commit -w badcommit) &&
   git --no-pager blame $COMMIT -- uno >/dev/null
 '
 
 test_expect_success 'blame -L with invalid start' '
        test_must_fail git blame -L5 tres 2>errors &&
-       test_i18ngrep "has only 2 lines" errors
+       test_grep "has only 2 lines" errors
 '
 
 test_expect_success 'blame -L with invalid end' '
index b18633dee1bfb2339033f1d0ab708979e98da018..9a03b0f361ff6db76fb9df2b8bd6dd8d50c4d57b 100755 (executable)
@@ -129,14 +129,14 @@ test_expect_success override_ignore_revs_file '
        '
 test_expect_success bad_files_and_revs '
        test_must_fail git blame file --ignore-rev NOREV 2>err &&
-       test_i18ngrep "cannot find revision NOREV to ignore" err &&
+       test_grep "cannot find revision NOREV to ignore" err &&
 
        test_must_fail git blame file --ignore-revs-file NOFILE 2>err &&
-       test_i18ngrep "could not open.*: NOFILE" err &&
+       test_grep "could not open.*: NOFILE" err &&
 
        echo NOREV >ignore_norev &&
        test_must_fail git blame file --ignore-revs-file ignore_norev 2>err &&
-       test_i18ngrep "invalid object name: NOREV" err
+       test_grep "invalid object name: NOREV" err
 '
 
 # For ignored revs that have added 'unblamable' lines, mark those lines with a
index 01c74b8b0756caf01483f8cafbdda55eeb7d0ddb..5a771000c9ca0de56bf21e4d8212a6581727b189 100755 (executable)
@@ -12,7 +12,7 @@ PREREQ="PERL"
 
 replace_variable_fields () {
        sed     -e "s/^\(Date:\).*/\1 DATE-STRING/" \
-               -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+               -e "s/^\(Message-ID:\).*/\1 MESSAGE-ID-STRING/" \
                -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/"
 }
 
@@ -47,7 +47,7 @@ clean_fake_sendmail () {
 
 test_expect_success $PREREQ 'Extract patches' '
        patches=$(git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1) &&
-       threaded_patches=$(git format-patch -o threaded -s --in-reply-to="format" HEAD^1)
+       threaded_patches=$(git format-patch -o threaded --thread=shallow -s --in-reply-to="format" HEAD^1)
 '
 
 # Test no confirm early to ensure remaining tests will not hang
@@ -61,8 +61,8 @@ test_no_confirm () {
                --smtp-server="$(pwd)/fake.sendmail" \
                $@ \
                $patches >stdout &&
-               ! grep "Send this email" stdout &&
-               >no_confirm_okay
+       ! grep "Send this email" stdout &&
+       >no_confirm_okay
 }
 
 # Exit immediately to prevent hang if a no-confirm test fails
@@ -225,7 +225,7 @@ Cc: cc@example.com,
        two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 In-Reply-To: <unique-message-id@example.com>
 References: <unique-message-id@example.com>
@@ -337,13 +337,14 @@ test_expect_success $PREREQ 'Show all headers' '
 test_expect_success $PREREQ 'Prompting works' '
        clean_fake_sendmail &&
        (echo "to@example.com" &&
-        echo ""
+        echo "my-message-id@example.com"
        ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
                $patches \
                2>errors &&
                grep "^From: A U Thor <author@example.com>\$" msgtxt1 &&
-               grep "^To: to@example.com\$" msgtxt1
+               grep "^To: to@example.com\$" msgtxt1 &&
+               grep "^In-Reply-To: <my-message-id@example.com>" msgtxt1
 '
 
 test_expect_success $PREREQ,AUTOIDENT 'implicit ident is allowed' '
@@ -370,17 +371,20 @@ test_expect_success $PREREQ,!AUTOIDENT 'broken implicit ident aborts send-email'
                --smtp-server="$(pwd)/fake.sendmail" \
                --to=to@example.com \
                $patches </dev/null 2>errors &&
-       test_i18ngrep "tell me who you are" errors
+       test_grep "tell me who you are" errors
        )
 '
 
-test_expect_success $PREREQ 'setup tocmd and cccmd scripts' '
+test_expect_success $PREREQ 'setup cmd scripts' '
        write_script tocmd-sed <<-\EOF &&
        sed -n -e "s/^tocmd--//p" "$1"
        EOF
-       write_script cccmd-sed <<-\EOF
+       write_script cccmd-sed <<-\EOF &&
        sed -n -e "s/^cccmd--//p" "$1"
        EOF
+       write_script headercmd-sed <<-\EOF
+       sed -n -e "s/^headercmd--//p" "$1"
+       EOF
 '
 
 test_expect_success $PREREQ 'tocmd works' '
@@ -410,6 +414,70 @@ test_expect_success $PREREQ 'cccmd works' '
        grep "^ cccmd@example.com" msgtxt1
 '
 
+test_expect_success $PREREQ 'headercmd works' '
+       clean_fake_sendmail &&
+       cp $patches headercmd.patch &&
+       echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --header-cmd=./headercmd-sed \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               headercmd.patch \
+               &&
+       grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '--no-header-cmd works' '
+       clean_fake_sendmail &&
+       cp $patches headercmd.patch &&
+       echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --header-cmd=./headercmd-sed \
+               --no-header-cmd \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               headercmd.patch \
+               &&
+       ! grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ 'multiline fields are correctly unfolded' '
+       clean_fake_sendmail &&
+       cp $patches headercmd.patch &&
+       write_script headercmd-multiline <<-\EOF &&
+       echo "X-Debbugs-CC: someone@example.com
+FoldedField: This is a tale
+ best told using
+ multiple lines."
+       EOF
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --header-cmd=./headercmd-multiline \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               headercmd.patch &&
+       grep "^FoldedField: This is a tale best told using multiple lines.$" msgtxt1
+'
+
+# Blank lines in the middle of the output of a command are invalid.
+test_expect_success $PREREQ 'malform output reported on blank lines in command output' '
+       clean_fake_sendmail &&
+       cp $patches headercmd.patch &&
+       write_script headercmd-malformed-output <<-\EOF &&
+       echo "X-Debbugs-CC: someone@example.com
+
+SomeOtherField: someone-else@example.com"
+       EOF
+       ! git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --header-cmd=./headercmd-malformed-output \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               headercmd.patch
+'
+
 test_expect_success $PREREQ 'reject long lines' '
        z8=zzzzzzzz &&
        z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
@@ -540,7 +608,7 @@ test_expect_success $PREREQ "--validate respects relative core.hooksPath path" '
        test_path_is_file my-hooks.ran &&
        cat >expect <<-EOF &&
        fatal: longline.patch: rejected by sendemail-validate hook
-       fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
+       fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
        warning: no patches were sent
        EOF
        test_cmp expect actual
@@ -559,12 +627,68 @@ test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" '
        test_path_is_file my-hooks.ran &&
        cat >expect <<-EOF &&
        fatal: longline.patch: rejected by sendemail-validate hook
-       fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
+       fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
+       warning: no patches were sent
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success $PREREQ "--validate hook supports multiple addresses in arguments" '
+       hooks_path="$(pwd)/my-hooks" &&
+       test_config core.hooksPath "$hooks_path" &&
+       test_when_finished "rm my-hooks.ran" &&
+       test_must_fail git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com,abc@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               --validate \
+               longline.patch 2>actual &&
+       test_path_is_file my-hooks.ran &&
+       cat >expect <<-EOF &&
+       fatal: longline.patch: rejected by sendemail-validate hook
+       fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
        warning: no patches were sent
        EOF
        test_cmp expect actual
 '
 
+test_expect_success $PREREQ "--validate hook supports header argument" '
+       write_script my-hooks/sendemail-validate <<-\EOF &&
+       if test "$#" -ge 2
+       then
+               grep "X-test-header: v1.0" "$2"
+       else
+               echo "No header arg passed"
+               exit 1
+       fi
+       EOF
+       test_config core.hooksPath "my-hooks" &&
+       rm -fr outdir &&
+       git format-patch \
+               --add-header="X-test-header: v1.0" \
+               -n HEAD^1 -o outdir &&
+       git send-email \
+               --dry-run \
+               --to=nobody@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               --validate \
+               outdir/000?-*.patch
+'
+
+test_expect_success $PREREQ 'clear message-id before parsing a new message' '
+       clean_fake_sendmail &&
+       echo true | write_script my-hooks/sendemail-validate &&
+       test_config core.hooksPath my-hooks &&
+       git send-email --validate --to=recipient@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               $patches $threaded_patches &&
+       id0=$(grep "^Message-ID: " $threaded_patches) &&
+       id1=$(grep "^Message-ID: " msgtxt1) &&
+       id2=$(grep "^Message-ID: " msgtxt2) &&
+       test "z$id0" = "z$id2" &&
+       test "z$id1" != "z$id2"
+'
+
 for enc in 7bit 8bit quoted-printable base64
 do
        test_expect_success $PREREQ "--transfer-encoding=$enc produces correct header" '
@@ -617,7 +741,7 @@ test_expect_success $PREREQ 'In-Reply-To without --chain-reply-to' '
        sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
        test_cmp expect actual &&
        # Second and subsequent messages are replies to the first one
-       sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+       sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt1 >expect &&
        sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
        test_cmp expect actual &&
        sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
@@ -637,10 +761,10 @@ test_expect_success $PREREQ 'In-Reply-To with --chain-reply-to' '
                2>errors &&
        sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
        test_cmp expect actual &&
-       sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+       sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt1 >expect &&
        sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
        test_cmp expect actual &&
-       sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt2 >expect &&
+       sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt2 >expect &&
        sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
        test_cmp expect actual
 '
@@ -713,7 +837,7 @@ Cc: cc@example.com,
        two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -759,7 +883,7 @@ Cc: A <author@example.com>,
        two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -796,7 +920,7 @@ Cc: A <author@example.com>,
        C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -824,7 +948,7 @@ From: Example <from@example.com>
 To: to@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -860,7 +984,7 @@ Cc: A <author@example.com>,
        cc-cmd@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -893,7 +1017,7 @@ Cc: A <author@example.com>,
        two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -926,7 +1050,7 @@ Cc: A <author@example.com>,
        two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -963,7 +1087,7 @@ Cc: A <author@example.com>,
        C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -993,7 +1117,7 @@ Cc: A <author@example.com>,
        C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -1478,7 +1602,7 @@ test_expect_success $PREREQ 'To headers from files reset each patch' '
 test_expect_success $PREREQ 'setup expect' '
 cat >email-using-8bit <<\EOF
 From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-Message-Id: <bogus-message-id@example.com>
+Message-ID: <bogus-message-id@example.com>
 From: author@example.com
 Date: Sat, 12 Jun 2010 15:53:58 +0200
 Subject: subject goes here
@@ -1519,7 +1643,7 @@ test_expect_success $PREREQ 'asks about and fixes 8bit encodings' '
        grep "do not declare a Content-Transfer-Encoding" stdout &&
        grep email-using-8bit stdout &&
        grep "Which 8bit encoding" stdout &&
-       egrep "Content|MIME" msgtxt1 >actual &&
+       grep -E "Content|MIME" msgtxt1 >actual &&
        test_cmp content-type-decl actual
 '
 
@@ -1530,7 +1654,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
        git send-email --from=author@example.com --to=nobody@example.com \
                        --smtp-server="$(pwd)/fake.sendmail" \
                        email-using-8bit >stdout &&
-       egrep "Content|MIME" msgtxt1 >actual &&
+       grep -E "Content|MIME" msgtxt1 >actual &&
        test_cmp content-type-decl actual
 '
 
@@ -1545,7 +1669,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding in .git/config overrides --g
        git send-email --from=author@example.com --to=nobody@example.com \
                        --smtp-server="$(pwd)/fake.sendmail" \
                        email-using-8bit >stdout &&
-       egrep "Content|MIME" msgtxt1 >actual &&
+       grep -E "Content|MIME" msgtxt1 >actual &&
        test_cmp content-type-decl actual
 '
 
@@ -1557,14 +1681,14 @@ test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
                        --smtp-server="$(pwd)/fake.sendmail" \
                        --8bit-encoding=UTF-8 \
                        email-using-8bit >stdout &&
-       egrep "Content|MIME" msgtxt1 >actual &&
+       grep -E "Content|MIME" msgtxt1 >actual &&
        test_cmp content-type-decl actual
 '
 
 test_expect_success $PREREQ 'setup expect' '
        cat >email-using-8bit <<-\EOF
        From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-       Message-Id: <bogus-message-id@example.com>
+       Message-ID: <bogus-message-id@example.com>
        From: author@example.com
        Date: Sat, 12 Jun 2010 15:53:58 +0200
        Subject: Dieser Betreff enthält auch einen Umlaut!
@@ -1593,7 +1717,7 @@ test_expect_success $PREREQ '--8bit-encoding also treats subject' '
 test_expect_success $PREREQ 'setup expect' '
        cat >email-using-8bit <<-\EOF
        From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-       Message-Id: <bogus-message-id@example.com>
+       Message-ID: <bogus-message-id@example.com>
        From: A U Thor <author@example.com>
        Date: Sat, 12 Jun 2010 15:53:58 +0200
        Content-Type: text/plain; charset=UTF-8
@@ -1674,7 +1798,7 @@ test_expect_success $PREREQ '8-bit and sendemail.transferencoding=base64' '
 test_expect_success $PREREQ 'setup expect' '
        cat >email-using-qp <<-\EOF
        From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-       Message-Id: <bogus-message-id@example.com>
+       Message-ID: <bogus-message-id@example.com>
        From: A U Thor <author@example.com>
        Date: Sat, 12 Jun 2010 15:53:58 +0200
        MIME-Version: 1.0
@@ -1700,7 +1824,7 @@ test_expect_success $PREREQ 'convert from quoted-printable to base64' '
 test_expect_success $PREREQ 'setup expect' "
 tr -d '\\015' | tr '%' '\\015' >email-using-crlf <<EOF
 From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-Message-Id: <bogus-message-id@example.com>
+Message-ID: <bogus-message-id@example.com>
 From: A U Thor <author@example.com>
 Date: Sat, 12 Jun 2010 15:53:58 +0200
 Content-Type: text/plain; charset=UTF-8
@@ -1957,7 +2081,7 @@ test_expect_success $PREREQ 'aliases and sendemail.identity' '
                -c sendemail.aliasesfile=default-aliases \
                -c sendemail.cloud.aliasesfile=cloud-aliases \
                send-email -1 2>stderr &&
-       test_i18ngrep "cloud-aliases" stderr
+       test_grep "cloud-aliases" stderr
 '
 
 test_sendmail_aliases () {
@@ -2322,10 +2446,41 @@ test_expect_success $PREREQ 'invoke hook' '
                        --to=nobody@example.com \
                        --smtp-server="$(pwd)/../fake.sendmail" \
                        ../another.patch 2>err &&
-               test_i18ngrep "rejected by sendemail-validate hook" err
+               test_grep "rejected by sendemail-validate hook" err
        )
 '
 
+expected_file_counter_output () {
+       total=$1
+       count=0
+       while test $count -ne $total
+       do
+               count=$((count + 1)) &&
+               echo "$count/$total" || return
+       done
+}
+
+test_expect_success $PREREQ '--validate hook allows counting of messages' '
+       test_when_finished "rm -rf my-hooks.log" &&
+       test_config core.hooksPath "my-hooks" &&
+       mkdir -p my-hooks &&
+
+       write_script my-hooks/sendemail-validate <<-\EOF &&
+               num=$GIT_SENDEMAIL_FILE_COUNTER &&
+               tot=$GIT_SENDEMAIL_FILE_TOTAL &&
+               echo "$num/$tot" >>my-hooks.log || exit 1
+       EOF
+
+       >my-hooks.log &&
+       expected_file_counter_output 4 >expect &&
+       git send-email \
+               --from="Example <from@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               --validate -3 --cover-letter --force &&
+       test_cmp expect my-hooks.log
+'
+
 test_expect_success $PREREQ 'test that send-email works outside a repo' '
        nongit git send-email \
                --from="Example <nobody@example.com>" \
@@ -2334,6 +2489,12 @@ test_expect_success $PREREQ 'test that send-email works outside a repo' '
                "$(pwd)/0001-add-main.patch"
 '
 
+test_expect_success $PREREQ 'send-email relays -v 3 to format-patch' '
+       test_when_finished "rm -f out" &&
+       git send-email --dry-run -v 3 -1 >out &&
+       grep "PATCH v3" out
+'
+
 test_expect_success $PREREQ 'test that sendmail config is rejected' '
        test_config sendmail.program sendmail &&
        test_must_fail git send-email \
@@ -2341,7 +2502,7 @@ test_expect_success $PREREQ 'test that sendmail config is rejected' '
                --to=nobody@example.com \
                --smtp-server="$(pwd)/fake.sendmail" \
                HEAD^ 2>err &&
-       test_i18ngrep "found configuration options for '"'"sendmail"'"'" err
+       test_grep "found configuration options for '"'"sendmail"'"'" err
 '
 
 test_expect_success $PREREQ 'test that sendmail config rejection is specific' '
@@ -2363,4 +2524,45 @@ test_expect_success $PREREQ 'test forbidSendmailVariables behavior override' '
                HEAD^
 '
 
+test_expect_success $PREREQ '--compose handles lowercase headers' '
+       write_script fake-editor <<-\EOF &&
+       sed "s/^From:.*/from: edited-from@example.com/i" "$1" >"$1.tmp" &&
+       mv "$1.tmp" "$1"
+       EOF
+       clean_fake_sendmail &&
+       git send-email \
+               --compose \
+               --from="Example <from@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               HEAD^ &&
+       grep "From: edited-from@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '--compose handles to headers' '
+       write_script fake-editor <<-\EOF &&
+       sed "s/^To: .*/&, edited-to@example.com/" <"$1" >"$1.tmp" &&
+       echo this is the body >>"$1.tmp" &&
+       mv "$1.tmp" "$1"
+       EOF
+       clean_fake_sendmail &&
+       GIT_SEND_EMAIL_NOTTY=1 \
+       git send-email \
+               --compose \
+               --from="Example <from@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               HEAD^ &&
+       # Check both that the cover letter used our modified "to" line,
+       # but also that it was picked up for the patch.
+       q_to_tab >expect <<-\EOF &&
+       To: nobody@example.com,
+       Qedited-to@example.com
+       EOF
+       grep -A1 "^To:" msgtxt1 >msgtxt1.to &&
+       test_cmp expect msgtxt1.to &&
+       grep -A1 "^To:" msgtxt2 >msgtxt2.to &&
+       test_cmp expect msgtxt2.to
+'
+
 test_done
index 6d3dbde3feb9a41dc59a42f75ef578b1ebc5b183..348cc40658235239c7e68671738cd030871451ad 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='git column'
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index f00deaf3815f3b49c42da4021335e011bbcb3c32..14a704d0a8c311ff776f65313178073e70b0a38f 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='help.autocorrect finding a match'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -60,4 +62,10 @@ test_expect_success 'autocorrect can be declined altogether' '
        test_line_count = 1 actual
 '
 
+test_expect_success 'autocorrect works in work tree created from bare repo' '
+       git clone --bare . bare.git &&
+       git -C bare.git worktree add ../worktree &&
+       git -C worktree -c help.autocorrect=immediate stauts
+'
+
 test_done
index 7e8894a4a70648fd12d3ab4425f1beac2c3e4641..590aab0304c679b6559df0e840ddce6dacc684af 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check that example code compiles and runs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'decorate' '
index fea41b3c3606df1fc6d8111cdd74522532ca8b0b..af28b01fefa49c647b3ab87a395dd8c2dfb70a5a 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success 'git svn help works anywhere' '
 '
 
 test_expect_success \
-    'initialize git svn' '
+       'initialize git svn' '
        mkdir import &&
        (
                cd import &&
@@ -38,9 +38,9 @@ test_expect_success \
        rm -rf import &&
        git svn init "$svnrepo"'
 
-test_expect_success \
-    'import an SVN revision into git' \
-    'git svn fetch'
+test_expect_success 'import an SVN revision into git' '
+       git svn fetch
+'
 
 test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
 
@@ -233,27 +233,26 @@ test_expect_success POSIXPERM,SYMLINKS "$name" '
 '
 
 test_expect_success 'exit if remote refs are ambigious' '
-        git config --add svn-remote.svn.fetch \
+       git config --add svn-remote.svn.fetch \
                bar:refs/remotes/git-svn &&
        test_must_fail git svn migrate
 '
 
 test_expect_success 'exit if init-ing a would clobber a URL' '
-        svnadmin create "${PWD}/svnrepo2" &&
-        svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
-        git config --unset svn-remote.svn.fetch \
+       svnadmin create "${PWD}/svnrepo2" &&
+       svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
+       git config --unset svn-remote.svn.fetch \
                "^bar:refs/remotes/git-svn$" &&
        test_must_fail git svn init "${svnrepo}2/bar"
         '
 
-test_expect_success \
-  'init allows us to connect to another directory in the same repo' '
-        git svn init --minimize-url -i bar "$svnrepo/bar" &&
-        git config --get svn-remote.svn.fetch \
-                              "^bar:refs/remotes/bar$" &&
-        git config --get svn-remote.svn.fetch \
-                             "^:refs/remotes/git-svn$"
-        '
+test_expect_success 'init allows us to connect to another directory in the same repo' '
+       git svn init --minimize-url -i bar "$svnrepo/bar" &&
+       git config --get svn-remote.svn.fetch \
+               "^bar:refs/remotes/bar$" &&
+       git config --get svn-remote.svn.fetch \
+               "^:refs/remotes/git-svn$"
+'
 
 test_expect_success 'dcommit $rev does not clobber current branch' '
        git svn fetch -i bar &&
index 85d735861fc9f500561e9f5fb4baa131657752c0..b5845e28feef8219ac5405e9e0400a40694eec30 100755 (executable)
@@ -41,51 +41,51 @@ test_expect_success 'init and fetch a moved directory' '
        '
 
 test_expect_success 'init and fetch from one svn-remote' '
-        git config svn-remote.svn.url "$svnrepo" &&
-        git config --add svn-remote.svn.fetch \
-          trunk:refs/remotes/svn/trunk &&
-        git config --add svn-remote.svn.fetch \
-          thunk:refs/remotes/svn/thunk &&
-        git svn fetch -i svn/thunk &&
+       git config svn-remote.svn.url "$svnrepo" &&
+       git config --add svn-remote.svn.fetch \
+               trunk:refs/remotes/svn/trunk &&
+       git config --add svn-remote.svn.fetch \
+               thunk:refs/remotes/svn/thunk &&
+       git svn fetch -i svn/thunk &&
        test "$(git rev-parse --verify refs/remotes/svn/trunk)" \
-          = "$(git rev-parse --verify refs/remotes/svn/thunk~1)" &&
+               = "$(git rev-parse --verify refs/remotes/svn/thunk~1)" &&
        git cat-file blob refs/remotes/svn/thunk:readme >actual &&
        test "$(sed -n -e "3p" actual)" = goodbye
-        '
+'
 
 test_expect_success 'follow deleted parent' '
-        (svn_cmd cp -m "resurrecting trunk as junk" \
-               "$svnrepo"/trunk@2 "$svnrepo"/junk ||
-         svn cp -m "resurrecting trunk as junk" \
-               -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
-        git config --add svn-remote.svn.fetch \
-          junk:refs/remotes/svn/junk &&
-        git svn fetch -i svn/thunk &&
-        git svn fetch -i svn/junk &&
+       (svn_cmd cp -m "resurrecting trunk as junk" \
+               "$svnrepo"/trunk@2 "$svnrepo"/junk ||
+        svn cp -m "resurrecting trunk as junk" \
+               -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
+       git config --add svn-remote.svn.fetch \
+               junk:refs/remotes/svn/junk &&
+       git svn fetch -i svn/thunk &&
+       git svn fetch -i svn/junk &&
        test -z "$(git diff svn/junk svn/trunk)" &&
        test "$(git merge-base svn/junk svn/trunk)" \
-          = "$(git rev-parse svn/trunk)"
-        '
+               = "$(git rev-parse svn/trunk)"
+'
 
 test_expect_success 'follow larger parent' '
-        mkdir -p import/trunk/thunk/bump/thud &&
-        echo hi > import/trunk/thunk/bump/thud/file &&
-        svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
-        svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
-        git svn init --minimize-url -i larger \
-         "$svnrepo"/larger-parent/trunk/thunk/bump/thud &&
-        git svn fetch -i larger &&
+       mkdir -p import/trunk/thunk/bump/thud &&
+       echo hi > import/trunk/thunk/bump/thud/file &&
+       svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
+       svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
+       git svn init --minimize-url -i larger \
+               "$svnrepo"/larger-parent/trunk/thunk/bump/thud &&
+       git svn fetch -i larger &&
        git svn init --minimize-url -i larger-parent \
-         "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
+               "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
        git svn fetch -i larger-parent &&
-        git rev-parse --verify refs/remotes/larger &&
-        git rev-parse --verify \
-          refs/remotes/larger-parent &&
+       git rev-parse --verify refs/remotes/larger &&
+       git rev-parse --verify \
+               refs/remotes/larger-parent &&
        test "$(git merge-base \
                 refs/remotes/larger-parent \
                 refs/remotes/larger)" = \
-            "$(git rev-parse refs/remotes/larger)"
-        '
+               "$(git rev-parse refs/remotes/larger)"
+'
 
 test_expect_success 'follow higher-level parent' '
        svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
index 3cab0b9720a6544ade5caf91a3f678ad577cc3b0..bca496c40e0a22011465083d6b36aa32e48e86b5 100755 (executable)
@@ -3,7 +3,6 @@
 # Copyright (c) 2006 Eric Wong
 test_description='git svn commit-diff clobber'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
index 32317d6bca5f45314a46082ecd9aa68061853904..e06538b1c854815c40aa43915d844089671cb2bd 100755 (executable)
@@ -27,7 +27,7 @@ cat << EOF
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
 #
 EOF
 }
index 419f055721dbc32122e9d595d691c77bb8a24212..743fbe1fe46929583aacacc60ba959c38d8a2f09 100755 (executable)
@@ -5,7 +5,6 @@
 
 test_description='git svn dcommit can commit renames of files with ugly names'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
index 8201c3e808a931d07bc7e135f87bcc0499f65e20..088d1c57a88a209d57bdc95e45368f40af87c7a6 100755 (executable)
@@ -28,7 +28,7 @@ test_cmp_info () {
        rm -f tmp.expect tmp.actual
 }
 
-quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
+quoted_svnrepo="$(echo $svnrepo | test_uri_escape)"
 
 test_expect_success 'setup repository and import' '
        mkdir info &&
index f894860867a1398555876ce0586479157189a376..8ca24670acb5a9280a163b11682e9a0dd52eaaa5 100755 (executable)
@@ -11,7 +11,7 @@ test_expect_success 'setup repo with a git repo inside it' '
        (
                cd s &&
                git init &&
-               test -f .git/HEAD &&
+               git symbolic-ref HEAD &&
                > .git/a &&
                echo a > a &&
                svn_cmd add .git a &&
@@ -35,7 +35,7 @@ test_expect_success 'SVN-side change outside of .git' '
                echo b >> a &&
                svn_cmd commit -m "SVN-side change outside of .git" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change outside of .git"
+               svn_cmd log -v | grep -F "SVN-side change outside of .git"
        )
 '
 
@@ -59,7 +59,7 @@ test_expect_success 'SVN-side change inside of .git' '
                svn_cmd add --force .git &&
                svn_cmd commit -m "SVN-side change inside of .git" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change inside of .git"
+               svn_cmd log -v | grep -F "SVN-side change inside of .git"
        )
 '
 
@@ -82,7 +82,7 @@ test_expect_success 'SVN-side change in and out of .git' '
                git commit -m "add a inside an SVN repo" &&
                svn_cmd commit -m "SVN-side change in and out of .git" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change in and out of .git"
+               svn_cmd log -v | grep -F "SVN-side change in and out of .git"
        )
 '
 
index 4a77eb9f60da3ade87ed701ecb0dca7f7ad13d87..31884002263fcf0b245bcf5231d65554911b698c 100755 (executable)
@@ -43,7 +43,7 @@ test_expect_success 'init+fetch an SVN repository with ignored www directory' '
 test_expect_success 'verify ignore-paths config saved by clone' '
        (
            cd g &&
-           git config --get svn-remote.svn.ignore-paths | fgrep "www"
+           git config --get svn-remote.svn.ignore-paths | grep www
        )
 '
 
@@ -53,7 +53,7 @@ test_expect_success 'SVN-side change outside of www' '
                echo b >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change outside of www" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change outside of www"
+               svn_cmd log -v | grep "SVN-side change outside of www"
        )
 '
 
@@ -85,7 +85,7 @@ test_expect_success 'SVN-side change inside of ignored www' '
                echo zaq >> www/test_www.txt &&
                svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+               svn_cmd log -v | grep -F "SVN-side change inside of www/test_www.txt"
        )
 '
 
@@ -118,7 +118,7 @@ test_expect_success 'SVN-side change in and out of ignored www' '
                echo ygg >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change in and out of ignored www" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+               svn_cmd log -v | grep "SVN-side change in and out of ignored www"
        )
 '
 
index e8559046296ce5570ec46b2b3a9280f0a493bbc4..a420b2a87af10a9a3e6f6136d0ff53d5a466804f 100755 (executable)
@@ -43,7 +43,7 @@ test_expect_success 'fetch fails on modified hidden file' '
          git svn find-rev refs/remotes/git-svn > ../expect &&
          test_must_fail git svn fetch 2> ../errors &&
          git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
-       fgrep "not found in commit" errors &&
+       grep "not found in commit" errors &&
        test_cmp expect expect2
 '
 
@@ -59,7 +59,7 @@ test_expect_success 'refetch succeeds not ignoring any files' '
        ( cd g &&
          git svn fetch &&
          git svn rebase &&
-         fgrep "mod hidden" hid/hid.txt
+         grep "mod hidden" hid/hid.txt
        )
 '
 
index 79c26ed69c16cad42a32a555bb33635a154441e7..09606f1b3cfeb4244de4fbc12e4e96ddb7fd7de5 100755 (executable)
@@ -4,7 +4,6 @@
 
 test_description='git svn creates empty directories'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
index 257fc8f2f8d194bbf1a0bb151da1b80d85874e28..63fa0b6732e33e393d0c47b258239b841a8a4bb8 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success 'init+fetch an SVN repository with included qqq directory' '
 test_expect_success 'verify include-paths config saved by clone' '
        (
            cd g &&
-           git config --get svn-remote.svn.include-paths | fgrep "qqq"
+           git config --get svn-remote.svn.include-paths | grep qqq
        )
 '
 
@@ -55,7 +55,7 @@ test_expect_success 'SVN-side change outside of www' '
                echo b >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change outside of www" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change outside of www"
+               svn_cmd log -v | grep "SVN-side change outside of www"
        )
 '
 
@@ -87,7 +87,7 @@ test_expect_success 'SVN-side change inside of ignored www' '
                echo zaq >> www/test_www.txt &&
                svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+               svn_cmd log -v | grep "SVN-side change inside of www/test_www.txt"
        )
 '
 
@@ -120,7 +120,7 @@ test_expect_success 'SVN-side change in and out of included qqq' '
                echo ygg >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change in and out of ignored www" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+               svn_cmd log -v | grep "SVN-side change in and out of ignored www"
        )
 '
 
index 6cc76a07b39a90d590759bbb7033da3953969092..aebb28995e50a0a589e8fff33997d9816de1fdb2 100755 (executable)
@@ -5,7 +5,6 @@
 
 test_description='git svn propset tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup propset via import' '
index 9cf7a1427ab081f2084b147b7dc4b1e3aa0f1005..36c6b1a12ffd95a1d6fe4ee4efd18a47f1581078 100755 (executable)
@@ -9,7 +9,6 @@ This test uses git to clone a Subversion repository that contains empty
 directories, and checks that corresponding directories are created in the
 local Git repository with placeholder files.'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 GIT_REPO=git-svn-repo
index 1465156072e32a75fb759784e65f9f7e2ca4c84a..d1dec89c3b7f6c46f18a2838c3d8cdba640146ea 100755 (executable)
@@ -5,7 +5,6 @@
 
 test_description='concurrent git svn dcommit'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 
@@ -47,6 +46,14 @@ setup_hook()
                        "passed to setup_hook" >&2 ; return 1; }
        echo "cnt=$skip_revs" > "$hook_type-counter"
        rm -f "$rawsvnrepo/hooks/"*-commit # drop previous hooks
+
+       # Subversion hooks run with an empty environment by default. We thus
+       # need to propagate PATH so that we can find executables.
+       cat >"$rawsvnrepo/conf/hooks-env" <<-EOF
+       [default]
+       PATH = ${PATH}
+       EOF
+
        hook="$rawsvnrepo/hooks/$hook_type"
        cat > "$hook" <<- 'EOF1'
                #!/bin/sh
@@ -64,7 +71,6 @@ EOF1
        if [ "$hook_type" = "pre-commit" ]; then
                echo "echo 'commit disallowed' >&2; exit 1" >>"$hook"
        else
-               echo "PATH=\"$PATH\"; export PATH" >>"$hook"
                echo "svnconf=\"$svnconf\"" >>"$hook"
                cat >>"$hook" <<- 'EOF2'
                        cd work-auto-commits.svn
index c5946cb0b8a94cfc8c4e2a30284c3343acb8e876..a44eabf0d80fa8db991d77af4da49f58750cf43a 100755 (executable)
@@ -50,56 +50,56 @@ check_entries () {
        fi
 }
 
-test_expect_success \
-    'New file' \
-    'mkdir A B C D E F &&
-     echo hello1 >A/newfile1.txt &&
-     echo hello2 >B/newfile2.txt &&
-     cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
-     cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
-     git add A/newfile1.txt &&
-     git add B/newfile2.txt &&
-     git add C/newfile3.png &&
-     git add D/newfile4.png &&
-     git commit -a -m "Test: New file" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     git cvsexportcommit -c $id &&
-     check_entries A "newfile1.txt/1.1/" &&
-     check_entries B "newfile2.txt/1.1/" &&
-     check_entries C "newfile3.png/1.1/-kb" &&
-     check_entries D "newfile4.png/1.1/-kb" &&
-     test_cmp A/newfile1.txt ../A/newfile1.txt &&
-     test_cmp B/newfile2.txt ../B/newfile2.txt &&
-     test_cmp C/newfile3.png ../C/newfile3.png &&
-     test_cmp D/newfile4.png ../D/newfile4.png
-     )'
+test_expect_success 'New file' '
+       mkdir A B C D E F &&
+       echo hello1 >A/newfile1.txt &&
+       echo hello2 >B/newfile2.txt &&
+       cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
+       cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
+       git add A/newfile1.txt &&
+       git add B/newfile2.txt &&
+       git add C/newfile3.png &&
+       git add D/newfile4.png &&
+       git commit -a -m "Test: New file" &&
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$CVSWORK" &&
+       git cvsexportcommit -c $id &&
+       check_entries A "newfile1.txt/1.1/" &&
+       check_entries B "newfile2.txt/1.1/" &&
+       check_entries C "newfile3.png/1.1/-kb" &&
+       check_entries D "newfile4.png/1.1/-kb" &&
+       test_cmp A/newfile1.txt ../A/newfile1.txt &&
+       test_cmp B/newfile2.txt ../B/newfile2.txt &&
+       test_cmp C/newfile3.png ../C/newfile3.png &&
+       test_cmp D/newfile4.png ../D/newfile4.png
+       )
+'
 
-test_expect_success \
-    'Remove two files, add two and update two' \
-    'echo Hello1 >>A/newfile1.txt &&
-     rm -f B/newfile2.txt &&
-     rm -f C/newfile3.png &&
-     echo Hello5  >E/newfile5.txt &&
-     cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
-     cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
-     git add E/newfile5.txt &&
-     git add F/newfile6.png &&
-     git commit -a -m "Test: Remove, add and update" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     git cvsexportcommit -c $id &&
-     check_entries A "newfile1.txt/1.2/" &&
-     check_entries B "" &&
-     check_entries C "" &&
-     check_entries D "newfile4.png/1.2/-kb" &&
-     check_entries E "newfile5.txt/1.1/" &&
-     check_entries F "newfile6.png/1.1/-kb" &&
-     test_cmp A/newfile1.txt ../A/newfile1.txt &&
-     test_cmp D/newfile4.png ../D/newfile4.png &&
-     test_cmp E/newfile5.txt ../E/newfile5.txt &&
-     test_cmp F/newfile6.png ../F/newfile6.png
-     )'
+test_expect_success 'Remove two files, add two and update two' '
+       echo Hello1 >>A/newfile1.txt &&
+       rm -f B/newfile2.txt &&
+       rm -f C/newfile3.png &&
+       echo Hello5  >E/newfile5.txt &&
+       cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
+       cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
+       git add E/newfile5.txt &&
+       git add F/newfile6.png &&
+       git commit -a -m "Test: Remove, add and update" &&
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$CVSWORK" &&
+       git cvsexportcommit -c $id &&
+       check_entries A "newfile1.txt/1.2/" &&
+       check_entries B "" &&
+       check_entries C "" &&
+       check_entries D "newfile4.png/1.2/-kb" &&
+       check_entries E "newfile5.txt/1.1/" &&
+       check_entries F "newfile6.png/1.1/-kb" &&
+       test_cmp A/newfile1.txt ../A/newfile1.txt &&
+       test_cmp D/newfile4.png ../D/newfile4.png &&
+       test_cmp E/newfile5.txt ../E/newfile5.txt &&
+       test_cmp F/newfile6.png ../F/newfile6.png
+       )
+'
 
 # Should fail (but only on the git cvsexportcommit stage)
 test_expect_success \
@@ -129,67 +129,67 @@ test_expect_success \
 
 # This test is here because a patch for only binary files will
 # fail with gnu patch, so cvsexportcommit must handle that.
-test_expect_success \
-    'Remove only binary files' \
-    'git reset --hard HEAD^^ &&
-     rm -f D/newfile4.png &&
-     git commit -a -m "test: remove only a binary file" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     git cvsexportcommit -c $id &&
-     check_entries A "newfile1.txt/1.2/" &&
-     check_entries B "" &&
-     check_entries C "" &&
-     check_entries D "" &&
-     check_entries E "newfile5.txt/1.1/" &&
-     check_entries F "newfile6.png/1.1/-kb" &&
-     test_cmp A/newfile1.txt ../A/newfile1.txt &&
-     test_cmp E/newfile5.txt ../E/newfile5.txt &&
-     test_cmp F/newfile6.png ../F/newfile6.png
-     )'
+test_expect_success 'Remove only binary files' '
+       git reset --hard HEAD^^ &&
+       rm -f D/newfile4.png &&
+       git commit -a -m "test: remove only a binary file" &&
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$CVSWORK" &&
+       git cvsexportcommit -c $id &&
+       check_entries A "newfile1.txt/1.2/" &&
+       check_entries B "" &&
+       check_entries C "" &&
+       check_entries D "" &&
+       check_entries E "newfile5.txt/1.1/" &&
+       check_entries F "newfile6.png/1.1/-kb" &&
+       test_cmp A/newfile1.txt ../A/newfile1.txt &&
+       test_cmp E/newfile5.txt ../E/newfile5.txt &&
+       test_cmp F/newfile6.png ../F/newfile6.png
+       )
+'
 
-test_expect_success \
-    'Remove only a text file' \
-    'rm -f A/newfile1.txt &&
-     git commit -a -m "test: remove only a binary file" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     git cvsexportcommit -c $id &&
-     check_entries A "" &&
-     check_entries B "" &&
-     check_entries C "" &&
-     check_entries D "" &&
-     check_entries E "newfile5.txt/1.1/" &&
-     check_entries F "newfile6.png/1.1/-kb" &&
-     test_cmp E/newfile5.txt ../E/newfile5.txt &&
-     test_cmp F/newfile6.png ../F/newfile6.png
-     )'
+test_expect_success 'Remove only a text file' '
+       rm -f A/newfile1.txt &&
+       git commit -a -m "test: remove only a binary file" &&
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$CVSWORK" &&
+       git cvsexportcommit -c $id &&
+       check_entries A "" &&
+       check_entries B "" &&
+       check_entries C "" &&
+       check_entries D "" &&
+       check_entries E "newfile5.txt/1.1/" &&
+       check_entries F "newfile6.png/1.1/-kb" &&
+       test_cmp E/newfile5.txt ../E/newfile5.txt &&
+       test_cmp F/newfile6.png ../F/newfile6.png
+       )
+'
 
-test_expect_success \
-     'New file with spaces in file name' \
-     'mkdir "G g" &&
-      echo ok then >"G g/with spaces.txt" &&
-      git add "G g/with spaces.txt" && \
-      cp "$TEST_DIRECTORY"/test-binary-1.png "G g/with spaces.png" && \
-      git add "G g/with spaces.png" &&
-      git commit -a -m "With spaces" &&
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$CVSWORK" &&
-      git cvsexportcommit -c $id &&
-      check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
-      )'
+test_expect_success 'New file with spaces in file name' '
+       mkdir "G g" &&
+       echo ok then >"G g/with spaces.txt" &&
+       git add "G g/with spaces.txt" && \
+       cp "$TEST_DIRECTORY"/test-binary-1.png "G g/with spaces.png" && \
+       git add "G g/with spaces.png" &&
+       git commit -a -m "With spaces" &&
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$CVSWORK" &&
+       git cvsexportcommit -c $id &&
+       check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
+       )
+'
 
-test_expect_success \
-     'Update file with spaces in file name' \
-     'echo Ok then >>"G g/with spaces.txt" &&
-      cat "$TEST_DIRECTORY"/test-binary-1.png >>"G g/with spaces.png" && \
-      git add "G g/with spaces.png" &&
-      git commit -a -m "Update with spaces" &&
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$CVSWORK" &&
-      git cvsexportcommit -c $id &&
-      check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
-      )'
+test_expect_success 'Update file with spaces in file name' '
+       echo Ok then >>"G g/with spaces.txt" &&
+       cat "$TEST_DIRECTORY"/test-binary-1.png >>"G g/with spaces.png" && \
+       git add "G g/with spaces.png" &&
+       git commit -a -m "Update with spaces" &&
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$CVSWORK" &&
+       git cvsexportcommit -c $id &&
+       check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
+       )
+'
 
 # Some filesystems mangle pathnames with UTF-8 characters --
 # check and skip
@@ -202,68 +202,68 @@ if p="Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" &&
 then
 
 # This test contains UTF-8 characters
-test_expect_success !MINGW \
-     'File with non-ascii file name' \
-     'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
-      echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
-      git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
-      cp "$TEST_DIRECTORY"/test-binary-1.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
-      git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
-      git commit -a -m "Går det så går det" && \
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$CVSWORK" &&
-      git cvsexportcommit -v -c $id &&
-      check_entries \
-      "Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
-      "gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
-      )'
+test_expect_success !MINGW 'File with non-ascii file name' '
+       mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
+       echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
+       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
+       cp "$TEST_DIRECTORY"/test-binary-1.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+       git commit -a -m "Går det så går det" && \
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$CVSWORK" &&
+       git cvsexportcommit -v -c $id &&
+       check_entries \
+       "Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
+       "gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
+       )
+'
 
 fi
 
 rm -fr tst
 
-test_expect_success \
-     'Mismatching patch should fail' \
-     'date >>"E/newfile5.txt" &&
-      git add "E/newfile5.txt" &&
-      git commit -a -m "Update one" &&
-      date >>"E/newfile5.txt" &&
-      git add "E/newfile5.txt" &&
-      git commit -a -m "Update two" &&
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$CVSWORK" &&
-      test_must_fail git cvsexportcommit -c $id
-      )'
-
-test_expect_success FILEMODE \
-     'Retain execute bit' \
-     'mkdir G &&
-      echo executeon >G/on &&
-      chmod +x G/on &&
-      echo executeoff >G/off &&
-      git add G/on &&
-      git add G/off &&
-      git commit -a -m "Execute test" &&
-      (cd "$CVSWORK" &&
-      git cvsexportcommit -c HEAD &&
-      test -x G/on &&
-      ! test -x G/off
-      )'
+test_expect_success 'Mismatching patch should fail' '
+       date >>"E/newfile5.txt" &&
+       git add "E/newfile5.txt" &&
+       git commit -a -m "Update one" &&
+       date >>"E/newfile5.txt" &&
+       git add "E/newfile5.txt" &&
+       git commit -a -m "Update two" &&
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$CVSWORK" &&
+       test_must_fail git cvsexportcommit -c $id
+       )
+'
+
+test_expect_success FILEMODE 'Retain execute bit' '
+       mkdir G &&
+       echo executeon >G/on &&
+       chmod +x G/on &&
+       echo executeoff >G/off &&
+       git add G/on &&
+       git add G/off &&
+       git commit -a -m "Execute test" &&
+       (cd "$CVSWORK" &&
+       git cvsexportcommit -c HEAD &&
+       test -x G/on &&
+       ! test -x G/off
+       )
+'
 
 test_expect_success '-w option should work with relative GIT_DIR' '
-      mkdir W &&
-      echo foobar >W/file1.txt &&
-      echo bazzle >W/file2.txt &&
-      git add W/file1.txt &&
-      git add W/file2.txt &&
-      git commit -m "More updates" &&
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$GIT_DIR" &&
-      GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
-      check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
-      test_cmp "$CVSWORK/W/file1.txt" ../W/file1.txt &&
-      test_cmp "$CVSWORK/W/file2.txt" ../W/file2.txt
-      )
+       mkdir W &&
+       echo foobar >W/file1.txt &&
+       echo bazzle >W/file2.txt &&
+       git add W/file1.txt &&
+       git add W/file2.txt &&
+       git commit -m "More updates" &&
+       id=$(git rev-list --max-count=1 HEAD) &&
+       (cd "$GIT_DIR" &&
+       GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
+       check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
+       test_cmp "$CVSWORK/W/file1.txt" ../W/file1.txt &&
+       test_cmp "$CVSWORK/W/file2.txt" ../W/file2.txt
+       )
 '
 
 test_expect_success 'check files before directories' '
@@ -290,21 +290,20 @@ test_expect_success 'check files before directories' '
 '
 
 test_expect_success 're-commit a removed filename which remains in CVS attic' '
-
-    (cd "$CVSWORK" &&
-     echo >attic_gremlin &&
-     cvs -Q add attic_gremlin &&
-     cvs -Q ci -m "added attic_gremlin" &&
-     rm attic_gremlin &&
-     cvs -Q rm attic_gremlin &&
-     cvs -Q ci -m "removed attic_gremlin") &&
-
-    echo > attic_gremlin &&
-    git add attic_gremlin &&
-    git commit -m "Added attic_gremlin" &&
+       (cd "$CVSWORK" &&
+       echo >attic_gremlin &&
+       cvs -Q add attic_gremlin &&
+       cvs -Q ci -m "added attic_gremlin" &&
+       rm attic_gremlin &&
+       cvs -Q rm attic_gremlin &&
+       cvs -Q ci -m "removed attic_gremlin") &&
+
+       echo > attic_gremlin &&
+       git add attic_gremlin &&
+       git commit -m "Added attic_gremlin" &&
        git cvsexportcommit -w "$CVSWORK" -c HEAD &&
-    (cd "$CVSWORK" && cvs -Q update -d) &&
-    test -f "$CVSWORK/attic_gremlin"
+       (cd "$CVSWORK" && cvs -Q update -d) &&
+       test -f "$CVSWORK/attic_gremlin"
 '
 
 # the state of the CVS sandbox may be indeterminate for ' space'
index 14ca575a214f3b74409f89ada717277aeabe5e82..4432a30d10b69a168ca477f222bfa52d36447006 100755 (executable)
@@ -104,6 +104,13 @@ test_expect_success FSMONITOR_DAEMON 'scalar register starts fsmon daemon' '
        test_cmp_config -C test/src true core.fsmonitor
 '
 
+test_expect_success 'scalar register warns when background maintenance fails' '
+       git init register-repo &&
+       GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \
+               scalar register register-repo 2>err &&
+       grep "could not turn on maintenance" err
+'
+
 test_expect_success 'scalar unregister' '
        git init vanish/src &&
        scalar register vanish/src &&
@@ -116,7 +123,10 @@ test_expect_success 'scalar unregister' '
        test_must_fail git config --get --global --fixed-value \
                maintenance.repo "$(pwd)/vanish/src" &&
        scalar list >scalar.repos &&
-       ! grep -F "$(pwd)/vanish/src" scalar.repos
+       ! grep -F "$(pwd)/vanish/src" scalar.repos &&
+
+       # scalar unregister should be idempotent
+       scalar unregister vanish
 '
 
 test_expect_success 'set up repository to clone' '
@@ -163,6 +173,20 @@ test_expect_success 'scalar reconfigure' '
        test true = "$(git -C one/src config core.preloadIndex)"
 '
 
+test_expect_success '`reconfigure -a` removes stale config entries' '
+       git init stale/src &&
+       scalar register stale &&
+       scalar list >scalar.repos &&
+       grep stale scalar.repos &&
+
+       grep -v stale scalar.repos >expect &&
+
+       rm -rf stale &&
+       scalar reconfigure -a &&
+       scalar list >scalar.repos &&
+       test_cmp expect scalar.repos
+'
+
 test_expect_success 'scalar delete without enlistment shows a usage' '
        test_expect_code 129 scalar delete
 '
index dd33d87e9be16515276b4b75758ba5eb3a7f8d05..7869f45ee646dd511b16e404a8b165200f9c8608 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='test the `scalar clone` subcommand'
 
 . ./test-lib.sh
+. "${TEST_DIRECTORY}/lib-terminal.sh"
 
 GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
 export GIT_TEST_MAINT_SCHEDULER
@@ -148,4 +149,47 @@ test_expect_success '--no-single-branch clones all branches' '
        cleanup_clone $enlistment
 '
 
+test_expect_success TTY 'progress with tty' '
+       enlistment=progress1 &&
+
+       test_config -C to-clone uploadpack.allowfilter true &&
+       test_config -C to-clone uploadpack.allowanysha1inwant true &&
+
+       test_terminal env GIT_PROGRESS_DELAY=0 \
+               scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr &&
+       grep "Enumerating objects" stderr >actual &&
+       test_line_count = 2 actual &&
+       cleanup_clone $enlistment
+'
+
+test_expect_success 'progress without tty' '
+       enlistment=progress2 &&
+
+       test_config -C to-clone uploadpack.allowfilter true &&
+       test_config -C to-clone uploadpack.allowanysha1inwant true &&
+
+       GIT_PROGRESS_DELAY=0 scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr &&
+       ! grep "Enumerating objects" stderr &&
+       ! grep "Updating files" stderr &&
+       cleanup_clone $enlistment
+'
+
+test_expect_success 'scalar clone warns when background maintenance fails' '
+       GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \
+               scalar clone "file://$(pwd)/to-clone" maint-fail 2>err &&
+       grep "could not turn on maintenance" err
+'
+
+test_expect_success '`scalar clone --no-src`' '
+       scalar clone --src "file://$(pwd)/to-clone" with-src &&
+       scalar clone --no-src "file://$(pwd)/to-clone" without-src &&
+
+       test_path_is_dir with-src/src &&
+       test_path_is_missing without-src/src &&
+
+       (cd with-src/src && ls ?*) >with &&
+       (cd without-src && ls ?*) >without &&
+       test_cmp with without
+'
+
 test_done
index aa55b41b9a97eaaadf9910ddfd62e0e349d405c8..dbb5042b0b8f1a00212ad01bcc8f8907ab47569a 100755 (executable)
@@ -388,9 +388,7 @@ test_expect_success 'B: accept branch name "TEMP_TAG"' '
 
        INPUT_END
 
-       test_when_finished "rm -f .git/TEMP_TAG
-               git gc
-               git prune" &&
+       test_when_finished "rm -f .git/TEMP_TAG && git gc --prune=now" &&
        git fast-import <input &&
        test $(test-tool ref-store main resolve-ref TEMP_TAG 0 | cut -f1 -d " " ) != "$ZERO_OID" &&
        test $(git rev-parse main) = $(git rev-parse TEMP_TAG^)
@@ -406,8 +404,7 @@ test_expect_success 'B: accept empty committer' '
        INPUT_END
 
        test_when_finished "git update-ref -d refs/heads/empty-committer-1
-               git gc
-               git prune" &&
+               git gc --prune=now" &&
        git fast-import <input &&
        out=$(git fsck) &&
        echo "$out" &&
@@ -452,8 +449,7 @@ test_expect_success 'B: accept and fixup committer with no name' '
        INPUT_END
 
        test_when_finished "git update-ref -d refs/heads/empty-committer-2
-               git gc
-               git prune" &&
+               git gc --prune=now" &&
        git fast-import <input &&
        out=$(git fsck) &&
        echo "$out" &&
@@ -1778,8 +1774,7 @@ test_expect_success 'P: verbatim SHA gitlinks' '
        INPUT_END
 
        git branch -D sub &&
-       git gc &&
-       git prune &&
+       git gc --prune=now &&
        git fast-import <input &&
        test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)
 '
@@ -2884,7 +2879,7 @@ test_expect_success 'S: filemodify with garbage after mark must fail' '
        COMMIT
        M 100644 :403x hello.c
        EOF
-       test_i18ngrep "space after mark" err
+       test_grep "space after mark" err
 '
 
 # inline is misspelled; fast-import thinks it is some unknown dataref
@@ -2900,7 +2895,7 @@ test_expect_success 'S: filemodify with garbage after inline must fail' '
        inline
        BLOB
        EOF
-       test_i18ngrep "nvalid dataref" err
+       test_grep "nvalid dataref" err
 '
 
 test_expect_success 'S: filemodify with garbage after sha1 must fail' '
@@ -2913,7 +2908,7 @@ test_expect_success 'S: filemodify with garbage after sha1 must fail' '
        COMMIT
        M 100644 ${sha1}x hello.c
        EOF
-       test_i18ngrep "space after SHA1" err
+       test_grep "space after SHA1" err
 '
 
 #
@@ -2928,7 +2923,7 @@ test_expect_success 'S: notemodify with garbage after mark dataref must fail' '
        COMMIT
        N :202x :302
        EOF
-       test_i18ngrep "space after mark" err
+       test_grep "space after mark" err
 '
 
 test_expect_success 'S: notemodify with garbage after inline dataref must fail' '
@@ -2943,7 +2938,7 @@ test_expect_success 'S: notemodify with garbage after inline dataref must fail'
        note blob
        BLOB
        EOF
-       test_i18ngrep "nvalid dataref" err
+       test_grep "nvalid dataref" err
 '
 
 test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
@@ -2956,7 +2951,7 @@ test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
        COMMIT
        N ${sha1}x :302
        EOF
-       test_i18ngrep "space after SHA1" err
+       test_grep "space after SHA1" err
 '
 
 #
@@ -2971,7 +2966,7 @@ test_expect_success 'S: notemodify with garbage after mark commit-ish must fail'
        COMMIT
        N :202 :302x
        EOF
-       test_i18ngrep "after mark" err
+       test_grep "after mark" err
 '
 
 #
@@ -3004,7 +2999,7 @@ test_expect_success 'S: from with garbage after mark must fail' '
        EOF
 
        # now evaluate the error
-       test_i18ngrep "after mark" err
+       test_grep "after mark" err
 '
 
 
@@ -3023,7 +3018,7 @@ test_expect_success 'S: merge with garbage after mark must fail' '
        merge :303x
        M 100644 :403 hello.c
        EOF
-       test_i18ngrep "after mark" err
+       test_grep "after mark" err
 '
 
 #
@@ -3038,7 +3033,7 @@ test_expect_success 'S: tag with garbage after mark must fail' '
        tag S
        TAG
        EOF
-       test_i18ngrep "after mark" err
+       test_grep "after mark" err
 '
 
 #
@@ -3048,7 +3043,7 @@ test_expect_success 'S: cat-blob with garbage after mark must fail' '
        test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
        cat-blob :403x
        EOF
-       test_i18ngrep "after mark" err
+       test_grep "after mark" err
 '
 
 #
@@ -3058,7 +3053,7 @@ test_expect_success 'S: ls with garbage after mark must fail' '
        test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
        ls :302x hello.c
        EOF
-       test_i18ngrep "space after mark" err
+       test_grep "space after mark" err
 '
 
 test_expect_success 'S: ls with garbage after sha1 must fail' '
@@ -3066,7 +3061,7 @@ test_expect_success 'S: ls with garbage after sha1 must fail' '
        test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
        ls ${sha1}x hello.c
        EOF
-       test_i18ngrep "space after tree-ish" err
+       test_grep "space after tree-ish" err
 '
 
 ###
index a98ef032d94c106c8f2ee604b60b771e11880f2d..410a871c52af87cd9fe9ab9da5350b032fc5689d 100755 (executable)
@@ -49,4 +49,33 @@ test_expect_success 'import with submodule mapping' '
        test_cmp expect actual
 '
 
+test_expect_success 'paths adjusted for relative subdir' '
+       git init deep-dst &&
+       mkdir deep-dst/subdir &&
+       >deep-dst/subdir/empty-marks &&
+       git -C deep-dst/subdir fast-import \
+               --rewrite-submodules-from=sub:../../from \
+               --rewrite-submodules-to=sub:../../to \
+               --import-marks=empty-marks \
+               --export-marks=exported-marks \
+               --export-pack-edges=exported-edges \
+               <dump &&
+       # we do not bother checking resulting repo; we just care that nothing
+       # complained about failing to open files for reading, and that files
+       # for writing were created in the expected spot
+       test_path_is_file deep-dst/subdir/exported-marks &&
+       test_path_is_file deep-dst/subdir/exported-edges
+'
+
+test_expect_success 'relative marks are not affected by subdir' '
+       git init deep-relative &&
+       mkdir deep-relative/subdir &&
+       git -C deep-relative/subdir fast-import \
+               --relative-marks \
+               --export-marks=exported-marks \
+               <dump &&
+       test_path_is_missing deep-relative/subdir/exported-marks &&
+       test_path_is_file deep-relative/.git/info/fast-import/exported-marks
+'
+
 test_done
index ff21a12ee6e4eb1557eca22bb60ab2e01aa6b909..e9a12c18bbd3f8bd43659d0b4eee0e4dfbad30ab 100755 (executable)
@@ -373,7 +373,7 @@ EOF
 
 test_expect_success 'cope with tagger-less tags' '
 
-       TAG=$(git hash-object -t tag -w tag-content) &&
+       TAG=$(git hash-object --literally -t tag -w tag-content) &&
        git update-ref refs/tags/sonnenschein $TAG &&
        git fast-export -C -C --signed-tags=strip --all > output &&
        test $(grep -c "^tag " output) = 4 &&
@@ -791,4 +791,14 @@ test_expect_success 'fast-export --first-parent outputs all revisions output by
        )
 '
 
+test_expect_success 'fast-export handles --end-of-options' '
+       git update-ref refs/heads/nodash HEAD &&
+       git update-ref refs/heads/--dashes HEAD &&
+       git fast-export --end-of-options nodash >expect &&
+       git fast-export --end-of-options --dashes >actual.raw &&
+       # fix up lines which mention the ref for comparison
+       sed s/--dashes/nodash/ <actual.raw >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 77047e250dc2c862182619f717b6b96206a38ec7..156a6474847cf6e5bc86df2212ce98211986e6d6 100755 (executable)
@@ -25,6 +25,7 @@ test_expect_success 'setup simple repo' '
 test_expect_success 'export anonymized stream' '
        git fast-export --anonymize --all \
                --anonymize-map=retain-me \
+               --anonymize-map=xyzzy:should-not-appear \
                --anonymize-map=xyzzy:custom-name \
                --anonymize-map=other \
                >stream
@@ -41,6 +42,7 @@ test_expect_success 'stream omits path names' '
 
 test_expect_success 'stream contains user-specified names' '
        grep retain-me stream &&
+       ! grep should-not-appear stream &&
        grep custom-name stream
 '
 
index 379b19f2f85157c83e8259b3ae7df52c44665820..003c0b61d0ff45864630782a509a018ff4079116 100755 (executable)
@@ -66,10 +66,11 @@ test_expect_success 'setup' '
 
 # note that cvs doesn't accept absolute pathnames
 # as argument to co -d
-test_expect_success 'basic checkout' \
-  'GIT_CONFIG="$git_config" cvs -Q co -d cvswork main &&
-   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
-   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"'
+test_expect_success 'basic checkout' '
+       GIT_CONFIG="$git_config" cvs -Q co -d cvswork main &&
+       test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
+       test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"
+'
 
 #------------------------
 # PSERVER AUTHENTICATION
@@ -115,35 +116,40 @@ Ah<Z:yZZ30 e
 END VERIFICATION REQUEST
 EOF
 
-test_expect_success 'pserver authentication' \
-  'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication' '
+       cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+       sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
-test_expect_success 'pserver authentication failure (non-anonymous user)' \
-  'if cat request-git | git-cvsserver pserver >log 2>&1
-   then
-       false
-   else
-       true
-   fi &&
-   sed -ne \$p log | grep "^I HATE YOU\$"'
+test_expect_success 'pserver authentication failure (non-anonymous user)' '
+       if cat request-git | git-cvsserver pserver >log 2>&1
+       then
+           false
+       else
+           true
+       fi &&
+       sed -ne \$p log | grep "^I HATE YOU\$"
+'
 
-test_expect_success 'pserver authentication success (non-anonymous user with password)' \
-  'cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication success (non-anonymous user with password)' '
+       cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+       sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
-test_expect_success 'pserver authentication (login)' \
-  'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication (login)' '
+       cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
+       sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
-test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
-  'if cat login-git | git-cvsserver pserver >log 2>&1
-   then
-       false
-   else
-       true
-   fi &&
-   sed -ne \$p log | grep "^I HATE YOU\$"'
+test_expect_success 'pserver authentication failure (login/non-anonymous user)' '
+       if cat login-git | git-cvsserver pserver >log 2>&1
+       then
+           false
+       else
+           true
+       fi &&
+       sed -ne \$p log | grep "^I HATE YOU\$"
+'
 
 
 # misuse pserver authentication for testing of req_Root
@@ -165,36 +171,40 @@ END AUTH REQUEST
 Root $WORKDIR
 EOF
 
-test_expect_success 'req_Root failure (relative pathname)' \
-  'if cat request-relative | git-cvsserver pserver >log 2>&1
-   then
-       echo unexpected success
-       false
-   else
-       true
-   fi &&
-   tail log | grep "^error 1 Root must be an absolute pathname$"'
+test_expect_success 'req_Root failure (relative pathname)' '
+       if cat request-relative | git-cvsserver pserver >log 2>&1
+       then
+               echo unexpected success
+               false
+       else
+               true
+       fi &&
+       tail log | grep "^error 1 Root must be an absolute pathname$"
+'
 
-test_expect_success 'req_Root failure (conflicting roots)' \
-  'cat request-conflict | git-cvsserver pserver >log 2>&1 &&
-   tail log | grep "^error 1 Conflicting roots specified$"'
+test_expect_success 'req_Root failure (conflicting roots)' '
+       cat request-conflict | git-cvsserver pserver >log 2>&1 &&
+       tail log | grep "^error 1 Conflicting roots specified$"
+'
 
-test_expect_success 'req_Root (strict paths)' \
-  'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (strict paths)' '
+       cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
+       sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
 test_expect_success 'req_Root failure (strict-paths)' '
-    ! cat request-anonymous |
-    git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
+       ! cat request-anonymous |
+       git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
 '
 
-test_expect_success 'req_Root (w/o strict-paths)' \
-  'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (w/o strict-paths)' '
+       cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
+       sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
 test_expect_success 'req_Root failure (w/o strict-paths)' '
-    ! cat request-anonymous |
-    git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
+       ! cat request-anonymous |
+       git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
 '
 
 cat >request-base  <<EOF
@@ -206,27 +216,30 @@ END AUTH REQUEST
 Root /gitcvs.git
 EOF
 
-test_expect_success 'req_Root (base-path)' \
-  'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (base-path)' '
+       cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+       sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
 test_expect_success 'req_Root failure (base-path)' '
-    ! cat request-anonymous |
-    git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
+       ! cat request-anonymous |
+       git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
 '
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
 
-test_expect_success 'req_Root (export-all)' \
-  'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (export-all)' '
+       cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
+       sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
-test_expect_success 'req_Root failure (export-all w/o directory list)' \
-  '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+test_expect_success 'req_Root failure (export-all w/o directory list)' '
+       ! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
 
-test_expect_success 'req_Root (everything together)' \
-  'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (everything together)' '
+       cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+       sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
 
@@ -247,45 +260,49 @@ test_expect_success 'gitcvs.enabled = false' \
    test ! -d cvswork2'
 
 rm -fr cvswork2
-test_expect_success 'gitcvs.ext.enabled = true' \
-  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
-   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
-   test_cmp cvswork cvswork2'
+test_expect_success 'gitcvs.ext.enabled = true' '
+       GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+       GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+       GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+       test_cmp cvswork cvswork2
+'
 
 rm -fr cvswork2
-test_expect_success 'gitcvs.ext.enabled = false' \
-  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
-   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
-   if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1
-   then
-     echo unexpected cvs success
-     false
-   else
-     true
-   fi &&
-   grep "GITCVS emulation disabled" cvs.log &&
-   test ! -d cvswork2'
+test_expect_success 'gitcvs.ext.enabled = false' '
+       GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
+       GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+       if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1
+       then
+               echo unexpected cvs success
+               false
+       else
+               true
+       fi &&
+       grep "GITCVS emulation disabled" cvs.log &&
+       test ! -d cvswork2
+'
 
 rm -fr cvswork2
-test_expect_success 'gitcvs.dbname' \
-  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
-   GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
-   test_cmp cvswork cvswork2 &&
-   test -f "$SERVERDIR/gitcvs.ext.main.sqlite" &&
-   cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs.ext.main.sqlite"'
+test_expect_success 'gitcvs.dbname' '
+       GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+       GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
+       GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+       test_cmp cvswork cvswork2 &&
+       test -f "$SERVERDIR/gitcvs.ext.main.sqlite" &&
+       cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs.ext.main.sqlite"
+'
 
 rm -fr cvswork2
-test_expect_success 'gitcvs.ext.dbname' \
-  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
-   GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
-   GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
-   test_cmp cvswork cvswork2 &&
-   test -f "$SERVERDIR/gitcvs1.ext.main.sqlite" &&
-   test ! -f "$SERVERDIR/gitcvs2.ext.main.sqlite" &&
-   cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs1.ext.main.sqlite"'
+test_expect_success 'gitcvs.ext.dbname' '
+       GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+       GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+       GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+       GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+       test_cmp cvswork cvswork2 &&
+       test -f "$SERVERDIR/gitcvs1.ext.main.sqlite" &&
+       test ! -f "$SERVERDIR/gitcvs2.ext.main.sqlite" &&
+       cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs1.ext.main.sqlite"
+'
 
 
 #------------
@@ -299,109 +316,115 @@ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
 GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
 exit 1
 
-test_expect_success 'cvs update (create new file)' \
-  'echo testfile1 >testfile1 &&
-   git add testfile1 &&
-   git commit -q -m "Add testfile1" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
-   test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (create new file)' '
+       echo testfile1 >testfile1 &&
+       git add testfile1 &&
+       git commit -q -m "Add testfile1" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
+       test_cmp testfile1 ../testfile1
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (update existing file)' \
-  'echo line 2 >>testfile1 &&
-   git add testfile1 &&
-   git commit -q -m "Append to testfile1" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
-   test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (update existing file)' '
+       echo line 2 >>testfile1 &&
+       git add testfile1 &&
+       git commit -q -m "Append to testfile1" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
+       test_cmp testfile1 ../testfile1
+'
 
 cd "$WORKDIR"
 #TODO: cvsserver doesn't support update w/o -d
 test_expect_failure "cvs update w/o -d doesn't create subdir (TODO)" '
-   mkdir test &&
-   echo >test/empty &&
-   git add test &&
-   git commit -q -m "Single Subdirectory" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test ! -d test
+       mkdir test &&
+       echo >test/empty &&
+       git add test &&
+       git commit -q -m "Single Subdirectory" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test ! -d test
 '
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (subdirectories)' \
-  '(for dir in A A/B A/B/C A/D E; do
-      mkdir $dir &&
-      echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")"  &&
-      git add $dir || exit 1
-   done) &&
-   git commit -q -m "deep sub directory structure" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update -d &&
-   (for dir in A A/B A/B/C A/D E; do
-      filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
-      if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
-       test_cmp "$dir/$filename" "../$dir/$filename"; then
-        :
-      else
-       exit 1
-      fi
-    done)'
+test_expect_success 'cvs update (subdirectories)' '
+       (for dir in A A/B A/B/C A/D E; do
+               mkdir $dir &&
+               echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")"  &&
+               git add $dir || exit 1
+       done) &&
+       git commit -q -m "deep sub directory structure" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update -d &&
+       (for dir in A A/B A/B/C A/D E; do
+               filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
+               if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
+                       test_cmp "$dir/$filename" "../$dir/$filename"; then
+               :
+               else
+                       exit 1
+               fi
+       done)
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (delete file)' \
-  'git rm testfile1 &&
-   git commit -q -m "Remove testfile1" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test -z "$(grep testfile1 CVS/Entries)" &&
-   test ! -f testfile1'
+test_expect_success 'cvs update (delete file)' '
+       git rm testfile1 &&
+       git commit -q -m "Remove testfile1" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test -z "$(grep testfile1 CVS/Entries)" &&
+       test ! -f testfile1
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (re-add deleted file)' \
-  'echo readded testfile >testfile1 &&
-   git add testfile1 &&
-   git commit -q -m "Re-Add testfile1" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
-   test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (re-add deleted file)' '
+       echo readded testfile >testfile1 &&
+       git add testfile1 &&
+       git commit -q -m "Re-Add testfile1" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
+       test_cmp testfile1 ../testfile1
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (merge)' \
-  'echo Line 0 >expected &&
-   for i in 1 2 3 4 5 6 7
-   do
-     echo Line $i >>merge &&
-     echo Line $i >>expected || return 1
-   done &&
-   echo Line 8 >>expected &&
-   git add merge &&
-   git commit -q -m "Merge test (pre-merge)" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
-   test_cmp merge ../merge &&
-   ( echo Line 0 && cat merge ) >merge.tmp &&
-   mv merge.tmp merge &&
-   cd "$WORKDIR" &&
-   echo Line 8 >>merge &&
-   git add merge &&
-   git commit -q -m "Merge test (merge)" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   sleep 1 && touch merge &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test_cmp merge ../expected'
+test_expect_success 'cvs update (merge)' '
+       echo Line 0 >expected &&
+       for i in 1 2 3 4 5 6 7
+       do
+               echo Line $i >>merge &&
+               echo Line $i >>expected || return 1
+       done &&
+       echo Line 8 >>expected &&
+       git add merge &&
+       git commit -q -m "Merge test (pre-merge)" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
+       test_cmp merge ../merge &&
+       ( echo Line 0 && cat merge ) >merge.tmp &&
+       mv merge.tmp merge &&
+       cd "$WORKDIR" &&
+       echo Line 8 >>merge &&
+       git add merge &&
+       git commit -q -m "Merge test (merge)" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       sleep 1 && touch merge &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test_cmp merge ../expected
+'
 
 cd "$WORKDIR"
 
@@ -418,55 +441,58 @@ do
   echo Line $i >>expected.C
 done
 
-test_expect_success 'cvs update (conflict merge)' \
-  '( echo LINE 0 && cat merge ) >merge.tmp &&
-   mv merge.tmp merge &&
-   git add merge &&
-   git commit -q -m "Merge test (conflict)" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test_cmp merge ../expected.C'
+test_expect_success 'cvs update (conflict merge)' '
+       ( echo LINE 0 && cat merge ) >merge.tmp &&
+       mv merge.tmp merge &&
+       git add merge &&
+       git commit -q -m "Merge test (conflict)" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test_cmp merge ../expected.C
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (-C)' \
-  'cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update -C &&
-   test_cmp merge ../merge'
+test_expect_success 'cvs update (-C)' '
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs -Q update -C &&
+       test_cmp merge ../merge
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (merge no-op)' \
-   'echo Line 9 >>merge &&
-    cp merge cvswork/merge &&
-    git add merge &&
-    git commit -q -m "Merge test (no-op)" &&
-    git push gitcvs.git >/dev/null &&
-    cd cvswork &&
-    sleep 1 && touch merge &&
-    GIT_CONFIG="$git_config" cvs -Q update &&
-    test_cmp merge ../merge'
+test_expect_success 'cvs update (merge no-op)' '
+       echo Line 9 >>merge &&
+       cp merge cvswork/merge &&
+       git add merge &&
+       git commit -q -m "Merge test (no-op)" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       sleep 1 && touch merge &&
+       GIT_CONFIG="$git_config" cvs -Q update &&
+       test_cmp merge ../merge
+'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (-p)' '
-    touch really-empty &&
-    echo Line 1 > no-lf &&
-    printf "Line 2" >> no-lf &&
-    git add really-empty no-lf &&
-    git commit -q -m "Update -p test" &&
-    git push gitcvs.git >/dev/null &&
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs update &&
-    for i in merge no-lf empty really-empty; do
-       GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out &&
-       test_cmp $i.out ../$i || return 1
-    done
+       touch really-empty &&
+       echo Line 1 > no-lf &&
+       printf "Line 2" >> no-lf &&
+       git add really-empty no-lf &&
+       git commit -q -m "Update -p test" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs update &&
+       for i in merge no-lf empty really-empty; do
+               GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out &&
+               test_cmp $i.out ../$i || return 1
+       done
 '
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (module list supports packed refs)' '
-    GIT_DIR="$SERVERDIR" git pack-refs --all &&
-    GIT_CONFIG="$git_config" cvs -n up -d 2> out &&
-    grep "cvs update: New directory \`main'\''" < out
+       GIT_DIR="$SERVERDIR" git pack-refs --all &&
+       GIT_CONFIG="$git_config" cvs -n up -d 2> out &&
+       grep "cvs update: New directory \`main'\''" < out
 '
 
 #------------
@@ -475,30 +501,30 @@ test_expect_success 'cvs update (module list supports packed refs)' '
 
 cd "$WORKDIR"
 test_expect_success 'cvs status' '
-    mkdir status.dir &&
-    echo Line > status.dir/status.file &&
-    echo Line > status.file &&
-    git add status.dir status.file &&
-    git commit -q -m "Status test" &&
-    git push gitcvs.git >/dev/null &&
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs update &&
-    GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
-    test_line_count = 2 ../out
+       mkdir status.dir &&
+       echo Line > status.dir/status.file &&
+       echo Line > status.file &&
+       git add status.dir status.file &&
+       git commit -q -m "Status test" &&
+       git push gitcvs.git >/dev/null &&
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs update &&
+       GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
+       test_line_count = 2 ../out
 '
 
 cd "$WORKDIR"
 test_expect_success 'cvs status (nonrecursive)' '
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
-    test_line_count = 1 ../out
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
+       test_line_count = 1 ../out
 '
 
 cd "$WORKDIR"
 test_expect_success 'cvs status (no subdirs in header)' '
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
-    ! grep / <../out
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
+       ! grep / <../out
 '
 
 #------------
@@ -507,9 +533,9 @@ test_expect_success 'cvs status (no subdirs in header)' '
 
 cd "$WORKDIR"
 test_expect_success 'cvs co -c (shows module database)' '
-    GIT_CONFIG="$git_config" cvs co -c > out &&
-    grep "^main[        ][     ]*main$" <out &&
-    ! grep -v "^main[   ][     ]*main$" <out
+       GIT_CONFIG="$git_config" cvs co -c > out &&
+       grep "^main[     ][     ]*main$" <out &&
+       ! grep -v "^main[        ][     ]*main$" <out
 '
 
 #------------
@@ -575,11 +601,11 @@ expectStat="$?"
 
 cd "$WORKDIR"
 test_expect_success 'cvs log' '
-    cd cvswork &&
-    test x"$expectStat" = x"0" &&
-    GIT_CONFIG="$git_config" cvs log merge >../out &&
-    sed -e "s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__%" ../out > ../actual &&
-    test_cmp ../expect ../actual
+       cd cvswork &&
+       test x"$expectStat" = x"0" &&
+       GIT_CONFIG="$git_config" cvs log merge >../out &&
+sed -e "s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__%" ../out > ../actual &&
+       test_cmp ../expect ../actual
 '
 
 #------------
@@ -588,11 +614,11 @@ test_expect_success 'cvs log' '
 
 cd "$WORKDIR"
 test_expect_success 'cvs annotate' '
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs annotate merge >../out &&
-    sed -e "s/ .*//" ../out >../actual &&
-    printf "1.%d\n" 3 1 1 1 1 1 1 1 2 4 >../expect &&
-    test_cmp ../expect ../actual
+       cd cvswork &&
+       GIT_CONFIG="$git_config" cvs annotate merge >../out &&
+       sed -e "s/ .*//" ../out >../actual &&
+       printf "1.%d\n" 3 1 1 1 1 1 1 1 2 4 >../expect &&
+       test_cmp ../expect ../actual
 '
 
 #------------
index 0333065d4d60a0113cb5ac1de391a4a997d2eaa8..7679780fb87b4f14e9c0f5a22708b1e8925a4d53 100755 (executable)
@@ -627,6 +627,7 @@ test_expect_success \
 test_expect_success 'setup' '
        version=$(git config core.repositoryformatversion) &&
        algo=$(test_might_fail git config extensions.objectformat) &&
+       refstorage=$(test_might_fail git config extensions.refstorage) &&
        cat >.git/config <<-\EOF &&
        # testing noval and alternate separator
        [gitweb]
@@ -637,6 +638,10 @@ test_expect_success 'setup' '
        if test -n "$algo"
        then
                git config extensions.objectformat "$algo"
+       fi &&
+       if test -n "$refstorage"
+       then
+               git config extensions.refstorage "$refstorage"
        fi
 '
 
index 4aa5d90d328aca4adcac5f0d3a2ee4946a393812..ccc8212d732e22170ed74ada1510a72a71d7aaf7 100755 (executable)
@@ -13,37 +13,40 @@ skip_all_if_no_Test_More
 
 # set up test repository
 
-test_expect_success \
-    'set up test repository' \
-    'echo "test file 1" > file1 &&
-     echo "test file 2" > file2 &&
-     mkdir directory1 &&
-     echo "in directory1" >> directory1/file &&
-     mkdir directory2 &&
-     echo "in directory2" >> directory2/file &&
-     git add . &&
-     git commit -m "first commit" &&
-
-     echo "new file in subdir 2" > directory2/file2 &&
-     git add . &&
-     git commit -m "commit in directory2" &&
-
-     echo "changed file 1" > file1 &&
-     git commit -a -m "second commit" &&
-
-     git config --add color.test.slot1 green &&
-     git config --add test.string value &&
-     git config --add test.dupstring value1 &&
-     git config --add test.dupstring value2 &&
-     git config --add test.booltrue true &&
-     git config --add test.boolfalse no &&
-     git config --add test.boolother other &&
-     git config --add test.int 2k &&
-     git config --add test.path "~/foo" &&
-     git config --add test.pathexpanded "$HOME/foo" &&
-     git config --add test.pathmulti foo &&
-     git config --add test.pathmulti bar
-     '
+test_expect_success 'set up test repository' '
+       echo "test file 1" >file1 &&
+       echo "test file 2" >file2 &&
+       mkdir directory1 &&
+       echo "in directory1" >>directory1/file &&
+       mkdir directory2 &&
+       echo "in directory2" >>directory2/file &&
+       git add . &&
+       git commit -m "first commit" &&
+
+       echo "new file in subdir 2" >directory2/file2 &&
+       git add . &&
+       git commit -m "commit in directory2" &&
+
+       echo "changed file 1" >file1 &&
+       git commit -a -m "second commit" &&
+
+       git config --add color.test.slot1 green &&
+       git config --add test.string value &&
+       git config --add test.dupstring value1 &&
+       git config --add test.dupstring value2 &&
+       git config --add test.booltrue true &&
+       git config --add test.boolfalse no &&
+       git config --add test.boolother other &&
+       git config --add test.int 2k &&
+       git config --add test.path "~/foo" &&
+       git config --add test.pathexpanded "$HOME/foo" &&
+       git config --add test.pathmulti foo &&
+       git config --add test.pathmulti bar
+'
+
+test_expect_success 'set up bare repository' '
+       git init --bare bare.git
+'
 
 test_expect_success 'use t9700/test.pl to test Git.pm' '
        "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl 2>stderr &&
index e046f7db762d355228425ff5b77639be65b1272c..d8e85482ab2ba7b28cb1e899f07dc1379969d1c1 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use lib (split(/:/, $ENV{GITPERLLIB}));
 
-use 5.008;
+use 5.008001;
 use warnings;
 use strict;
 
@@ -30,6 +30,18 @@ BEGIN { use_ok('Git') }
 # set up
 our $abs_repo_dir = cwd();
 ok(our $r = Git->repository(Directory => "."), "open repository");
+{
+       local $ENV{GIT_TEST_ASSUME_DIFFERENT_OWNER} = 1;
+       my $failed;
+
+       $failed = eval { Git->repository(Directory => $abs_repo_dir) };
+       ok(!$failed, "reject unsafe non-bare repository");
+       like($@, qr/not a git repository/i, "unsafe error message");
+
+       $failed = eval { Git->repository(Directory => "$abs_repo_dir/bare.git") };
+       ok(!$failed, "reject unsafe bare repository");
+       like($@, qr/not a git repository/i, "unsafe error message");
+}
 
 # config
 is($r->config("test.string"), "value", "config scalar: string");
index dc88d0e064931aab48a3e93002cc67416090282c..53af8e34ac1c40a31dee6e6f4449f8c52dcf6f59 100755 (executable)
@@ -54,7 +54,7 @@ test_expect_success 'git p4 sync uninitialized repo' '
        (
                cd "$git" &&
                test_must_fail git p4 sync 2>errs &&
-               test_i18ngrep "Perhaps you never did" errs
+               test_grep "Perhaps you never did" errs
        )
 '
 
@@ -86,7 +86,7 @@ test_expect_success 'git p4 sync existing branch without changes' '
                test_commit head &&
                git p4 sync --branch=depot //depot@all &&
                git p4 sync --branch=refs/remotes/p4/depot >out &&
-               test_i18ngrep "No changes to import!" out
+               test_grep "No changes to import!" out
        )
 '
 
@@ -101,7 +101,7 @@ test_expect_success 'git p4 sync existing branch with relative name' '
                test_commit head &&
                git p4 sync --branch=branch1 //depot@all &&
                git p4 sync --branch=p4/branch1 >out &&
-               test_i18ngrep "No changes to import!" out
+               test_grep "No changes to import!" out
        )
 '
 
@@ -116,7 +116,7 @@ test_expect_success 'git p4 sync existing branch with nested path' '
                test_commit head &&
                git p4 sync --branch=p4/some/path //depot@all &&
                git p4 sync --branch=some/path >out &&
-               test_i18ngrep "No changes to import!" out
+               test_grep "No changes to import!" out
        )
 '
 
@@ -131,7 +131,7 @@ test_expect_success 'git p4 sync branch explicit ref without p4 in path' '
                test_commit head &&
                git p4 sync --branch=refs/remotes/someremote/depot //depot@all &&
                git p4 sync --branch=refs/remotes/someremote/depot >out &&
-               test_i18ngrep "No changes to import!" out
+               test_grep "No changes to import!" out
        )
 '
 
@@ -143,7 +143,7 @@ test_expect_success 'git p4 sync nonexistent ref' '
                test_commit head &&
                git p4 sync --branch=depot //depot@all &&
                test_must_fail git p4 sync --branch=depot2 2>errs &&
-               test_i18ngrep "Perhaps you never did" errs
+               test_grep "Perhaps you never did" errs
        )
 '
 
@@ -155,7 +155,7 @@ test_expect_success 'git p4 sync existing non-p4-imported ref' '
                test_commit head &&
                git p4 sync --branch=depot //depot@all &&
                test_must_fail git p4 sync --branch=refs/heads/master 2>errs &&
-               test_i18ngrep "Perhaps you never did" errs
+               test_grep "Perhaps you never did" errs
        )
 '
 
@@ -290,7 +290,7 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '
                export PATH &&
                test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1
        ) &&
-       test_i18ngrep ! Traceback errs
+       test_grep ! Traceback errs
 '
 
 # Hide a file from p4d, make sure we catch its complaint.  This won't fail in
@@ -301,7 +301,7 @@ test_expect_success 'exit gracefully for p4 server errors' '
        mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden &&
        test_when_finished cleanup_git &&
        test_expect_code 1 git p4 clone --dest="$git" //depot@1 >out 2>err &&
-       test_i18ngrep "Error from p4 print" err
+       test_grep "Error from p4 print" err
 '
 
 test_expect_success 'clone --bare should make a bare repository' '
@@ -330,7 +330,7 @@ test_expect_success 'initial import time from top change time' '
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
-               gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
+               gittime=$(git show -s --pretty=format:%at HEAD) &&
                echo $p4time $gittime &&
                test $p4time = $gittime
        )
index 759a14fa87ce6ae540b2c98cfd3b83a7bf6928f2..c598011635ac2f44d0764acaf1ba7c53f569514c 100755 (executable)
@@ -135,7 +135,7 @@ test_expect_success 'sync specific detected branch' '
        (
                cd "$git" &&
                git p4 sync --branch=depot/branch2 >out &&
-               test_i18ngrep "No changes to import!" out
+               test_grep "No changes to import!" out
        )
 '
 
@@ -466,7 +466,7 @@ test_expect_success 'git p4 clone complex branches with excluded files' '
        )
 '
 
-# From a report in http://stackoverflow.com/questions/11893688
+# From a report in https://stackoverflow.com/questions/11893688
 # where --use-client-spec caused branch prefixes not to be removed;
 # every file in git appeared into a subdirectory of the branch name.
 test_expect_success 'use-client-spec detect-branches setup' '
index 7d4109f29d5e3b5751a9fba8b15c5a6d5d3e0fa6..af4b286f9d51af7655e86036d9938a691780af23 100755 (executable)
@@ -75,7 +75,7 @@ test_expect_success 'submit --dry-run' '
                test_commit "dry-run1" &&
                test_commit "dry-run2" &&
                git p4 submit --dry-run >out &&
-               test_i18ngrep "Would apply" out
+               test_grep "Would apply" out
        ) &&
        (
                cd "$cli" &&
@@ -99,7 +99,7 @@ test_expect_success 'submit --dry-run --export-labels' '
                git commit -m "dry-run2" dry-run2 &&
                git tag -m "dry-run-tag1" dry-run-tag1 HEAD^ &&
                git p4 submit --dry-run --export-labels >out &&
-               test_i18ngrep "Would create p4 label" out
+               test_grep "Would create p4 label" out
        ) &&
        (
                cd "$cli" &&
@@ -443,7 +443,7 @@ test_expect_success 'description with Jobs section and bogus following text' '
                # build a job
                make_job $(cat jobname) &&
                test_must_fail git p4 submit 2>err &&
-               test_i18ngrep "Unknown field name" err
+               test_grep "Unknown field name" err
        ) &&
        (
                cd "$cli" &&
@@ -461,9 +461,9 @@ test_expect_success 'submit --prepare-p4-only' '
                git add prep-only-add &&
                git commit -m "prep only add" &&
                git p4 submit --prepare-p4-only >out &&
-               test_i18ngrep "prepared for submission" out &&
-               test_i18ngrep "must be deleted" out &&
-               test_i18ngrep ! "everything below this line is just the diff" out
+               test_grep "prepared for submission" out &&
+               test_grep "must be deleted" out &&
+               test_grep ! "everything below this line is just the diff" out
        ) &&
        (
                cd "$cli" &&
index 468767cbf4b93eb20f209fc25b46cc0f53d5b907..2a9838f37fe293738a75e626041531456c274f72 100755 (executable)
@@ -216,7 +216,7 @@ test_expect_success 'detect copies' '
 # variable exists, which allows admins to disable the "p4 move" command.
 test_lazy_prereq P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW '
        p4 configure show run.move.allow >out &&
-       egrep ^run.move.allow: out
+       grep -E ^run.move.allow: out
 '
 
 # If move can be disabled, turn it off and test p4 move handling
index 9779dc0d11f33b6d8a6c5b8d8ffded834eddd8b2..c766fd159f14b69eb46f9dc9bfb4357fdd7a5e34 100755 (executable)
@@ -35,7 +35,7 @@ test_expect_success 'conflict on one commit' '
                git add file1 &&
                git commit -m "line3 in file1 will conflict" &&
                test_expect_code 1 git p4 submit >out &&
-               test_i18ngrep "No commits applied" out
+               test_grep "No commits applied" out
        )
 '
 
@@ -58,7 +58,7 @@ test_expect_success 'conflict on second of two commits' '
                git add file1 &&
                git commit -m "line4 in file1 will conflict" &&
                test_expect_code 1 git p4 submit >out &&
-               test_i18ngrep "Applied only the commits" out
+               test_grep "Applied only the commits" out
        )
 '
 
@@ -81,7 +81,7 @@ test_expect_success 'conflict on first of two commits, skip' '
                # but this commit is okay
                test_commit "okay_commit_after_skip" &&
                echo s | test_expect_code 1 git p4 submit >out &&
-               test_i18ngrep "Applied only the commits" out
+               test_grep "Applied only the commits" out
        )
 '
 
@@ -104,7 +104,7 @@ test_expect_success 'conflict on first of two commits, quit' '
                # but this commit is okay
                test_commit "okay_commit_after_quit" &&
                echo q | test_expect_code 1 git p4 submit >out &&
-               test_i18ngrep "No commits applied" out
+               test_grep "No commits applied" out
        )
 '
 
@@ -144,7 +144,7 @@ test_expect_success 'conflict on first of two commits, --conflict=skip' '
                # but this commit is okay
                test_commit "okay_commit_after_auto_skip" &&
                test_expect_code 1 git p4 submit --conflict=skip >out &&
-               test_i18ngrep "Applied only the commits" out
+               test_grep "Applied only the commits" out
        )
 '
 
@@ -167,7 +167,7 @@ test_expect_success 'conflict on first of two commits, --conflict=quit' '
                # but this commit is okay
                test_commit "okay_commit_after_auto_quit" &&
                test_expect_code 1 git p4 submit --conflict=quit >out &&
-               test_i18ngrep "No commits applied" out
+               test_grep "No commits applied" out
        )
 '
 
@@ -417,8 +417,8 @@ test_expect_success 'cleanup chmod after submit cancel' '
                ! p4 fstat -T action text &&
                test_path_is_file text+x &&
                ! p4 fstat -T action text+x &&
-               ls -l text | egrep ^-r-- &&
-               ls -l text+x | egrep ^-r-x
+               ls -l text | grep -E ^-r-- &&
+               ls -l text+x | grep -E ^-r-x
        )
 '
 
index 932841003cfc4e3f6273087f8567d6c6a660bb0b..5e904ac80d85226a74296774ef4279369da15d4a 100755 (executable)
@@ -9,7 +9,7 @@ test_expect_success 'start p4d' '
 '
 
 # See
-# http://www.perforce.com/perforce/doc.current/manuals/p4sag/03_superuser.html#1088563
+# https://web.archive.org/web/20150602090517/http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html#superuser.basic.typemap_locking
 # for suggestions on how to configure "sitewide pessimistic locking"
 # where only one person can have a file open for edit at a time.
 test_expect_success 'init depot' '
index 43de868b8005d330c10ec5d81fcacdc325aff858..aa9a614de33a17f886b7517daeabad5f48bdc42c 100755 (executable)
@@ -405,40 +405,40 @@ test_expect_success '__gitdir - remote as argument' '
 
 test_expect_success '__git_dequote - plain unquoted word' '
        __git_dequote unquoted-word &&
-       verbose test unquoted-word = "$dequoted_word"
+       test unquoted-word = "$dequoted_word"
 '
 
 # input:    b\a\c\k\'\\\"s\l\a\s\h\es
 # expected: back'\"slashes
 test_expect_success '__git_dequote - backslash escaped' '
        __git_dequote "b\a\c\k\\'\''\\\\\\\"s\l\a\s\h\es" &&
-       verbose test "back'\''\\\"slashes" = "$dequoted_word"
+       test "back'\''\\\"slashes" = "$dequoted_word"
 '
 
 # input:    sin'gle\' '"quo'ted
 # expected: single\ "quoted
 test_expect_success '__git_dequote - single quoted' '
        __git_dequote "'"sin'gle\\\\' '\\\"quo'ted"'" &&
-       verbose test '\''single\ "quoted'\'' = "$dequoted_word"
+       test '\''single\ "quoted'\'' = "$dequoted_word"
 '
 
 # input:    dou"ble\\" "\"\quot"ed
 # expected: double\ "\quoted
 test_expect_success '__git_dequote - double quoted' '
        __git_dequote '\''dou"ble\\" "\"\quot"ed'\'' &&
-       verbose test '\''double\ "\quoted'\'' = "$dequoted_word"
+       test '\''double\ "\quoted'\'' = "$dequoted_word"
 '
 
 # input: 'open single quote
 test_expect_success '__git_dequote - open single quote' '
        __git_dequote "'\''open single quote" &&
-       verbose test "open single quote" = "$dequoted_word"
+       test "open single quote" = "$dequoted_word"
 '
 
 # input: "open double quote
 test_expect_success '__git_dequote - open double quote' '
        __git_dequote "\"open double quote" &&
-       verbose test "open double quote" = "$dequoted_word"
+       test "open double quote" = "$dequoted_word"
 '
 
 
@@ -616,7 +616,7 @@ test_expect_success '__git_is_configured_remote' '
        test_when_finished "git remote remove remote_2" &&
        git remote add remote_2 git://remote_2 &&
        (
-               verbose __git_is_configured_remote remote_2 &&
+               __git_is_configured_remote remote_2 &&
                test_must_fail __git_is_configured_remote non-existent
        )
 '
@@ -1571,7 +1571,7 @@ test_expect_success FUNNYNAMES,!CYGWIN 'cone mode sparse-checkout completes dire
        )
 '
 
-test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
+test_expect_success 'non-cone mode sparse-checkout gives rooted paths' '
        # reset sparse-checkout repo to non-cone mode
        git -C sparse-checkout sparse-checkout disable &&
        git -C sparse-checkout sparse-checkout set --no-cone &&
@@ -1581,7 +1581,12 @@ test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
                # expected to be empty since we have not configured
                # custom completion for non-cone mode
                test_completion "git sparse-checkout set f" <<-\EOF
-
+               /folder1/0/1/t.txt Z
+               /folder1/expected Z
+               /folder1/out Z
+               /folder1/out_sorted Z
+               /folder2/0/t.txt Z
+               /folder3/t.txt Z
                EOF
        )
 '
@@ -1622,14 +1627,22 @@ test_expect_success 'git checkout - with -d, complete only references' '
 '
 
 test_expect_success 'git switch - with --track, complete only remote branches' '
-       test_completion "git switch --track " <<-\EOF
+       test_completion "git switch --track " <<-\EOF &&
+       other/branch-in-other Z
+       other/main-in-other Z
+       EOF
+       test_completion "git switch -t " <<-\EOF
        other/branch-in-other Z
        other/main-in-other Z
        EOF
 '
 
 test_expect_success 'git checkout - with --track, complete only remote branches' '
-       test_completion "git checkout --track " <<-\EOF
+       test_completion "git checkout --track " <<-\EOF &&
+       other/branch-in-other Z
+       other/main-in-other Z
+       EOF
+       test_completion "git checkout -t " <<-\EOF
        other/branch-in-other Z
        other/main-in-other Z
        EOF
@@ -2255,6 +2268,36 @@ test_expect_success 'checkout completes ref names' '
        EOF
 '
 
+test_expect_success 'checkout does not match ref names of a different case' '
+       test_completion "git checkout M" ""
+'
+
+test_expect_success 'checkout matches case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+       (
+               GIT_COMPLETION_IGNORE_CASE=1 &&
+               test_completion "git checkout M" <<-\EOF
+               main Z
+               mybranch Z
+               mytag Z
+               EOF
+       )
+'
+
+test_expect_success 'checkout completes pseudo refs' '
+       test_completion "git checkout H" <<-\EOF
+       HEAD Z
+       EOF
+'
+
+test_expect_success 'checkout completes pseudo refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+       (
+               GIT_COMPLETION_IGNORE_CASE=1 &&
+               test_completion "git checkout h" <<-\EOF
+               HEAD Z
+               EOF
+       )
+'
+
 test_expect_success 'git -C <path> checkout uses the right repo' '
        test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF
        branch-in-other Z
@@ -2426,6 +2469,24 @@ test_expect_success 'completion used <cmd> completion for alias: !f() { : git <c
        EOF
 '
 
+test_expect_success 'completion used <cmd> completion for alias: !f() { : <cmd> ; ... }' '
+       test_config alias.co "!f() { : checkout ; if ... } f" &&
+       test_completion "git co m" <<-\EOF
+       main Z
+       mybranch Z
+       mytag Z
+       EOF
+'
+
+test_expect_success 'completion used <cmd> completion for alias: !f() { : <cmd>; ... }' '
+       test_config alias.co "!f() { : checkout; if ... } f" &&
+       test_completion "git co m" <<-\EOF
+       main Z
+       mybranch Z
+       mytag Z
+       EOF
+'
+
 test_expect_success 'completion without explicit _git_xxx function' '
        test_completion "git version --" <<-\EOF
        --build-options Z
@@ -2566,30 +2627,30 @@ test_expect_success 'options with value' '
 test_expect_success 'sourcing the completion script clears cached commands' '
        (
                __git_compute_all_commands &&
-               verbose test -n "$__git_all_commands" &&
+               test -n "$__git_all_commands" &&
                . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
-               verbose test -z "$__git_all_commands"
+               test -z "$__git_all_commands"
        )
 '
 
 test_expect_success 'sourcing the completion script clears cached merge strategies' '
        (
                __git_compute_merge_strategies &&
-               verbose test -n "$__git_merge_strategies" &&
+               test -n "$__git_merge_strategies" &&
                . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
-               verbose test -z "$__git_merge_strategies"
+               test -z "$__git_merge_strategies"
        )
 '
 
 test_expect_success 'sourcing the completion script clears cached --options' '
        (
                __gitcomp_builtin checkout &&
-               verbose test -n "$__gitcomp_builtin_checkout" &&
+               test -n "$__gitcomp_builtin_checkout" &&
                __gitcomp_builtin notes_edit &&
-               verbose test -n "$__gitcomp_builtin_notes_edit" &&
+               test -n "$__gitcomp_builtin_notes_edit" &&
                . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
-               verbose test -z "$__gitcomp_builtin_checkout" &&
-               verbose test -z "$__gitcomp_builtin_notes_edit"
+               test -z "$__gitcomp_builtin_checkout" &&
+               test -z "$__gitcomp_builtin_notes_edit"
        )
 '
 
index d459fae6551bd7b5812f8b76a5faae8ebd4075df..d667dda654e2de7f32d5f7d065f6eac59ca9fdbf 100755 (executable)
@@ -13,10 +13,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
 
 actual="$TRASH_DIRECTORY/actual"
-c_red='\\[\\e[31m\\]'
-c_green='\\[\\e[32m\\]'
-c_lblue='\\[\\e[1;34m\\]'
-c_clear='\\[\\e[0m\\]'
+c_red='\001\e[31m\002'
+c_green='\001\e[32m\002'
+c_lblue='\001\e[1;34m\002'
+c_clear='\001\e[0m\002'
 
 test_expect_success 'setup for prompt tests' '
        git init otherrepo &&
index c6479f24eb5ac291a29664903b3b6393d04748c6..b5eaf7fdc1186d4420388c355e794bfdba783f2f 100644 (file)
@@ -14,7 +14,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # The semantics of the editor variables are that of invoking
 # sh -c "$EDITOR \"$@\"" files ...
@@ -32,6 +32,14 @@ test_set_editor () {
        export EDITOR
 }
 
+# Like test_set_editor but sets GIT_SEQUENCE_EDITOR instead of EDITOR
+test_set_sequence_editor () {
+       FAKE_SEQUENCE_EDITOR="$1"
+       export FAKE_SEQUENCE_EDITOR
+       GIT_SEQUENCE_EDITOR='"$FAKE_SEQUENCE_EDITOR"'
+       export GIT_SEQUENCE_EDITOR
+}
+
 test_decode_color () {
        awk '
                function name(n) {
@@ -243,6 +251,61 @@ debug () {
        done
 }
 
+# Usage: test_ref_exists [options] <ref>
+#
+#   -C <dir>:
+#      Run all git commands in directory <dir>
+#
+# This helper function checks whether a reference exists. Symrefs or object IDs
+# will not be resolved. Can be used to check references with bad names.
+test_ref_exists () {
+       local indir=
+
+       while test $# != 0
+       do
+               case "$1" in
+               -C)
+                       indir="$2"
+                       shift
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done &&
+
+       indir=${indir:+"$indir"/} &&
+
+       if test "$#" != 1
+       then
+               BUG "expected exactly one reference"
+       fi &&
+
+       git ${indir:+ -C "$indir"} show-ref --exists "$1"
+}
+
+# Behaves the same as test_ref_exists, except that it checks for the absence of
+# a reference. This is preferable to `! test_ref_exists` as this function is
+# able to distinguish actually-missing references from other, generic errors.
+test_ref_missing () {
+       test_ref_exists "$@"
+       case "$?" in
+       2)
+               # This is the good case.
+               return 0
+               ;;
+       0)
+               echo >&4 "test_ref_missing: reference exists"
+               return 1
+               ;;
+       *)
+               echo >&4 "test_ref_missing: generic error"
+               return 1
+               ;;
+       esac
+}
+
 # Usage: test_commit [options] <message> [<file> [<contents> [<tag>]]]
 #   -C <dir>:
 #      Run all git commands in directory <dir>
@@ -273,13 +336,13 @@ debug () {
 # <file>, <contents>, and <tag> all default to <message>.
 
 test_commit () {
-       notick= &&
-       echo=echo &&
-       append= &&
-       author= &&
-       signoff= &&
-       indir= &&
-       tag=light &&
+       local notick= &&
+       local echo=echo &&
+       local append= &&
+       local author= &&
+       local signoff= &&
+       local indir= &&
+       local tag=light &&
        while test $# != 0
        do
                case "$1" in
@@ -322,7 +385,7 @@ test_commit () {
                shift
        done &&
        indir=${indir:+"$indir"/} &&
-       file=${2:-"$1.t"} &&
+       local file=${2:-"$1.t"} &&
        if test -n "$append"
        then
                $echo "${3-$1}" >>"$indir$file"
@@ -534,8 +597,17 @@ test_config () {
                config_dir=$1
                shift
        fi
-       test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} '$1'" &&
-       git ${config_dir:+-C "$config_dir"} config "$@"
+
+       # If --worktree is provided, use it to configure/unconfigure
+       is_worktree=
+       if test "$1" = --worktree
+       then
+               is_worktree=1
+               shift
+       fi
+
+       test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} ${is_worktree:+--worktree} '$1'" &&
+       git ${config_dir:+-C "$config_dir"} config ${is_worktree:+--worktree} "$@"
 }
 
 test_config_global () {
@@ -893,11 +965,20 @@ test_path_is_symlink () {
        fi
 }
 
+test_path_is_executable () {
+       test "$#" -ne 1 && BUG "1 param"
+       if ! test -x "$1"
+       then
+               echo "$1 is not executable"
+               false
+       fi
+}
+
 # Check if the directory exists and is empty as expected, barf otherwise.
 test_dir_is_empty () {
        test "$#" -ne 1 && BUG "1 param"
        test_path_is_dir "$1" &&
-       if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+       if test -n "$(ls -a1 "$1" | grep -E -v '^\.\.?$')"
        then
                echo "Directory '$1' is not empty, it contains:"
                ls -la "$1"
@@ -921,10 +1002,6 @@ test_path_is_missing () {
        then
                echo "Path exists:"
                ls -ld "$1"
-               if test $# -ge 1
-               then
-                       echo "$*"
-               fi
                false
        fi
 }
@@ -1020,7 +1097,7 @@ test_must_fail_acceptable () {
        fi
 
        case "$1" in
-       git|__git*|test-tool|test_terminal)
+       git|__git*|scalar|test-tool|test_terminal)
                return 0
                ;;
        *)
@@ -1186,19 +1263,21 @@ test_cmp_bin () {
        cmp "$@"
 }
 
-# Wrapper for grep which used to be used for
-# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other
-# in-flight changes. Should not be used and will be removed soon.
+# Deprecated - do not use this in new code
 test_i18ngrep () {
+       test_grep "$@"
+}
+
+test_grep () {
        eval "last_arg=\${$#}"
 
        test -f "$last_arg" ||
-       BUG "test_i18ngrep requires a file to read as the last parameter"
+       BUG "test_grep requires a file to read as the last parameter"
 
        if test $# -lt 2 ||
           { test "x!" = "x$1" && test $# -lt 3 ; }
        then
-               BUG "too few parameters to test_i18ngrep"
+               BUG "too few parameters to test_grep"
        fi
 
        if test "x!" = "x$1"
@@ -1223,15 +1302,6 @@ test_i18ngrep () {
        return 1
 }
 
-# Call any command "$@" but be more verbose about its
-# failure. This is handy for commands like "test" which do
-# not output anything when they fail.
-verbose () {
-       "$@" && return 0
-       echo >&4 "command failed: $(git rev-parse --sq-quote "$@")"
-       return 1
-}
-
 # Check if the file expected to be empty is indeed empty, and barfs
 # otherwise.
 
@@ -1278,6 +1348,39 @@ test_cmp_rev () {
        fi
 }
 
+# Tests that a commit message matches the expected text
+#
+# Usage: test_commit_message <rev> [-m <msg> | <file>]
+#
+# When using "-m" <msg> will have a line feed appended. If the second
+# argument is omitted then the expected message is read from stdin.
+
+test_commit_message () {
+       local msg_file=expect.msg
+
+       case $# in
+       3)
+               if test "$2" = "-m"
+               then
+                       printf "%s\n" "$3" >"$msg_file"
+               else
+                       BUG "Usage: test_commit_message <rev> [-m <message> | <file>]"
+               fi
+               ;;
+       2)
+               msg_file="$2"
+               ;;
+       1)
+               cat >"$msg_file"
+               ;;
+       *)
+               BUG "Usage: test_commit_message <rev> [-m <message> | <file>]"
+               ;;
+       esac
+       git show --no-patch --pretty=format:%B "$1" -- >actual.msg &&
+       test_cmp "$msg_file" actual.msg
+}
+
 # Compare paths respecting core.ignoreCase
 test_cmp_fspath () {
        if test "x$1" = "x$2"
@@ -1426,7 +1529,7 @@ test_bool_env () {
                BUG "test_bool_env requires two parameters (variable name and default value)"
        fi
 
-       git env--helper --type=bool --default="$2" --exit-code "$1"
+       test-tool env-helper --type=bool --default="$2" --exit-code "$1"
        ret=$?
        case $ret in
        0|1)    # unset or valid bool value
@@ -1454,72 +1557,6 @@ test_skip_or_die () {
        error "$2"
 }
 
-# The following mingw_* functions obey POSIX shell syntax, but are actually
-# bash scripts, and are meant to be used only with bash on Windows.
-
-# A test_cmp function that treats LF and CRLF equal and avoids to fork
-# diff when possible.
-mingw_test_cmp () {
-       # Read text into shell variables and compare them. If the results
-       # are different, use regular diff to report the difference.
-       local test_cmp_a= test_cmp_b=
-
-       # When text came from stdin (one argument is '-') we must feed it
-       # to diff.
-       local stdin_for_diff=
-
-       # Since it is difficult to detect the difference between an
-       # empty input file and a failure to read the files, we go straight
-       # to diff if one of the inputs is empty.
-       if test -s "$1" && test -s "$2"
-       then
-               # regular case: both files non-empty
-               mingw_read_file_strip_cr_ test_cmp_a <"$1"
-               mingw_read_file_strip_cr_ test_cmp_b <"$2"
-       elif test -s "$1" && test "$2" = -
-       then
-               # read 2nd file from stdin
-               mingw_read_file_strip_cr_ test_cmp_a <"$1"
-               mingw_read_file_strip_cr_ test_cmp_b
-               stdin_for_diff='<<<"$test_cmp_b"'
-       elif test "$1" = - && test -s "$2"
-       then
-               # read 1st file from stdin
-               mingw_read_file_strip_cr_ test_cmp_a
-               mingw_read_file_strip_cr_ test_cmp_b <"$2"
-               stdin_for_diff='<<<"$test_cmp_a"'
-       fi
-       test -n "$test_cmp_a" &&
-       test -n "$test_cmp_b" &&
-       test "$test_cmp_a" = "$test_cmp_b" ||
-       eval "diff -u \"\$@\" $stdin_for_diff"
-}
-
-# $1 is the name of the shell variable to fill in
-mingw_read_file_strip_cr_ () {
-       # Read line-wise using LF as the line separator
-       # and use IFS to strip CR.
-       local line
-       while :
-       do
-               if IFS=$'\r' read -r -d $'\n' line
-               then
-                       # good
-                       line=$line$'\n'
-               else
-                       # we get here at EOF, but also if the last line
-                       # was not terminated by LF; in the latter case,
-                       # some text was read
-                       if test -z "$line"
-                       then
-                               # EOF, really
-                               break
-                       fi
-               fi
-               eval "$1=\$$1\$line"
-       done
-}
-
 # Like "env FOO=BAR some-program", but run inside a subshell, which means
 # it also works for shell functions (though those functions cannot impact
 # the environment outside of the test_env invocation).
@@ -1622,6 +1659,11 @@ test_detect_hash () {
        test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
 }
 
+# Detect the hash algorithm in use.
+test_detect_ref_format () {
+       echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
+}
+
 # Load common hash metadata and common placeholder object IDs for use with
 # test_oid.
 test_oid_init () {
@@ -1686,7 +1728,7 @@ test_oid () {
        then
                BUG "undefined key '$1'"
        fi &&
-       eval "printf '%s' \"\${$var}\""
+       eval "printf '%s\n' \"\${$var}\""
 }
 
 # Insert a slash into an object ID so it can be used to reference a location
@@ -1755,6 +1797,13 @@ test_path_is_hidden () {
        return 1
 }
 
+# Poor man's URI escaping. Good enough for the test suite whose trash
+# directory has a space in it. See 93c3fcbe4d4 (git-svn: attempt to
+# mimic SVN 1.7 URL canonicalization, 2012-07-28) for prior art.
+test_uri_escape() {
+       sed 's/ /%20/g'
+}
+
 # Check that the given command was invoked as part of the
 # trace2-format trace on stdin.
 #
@@ -1830,6 +1879,28 @@ test_region () {
        return 0
 }
 
+# Check that the given data fragment was included as part of the
+# trace2-format trace on stdin.
+#
+#      test_trace2_data <category> <key> <value>
+#
+# For example, to look for trace2_data_intmax("pack-objects", repo,
+# "reused", N) in an invocation of "git pack-objects", run:
+#
+#      GIT_TRACE2_EVENT="$(pwd)/trace.txt" git pack-objects ... &&
+#      test_trace2_data pack-objects reused N <trace2.txt
+test_trace2_data () {
+       grep -e '"category":"'"$1"'","key":"'"$2"'","value":"'"$3"'"'
+}
+
+# Given a GIT_TRACE2_EVENT log over stdin, writes to stdout a list of URLs
+# sent to git-remote-https child processes.
+test_remote_https_urls() {
+       grep -e '"event":"child_start".*"argv":\["git-remote-https",".*"\]' |
+               sed -e 's/{"event":"child_start".*"argv":\["git-remote-https","//g' \
+                   -e 's/"\]}//g'
+}
+
 # Print the destination of symlink(s) provided as arguments. Basically
 # the same as the readlink command, but it's not available everywhere.
 test_readlink () {
@@ -1868,3 +1939,22 @@ test_is_magic_mtime () {
        rm -f .git/test-mtime-actual
        return $ret
 }
+
+# Given two filenames, parse both using 'git config --list --file'
+# and compare the sorted output of those commands. Useful when
+# wanting to ignore whitespace differences and sorting concerns.
+test_cmp_config_output () {
+       git config --list --file="$1" >config-expect &&
+       git config --list --file="$2" >config-actual &&
+       sort config-expect >sorted-expect &&
+       sort config-actual >sorted-actual &&
+       test_cmp sorted-expect sorted-actual
+}
+
+# Given a filename, extract its trailing hash as a hex string
+test_trailing_hash () {
+       local file="$1" &&
+       tail -c $(test_oid rawsz) "$file" |
+               test-tool hexdump |
+               sed "s/ //g"
+}
index 9c5339c577ac3fd50090baf0d84caf6d62df0697..970c6538cba7a8d9465b6774b23e367c4b257d88 100644 (file)
@@ -14,7 +14,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 #
 # The idea is for `test-lib.sh` to source this file when run in GitHub
 # workflows; these functions will then override (empty) functions
index 79c31c788b921baa3118c918f731ed46b450393b..76cbbd3299d64a82276c0aa30241479388014af8 100644 (file)
@@ -15,7 +15,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 #
 # The idea is for `test-lib.sh` to source this file when the user asks
 # for JUnit XML; these functions will then override (empty) functions
index 6ca68311eb9ea3db2671dabd794b0229e270edd2..fc93aa57e6116d5602b17a63f41bd4b8746289e2 100644 (file)
@@ -13,7 +13,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
@@ -47,6 +47,16 @@ then
        echo "PANIC: Running in a $TEST_DIRECTORY that doesn't end in '/t'?" >&2
        exit 1
 fi
+if test -f "$GIT_BUILD_DIR/GIT-BUILD-DIR"
+then
+       GIT_BUILD_DIR="$(cat "$GIT_BUILD_DIR/GIT-BUILD-DIR")" || exit 1
+       # On Windows, we must convert Windows paths lest they contain a colon
+       case "$(uname -s)" in
+       *MINGW*)
+               GIT_BUILD_DIR="$(cygpath -au "$GIT_BUILD_DIR")"
+               ;;
+       esac
+fi
 
 # Prepend a string to a VAR using an arbitrary ":" delimiter, not
 # adding the delimiter if VAR or VALUE is empty. I.e. a generalized:
@@ -79,6 +89,9 @@ prepend_var LSAN_OPTIONS : $GIT_SAN_OPTIONS
 prepend_var LSAN_OPTIONS : fast_unwind_on_malloc=0
 export LSAN_OPTIONS
 
+prepend_var UBSAN_OPTIONS : $GIT_SAN_OPTIONS
+export UBSAN_OPTIONS
+
 if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
 then
        echo >&2 'error: GIT-BUILD-OPTIONS missing (has Git been built?).'
@@ -324,6 +337,7 @@ nr_san_dir_leaks_ () {
        find "$TEST_RESULTS_SAN_DIR" \
                -type f \
                -name "$TEST_RESULTS_SAN_FILE_PFX.*" 2>/dev/null |
+       xargs grep -lv "Unable to get registers from thread" |
        wc -l
 }
 
@@ -528,6 +542,8 @@ export EDITOR
 
 GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}"
 export GIT_DEFAULT_HASH
+GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
+export GIT_DEFAULT_REF_FORMAT
 GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}"
 export GIT_TEST_MERGE_ALGORITHM
 
@@ -635,12 +651,6 @@ u200c=$(printf '\342\200\214')
 
 export _x05 _x35 LF u200c EMPTY_TREE EMPTY_BLOB ZERO_OID OID_REGEX
 
-# Each test should start with something like this, after copyright notices:
-#
-# test_description='Description of this test...
-# This test checks if command xyzzy does the right thing...
-# '
-# . ./test-lib.sh
 test "x$TERM" != "xdumb" && (
                test -t 1 &&
                tput bold >/dev/null 2>&1 &&
@@ -1037,10 +1047,7 @@ want_trace () {
 # (and we want to make sure we run any cleanup like
 # "set +x").
 test_eval_inner_ () {
-       # Do not add anything extra (including LF) after '$*'
-       eval "
-               want_trace && trace_level_=$(($trace_level_+1)) && set -x
-               $*"
+       eval "$*"
 }
 
 test_eval_ () {
@@ -1065,7 +1072,10 @@ test_eval_ () {
        #     be _inside_ the block to avoid polluting the "set -x" output
        #
 
-       test_eval_inner_ "$@" </dev/null >&3 2>&4
+       # Do not add anything extra (including LF) after '$*'
+       test_eval_inner_ </dev/null >&3 2>&4 "
+               want_trace && trace_level_=$(($trace_level_+1)) && set -x
+               $*"
        {
                test_eval_ret_=$?
                if want_trace
@@ -1082,22 +1092,22 @@ test_eval_ () {
        return $test_eval_ret_
 }
 
+fail_117 () {
+       return 117
+}
+
 test_run_ () {
        test_cleanup=:
        expecting_failure=$2
 
        if test "${GIT_TEST_CHAIN_LINT:-1}" != 0; then
-               # turn off tracing for this test-eval, as it simply creates
-               # confusing noise in the "-x" output
-               trace_tmp=$trace
-               trace=
                # 117 is magic because it is unlikely to match the exit
                # code of other programs
-               if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
+               test_eval_inner_ "fail_117 && $1" </dev/null >&3 2>&4
+               if test $? != 117
                then
-                       BUG "broken &&-chain or run-away HERE-DOC: $1"
+                       BUG "broken &&-chain: $1"
                fi
-               trace=$trace_tmp
        fi
 
        setup_malloc_check
@@ -1532,8 +1542,8 @@ then
        # Normalize with test_bool_env
        passes_sanitize_leak=
 
-       # We need to see TEST_PASSES_SANITIZE_LEAK in "git
-       # env--helper" (via test_bool_env)
+       # We need to see TEST_PASSES_SANITIZE_LEAK in "test-tool
+       # env-helper" (via test_bool_env)
        export TEST_PASSES_SANITIZE_LEAK
        if test_bool_env TEST_PASSES_SANITIZE_LEAK false
        then
@@ -1589,7 +1599,8 @@ then
        BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_SANITIZE_LEAK_LOG=true"
 fi
 
-if test "${GIT_TEST_CHAIN_LINT:-1}" != 0
+if test "${GIT_TEST_CHAIN_LINT:-1}" != 0 &&
+   test "${GIT_TEST_EXT_CHAIN_LINT:-1}" != 0
 then
        "$PERL_PATH" "$TEST_DIRECTORY/chainlint.pl" "$0" ||
                BUG "lint error (see '?!...!? annotations above)"
@@ -1672,7 +1683,7 @@ yes () {
 # The GIT_TEST_FAIL_PREREQS code hooks into test_set_prereq(), and
 # thus needs to be set up really early, and set an internal variable
 # for convenience so the hot test_set_prereq() codepath doesn't need
-# to call "git env--helper" (via test_bool_env). Only do that work
+# to call "test-tool env-helper" (via test_bool_env). Only do that work
 # if needed by seeing if GIT_TEST_FAIL_PREREQS is set at all.
 GIT_TEST_FAIL_PREREQS_INTERNAL=
 if test -n "$GIT_TEST_FAIL_PREREQS"
@@ -1711,7 +1722,7 @@ case $uname_s in
        test_set_prereq SED_STRIPS_CR
        test_set_prereq GREP_STRIPS_CR
        test_set_prereq WINDOWS
-       GIT_TEST_CMP=mingw_test_cmp
+       GIT_TEST_CMP="GIT_DIR=/dev/null git diff --no-index --ignore-cr-at-eol --"
        ;;
 *CYGWIN*)
        test_set_prereq POSIXPERM
@@ -1736,7 +1747,14 @@ parisc* | hppa*)
        ;;
 esac
 
-test_set_prereq REFFILES
+case "$GIT_DEFAULT_REF_FORMAT" in
+files)
+       test_set_prereq REFFILES;;
+*)
+       echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT"
+       exit 1
+       ;;
+esac
 
 ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_CURL" && test_set_prereq LIBCURL
@@ -1927,8 +1945,8 @@ test_lazy_prereq SHA1 '
        esac
 '
 
-test_lazy_prereq ADD_I_USE_BUILTIN '
-       test_bool_env GIT_TEST_ADD_I_USE_BUILTIN true
+test_lazy_prereq DEFAULT_REPO_FORMAT '
+       test_have_prereq SHA1,REFFILES
 '
 
 # Ensure that no test accidentally triggers a Git command
index 1bcf01a9a42a6147a85db9bb4d82523740a95ad7..3810e9bb43190322cd932d520ace6ab1e874be82 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/perl
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use IO::Pty;
diff --git a/t/unit-tests/.gitignore b/t/unit-tests/.gitignore
new file mode 100644 (file)
index 0000000..5e56e04
--- /dev/null
@@ -0,0 +1 @@
+/bin
diff --git a/t/unit-tests/t-basic.c b/t/unit-tests/t-basic.c
new file mode 100644 (file)
index 0000000..fda1ae5
--- /dev/null
@@ -0,0 +1,95 @@
+#include "test-lib.h"
+
+/*
+ * The purpose of this "unit test" is to verify a few invariants of the unit
+ * test framework itself, as well as to provide examples of output from actually
+ * failing tests. As such, it is intended that this test fails, and thus it
+ * should not be run as part of `make unit-tests`. Instead, we verify it behaves
+ * as expected in the integration test t0080-unit-test-output.sh
+ */
+
+/* Used to store the return value of check_int(). */
+static int check_res;
+
+/* Used to store the return value of TEST(). */
+static int test_res;
+
+static void t_res(int expect)
+{
+       check_int(check_res, ==, expect);
+       check_int(test_res, ==, expect);
+}
+
+static void t_todo(int x)
+{
+       check_res = TEST_TODO(check(x));
+}
+
+static void t_skip(void)
+{
+       check(0);
+       test_skip("missing prerequisite");
+       check(1);
+}
+
+static int do_skip(void)
+{
+       test_skip("missing prerequisite");
+       return 1;
+}
+
+static void t_skip_todo(void)
+{
+       check_res = TEST_TODO(do_skip());
+}
+
+static void t_todo_after_fail(void)
+{
+       check(0);
+       TEST_TODO(check(0));
+}
+
+static void t_fail_after_todo(void)
+{
+       check(1);
+       TEST_TODO(check(0));
+       check(0);
+}
+
+static void t_messages(void)
+{
+       check_str("\thello\\", "there\"\n");
+       check_str("NULL", NULL);
+       check_char('a', ==, '\n');
+       check_char('\\', ==, '\'');
+}
+
+static void t_empty(void)
+{
+       ; /* empty */
+}
+
+int cmd_main(int argc, const char **argv)
+{
+       test_res = TEST(check_res = check_int(1, ==, 1), "passing test");
+       TEST(t_res(1), "passing test and assertion return 1");
+       test_res = TEST(check_res = check_int(1, ==, 2), "failing test");
+       TEST(t_res(0), "failing test and assertion return 0");
+       test_res = TEST(t_todo(0), "passing TEST_TODO()");
+       TEST(t_res(1), "passing TEST_TODO() returns 1");
+       test_res = TEST(t_todo(1), "failing TEST_TODO()");
+       TEST(t_res(0), "failing TEST_TODO() returns 0");
+       test_res = TEST(t_skip(), "test_skip()");
+       TEST(check_int(test_res, ==, 1), "skipped test returns 1");
+       test_res = TEST(t_skip_todo(), "test_skip() inside TEST_TODO()");
+       TEST(t_res(1), "test_skip() inside TEST_TODO() returns 1");
+       test_res = TEST(t_todo_after_fail(), "TEST_TODO() after failing check");
+       TEST(check_int(test_res, ==, 0), "TEST_TODO() after failing check returns 0");
+       test_res = TEST(t_fail_after_todo(), "failing check after TEST_TODO()");
+       TEST(check_int(test_res, ==, 0), "failing check after TEST_TODO() returns 0");
+       TEST(t_messages(), "messages from failing string and char comparison");
+       test_res = TEST(t_empty(), "test with no checks");
+       TEST(check_int(test_res, ==, 0), "test with no checks returns 0");
+
+       return test_done();
+}
diff --git a/t/unit-tests/t-mem-pool.c b/t/unit-tests/t-mem-pool.c
new file mode 100644 (file)
index 0000000..a0d57df
--- /dev/null
@@ -0,0 +1,31 @@
+#include "test-lib.h"
+#include "mem-pool.h"
+
+static void setup_static(void (*f)(struct mem_pool *), size_t block_alloc)
+{
+       struct mem_pool pool = { .block_alloc = block_alloc };
+       f(&pool);
+       mem_pool_discard(&pool, 0);
+}
+
+static void t_calloc_100(struct mem_pool *pool)
+{
+       size_t size = 100;
+       char *buffer = mem_pool_calloc(pool, 1, size);
+       for (size_t i = 0; i < size; i++)
+               check_int(buffer[i], ==, 0);
+       if (!check(pool->mp_block != NULL))
+               return;
+       check(pool->mp_block->next_free != NULL);
+       check(pool->mp_block->end != NULL);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+       TEST(setup_static(t_calloc_100, 1024 * 1024),
+            "mem_pool_calloc returns 100 zeroed bytes with big block");
+       TEST(setup_static(t_calloc_100, 1),
+            "mem_pool_calloc returns 100 zeroed bytes with tiny block");
+
+       return test_done();
+}
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
new file mode 100644 (file)
index 0000000..de434a4
--- /dev/null
@@ -0,0 +1,120 @@
+#include "test-lib.h"
+#include "strbuf.h"
+
+/* wrapper that supplies tests with an empty, initialized strbuf */
+static void setup(void (*f)(struct strbuf*, void*), void *data)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       f(&buf, data);
+       strbuf_release(&buf);
+       check_uint(buf.len, ==, 0);
+       check_uint(buf.alloc, ==, 0);
+}
+
+/* wrapper that supplies tests with a populated, initialized strbuf */
+static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       strbuf_addstr(&buf, init_str);
+       check_uint(buf.len, ==, strlen(init_str));
+       f(&buf, data);
+       strbuf_release(&buf);
+       check_uint(buf.len, ==, 0);
+       check_uint(buf.alloc, ==, 0);
+}
+
+static int assert_sane_strbuf(struct strbuf *buf)
+{
+       /* Initialized strbufs should always have a non-NULL buffer */
+       if (!check(!!buf->buf))
+               return 0;
+       /* Buffers should always be NUL-terminated */
+       if (!check_char(buf->buf[buf->len], ==, '\0'))
+               return 0;
+       /*
+        * Freshly-initialized strbufs may not have a dynamically allocated
+        * buffer
+        */
+       if (buf->len == 0 && buf->alloc == 0)
+               return 1;
+       /* alloc must be at least one byte larger than len */
+       return check_uint(buf->len, <, buf->alloc);
+}
+
+static void t_static_init(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       check_uint(buf.len, ==, 0);
+       check_uint(buf.alloc, ==, 0);
+       check_char(buf.buf[0], ==, '\0');
+}
+
+static void t_dynamic_init(void)
+{
+       struct strbuf buf;
+
+       strbuf_init(&buf, 1024);
+       check(assert_sane_strbuf(&buf));
+       check_uint(buf.len, ==, 0);
+       check_uint(buf.alloc, >=, 1024);
+       check_char(buf.buf[0], ==, '\0');
+       strbuf_release(&buf);
+}
+
+static void t_addch(struct strbuf *buf, void *data)
+{
+       const char *p_ch = data;
+       const char ch = *p_ch;
+       size_t orig_alloc = buf->alloc;
+       size_t orig_len = buf->len;
+
+       if (!check(assert_sane_strbuf(buf)))
+               return;
+       strbuf_addch(buf, ch);
+       if (!check(assert_sane_strbuf(buf)))
+               return;
+       if (!(check_uint(buf->len, ==, orig_len + 1) &&
+             check_uint(buf->alloc, >=, orig_alloc)))
+               return; /* avoid de-referencing buf->buf */
+       check_char(buf->buf[buf->len - 1], ==, ch);
+       check_char(buf->buf[buf->len], ==, '\0');
+}
+
+static void t_addstr(struct strbuf *buf, void *data)
+{
+       const char *text = data;
+       size_t len = strlen(text);
+       size_t orig_alloc = buf->alloc;
+       size_t orig_len = buf->len;
+
+       if (!check(assert_sane_strbuf(buf)))
+               return;
+       strbuf_addstr(buf, text);
+       if (!check(assert_sane_strbuf(buf)))
+               return;
+       if (!(check_uint(buf->len, ==, orig_len + len) &&
+             check_uint(buf->alloc, >=, orig_alloc) &&
+             check_uint(buf->alloc, >, orig_len + len) &&
+             check_char(buf->buf[orig_len + len], ==, '\0')))
+           return;
+       check_str(buf->buf + orig_len, text);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+       if (!TEST(t_static_init(), "static initialization works"))
+               test_skip_all("STRBUF_INIT is broken");
+       TEST(t_dynamic_init(), "dynamic initialization works");
+       TEST(setup(t_addch, "a"), "strbuf_addch adds char");
+       TEST(setup(t_addch, ""), "strbuf_addch adds NUL char");
+       TEST(setup_populated(t_addch, "initial value", "a"),
+            "strbuf_addch appends to initial value");
+       TEST(setup(t_addstr, "hello there"), "strbuf_addstr adds string");
+       TEST(setup_populated(t_addstr, "initial value", "hello there"),
+            "strbuf_addstr appends string to initial value");
+
+       return test_done();
+}
diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c
new file mode 100644 (file)
index 0000000..7bf9dfd
--- /dev/null
@@ -0,0 +1,374 @@
+#include "test-lib.h"
+
+enum result {
+       RESULT_NONE,
+       RESULT_FAILURE,
+       RESULT_SKIP,
+       RESULT_SUCCESS,
+       RESULT_TODO
+};
+
+static struct {
+       enum result result;
+       int count;
+       unsigned failed :1;
+       unsigned lazy_plan :1;
+       unsigned running :1;
+       unsigned skip_all :1;
+       unsigned todo :1;
+} ctx = {
+       .lazy_plan = 1,
+       .result = RESULT_NONE,
+};
+
+#ifndef _MSC_VER
+#define make_relative(location) location
+#else
+/*
+ * Visual C interpolates the absolute Windows path for `__FILE__`,
+ * but we want to see relative paths, as verified by t0080.
+ */
+#include "dir.h"
+
+static const char *make_relative(const char *location)
+{
+       static char prefix[] = __FILE__, buf[PATH_MAX], *p;
+       static size_t prefix_len;
+
+       if (!prefix_len) {
+               size_t len = strlen(prefix);
+               const char *needle = "\\t\\unit-tests\\test-lib.c";
+               size_t needle_len = strlen(needle);
+
+               if (len < needle_len || strcmp(needle, prefix + len - needle_len))
+                       die("unexpected suffix of '%s'", prefix);
+
+               /* let it end in a directory separator */
+               prefix_len = len - needle_len + 1;
+       }
+
+       /* Does it not start with the expected prefix? */
+       if (fspathncmp(location, prefix, prefix_len))
+               return location;
+
+       strlcpy(buf, location + prefix_len, sizeof(buf));
+       /* convert backslashes to forward slashes */
+       for (p = buf; *p; p++)
+               if (*p == '\\')
+                       *p = '/';
+
+       return buf;
+}
+#endif
+
+static void msg_with_prefix(const char *prefix, const char *format, va_list ap)
+{
+       fflush(stderr);
+       if (prefix)
+               fprintf(stdout, "%s", prefix);
+       vprintf(format, ap); /* TODO: handle newlines */
+       putc('\n', stdout);
+       fflush(stdout);
+}
+
+void test_msg(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+       msg_with_prefix("# ", format, ap);
+       va_end(ap);
+}
+
+void test_plan(int count)
+{
+       assert(!ctx.running);
+
+       fflush(stderr);
+       printf("1..%d\n", count);
+       fflush(stdout);
+       ctx.lazy_plan = 0;
+}
+
+int test_done(void)
+{
+       assert(!ctx.running);
+
+       if (ctx.lazy_plan)
+               test_plan(ctx.count);
+
+       return ctx.failed;
+}
+
+void test_skip(const char *format, ...)
+{
+       va_list ap;
+
+       assert(ctx.running);
+
+       ctx.result = RESULT_SKIP;
+       va_start(ap, format);
+       if (format)
+               msg_with_prefix("# skipping test - ", format, ap);
+       va_end(ap);
+}
+
+void test_skip_all(const char *format, ...)
+{
+       va_list ap;
+       const char *prefix;
+
+       if (!ctx.count && ctx.lazy_plan) {
+               /* We have not printed a test plan yet */
+               prefix = "1..0 # SKIP ";
+               ctx.lazy_plan = 0;
+       } else {
+               /* We have already printed a test plan */
+               prefix = "Bail out! # ";
+               ctx.failed = 1;
+       }
+       ctx.skip_all = 1;
+       ctx.result = RESULT_SKIP;
+       va_start(ap, format);
+       msg_with_prefix(prefix, format, ap);
+       va_end(ap);
+}
+
+int test__run_begin(void)
+{
+       assert(!ctx.running);
+
+       ctx.count++;
+       ctx.result = RESULT_NONE;
+       ctx.running = 1;
+
+       return ctx.skip_all;
+}
+
+static void print_description(const char *format, va_list ap)
+{
+       if (format) {
+               fputs(" - ", stdout);
+               vprintf(format, ap);
+       }
+}
+
+int test__run_end(int was_run UNUSED, const char *location, const char *format, ...)
+{
+       va_list ap;
+
+       assert(ctx.running);
+       assert(!ctx.todo);
+
+       fflush(stderr);
+       va_start(ap, format);
+       if (!ctx.skip_all) {
+               switch (ctx.result) {
+               case RESULT_SUCCESS:
+                       printf("ok %d", ctx.count);
+                       print_description(format, ap);
+                       break;
+
+               case RESULT_FAILURE:
+                       printf("not ok %d", ctx.count);
+                       print_description(format, ap);
+                       break;
+
+               case RESULT_TODO:
+                       printf("not ok %d", ctx.count);
+                       print_description(format, ap);
+                       printf(" # TODO");
+                       break;
+
+               case RESULT_SKIP:
+                       printf("ok %d", ctx.count);
+                       print_description(format, ap);
+                       printf(" # SKIP");
+                       break;
+
+               case RESULT_NONE:
+                       test_msg("BUG: test has no checks at %s",
+                                make_relative(location));
+                       printf("not ok %d", ctx.count);
+                       print_description(format, ap);
+                       ctx.result = RESULT_FAILURE;
+                       break;
+               }
+       }
+       va_end(ap);
+       ctx.running = 0;
+       if (ctx.skip_all)
+               return 1;
+       putc('\n', stdout);
+       fflush(stdout);
+       ctx.failed |= ctx.result == RESULT_FAILURE;
+
+       return ctx.result != RESULT_FAILURE;
+}
+
+static void test_fail(void)
+{
+       assert(ctx.result != RESULT_SKIP);
+
+       ctx.result = RESULT_FAILURE;
+}
+
+static void test_pass(void)
+{
+       assert(ctx.result != RESULT_SKIP);
+
+       if (ctx.result == RESULT_NONE)
+               ctx.result = RESULT_SUCCESS;
+}
+
+static void test_todo(void)
+{
+       assert(ctx.result != RESULT_SKIP);
+
+       if (ctx.result != RESULT_FAILURE)
+               ctx.result = RESULT_TODO;
+}
+
+int test_assert(const char *location, const char *check, int ok)
+{
+       assert(ctx.running);
+
+       if (ctx.result == RESULT_SKIP) {
+               test_msg("skipping check '%s' at %s", check,
+                        make_relative(location));
+               return 1;
+       }
+       if (!ctx.todo) {
+               if (ok) {
+                       test_pass();
+               } else {
+                       test_msg("check \"%s\" failed at %s", check,
+                                make_relative(location));
+                       test_fail();
+               }
+       }
+
+       return !!ok;
+}
+
+void test__todo_begin(void)
+{
+       assert(ctx.running);
+       assert(!ctx.todo);
+
+       ctx.todo = 1;
+}
+
+int test__todo_end(const char *location, const char *check, int res)
+{
+       assert(ctx.running);
+       assert(ctx.todo);
+
+       ctx.todo = 0;
+       if (ctx.result == RESULT_SKIP)
+               return 1;
+       if (res) {
+               test_msg("todo check '%s' succeeded at %s", check,
+                        make_relative(location));
+               test_fail();
+       } else {
+               test_todo();
+       }
+
+       return !res;
+}
+
+int check_bool_loc(const char *loc, const char *check, int ok)
+{
+       return test_assert(loc, check, ok);
+}
+
+union test__tmp test__tmp[2];
+
+int check_int_loc(const char *loc, const char *check, int ok,
+                 intmax_t a, intmax_t b)
+{
+       int ret = test_assert(loc, check, ok);
+
+       if (!ret) {
+               test_msg("   left: %"PRIdMAX, a);
+               test_msg("  right: %"PRIdMAX, b);
+       }
+
+       return ret;
+}
+
+int check_uint_loc(const char *loc, const char *check, int ok,
+                  uintmax_t a, uintmax_t b)
+{
+       int ret = test_assert(loc, check, ok);
+
+       if (!ret) {
+               test_msg("   left: %"PRIuMAX, a);
+               test_msg("  right: %"PRIuMAX, b);
+       }
+
+       return ret;
+}
+
+static void print_one_char(char ch, char quote)
+{
+       if ((unsigned char)ch < 0x20u || ch == 0x7f) {
+               /* TODO: improve handling of \a, \b, \f ... */
+               printf("\\%03o", (unsigned char)ch);
+       } else {
+               if (ch == '\\' || ch == quote)
+                       putc('\\', stdout);
+               putc(ch, stdout);
+       }
+}
+
+static void print_char(const char *prefix, char ch)
+{
+       printf("# %s: '", prefix);
+       print_one_char(ch, '\'');
+       fputs("'\n", stdout);
+}
+
+int check_char_loc(const char *loc, const char *check, int ok, char a, char b)
+{
+       int ret = test_assert(loc, check, ok);
+
+       if (!ret) {
+               fflush(stderr);
+               print_char("   left", a);
+               print_char("  right", b);
+               fflush(stdout);
+       }
+
+       return ret;
+}
+
+static void print_str(const char *prefix, const char *str)
+{
+       printf("# %s: ", prefix);
+       if (!str) {
+               fputs("NULL\n", stdout);
+       } else {
+               putc('"', stdout);
+               while (*str)
+                       print_one_char(*str++, '"');
+               fputs("\"\n", stdout);
+       }
+}
+
+int check_str_loc(const char *loc, const char *check,
+                 const char *a, const char *b)
+{
+       int ok = (!a && !b) || (a && b && !strcmp(a, b));
+       int ret = test_assert(loc, check, ok);
+
+       if (!ret) {
+               fflush(stderr);
+               print_str("   left", a);
+               print_str("  right", b);
+               fflush(stdout);
+       }
+
+       return ret;
+}
diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h
new file mode 100644 (file)
index 0000000..a8f07ae
--- /dev/null
@@ -0,0 +1,149 @@
+#ifndef TEST_LIB_H
+#define TEST_LIB_H
+
+#include "git-compat-util.h"
+
+/*
+ * Run a test function, returns 1 if the test succeeds, 0 if it
+ * fails. If test_skip_all() has been called then the test will not be
+ * run. The description for each test should be unique. For example:
+ *
+ *  TEST(test_something(arg1, arg2), "something %d %d", arg1, arg2)
+ */
+#define TEST(t, ...)                                   \
+       test__run_end(test__run_begin() ? 0 : (t, 1),   \
+                     TEST_LOCATION(),  __VA_ARGS__)
+
+/*
+ * Print a test plan, should be called before any tests. If the number
+ * of tests is not known in advance test_done() will automatically
+ * print a plan at the end of the test program.
+ */
+void test_plan(int count);
+
+/*
+ * test_done() must be called at the end of main(). It will print the
+ * plan if plan() was not called at the beginning of the test program
+ * and returns the exit code for the test program.
+ */
+int test_done(void);
+
+/* Skip the current test. */
+__attribute__((format (printf, 1, 2)))
+void test_skip(const char *format, ...);
+
+/* Skip all remaining tests. */
+__attribute__((format (printf, 1, 2)))
+void test_skip_all(const char *format, ...);
+
+/* Print a diagnostic message to stdout. */
+__attribute__((format (printf, 1, 2)))
+void test_msg(const char *format, ...);
+
+/*
+ * Test checks are built around test_assert(). checks return 1 on
+ * success, 0 on failure. If any check fails then the test will fail. To
+ * create a custom check define a function that wraps test_assert() and
+ * a macro to wrap that function to provide a source location and
+ * stringified arguments. Custom checks that take pointer arguments
+ * should be careful to check that they are non-NULL before
+ * dereferencing them. For example:
+ *
+ *  static int check_oid_loc(const char *loc, const char *check,
+ *                          struct object_id *a, struct object_id *b)
+ *  {
+ *         int res = test_assert(loc, check, a && b && oideq(a, b));
+ *
+ *         if (!res) {
+ *                 test_msg("   left: %s", a ? oid_to_hex(a) : "NULL";
+ *                 test_msg("  right: %s", b ? oid_to_hex(a) : "NULL";
+ *
+ *         }
+ *         return res;
+ *  }
+ *
+ *  #define check_oid(a, b) \
+ *         check_oid_loc(TEST_LOCATION(), "oideq("#a", "#b")", a, b)
+ */
+int test_assert(const char *location, const char *check, int ok);
+
+/* Helper macro to pass the location to checks */
+#define TEST_LOCATION() TEST__MAKE_LOCATION(__LINE__)
+
+/* Check a boolean condition. */
+#define check(x)                               \
+       check_bool_loc(TEST_LOCATION(), #x, x)
+int check_bool_loc(const char *loc, const char *check, int ok);
+
+/*
+ * Compare two integers. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
+#define check_int(a, op, b)                                            \
+       (test__tmp[0].i = (a), test__tmp[1].i = (b),                    \
+        check_int_loc(TEST_LOCATION(), #a" "#op" "#b,                  \
+                      test__tmp[0].i op test__tmp[1].i,                \
+                      test__tmp[0].i, test__tmp[1].i))
+int check_int_loc(const char *loc, const char *check, int ok,
+                 intmax_t a, intmax_t b);
+
+/*
+ * Compare two unsigned integers. Prints a message with the two values
+ * if the comparison fails. NB this is not thread safe.
+ */
+#define check_uint(a, op, b)                                           \
+       (test__tmp[0].u = (a), test__tmp[1].u = (b),                    \
+        check_uint_loc(TEST_LOCATION(), #a" "#op" "#b,                 \
+                       test__tmp[0].u op test__tmp[1].u,               \
+                       test__tmp[0].u, test__tmp[1].u))
+int check_uint_loc(const char *loc, const char *check, int ok,
+                  uintmax_t a, uintmax_t b);
+
+/*
+ * Compare two chars. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
+#define check_char(a, op, b)                                           \
+       (test__tmp[0].c = (a), test__tmp[1].c = (b),                    \
+        check_char_loc(TEST_LOCATION(), #a" "#op" "#b,                 \
+                       test__tmp[0].c op test__tmp[1].c,               \
+                       test__tmp[0].c, test__tmp[1].c))
+int check_char_loc(const char *loc, const char *check, int ok,
+                  char a, char b);
+
+/* Check whether two strings are equal. */
+#define check_str(a, b)                                                        \
+       check_str_loc(TEST_LOCATION(), "!strcmp("#a", "#b")", a, b)
+int check_str_loc(const char *loc, const char *check,
+                 const char *a, const char *b);
+
+/*
+ * Wrap a check that is known to fail. If the check succeeds then the
+ * test will fail. Returns 1 if the check fails, 0 if it
+ * succeeds. For example:
+ *
+ *  TEST_TODO(check(0));
+ */
+#define TEST_TODO(check) \
+       (test__todo_begin(), test__todo_end(TEST_LOCATION(), #check, check))
+
+/* Private helpers */
+
+#define TEST__STR(x) #x
+#define TEST__MAKE_LOCATION(line) __FILE__ ":" TEST__STR(line)
+
+union test__tmp {
+       intmax_t i;
+       uintmax_t u;
+       char c;
+};
+
+extern union test__tmp test__tmp[2];
+
+int test__run_begin(void);
+__attribute__((format (printf, 3, 4)))
+int test__run_end(int, const char *, const char *, ...);
+void test__todo_begin(void);
+int test__todo_end(const char *, const char *, int);
+
+#endif /* TEST_LIB_H */
index 669ebaf68be006ee53911f02da72e56cb5a100f0..3c8ee19975bb74d755fd40d3ee8d27fbaecc0e58 100755 (executable)
@@ -23,7 +23,7 @@ memcheck)
        VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
        VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
        test 3 -gt "$VALGRIND_MAJOR" ||
-       test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+       { test 3 -eq "$VALGRIND_MAJOR" && test 4 -gt "$VALGRIND_MINOR"; } ||
        TOOL_OPTIONS="$TOOL_OPTIONS --track-origins=yes"
        ;;
 *)
diff --git a/tag.c b/tag.c
index dfbcd7fcc244ac2500d41655afac3136758bb331..fc3834db467dc4e83ca0a24da61a3f39048f3ec2 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -1,11 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
 #include "tag.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "commit.h"
 #include "tree.h"
 #include "blob.h"
 #include "alloc.h"
 #include "gpg-interface.h"
+#include "hex.h"
 #include "packfile.h"
 
 const char *tag_type = "tag";
@@ -51,15 +54,15 @@ int gpg_verify_tag(const struct object_id *oid, const char *name_to_report,
                return error("%s: cannot verify a non-tag object of type %s.",
                                name_to_report ?
                                name_to_report :
-                               find_unique_abbrev(oid, DEFAULT_ABBREV),
+                               repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV),
                                type_name(type));
 
-       buf = read_object_file(oid, &type, &size);
+       buf = repo_read_object_file(the_repository, oid, &type, &size);
        if (!buf)
                return error("%s: unable to read file.",
                                name_to_report ?
                                name_to_report :
-                               find_unique_abbrev(oid, DEFAULT_ABBREV));
+                               repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
 
        ret = run_gpg_verify(buf, size, flags);
 
@@ -216,7 +219,8 @@ int parse_tag(struct tag *item)
 
        if (item->object.parsed)
                return 0;
-       data = read_object_file(&item->object.oid, &type, &size);
+       data = repo_read_object_file(the_repository, &item->object.oid, &type,
+                                    &size);
        if (!data)
                return error("Could not read %s",
                             oid_to_hex(&item->object.oid));
index e27048f970ba3a22823a0a7eab870c424d2e3e1d..ecdebf1afb0d8465717c18409877fe310f980e46 100644 (file)
@@ -42,7 +42,9 @@
  * file created by its parent.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "path.h"
 #include "tempfile.h"
 #include "sigchain.h"
 
index e144712c85c055bcf3248ab342592b440a477062..29ed5ee486a4f07c3f0558101ef8efc46f3d6ab7 100755 (executable)
@@ -28,7 +28,7 @@ if [ "$allownonascii" != "true" ] &&
        # Note that the use of brackets around a tr range is ok here, (it's
        # even required, for portability to Solaris 10's /usr/bin/tr), since
        # the square bracket bytes happen to fall in the designated range.
-       test $(git diff --cached --name-only --diff-filter=A -z $against |
+       test $(git diff-index --cached --name-only --diff-filter=A -z $against |
          LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
 then
        cat <<\EOF
diff --git a/templates/hooks--sendemail-validate.sample b/templates/hooks--sendemail-validate.sample
new file mode 100755 (executable)
index 0000000..640bcf8
--- /dev/null
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# An example hook script to validate a patch (and/or patch series) before
+# sending it via email.
+#
+# The hook should exit with non-zero status after issuing an appropriate
+# message if it wants to prevent the email(s) from being sent.
+#
+# To enable this hook, rename this file to "sendemail-validate".
+#
+# By default, it will only check that the patch(es) can be applied on top of
+# the default upstream branch without conflicts in a secondary worktree. After
+# validation (successful or not) of the last patch of a series, the worktree
+# will be deleted.
+#
+# The following config variables can be set to change the default remote and
+# remote ref that are used to apply the patches against:
+#
+#   sendemail.validateRemote (default: origin)
+#   sendemail.validateRemoteRef (default: HEAD)
+#
+# Replace the TODO placeholders with appropriate checks according to your
+# needs.
+
+validate_cover_letter () {
+       file="$1"
+       # TODO: Replace with appropriate checks (e.g. spell checking).
+       true
+}
+
+validate_patch () {
+       file="$1"
+       # Ensure that the patch applies without conflicts.
+       git am -3 "$file" || return
+       # TODO: Replace with appropriate checks for this patch
+       # (e.g. checkpatch.pl).
+       true
+}
+
+validate_series () {
+       # TODO: Replace with appropriate checks for the whole series
+       # (e.g. quick build, coding style checks, etc.).
+       true
+}
+
+# main -------------------------------------------------------------------------
+
+if test "$GIT_SENDEMAIL_FILE_COUNTER" = 1
+then
+       remote=$(git config --default origin --get sendemail.validateRemote) &&
+       ref=$(git config --default HEAD --get sendemail.validateRemoteRef) &&
+       worktree=$(mktemp --tmpdir -d sendemail-validate.XXXXXXX) &&
+       git worktree add -fd --checkout "$worktree" "refs/remotes/$remote/$ref" &&
+       git config --replace-all sendemail.validateWorktree "$worktree"
+else
+       worktree=$(git config --get sendemail.validateWorktree)
+fi || {
+       echo "sendemail-validate: error: failed to prepare worktree" >&2
+       exit 1
+}
+
+unset GIT_DIR GIT_WORK_TREE
+cd "$worktree" &&
+
+if grep -q "^diff --git " "$1"
+then
+       validate_patch "$1"
+else
+       validate_cover_letter "$1"
+fi &&
+
+if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL"
+then
+       git config --unset-all sendemail.validateWorktree &&
+       trap 'git worktree remove -ff "$worktree"' EXIT &&
+       validate_series
+fi
index 532984569132de11f6f05b561f817817523a3468..1f89ffab4c32bc02b5d955851401628a5b9a540e 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "thread-utils.h"
 
 #if defined(hpux) || defined(__hpux) || defined(_hpux)
index 2a2012eb6d0d4b38c047b570ca06697ffead6dbd..3509258be53644973ca2b2b04cc0ec5b27ea96c6 100644 (file)
@@ -1,13 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tmp-objdir.h"
+#include "abspath.h"
 #include "chdir-notify.h"
 #include "dir.h"
-#include "sigchain.h"
+#include "environment.h"
+#include "object-file.h"
+#include "path.h"
 #include "string-list.h"
 #include "strbuf.h"
 #include "strvec.h"
 #include "quote.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 struct tmp_objdir {
        struct strbuf path;
index 76efc7edee5be4a9197a242f526c74b05f49802a..237d96b66050c82340af3c18b531bd5c877c15ab 100644 (file)
  *
  * Example:
  *
+ *     struct child_process child = CHILD_PROCESS_INIT;
  *     struct tmp_objdir *t = tmp_objdir_create("incoming");
- *     if (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&
- *         !tmp_objdir_migrate(t))
+ *     strvec_push(&child.args, cmd);
+ *     strvec_pushv(&child.env, tmp_objdir_env(t));
+ *     if (!run_command(&child)) && !tmp_objdir_migrate(t))
  *             printf("success!\n");
  *     else
  *             die("failed...tmp_objdir will clean up for us");
diff --git a/trace.c b/trace.c
index 794a087c21e100e147665181a592bc811b3c1265..8669ddfca25e8fe91b57d96875f63214e5700a9e 100644 (file)
--- a/trace.c
+++ b/trace.c
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *  along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
 #include "quote.h"
+#include "setup.h"
+#include "trace.h"
 
 struct trace_key trace_default_key = { "GIT_TRACE", 0, 0, 0 };
 struct trace_key trace_perf_key = TRACE_KEY_INIT(PERFORMANCE);
@@ -291,10 +295,9 @@ static const char *quote_crnl(const char *path)
        return new_path.buf;
 }
 
-/* FIXME: move prefix to startup_info struct and get rid of this arg */
-void trace_repo_setup(const char *prefix)
+void trace_repo_setup(void)
 {
-       const char *git_work_tree;
+       const char *git_work_tree, *prefix = startup_info->prefix;
        char *cwd;
 
        if (!trace_want(&trace_setup_key))
@@ -305,7 +308,7 @@ void trace_repo_setup(const char *prefix)
        if (!(git_work_tree = get_git_work_tree()))
                git_work_tree = "(null)";
 
-       if (!prefix)
+       if (!startup_info->prefix)
                prefix = "(null)";
 
        trace_printf_key(&trace_setup_key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
diff --git a/trace.h b/trace.h
index 4e771f86ac289ada2f5b97eb4013ef313fbb3cbf..d304d55aa1d706dc3f65bbf15c7b2506bc0e9499 100644 (file)
--- a/trace.h
+++ b/trace.h
@@ -1,7 +1,6 @@
 #ifndef TRACE_H
 #define TRACE_H
 
-#include "git-compat-util.h"
 #include "strbuf.h"
 
 /**
@@ -93,7 +92,7 @@ extern struct trace_key trace_default_key;
 extern struct trace_key trace_perf_key;
 extern struct trace_key trace_setup_key;
 
-void trace_repo_setup(const char *prefix);
+void trace_repo_setup(void);
 
 /**
  * Checks whether the trace key is enabled. Used to prevent expensive
index 0c0a11e07d522e031bb4e8e3188953c74d872cc4..f1e268bd159ecb3d491721e67c34eabd397a66ea 100644 (file)
--- a/trace2.c
+++ b/trace2.c
@@ -1,20 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "json-writer.h"
-#include "quote.h"
+#include "repository.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "thread-utils.h"
-#include "version.h"
+#include "trace.h"
+#include "trace2.h"
 #include "trace2/tr2_cfg.h"
 #include "trace2/tr2_cmd_name.h"
+#include "trace2/tr2_ctr.h"
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_sid.h"
 #include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
+#include "trace2/tr2_tmr.h"
 
 static int trace2_enabled;
+static int trace2_redact = 1;
 
 static int tr2_next_child_id; /* modify under lock */
 static int tr2_next_exec_id; /* modify under lock */
@@ -52,7 +55,7 @@ static struct tr2_tgt *tr2_tgt_builtins[] =
  * Force (rather than lazily) initialize any of the requested
  * builtin TRACE2 targets at startup (and before we've seen an
  * actual TRACE2 event call) so we can see if we need to setup
- * the TR2 and TLS machinery.
+ * private data structures and thread-local storage.
  *
  * Return the number of builtin targets enabled.
  */
@@ -83,6 +86,39 @@ static void tr2_tgt_disable_builtins(void)
                tgt_j->pfn_term();
 }
 
+/*
+ * The signature of this function must match the pfn_timer
+ * method in the targets.  (Think of this is an apply operation
+ * across the set of active targets.)
+ */
+static void tr2_tgt_emit_a_timer(const struct tr2_timer_metadata *meta,
+                                const struct tr2_timer *timer,
+                                int is_final_data)
+{
+       struct tr2_tgt *tgt_j;
+       int j;
+
+       for_each_wanted_builtin (j, tgt_j)
+               if (tgt_j->pfn_timer)
+                       tgt_j->pfn_timer(meta, timer, is_final_data);
+}
+
+/*
+ * The signature of this function must match the pfn_counter
+ * method in the targets.
+ */
+static void tr2_tgt_emit_a_counter(const struct tr2_counter_metadata *meta,
+                                  const struct tr2_counter *counter,
+                                  int is_final_data)
+{
+       struct tr2_tgt *tgt_j;
+       int j;
+
+       for_each_wanted_builtin (j, tgt_j)
+               if (tgt_j->pfn_counter)
+                       tgt_j->pfn_counter(meta, counter, is_final_data);
+}
+
 static int tr2main_exit_code;
 
 /*
@@ -110,6 +146,32 @@ static void tr2main_atexit_handler(void)
         */
        tr2tls_pop_unwind_self();
 
+       /*
+        * Some timers want per-thread details.  If the main thread
+        * used one of those timers, emit the details now (before
+        * we emit the aggregate timer values).
+        *
+        * Likewise for counters.
+        */
+       tr2_emit_per_thread_timers(tr2_tgt_emit_a_timer);
+       tr2_emit_per_thread_counters(tr2_tgt_emit_a_counter);
+
+       /*
+        * Add stopwatch timer and counter data for the main thread to
+        * the final totals.  And then emit the final values.
+        *
+        * Technically, we shouldn't need to hold the lock to update
+        * and output the final_timer_block and final_counter_block
+        * (since all other threads should be dead by now), but it
+        * doesn't hurt anything.
+        */
+       tr2tls_lock();
+       tr2_update_final_timers();
+       tr2_update_final_counters();
+       tr2_emit_final_timers(tr2_tgt_emit_a_timer);
+       tr2_emit_final_counters(tr2_tgt_emit_a_counter);
+       tr2tls_unlock();
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_atexit)
                        tgt_j->pfn_atexit(us_elapsed_absolute,
@@ -163,6 +225,8 @@ void trace2_initialize_fl(const char *file, int line)
        if (!tr2_tgt_want_builtins())
                return;
        trace2_enabled = 1;
+       if (!git_env_bool("GIT_TRACE2_REDACT", 1))
+               trace2_redact = 0;
 
        tr2_sid_get();
 
@@ -183,12 +247,93 @@ int trace2_is_enabled(void)
        return trace2_enabled;
 }
 
+/*
+ * Redacts an argument, i.e. ensures that no password in
+ * https://user:password@host/-style URLs is logged.
+ *
+ * Returns the original if nothing needed to be redacted.
+ * Returns a pointer that needs to be `free()`d otherwise.
+ */
+static const char *redact_arg(const char *arg)
+{
+       const char *p, *colon;
+       size_t at;
+
+       if (!trace2_redact ||
+           (!skip_prefix(arg, "https://", &p) &&
+            !skip_prefix(arg, "http://", &p)))
+               return arg;
+
+       at = strcspn(p, "@/");
+       if (p[at] != '@')
+               return arg;
+
+       colon = memchr(p, ':', at);
+       if (!colon)
+               return arg;
+
+       return xstrfmt("%.*s:<REDACTED>%s", (int)(colon - arg), arg, p + at);
+}
+
+/*
+ * Redacts arguments in an argument list.
+ *
+ * Returns the original if nothing needed to be redacted.
+ * Otherwise, returns a new array that needs to be released
+ * via `free_redacted_argv()`.
+ */
+static const char **redact_argv(const char **argv)
+{
+       int i, j;
+       const char *redacted = NULL;
+       const char **ret;
+
+       if (!trace2_redact)
+               return argv;
+
+       for (i = 0; argv[i]; i++)
+               if ((redacted = redact_arg(argv[i])) != argv[i])
+                       break;
+
+       if (!argv[i])
+               return argv;
+
+       for (j = 0; argv[j]; j++)
+               ; /* keep counting */
+
+       ALLOC_ARRAY(ret, j + 1);
+       ret[j] = NULL;
+
+       for (j = 0; j < i; j++)
+               ret[j] = argv[j];
+       ret[i] = redacted;
+       for (++i; argv[i]; i++) {
+               redacted = redact_arg(argv[i]);
+               ret[i] = redacted ? redacted : argv[i];
+       }
+
+       return ret;
+}
+
+static void free_redacted_argv(const char **redacted, const char **argv)
+{
+       int i;
+
+       if (redacted != argv) {
+               for (i = 0; argv[i]; i++)
+                       if (redacted[i] != argv[i])
+                               free((void *)redacted[i]);
+               free((void *)redacted);
+       }
+}
+
 void trace2_cmd_start_fl(const char *file, int line, const char **argv)
 {
        struct tr2_tgt *tgt_j;
        int j;
        uint64_t us_now;
        uint64_t us_elapsed_absolute;
+       const char **redacted;
 
        if (!trace2_enabled)
                return;
@@ -196,10 +341,14 @@ void trace2_cmd_start_fl(const char *file, int line, const char **argv)
        us_now = getnanotime() / 1000;
        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 
+       redacted = redact_argv(argv);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_start_fl)
                        tgt_j->pfn_start_fl(file, line, us_elapsed_absolute,
-                                           argv);
+                                           redacted);
+
+       free_redacted_argv(redacted, argv);
 }
 
 void trace2_cmd_exit_fl(const char *file, int line, int code)
@@ -212,7 +361,6 @@ void trace2_cmd_exit_fl(const char *file, int line, int code)
        if (!trace2_enabled)
                return;
 
-       trace_git_fsync_stats();
        trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT);
 
        tr2main_exit_code = code;
@@ -346,6 +494,7 @@ void trace2_child_start_fl(const char *file, int line,
        int j;
        uint64_t us_now;
        uint64_t us_elapsed_absolute;
+       const char **orig_argv = cmd->args.v;
 
        if (!trace2_enabled)
                return;
@@ -356,10 +505,24 @@ void trace2_child_start_fl(const char *file, int line,
        cmd->trace2_child_id = tr2tls_locked_increment(&tr2_next_child_id);
        cmd->trace2_child_us_start = us_now;
 
+       /*
+        * The `pfn_child_start_fl` API takes a `struct child_process`
+        * rather than a simple `argv` for the child because some
+        * targets make use of the additional context bits/values. So
+        * temporarily replace the original argv (inside the `strvec`)
+        * with a possibly redacted version.
+        */
+       cmd->args.v = redact_argv(orig_argv);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_child_start_fl)
                        tgt_j->pfn_child_start_fl(file, line,
                                                  us_elapsed_absolute, cmd);
+
+       if (cmd->args.v != orig_argv) {
+               free_redacted_argv(cmd->args.v, orig_argv);
+               cmd->args.v = orig_argv;
+       }
 }
 
 void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
@@ -430,6 +593,7 @@ int trace2_exec_fl(const char *file, int line, const char *exe,
        int exec_id;
        uint64_t us_now;
        uint64_t us_elapsed_absolute;
+       const char **redacted;
 
        if (!trace2_enabled)
                return -1;
@@ -439,10 +603,14 @@ int trace2_exec_fl(const char *file, int line, const char *exe,
 
        exec_id = tr2tls_locked_increment(&tr2_next_exec_id);
 
+       redacted = redact_argv(argv);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_exec_fl)
                        tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute,
-                                          exec_id, exe, argv);
+                                          exec_id, exe, redacted);
+
+       free_redacted_argv(redacted, argv);
 
        return exec_id;
 }
@@ -466,7 +634,7 @@ void trace2_exec_result_fl(const char *file, int line, int exec_id, int code)
                                file, line, us_elapsed_absolute, exec_id, code);
 }
 
-void trace2_thread_start_fl(const char *file, int line, const char *thread_name)
+void trace2_thread_start_fl(const char *file, int line, const char *thread_base_name)
 {
        struct tr2_tgt *tgt_j;
        int j;
@@ -488,14 +656,14 @@ void trace2_thread_start_fl(const char *file, int line, const char *thread_name)
                 */
                trace2_region_enter_printf_fl(file, line, NULL, NULL, NULL,
                                              "thread-proc on main: %s",
-                                             thread_name);
+                                             thread_base_name);
                return;
        }
 
        us_now = getnanotime() / 1000;
        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 
-       tr2tls_create_self(thread_name, us_now);
+       tr2tls_create_self(thread_base_name, us_now);
 
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_thread_start_fl)
@@ -541,6 +709,25 @@ void trace2_thread_exit_fl(const char *file, int line)
        tr2tls_pop_unwind_self();
        us_elapsed_thread = tr2tls_region_elasped_self(us_now);
 
+       /*
+        * Some timers want per-thread details.  If this thread used
+        * one of those timers, emit the details now.
+        *
+        * Likewise for counters.
+        */
+       tr2_emit_per_thread_timers(tr2_tgt_emit_a_timer);
+       tr2_emit_per_thread_counters(tr2_tgt_emit_a_counter);
+
+       /*
+        * Add stopwatch timer and counter data from the current
+        * (non-main) thread to the final totals.  (We'll accumulate
+        * data for the main thread later during "atexit".)
+        */
+       tr2tls_lock();
+       tr2_update_final_timers();
+       tr2_update_final_counters();
+       tr2tls_unlock();
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_thread_exit_fl)
                        tgt_j->pfn_thread_exit_fl(file, line,
@@ -551,17 +738,23 @@ void trace2_thread_exit_fl(const char *file, int line)
 }
 
 void trace2_def_param_fl(const char *file, int line, const char *param,
-                        const char *value)
+                        const char *value, const struct key_value_info *kvi)
 {
        struct tr2_tgt *tgt_j;
        int j;
+       const char *redacted;
 
        if (!trace2_enabled)
                return;
 
+       redacted = redact_arg(value);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_param_fl)
-                       tgt_j->pfn_param_fl(file, line, param, value);
+                       tgt_j->pfn_param_fl(file, line, param, redacted, kvi);
+
+       if (redacted != value)
+               free((void *)redacted);
 }
 
 void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
@@ -795,6 +988,39 @@ void trace2_printf_fl(const char *file, int line, const char *fmt, ...)
        va_end(ap);
 }
 
+void trace2_timer_start(enum trace2_timer_id tid)
+{
+       if (!trace2_enabled)
+               return;
+
+       if (tid < 0 || tid >= TRACE2_NUMBER_OF_TIMERS)
+               BUG("trace2_timer_start: invalid timer id: %d", tid);
+
+       tr2_start_timer(tid);
+}
+
+void trace2_timer_stop(enum trace2_timer_id tid)
+{
+       if (!trace2_enabled)
+               return;
+
+       if (tid < 0 || tid >= TRACE2_NUMBER_OF_TIMERS)
+               BUG("trace2_timer_stop: invalid timer id: %d", tid);
+
+       tr2_stop_timer(tid);
+}
+
+void trace2_counter_add(enum trace2_counter_id cid, uint64_t value)
+{
+       if (!trace2_enabled)
+               return;
+
+       if (cid < 0 || cid >= TRACE2_NUMBER_OF_COUNTERS)
+               BUG("trace2_counter_add: invalid counter id: %d", cid);
+
+       tr2_counter_increment(cid, value);
+}
+
 const char *trace2_session_id(void)
 {
        return tr2_sid_get();
index 88d906ea830e6384b8fc2db86a34203525b72ec7..1f0669bbd2d4f0ade2b39960ad89d8e5397cb6b1 100644 (file)
--- a/trace2.h
+++ b/trace2.h
@@ -51,6 +51,8 @@ struct json_writer;
  * [] trace2_region*    -- emit region nesting messages.
  * [] trace2_data*      -- emit region/thread/repo data messages.
  * [] trace2_printf*    -- legacy trace[1] messages.
+ * [] trace2_timer*     -- stopwatch timers (messages are deferred).
+ * [] trace2_counter*   -- global counters (messages are deferred).
  */
 
 /*
@@ -73,8 +75,7 @@ void trace2_initialize_clock(void);
 /*
  * Initialize TRACE2 tracing facility if any of the builtin TRACE2
  * targets are enabled in the system config or the environment.
- * This includes setting up the Trace2 thread local storage (TLS).
- * Emits a 'version' message containing the version of git
+ * This emits a 'version' message containing the version of git
  * and the Trace2 protocol.
  *
  * This function should be called from `main()` as early as possible in
@@ -302,26 +303,29 @@ void trace2_exec_result_fl(const char *file, int line, int exec_id, int code);
 
 /*
  * Emit a 'thread_start' event.  This must be called from inside the
- * thread-proc to set up the trace2 TLS data for the thread.
+ * thread-proc to allow the thread to create its own thread-local
+ * storage.
  *
- * Thread names should be descriptive, like "preload_index".
- * Thread names will be decorated with an instance number automatically.
+ * The thread base name should be descriptive, like "preload_index" or
+ * taken from the thread-proc function.  A unique thread name will be
+ * created from the given base name and the thread id automatically.
  */
 void trace2_thread_start_fl(const char *file, int line,
-                           const char *thread_name);
+                           const char *thread_base_name);
 
-#define trace2_thread_start(thread_name) \
-       trace2_thread_start_fl(__FILE__, __LINE__, (thread_name))
+#define trace2_thread_start(thread_base_name) \
+       trace2_thread_start_fl(__FILE__, __LINE__, (thread_base_name))
 
 /*
  * Emit a 'thread_exit' event.  This must be called from inside the
- * thread-proc to report thread-specific data and cleanup TLS data
- * for the thread.
+ * thread-proc so that the thread can access and clean up its
+ * thread-local storage.
  */
 void trace2_thread_exit_fl(const char *file, int line);
 
 #define trace2_thread_exit() trace2_thread_exit_fl(__FILE__, __LINE__)
 
+struct key_value_info;
 /*
  * Emits a "def_param" message containing a key/value pair.
  *
@@ -331,10 +335,10 @@ void trace2_thread_exit_fl(const char *file, int line);
  * `core.abbrev`, `status.showUntrackedFiles`, or `--no-ahead-behind`.
  */
 void trace2_def_param_fl(const char *file, int line, const char *param,
-                        const char *value);
+                        const char *value, const struct key_value_info *kvi);
 
-#define trace2_def_param(param, value) \
-       trace2_def_param_fl(__FILE__, __LINE__, (param), (value))
+#define trace2_def_param(param, value, kvi) \
+       trace2_def_param_fl(__FILE__, __LINE__, (param), (value), (kvi))
 
 /*
  * Tell trace2 about a newly instantiated repo object and assign
@@ -484,6 +488,90 @@ void trace2_printf_fl(const char *file, int line, const char *fmt, ...);
 
 #define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)
 
+/*
+ * Define the set of stopwatch timers.
+ *
+ * We can add more at any time, but they must be defined at compile
+ * time (to avoid the need to dynamically allocate and synchronize
+ * them between different threads).
+ *
+ * These must start at 0 and be contiguous (because we use them
+ * elsewhere as array indexes).
+ *
+ * Any values added to this enum must also be added to the
+ * `tr2_timer_metadata[]` in `trace2/tr2_tmr.c`.
+ */
+enum trace2_timer_id {
+       /*
+        * Define two timers for testing.  See `t/helper/test-trace2.c`.
+        * These can be used for ad hoc testing, but should not be used
+        * for permanent analysis code.
+        */
+       TRACE2_TIMER_ID_TEST1 = 0, /* emits summary event only */
+       TRACE2_TIMER_ID_TEST2,     /* emits summary and thread events */
+
+       /* Add additional timer definitions before here. */
+       TRACE2_NUMBER_OF_TIMERS
+};
+
+/*
+ * Start/Stop the indicated stopwatch timer in the current thread.
+ *
+ * The time spent by the current thread between the _start and _stop
+ * calls will be added to the thread's partial sum for this timer.
+ *
+ * Timer events are emitted at thread and program exit.
+ *
+ * Note: Since the stopwatch API routines do not generate individual
+ * events, they do not take (file, line) arguments.  Similarly, the
+ * category and timer name values are defined at compile-time in the
+ * timer definitions array, so they are not needed here in the API.
+ */
+void trace2_timer_start(enum trace2_timer_id tid);
+void trace2_timer_stop(enum trace2_timer_id tid);
+
+/*
+ * Define the set of global counters.
+ *
+ * We can add more at any time, but they must be defined at compile
+ * time (to avoid the need to dynamically allocate and synchronize
+ * them between different threads).
+ *
+ * These must start at 0 and be contiguous (because we use them
+ * elsewhere as array indexes).
+ *
+ * Any values added to this enum be also be added to the
+ * `tr2_counter_metadata[]` in `trace2/tr2_ctr.c`.
+ */
+enum trace2_counter_id {
+       /*
+        * Define two counters for testing.  See `t/helper/test-trace2.c`.
+        * These can be used for ad hoc testing, but should not be used
+        * for permanent analysis code.
+        */
+       TRACE2_COUNTER_ID_TEST1 = 0, /* emits summary event only */
+       TRACE2_COUNTER_ID_TEST2,     /* emits summary and thread events */
+
+       TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, /* counts number of jumps */
+
+       /* counts number of fsyncs */
+       TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY,
+       TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH,
+
+       /* Add additional counter definitions before here. */
+       TRACE2_NUMBER_OF_COUNTERS
+};
+
+/*
+ * Increase the named global counter by value.
+ *
+ * Note that this adds `value` to the current thread's partial sum for
+ * this counter (without locking) and that the complete sum is not
+ * available until all threads have exited, so it does not return the
+ * new value of the counter.
+ */
+void trace2_counter_add(enum trace2_counter_id cid, uint64_t value);
+
 /*
  * Optional platform-specific code to dump information about the
  * current and any parent process(es).  This is intended to allow
index ec9ac1a6efd3f62b4ccae9db8a9d3ac3ceb1c64a..d96d908bb9df6fddb22e4dbecfa07882049bdf78 100644 (file)
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "strbuf.h"
+#include "trace2.h"
 #include "trace2/tr2_cfg.h"
 #include "trace2/tr2_sysenv.h"
+#include "wildmatch.h"
 
 static struct strbuf **tr2_cfg_patterns;
 static int tr2_cfg_count_patterns;
@@ -97,7 +100,8 @@ struct tr2_cfg_data {
 /*
  * See if the given config key matches any of our patterns of interest.
  */
-static int tr2_cfg_cb(const char *key, const char *value, void *d)
+static int tr2_cfg_cb(const char *key, const char *value,
+                     const struct config_context *ctx, void *d)
 {
        struct strbuf **s;
        struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
@@ -106,7 +110,8 @@ static int tr2_cfg_cb(const char *key, const char *value, void *d)
                struct strbuf *buf = *s;
                int wm = wildmatch(buf->buf, key, WM_CASEFOLD);
                if (wm == WM_MATCH) {
-                       trace2_def_param_fl(data->file, data->line, key, value);
+                       trace2_def_param_fl(data->file, data->line, key, value,
+                                           ctx->kvi);
                        return 0;
                }
        }
@@ -124,8 +129,10 @@ void tr2_cfg_list_config_fl(const char *file, int line)
 
 void tr2_list_env_vars_fl(const char *file, int line)
 {
+       struct key_value_info kvi = KVI_INIT;
        struct strbuf **s;
 
+       kvi_from_param(&kvi);
        if (tr2_load_env_vars() <= 0)
                return;
 
@@ -133,15 +140,19 @@ void tr2_list_env_vars_fl(const char *file, int line)
                struct strbuf *buf = *s;
                const char *val = getenv(buf->buf);
                if (val && *val)
-                       trace2_def_param_fl(file, line, buf->buf, val);
+                       trace2_def_param_fl(file, line, buf->buf, val, &kvi);
        }
 }
 
 void tr2_cfg_set_fl(const char *file, int line, const char *key,
                    const char *value)
 {
+       struct key_value_info kvi = KVI_INIT;
+       struct config_context ctx = {
+               .kvi = &kvi,
+       };
        struct tr2_cfg_data data = { file, line };
 
        if (tr2_cfg_load_patterns() > 0)
-               tr2_cfg_cb(key, value, &data);
+               tr2_cfg_cb(key, value, &ctx, &data);
 }
index dd313204f517183ccde962e84b6d4be611adb280..b7b5a869b74bcd02c4157d87212359c7dc47e668 100644 (file)
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
 #include "trace2/tr2_cmd_name.h"
 
 #define TR2_ENVVAR_PARENT_NAME "GIT_TRACE2_PARENT_NAME"
diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c
new file mode 100644 (file)
index 0000000..d3a3371
--- /dev/null
@@ -0,0 +1,115 @@
+#include "git-compat-util.h"
+#include "trace2/tr2_tgt.h"
+#include "trace2/tr2_tls.h"
+#include "trace2/tr2_ctr.h"
+
+/*
+ * A global counter block to aggregrate values from the partial sums
+ * from each thread.
+ */
+static struct tr2_counter_block final_counter_block; /* access under tr2tls_mutex */
+
+/*
+ * Define metadata for each global counter.
+ *
+ * This array must match the "enum trace2_counter_id" and the values
+ * in "struct tr2_counter_block.counter[*]".
+ */
+static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTERS] = {
+       [TRACE2_COUNTER_ID_TEST1] = {
+               .category = "test",
+               .name = "test1",
+               .want_per_thread_events = 0,
+       },
+       [TRACE2_COUNTER_ID_TEST2] = {
+               .category = "test",
+               .name = "test2",
+               .want_per_thread_events = 1,
+       },
+       [TRACE2_COUNTER_ID_PACKED_REFS_JUMPS] = {
+               .category = "packed-refs",
+               .name = "jumps_made",
+               .want_per_thread_events = 0,
+       },
+       [TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY] = {
+               .category = "fsync",
+               .name = "writeout-only",
+               .want_per_thread_events = 0,
+       },
+       [TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH] = {
+               .category = "fsync",
+               .name = "hardware-flush",
+               .want_per_thread_events = 0,
+       },
+
+       /* Add additional metadata before here. */
+};
+
+void tr2_counter_increment(enum trace2_counter_id cid, uint64_t value)
+{
+       struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+       struct tr2_counter *c = &ctx->counter_block.counter[cid];
+
+       c->value += value;
+
+       ctx->used_any_counter = 1;
+       if (tr2_counter_metadata[cid].want_per_thread_events)
+               ctx->used_any_per_thread_counter = 1;
+}
+
+void tr2_update_final_counters(void)
+{
+       struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+       enum trace2_counter_id cid;
+
+       if (!ctx->used_any_counter)
+               return;
+
+       /*
+        * Access `final_counter_block` requires holding `tr2tls_mutex`.
+        * We assume that our caller is holding the lock.
+        */
+
+       for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++) {
+               struct tr2_counter *c_final = &final_counter_block.counter[cid];
+               const struct tr2_counter *c = &ctx->counter_block.counter[cid];
+
+               c_final->value += c->value;
+       }
+}
+
+void tr2_emit_per_thread_counters(tr2_tgt_evt_counter_t *fn_apply)
+{
+       struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+       enum trace2_counter_id cid;
+
+       if (!ctx->used_any_per_thread_counter)
+               return;
+
+       /*
+        * For each counter, if the counter wants per-thread events
+        * and this thread used it (the value is non-zero), emit it.
+        */
+       for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++)
+               if (tr2_counter_metadata[cid].want_per_thread_events &&
+                   ctx->counter_block.counter[cid].value)
+                       fn_apply(&tr2_counter_metadata[cid],
+                                &ctx->counter_block.counter[cid],
+                                0);
+}
+
+void tr2_emit_final_counters(tr2_tgt_evt_counter_t *fn_apply)
+{
+       enum trace2_counter_id cid;
+
+       /*
+        * Access `final_counter_block` requires holding `tr2tls_mutex`.
+        * We assume that our caller is holding the lock.
+        */
+
+       for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++)
+               if (final_counter_block.counter[cid].value)
+                       fn_apply(&tr2_counter_metadata[cid],
+                                &final_counter_block.counter[cid],
+                                1);
+}
diff --git a/trace2/tr2_ctr.h b/trace2/tr2_ctr.h
new file mode 100644 (file)
index 0000000..a2267ee
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef TR2_CTR_H
+#define TR2_CTR_H
+
+#include "trace2.h"
+#include "trace2/tr2_tgt.h"
+
+/*
+ * Define a mechanism to allow global "counters".
+ *
+ * Counters can be used count interesting activity that does not fit
+ * the "region and data" model, such as code called from many
+ * different regions and/or where you want to count a number of items,
+ * but don't have control of when the last item will be processed,
+ * such as counter the number of calls to `lstat()`.
+ *
+ * Counters differ from Trace2 "data" events.  Data events are emitted
+ * immediately and are appropriate for documenting loop counters at
+ * the end of a region, for example.  Counter values are accumulated
+ * during the program and final counter values are emitted at program
+ * exit.
+ *
+ * To make this model efficient, we define a compile-time fixed set of
+ * counters and counter ids using a fixed size "counter block" array
+ * in thread-local storage.  This gives us constant time, lock-free
+ * access to each counter within each thread.  This lets us avoid the
+ * complexities of dynamically allocating a counter and sharing that
+ * definition with other threads.
+ *
+ * Each thread uses the counter block in its thread-local storage to
+ * increment partial sums for each counter (without locking).  When a
+ * thread exits, those partial sums are (under lock) added to the
+ * global final sum.
+ *
+ * Partial sums for each counter are optionally emitted when a thread
+ * exits.
+ *
+ * Final sums for each counter are emitted between the "exit" and
+ * "atexit" events.
+ *
+ * A parallel "counter metadata" table contains the "category" and
+ * "name" fields for each counter.  This eliminates the need to
+ * include those args in the various counter APIs.
+ */
+
+/*
+ * The definition of an individual counter as used by an individual
+ * thread (and later in aggregation).
+ */
+struct tr2_counter {
+       uint64_t value;
+};
+
+/*
+ * Metadata for a counter.
+ */
+struct tr2_counter_metadata {
+       const char *category;
+       const char *name;
+
+       /*
+        * True if we should emit per-thread events for this counter
+        * when individual threads exit.
+        */
+       unsigned int want_per_thread_events:1;
+};
+
+/*
+ * A compile-time fixed block of counters to insert into thread-local
+ * storage.  This wrapper is used to avoid quirks of C and the usual
+ * need to pass an array size argument.
+ */
+struct tr2_counter_block {
+       struct tr2_counter counter[TRACE2_NUMBER_OF_COUNTERS];
+};
+
+/*
+ * Private routines used by trace2.c to increment a counter for the
+ * current thread.
+ */
+void tr2_counter_increment(enum trace2_counter_id cid, uint64_t value);
+
+/*
+ * Add the current thread's counter data to the global totals.
+ * This is called during thread-exit.
+ *
+ * Caller must be holding the tr2tls_mutex.
+ */
+void tr2_update_final_counters(void);
+
+/*
+ * Emit per-thread counter data for the current thread.
+ * This is called during thread-exit.
+ */
+void tr2_emit_per_thread_counters(tr2_tgt_evt_counter_t *fn_apply);
+
+/*
+ * Emit global counter values.
+ * This is called during atexit handling.
+ *
+ * Caller must be holding the tr2tls_mutex.
+ */
+void tr2_emit_final_counters(tr2_tgt_evt_counter_t *fn_apply);
+
+#endif /* TR2_CTR_H */
index 8a21dd29725a074a5f9018d7fd8d01750359baee..5be892cd5cdefa654cfd538ea562c2656d23182e 100644 (file)
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "sigchain.h"
+#include "strbuf.h"
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_sid.h"
 #include "trace2/tr2_sysenv.h"
index dc6e75ef13151fe011c738f8e3dbe706e24c4773..09c4ef0d17378748ee67a870301f7b086bcf6163 100644 (file)
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hex.h"
+#include "strbuf.h"
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_sid.h"
 
index a380dcf9105e8d7b0022b34508ca384923b2fcd6..048cdd543830418bfe53957b278ab654ecfdcb66 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "dir.h"
 #include "tr2_sysenv.h"
@@ -57,7 +57,9 @@ static struct tr2_sysenv_entry tr2_sysenv_settings[] = {
 };
 /* clang-format on */
 
-static int tr2_sysenv_cb(const char *key, const char *value, void *d)
+static int tr2_sysenv_cb(const char *key, const char *value,
+                        const struct config_context *ctx UNUSED,
+                        void *d UNUSED)
 {
        int k;
 
@@ -66,6 +68,8 @@ static int tr2_sysenv_cb(const char *key, const char *value, void *d)
 
        for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) {
                if (!strcmp(key, tr2_sysenv_settings[k].git_config_name)) {
+                       if (!value)
+                               return config_error_nonbool(key);
                        free(tr2_sysenv_settings[k].value);
                        tr2_sysenv_settings[k].value = xstrdup(value);
                        return 0;
index 2498482d9ad82a29b2e734b0d726599d594a20c6..c3b3822ed7e4af449adeb0af9b5e512cefdd4779 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tr2_tbuf.h"
 
 void tr2_tbuf_local_time(struct tr2_tbuf *tb)
index 65f94e15748aa2497856a19afd6cc0de7f7736dc..1f626cffea0fc01508beaa65248398faa8c0a8ab 100644 (file)
@@ -4,6 +4,12 @@
 struct child_process;
 struct repository;
 struct json_writer;
+struct tr2_timer_metadata;
+struct tr2_timer;
+struct tr2_counter_metadata;
+struct tr2_counter;
+
+#define NS_TO_SEC(ns) ((double)(ns) / 1.0e9)
 
 /*
  * Function prototypes for a TRACE2 "target" vtable.
@@ -63,8 +69,10 @@ typedef void(tr2_tgt_evt_exec_result_fl_t)(const char *file, int line,
                                           uint64_t us_elapsed_absolute,
                                           int exec_id, int code);
 
+struct key_value_info;
 typedef void(tr2_tgt_evt_param_fl_t)(const char *file, int line,
-                                    const char *param, const char *value);
+                                    const char *param, const char *value,
+                                    const struct key_value_info *kvi);
 
 typedef void(tr2_tgt_evt_repo_fl_t)(const char *file, int line,
                                    const struct repository *repo);
@@ -96,6 +104,14 @@ typedef void(tr2_tgt_evt_printf_va_fl_t)(const char *file, int line,
                                         uint64_t us_elapsed_absolute,
                                         const char *fmt, va_list ap);
 
+typedef void(tr2_tgt_evt_timer_t)(const struct tr2_timer_metadata *meta,
+                                 const struct tr2_timer *timer,
+                                 int is_final_data);
+
+typedef void(tr2_tgt_evt_counter_t)(const struct tr2_counter_metadata *meta,
+                                   const struct tr2_counter *counter,
+                                   int is_final_data);
+
 /*
  * "vtable" for a TRACE2 target.  Use NULL if a target does not want
  * to emit that message.
@@ -132,6 +148,8 @@ struct tr2_tgt {
        tr2_tgt_evt_data_fl_t                   *pfn_data_fl;
        tr2_tgt_evt_data_json_fl_t              *pfn_data_json_fl;
        tr2_tgt_evt_printf_va_fl_t              *pfn_printf_va_fl;
+       tr2_tgt_evt_timer_t                     *pfn_timer;
+       tr2_tgt_evt_counter_t                   *pfn_counter;
 };
 /* clang-format on */
 
index 37a3163be12110ef9e830eba534d19064408c581..59910a1a4f7c0f280fb6839873429d9cc877d3cf 100644 (file)
@@ -1,6 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "json-writer.h"
+#include "repository.h"
 #include "run-command.h"
 #include "version.h"
 #include "trace2/tr2_dst.h"
@@ -9,6 +10,7 @@
 #include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
+#include "trace2/tr2_tmr.h"
 
 static struct tr2_dst tr2dst_event = {
        .sysenv_var = TR2_SYSENV_EVENT,
@@ -90,7 +92,7 @@ static void event_fmt_prepare(const char *event_name, const char *file,
 
        jw_object_string(jw, "event", event_name);
        jw_object_string(jw, "sid", tr2_sid_get());
-       jw_object_string(jw, "thread", ctx->thread_name.buf);
+       jw_object_string(jw, "thread", ctx->thread_name);
 
        /*
         * In brief mode, only emit <time> on these 2 event types.
@@ -333,7 +335,7 @@ static void fn_alias_fl(const char *file, int line, const char *alias,
 }
 
 static void fn_child_start_fl(const char *file, int line,
-                             uint64_t us_elapsed_absolute,
+                             uint64_t us_elapsed_absolute UNUSED,
                              const struct child_process *cmd)
 {
        const char *event_name = "child_start";
@@ -365,7 +367,8 @@ static void fn_child_start_fl(const char *file, int line,
 }
 
 static void fn_child_exit_fl(const char *file, int line,
-                            uint64_t us_elapsed_absolute, int cid, int pid,
+                            uint64_t us_elapsed_absolute UNUSED,
+                            int cid, int pid,
                             int code, uint64_t us_elapsed_child)
 {
        const char *event_name = "child_exit";
@@ -386,7 +389,8 @@ static void fn_child_exit_fl(const char *file, int line,
 }
 
 static void fn_child_ready_fl(const char *file, int line,
-                             uint64_t us_elapsed_absolute, int cid, int pid,
+                             uint64_t us_elapsed_absolute UNUSED,
+                             int cid, int pid,
                              const char *ready, uint64_t us_elapsed_child)
 {
        const char *event_name = "child_ready";
@@ -407,7 +411,7 @@ static void fn_child_ready_fl(const char *file, int line,
 }
 
 static void fn_thread_start_fl(const char *file, int line,
-                              uint64_t us_elapsed_absolute)
+                              uint64_t us_elapsed_absolute UNUSED)
 {
        const char *event_name = "thread_start";
        struct json_writer jw = JSON_WRITER_INIT;
@@ -421,7 +425,7 @@ static void fn_thread_start_fl(const char *file, int line,
 }
 
 static void fn_thread_exit_fl(const char *file, int line,
-                             uint64_t us_elapsed_absolute,
+                             uint64_t us_elapsed_absolute UNUSED,
                              uint64_t us_elapsed_thread)
 {
        const char *event_name = "thread_exit";
@@ -437,7 +441,8 @@ static void fn_thread_exit_fl(const char *file, int line,
        jw_release(&jw);
 }
 
-static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
+static void fn_exec_fl(const char *file, int line,
+                      uint64_t us_elapsed_absolute UNUSED,
                       int exec_id, const char *exe, const char **argv)
 {
        const char *event_name = "exec";
@@ -458,8 +463,8 @@ static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
 }
 
 static void fn_exec_result_fl(const char *file, int line,
-                             uint64_t us_elapsed_absolute, int exec_id,
-                             int code)
+                             uint64_t us_elapsed_absolute UNUSED,
+                             int exec_id, int code)
 {
        const char *event_name = "exec_result";
        struct json_writer jw = JSON_WRITER_INIT;
@@ -475,11 +480,11 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-                       const char *value)
+                       const char *value, const struct key_value_info *kvi)
 {
        const char *event_name = "def_param";
        struct json_writer jw = JSON_WRITER_INIT;
-       enum config_scope scope = current_config_scope();
+       enum config_scope scope = kvi->scope;
        const char *scope_name = config_scope_name(scope);
 
        jw_object_begin(&jw, 0);
@@ -509,7 +514,7 @@ static void fn_repo_fl(const char *file, int line,
 }
 
 static void fn_region_enter_printf_va_fl(const char *file, int line,
-                                        uint64_t us_elapsed_absolute,
+                                        uint64_t us_elapsed_absolute UNUSED,
                                         const char *category,
                                         const char *label,
                                         const struct repository *repo,
@@ -536,7 +541,7 @@ static void fn_region_enter_printf_va_fl(const char *file, int line,
 }
 
 static void fn_region_leave_printf_va_fl(
-       const char *file, int line, uint64_t us_elapsed_absolute,
+       const char *file, int line, uint64_t us_elapsed_absolute UNUSED,
        uint64_t us_elapsed_region, const char *category, const char *label,
        const struct repository *repo, const char *fmt, va_list ap)
 {
@@ -617,6 +622,48 @@ static void fn_data_json_fl(const char *file, int line,
        }
 }
 
+static void fn_timer(const struct tr2_timer_metadata *meta,
+                    const struct tr2_timer *timer,
+                    int is_final_data)
+{
+       const char *event_name = is_final_data ? "timer" : "th_timer";
+       struct json_writer jw = JSON_WRITER_INIT;
+       double t_total = NS_TO_SEC(timer->total_ns);
+       double t_min = NS_TO_SEC(timer->min_ns);
+       double t_max = NS_TO_SEC(timer->max_ns);
+
+       jw_object_begin(&jw, 0);
+       event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw);
+       jw_object_string(&jw, "category", meta->category);
+       jw_object_string(&jw, "name", meta->name);
+       jw_object_intmax(&jw, "intervals", timer->interval_count);
+       jw_object_double(&jw, "t_total", 6, t_total);
+       jw_object_double(&jw, "t_min", 6, t_min);
+       jw_object_double(&jw, "t_max", 6, t_max);
+       jw_end(&jw);
+
+       tr2_dst_write_line(&tr2dst_event, &jw.json);
+       jw_release(&jw);
+}
+
+static void fn_counter(const struct tr2_counter_metadata *meta,
+                      const struct tr2_counter *counter,
+                      int is_final_data)
+{
+       const char *event_name = is_final_data ? "counter" : "th_counter";
+       struct json_writer jw = JSON_WRITER_INIT;
+
+       jw_object_begin(&jw, 0);
+       event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw);
+       jw_object_string(&jw, "category", meta->category);
+       jw_object_string(&jw, "name", meta->name);
+       jw_object_intmax(&jw, "count", counter->value);
+       jw_end(&jw);
+
+       tr2_dst_write_line(&tr2dst_event, &jw.json);
+       jw_release(&jw);
+}
+
 struct tr2_tgt tr2_tgt_event = {
        .pdst = &tr2dst_event,
 
@@ -648,4 +695,6 @@ struct tr2_tgt tr2_tgt_event = {
        .pfn_data_fl = fn_data_fl,
        .pfn_data_json_fl = fn_data_json_fl,
        .pfn_printf_va_fl = NULL,
+       .pfn_timer = fn_timer,
+       .pfn_counter = fn_counter,
 };
index 69f80330778b40e8293b46ed1a49bdb9feedfeff..baef48aa6989cede1d8bf62837388ab3379e9604 100644 (file)
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "repository.h"
 #include "run-command.h"
+#include "strbuf.h"
 #include "quote.h"
 #include "version.h"
 #include "trace2/tr2_dst.h"
@@ -8,6 +10,7 @@
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
+#include "trace2/tr2_tmr.h"
 
 static struct tr2_dst tr2dst_normal = {
        .sysenv_var = TR2_SYSENV_NORMAL,
@@ -84,7 +87,7 @@ static void fn_version_fl(const char *file, int line)
 }
 
 static void fn_start_fl(const char *file, int line,
-                       uint64_t us_elapsed_absolute, const char **argv)
+                       uint64_t us_elapsed_absolute UNUSED, const char **argv)
 {
        struct strbuf buf_payload = STRBUF_INIT;
 
@@ -213,7 +216,7 @@ static void fn_alias_fl(const char *file, int line, const char *alias,
 }
 
 static void fn_child_start_fl(const char *file, int line,
-                             uint64_t us_elapsed_absolute,
+                             uint64_t us_elapsed_absolute UNUSED,
                              const struct child_process *cmd)
 {
        struct strbuf buf_payload = STRBUF_INIT;
@@ -241,7 +244,8 @@ static void fn_child_start_fl(const char *file, int line,
 }
 
 static void fn_child_exit_fl(const char *file, int line,
-                            uint64_t us_elapsed_absolute, int cid, int pid,
+                            uint64_t us_elapsed_absolute UNUSED,
+                            int cid, int pid,
                             int code, uint64_t us_elapsed_child)
 {
        struct strbuf buf_payload = STRBUF_INIT;
@@ -254,7 +258,8 @@ static void fn_child_exit_fl(const char *file, int line,
 }
 
 static void fn_child_ready_fl(const char *file, int line,
-                             uint64_t us_elapsed_absolute, int cid, int pid,
+                             uint64_t us_elapsed_absolute UNUSED,
+                             int cid, int pid,
                              const char *ready, uint64_t us_elapsed_child)
 {
        struct strbuf buf_payload = STRBUF_INIT;
@@ -266,7 +271,8 @@ static void fn_child_ready_fl(const char *file, int line,
        strbuf_release(&buf_payload);
 }
 
-static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
+static void fn_exec_fl(const char *file, int line,
+                      uint64_t us_elapsed_absolute UNUSED,
                       int exec_id, const char *exe, const char **argv)
 {
        struct strbuf buf_payload = STRBUF_INIT;
@@ -282,8 +288,8 @@ static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
 }
 
 static void fn_exec_result_fl(const char *file, int line,
-                             uint64_t us_elapsed_absolute, int exec_id,
-                             int code)
+                             uint64_t us_elapsed_absolute UNUSED,
+                             int exec_id, int code)
 {
        struct strbuf buf_payload = STRBUF_INIT;
 
@@ -295,10 +301,10 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-                       const char *value)
+                       const char *value, const struct key_value_info *kvi)
 {
        struct strbuf buf_payload = STRBUF_INIT;
-       enum config_scope scope = current_config_scope();
+       enum config_scope scope = kvi->scope;
        const char *scope_name = config_scope_name(scope);
 
        strbuf_addf(&buf_payload, "def_param scope:%s %s=%s", scope_name, param,
@@ -319,7 +325,8 @@ static void fn_repo_fl(const char *file, int line,
 }
 
 static void fn_printf_va_fl(const char *file, int line,
-                           uint64_t us_elapsed_absolute, const char *fmt,
+                           uint64_t us_elapsed_absolute UNUSED,
+                           const char *fmt,
                            va_list ap)
 {
        struct strbuf buf_payload = STRBUF_INIT;
@@ -329,6 +336,42 @@ static void fn_printf_va_fl(const char *file, int line,
        strbuf_release(&buf_payload);
 }
 
+static void fn_timer(const struct tr2_timer_metadata *meta,
+                    const struct tr2_timer *timer,
+                    int is_final_data)
+{
+       const char *event_name = is_final_data ? "timer" : "th_timer";
+       struct strbuf buf_payload = STRBUF_INIT;
+       double t_total = NS_TO_SEC(timer->total_ns);
+       double t_min = NS_TO_SEC(timer->min_ns);
+       double t_max = NS_TO_SEC(timer->max_ns);
+
+       strbuf_addf(&buf_payload, ("%s %s/%s"
+                                  " intervals:%"PRIu64
+                                  " total:%8.6f min:%8.6f max:%8.6f"),
+                   event_name, meta->category, meta->name,
+                   timer->interval_count,
+                   t_total, t_min, t_max);
+
+       normal_io_write_fl(__FILE__, __LINE__, &buf_payload);
+       strbuf_release(&buf_payload);
+}
+
+static void fn_counter(const struct tr2_counter_metadata *meta,
+                      const struct tr2_counter *counter,
+                      int is_final_data)
+{
+       const char *event_name = is_final_data ? "counter" : "th_counter";
+       struct strbuf buf_payload = STRBUF_INIT;
+
+       strbuf_addf(&buf_payload, "%s %s/%s value:%"PRIu64,
+                   event_name, meta->category, meta->name,
+                   counter->value);
+
+       normal_io_write_fl(__FILE__, __LINE__, &buf_payload);
+       strbuf_release(&buf_payload);
+}
+
 struct tr2_tgt tr2_tgt_normal = {
        .pdst = &tr2dst_normal,
 
@@ -360,4 +403,6 @@ struct tr2_tgt tr2_tgt_normal = {
        .pfn_data_fl = NULL,
        .pfn_data_json_fl = NULL,
        .pfn_printf_va_fl = fn_printf_va_fl,
+       .pfn_timer = fn_timer,
+       .pfn_counter = fn_counter,
 };
index 8cb792488c8feee60df3c0e38301d4008b14c50c..a6f9a8a193e058fc164a8e9bd8dc823b7a317a85 100644 (file)
@@ -1,5 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "repository.h"
 #include "run-command.h"
 #include "quote.h"
 #include "version.h"
@@ -10,6 +11,7 @@
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
+#include "trace2/tr2_tmr.h"
 
 static struct tr2_dst tr2dst_perf = {
        .sysenv_var = TR2_SYSENV_PERF,
@@ -108,7 +110,7 @@ static void perf_fmt_prepare(const char *event_name,
 
        strbuf_addf(buf, "d%d | ", tr2_sid_depth());
        strbuf_addf(buf, "%-*s | %-*s | ", TR2_MAX_THREAD_NAME,
-                   ctx->thread_name.buf, TR2FMT_PERF_MAX_EVENT_NAME,
+                   ctx->thread_name, TR2FMT_PERF_MAX_EVENT_NAME,
                    event_name);
 
        len = buf->len + TR2FMT_PERF_REPO_WIDTH;
@@ -437,12 +439,12 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-                       const char *value)
+                       const char *value, const struct key_value_info *kvi)
 {
        const char *event_name = "def_param";
        struct strbuf buf_payload = STRBUF_INIT;
        struct strbuf scope_payload = STRBUF_INIT;
-       enum config_scope scope = current_config_scope();
+       enum config_scope scope = kvi->scope;
        const char *scope_name = config_scope_name(scope);
 
        strbuf_addf(&buf_payload, "%s:%s", param, value);
@@ -555,6 +557,44 @@ static void fn_printf_va_fl(const char *file, int line,
        strbuf_release(&buf_payload);
 }
 
+static void fn_timer(const struct tr2_timer_metadata *meta,
+                    const struct tr2_timer *timer,
+                    int is_final_data)
+{
+       const char *event_name = is_final_data ? "timer" : "th_timer";
+       struct strbuf buf_payload = STRBUF_INIT;
+       double t_total = NS_TO_SEC(timer->total_ns);
+       double t_min = NS_TO_SEC(timer->min_ns);
+       double t_max = NS_TO_SEC(timer->max_ns);
+
+       strbuf_addf(&buf_payload, ("name:%s"
+                                  " intervals:%"PRIu64
+                                  " total:%8.6f min:%8.6f max:%8.6f"),
+                   meta->name,
+                   timer->interval_count,
+                   t_total, t_min, t_max);
+
+       perf_io_write_fl(__FILE__, __LINE__, event_name, NULL, NULL, NULL,
+                        meta->category, &buf_payload);
+       strbuf_release(&buf_payload);
+}
+
+static void fn_counter(const struct tr2_counter_metadata *meta,
+                      const struct tr2_counter *counter,
+                      int is_final_data)
+{
+       const char *event_name = is_final_data ? "counter" : "th_counter";
+       struct strbuf buf_payload = STRBUF_INIT;
+
+       strbuf_addf(&buf_payload, "name:%s value:%"PRIu64,
+                   meta->name,
+                   counter->value);
+
+       perf_io_write_fl(__FILE__, __LINE__, event_name, NULL, NULL, NULL,
+                        meta->category, &buf_payload);
+       strbuf_release(&buf_payload);
+}
+
 struct tr2_tgt tr2_tgt_perf = {
        .pdst = &tr2dst_perf,
 
@@ -586,4 +626,6 @@ struct tr2_tgt tr2_tgt_perf = {
        .pfn_data_fl = fn_data_fl,
        .pfn_data_json_fl = fn_data_json_fl,
        .pfn_printf_va_fl = fn_printf_va_fl,
+       .pfn_timer = fn_timer,
+       .pfn_counter = fn_counter,
 };
index 7da94aba522f5435138f5aff51d530a083a867f3..4f75392952b6a429682d8989fe7146487ce0cb6a 100644 (file)
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
 #include "thread-utils.h"
+#include "trace.h"
 #include "trace2/tr2_tls.h"
 
 /*
@@ -31,10 +33,11 @@ void tr2tls_start_process_clock(void)
        tr2tls_us_start_process = getnanotime() / 1000;
 }
 
-struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name,
+struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_base_name,
                                             uint64_t us_thread_start)
 {
        struct tr2tls_thread_ctx *ctx = xcalloc(1, sizeof(*ctx));
+       struct strbuf buf = STRBUF_INIT;
 
        /*
         * Implicitly "tr2tls_push_self()" to capture the thread's start
@@ -47,12 +50,13 @@ struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name,
 
        ctx->thread_id = tr2tls_locked_increment(&tr2_next_thread_id);
 
-       strbuf_init(&ctx->thread_name, 0);
+       strbuf_init(&buf, 0);
        if (ctx->thread_id)
-               strbuf_addf(&ctx->thread_name, "th%02d:", ctx->thread_id);
-       strbuf_addstr(&ctx->thread_name, thread_name);
-       if (ctx->thread_name.len > TR2_MAX_THREAD_NAME)
-               strbuf_setlen(&ctx->thread_name, TR2_MAX_THREAD_NAME);
+               strbuf_addf(&buf, "th%02d:", ctx->thread_id);
+       strbuf_addstr(&buf, thread_base_name);
+       if (buf.len > TR2_MAX_THREAD_NAME)
+               strbuf_setlen(&buf, TR2_MAX_THREAD_NAME);
+       ctx->thread_name = strbuf_detach(&buf, NULL);
 
        pthread_setspecific(tr2tls_key, ctx);
 
@@ -69,9 +73,9 @@ struct tr2tls_thread_ctx *tr2tls_get_self(void)
        ctx = pthread_getspecific(tr2tls_key);
 
        /*
-        * If the thread-proc did not call trace2_thread_start(), we won't
-        * have any TLS data associated with the current thread.  Fix it
-        * here and silently continue.
+        * If the current thread's thread-proc did not call
+        * trace2_thread_start(), then the thread will not have any
+        * thread-local storage.  Create it now and silently continue.
         */
        if (!ctx)
                ctx = tr2tls_create_self("unknown", getnanotime() / 1000);
@@ -95,7 +99,7 @@ void tr2tls_unset_self(void)
 
        pthread_setspecific(tr2tls_key, NULL);
 
-       strbuf_release(&ctx->thread_name);
+       free((char *)ctx->thread_name);
        free(ctx->array_us_start);
        free(ctx);
 }
@@ -113,7 +117,7 @@ void tr2tls_pop_self(void)
        struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
 
        if (!ctx->nr_open_regions)
-               BUG("no open regions in thread '%s'", ctx->thread_name.buf);
+               BUG("no open regions in thread '%s'", ctx->thread_name);
 
        ctx->nr_open_regions--;
 }
@@ -179,3 +183,13 @@ int tr2tls_locked_increment(int *p)
 
        return current_value;
 }
+
+void tr2tls_lock(void)
+{
+       pthread_mutex_lock(&tr2tls_mutex);
+}
+
+void tr2tls_unlock(void)
+{
+       pthread_mutex_unlock(&tr2tls_mutex);
+}
index b1e327a928e2ba084714770c85c15a219dab35f3..3dfe6557fc46129edfa7a121522e9a6d54973f28 100644 (file)
@@ -1,7 +1,14 @@
 #ifndef TR2_TLS_H
 #define TR2_TLS_H
 
-#include "strbuf.h"
+#include "trace2/tr2_ctr.h"
+#include "trace2/tr2_tmr.h"
+
+/*
+ * Notice: the term "TLS" refers to "thread-local storage" in the
+ * Trace2 source files.  This usage is borrowed from GCC and Windows.
+ * There is NO relation to "transport layer security".
+ */
 
 /*
  * Arbitry limit for thread names for column alignment.
 #define TR2_MAX_THREAD_NAME (24)
 
 struct tr2tls_thread_ctx {
-       struct strbuf thread_name;
+       const char *thread_name;
        uint64_t *array_us_start;
-       int alloc;
-       int nr_open_regions; /* plays role of "nr" in ALLOC_GROW */
+       size_t alloc;
+       size_t nr_open_regions; /* plays role of "nr" in ALLOC_GROW */
        int thread_id;
+       struct tr2_timer_block timer_block;
+       struct tr2_counter_block counter_block;
+       unsigned int used_any_timer:1;
+       unsigned int used_any_per_thread_timer:1;
+       unsigned int used_any_counter:1;
+       unsigned int used_any_per_thread_counter:1;
 };
 
 /*
- * Create TLS data for the current thread.  This gives us a place to
- * put per-thread data, such as thread start time, function nesting
- * and a per-thread label for our messages.
- *
- * We assume the first thread is "main".  Other threads are given
- * non-zero thread-ids to help distinguish messages from concurrent
- * threads.
+ * Create thread-local storage for the current thread.
  *
- * Truncate the thread name if necessary to help with column alignment
- * in printf-style messages.
+ * The first thread in the process will have:
+ *     { .thread_id=0, .thread_name="main" }
+ * Subsequent threads are given a non-zero thread_id and a thread_name
+ * constructed from the id and a thread base name (which is usually just
+ * the name of the thread-proc function).  For example:
+ *     { .thread_id=10, .thread_name="th10:fsm-listen" }
+ * This helps to identify and distinguish messages from concurrent threads.
+ * The ctx.thread_name field is truncated if necessary to help with column
+ * alignment in printf-style messages.
  *
  * In this and all following functions the term "self" refers to the
  * current thread.
  */
-struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name,
+struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_base_name,
                                             uint64_t us_thread_start);
 
 /*
- * Get our TLS data.
+ * Get the thread-local storage pointer of the current thread.
  */
 struct tr2tls_thread_ctx *tr2tls_get_self(void);
 
@@ -45,7 +59,7 @@ struct tr2tls_thread_ctx *tr2tls_get_self(void);
 int tr2tls_is_main_thread(void);
 
 /*
- * Free our TLS data.
+ * Free the current thread's thread-local storage.
  */
 void tr2tls_unset_self(void);
 
@@ -81,12 +95,12 @@ uint64_t tr2tls_region_elasped_self(uint64_t us);
 uint64_t tr2tls_absolute_elapsed(uint64_t us);
 
 /*
- * Initialize the tr2 TLS system.
+ * Initialize thread-local storage for Trace2.
  */
 void tr2tls_init(void);
 
 /*
- * Free all tr2 TLS resources.
+ * Free all Trace2 thread-local storage resources.
  */
 void tr2tls_release(void);
 
@@ -100,4 +114,10 @@ int tr2tls_locked_increment(int *p);
  */
 void tr2tls_start_process_clock(void);
 
+/*
+ * Explicitly lock/unlock our mutex.
+ */
+void tr2tls_lock(void);
+void tr2tls_unlock(void);
+
 #endif /* TR2_TLS_H */
diff --git a/trace2/tr2_tmr.c b/trace2/tr2_tmr.c
new file mode 100644 (file)
index 0000000..51f564b
--- /dev/null
@@ -0,0 +1,182 @@
+#include "git-compat-util.h"
+#include "trace2/tr2_tgt.h"
+#include "trace2/tr2_tls.h"
+#include "trace2/tr2_tmr.h"
+#include "trace.h"
+
+#define MY_MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MY_MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/*
+ * A global timer block to aggregate values from the partial sums from
+ * each thread.
+ */
+static struct tr2_timer_block final_timer_block; /* access under tr2tls_mutex */
+
+/*
+ * Define metadata for each stopwatch timer.
+ *
+ * This array must match "enum trace2_timer_id" and the values
+ * in "struct tr2_timer_block.timer[*]".
+ */
+static struct tr2_timer_metadata tr2_timer_metadata[TRACE2_NUMBER_OF_TIMERS] = {
+       [TRACE2_TIMER_ID_TEST1] = {
+               .category = "test",
+               .name = "test1",
+               .want_per_thread_events = 0,
+       },
+       [TRACE2_TIMER_ID_TEST2] = {
+               .category = "test",
+               .name = "test2",
+               .want_per_thread_events = 1,
+       },
+
+       /* Add additional metadata before here. */
+};
+
+void tr2_start_timer(enum trace2_timer_id tid)
+{
+       struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+       struct tr2_timer *t = &ctx->timer_block.timer[tid];
+
+       t->recursion_count++;
+       if (t->recursion_count > 1)
+               return; /* ignore recursive starts */
+
+       t->start_ns = getnanotime();
+}
+
+void tr2_stop_timer(enum trace2_timer_id tid)
+{
+       struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+       struct tr2_timer *t = &ctx->timer_block.timer[tid];
+       uint64_t ns_now;
+       uint64_t ns_interval;
+
+       assert(t->recursion_count > 0);
+
+       t->recursion_count--;
+       if (t->recursion_count)
+               return; /* still in recursive call(s) */
+
+       ns_now = getnanotime();
+       ns_interval = ns_now - t->start_ns;
+
+       t->total_ns += ns_interval;
+
+       /*
+        * min_ns was initialized to zero (in the xcalloc()) rather
+        * than UINT_MAX when the block of timers was allocated,
+        * so we should always set both the min_ns and max_ns values
+        * the first time that the timer is used.
+        */
+       if (!t->interval_count) {
+               t->min_ns = ns_interval;
+               t->max_ns = ns_interval;
+       } else {
+               t->min_ns = MY_MIN(ns_interval, t->min_ns);
+               t->max_ns = MY_MAX(ns_interval, t->max_ns);
+       }
+
+       t->interval_count++;
+
+       ctx->used_any_timer = 1;
+       if (tr2_timer_metadata[tid].want_per_thread_events)
+               ctx->used_any_per_thread_timer = 1;
+}
+
+void tr2_update_final_timers(void)
+{
+       struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+       enum trace2_timer_id tid;
+
+       if (!ctx->used_any_timer)
+               return;
+
+       /*
+        * Accessing `final_timer_block` requires holding `tr2tls_mutex`.
+        * We assume that our caller is holding the lock.
+        */
+
+       for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++) {
+               struct tr2_timer *t_final = &final_timer_block.timer[tid];
+               struct tr2_timer *t = &ctx->timer_block.timer[tid];
+
+               if (t->recursion_count) {
+                       /*
+                        * The current thread is exiting with
+                        * timer[tid] still running.
+                        *
+                        * Technically, this is a bug, but I'm going
+                        * to ignore it.
+                        *
+                        * I don't think it is worth calling die()
+                        * for.  I don't think it is worth killing the
+                        * process for this bookkeeping error.  We
+                        * might want to call warning(), but I'm going
+                        * to wait on that.
+                        *
+                        * The downside here is that total_ns won't
+                        * include the current open interval (now -
+                        * start_ns).  I can live with that.
+                        */
+               }
+
+               if (!t->interval_count)
+                       continue; /* this timer was not used by this thread */
+
+               t_final->total_ns += t->total_ns;
+
+               /*
+                * final_timer_block.timer[tid].min_ns was initialized to
+                * was initialized to zero rather than UINT_MAX, so we should
+                * always set both the min_ns and max_ns values the first time
+                * that we add a partial sum into it.
+                */
+               if (!t_final->interval_count) {
+                       t_final->min_ns = t->min_ns;
+                       t_final->max_ns = t->max_ns;
+               } else {
+                       t_final->min_ns = MY_MIN(t_final->min_ns, t->min_ns);
+                       t_final->max_ns = MY_MAX(t_final->max_ns, t->max_ns);
+               }
+
+               t_final->interval_count += t->interval_count;
+       }
+}
+
+void tr2_emit_per_thread_timers(tr2_tgt_evt_timer_t *fn_apply)
+{
+       struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+       enum trace2_timer_id tid;
+
+       if (!ctx->used_any_per_thread_timer)
+               return;
+
+       /*
+        * For each timer, if the timer wants per-thread events and
+        * this thread used it, emit it.
+        */
+       for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++)
+               if (tr2_timer_metadata[tid].want_per_thread_events &&
+                   ctx->timer_block.timer[tid].interval_count)
+                       fn_apply(&tr2_timer_metadata[tid],
+                                &ctx->timer_block.timer[tid],
+                                0);
+}
+
+void tr2_emit_final_timers(tr2_tgt_evt_timer_t *fn_apply)
+{
+       enum trace2_timer_id tid;
+
+       /*
+        * Accessing `final_timer_block` requires holding `tr2tls_mutex`.
+        * We assume that our caller is holding the lock.
+        */
+
+       for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++)
+               if (final_timer_block.timer[tid].interval_count)
+                       fn_apply(&tr2_timer_metadata[tid],
+                                &final_timer_block.timer[tid],
+                                1);
+}
diff --git a/trace2/tr2_tmr.h b/trace2/tr2_tmr.h
new file mode 100644 (file)
index 0000000..d575357
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef TR2_TMR_H
+#define TR2_TMR_H
+
+#include "trace2.h"
+#include "trace2/tr2_tgt.h"
+
+/*
+ * Define a mechanism to allow "stopwatch" timers.
+ *
+ * Timers can be used to measure "interesting" activity that does not
+ * fit the "region" model, such as code called from many different
+ * regions (like zlib) and/or where data for individual calls are not
+ * interesting or are too numerous to be efficiently logged.
+ *
+ * Timer values are accumulated during program execution and emitted
+ * to the Trace2 logs at program exit.
+ *
+ * To make this model efficient, we define a compile-time fixed set of
+ * timers and timer ids using a "timer block" array in thread-local
+ * storage.  This gives us constant time access to each timer within
+ * each thread, since we want start/stop operations to be as fast as
+ * possible.  This lets us avoid the complexities of dynamically
+ * allocating a timer on the first use by a thread and/or possibly
+ * sharing that timer definition with other concurrent threads.
+ * However, this does require that we define time the set of timers at
+ * compile time.
+ *
+ * Each thread uses the timer block in its thread-local storage to
+ * compute partial sums for each timer (without locking).  When a
+ * thread exits, those partial sums are (under lock) added to the
+ * global final sum.
+ *
+ * Using this "timer block" model costs ~48 bytes per timer per thread
+ * (we have about six uint64 fields per timer).  This does increase
+ * the size of the thread-local storage block, but it is allocated (at
+ * thread create time) and not on the thread stack, so I'm not worried
+ * about the size.
+ *
+ * Partial sums for each timer are optionally emitted when a thread
+ * exits.
+ *
+ * Final sums for each timer are emitted between the "exit" and
+ * "atexit" events.
+ *
+ * A parallel "timer metadata" table contains the "category" and "name"
+ * fields for each timer.  This eliminates the need to include those
+ * args in the various timer APIs.
+ */
+
+/*
+ * The definition of an individual timer and used by an individual
+ * thread.
+ */
+struct tr2_timer {
+       /*
+        * Total elapsed time for this timer in this thread in nanoseconds.
+        */
+       uint64_t total_ns;
+
+       /*
+        * The maximum and minimum interval values observed for this
+        * timer in this thread.
+        */
+       uint64_t min_ns;
+       uint64_t max_ns;
+
+       /*
+        * The value of the clock when this timer was started in this
+        * thread.  (Undefined when the timer is not active in this
+        * thread.)
+        */
+       uint64_t start_ns;
+
+       /*
+        * Number of times that this timer has been started and stopped
+        * in this thread.  (Recursive starts are ignored.)
+        */
+       uint64_t interval_count;
+
+       /*
+        * Number of nested starts on the stack in this thread.  (We
+        * ignore recursive starts and use this to track the recursive
+        * calls.)
+        */
+       unsigned int recursion_count;
+};
+
+/*
+ * Metadata for a timer.
+ */
+struct tr2_timer_metadata {
+       const char *category;
+       const char *name;
+
+       /*
+        * True if we should emit per-thread events for this timer
+        * when individual threads exit.
+        */
+       unsigned int want_per_thread_events:1;
+};
+
+/*
+ * A compile-time fixed-size block of timers to insert into
+ * thread-local storage.  This wrapper is used to avoid quirks
+ * of C and the usual need to pass an array size argument.
+ */
+struct tr2_timer_block {
+       struct tr2_timer timer[TRACE2_NUMBER_OF_TIMERS];
+};
+
+/*
+ * Private routines used by trace2.c to actually start/stop an
+ * individual timer in the current thread.
+ */
+void tr2_start_timer(enum trace2_timer_id tid);
+void tr2_stop_timer(enum trace2_timer_id tid);
+
+/*
+ * Add the current thread's timer data to the global totals.
+ * This is called during thread-exit.
+ *
+ * Caller must be holding the tr2tls_mutex.
+ */
+void tr2_update_final_timers(void);
+
+/*
+ * Emit per-thread timer data for the current thread.
+ * This is called during thread-exit.
+ */
+void tr2_emit_per_thread_timers(tr2_tgt_evt_timer_t *fn_apply);
+
+/*
+ * Emit global total timer values.
+ * This is called during atexit handling.
+ *
+ * Caller must be holding the tr2tls_mutex.
+ */
+void tr2_emit_final_timers(tr2_tgt_evt_timer_t *fn_apply);
+
+#endif /* TR2_TMR_H */
index 0fd5b142a377056d1e45f8dfd1ff6ae7a9c40097..3a0710a4583a4a11fa5a87cda88ef6a3d0610870 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
 #include "string-list.h"
 #include "run-command.h"
 #include "commit.h"
@@ -479,6 +481,7 @@ static struct {
 };
 
 static int git_trailer_default_config(const char *conf_key, const char *value,
+                                     const struct config_context *ctx UNUSED,
                                      void *cb UNUSED)
 {
        const char *trailer_item, *variable_name;
@@ -504,6 +507,8 @@ static int git_trailer_default_config(const char *conf_key, const char *value,
                                warning(_("unknown value '%s' for key '%s'"),
                                        value, conf_key);
                } else if (!strcmp(trailer_item, "separators")) {
+                       if (!value)
+                               return config_error_nonbool(conf_key);
                        separators = xstrdup(value);
                }
        }
@@ -511,6 +516,7 @@ static int git_trailer_default_config(const char *conf_key, const char *value,
 }
 
 static int git_trailer_config(const char *conf_key, const char *value,
+                             const struct config_context *ctx UNUSED,
                              void *cb UNUSED)
 {
        const char *trailer_item, *variable_name;
@@ -547,16 +553,22 @@ static int git_trailer_config(const char *conf_key, const char *value,
        case TRAILER_KEY:
                if (conf->key)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->key = xstrdup(value);
                break;
        case TRAILER_COMMAND:
                if (conf->command)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->command = xstrdup(value);
                break;
        case TRAILER_CMD:
                if (conf->cmd)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->cmd = xstrdup(value);
                break;
        case TRAILER_WHERE:
@@ -707,30 +719,35 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
        list_add_tail(&new_item->list, arg_head);
 }
 
-static void process_command_line_args(struct list_head *arg_head,
-                                     struct list_head *new_trailer_head)
+static void parse_trailers_from_config(struct list_head *config_head)
 {
        struct arg_item *item;
-       struct strbuf tok = STRBUF_INIT;
-       struct strbuf val = STRBUF_INIT;
-       const struct conf_info *conf;
        struct list_head *pos;
 
-       /*
-        * In command-line arguments, '=' is accepted (in addition to the
-        * separators that are defined).
-        */
-       char *cl_separators = xstrfmt("=%s", separators);
-
        /* Add an arg item for each configured trailer with a command */
        list_for_each(pos, &conf_head) {
                item = list_entry(pos, struct arg_item, list);
                if (item->conf.command)
-                       add_arg_item(arg_head,
+                       add_arg_item(config_head,
                                     xstrdup(token_from_item(item, NULL)),
                                     xstrdup(""),
                                     &item->conf, NULL);
        }
+}
+
+static void parse_trailers_from_command_line_args(struct list_head *arg_head,
+                                                 struct list_head *new_trailer_head)
+{
+       struct strbuf tok = STRBUF_INIT;
+       struct strbuf val = STRBUF_INIT;
+       const struct conf_info *conf;
+       struct list_head *pos;
+
+       /*
+        * In command-line arguments, '=' is accepted (in addition to the
+        * separators that are defined).
+        */
+       char *cl_separators = xstrfmt("=%s", separators);
 
        /* Add an arg item for each trailer on the command line */
        list_for_each(pos, new_trailer_head) {
@@ -800,28 +817,56 @@ static ssize_t last_line(const char *buf, size_t len)
 }
 
 /*
- * Return the position of the start of the patch or the length of str if there
- * is no patch in the message.
+ * Find the end of the log message as an offset from the start of the input
+ * (where callers of this function are interested in looking for a trailers
+ * block in the same input). We have to consider two categories of content that
+ * can come at the end of the input which we want to ignore (because they don't
+ * belong in the log message):
+ *
+ * (1) the "patch part" which begins with a "---" divider and has patch
+ * information (like the output of git-format-patch), and
+ *
+ * (2) any trailing comment lines, blank lines like in the output of "git
+ * commit -v", or stuff below the "cut" (scissor) line.
+ *
+ * As a formula, the situation looks like this:
+ *
+ *     INPUT = LOG MESSAGE + IGNORED
+ *
+ * where IGNORED can be either of the two categories described above. It may be
+ * that there is nothing to ignore. Now it may be the case that the LOG MESSAGE
+ * contains a trailer block, but that's not the concern of this function.
  */
-static size_t find_patch_start(const char *str)
+static size_t find_end_of_log_message(const char *input, int no_divider)
 {
+       size_t end;
        const char *s;
 
-       for (s = str; *s; s = next_line(s)) {
+       /* Assume the naive end of the input is already what we want. */
+       end = strlen(input);
+
+       if (no_divider)
+               return end;
+
+       /* Optionally skip over any patch part ("---" line and below). */
+       for (s = input; *s; s = next_line(s)) {
                const char *v;
 
-               if (skip_prefix(s, "---", &v) && isspace(*v))
-                       return s - str;
+               if (skip_prefix(s, "---", &v) && isspace(*v)) {
+                       end = s - input;
+                       break;
+               }
        }
 
-       return s - str;
+       /* Skip over other ignorable bits. */
+       return end - ignored_log_message_bytes(input, end);
 }
 
 /*
  * Return the position of the first trailer line or len if there are no
  * trailers.
  */
-static size_t find_trailer_start(const char *buf, size_t len)
+static size_t find_trailer_block_start(const char *buf, size_t len)
 {
        const char *s;
        ssize_t end_of_title, l;
@@ -916,12 +961,6 @@ continue_outer_loop:
        return len;
 }
 
-/* Return the position of the end of the trailers. */
-static size_t find_trailer_end(const char *buf, size_t len)
-{
-       return len - ignore_non_trailer(buf, len);
-}
-
 static int ends_with_blank_line(const char *buf, size_t len)
 {
        ssize_t ll = last_line(buf, len);
@@ -957,28 +996,24 @@ static void unfold_value(struct strbuf *val)
        strbuf_release(&out);
 }
 
-static size_t process_input_file(FILE *outfile,
-                                const char *str,
-                                struct list_head *head,
-                                const struct process_trailer_options *opts)
+/*
+ * Parse trailers in "str", populating the trailer info and "head"
+ * linked list structure.
+ */
+static void parse_trailers(struct trailer_info *info,
+                            const char *str,
+                            struct list_head *head,
+                            const struct process_trailer_options *opts)
 {
-       struct trailer_info info;
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
        size_t i;
 
-       trailer_info_get(&info, str, opts);
-
-       /* Print lines before the trailers as is */
-       if (!opts->only_trailers)
-               fwrite(str, 1, info.trailer_start - str, outfile);
+       trailer_info_get(info, str, opts);
 
-       if (!opts->only_trailers && !info.blank_line_before_trailer)
-               fprintf(outfile, "\n");
-
-       for (i = 0; i < info.trailer_nr; i++) {
+       for (i = 0; i < info->trailer_nr; i++) {
                int separator_pos;
-               char *trailer = info.trailers[i];
+               char *trailer = info->trailers[i];
                if (trailer[0] == comment_line_char)
                        continue;
                separator_pos = find_separator(trailer, separators);
@@ -998,10 +1033,6 @@ static size_t process_input_file(FILE *outfile,
                                         strbuf_detach(&val, NULL));
                }
        }
-
-       trailer_info_release(&info);
-
-       return info.trailer_end - str;
 }
 
 static void free_all(struct list_head *head)
@@ -1050,7 +1081,7 @@ void process_trailers(const char *file,
 {
        LIST_HEAD(head);
        struct strbuf sb = STRBUF_INIT;
-       size_t trailer_end;
+       struct trailer_info info;
        FILE *outfile = stdout;
 
        ensure_configured();
@@ -1060,22 +1091,33 @@ void process_trailers(const char *file,
        if (opts->in_place)
                outfile = create_in_place_tempfile(file);
 
+       parse_trailers(&info, sb.buf, &head, opts);
+
        /* Print the lines before the trailers */
-       trailer_end = process_input_file(outfile, sb.buf, &head, opts);
+       if (!opts->only_trailers)
+               fwrite(sb.buf, 1, info.trailer_block_start, outfile);
+
+       if (!opts->only_trailers && !info.blank_line_before_trailer)
+               fprintf(outfile, "\n");
+
 
        if (!opts->only_input) {
+               LIST_HEAD(config_head);
                LIST_HEAD(arg_head);
-               process_command_line_args(&arg_head, new_trailer_head);
+               parse_trailers_from_config(&config_head);
+               parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
+               list_splice(&config_head, &arg_head);
                process_trailers_lists(&head, &arg_head);
        }
 
        print_all(outfile, &head, opts);
 
        free_all(&head);
+       trailer_info_release(&info);
 
        /* Print the lines after the trailers as is */
        if (!opts->only_trailers)
-               fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
+               fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
 
        if (opts->in_place)
                if (rename_tempfile(&trailers_tempfile, file))
@@ -1087,7 +1129,7 @@ void process_trailers(const char *file,
 void trailer_info_get(struct trailer_info *info, const char *str,
                      const struct process_trailer_options *opts)
 {
-       int patch_start, trailer_end, trailer_start;
+       size_t end_of_log_message = 0, trailer_block_start = 0;
        struct strbuf **trailer_lines, **ptr;
        char **trailer_strings = NULL;
        size_t nr = 0, alloc = 0;
@@ -1095,16 +1137,11 @@ void trailer_info_get(struct trailer_info *info, const char *str,
 
        ensure_configured();
 
-       if (opts->no_divider)
-               patch_start = strlen(str);
-       else
-               patch_start = find_patch_start(str);
-
-       trailer_end = find_trailer_end(str, patch_start);
-       trailer_start = find_trailer_start(str, trailer_end);
+       end_of_log_message = find_end_of_log_message(str, opts->no_divider);
+       trailer_block_start = find_trailer_block_start(str, end_of_log_message);
 
-       trailer_lines = strbuf_split_buf(str + trailer_start,
-                                        trailer_end - trailer_start,
+       trailer_lines = strbuf_split_buf(str + trailer_block_start,
+                                        end_of_log_message - trailer_block_start,
                                         '\n',
                                         0);
        for (ptr = trailer_lines; *ptr; ptr++) {
@@ -1125,9 +1162,9 @@ void trailer_info_get(struct trailer_info *info, const char *str,
        strbuf_list_free(trailer_lines);
 
        info->blank_line_before_trailer = ends_with_blank_line(str,
-                                                              trailer_start);
-       info->trailer_start = str + trailer_start;
-       info->trailer_end = str + trailer_end;
+                                                              trailer_block_start);
+       info->trailer_block_start = trailer_block_start;
+       info->trailer_block_end = end_of_log_message;
        info->trailers = trailer_strings;
        info->trailer_nr = nr;
 }
@@ -1142,6 +1179,7 @@ void trailer_info_release(struct trailer_info *info)
 
 static void format_trailer_info(struct strbuf *out,
                                const struct trailer_info *info,
+                               const char *msg,
                                const struct process_trailer_options *opts)
 {
        size_t origlen = out->len;
@@ -1151,8 +1189,8 @@ static void format_trailer_info(struct strbuf *out,
        if (!opts->only_trailers && !opts->unfold && !opts->filter &&
            !opts->separator && !opts->key_only && !opts->value_only &&
            !opts->key_value_separator) {
-               strbuf_add(out, info->trailer_start,
-                          info->trailer_end - info->trailer_start);
+               strbuf_add(out, msg + info->trailer_block_start,
+                          info->trailer_block_end - info->trailer_block_start);
                return;
        }
 
@@ -1206,7 +1244,7 @@ void format_trailers_from_commit(struct strbuf *out, const char *msg,
        struct trailer_info info;
 
        trailer_info_get(&info, msg, opts);
-       format_trailer_info(out, &info, opts);
+       format_trailer_info(out, &info, msg, opts);
        trailer_info_release(&info);
 }
 
@@ -1216,14 +1254,14 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
        strbuf_init(&iter->key, 0);
        strbuf_init(&iter->val, 0);
        opts.no_divider = 1;
-       trailer_info_get(&iter->info, msg, &opts);
-       iter->cur = 0;
+       trailer_info_get(&iter->internal.info, msg, &opts);
+       iter->internal.cur = 0;
 }
 
 int trailer_iterator_advance(struct trailer_iterator *iter)
 {
-       while (iter->cur < iter->info.trailer_nr) {
-               char *trailer = iter->info.trailers[iter->cur++];
+       while (iter->internal.cur < iter->internal.info.trailer_nr) {
+               char *trailer = iter->internal.info.trailers[iter->internal.cur++];
                int separator_pos = find_separator(trailer, separators);
 
                if (separator_pos < 1)
@@ -1241,7 +1279,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter)
 
 void trailer_iterator_release(struct trailer_iterator *iter)
 {
-       trailer_info_release(&iter->info);
+       trailer_info_release(&iter->internal.info);
        strbuf_release(&iter->val);
        strbuf_release(&iter->key);
 }
index 795d2fccfd9579500b798ac667feef2ee549f4b4..1644cd05f60d9f3ceb89ffa66fb5c5b3da9e5cd4 100644 (file)
--- a/trailer.h
+++ b/trailer.h
@@ -32,16 +32,16 @@ int trailer_set_if_missing(enum trailer_if_missing *item, const char *value);
 struct trailer_info {
        /*
         * True if there is a blank line before the location pointed to by
-        * trailer_start.
+        * trailer_block_start.
         */
        int blank_line_before_trailer;
 
        /*
-        * Pointers to the start and end of the trailer block found. If there
-        * is no trailer block found, these 2 pointers point to the end of the
-        * input string.
+        * Offsets to the trailer block start and end positions in the input
+        * string. If no trailer block is found, these are both set to the
+        * "true" end of the input (find_end_of_log_message()).
         */
-       const char *trailer_start, *trailer_end;
+       size_t trailer_block_start, trailer_block_end;
 
        /*
         * Array of trailers found.
@@ -119,8 +119,10 @@ struct trailer_iterator {
        struct strbuf val;
 
        /* private */
-       struct trailer_info info;
-       size_t cur;
+       struct {
+               struct trailer_info info;
+               size_t cur;
+       } internal;
 };
 
 /*
index e95267a4ab54dc12318435d157d1fdd37821f567..e34a8f47cfbf5cba150e1aa7c6267fc2b1c080c0 100644 (file)
@@ -1,10 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "transport.h"
 #include "quote.h"
 #include "run-command.h"
 #include "commit.h"
-#include "diff.h"
-#include "revision.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "repository.h"
 #include "remote.h"
 #include "string-list.h"
 #include "thread-utils.h"
@@ -1081,7 +1084,7 @@ static int push_refs_with_export(struct transport *transport,
                struct object_id oid;
 
                private = apply_refspecs(&data->rs, ref->name);
-               if (private && !get_oid(private, &oid)) {
+               if (private && !repo_get_oid(the_repository, private, &oid)) {
                        strbuf_addf(&buf, "^%s", private);
                        string_list_append_nodup(&revlist_args,
                                                 strbuf_detach(&buf, NULL));
@@ -1267,9 +1270,22 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
        return ret;
 }
 
+static int get_bundle_uri(struct transport *transport)
+{
+       get_helper(transport);
+
+       if (process_connect(transport, 0)) {
+               do_take_over(transport);
+               return transport->vtable->get_bundle_uri(transport);
+       }
+
+       return -1;
+}
+
 static struct transport_vtable vtable = {
        .set_option     = set_helper_option,
        .get_refs_list  = get_refs_list,
+       .get_bundle_uri = get_bundle_uri,
        .fetch_refs     = fetch_refs,
        .push_refs      = push_refs,
        .connect        = connect_helper,
index c4ca0b733acbdbe7fc7a5906490e396240ec7b06..90ea749e5cfbf7de994a8747186ad5fb3ae7c653 100644 (file)
@@ -26,6 +26,13 @@ struct transport_vtable {
        struct ref *(*get_refs_list)(struct transport *transport, int for_push,
                                     struct transport_ls_refs_options *transport_options);
 
+       /**
+        * Populates the remote side's bundle-uri under protocol v2,
+        * if the "bundle-uri" capability was advertised. Returns 0 if
+        * OK, negative values on error.
+        */
+       int (*get_bundle_uri)(struct transport *transport);
+
        /**
         * Fetch the objects for the given refs. Note that this gets
         * an array, and should ignore the list structure.
index f5d5c2281242621abeadbd96b9dca3101200d648..df518ead70c387dd354dd69a5ee4af8a3ab0d560 100644 (file)
@@ -1,5 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
+#include "hex.h"
 #include "transport.h"
 #include "hook.h"
 #include "pkt-line.h"
@@ -7,9 +10,8 @@
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
-#include "walker.h"
 #include "bundle.h"
-#include "dir.h"
+#include "gettext.h"
 #include "refs.h"
 #include "refspec.h"
 #include "branch.h"
 #include "string-list.h"
 #include "oid-array.h"
 #include "sigchain.h"
+#include "trace2.h"
 #include "transport-internal.h"
 #include "protocol.h"
-#include "object-store.h"
+#include "object-name.h"
 #include "color.h"
+#include "bundle-uri.h"
 
 static int transport_use_color = -1;
 static char transport_colors[][COLOR_MAXLEN] = {
@@ -166,7 +170,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
 }
 
 static int fetch_refs_from_bundle(struct transport *transport,
-                              int nr_heads, struct ref **to_fetch)
+                                 int nr_heads UNUSED,
+                                 struct ref **to_fetch UNUSED)
 {
        struct bundle_transport_data *data = transport->data;
        struct strvec extra_index_pack_args = STRVEC_INIT;
@@ -178,7 +183,7 @@ static int fetch_refs_from_bundle(struct transport *transport,
        if (!data->get_refs_from_bundle_called)
                get_refs_from_bundle_inner(transport);
        ret = unbundle(the_repository, &data->header, data->fd,
-                      &extra_index_pack_args);
+                      &extra_index_pack_args, 0);
        transport->hash_algo = data->header.hash_algo;
        return ret;
 }
@@ -197,7 +202,7 @@ struct git_transport_data {
        struct git_transport_options options;
        struct child_process *conn;
        int fd[2];
-       unsigned got_remote_heads : 1;
+       unsigned finished_handshake : 1;
        enum protocol_version version;
        struct oid_array extra_have;
        struct oid_array shallow;
@@ -275,8 +280,12 @@ static int connect_setup(struct transport *transport, int for_push)
        }
 
        data->conn = git_connect(data->fd, transport->url,
-                                for_push ? data->options.receivepack :
-                                data->options.uploadpack,
+                                for_push ?
+                                       "git-receive-pack" :
+                                       "git-upload-pack",
+                                for_push ?
+                                       data->options.receivepack :
+                                       data->options.uploadpack,
                                 flags);
 
        return 0;
@@ -306,7 +315,7 @@ static struct ref *handshake(struct transport *transport, int for_push,
        struct git_transport_data *data = transport->data;
        struct ref *refs = NULL;
        struct packet_reader reader;
-       int sid_len;
+       size_t sid_len;
        const char *server_sid;
 
        connect_setup(transport, for_push);
@@ -344,7 +353,7 @@ static struct ref *handshake(struct transport *transport, int for_push,
        case protocol_unknown_version:
                BUG("unknown protocol version");
        }
-       data->got_remote_heads = 1;
+       data->finished_handshake = 1;
        transport->hash_algo = reader.hash_algo;
 
        if (reader.line_peeked)
@@ -359,6 +368,39 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
        return handshake(transport, for_push, options, 1);
 }
 
+static int get_bundle_uri(struct transport *transport)
+{
+       struct git_transport_data *data = transport->data;
+       struct packet_reader reader;
+       int stateless_rpc = transport->stateless_rpc;
+
+       if (!transport->bundles) {
+               CALLOC_ARRAY(transport->bundles, 1);
+               init_bundle_list(transport->bundles);
+       }
+
+       if (!data->finished_handshake) {
+               struct ref *refs = handshake(transport, 0, NULL, 0);
+
+               if (refs)
+                       free_refs(refs);
+       }
+
+       /*
+        * "Support" protocol v0 and v2 without bundle-uri support by
+        * silently degrading to a NOOP.
+        */
+       if (!server_supports_v2("bundle-uri"))
+               return 0;
+
+       packet_reader_init(&reader, data->fd[0], NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_GENTLE_ON_EOF);
+
+       return get_remote_bundle_uri(data->fd[1], &reader,
+                                    transport->bundles, stateless_rpc);
+}
+
 static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
@@ -394,7 +436,7 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.negotiation_tips = data->options.negotiation_tips;
        args.reject_shallow_remote = transport->smart_options->reject_shallow;
 
-       if (!data->got_remote_heads) {
+       if (!data->finished_handshake) {
                int i;
                int must_list_refs = 0;
                for (i = 0; i < nr_heads; i++) {
@@ -434,7 +476,7 @@ static int fetch_refs_via_pack(struct transport *transport,
                          to_fetch, nr_heads, &data->shallow,
                          &transport->pack_lockfiles, data->version);
 
-       data->got_remote_heads = 0;
+       data->finished_handshake = 0;
        data->options.self_contained_and_connected =
                args.self_contained_and_connected;
        data->options.connectivity_checked = args.connectivity_checked;
@@ -742,7 +784,8 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count,
 static int measure_abbrev(const struct object_id *oid, int sofar)
 {
        char hex[GIT_MAX_HEXSZ + 1];
-       int w = find_unique_abbrev_r(hex, oid, DEFAULT_ABBREV);
+       int w = repo_find_unique_abbrev_r(the_repository, hex, oid,
+                                         DEFAULT_ABBREV);
 
        return (w < sofar) ? sofar : w;
 }
@@ -819,7 +862,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        if (transport_color_config() < 0)
                return -1;
 
-       if (!data->got_remote_heads)
+       if (!data->finished_handshake)
                get_refs_via_connect(transport, 1, NULL);
 
        memset(&args, 0, sizeof(args));
@@ -867,7 +910,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        else
                ret = finish_connect(data->conn);
        data->conn = NULL;
-       data->got_remote_heads = 0;
+       data->finished_handshake = 0;
 
        return ret;
 }
@@ -877,7 +920,7 @@ static int connect_git(struct transport *transport, const char *name,
 {
        struct git_transport_data *data = transport->data;
        data->conn = git_connect(data->fd, transport->url,
-                                executable, 0);
+                                name, executable, 0);
        fd[0] = data->fd[0];
        fd[1] = data->fd[1];
        return 0;
@@ -887,7 +930,7 @@ static int disconnect_git(struct transport *transport)
 {
        struct git_transport_data *data = transport->data;
        if (data->conn) {
-               if (data->got_remote_heads && !transport->stateless_rpc)
+               if (data->finished_handshake && !transport->stateless_rpc)
                        packet_flush(data->fd[1]);
                close(data->fd[0]);
                if (data->fd[1] >= 0)
@@ -902,6 +945,7 @@ static int disconnect_git(struct transport *transport)
 
 static struct transport_vtable taken_over_vtable = {
        .get_refs_list  = get_refs_via_connect,
+       .get_bundle_uri = get_bundle_uri,
        .fetch_refs     = fetch_refs_via_pack,
        .push_refs      = git_transport_push,
        .disconnect     = disconnect_git
@@ -921,7 +965,7 @@ void transport_take_over(struct transport *transport,
        data->conn = child;
        data->fd[0] = data->conn->out;
        data->fd[1] = data->conn->in;
-       data->got_remote_heads = 0;
+       data->finished_handshake = 0;
        transport->data = data;
 
        transport->vtable = &taken_over_vtable;
@@ -1054,6 +1098,7 @@ static struct transport_vtable bundle_vtable = {
 
 static struct transport_vtable builtin_smart_vtable = {
        .get_refs_list  = get_refs_via_connect,
+       .get_bundle_uri = get_bundle_uri,
        .fetch_refs     = fetch_refs_via_pack,
        .push_refs      = git_transport_push,
        .connect        = connect_git,
@@ -1068,6 +1113,9 @@ struct transport *transport_get(struct remote *remote, const char *url)
        ret->progress = isatty(2);
        string_list_init_dup(&ret->pack_lockfiles);
 
+       CALLOC_ARRAY(ret->bundles, 1);
+       init_bundle_list(ret->bundles);
+
        if (!remote)
                BUG("No remote provided to transport_get()");
 
@@ -1118,7 +1166,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
                ret->smart_options = &(data->options);
 
                data->conn = NULL;
-               data->got_remote_heads = 0;
+               data->finished_handshake = 0;
        } else {
                /* Unknown protocol in URL. Pass to external handler. */
                int len = external_specification_len(url);
@@ -1483,6 +1531,34 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
        return rc;
 }
 
+int transport_get_remote_bundle_uri(struct transport *transport)
+{
+       int value = 0;
+       const struct transport_vtable *vtable = transport->vtable;
+
+       /* Check config only once. */
+       if (transport->got_remote_bundle_uri)
+               return 0;
+       transport->got_remote_bundle_uri = 1;
+
+       /*
+        * Don't request bundle-uri from the server unless configured to
+        * do so by the transfer.bundleURI=true config option.
+        */
+       if (git_config_get_bool("transfer.bundleuri", &value) || !value)
+               return 0;
+
+       if (!transport->bundles->baseURI)
+               transport->bundles->baseURI = xstrdup(transport->url);
+
+       if (!vtable->get_bundle_uri)
+               return error(_("bundle-uri operation not supported by protocol"));
+
+       if (vtable->get_bundle_uri(transport) < 0)
+               return error(_("could not retrieve server-advertised bundle-uri list"));
+       return 0;
+}
+
 void transport_unlock_pack(struct transport *transport, unsigned int flags)
 {
        int in_signal_handler = !!(flags & TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER);
@@ -1513,6 +1589,8 @@ int transport_disconnect(struct transport *transport)
                ret = transport->vtable->disconnect(transport);
        if (transport->got_remote_refs)
                free_refs((void *)transport->remote_refs);
+       clear_bundle_list(transport->bundles);
+       free(transport->bundles);
        free(transport);
        return ret;
 }
index b5bf7b3e70418a9ded196bf124b1e445bc8076cb..6393cd9823c01f878000ded305cf621b2b526824 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef TRANSPORT_H
 #define TRANSPORT_H
 
-#include "cache.h"
 #include "run-command.h"
 #include "remote.h"
 #include "list-objects-filter-options.h"
@@ -62,6 +61,7 @@ enum transport_family {
        TRANSPORT_FAMILY_IPV6
 };
 
+struct bundle_list;
 struct transport {
        const struct transport_vtable *vtable;
 
@@ -76,6 +76,18 @@ struct transport {
         */
        unsigned got_remote_refs : 1;
 
+       /**
+        * Indicates whether we already called get_bundle_uri_list(); set by
+        * transport.c::transport_get_remote_bundle_uri().
+        */
+       unsigned got_remote_bundle_uri : 1;
+
+       /*
+        * The results of "command=bundle-uri", if both sides support
+        * the "bundle-uri" capability.
+        */
+       struct bundle_list *bundles;
+
        /*
         * Transports that call take-over destroys the data specific to
         * the transport type while doing so, and cannot be reused.
@@ -281,6 +293,12 @@ void transport_ls_refs_options_release(struct transport_ls_refs_options *opts);
 const struct ref *transport_get_remote_refs(struct transport *transport,
                                            struct transport_ls_refs_options *transport_options);
 
+/**
+ * Retrieve bundle URI(s) from a remote. Populates "struct
+ * transport"'s "bundle_uri" and "got_remote_bundle_uri".
+ */
+int transport_get_remote_bundle_uri(struct transport *transport);
+
 /*
  * Fetch the hash algorithm used by a remote.
  *
index 69031d7cbae6a88c1fca3242e974d9cd435a4c78..46107772d178f9b3706f288e6055a34a040db00c 100644 (file)
@@ -1,10 +1,26 @@
 /*
  * Helper functions for tree diff generation
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "hash.h"
 #include "tree.h"
+#include "tree-walk.h"
+#include "environment.h"
+
+/*
+ * Some mode bits are also used internally for computations.
+ *
+ * They *must* not overlap with any valid modes, and they *must* not be emitted
+ * to outside world - i.e. appear on disk or network. In other words, it's just
+ * temporary fields, which we internally use, but they have to stay in-house.
+ *
+ * ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
+ *   codebase mode is `unsigned int` which is assumed to be at least 32 bits )
+ */
+
+#define S_DIFFTREE_IFXMIN_NEQ  0x80000000
 
 /*
  * internal mode marker, saying a tree entry != entry of tp[imin]
@@ -30,7 +46,8 @@
 static struct combine_diff_path *ll_diff_tree_paths(
        struct combine_diff_path *p, const struct object_id *oid,
        const struct object_id **parents_oid, int nparent,
-       struct strbuf *base, struct diff_options *opt);
+       struct strbuf *base, struct diff_options *opt,
+       int depth);
 static void ll_diff_tree_oid(const struct object_id *old_oid,
                             const struct object_id *new_oid,
                             struct strbuf *base, struct diff_options *opt);
@@ -181,7 +198,7 @@ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last,
 static struct combine_diff_path *emit_path(struct combine_diff_path *p,
        struct strbuf *base, struct diff_options *opt, int nparent,
        struct tree_desc *t, struct tree_desc *tp,
-       int imin)
+       int imin, int depth)
 {
        unsigned short mode;
        const char *path;
@@ -287,7 +304,8 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
 
                strbuf_add(base, path, pathlen);
                strbuf_addch(base, '/');
-               p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt);
+               p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt,
+                                      depth + 1);
                FAST_ARRAY_FREE(parents_oid, nparent);
        }
 
@@ -302,7 +320,7 @@ static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
 
        while (t->size) {
                match = tree_entry_interesting(opt->repo->index, &t->entry,
-                                              base, 0, &opt->pathspec);
+                                              base, &opt->pathspec);
                if (match) {
                        if (match == all_entries_not_interesting)
                                t->size = 0;
@@ -408,12 +426,16 @@ static inline void update_tp_entries(struct tree_desc *tp, int nparent)
 static struct combine_diff_path *ll_diff_tree_paths(
        struct combine_diff_path *p, const struct object_id *oid,
        const struct object_id **parents_oid, int nparent,
-       struct strbuf *base, struct diff_options *opt)
+       struct strbuf *base, struct diff_options *opt,
+       int depth)
 {
        struct tree_desc t, *tp;
        void *ttree, **tptree;
        int i;
 
+       if (depth > max_allowed_tree_depth)
+               die("exceeded maximum allowed tree depth");
+
        FAST_ARRAY_ALLOC(tp, nparent);
        FAST_ARRAY_ALLOC(tptree, nparent);
 
@@ -507,7 +529,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
 
                        /* D += {δ(t,pi) if pi=p[imin];  "+a" if pi > p[imin]} */
                        p = emit_path(p, base, opt, nparent,
-                                       &t, tp, imin);
+                                       &t, tp, imin, depth);
 
                skip_emit_t_tp:
                        /* t↓,  ∀ pi=p[imin]  pi↓ */
@@ -519,7 +541,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
                else if (cmp < 0) {
                        /* D += "+t" */
                        p = emit_path(p, base, opt, nparent,
-                                       &t, /*tp=*/NULL, -1);
+                                       &t, /*tp=*/NULL, -1, depth);
 
                        /* t↓ */
                        update_tree_entry(&t);
@@ -535,7 +557,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
                        }
 
                        p = emit_path(p, base, opt, nparent,
-                                       /*t=*/NULL, tp, imin);
+                                       /*t=*/NULL, tp, imin, depth);
 
                skip_emit_tp:
                        /* ∀ pi=p[imin]  pi↓ */
@@ -557,7 +579,7 @@ struct combine_diff_path *diff_tree_paths(
        const struct object_id **parents_oid, int nparent,
        struct strbuf *base, struct diff_options *opt)
 {
-       p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt);
+       p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt, 0);
 
        /*
         * free pre-allocated last element, if any
index 74f4d710e8f0f28410fae49a6b9c31bf1045e0ca..b517792ba23a2104f5f8207519e64cbc5d2daaa4 100644 (file)
@@ -1,10 +1,15 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tree-walk.h"
 #include "dir.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "trace2.h"
 #include "tree.h"
 #include "pathspec.h"
 #include "json-writer.h"
+#include "environment.h"
 
 static const char *get_mode(const char *str, unsigned int *modep)
 {
@@ -430,29 +435,32 @@ static inline int prune_traversal(struct index_state *istate,
        if (still_interesting < 0)
                return still_interesting;
        return tree_entry_interesting(istate, e, base,
-                                     0, info->pathspec);
+                                     info->pathspec);
 }
 
 int traverse_trees(struct index_state *istate,
                   int n, struct tree_desc *t,
                   struct traverse_info *info)
 {
-       int error = 0;
-       struct name_entry entry[MAX_TRAVERSE_TREES];
+       int ret = 0;
+       struct name_entry *entry;
        int i;
-       struct tree_desc_x tx[ARRAY_SIZE(entry)];
+       struct tree_desc_x *tx;
        struct strbuf base = STRBUF_INIT;
        int interesting = 1;
        char *traverse_path;
 
+       if (traverse_trees_cur_depth > max_allowed_tree_depth)
+               return error("exceeded maximum allowed tree depth");
+
        traverse_trees_count++;
        traverse_trees_cur_depth++;
 
        if (traverse_trees_cur_depth > traverse_trees_max_depth)
                traverse_trees_max_depth = traverse_trees_cur_depth;
 
-       if (n >= ARRAY_SIZE(entry))
-               BUG("traverse_trees() called with too many trees (%d)", n);
+       ALLOC_ARRAY(entry, n);
+       ALLOC_ARRAY(tx, n);
 
        for (i = 0; i < n; i++) {
                tx[i].d = t[i];
@@ -535,7 +543,7 @@ int traverse_trees(struct index_state *istate,
                if (interesting) {
                        trees_used = info->fn(n, mask, dirmask, entry, info);
                        if (trees_used < 0) {
-                               error = trees_used;
+                               ret = trees_used;
                                if (!info->show_all_errors)
                                        break;
                        }
@@ -547,12 +555,14 @@ int traverse_trees(struct index_state *istate,
        }
        for (i = 0; i < n; i++)
                free_extended_entry(tx + i);
+       free(tx);
+       free(entry);
        free(traverse_path);
        info->traverse_path = NULL;
        strbuf_release(&base);
 
        traverse_trees_cur_depth--;
-       return error;
+       return ret;
 }
 
 struct dir_state {
@@ -1011,17 +1021,17 @@ static int match_wildcard_base(const struct pathspec_item *item,
 /*
  * Is a tree entry interesting given the pathspec we have?
  *
- * Pre-condition: either baselen == base_offset (i.e. empty path)
+ * Pre-condition: either baselen == 0 (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
 static enum interesting do_match(struct index_state *istate,
                                 const struct name_entry *entry,
-                                struct strbuf *base, int base_offset,
+                                struct strbuf *base,
                                 const struct pathspec *ps,
                                 int exclude)
 {
        int i;
-       int pathlen, baselen = base->len - base_offset;
+       int pathlen, baselen = base->len;
        enum interesting never_interesting = ps->has_wildcard ?
                entry_not_interesting : all_entries_not_interesting;
 
@@ -1039,7 +1049,7 @@ static enum interesting do_match(struct index_state *istate,
                    !(ps->magic & PATHSPEC_MAXDEPTH) ||
                    ps->max_depth == -1)
                        return all_entries_interesting;
-               return within_depth(base->buf + base_offset, baselen,
+               return within_depth(base->buf, baselen,
                                    !!S_ISDIR(entry->mode),
                                    ps->max_depth) ?
                        entry_interesting : entry_not_interesting;
@@ -1050,7 +1060,7 @@ static enum interesting do_match(struct index_state *istate,
        for (i = ps->nr - 1; i >= 0; i--) {
                const struct pathspec_item *item = ps->items+i;
                const char *match = item->match;
-               const char *base_str = base->buf + base_offset;
+               const char *base_str = base->buf;
                int matchlen = item->len, matched = 0;
 
                if ((!exclude &&   item->magic & PATHSPEC_EXCLUDE) ||
@@ -1143,9 +1153,9 @@ match_wildcards:
 
                strbuf_add(base, entry->path, pathlen);
 
-               if (!git_fnmatch(item, match, base->buf + base_offset,
+               if (!git_fnmatch(item, match, base->buf,
                                 item->nowildcard_len)) {
-                       strbuf_setlen(base, base_offset + baselen);
+                       strbuf_setlen(base, baselen);
                        goto interesting;
                }
 
@@ -1157,13 +1167,13 @@ match_wildcards:
                 * be performed in the submodule itself.
                 */
                if (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&
-                   !ps_strncmp(item, match, base->buf + base_offset,
+                   !ps_strncmp(item, match, base->buf,
                                item->nowildcard_len)) {
-                       strbuf_setlen(base, base_offset + baselen);
+                       strbuf_setlen(base, baselen);
                        goto interesting;
                }
 
-               strbuf_setlen(base, base_offset + baselen);
+               strbuf_setlen(base, baselen);
 
                /*
                 * Match all directories. We'll try to match files
@@ -1199,9 +1209,9 @@ interesting:
                                return entry_interesting;
 
                        strbuf_add(base, entry->path, pathlen);
-                       ret = match_pathspec_attrs(istate, base->buf + base_offset,
-                                                  base->len - base_offset, item);
-                       strbuf_setlen(base, base_offset + baselen);
+                       ret = match_pathspec_attrs(istate, base->buf,
+                                                  base->len, item);
+                       strbuf_setlen(base, baselen);
                        if (!ret)
                                continue;
                }
@@ -1213,16 +1223,16 @@ interesting:
 /*
  * Is a tree entry interesting given the pathspec we have?
  *
- * Pre-condition: either baselen == base_offset (i.e. empty path)
+ * Pre-condition: either baselen == 0 (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
 enum interesting tree_entry_interesting(struct index_state *istate,
                                        const struct name_entry *entry,
-                                       struct strbuf *base, int base_offset,
+                                       struct strbuf *base,
                                        const struct pathspec *ps)
 {
        enum interesting positive, negative;
-       positive = do_match(istate, entry, base, base_offset, ps, 0);
+       positive = do_match(istate, entry, base, ps, 0);
 
        /*
         * case | entry | positive | negative | result
@@ -1259,7 +1269,7 @@ enum interesting tree_entry_interesting(struct index_state *istate,
            positive <= entry_not_interesting) /* #1, #2, #11, #12 */
                return positive;
 
-       negative = do_match(istate, entry, base, base_offset, ps, 1);
+       negative = do_match(istate, entry, base, ps, 1);
 
        /* #8, #18 */
        if (positive == all_entries_interesting &&
index 6305d531503f25cd0b2632914d9aa7ddea55ff8e..a6bfa3da3a826bf5c218132b93582e53ace876be 100644 (file)
@@ -1,9 +1,10 @@
 #ifndef TREE_WALK_H
 #define TREE_WALK_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
-#define MAX_TRAVERSE_TREES 8
+struct index_state;
+struct repository;
 
 /**
  * The tree walking API is used to traverse and inspect trees.
@@ -221,7 +222,7 @@ enum interesting {
 
 enum interesting tree_entry_interesting(struct index_state *istate,
                                        const struct name_entry *,
-                                       struct strbuf *, int,
+                                       struct strbuf *,
                                        const struct pathspec *ps);
 
 #endif
diff --git a/tree.c b/tree.c
index 410e3b477e557f55ad6843e1106567a3fd155557..508e5fd76fd5bb588796bb9c51a78557880ba074 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -1,18 +1,19 @@
-#include "cache.h"
-#include "cache-tree.h"
+#include "git-compat-util.h"
+#include "hex.h"
 #include "tree.h"
-#include "object-store.h"
-#include "blob.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "commit.h"
-#include "tag.h"
 #include "alloc.h"
 #include "tree-walk.h"
 #include "repository.h"
+#include "environment.h"
 
 const char *tree_type = "tree";
 
 int read_tree_at(struct repository *r,
                 struct tree *tree, struct strbuf *base,
+                int depth,
                 const struct pathspec *pathspec,
                 read_tree_fn_t fn, void *context)
 {
@@ -22,6 +23,9 @@ int read_tree_at(struct repository *r,
        int len, oldlen = base->len;
        enum interesting retval = entry_not_interesting;
 
+       if (depth > max_allowed_tree_depth)
+               return error("exceeded maximum allowed tree depth");
+
        if (parse_tree(tree))
                return -1;
 
@@ -30,7 +34,7 @@ int read_tree_at(struct repository *r,
        while (tree_entry(&desc, &entry)) {
                if (retval != all_entries_interesting) {
                        retval = tree_entry_interesting(r->index, &entry,
-                                                       base, 0, pathspec);
+                                                       base, pathspec);
                        if (retval == all_entries_not_interesting)
                                break;
                        if (retval == entry_not_interesting)
@@ -58,7 +62,7 @@ int read_tree_at(struct repository *r,
                                    oid_to_hex(&entry.oid),
                                    base->buf, entry.path);
 
-                       if (parse_commit(commit))
+                       if (repo_parse_commit(r, commit))
                                die("Invalid commit %s in submodule path %s%s",
                                    oid_to_hex(&entry.oid),
                                    base->buf, entry.path);
@@ -72,7 +76,7 @@ int read_tree_at(struct repository *r,
                strbuf_add(base, entry.path, len);
                strbuf_addch(base, '/');
                retval = read_tree_at(r, lookup_tree(r, &oid),
-                                     base, pathspec,
+                                     base, depth + 1, pathspec,
                                      fn, context);
                strbuf_setlen(base, oldlen);
                if (retval)
@@ -87,19 +91,77 @@ int read_tree(struct repository *r,
              read_tree_fn_t fn, void *context)
 {
        struct strbuf sb = STRBUF_INIT;
-       int ret = read_tree_at(r, tree, &sb, pathspec, fn, context);
+       int ret = read_tree_at(r, tree, &sb, 0, pathspec, fn, context);
        strbuf_release(&sb);
        return ret;
 }
 
-int cmp_cache_name_compare(const void *a_, const void *b_)
+int base_name_compare(const char *name1, size_t len1, int mode1,
+                     const char *name2, size_t len2, int mode2)
 {
-       const struct cache_entry *ce1, *ce2;
+       unsigned char c1, c2;
+       size_t len = len1 < len2 ? len1 : len2;
+       int cmp;
+
+       cmp = memcmp(name1, name2, len);
+       if (cmp)
+               return cmp;
+       c1 = name1[len];
+       c2 = name2[len];
+       if (!c1 && S_ISDIR(mode1))
+               c1 = '/';
+       if (!c2 && S_ISDIR(mode2))
+               c2 = '/';
+       return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
+}
 
-       ce1 = *((const struct cache_entry **)a_);
-       ce2 = *((const struct cache_entry **)b_);
-       return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),
-                                 ce2->name, ce2->ce_namelen, ce_stage(ce2));
+/*
+ * df_name_compare() is identical to base_name_compare(), except it
+ * compares conflicting directory/file entries as equal. Note that
+ * while a directory name compares as equal to a regular file, they
+ * then individually compare _differently_ to a filename that has
+ * a dot after the basename (because '\0' < '.' < '/').
+ *
+ * This is used by routines that want to traverse the git namespace
+ * but then handle conflicting entries together when possible.
+ */
+int df_name_compare(const char *name1, size_t len1, int mode1,
+                   const char *name2, size_t len2, int mode2)
+{
+       unsigned char c1, c2;
+       size_t len = len1 < len2 ? len1 : len2;
+       int cmp;
+
+       cmp = memcmp(name1, name2, len);
+       if (cmp)
+               return cmp;
+       /* Directories and files compare equal (same length, same name) */
+       if (len1 == len2)
+               return 0;
+       c1 = name1[len];
+       if (!c1 && S_ISDIR(mode1))
+               c1 = '/';
+       c2 = name2[len];
+       if (!c2 && S_ISDIR(mode2))
+               c2 = '/';
+       if (c1 == '/' && !c2)
+               return 0;
+       if (c2 == '/' && !c1)
+               return 0;
+       return c1 - c2;
+}
+
+int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)
+{
+       size_t min_len = (len1 < len2) ? len1 : len2;
+       int cmp = memcmp(name1, name2, min_len);
+       if (cmp)
+               return cmp;
+       if (len1 < len2)
+               return -1;
+       if (len1 > len2)
+               return 1;
+       return 0;
 }
 
 struct tree *lookup_tree(struct repository *r, const struct object_id *oid)
@@ -129,7 +191,8 @@ int parse_tree_gently(struct tree *item, int quiet_on_missing)
 
        if (item->object.parsed)
                return 0;
-       buffer = read_object_file(&item->object.oid, &type, &size);
+       buffer = repo_read_object_file(the_repository, &item->object.oid,
+                                      &type, &size);
        if (!buffer)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
diff --git a/tree.h b/tree.h
index 6efff003e2120e3cc5a82cda5bde904bfefa786d..cc6ddf51b3273c2dbeb798b2cb945de29dd28a36 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -3,6 +3,7 @@
 
 #include "object.h"
 
+struct pathspec;
 struct repository;
 struct strbuf;
 
@@ -28,13 +29,22 @@ void free_tree_buffer(struct tree *tree);
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const struct object_id *oid);
 
-int cmp_cache_name_compare(const void *a_, const void *b_);
+/*
+ * Functions for comparing pathnames
+ */
+int base_name_compare(const char *name1, size_t len1, int mode1,
+                     const char *name2, size_t len2, int mode2);
+int df_name_compare(const char *name1, size_t len1, int mode1,
+                   const char *name2, size_t len2, int mode2);
+int name_compare(const char *name1, size_t len1,
+                const char *name2, size_t len2);
 
 #define READ_TREE_RECURSIVE 1
 typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);
 
 int read_tree_at(struct repository *r,
                 struct tree *tree, struct strbuf *base,
+                int depth,
                 const struct pathspec *pathspec,
                 read_tree_fn_t fn, void *context);
 
index 97c851b27df4a7ea2bc33362a9d2f9dce38c0522..be5bf8c4f2ff28827507a70846907aeca731d530 100644 (file)
@@ -94,7 +94,7 @@ static const struct interval zero_width[] = {
 { 0x0E47, 0x0E4E },
 { 0x0EB1, 0x0EB1 },
 { 0x0EB4, 0x0EBC },
-{ 0x0EC8, 0x0ECD },
+{ 0x0EC8, 0x0ECE },
 { 0x0F18, 0x0F19 },
 { 0x0F35, 0x0F35 },
 { 0x0F37, 0x0F37 },
@@ -228,6 +228,7 @@ static const struct interval zero_width[] = {
 { 0x10AE5, 0x10AE6 },
 { 0x10D24, 0x10D27 },
 { 0x10EAB, 0x10EAC },
+{ 0x10EFD, 0x10EFF },
 { 0x10F46, 0x10F50 },
 { 0x10F82, 0x10F85 },
 { 0x11001, 0x11001 },
@@ -252,6 +253,7 @@ static const struct interval zero_width[] = {
 { 0x11234, 0x11234 },
 { 0x11236, 0x11237 },
 { 0x1123E, 0x1123E },
+{ 0x11241, 0x11241 },
 { 0x112DF, 0x112DF },
 { 0x112E3, 0x112EA },
 { 0x11300, 0x11301 },
@@ -313,7 +315,12 @@ static const struct interval zero_width[] = {
 { 0x11D95, 0x11D95 },
 { 0x11D97, 0x11D97 },
 { 0x11EF3, 0x11EF4 },
-{ 0x13430, 0x13438 },
+{ 0x11F00, 0x11F01 },
+{ 0x11F36, 0x11F3A },
+{ 0x11F40, 0x11F40 },
+{ 0x11F42, 0x11F42 },
+{ 0x13430, 0x13440 },
+{ 0x13447, 0x13455 },
 { 0x16AF0, 0x16AF4 },
 { 0x16B30, 0x16B36 },
 { 0x16F4F, 0x16F4F },
@@ -339,9 +346,11 @@ static const struct interval zero_width[] = {
 { 0x1E01B, 0x1E021 },
 { 0x1E023, 0x1E024 },
 { 0x1E026, 0x1E02A },
+{ 0x1E08F, 0x1E08F },
 { 0x1E130, 0x1E136 },
 { 0x1E2AE, 0x1E2AE },
 { 0x1E2EC, 0x1E2EF },
+{ 0x1E4EC, 0x1E4EF },
 { 0x1E8D0, 0x1E8D6 },
 { 0x1E944, 0x1E94A },
 { 0xE0001, 0xE0001 },
@@ -387,14 +396,13 @@ static const struct interval double_width[] = {
 { 0x2E80, 0x2E99 },
 { 0x2E9B, 0x2EF3 },
 { 0x2F00, 0x2FD5 },
-{ 0x2FF0, 0x2FFB },
-{ 0x3000, 0x303E },
+{ 0x2FF0, 0x303E },
 { 0x3041, 0x3096 },
 { 0x3099, 0x30FF },
 { 0x3105, 0x312F },
 { 0x3131, 0x318E },
 { 0x3190, 0x31E3 },
-{ 0x31F0, 0x321E },
+{ 0x31EF, 0x321E },
 { 0x3220, 0x3247 },
 { 0x3250, 0x4DBF },
 { 0x4E00, 0xA48C },
@@ -417,7 +425,9 @@ static const struct interval double_width[] = {
 { 0x1AFF5, 0x1AFFB },
 { 0x1AFFD, 0x1AFFE },
 { 0x1B000, 0x1B122 },
+{ 0x1B132, 0x1B132 },
 { 0x1B150, 0x1B152 },
+{ 0x1B155, 0x1B155 },
 { 0x1B164, 0x1B167 },
 { 0x1B170, 0x1B2FB },
 { 0x1F004, 0x1F004 },
@@ -451,7 +461,7 @@ static const struct interval double_width[] = {
 { 0x1F6CC, 0x1F6CC },
 { 0x1F6D0, 0x1F6D2 },
 { 0x1F6D5, 0x1F6D7 },
-{ 0x1F6DD, 0x1F6DF },
+{ 0x1F6DC, 0x1F6DF },
 { 0x1F6EB, 0x1F6EC },
 { 0x1F6F4, 0x1F6FC },
 { 0x1F7E0, 0x1F7EB },
@@ -459,15 +469,13 @@ static const struct interval double_width[] = {
 { 0x1F90C, 0x1F93A },
 { 0x1F93C, 0x1F945 },
 { 0x1F947, 0x1F9FF },
-{ 0x1FA70, 0x1FA74 },
-{ 0x1FA78, 0x1FA7C },
-{ 0x1FA80, 0x1FA86 },
-{ 0x1FA90, 0x1FAAC },
-{ 0x1FAB0, 0x1FABA },
-{ 0x1FAC0, 0x1FAC5 },
-{ 0x1FAD0, 0x1FAD9 },
-{ 0x1FAE0, 0x1FAE7 },
-{ 0x1FAF0, 0x1FAF6 },
+{ 0x1FA70, 0x1FA7C },
+{ 0x1FA80, 0x1FA88 },
+{ 0x1FA90, 0x1FABD },
+{ 0x1FABF, 0x1FAC5 },
+{ 0x1FACE, 0x1FADB },
+{ 0x1FAE0, 0x1FAE8 },
+{ 0x1FAF0, 0x1FAF8 },
 { 0x20000, 0x2FFFD },
 { 0x30000, 0x3FFFD }
 };
index e0be1badb58dee8af8e28c4a4486bf0fdba18a9c..79800d80636fc57fa8cc2696e33013c1c29e86b3 100644 (file)
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
 #include "unix-socket.h"
 
 #define DEFAULT_UNIX_STREAM_LISTEN_BACKLOG (5)
index efa2a207abcd42021fcae682da1d7e5afbc856fa..22ac2373e0788d9a0c7f377b86a756e5ef4ee518 100644 (file)
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "lockfile.h"
 #include "unix-socket.h"
 #include "unix-stream-server.h"
index bae812156c4fedb8e296e422d4cb6a8af1a2dada..c2b20b80d5a448b7b5c76d2de4ac9358fa596b69 100644 (file)
@@ -1,8 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "strvec.h"
 #include "repository.h"
-#include "config.h"
+#include "parse.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "name-hash.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
 #include "progress.h"
 #include "refs.h"
 #include "attr.h"
+#include "read-cache.h"
 #include "split-index.h"
 #include "sparse-index.h"
 #include "submodule.h"
 #include "submodule-config.h"
+#include "symlinks.h"
+#include "trace2.h"
 #include "fsmonitor.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "promisor-remote.h"
 #include "entry.h"
 #include "parallel-checkout.h"
-#include "sparse-index.h"
+#include "setup.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -67,11 +75,11 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_WARNING_TYPES] = {
 };
 
 #define ERRORMSG(o,type) \
-       ( ((o) && (o)->msgs[(type)]) \
-         ? ((o)->msgs[(type)])      \
+       ( ((o) && (o)->internal.msgs[(type)]) \
+         ? ((o)->internal.msgs[(type)])      \
          : (unpack_plumbing_errors[(type)]) )
 
-static const char *super_prefixed(const char *path)
+static const char *super_prefixed(const char *path, const char *super_prefix)
 {
        /*
         * It is necessary and sufficient to have two static buffers
@@ -83,7 +91,6 @@ static const char *super_prefixed(const char *path)
        static unsigned idx = ARRAY_SIZE(buf) - 1;
 
        if (super_prefix_len < 0) {
-               const char *super_prefix = get_super_prefix();
                if (!super_prefix) {
                        super_prefix_len = 0;
                } else {
@@ -110,10 +117,10 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                                  const char *cmd)
 {
        int i;
-       const char **msgs = opts->msgs;
+       const char **msgs = opts->internal.msgs;
        const char *msg;
 
-       strvec_init(&opts->msgs_to_free);
+       strvec_init(&opts->internal.msgs_to_free);
 
        if (!strcmp(cmd, "checkout"))
                msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
@@ -131,7 +138,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                          "Please commit your changes or stash them before you %s.")
                      : _("Your local changes to the following files would be overwritten by %s:\n%%s");
        msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
-               strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+               strvec_pushf(&opts->internal.msgs_to_free, msg, cmd, cmd);
 
        msgs[ERROR_NOT_UPTODATE_DIR] =
                _("Updating the following directories would lose untracked files in them:\n%s");
@@ -155,7 +162,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                          "Please move or remove them before you %s.")
                      : _("The following untracked working tree files would be removed by %s:\n%%s");
        msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] =
-               strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+               strvec_pushf(&opts->internal.msgs_to_free, msg, cmd, cmd);
 
        if (!strcmp(cmd, "checkout"))
                msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
@@ -173,7 +180,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                          "Please move or remove them before you %s.")
                      : _("The following untracked working tree files would be overwritten by %s:\n%%s");
        msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] =
-               strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+               strvec_pushf(&opts->internal.msgs_to_free, msg, cmd, cmd);
 
        /*
         * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
@@ -191,16 +198,16 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
        msgs[WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN] =
                _("The following paths were already present and thus not updated despite sparse patterns:\n%s");
 
-       opts->show_all_errors = 1;
+       opts->internal.show_all_errors = 1;
        /* rejected paths may not have a static buffer */
-       for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++)
-               opts->unpack_rejects[i].strdup_strings = 1;
+       for (i = 0; i < ARRAY_SIZE(opts->internal.unpack_rejects); i++)
+               opts->internal.unpack_rejects[i].strdup_strings = 1;
 }
 
 void clear_unpack_trees_porcelain(struct unpack_trees_options *opts)
 {
-       strvec_clear(&opts->msgs_to_free);
-       memset(opts->msgs, 0, sizeof(opts->msgs));
+       strvec_clear(&opts->internal.msgs_to_free);
+       memset(opts->internal.msgs, 0, sizeof(opts->internal.msgs));
 }
 
 static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
@@ -212,7 +219,7 @@ static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
                set |= CE_WT_REMOVE;
 
        ce->ce_flags = (ce->ce_flags & ~clear) | set;
-       return add_index_entry(&o->result, ce,
+       return add_index_entry(&o->internal.result, ce,
                               ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 }
 
@@ -220,7 +227,7 @@ static void add_entry(struct unpack_trees_options *o,
                      const struct cache_entry *ce,
                      unsigned int set, unsigned int clear)
 {
-       do_add_entry(o, dup_cache_entry(ce, &o->result), set, clear);
+       do_add_entry(o, dup_cache_entry(ce, &o->internal.result), set, clear);
 }
 
 /*
@@ -235,14 +242,15 @@ static int add_rejected_path(struct unpack_trees_options *o,
        if (o->quiet)
                return -1;
 
-       if (!o->show_all_errors)
-               return error(ERRORMSG(o, e), super_prefixed(path));
+       if (!o->internal.show_all_errors)
+               return error(ERRORMSG(o, e), super_prefixed(path,
+                                                           o->super_prefix));
 
        /*
         * Otherwise, insert in a list for future display by
         * display_(error|warning)_msgs()
         */
-       string_list_append(&o->unpack_rejects[e], path);
+       string_list_append(&o->internal.unpack_rejects[e], path);
        return -1;
 }
 
@@ -254,7 +262,7 @@ static void display_error_msgs(struct unpack_trees_options *o)
        int e;
        unsigned error_displayed = 0;
        for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
-               struct string_list *rejects = &o->unpack_rejects[e];
+               struct string_list *rejects = &o->internal.unpack_rejects[e];
 
                if (rejects->nr > 0) {
                        int i;
@@ -263,7 +271,8 @@ static void display_error_msgs(struct unpack_trees_options *o)
                        error_displayed = 1;
                        for (i = 0; i < rejects->nr; i++)
                                strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
-                       error(ERRORMSG(o, e), super_prefixed(path.buf));
+                       error(ERRORMSG(o, e), super_prefixed(path.buf,
+                                                            o->super_prefix));
                        strbuf_release(&path);
                }
                string_list_clear(rejects, 0);
@@ -281,7 +290,7 @@ static void display_warning_msgs(struct unpack_trees_options *o)
        unsigned warning_displayed = 0;
        for (e = NB_UNPACK_TREES_ERROR_TYPES + 1;
             e < NB_UNPACK_TREES_WARNING_TYPES; e++) {
-               struct string_list *rejects = &o->unpack_rejects[e];
+               struct string_list *rejects = &o->internal.unpack_rejects[e];
 
                if (rejects->nr > 0) {
                        int i;
@@ -290,7 +299,8 @@ static void display_warning_msgs(struct unpack_trees_options *o)
                        warning_displayed = 1;
                        for (i = 0; i < rejects->nr; i++)
                                strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
-                       warning(ERRORMSG(o, e), super_prefixed(path.buf));
+                       warning(ERRORMSG(o, e), super_prefixed(path.buf,
+                                                              o->super_prefix));
                        strbuf_release(&path);
                }
                string_list_clear(rejects, 0);
@@ -312,7 +322,8 @@ static int check_submodule_move_head(const struct cache_entry *ce,
        if (o->reset)
                flags |= SUBMODULE_MOVE_HEAD_FORCE;
 
-       if (submodule_move_head(ce->name, old_id, new_id, flags))
+       if (submodule_move_head(ce->name, o->super_prefix, old_id, new_id,
+                               flags))
                return add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
        return 0;
 }
@@ -415,6 +426,7 @@ static int check_updates(struct unpack_trees_options *o,
        int i, pc_workers, pc_threshold;
 
        trace_performance_enter();
+       state.super_prefix = o->super_prefix;
        state.force = 1;
        state.quiet = 1;
        state.refresh_cache = 1;
@@ -445,7 +457,7 @@ static int check_updates(struct unpack_trees_options *o,
 
                if (ce->ce_flags & CE_WT_REMOVE) {
                        display_progress(progress, ++cnt);
-                       unlink_entry(ce);
+                       unlink_entry(ce, o->super_prefix);
                }
        }
 
@@ -455,7 +467,7 @@ static int check_updates(struct unpack_trees_options *o,
        if (should_update_submodules())
                load_gitmodules_file(index, &state);
 
-       if (has_promisor_remote())
+       if (repo_has_promisor_remote(the_repository))
                /*
                 * Prefetch the objects that are to be checked out in the loop
                 * below.
@@ -597,13 +609,14 @@ static void mark_ce_used(struct cache_entry *ce, struct unpack_trees_options *o)
 {
        ce->ce_flags |= CE_UNPACKED;
 
-       if (o->cache_bottom < o->src_index->cache_nr &&
-           o->src_index->cache[o->cache_bottom] == ce) {
-               int bottom = o->cache_bottom;
+       if (o->internal.cache_bottom < o->src_index->cache_nr &&
+           o->src_index->cache[o->internal.cache_bottom] == ce) {
+               int bottom = o->internal.cache_bottom;
+
                while (bottom < o->src_index->cache_nr &&
                       o->src_index->cache[bottom]->ce_flags & CE_UNPACKED)
                        bottom++;
-               o->cache_bottom = bottom;
+               o->internal.cache_bottom = bottom;
        }
 }
 
@@ -649,7 +662,7 @@ static void mark_ce_used_same_name(struct cache_entry *ce,
 static struct cache_entry *next_cache_entry(struct unpack_trees_options *o)
 {
        const struct index_state *index = o->src_index;
-       int pos = o->cache_bottom;
+       int pos = o->internal.cache_bottom;
 
        while (pos < index->cache_nr) {
                struct cache_entry *ce = index->cache[pos];
@@ -708,7 +721,7 @@ static void restore_cache_bottom(struct traverse_info *info, int bottom)
 
        if (o->diff_index_cached)
                return;
-       o->cache_bottom = bottom;
+       o->internal.cache_bottom = bottom;
 }
 
 static int switch_cache_bottom(struct traverse_info *info)
@@ -718,13 +731,13 @@ static int switch_cache_bottom(struct traverse_info *info)
 
        if (o->diff_index_cached)
                return 0;
-       ret = o->cache_bottom;
+       ret = o->internal.cache_bottom;
        pos = find_cache_pos(info->prev, info->name, info->namelen);
 
        if (pos < -1)
-               o->cache_bottom = -2 - pos;
+               o->internal.cache_bottom = -2 - pos;
        else if (pos < 0)
-               o->cache_bottom = o->src_index->cache_nr;
+               o->internal.cache_bottom = o->src_index->cache_nr;
        return ret;
 }
 
@@ -835,7 +848,7 @@ static int traverse_by_cache_tree(int pos, int nr_entries, int nr_names,
                mark_ce_used(src[0], o);
        }
        free(tree_ce);
-       if (o->debug_unpack)
+       if (o->internal.debug_unpack)
                printf("Unpacked %d entries from %s to %s using cache-tree\n",
                       nr_entries,
                       o->src_index->cache[pos]->name,
@@ -851,8 +864,8 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
        struct unpack_trees_options *o = info->data;
        int i, ret, bottom;
        int nr_buf = 0;
-       struct tree_desc t[MAX_UNPACK_TREES];
-       void *buf[MAX_UNPACK_TREES];
+       struct tree_desc *t;
+       void **buf;
        struct traverse_info newinfo;
        struct name_entry *p;
        int nr_entries;
@@ -870,9 +883,9 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
                 * save and restore cache_bottom anyway to not miss
                 * unprocessed entries before 'pos'.
                 */
-               bottom = o->cache_bottom;
+               bottom = o->internal.cache_bottom;
                ret = traverse_by_cache_tree(pos, nr_entries, n, info);
-               o->cache_bottom = bottom;
+               o->internal.cache_bottom = bottom;
                return ret;
        }
 
@@ -889,6 +902,9 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
        newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);
        newinfo.df_conflicts |= df_conflicts;
 
+       ALLOC_ARRAY(t, n);
+       ALLOC_ARRAY(buf, n);
+
        /*
         * Fetch the tree from the ODB for each peer directory in the
         * n commits.
@@ -924,6 +940,8 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
 
        for (i = 0; i < nr_buf; i++)
                free(buf[i]);
+       free(buf);
+       free(t);
 
        return ret;
 }
@@ -1209,8 +1227,8 @@ static int unpack_single_entry(int n, unsigned long mask,
                 * cache entry from the index aware logic.
                 */
                src[i + o->merge] = create_ce_entry(info, names + i, stage,
-                                                   &o->result, o->merge,
-                                                   bit & dirmask);
+                                                   &o->internal.result,
+                                                   o->merge, bit & dirmask);
        }
 
        if (o->merge) {
@@ -1234,7 +1252,7 @@ static int unpack_single_entry(int n, unsigned long mask,
 
 static int unpack_failed(struct unpack_trees_options *o, const char *message)
 {
-       discard_index(&o->result);
+       discard_index(&o->internal.result);
        if (!o->quiet && !o->exiting_early) {
                if (message)
                        return error("%s", message);
@@ -1257,7 +1275,7 @@ static int find_cache_pos(struct traverse_info *info,
        struct index_state *index = o->src_index;
        int pfxlen = info->pathlen;
 
-       for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
+       for (pos = o->internal.cache_bottom; pos < index->cache_nr; pos++) {
                const struct cache_entry *ce = index->cache[pos];
                const char *ce_name, *ce_slash;
                int cmp, ce_len;
@@ -1268,8 +1286,8 @@ static int find_cache_pos(struct traverse_info *info,
                         * we can never match it; don't check it
                         * again.
                         */
-                       if (pos == o->cache_bottom)
-                               ++o->cache_bottom;
+                       if (pos == o->internal.cache_bottom)
+                               ++o->internal.cache_bottom;
                        continue;
                }
                if (!ce_in_traverse_path(ce, info)) {
@@ -1447,7 +1465,7 @@ static int unpack_sparse_callback(int n, unsigned long mask, unsigned long dirma
         */
        if (!is_null_oid(&names[0].oid)) {
                src[0] = create_ce_entry(info, &names[0], 0,
-                                       &o->result, 1,
+                                       &o->internal.result, 1,
                                        dirmask & (1ul << 0));
                src[0]->ce_flags |= (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
        }
@@ -1484,7 +1502,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
        while (!p->mode)
                p++;
 
-       if (o->debug_unpack)
+       if (o->internal.debug_unpack)
                debug_unpack_callback(n, mask, dirmask, names, info);
 
        /* Are we supposed to look at the index too? */
@@ -1557,7 +1575,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
                                 * in 'mark_ce_used()'
                                 */
                                if (!src[0] || !S_ISSPARSEDIR(src[0]->ce_mode))
-                                       o->cache_bottom += matches;
+                                       o->internal.cache_bottom += matches;
                                return mask;
                        }
                }
@@ -1806,7 +1824,7 @@ static void populate_from_existing_patterns(struct unpack_trees_options *o,
        if (get_sparse_checkout_patterns(pl) < 0)
                o->skip_sparse_checkout = 1;
        else
-               o->pl = pl;
+               o->internal.pl = pl;
 }
 
 static void update_sparsity_for_prefix(const char *prefix,
@@ -1868,8 +1886,12 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
        if (len > MAX_UNPACK_TREES)
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
-       if (o->dir)
-               BUG("o->dir is for internal use only");
+       if (o->internal.dir)
+               BUG("o->internal.dir is for internal use only");
+       if (o->internal.pl)
+               BUG("o->internal.pl is for internal use only");
+       if (o->df_conflict_entry)
+               BUG("o->df_conflict_entry is an output only field");
 
        trace_performance_enter();
        trace2_region_enter("unpack_trees", "unpack_trees", the_repository);
@@ -1877,7 +1899,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        prepare_repo_settings(repo);
        if (repo->settings.command_requires_full_index) {
                ensure_full_index(o->src_index);
-               ensure_full_index(o->dst_index);
+               if (o->dst_index)
+                       ensure_full_index(o->dst_index);
        }
 
        if (o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED &&
@@ -1885,9 +1908,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                BUG("UNPACK_RESET_OVERWRITE_UNTRACKED incompatible with preserved ignored files");
 
        if (!o->preserve_ignored) {
-               o->dir = &dir;
-               o->dir->flags |= DIR_SHOW_IGNORED;
-               setup_standard_excludes(o->dir);
+               o->internal.dir = &dir;
+               o->internal.dir->flags |= DIR_SHOW_IGNORED;
+               setup_standard_excludes(o->internal.dir);
        }
 
        if (o->prefix)
@@ -1895,49 +1918,52 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
        if (!core_apply_sparse_checkout || !o->update)
                o->skip_sparse_checkout = 1;
-       if (!o->skip_sparse_checkout && !o->pl) {
+       if (!o->skip_sparse_checkout) {
                memset(&pl, 0, sizeof(pl));
                free_pattern_list = 1;
                populate_from_existing_patterns(o, &pl);
        }
 
-       memset(&o->result, 0, sizeof(o->result));
-       o->result.initialized = 1;
-       o->result.timestamp.sec = o->src_index->timestamp.sec;
-       o->result.timestamp.nsec = o->src_index->timestamp.nsec;
-       o->result.version = o->src_index->version;
+       index_state_init(&o->internal.result, o->src_index->repo);
+       o->internal.result.initialized = 1;
+       o->internal.result.timestamp.sec = o->src_index->timestamp.sec;
+       o->internal.result.timestamp.nsec = o->src_index->timestamp.nsec;
+       o->internal.result.version = o->src_index->version;
        if (!o->src_index->split_index) {
-               o->result.split_index = NULL;
+               o->internal.result.split_index = NULL;
        } else if (o->src_index == o->dst_index) {
                /*
                 * o->dst_index (and thus o->src_index) will be discarded
-                * and overwritten with o->result at the end of this function,
-                * so just use src_index's split_index to avoid having to
-                * create a new one.
+                * and overwritten with o->internal.result at the end of
+                * this function, so just use src_index's split_index to
+                * avoid having to create a new one.
                 */
-               o->result.split_index = o->src_index->split_index;
-               o->result.split_index->refcount++;
+               o->internal.result.split_index = o->src_index->split_index;
+               if (o->src_index->cache_changed & SPLIT_INDEX_ORDERED)
+                       o->internal.result.cache_changed |= SPLIT_INDEX_ORDERED;
+               o->internal.result.split_index->refcount++;
        } else {
-               o->result.split_index = init_split_index(&o->result);
+               o->internal.result.split_index =
+                       init_split_index(&o->internal.result);
        }
-       oidcpy(&o->result.oid, &o->src_index->oid);
-       o->merge_size = len;
+       oidcpy(&o->internal.result.oid, &o->src_index->oid);
+       o->internal.merge_size = len;
        mark_all_ce_unused(o->src_index);
 
-       o->result.fsmonitor_last_update =
+       o->internal.result.fsmonitor_last_update =
                xstrdup_or_null(o->src_index->fsmonitor_last_update);
-       o->result.fsmonitor_has_run_once = o->src_index->fsmonitor_has_run_once;
+       o->internal.result.fsmonitor_has_run_once = o->src_index->fsmonitor_has_run_once;
 
        if (!o->src_index->initialized &&
            !repo->settings.command_requires_full_index &&
-           is_sparse_index_allowed(&o->result, 0))
-               o->result.sparse_index = 1;
+           is_sparse_index_allowed(&o->internal.result, 0))
+               o->internal.result.sparse_index = 1;
 
        /*
         * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
         */
        if (!o->skip_sparse_checkout)
-               mark_new_skip_worktree(o->pl, o->src_index, 0,
+               mark_new_skip_worktree(o->internal.pl, o->src_index, 0,
                                       CE_NEW_SKIP_WORKTREE, o->verbose_update);
 
        if (!dfc)
@@ -1951,7 +1977,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                setup_traverse_info(&info, prefix);
                info.fn = unpack_callback;
                info.data = o;
-               info.show_all_errors = o->show_all_errors;
+               info.show_all_errors = o->internal.show_all_errors;
                info.pathspec = o->pathspec;
 
                if (o->prefix) {
@@ -1992,7 +2018,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        }
        mark_all_ce_unused(o->src_index);
 
-       if (o->trivial_merges_only && o->nontrivial_merge) {
+       if (o->trivial_merges_only && o->internal.nontrivial_merge) {
                ret = unpack_failed(o, "Merge requires file-level merging");
                goto done;
        }
@@ -2003,13 +2029,13 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                 * If they will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
                 * so apply_sparse_checkout() won't attempt to remove it from worktree
                 */
-               mark_new_skip_worktree(o->pl, &o->result,
+               mark_new_skip_worktree(o->internal.pl, &o->internal.result,
                                       CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE,
                                       o->verbose_update);
 
                ret = 0;
-               for (i = 0; i < o->result.cache_nr; i++) {
-                       struct cache_entry *ce = o->result.cache[i];
+               for (i = 0; i < o->internal.result.cache_nr; i++) {
+                       struct cache_entry *ce = o->internal.result.cache[i];
 
                        /*
                         * Entries marked with CE_ADDED in merged_entry() do not have
@@ -2023,7 +2049,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                            verify_absent(ce, WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN, o))
                                ret = 1;
 
-                       if (apply_sparse_checkout(&o->result, ce, o))
+                       if (apply_sparse_checkout(&o->internal.result, ce, o))
                                ret = 1;
                }
                if (ret == 1) {
@@ -2031,45 +2057,47 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                         * Inability to sparsify or de-sparsify individual
                         * paths is not an error, but just a warning.
                         */
-                       if (o->show_all_errors)
+                       if (o->internal.show_all_errors)
                                display_warning_msgs(o);
                        ret = 0;
                }
        }
 
-       ret = check_updates(o, &o->result) ? (-2) : 0;
+       ret = check_updates(o, &o->internal.result) ? (-2) : 0;
        if (o->dst_index) {
-               move_index_extensions(&o->result, o->src_index);
+               move_index_extensions(&o->internal.result, o->src_index);
                if (!ret) {
                        if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
-                               cache_tree_verify(the_repository, &o->result);
-                       if (!cache_tree_fully_valid(o->result.cache_tree))
-                               cache_tree_update(&o->result,
+                               cache_tree_verify(the_repository,
+                                                 &o->internal.result);
+                       if (!o->skip_cache_tree_update &&
+                           !cache_tree_fully_valid(o->internal.result.cache_tree))
+                               cache_tree_update(&o->internal.result,
                                                  WRITE_TREE_SILENT |
                                                  WRITE_TREE_REPAIR);
                }
 
-               o->result.updated_workdir = 1;
+               o->internal.result.updated_workdir = 1;
                discard_index(o->dst_index);
-               *o->dst_index = o->result;
+               *o->dst_index = o->internal.result;
        } else {
-               discard_index(&o->result);
+               discard_index(&o->internal.result);
        }
        o->src_index = NULL;
 
 done:
        if (free_pattern_list)
                clear_pattern_list(&pl);
-       if (o->dir) {
-               dir_clear(o->dir);
-               o->dir = NULL;
+       if (o->internal.dir) {
+               dir_clear(o->internal.dir);
+               o->internal.dir = NULL;
        }
        trace2_region_leave("unpack_trees", "unpack_trees", the_repository);
        trace_performance_leave("unpack_trees");
        return ret;
 
 return_failed:
-       if (o->show_all_errors)
+       if (o->internal.show_all_errors)
                display_error_msgs(o);
        mark_all_ce_unused(o->src_index);
        ret = unpack_failed(o, NULL);
@@ -2084,16 +2112,17 @@ return_failed:
  *
  * CE_NEW_SKIP_WORKTREE is used internally.
  */
-enum update_sparsity_result update_sparsity(struct unpack_trees_options *o)
+enum update_sparsity_result update_sparsity(struct unpack_trees_options *o,
+                                           struct pattern_list *pl)
 {
        enum update_sparsity_result ret = UPDATE_SPARSITY_SUCCESS;
-       struct pattern_list pl;
        int i;
        unsigned old_show_all_errors;
        int free_pattern_list = 0;
 
-       old_show_all_errors = o->show_all_errors;
-       o->show_all_errors = 1;
+       old_show_all_errors = o->internal.show_all_errors;
+       o->internal.show_all_errors = 1;
+       index_state_init(&o->internal.result, o->src_index->repo);
 
        /* Sanity checks */
        if (!o->update || o->index_only || o->skip_sparse_checkout)
@@ -2104,20 +2133,19 @@ enum update_sparsity_result update_sparsity(struct unpack_trees_options *o)
        trace_performance_enter();
 
        /* If we weren't given patterns, use the recorded ones */
-       if (!o->pl) {
-               memset(&pl, 0, sizeof(pl));
+       if (!pl) {
                free_pattern_list = 1;
-               populate_from_existing_patterns(o, &pl);
-               if (o->skip_sparse_checkout)
-                       goto skip_sparse_checkout;
+               pl = xcalloc(1, sizeof(*pl));
+               populate_from_existing_patterns(o, pl);
        }
+       o->internal.pl = pl;
 
        /* Expand sparse directories as needed */
-       expand_index(o->src_index, o->pl);
+       expand_index(o->src_index, o->internal.pl);
 
        /* Set NEW_SKIP_WORKTREE on existing entries. */
        mark_all_ce_unused(o->src_index);
-       mark_new_skip_worktree(o->pl, o->src_index, 0,
+       mark_new_skip_worktree(o->internal.pl, o->src_index, 0,
                               CE_NEW_SKIP_WORKTREE, o->verbose_update);
 
        /* Then loop over entries and update/remove as needed */
@@ -2137,14 +2165,16 @@ enum update_sparsity_result update_sparsity(struct unpack_trees_options *o)
                        ret = UPDATE_SPARSITY_WARNINGS;
        }
 
-skip_sparse_checkout:
        if (check_updates(o, o->src_index))
                ret = UPDATE_SPARSITY_WORKTREE_UPDATE_FAILURES;
 
        display_warning_msgs(o);
-       o->show_all_errors = old_show_all_errors;
-       if (free_pattern_list)
-               clear_pattern_list(&pl);
+       o->internal.show_all_errors = old_show_all_errors;
+       if (free_pattern_list) {
+               clear_pattern_list(pl);
+               free(pl);
+               o->internal.pl = NULL;
+       }
        trace_performance_leave("update_sparsity");
        return ret;
 }
@@ -2239,15 +2269,15 @@ static int verify_uptodate_sparse(const struct cache_entry *ce,
 }
 
 /*
- * TODO: We should actually invalidate o->result, not src_index [1].
+ * TODO: We should actually invalidate o->internal.result, not src_index [1].
  * But since cache tree and untracked cache both are not copied to
- * o->result until unpacking is complete, we invalidate them on
+ * o->internal.result until unpacking is complete, we invalidate them on
  * src_index instead with the assumption that they will be copied to
  * dst_index at the end.
  *
  * [1] src_index->cache_tree is also used in unpack_callback() so if
- * we invalidate o->result, we need to update it to use
- * o->result.cache_tree as well.
+ * we invalidate o->internal.result, we need to update it to use
+ * o->internal.result.cache_tree as well.
  */
 static void invalidate_ce_path(const struct cache_entry *ce,
                               struct unpack_trees_options *o)
@@ -2331,8 +2361,8 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
        pathbuf = xstrfmt("%.*s/", namelen, ce->name);
 
        memset(&d, 0, sizeof(d));
-       if (o->dir)
-               d.exclude_per_dir = o->dir->exclude_per_dir;
+       if (o->internal.dir)
+               setup_standard_excludes(&d);
        i = read_directory(&d, o->src_index, pathbuf, namelen+1, NULL);
        dir_clear(&d);
        free(pathbuf);
@@ -2386,8 +2416,8 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
        if (ignore_case && icase_exists(o, name, len, st))
                return 0;
 
-       if (o->dir &&
-           is_excluded(o->dir, o->src_index, name, &dtype))
+       if (o->internal.dir &&
+           is_excluded(o->internal.dir, o->src_index, name, &dtype))
                /*
                 * ce->name is explicitly excluded, so it is Ok to
                 * overwrite it.
@@ -2415,7 +2445,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
         * delete this path, which is in a subdirectory that
         * is being replaced with a blob.
         */
-       result = index_file_exists(&o->result, name, len, 0);
+       result = index_file_exists(&o->internal.result, name, len, 0);
        if (result) {
                if (result->ce_flags & CE_REMOVE)
                        return 0;
@@ -2516,7 +2546,7 @@ static int merged_entry(const struct cache_entry *ce,
                        struct unpack_trees_options *o)
 {
        int update = CE_UPDATE;
-       struct cache_entry *merge = dup_cache_entry(ce, &o->result);
+       struct cache_entry *merge = dup_cache_entry(ce, &o->internal.result);
 
        if (!old) {
                /*
@@ -2611,7 +2641,7 @@ static int merged_sparse_dir(const struct cache_entry * const *src, int n,
        setup_traverse_info(&info, src[0]->name);
        info.fn = unpack_sparse_callback;
        info.data = o;
-       info.show_all_errors = o->show_all_errors;
+       info.show_all_errors = o->internal.show_all_errors;
        info.pathspec = o->pathspec;
 
        /* Get the tree descriptors of the sparse directory in each of the merging trees */
@@ -2829,7 +2859,7 @@ int threeway_merge(const struct cache_entry * const *stages,
                        return -1;
        }
 
-       o->nontrivial_merge = 1;
+       o->internal.nontrivial_merge = 1;
 
        /* #2, #3, #4, #6, #7, #9, #10, #11. */
        count = 0;
@@ -2870,9 +2900,9 @@ int twoway_merge(const struct cache_entry * const *src,
        const struct cache_entry *oldtree = src[1];
        const struct cache_entry *newtree = src[2];
 
-       if (o->merge_size != 2)
+       if (o->internal.merge_size != 2)
                return error("Cannot do a twoway merge of %d trees",
-                            o->merge_size);
+                            o->internal.merge_size);
 
        if (oldtree == o->df_conflict_entry)
                oldtree = NULL;
@@ -2952,14 +2982,14 @@ int bind_merge(const struct cache_entry * const *src,
        const struct cache_entry *old = src[0];
        const struct cache_entry *a = src[1];
 
-       if (o->merge_size != 1)
+       if (o->internal.merge_size != 1)
                return error("Cannot do a bind merge of %d trees",
-                            o->merge_size);
+                            o->internal.merge_size);
        if (a && old)
                return o->quiet ? -1 :
                        error(ERRORMSG(o, ERROR_BIND_OVERLAP),
-                             super_prefixed(a->name),
-                             super_prefixed(old->name));
+                             super_prefixed(a->name, o->super_prefix),
+                             super_prefixed(old->name, o->super_prefix));
        if (!a)
                return keep_entry(old, o);
        else
@@ -2978,9 +3008,9 @@ int oneway_merge(const struct cache_entry * const *src,
        const struct cache_entry *old = src[0];
        const struct cache_entry *a = src[1];
 
-       if (o->merge_size != 1)
+       if (o->internal.merge_size != 1)
                return error("Cannot do a oneway merge of %d trees",
-                            o->merge_size);
+                            o->internal.merge_size);
 
        if (!a || a == o->df_conflict_entry)
                return deleted_entry(old, old, o);
@@ -3015,12 +3045,12 @@ int stash_worktree_untracked_merge(const struct cache_entry * const *src,
        const struct cache_entry *worktree = src[1];
        const struct cache_entry *untracked = src[2];
 
-       if (o->merge_size != 2)
-               BUG("invalid merge_size: %d", o->merge_size);
+       if (o->internal.merge_size != 2)
+               BUG("invalid merge_size: %d", o->internal.merge_size);
 
        if (worktree && untracked)
                return error(_("worktree and untracked commit have duplicate entries: %s"),
-                            super_prefixed(worktree->name));
+                            super_prefixed(worktree->name, o->super_prefix));
 
        return merged_entry(worktree ? worktree : untracked, NULL, o);
 }
index efb9edfbb2717b4739247ecdd58a104e1d44cfd2..5867e26e17777483f3fc5df0084deb7a4dd135da 100644 (file)
@@ -1,12 +1,13 @@
 #ifndef UNPACK_TREES_H
 #define UNPACK_TREES_H
 
-#include "cache.h"
+#include "convert.h"
+#include "read-cache-ll.h"
 #include "strvec.h"
 #include "string-list.h"
 #include "tree-walk.h"
 
-#define MAX_UNPACK_TREES MAX_TRAVERSE_TREES
+#define MAX_UNPACK_TREES 8
 
 struct cache_entry;
 struct unpack_trees_options;
@@ -59,45 +60,54 @@ struct unpack_trees_options {
                     preserve_ignored,
                     clone,
                     index_only,
-                    nontrivial_merge,
                     trivial_merges_only,
                     verbose_update,
                     aggressive,
                     skip_unmerged,
                     initial_checkout,
                     diff_index_cached,
-                    debug_unpack,
                     skip_sparse_checkout,
                     quiet,
                     exiting_early,
-                    show_all_errors,
-                    dry_run;
+                    dry_run,
+                    skip_cache_tree_update;
        enum unpack_trees_reset_type reset;
        const char *prefix;
-       int cache_bottom;
+       const char *super_prefix;
        struct pathspec *pathspec;
        merge_fn_t fn;
-       const char *msgs[NB_UNPACK_TREES_WARNING_TYPES];
-       struct strvec msgs_to_free;
-       /*
-        * Store error messages in an array, each case
-        * corresponding to a error message type
-        */
-       struct string_list unpack_rejects[NB_UNPACK_TREES_WARNING_TYPES];
 
        int head_idx;
-       int merge_size;
 
-       struct cache_entry *df_conflict_entry;
+       struct cache_entry *df_conflict_entry; /* output only */
        void *unpack_data;
 
        struct index_state *dst_index;
        struct index_state *src_index;
-       struct index_state result;
 
-       struct pattern_list *pl; /* for internal use */
-       struct dir_struct *dir; /* for internal use only */
        struct checkout_metadata meta;
+
+       struct unpack_trees_options_internal {
+               unsigned int nontrivial_merge,
+                            show_all_errors,
+                            debug_unpack; /* used by read-tree debugging */
+
+               int merge_size; /* used by read-tree debugging */
+               int cache_bottom;
+               const char *msgs[NB_UNPACK_TREES_WARNING_TYPES];
+               struct strvec msgs_to_free;
+
+               /*
+                * Store error messages in an array, each case
+                * corresponding to a error message type
+                */
+               struct string_list unpack_rejects[NB_UNPACK_TREES_WARNING_TYPES];
+
+               struct index_state result;
+
+               struct pattern_list *pl;
+               struct dir_struct *dir;
+       } internal;
 };
 
 int unpack_trees(unsigned n, struct tree_desc *t,
@@ -110,7 +120,8 @@ enum update_sparsity_result {
        UPDATE_SPARSITY_WORKTREE_UPDATE_FAILURES = -2
 };
 
-enum update_sparsity_result update_sparsity(struct unpack_trees_options *options);
+enum update_sparsity_result update_sparsity(struct unpack_trees_options *options,
+                                           struct pattern_list *pl);
 
 int verify_uptodate(const struct cache_entry *ce,
                    struct unpack_trees_options *o);
index 0b8311bd685f002ffb38672c670372b7e698de95..2537affa90762228a19d305be749f410d5afb5dc 100644 (file)
@@ -1,17 +1,18 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "sideband.h"
 #include "repository.h"
-#include "object-store.h"
-#include "tag.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
 #include "object.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
-#include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "run-command.h"
 #include "connect.h"
 #include "version.h"
 #include "string-list.h"
 #include "strvec.h"
-#include "prio-queue.h"
+#include "trace2.h"
 #include "protocol.h"
-#include "quote.h"
 #include "upload-pack.h"
-#include "serve.h"
 #include "commit-graph.h"
 #include "commit-reach.h"
 #include "shallow.h"
+#include "write-or-die.h"
+#include "json-writer.h"
 
 /* Remember to update object flag allocation in object.h */
 #define THEY_HAVE      (1u << 11)
@@ -62,6 +63,7 @@ struct upload_pack_data {
        struct object_array have_obj;
        struct oid_array haves;                                 /* v2 only */
        struct string_list wanted_refs;                         /* v2 only */
+       struct strvec hidden_refs;
 
        struct object_array shallows;
        struct string_list deepen_not;
@@ -112,12 +114,14 @@ struct upload_pack_data {
        unsigned allow_ref_in_want : 1;                         /* v2 only */
        unsigned allow_sideband_all : 1;                        /* v2 only */
        unsigned advertise_sid : 1;
+       unsigned sent_capabilities : 1;
 };
 
 static void upload_pack_data_init(struct upload_pack_data *data)
 {
        struct string_list symref = STRING_LIST_INIT_DUP;
        struct string_list wanted_refs = STRING_LIST_INIT_DUP;
+       struct strvec hidden_refs = STRVEC_INIT;
        struct object_array want_obj = OBJECT_ARRAY_INIT;
        struct object_array have_obj = OBJECT_ARRAY_INIT;
        struct oid_array haves = OID_ARRAY_INIT;
@@ -130,6 +134,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
        memset(data, 0, sizeof(*data));
        data->symref = symref;
        data->wanted_refs = wanted_refs;
+       data->hidden_refs = hidden_refs;
        data->want_obj = want_obj;
        data->have_obj = have_obj;
        data->haves = haves;
@@ -151,6 +156,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
 {
        string_list_clear(&data->symref, 1);
        string_list_clear(&data->wanted_refs, 1);
+       strvec_clear(&data->hidden_refs);
        object_array_clear(&data->want_obj);
        object_array_clear(&data->have_obj);
        oid_array_clear(&data->haves);
@@ -495,8 +501,8 @@ static int got_oid(struct upload_pack_data *data,
 {
        if (get_oid_hex(hex, oid))
                die("git upload-pack: expected SHA1 object, got '%s'", hex);
-       if (!has_object_file_with_flags(oid,
-                                       OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
+       if (!repo_has_object_file_with_flags(the_repository, oid,
+                                            OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
                return -1;
        return do_got_oid(data, oid);
 }
@@ -590,11 +596,36 @@ static int get_common_commits(struct upload_pack_data *data,
        }
 }
 
+static int allow_hidden_refs(enum allow_uor allow_uor)
+{
+       if ((allow_uor & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1)
+               return 1;
+       return !(allow_uor & (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
+}
+
+static void for_each_namespaced_ref_1(each_ref_fn fn,
+                                     struct upload_pack_data *data)
+{
+       const char **excludes = NULL;
+       /*
+        * If `data->allow_uor` allows fetching hidden refs, we need to
+        * mark all references (including hidden ones), to check in
+        * `is_our_ref()` below.
+        *
+        * Otherwise, we only care about whether each reference's object
+        * has the OUR_REF bit set or not, so do not need to visit
+        * hidden references.
+        */
+       if (allow_hidden_refs(data->allow_uor))
+               excludes = hidden_refs_to_excludes(&data->hidden_refs);
+
+       for_each_namespaced_ref(excludes, fn, data);
+}
+
+
 static int is_our_ref(struct object *o, enum allow_uor allow_uor)
 {
-       int allow_hidden_ref = (allow_uor &
-                               (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
-       return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
+       return o->flags & ((allow_hidden_refs(allow_uor) ? 0 : HIDDEN_REF) | OUR_REF);
 }
 
 /*
@@ -765,11 +796,12 @@ error:
        for (i = 0; i < data->want_obj.nr; i++) {
                struct object *o = data->want_obj.objects[i].item;
                if (!is_our_ref(o, data->allow_uor)) {
+                       error("git upload-pack: not our ref %s",
+                             oid_to_hex(&o->oid));
                        packet_writer_error(&data->writer,
                                            "upload-pack: not our ref %s",
                                            oid_to_hex(&o->oid));
-                       die("git upload-pack: not our ref %s",
-                           oid_to_hex(&o->oid));
+                       exit(128);
                }
        }
 }
@@ -842,8 +874,8 @@ static void deepen(struct upload_pack_data *data, int depth)
                 * Checking for reachable shallows requires that our refs be
                 * marked with OUR_REF.
                 */
-               head_ref_namespaced(check_ref, NULL);
-               for_each_namespaced_ref(check_ref, NULL);
+               head_ref_namespaced(check_ref, data);
+               for_each_namespaced_ref_1(check_ref, data);
 
                get_reachable_list(data, &reachable_shallows);
                result = get_shallow_commits(&reachable_shallows,
@@ -1059,7 +1091,7 @@ static void receive_needs(struct upload_pack_data *data,
                const char *features;
                struct object_id oid_buf;
                const char *arg;
-               int feature_len;
+               size_t feature_len;
 
                reset_timeout(data->timeout);
                if (packet_reader_read(reader) != PACKET_READ_NORMAL)
@@ -1158,11 +1190,11 @@ static void receive_needs(struct upload_pack_data *data,
 
 /* return non-zero if the ref is hidden, otherwise 0 */
 static int mark_our_ref(const char *refname, const char *refname_full,
-                       const struct object_id *oid)
+                       const struct object_id *oid, const struct strvec *hidden_refs)
 {
        struct object *o = lookup_unknown_object(the_repository, oid);
 
-       if (ref_is_hidden(refname, refname_full)) {
+       if (ref_is_hidden(refname, refname_full, hidden_refs)) {
                o->flags |= HIDDEN_REF;
                return 1;
        }
@@ -1171,11 +1203,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
 }
 
 static int check_ref(const char *refname_full, const struct object_id *oid,
-                    int flag UNUSED, void *cb_data UNUSED)
+                    int flag UNUSED, void *cb_data)
 {
        const char *refname = strip_namespace(refname_full);
+       struct upload_pack_data *data = cb_data;
 
-       mark_our_ref(refname, refname_full, oid);
+       mark_our_ref(refname, refname_full, oid, &data->hidden_refs);
        return 0;
 }
 
@@ -1194,18 +1227,17 @@ static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
                strbuf_addf(buf, " session-id=%s", trace2_session_id());
 }
 
-static int send_ref(const char *refname, const struct object_id *oid,
-                   int flag UNUSED, void *cb_data)
+static void write_v0_ref(struct upload_pack_data *data,
+                       const char *refname, const char *refname_nons,
+                       const struct object_id *oid)
 {
        static const char *capabilities = "multi_ack thin-pack side-band"
                " side-band-64k ofs-delta shallow deepen-since deepen-not"
                " deepen-relative no-progress include-tag multi_ack_detailed";
-       const char *refname_nons = strip_namespace(refname);
        struct object_id peeled;
-       struct upload_pack_data *data = cb_data;
 
-       if (mark_our_ref(refname_nons, refname, oid))
-               return 0;
+       if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
+               return;
 
        if (capabilities) {
                struct strbuf symref_info = STRBUF_INIT;
@@ -1228,12 +1260,20 @@ static int send_ref(const char *refname, const struct object_id *oid,
                             git_user_agent_sanitized());
                strbuf_release(&symref_info);
                strbuf_release(&session_id);
+               data->sent_capabilities = 1;
        } else {
                packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
        }
        capabilities = NULL;
        if (!peel_iterated_oid(oid, &peeled))
                packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
+       return;
+}
+
+static int send_ref(const char *refname, const struct object_id *oid,
+                   int flag UNUSED, void *cb_data)
+{
+       write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
        return 0;
 }
 
@@ -1255,7 +1295,8 @@ static int find_symref(const char *refname,
 }
 
 static int parse_object_filter_config(const char *var, const char *value,
-                                      struct upload_pack_data *data)
+                                     const struct key_value_info *kvi,
+                                     struct upload_pack_data *data)
 {
        struct strbuf buf = STRBUF_INIT;
        const char *sub, *key;
@@ -1282,14 +1323,17 @@ static int parse_object_filter_config(const char *var, const char *value,
                }
                string_list_insert(&data->allowed_filters, buf.buf)->util =
                        (void *)(intptr_t)1;
-               data->tree_filter_max_depth = git_config_ulong(var, value);
+               data->tree_filter_max_depth = git_config_ulong(var, value,
+                                                              kvi);
        }
 
        strbuf_release(&buf);
        return 0;
 }
 
-static int upload_pack_config(const char *var, const char *value, void *cb_data)
+static int upload_pack_config(const char *var, const char *value,
+                             const struct config_context *ctx,
+                             void *cb_data)
 {
        struct upload_pack_data *data = cb_data;
 
@@ -1309,7 +1353,7 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)
                else
                        data->allow_uor &= ~ALLOW_ANY_SHA1;
        } else if (!strcmp("uploadpack.keepalive", var)) {
-               data->keepalive = git_config_int(var, value);
+               data->keepalive = git_config_int(var, value, ctx->kvi);
                if (!data->keepalive)
                        data->keepalive = -1;
        } else if (!strcmp("uploadpack.allowfilter", var)) {
@@ -1324,13 +1368,15 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)
                data->advertise_sid = git_config_bool(var, value);
        }
 
-       if (parse_object_filter_config(var, value, data) < 0)
+       if (parse_object_filter_config(var, value, ctx->kvi, data) < 0)
                return -1;
 
-       return parse_hide_refs_config(var, value, "uploadpack");
+       return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
 }
 
-static int upload_pack_protected_config(const char *var, const char *value, void *cb_data)
+static int upload_pack_protected_config(const char *var, const char *value,
+                                       const struct config_context *ctx UNUSED,
+                                       void *cb_data)
 {
        struct upload_pack_data *data = cb_data;
 
@@ -1366,7 +1412,11 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
                if (advertise_refs)
                        data.no_done = 1;
                head_ref_namespaced(send_ref, &data);
-               for_each_namespaced_ref(send_ref, &data);
+               for_each_namespaced_ref_1(send_ref, &data);
+               if (!data.sent_capabilities) {
+                       const char *refname = "capabilities^{}";
+                       write_v0_ref(&data, refname, refname, null_oid());
+               }
                /*
                 * fflush stdout before calling advertise_shallow_grafts because send_ref
                 * uses stdio.
@@ -1375,8 +1425,8 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
                advertise_shallow_grafts(1);
                packet_flush(1);
        } else {
-               head_ref_namespaced(check_ref, NULL);
-               for_each_namespaced_ref(check_ref, NULL);
+               head_ref_namespaced(check_ref, &data);
+               for_each_namespaced_ref_1(check_ref, &data);
        }
 
        if (!advertise_refs) {
@@ -1441,6 +1491,7 @@ static int parse_want(struct packet_writer *writer, const char *line,
 
 static int parse_want_ref(struct packet_writer *writer, const char *line,
                          struct string_list *wanted_refs,
+                         struct strvec *hidden_refs,
                          struct object_array *want_obj)
 {
        const char *refname_nons;
@@ -1451,7 +1502,7 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
                struct strbuf refname = STRBUF_INIT;
 
                strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
-               if (ref_is_hidden(refname_nons, refname.buf) ||
+               if (ref_is_hidden(refname_nons, refname.buf, hidden_refs) ||
                    read_ref(refname.buf, &oid)) {
                        packet_writer_error(writer, "unknown ref %s", refname_nons);
                        die("unknown ref %s", refname_nons);
@@ -1496,6 +1547,30 @@ static int parse_have(const char *line, struct oid_array *haves)
        return 0;
 }
 
+static void trace2_fetch_info(struct upload_pack_data *data)
+{
+       struct json_writer jw = JSON_WRITER_INIT;
+
+       jw_object_begin(&jw, 0);
+       jw_object_intmax(&jw, "haves", data->haves.nr);
+       jw_object_intmax(&jw, "wants", data->want_obj.nr);
+       jw_object_intmax(&jw, "want-refs", data->wanted_refs.nr);
+       jw_object_intmax(&jw, "depth", data->depth);
+       jw_object_intmax(&jw, "shallows", data->shallows.nr);
+       jw_object_bool(&jw, "deepen-since", data->deepen_since);
+       jw_object_intmax(&jw, "deepen-not", data->deepen_not.nr);
+       jw_object_bool(&jw, "deepen-relative", data->deepen_relative);
+       if (data->filter_options.choice)
+               jw_object_string(&jw, "filter", list_object_filter_config_name(data->filter_options.choice));
+       else
+               jw_object_null(&jw, "filter");
+       jw_end(&jw);
+
+       trace2_data_json("upload-pack", the_repository, "fetch-info", &jw);
+
+       jw_release(&jw);
+}
+
 static void process_args(struct packet_reader *request,
                         struct upload_pack_data *data)
 {
@@ -1508,7 +1583,7 @@ static void process_args(struct packet_reader *request,
                        continue;
                if (data->allow_ref_in_want &&
                    parse_want_ref(&data->writer, arg, &data->wanted_refs,
-                                  &data->want_obj))
+                                  &data->hidden_refs, &data->want_obj))
                        continue;
                /* process have line */
                if (parse_have(arg, &data->haves))
@@ -1584,6 +1659,9 @@ static void process_args(struct packet_reader *request,
 
        if (request->status != PACKET_READ_FLUSH)
                die(_("expected flush after fetch arguments"));
+
+       if (trace2_is_enabled())
+               trace2_fetch_info(data);
 }
 
 static int process_haves(struct upload_pack_data *data, struct oid_array *common)
@@ -1594,8 +1672,8 @@ static int process_haves(struct upload_pack_data *data, struct oid_array *common
        for (i = 0; i < data->haves.nr; i++) {
                const struct object_id *oid = &data->haves.oid[i];
 
-               if (!has_object_file_with_flags(oid,
-                                               OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
+               if (!repo_has_object_file_with_flags(the_repository, oid,
+                                                    OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
                        continue;
 
                oid_array_append(common, oid);
@@ -1693,7 +1771,7 @@ enum fetch_state {
        FETCH_DONE,
 };
 
-int upload_pack_v2(struct repository *r, struct packet_reader *request)
+int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
 {
        enum fetch_state state = FETCH_PROCESS_ARGS;
        struct upload_pack_data data;
@@ -1769,26 +1847,26 @@ int upload_pack_advertise(struct repository *r,
 
                strbuf_addstr(value, "shallow wait-for-done");
 
-               if (!repo_config_get_bool(the_repository,
+               if (!repo_config_get_bool(r,
                                         "uploadpack.allowfilter",
                                         &allow_filter_value) &&
                    allow_filter_value)
                        strbuf_addstr(value, " filter");
 
-               if (!repo_config_get_bool(the_repository,
+               if (!repo_config_get_bool(r,
                                         "uploadpack.allowrefinwant",
                                         &allow_ref_in_want) &&
                    allow_ref_in_want)
                        strbuf_addstr(value, " ref-in-want");
 
                if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
-                   (!repo_config_get_bool(the_repository,
+                   (!repo_config_get_bool(r,
                                           "uploadpack.allowsidebandall",
                                           &allow_sideband_all_value) &&
                     allow_sideband_all_value))
                        strbuf_addstr(value, " sideband-all");
 
-               if (!repo_config_get_string(the_repository,
+               if (!repo_config_get_string(r,
                                            "uploadpack.blobpackfileuri",
                                            &str) &&
                    str) {
diff --git a/url.c b/url.c
index e04bd60b6bead493e3236949b5b4a837c729c146..282b12495ae7d4af9db51a76017af6065b924d3a 100644 (file)
--- a/url.c
+++ b/url.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hex-ll.h"
+#include "strbuf.h"
 #include "url.h"
 
 int is_urlschemechar(int first_flag, int ch)
index b615adc923ae019b756e25a1cdcfb251333e69ff..1d0254abacbc3f393062351bc55ccd9bef32051a 100644 (file)
@@ -1,4 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex-ll.h"
+#include "strbuf.h"
 #include "urlmatch.h"
 
 #define URL_ALPHA "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@@ -209,7 +212,7 @@ static char *url_normalize_1(const char *url, struct url_info *out_info, char al
         */
        if (!url_len || strchr(":/?#", *url)) {
                /* Missing host invalid for all URL schemes except file */
-               if (strncmp(norm.buf, "file:", 5)) {
+               if (!starts_with(norm.buf, "file:")) {
                        if (out_info) {
                                out_info->url = NULL;
                                out_info->err = _("missing host and scheme is not 'file:'");
@@ -268,11 +271,11 @@ static char *url_normalize_1(const char *url, struct url_info *out_info, char al
                if (url == slash_ptr) {
                        /* Skip ":" port with no number, it's same as default */
                } else if (slash_ptr - url == 2 &&
-                          !strncmp(norm.buf, "http:", 5) &&
+                          starts_with(norm.buf, "http:") &&
                           !strncmp(url, "80", 2)) {
                        /* Skip http :80 as it's the default */
                } else if (slash_ptr - url == 3 &&
-                          !strncmp(norm.buf, "https:", 6) &&
+                          starts_with(norm.buf, "https:") &&
                           !strncmp(url, "443", 3)) {
                        /* Skip https :443 as it's the default */
                } else {
@@ -548,7 +551,8 @@ static int cmp_matches(const struct urlmatch_item *a,
        return 0;
 }
 
-int urlmatch_config_entry(const char *var, const char *value, void *cb)
+int urlmatch_config_entry(const char *var, const char *value,
+                         const struct config_context *ctx, void *cb)
 {
        struct string_list_item *item;
        struct urlmatch_config *collect = cb;
@@ -562,7 +566,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
 
        if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
                if (collect->cascade_fn)
-                       return collect->cascade_fn(var, value, cb);
+                       return collect->cascade_fn(var, value, ctx, cb);
                return 0; /* not interested */
        }
        dot = strrchr(key, '.');
@@ -606,7 +610,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
        strbuf_addstr(&synthkey, collect->section);
        strbuf_addch(&synthkey, '.');
        strbuf_addstr(&synthkey, key);
-       retval = collect->collect_fn(synthkey.buf, value, collect->cb);
+       retval = collect->collect_fn(synthkey.buf, value, ctx, collect->cb);
 
        strbuf_release(&synthkey);
        return retval;
index 9f40b00bfb82b1f32bf75e60538be145bbe9d116..5ba85cea1396dd10d18849eb9e6de85c9750403a 100644 (file)
@@ -2,6 +2,7 @@
 #define URL_MATCH_H
 
 #include "string-list.h"
+#include "config.h"
 
 struct url_info {
        /* normalized url on success, must be freed, otherwise NULL */
@@ -48,8 +49,8 @@ struct urlmatch_config {
        const char *key;
 
        void *cb;
-       int (*collect_fn)(const char *var, const char *value, void *cb);
-       int (*cascade_fn)(const char *var, const char *value, void *cb);
+       config_fn_t collect_fn;
+       config_fn_t cascade_fn;
        /*
         * Compare the two matches, the one just discovered and the existing
         * best match and return a negative value if the found item is to be
@@ -70,7 +71,8 @@ struct urlmatch_config {
        .vars = STRING_LIST_INIT_DUP, \
 }
 
-int urlmatch_config_entry(const char *var, const char *value, void *cb);
+int urlmatch_config_entry(const char *var, const char *value,
+                         const struct config_context *ctx, void *cb);
 void urlmatch_config_release(struct urlmatch_config *config);
 
 #endif /* URL_MATCH_H */
diff --git a/usage.c b/usage.c
index 5a7c6c346c14ca1fb03b99b8ee36574226cf3fd4..09f0ed509b79c5a162f06a7f4cb5c5296d36be56 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -4,7 +4,8 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "git-compat-util.h"
-#include "cache.h"
+#include "gettext.h"
+#include "trace2.h"
 
 static void vreportf(const char *prefix, const char *err, va_list params)
 {
index 151d9a52784b1f4a149913d38739c91e389d95d6..e399543823bdf2c0c8f24fb87f7622ac4d8a366b 100644 (file)
@@ -1,7 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "userdiff.h"
 #include "attr.h"
+#include "strbuf.h"
 
 static struct userdiff_driver *drivers;
 static int ndrivers;
@@ -15,6 +16,7 @@ static int drivers_alloc;
                .cflags = REG_EXTENDED, \
        }, \
        .word_regex = wrx "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+", \
+       .word_regex_multi_byte = wrx "|[^[:space:]]", \
 }
 #define IPATTERN(lang, rx, wrx) { \
        .name = lang, \
@@ -24,6 +26,7 @@ static int drivers_alloc;
                .cflags = REG_EXTENDED | REG_ICASE, \
        }, \
        .word_regex = wrx "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+", \
+       .word_regex_multi_byte = wrx "|[^[:space:]]", \
 }
 
 /*
@@ -170,8 +173,8 @@ PATTERNS("html",
         "[^<>= \t]+"),
 PATTERNS("java",
         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
-        /* Class, enum, and interface declarations */
-        "^[ \t]*(([a-z]+[ \t]+)*(class|enum|interface)[ \t]+[A-Za-z][A-Za-z0-9_$]*[ \t]+.*)$\n"
+        /* Class, enum, interface, and record declarations */
+        "^[ \t]*(([a-z-]+[ \t]+)*(class|enum|interface|record)[ \t]+.*)$\n"
         /* Method definitions; note that constructor signatures are not */
         /* matched because they are indistinguishable from method calls. */
         "^[ \t]*(([A-Za-z_<>&][][?&<>.,A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
@@ -292,8 +295,8 @@ PATTERNS("scheme",
         /* All other words should be delimited by spaces or parentheses */
         "|([^][)(}{[ \t])+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
-        "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
-{ "default", NULL, -1, { NULL, 0 } },
+        "\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
+{ "default", NULL, NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
 #undef IPATTERN
@@ -315,7 +318,8 @@ struct find_by_namelen_data {
 };
 
 static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
-                                      enum userdiff_driver_type type, void *priv)
+                                      enum userdiff_driver_type type UNUSED,
+                                      void *priv)
 {
        struct find_by_namelen_data *cb_data = priv;
 
@@ -327,6 +331,25 @@ static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
        return 0;
 }
 
+static int regexec_supports_multi_byte_chars(void)
+{
+       static const char not_space[] = "[^[:space:]]";
+       static const char utf8_multi_byte_char[] = "\xc2\xa3";
+       regex_t re;
+       regmatch_t match;
+       static int result = -1;
+
+       if (result != -1)
+               return result;
+       if (regcomp(&re, not_space, REG_EXTENDED))
+               BUG("invalid regular expression: %s", not_space);
+       result = !regexec(&re, utf8_multi_byte_char, 1, &match, 0) &&
+               match.rm_so == 0 &&
+               match.rm_eo == strlen(utf8_multi_byte_char);
+       regfree(&re);
+       return result;
+}
+
 static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t len)
 {
        struct find_by_namelen_data udcbdata = {
@@ -393,6 +416,8 @@ int userdiff_config(const char *k, const char *v)
                return parse_bool(&drv->textconv_want_cache, k, v);
        if (!strcmp(type, "wordregex"))
                return git_config_string(&drv->word_regex, k, v);
+       if (!strcmp(type, "algorithm"))
+               return git_config_string(&drv->algorithm, k, v);
 
        return 0;
 }
@@ -400,7 +425,13 @@ int userdiff_config(const char *k, const char *v)
 struct userdiff_driver *userdiff_find_by_name(const char *name)
 {
        int len = strlen(name);
-       return userdiff_find_by_namelen(name, len);
+       struct userdiff_driver *driver = userdiff_find_by_namelen(name, len);
+       if (driver && driver->word_regex_multi_byte) {
+               if (regexec_supports_multi_byte_chars())
+                       driver->word_regex = driver->word_regex_multi_byte;
+               driver->word_regex_multi_byte = NULL;
+       }
+       return driver;
 }
 
 struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
index aee91bc77e6d592021d8597da7fce0123c779a32..d726804c3e5ee8879ff41d5a424569f9e1b10e3d 100644 (file)
@@ -14,9 +14,11 @@ struct userdiff_funcname {
 struct userdiff_driver {
        const char *name;
        const char *external;
+       const char *algorithm;
        int binary;
        struct userdiff_funcname funcname;
        const char *word_regex;
+       const char *word_regex_multi_byte;
        const char *textconv;
        struct notes_cache *textconv_cache;
        int textconv_want_cache;
diff --git a/utf8.c b/utf8.c
index 6a0dd25b0fe414764f89ae48384f68c959dcafe3..6bfaefa28ebbbfd145b30b58fe0e937ea921ec91 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -2,7 +2,7 @@
 #include "strbuf.h"
 #include "utf8.h"
 
-/* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
+/* This code is originally from https://www.cl.cam.ac.uk/~mgk25/ucs/ */
 
 static const char utf16_be_bom[] = {'\xFE', '\xFF'};
 static const char utf16_le_bom[] = {'\xFF', '\xFE'};
diff --git a/utf8.h b/utf8.h
index b68efef6f4398dae9617e6c5eaa81b0ddcebcb6e..35df76086a6c91a414190f7af8d05f0f0042ecda 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -83,7 +83,7 @@ void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int wid
  * BOM must not be used [1]. The same applies for the UTF-32 equivalents.
  * The function returns true if this rule is violated.
  *
- * [1] http://unicode.org/faq/utf_bom.html#bom10
+ * [1] https://unicode.org/faq/utf_bom.html#bom10
  */
 int has_prohibited_utf_bom(const char *enc, const char *data, size_t len);
 
@@ -99,8 +99,8 @@ int has_prohibited_utf_bom(const char *enc, const char *data, size_t len);
  * Therefore, strictly requiring a BOM seems to be the safest option for
  * content in Git.
  *
- * [1] http://unicode.org/faq/utf_bom.html#gen6
- * [2] http://www.unicode.org/versions/Unicode10.0.0/ch03.pdf
+ * [1] https://unicode.org/faq/utf_bom.html#gen6
+ * [2] https://www.unicode.org/versions/Unicode10.0.0/ch03.pdf
  *     Section 3.10, D98, page 132
  * [3] https://encoding.spec.whatwg.org/#utf-16le
  */
index 069ee94a4d79422ea659a7ebe3923662f0626afa..45e676cbca6314d1b65c11e76f754898f060dfda 100644 (file)
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "strbuf.h"
 #include "string-list.h"
+#include "versioncmp.h"
 
 /*
  * versioncmp(): copied from string/strverscmp.c in glibc commit
@@ -160,15 +162,21 @@ int versioncmp(const char *s1, const char *s2)
        }
 
        if (!initialized) {
-               const struct string_list *deprecated_prereleases;
+               const char *const newk = "versionsort.suffix";
+               const char *const oldk = "versionsort.prereleasesuffix";
+               const struct string_list *newl;
+               const struct string_list *oldl;
+               int new = git_config_get_string_multi(newk, &newl);
+               int old = git_config_get_string_multi(oldk, &oldl);
+
+               if (!new && !old)
+                       warning("ignoring %s because %s is set", oldk, newk);
+               if (!new)
+                       prereleases = newl;
+               else if (!old)
+                       prereleases = oldl;
+
                initialized = 1;
-               prereleases = git_config_get_value_multi("versionsort.suffix");
-               deprecated_prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
-               if (prereleases) {
-                       if (deprecated_prereleases)
-                               warning("ignoring versionsort.prereleasesuffix because versionsort.suffix is set");
-               } else
-                       prereleases = deprecated_prereleases;
        }
        if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1,
                                            &diff))
diff --git a/versioncmp.h b/versioncmp.h
new file mode 100644 (file)
index 0000000..879b510
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef VERSIONCMP_H
+#define VERSIONCMP_H
+
+int versioncmp(const char *s1, const char *s2);
+
+#endif /* VERSIONCMP_H */
index 99d0e0eae047410660f0bf3b0d3f487d45c1134d..65002a7220adc2e60aafe6a29555df1cee167ad0 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -1,8 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
 #include "walker.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "commit.h"
+#include "strbuf.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "tag.h"
@@ -79,7 +82,7 @@ static int process_commit(struct walker *walker, struct commit *commit)
 {
        struct commit_list *parents;
 
-       if (parse_commit(commit))
+       if (repo_parse_commit(the_repository, commit))
                return -1;
 
        while (complete && complete->item->date >= commit->date) {
@@ -93,7 +96,7 @@ static int process_commit(struct walker *walker, struct commit *commit)
 
        walker_say(walker, "walk %s\n", oid_to_hex(&commit->object.oid));
 
-       if (process(walker, &get_commit_tree(commit)->object))
+       if (process(walker, &repo_get_commit_tree(the_repository, commit)->object))
                return -1;
 
        for (parents = commit->parents; parents; parents = parents->next) {
@@ -145,7 +148,7 @@ static int process(struct walker *walker, struct object *obj)
                return 0;
        obj->flags |= SEEN;
 
-       if (has_object_file(&obj->oid)) {
+       if (repo_has_object_file(the_repository, &obj->oid)) {
                /* We already have it, so we should scan it now. */
                obj->flags |= TO_SCAN;
        }
index 7e5a7ea1eaa9d9d49219537b70256f1d947633ea..8ea29141bd7c5233a25c89aa55ceeafcf7e7529d 100644 (file)
@@ -9,11 +9,15 @@
 **  work differently than '*', and to fix the character-class code.
 */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "wildmatch.h"
 
 typedef unsigned char uchar;
 
+/* Internal return values */
+#define WM_ABORT_ALL -1
+#define WM_ABORT_TO_STARSTAR -2
+
 /* What character marks an inverted character class? */
 #define NEGATE_CLASS   '!'
 #define NEGATE_CLASS2  '^'
@@ -83,12 +87,12 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                        continue;
                case '*':
                        if (*++p == '*') {
-                               const uchar *prev_p = p - 2;
+                               const uchar *prev_p = p;
                                while (*++p == '*') {}
                                if (!(flags & WM_PATHNAME))
                                        /* without WM_PATHNAME, '*' == '**' */
                                        match_slash = 1;
-                               else if ((prev_p < pattern || *prev_p == '/') &&
+                               else if ((prev_p - pattern < 2 || *(prev_p - 2) == '/') &&
                                    (*p == '\0' || *p == '/' ||
                                     (p[0] == '\\' && p[1] == '/'))) {
                                        /*
@@ -114,7 +118,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                 * only if there are no more slash characters. */
                                if (!match_slash) {
                                        if (strchr((char *)text, '/'))
-                                               return WM_NOMATCH;
+                                               return WM_ABORT_TO_STARSTAR;
                                }
                                return WM_MATCH;
                        } else if (!match_slash && *p == '/') {
@@ -125,7 +129,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                 */
                                const char *slash = strchr((char*)text, '/');
                                if (!slash)
-                                       return WM_NOMATCH;
+                                       return WM_ABORT_ALL;
                                text = (const uchar*)slash;
                                /* the slash is consumed by the top-level for loop */
                                break;
@@ -153,8 +157,12 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                                        break;
                                                text++;
                                        }
-                                       if (t_ch != p_ch)
-                                               return WM_NOMATCH;
+                                       if (t_ch != p_ch) {
+                                               if (match_slash)
+                                                       return WM_ABORT_ALL;
+                                               else
+                                                       return WM_ABORT_TO_STARSTAR;
+                                       }
                                }
                                if ((matched = dowild(p, text, flags)) != WM_NOMATCH) {
                                        if (!match_slash || matched != WM_ABORT_TO_STARSTAR)
@@ -274,5 +282,6 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
 /* Match the "pattern" against the "text" string. */
 int wildmatch(const char *pattern, const char *text, unsigned int flags)
 {
-       return dowild((const uchar*)pattern, (const uchar*)text, flags);
+       int res = dowild((const uchar*)pattern, (const uchar*)text, flags);
+       return res == WM_MATCH ? WM_MATCH : WM_NOMATCH;
 }
index 599369629824ec5f2bbb8e8affb19e8cc9058cc3..0c890cb56ba303db40a4c4f3244cc4a672303ac1 100644 (file)
@@ -6,8 +6,6 @@
 
 #define WM_NOMATCH 1
 #define WM_MATCH 0
-#define WM_ABORT_ALL -1
-#define WM_ABORT_TO_STARSTAR -2
 
 int wildmatch(const char *pattern, const char *text, unsigned int flags);
 #endif
index 257ba4cf1ee5b71de8be0bff85511415f6600347..dbc670d369588caedafa646401b037675d4f05af 100644 (file)
@@ -1,6 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "path.h"
 #include "repository.h"
 #include "refs.h"
+#include "setup.h"
 #include "strbuf.h"
 #include "worktree.h"
 #include "dir.h"
@@ -46,7 +51,7 @@ static void add_head_info(struct worktree *wt)
 /**
  * get the main worktree
  */
-static struct worktree *get_main_worktree(void)
+static struct worktree *get_main_worktree(int skip_reading_head)
 {
        struct worktree *worktree = NULL;
        struct strbuf worktree_path = STRBUF_INIT;
@@ -65,11 +70,13 @@ static struct worktree *get_main_worktree(void)
         */
        worktree->is_bare = (is_bare_repository_cfg == 1) ||
                is_bare_repository();
-       add_head_info(worktree);
+       if (!skip_reading_head)
+               add_head_info(worktree);
        return worktree;
 }
 
-static struct worktree *get_linked_worktree(const char *id)
+static struct worktree *get_linked_worktree(const char *id,
+                                           int skip_reading_head)
 {
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
@@ -88,7 +95,8 @@ static struct worktree *get_linked_worktree(const char *id)
        CALLOC_ARRAY(worktree, 1);
        worktree->path = strbuf_detach(&worktree_path, NULL);
        worktree->id = xstrdup(id);
-       add_head_info(worktree);
+       if (!skip_reading_head)
+               add_head_info(worktree);
 
 done:
        strbuf_release(&path);
@@ -113,7 +121,14 @@ static void mark_current_worktree(struct worktree **worktrees)
        free(git_dir);
 }
 
-struct worktree **get_worktrees(void)
+/*
+ * NEEDSWORK: This function exists so that we can look up metadata of a
+ * worktree without trying to access any of its internals like the refdb. It
+ * would be preferable to instead have a corruption-tolerant function for
+ * retrieving worktree metadata that could be used when the worktree is known
+ * to not be in a healthy state, e.g. when creating or repairing it.
+ */
+static struct worktree **get_worktrees_internal(int skip_reading_head)
 {
        struct worktree **list = NULL;
        struct strbuf path = STRBUF_INIT;
@@ -123,7 +138,7 @@ struct worktree **get_worktrees(void)
 
        ALLOC_ARRAY(list, alloc);
 
-       list[counter++] = get_main_worktree();
+       list[counter++] = get_main_worktree(skip_reading_head);
 
        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
        dir = opendir(path.buf);
@@ -132,7 +147,7 @@ struct worktree **get_worktrees(void)
                while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
                        struct worktree *linked = NULL;
 
-                       if ((linked = get_linked_worktree(d->d_name))) {
+                       if ((linked = get_linked_worktree(d->d_name, skip_reading_head))) {
                                ALLOC_GROW(list, counter + 1, alloc);
                                list[counter++] = linked;
                        }
@@ -146,6 +161,11 @@ struct worktree **get_worktrees(void)
        return list;
 }
 
+struct worktree **get_worktrees(void)
+{
+       return get_worktrees_internal(0);
+}
+
 const char *get_worktree_git_dir(const struct worktree *wt)
 {
        if (!wt)
@@ -390,9 +410,9 @@ int is_worktree_being_bisected(const struct worktree *wt,
 
        memset(&state, 0, sizeof(state));
        found_bisect = wt_status_check_bisect(wt, &state) &&
-                      state.branch &&
+                      state.bisecting_from &&
                       skip_prefix(target, "refs/heads/", &target) &&
-                      !strcmp(state.branch, target);
+                      !strcmp(state.bisecting_from, target);
        wt_status_state_free_buffers(&state);
        return found_bisect;
 }
@@ -403,44 +423,43 @@ int is_worktree_being_bisected(const struct worktree *wt,
  * bisect). New commands that do similar things should update this
  * function as well.
  */
-const struct worktree *find_shared_symref(struct worktree **worktrees,
-                                         const char *symref,
-                                         const char *target)
+int is_shared_symref(const struct worktree *wt, const char *symref,
+                    const char *target)
 {
-       const struct worktree *existing = NULL;
-       int i = 0;
+       const char *symref_target;
+       struct ref_store *refs;
+       int flags;
 
-       for (i = 0; worktrees[i]; i++) {
-               struct worktree *wt = worktrees[i];
-               const char *symref_target;
-               struct ref_store *refs;
-               int flags;
+       if (wt->is_bare)
+               return 0;
 
-               if (wt->is_bare)
-                       continue;
+       if (wt->is_detached && !strcmp(symref, "HEAD")) {
+               if (is_worktree_being_rebased(wt, target))
+                       return 1;
+               if (is_worktree_being_bisected(wt, target))
+                       return 1;
+       }
 
-               if (wt->is_detached && !strcmp(symref, "HEAD")) {
-                       if (is_worktree_being_rebased(wt, target)) {
-                               existing = wt;
-                               break;
-                       }
-                       if (is_worktree_being_bisected(wt, target)) {
-                               existing = wt;
-                               break;
-                       }
-               }
+       refs = get_worktree_ref_store(wt);
+       symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
+                                               NULL, &flags);
+       if ((flags & REF_ISSYMREF) &&
+           symref_target && !strcmp(symref_target, target))
+               return 1;
 
-               refs = get_worktree_ref_store(wt);
-               symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
-                                                       NULL, &flags);
-               if ((flags & REF_ISSYMREF) &&
-                   symref_target && !strcmp(symref_target, target)) {
-                       existing = wt;
-                       break;
-               }
-       }
+       return 0;
+}
+
+const struct worktree *find_shared_symref(struct worktree **worktrees,
+                                         const char *symref,
+                                         const char *target)
+{
+
+       for (int i = 0; worktrees[i]; i++)
+               if (is_shared_symref(worktrees[i], symref, target))
+                       return worktrees[i];
 
-       return existing;
+       return NULL;
 }
 
 int submodule_uses_worktrees(const char *path)
@@ -489,62 +508,17 @@ int submodule_uses_worktrees(const char *path)
        return ret;
 }
 
-int parse_worktree_ref(const char *worktree_ref, const char **name,
-                      int *name_length, const char **ref)
-{
-       if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
-               if (!*worktree_ref)
-                       return -1;
-               if (name)
-                       *name = NULL;
-               if (name_length)
-                       *name_length = 0;
-               if (ref)
-                       *ref = worktree_ref;
-               return 0;
-       }
-       if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
-               const char *slash = strchr(worktree_ref, '/');
-
-               if (!slash || slash == worktree_ref || !slash[1])
-                       return -1;
-               if (name)
-                       *name = worktree_ref;
-               if (name_length)
-                       *name_length = slash - worktree_ref;
-               if (ref)
-                       *ref = slash + 1;
-               return 0;
-       }
-       return -1;
-}
-
 void strbuf_worktree_ref(const struct worktree *wt,
                         struct strbuf *sb,
                         const char *refname)
 {
-       switch (ref_type(refname)) {
-       case REF_TYPE_PSEUDOREF:
-       case REF_TYPE_PER_WORKTREE:
-               if (wt && !wt->is_current) {
-                       if (is_main_worktree(wt))
-                               strbuf_addstr(sb, "main-worktree/");
-                       else
-                               strbuf_addf(sb, "worktrees/%s/", wt->id);
-               }
-               break;
-
-       case REF_TYPE_MAIN_PSEUDOREF:
-       case REF_TYPE_OTHER_PSEUDOREF:
-               break;
-
-       case REF_TYPE_NORMAL:
-               /*
-                * For shared refs, don't prefix worktrees/ or
-                * main-worktree/. It's not necessary and
-                * files-backend.c can't handle it anyway.
-                */
-               break;
+       if (parse_worktree_ref(refname, NULL, NULL, NULL) ==
+                   REF_WORKTREE_CURRENT &&
+           wt && !wt->is_current) {
+               if (is_main_worktree(wt))
+                       strbuf_addstr(sb, "main-worktree/");
+               else
+                       strbuf_addf(sb, "worktrees/%s/", wt->id);
        }
        strbuf_addstr(sb, refname);
 }
@@ -622,15 +596,17 @@ static void repair_gitfile(struct worktree *wt,
        strbuf_release(&dotgit);
 }
 
-static void repair_noop(int iserr, const char *path, const char *msg,
-                       void *cb_data)
+static void repair_noop(int iserr UNUSED,
+                       const char *path UNUSED,
+                       const char *msg UNUSED,
+                       void *cb_data UNUSED)
 {
        /* nothing */
 }
 
 void repair_worktrees(worktree_repair_fn fn, void *cb_data)
 {
-       struct worktree **worktrees = get_worktrees();
+       struct worktree **worktrees = get_worktrees_internal(1);
        struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
 
        if (!fn)
@@ -846,7 +822,7 @@ int init_worktree_config(struct repository *r)
         * If the extension is already enabled, then we can skip the
         * upgrade process.
         */
-       if (repository_format_worktree_config)
+       if (r->repository_format_worktree_config)
                return 0;
        if ((res = git_config_set_gently("extensions.worktreeConfig", "true")))
                return error(_("failed to set extensions.worktreeConfig setting"));
@@ -875,7 +851,7 @@ int init_worktree_config(struct repository *r)
         * Relocate that value to avoid breaking all worktrees with this
         * upgrade to worktree config.
         */
-       if (!git_configset_get_value(&cs, "core.worktree", &core_worktree)) {
+       if (!git_configset_get_value(&cs, "core.worktree", &core_worktree, NULL)) {
                if ((res = move_config_setting("core.worktree", core_worktree,
                                               common_config_file,
                                               main_worktree_file)))
@@ -886,7 +862,7 @@ int init_worktree_config(struct repository *r)
         * Ensure that we use worktree config for the remaining lifetime
         * of the current process.
         */
-       repository_format_worktree_config = 1;
+       r->repository_format_worktree_config = 1;
 
 cleanup:
        git_configset_clear(&cs);
index e9e839926b0b83a605acdeb5b82c2e01f7104f3f..ce45b66de9e82d4d619b6f74e0e77c8bd5ab3f53 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef WORKTREE_H
 #define WORKTREE_H
 
-#include "cache.h"
 #include "refs.h"
 
 struct strbuf;
@@ -149,6 +148,12 @@ const struct worktree *find_shared_symref(struct worktree **worktrees,
                                          const char *symref,
                                          const char *target);
 
+/*
+ * Returns true if a symref points to a ref in a worktree.
+ */
+int is_shared_symref(const struct worktree *wt,
+                    const char *symref, const char *target);
+
 /*
  * Similar to head_ref() for all HEADs _except_ one from the current
  * worktree, which is covered by head_ref().
@@ -166,16 +171,6 @@ const char *worktree_git_path(const struct worktree *wt,
                              const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 
-/*
- * Parse a worktree ref (i.e. with prefix main-worktree/ or
- * worktrees/) and return the position of the worktree's name and
- * length (or NULL and zero if it's main worktree), and ref.
- *
- * All name, name_length and ref arguments could be NULL.
- */
-int parse_worktree_ref(const char *worktree_ref, const char **name,
-                      int *name_length, const char **ref);
-
 /*
  * Return a refname suitable for access from the current ref store.
  */
index 299d6489a6b0a148b57fa6c8a11f9245e1dfd0dd..eeac3741cf1a4e90ceab3bef07028aad632ebfb6 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -1,16 +1,17 @@
 /*
  * Various trivial helper wrappers around standard functions
  */
-#include "cache.h"
-#include "config.h"
-
-static intmax_t count_fsync_writeout_only;
-static intmax_t count_fsync_hardware_flush;
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "parse.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "trace2.h"
 
 #ifdef HAVE_RTLGENRANDOM
 /* This is required to get access to RtlGenRandom. */
 #define SystemFunction036 NTAPI SystemFunction036
-#include <NTSecAPI.h>
+#include <ntsecapi.h>
 #undef SystemFunction036
 #endif
 
@@ -545,7 +546,7 @@ int git_fsync(int fd, enum fsync_action action)
 {
        switch (action) {
        case FSYNC_WRITEOUT_ONLY:
-               count_fsync_writeout_only += 1;
+               trace2_counter_add(TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY, 1);
 
 #ifdef __APPLE__
                /*
@@ -577,7 +578,7 @@ int git_fsync(int fd, enum fsync_action action)
                return -1;
 
        case FSYNC_HARDWARE_FLUSH:
-               count_fsync_hardware_flush += 1;
+               trace2_counter_add(TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH, 1);
 
                /*
                 * On macOS, a special fcntl is required to really flush the
@@ -594,18 +595,6 @@ int git_fsync(int fd, enum fsync_action action)
        }
 }
 
-static void log_trace_fsync_if(const char *key, intmax_t value)
-{
-       if (value)
-               trace2_data_intmax("fsync", the_repository, key, value);
-}
-
-void trace_git_fsync_stats(void)
-{
-       log_trace_fsync_if("fsync/writeout-only", count_fsync_writeout_only);
-       log_trace_fsync_if("fsync/hardware-flush", count_fsync_hardware_flush);
-}
-
 static int warn_if_unremovable(const char *op, const char *file, int rc)
 {
        int err;
@@ -641,11 +630,6 @@ int rmdir_or_warn(const char *file)
        return warn_if_unremovable("rmdir", file, rmdir(file));
 }
 
-int remove_or_warn(unsigned int mode, const char *file)
-{
-       return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
-}
-
 static int access_error_is_ok(int err, unsigned flag)
 {
        return (is_missing_file_error(err) ||
@@ -828,3 +812,13 @@ int csprng_bytes(void *buf, size_t len)
        return 0;
 #endif
 }
+
+uint32_t git_rand(void)
+{
+       uint32_t result;
+
+       if (csprng_bytes(&result, sizeof(result)) < 0)
+               die(_("unable to get random bytes"));
+
+       return result;
+}
diff --git a/wrapper.h b/wrapper.h
new file mode 100644 (file)
index 0000000..1b2b047
--- /dev/null
+++ b/wrapper.h
@@ -0,0 +1,143 @@
+#ifndef WRAPPER_H
+#define WRAPPER_H
+
+char *xstrdup(const char *str);
+void *xmalloc(size_t size);
+void *xmallocz(size_t size);
+void *xmallocz_gently(size_t size);
+void *xmemdupz(const void *data, size_t len);
+char *xstrndup(const char *str, size_t len);
+void *xrealloc(void *ptr, size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+void xsetenv(const char *name, const char *value, int overwrite);
+void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+const char *mmap_os_err(void);
+void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int xopen(const char *path, int flags, ...);
+ssize_t xread(int fd, void *buf, size_t len);
+ssize_t xwrite(int fd, const void *buf, size_t len);
+ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
+int xdup(int fd);
+FILE *xfopen(const char *path, const char *mode);
+FILE *xfdopen(int fd, const char *mode);
+int xmkstemp(char *temp_filename);
+int xmkstemp_mode(char *temp_filename, int mode);
+char *xgetcwd(void);
+FILE *fopen_for_writing(const char *path);
+FILE *fopen_or_warn(const char *path, const char *mode);
+
+/*
+ * Like strncmp, but only return zero if s is NUL-terminated and exactly len
+ * characters long.  If it is not, consider it greater than t.
+ */
+int xstrncmpz(const char *s, const char *t, size_t len);
+
+__attribute__((format (printf, 3, 4)))
+int xsnprintf(char *dst, size_t max, const char *fmt, ...);
+
+int xgethostname(char *buf, size_t len);
+
+/* set default permissions by passing mode arguments to open(2) */
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
+int git_mkstemp_mode(char *pattern, int mode);
+
+ssize_t read_in_full(int fd, void *buf, size_t count);
+ssize_t write_in_full(int fd, const void *buf, size_t count);
+ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
+
+static inline ssize_t write_str_in_full(int fd, const char *str)
+{
+       return write_in_full(fd, str, strlen(str));
+}
+
+/**
+ * Open (and truncate) the file at path, write the contents of buf to it,
+ * and close it. Dies if any errors are encountered.
+ */
+void write_file_buf(const char *path, const char *buf, size_t len);
+
+/**
+ * Like write_file_buf(), but format the contents into a buffer first.
+ * Additionally, write_file() will append a newline if one is not already
+ * present, making it convenient to write text files:
+ *
+ *   write_file(path, "counter: %d", ctr);
+ */
+__attribute__((format (printf, 2, 3)))
+void write_file(const char *path, const char *fmt, ...);
+
+/* Return 1 if the file is empty or does not exists, 0 otherwise. */
+int is_empty_or_missing_file(const char *filename);
+
+enum fsync_action {
+       FSYNC_WRITEOUT_ONLY,
+       FSYNC_HARDWARE_FLUSH
+};
+
+/*
+ * Issues an fsync against the specified file according to the specified mode.
+ *
+ * FSYNC_WRITEOUT_ONLY attempts to use interfaces available on some operating
+ * systems to flush the OS cache without issuing a flush command to the storage
+ * controller. If those interfaces are unavailable, the function fails with
+ * ENOSYS.
+ *
+ * FSYNC_HARDWARE_FLUSH does an OS writeout and hardware flush to ensure that
+ * changes are durable. It is not expected to fail.
+ */
+int git_fsync(int fd, enum fsync_action action);
+
+/*
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Returns 0 on success, which includes trying to unlink an object that does
+ * not exist.
+ */
+int unlink_or_warn(const char *path);
+ /*
+  * Tries to unlink file.  Returns 0 if unlink succeeded
+  * or the file already didn't exist.  Returns -1 and
+  * appends a message to err suitable for
+  * 'error("%s", err->buf)' on error.
+  */
+int unlink_or_msg(const char *file, struct strbuf *err);
+/*
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Returns 0 on success, which includes trying to remove a directory that does
+ * not exist.
+ */
+int rmdir_or_warn(const char *path);
+
+/*
+ * Call access(2), but warn for any error except "missing file"
+ * (ENOENT or ENOTDIR).
+ */
+#define ACCESS_EACCES_OK (1U << 0)
+int access_or_warn(const char *path, int mode, unsigned flag);
+int access_or_die(const char *path, int mode, unsigned flag);
+
+/* Warn on an inaccessible file if errno indicates this is an error */
+int warn_on_fopen_errors(const char *path);
+
+/*
+ * Open with O_NOFOLLOW, or equivalent. Note that the fallback equivalent
+ * may be racy. Do not use this as protection against an attacker who can
+ * simultaneously create paths.
+ */
+int open_nofollow(const char *path, int flags);
+
+void sleep_millisec(int millisec);
+
+/*
+ * Generate len bytes from the system cryptographically secure PRNG.
+ * Returns 0 on success and -1 on error, setting errno.  The inability to
+ * satisfy the full request is an error.
+ */
+int csprng_bytes(void *buf, size_t len);
+
+/*
+ * Returns a random uint32_t, uniformly distributed across all possible
+ * values.
+ */
+uint32_t git_rand(void);
+
+#endif /* WRAPPER_H */
index aaa0318e8248116af4d14143eace6c34e59d730b..39421528653e29cbd724153edf94d2613ea1d6b4 100644 (file)
@@ -1,6 +1,7 @@
-#include "cache.h"
-#include "config.h"
+#include "git-compat-util.h"
+#include "parse.h"
 #include "run-command.h"
+#include "write-or-die.h"
 
 /*
  * Some cases use stdio, but want to flush after the write
 void maybe_flush_or_die(FILE *f, const char *desc)
 {
        static int skip_stdout_flush = -1;
-       struct stat st;
-       char *cp;
 
        if (f == stdout) {
                if (skip_stdout_flush < 0) {
-                       /* NEEDSWORK: make this a normal Boolean */
-                       cp = getenv("GIT_FLUSH");
-                       if (cp)
-                               skip_stdout_flush = (atoi(cp) == 0);
-                       else if ((fstat(fileno(stdout), &st) == 0) &&
-                                S_ISREG(st.st_mode))
-                               skip_stdout_flush = 1;
-                       else
-                               skip_stdout_flush = 0;
+                       skip_stdout_flush = git_env_bool("GIT_FLUSH", -1);
+                       if (skip_stdout_flush < 0) {
+                               struct stat st;
+                               if (fstat(fileno(stdout), &st))
+                                       skip_stdout_flush = 0;
+                               else
+                                       skip_stdout_flush = S_ISREG(st.st_mode);
+                       }
                }
                if (skip_stdout_flush && !ferror(f))
                        return;
diff --git a/write-or-die.h b/write-or-die.h
new file mode 100644 (file)
index 0000000..65a5c42
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef WRITE_OR_DIE_H
+#define WRITE_OR_DIE_H
+
+void maybe_flush_or_die(FILE *, const char *);
+__attribute__((format (printf, 2, 3)))
+void fprintf_or_die(FILE *, const char *fmt, ...);
+void fwrite_or_die(FILE *f, const void *buf, size_t count);
+void fflush_or_die(FILE *f);
+void write_or_die(int fd, const void *buf, size_t count);
+
+/*
+ * These values are used to help identify parts of a repository to fsync.
+ * FSYNC_COMPONENT_NONE identifies data that will not be a persistent part of the
+ * repository and so shouldn't be fsynced.
+ */
+enum fsync_component {
+       FSYNC_COMPONENT_NONE,
+       FSYNC_COMPONENT_LOOSE_OBJECT            = 1 << 0,
+       FSYNC_COMPONENT_PACK                    = 1 << 1,
+       FSYNC_COMPONENT_PACK_METADATA           = 1 << 2,
+       FSYNC_COMPONENT_COMMIT_GRAPH            = 1 << 3,
+       FSYNC_COMPONENT_INDEX                   = 1 << 4,
+       FSYNC_COMPONENT_REFERENCE               = 1 << 5,
+};
+
+#define FSYNC_COMPONENTS_OBJECTS (FSYNC_COMPONENT_LOOSE_OBJECT | \
+                                 FSYNC_COMPONENT_PACK)
+
+#define FSYNC_COMPONENTS_DERIVED_METADATA (FSYNC_COMPONENT_PACK_METADATA | \
+                                          FSYNC_COMPONENT_COMMIT_GRAPH)
+
+#define FSYNC_COMPONENTS_DEFAULT ((FSYNC_COMPONENTS_OBJECTS | \
+                                  FSYNC_COMPONENTS_DERIVED_METADATA) & \
+                                 ~FSYNC_COMPONENT_LOOSE_OBJECT)
+
+#define FSYNC_COMPONENTS_COMMITTED (FSYNC_COMPONENTS_OBJECTS | \
+                                   FSYNC_COMPONENT_REFERENCE)
+
+#define FSYNC_COMPONENTS_ADDED (FSYNC_COMPONENTS_COMMITTED | \
+                               FSYNC_COMPONENT_INDEX)
+
+#define FSYNC_COMPONENTS_ALL (FSYNC_COMPONENT_LOOSE_OBJECT | \
+                             FSYNC_COMPONENT_PACK | \
+                             FSYNC_COMPONENT_PACK_METADATA | \
+                             FSYNC_COMPONENT_COMMIT_GRAPH | \
+                             FSYNC_COMPONENT_INDEX | \
+                             FSYNC_COMPONENT_REFERENCE)
+
+#ifndef FSYNC_COMPONENTS_PLATFORM_DEFAULT
+#define FSYNC_COMPONENTS_PLATFORM_DEFAULT FSYNC_COMPONENTS_DEFAULT
+#endif
+
+/* IO helper functions */
+void fsync_or_die(int fd, const char *);
+int fsync_component(enum fsync_component component, int fd);
+void fsync_component_or_die(enum fsync_component component, int fd, const char *msg);
+
+/*
+ * A bitmask indicating which components of the repo should be fsynced.
+ */
+extern enum fsync_component fsync_components;
+extern int fsync_object_files;
+extern int use_fsync;
+
+enum fsync_method {
+       FSYNC_METHOD_FSYNC,
+       FSYNC_METHOD_WRITEOUT_ONLY,
+       FSYNC_METHOD_BATCH,
+};
+
+extern enum fsync_method fsync_method;
+
+static inline int batch_fsync_enabled(enum fsync_component component)
+{
+       return (fsync_components & component) && (fsync_method == FSYNC_METHOD_BATCH);
+}
+
+#endif /* WRITE_OR_DIE_H */
diff --git a/ws.c b/ws.c
index 6e69877f25791632d98bf7b109a2eaebd04c96af..9456e2fdbe3766b89497cf740b6280770daf5fd8 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -3,8 +3,12 @@
  *
  * Copyright (c) 2007 Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "attr.h"
+#include "strbuf.h"
+#include "ws.h"
+
+unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 
 static struct whitespace_rule {
        const char *rule_name;
@@ -29,6 +33,7 @@ unsigned parse_whitespace_rule(const char *string)
                int i;
                size_t len;
                const char *ep;
+               const char *arg;
                int negated = 0;
 
                string = string + strspn(string, ", \t\n\r");
@@ -52,15 +57,15 @@ unsigned parse_whitespace_rule(const char *string)
                                rule |= whitespace_rule_names[i].rule_bits;
                        break;
                }
-               if (strncmp(string, "tabwidth=", 9) == 0) {
-                       unsigned tabwidth = atoi(string + 9);
+               if (skip_prefix(string, "tabwidth=", &arg)) {
+                       unsigned tabwidth = atoi(arg);
                        if (0 < tabwidth && tabwidth < 0100) {
                                rule &= ~WS_TAB_WIDTH_MASK;
                                rule |= tabwidth;
                        }
                        else
                                warning("tabwidth %.*s out of range",
-                                       (int)(len - 9), string + 9);
+                                       (int)(ep - arg), arg);
                }
                string = ep;
        }
@@ -252,7 +257,7 @@ unsigned ws_check(const char *line, int len, unsigned ws_rule)
        return ws_check_emit_1(line, len, ws_rule, NULL, NULL, NULL, NULL);
 }
 
-int ws_blank_line(const char *line, int len, unsigned ws_rule)
+int ws_blank_line(const char *line, int len)
 {
        /*
         * We _might_ want to treat CR differently from other
diff --git a/ws.h b/ws.h
new file mode 100644 (file)
index 0000000..5ba676c
--- /dev/null
+++ b/ws.h
@@ -0,0 +1,33 @@
+#ifndef WS_H
+#define WS_H
+
+struct index_state;
+struct strbuf;
+
+/*
+ * whitespace rules.
+ * used by both diff and apply
+ * last two digits are tab width
+ */
+#define WS_BLANK_AT_EOL         0100
+#define WS_SPACE_BEFORE_TAB     0200
+#define WS_INDENT_WITH_NON_TAB  0400
+#define WS_CR_AT_EOL           01000
+#define WS_BLANK_AT_EOF        02000
+#define WS_TAB_IN_INDENT       04000
+#define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
+#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
+#define WS_TAB_WIDTH_MASK        077
+/* All WS_* -- when extended, adapt diff.c emit_symbol */
+#define WS_RULE_MASK           07777
+extern unsigned whitespace_rule_cfg;
+unsigned whitespace_rule(struct index_state *, const char *);
+unsigned parse_whitespace_rule(const char *);
+unsigned ws_check(const char *line, int len, unsigned ws_rule);
+void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
+char *whitespace_error_string(unsigned ws);
+void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
+int ws_blank_line(const char *line, int len);
+#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
+
+#endif /* WS_H */
index 5813174896cc9ae5fa9287c8bcc432dc8cf2ef47..b5a29083df503e3adfb6f2e4a74e55d4f44514b6 100644 (file)
@@ -1,9 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "wt-status.h"
 #include "object.h"
 #include "dir.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "object-name.h"
+#include "path.h"
 #include "revision.h"
 #include "diffcore.h"
 #include "quote.h"
 #include "refs.h"
 #include "submodule.h"
 #include "column.h"
+#include "read-cache.h"
+#include "setup.h"
 #include "strbuf.h"
+#include "trace.h"
+#include "trace2.h"
+#include "tree.h"
 #include "utf8.h"
 #include "worktree.h"
 #include "lockfile.h"
 #include "sequencer.h"
+#include "fsmonitor-settings.h"
 
 #define AB_DELAY_WARNING_IN_MS (2 * 1000)
+#define UF_DELAY_WARNING_IN_MS (2 * 1000)
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -438,7 +452,7 @@ static char short_submodule_status(struct wt_status_change_data *d)
 }
 
 static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
-                                        struct diff_options *options,
+                                        struct diff_options *options UNUSED,
                                         void *data)
 {
        struct wt_status *s = data;
@@ -525,7 +539,7 @@ static int unmerged_mask(struct index_state *istate, const char *path)
 }
 
 static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
-                                        struct diff_options *options,
+                                        struct diff_options *options UNUSED,
                                         void *data)
 {
        struct wt_status *s = data;
@@ -661,7 +675,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        rev.diffopt.flags.recursive = 1;
 
        copy_pathspec(&rev.prune_data, &s->pathspec);
-       run_diff_index(&rev, 1);
+       run_diff_index(&rev, DIFF_INDEX_CACHED);
        release_revisions(&rev);
 }
 
@@ -725,7 +739,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
                        ps.max_depth = -1;
 
                        strbuf_add(&base, ce->name, ce->ce_namelen);
-                       read_tree_at(istate->repo, tree, &base, &ps,
+                       read_tree_at(istate->repo, tree, &base, 0, &ps,
                                     add_file_to_list, s);
                        continue;
                }
@@ -847,6 +861,7 @@ void wt_status_state_free_buffers(struct wt_status_state *state)
        FREE_AND_NULL(state->branch);
        FREE_AND_NULL(state->onto);
        FREE_AND_NULL(state->detached_from);
+       FREE_AND_NULL(state->bisecting_from);
 }
 
 static void wt_longstatus_print_unmerged(struct wt_status *s)
@@ -1013,7 +1028,7 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom
        if (s->display_comment_prefix) {
                size_t len;
                summary_content = strbuf_detach(&summary, &len);
-               strbuf_add_commented_lines(&summary, summary_content, len);
+               strbuf_add_commented_lines(&summary, summary_content, len, comment_line_char);
                free(summary_content);
        }
 
@@ -1088,8 +1103,8 @@ void wt_status_append_cut_line(struct strbuf *buf)
 {
        const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
 
-       strbuf_commented_addf(buf, "%s", cut_line);
-       strbuf_add_commented_lines(buf, explanation, strlen(explanation));
+       strbuf_commented_addf(buf, comment_line_char, "%s", cut_line);
+       strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_char);
 }
 
 void wt_status_add_cut_line(FILE *fp)
@@ -1142,7 +1157,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
                rev.diffopt.a_prefix = "c/";
                rev.diffopt.b_prefix = "i/";
        } /* else use prefix as per user config */
-       run_diff_index(&rev, 1);
+       run_diff_index(&rev, DIFF_INDEX_CACHED);
        if (s->verbose > 1 &&
            wt_status_check_worktree_changes(s, &dirty_submodules)) {
                status_printf_ln(s, c,
@@ -1172,7 +1187,8 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
 
        t_begin = getnanotime();
 
-       if (!format_tracking_info(branch, &sb, s->ahead_behind_flags))
+       if (!format_tracking_info(branch, &sb, s->ahead_behind_flags,
+                                 !s->commit_template))
                return;
 
        if (advice_enabled(ADVICE_STATUS_AHEAD_BEHIND_WARNING) &&
@@ -1205,6 +1221,13 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
        strbuf_release(&sb);
 }
 
+static int uf_was_slow(struct wt_status *s)
+{
+       if (getenv("GIT_TEST_UF_DELAY_WARNING"))
+               s->untracked_in_ms = 3250;
+       return UF_DELAY_WARNING_IN_MS < s->untracked_in_ms;
+}
+
 static void show_merge_in_progress(struct wt_status *s,
                                   const char *color)
 {
@@ -1273,26 +1296,32 @@ static char *read_line_from_git_path(const char *filename)
 static int split_commit_in_progress(struct wt_status *s)
 {
        int split_in_progress = 0;
-       char *head, *orig_head, *rebase_amend, *rebase_orig_head;
+       struct object_id head_oid, orig_head_oid;
+       char *rebase_amend, *rebase_orig_head;
+       int head_flags, orig_head_flags;
 
        if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
            !s->branch || strcmp(s->branch, "HEAD"))
                return 0;
 
-       head = read_line_from_git_path("HEAD");
-       orig_head = read_line_from_git_path("ORIG_HEAD");
+       if (read_ref_full("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                         &head_oid, &head_flags) ||
+           read_ref_full("ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                         &orig_head_oid, &orig_head_flags))
+               return 0;
+       if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
+               return 0;
+
        rebase_amend = read_line_from_git_path("rebase-merge/amend");
        rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
 
-       if (!head || !orig_head || !rebase_amend || !rebase_orig_head)
+       if (!rebase_amend || !rebase_orig_head)
                ; /* fall through, no split in progress */
        else if (!strcmp(rebase_amend, rebase_orig_head))
-               split_in_progress = !!strcmp(head, rebase_amend);
-       else if (strcmp(orig_head, rebase_orig_head))
+               split_in_progress = !!strcmp(oid_to_hex(&head_oid), rebase_amend);
+       else if (strcmp(oid_to_hex(&orig_head_oid), rebase_orig_head))
                split_in_progress = 1;
 
-       free(head);
-       free(orig_head);
        free(rebase_amend);
        free(rebase_orig_head);
 
@@ -1328,7 +1357,7 @@ static void abbrev_oid_in_line(struct strbuf *line)
                 * it after abbreviation.
                 */
                strbuf_trim(split[1]);
-               if (!get_oid(split[1]->buf, &oid)) {
+               if (!repo_get_oid(the_repository, split[1]->buf, &oid)) {
                        strbuf_reset(split[1]);
                        strbuf_add_unique_abbrev(split[1], &oid,
                                                 DEFAULT_ABBREV);
@@ -1494,8 +1523,8 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
        else
                status_printf_ln(s, color,
                        _("You are currently cherry-picking commit %s."),
-                       find_unique_abbrev(&s->state.cherry_pick_head_oid,
-                                          DEFAULT_ABBREV));
+                       repo_find_unique_abbrev(the_repository, &s->state.cherry_pick_head_oid,
+                                               DEFAULT_ABBREV));
 
        if (s->hints) {
                if (has_unmerged(s))
@@ -1524,8 +1553,8 @@ static void show_revert_in_progress(struct wt_status *s,
        else
                status_printf_ln(s, color,
                        _("You are currently reverting commit %s."),
-                       find_unique_abbrev(&s->state.revert_head_oid,
-                                          DEFAULT_ABBREV));
+                       repo_find_unique_abbrev(the_repository, &s->state.revert_head_oid,
+                                               DEFAULT_ABBREV));
        if (s->hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
@@ -1547,10 +1576,10 @@ static void show_revert_in_progress(struct wt_status *s,
 static void show_bisect_in_progress(struct wt_status *s,
                                    const char *color)
 {
-       if (s->state.branch)
+       if (s->state.bisecting_from)
                status_printf_ln(s, color,
                                 _("You are currently bisecting, started from branch '%s'."),
-                                s->state.branch);
+                                s->state.bisecting_from);
        else
                status_printf_ln(s, color,
                                 _("You are currently bisecting."));
@@ -1655,7 +1684,8 @@ static void wt_status_get_detached_from(struct repository *r,
                return;
        }
 
-       if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref, 1) == 1 &&
+       if (repo_dwim_ref(r, cb.buf.buf, cb.buf.len, &oid, &ref,
+                         1) == 1 &&
            /* oid is a commit? match without further lookup */
            (oideq(&cb.noid, &oid) ||
             /* perhaps oid is a tag, try to dereference to a commit */
@@ -1667,9 +1697,9 @@ static void wt_status_get_detached_from(struct repository *r,
                state->detached_from = xstrdup(from);
        } else
                state->detached_from =
-                       xstrdup(find_unique_abbrev(&cb.noid, DEFAULT_ABBREV));
+                       xstrdup(repo_find_unique_abbrev(r, &cb.noid, DEFAULT_ABBREV));
        oidcpy(&state->detached_oid, &cb.noid);
-       state->detached_at = !get_oid("HEAD", &oid) &&
+       state->detached_at = !repo_get_oid(r, "HEAD", &oid) &&
                             oideq(&oid, &state->detached_oid);
 
        free(ref);
@@ -1710,7 +1740,7 @@ int wt_status_check_bisect(const struct worktree *wt,
 
        if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
                state->bisect_in_progress = 1;
-               state->branch = get_branch(wt, "BISECT_START");
+               state->bisecting_from = get_branch(wt, "BISECT_START");
                return 1;
        }
        return 0;
@@ -1760,21 +1790,21 @@ void wt_status_get_state(struct repository *r,
        } else if (wt_status_check_rebase(NULL, state)) {
                ;               /* all set */
        } else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
-                  !get_oid("CHERRY_PICK_HEAD", &oid)) {
+                  !repo_get_oid(r, "CHERRY_PICK_HEAD", &oid)) {
                state->cherry_pick_in_progress = 1;
                oidcpy(&state->cherry_pick_head_oid, &oid);
        }
        wt_status_check_bisect(NULL, state);
        if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") &&
-           !get_oid("REVERT_HEAD", &oid)) {
+           !repo_get_oid(r, "REVERT_HEAD", &oid)) {
                state->revert_in_progress = 1;
                oidcpy(&state->revert_head_oid, &oid);
        }
        if (!sequencer_get_last_command(r, &action)) {
-               if (action == REPLAY_PICK) {
+               if (action == REPLAY_PICK && !state->cherry_pick_in_progress) {
                        state->cherry_pick_in_progress = 1;
                        oidcpy(&state->cherry_pick_head_oid, null_oid());
-               } else {
+               } else if (action == REPLAY_REVERT && !state->revert_in_progress) {
                        state->revert_in_progress = 1;
                        oidcpy(&state->revert_head_oid, null_oid());
                }
@@ -1814,6 +1844,7 @@ static void wt_longstatus_print(struct wt_status *s)
 {
        const char *branch_color = color(WT_STATUS_ONBRANCH, s);
        const char *branch_status_color = color(WT_STATUS_HEADER, s);
+       enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);
 
        if (s->branch) {
                const char *on_what = _("On branch ");
@@ -1870,13 +1901,21 @@ static void wt_longstatus_print(struct wt_status *s)
                wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
                if (s->show_ignored_mode)
                        wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
-               if (advice_enabled(ADVICE_STATUS_U_OPTION) && 2000 < s->untracked_in_ms) {
+               if (advice_enabled(ADVICE_STATUS_U_OPTION) && uf_was_slow(s)) {
                        status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
+                       if (fsm_mode > FSMONITOR_MODE_DISABLED) {
+                               status_printf_ln(s, GIT_COLOR_NORMAL,
+                                               _("It took %.2f seconds to enumerate untracked files,\n"
+                                               "but the results were cached, and subsequent runs may be faster."),
+                                               s->untracked_in_ms / 1000.0);
+                       } else {
+                               status_printf_ln(s, GIT_COLOR_NORMAL,
+                                               _("It took %.2f seconds to enumerate untracked files."),
+                                               s->untracked_in_ms / 1000.0);
+                       }
                        status_printf_ln(s, GIT_COLOR_NORMAL,
-                                        _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-                                          "may speed it up, but you have to be careful not to forget to add\n"
-                                          "new files yourself (see 'git help status')."),
-                                        s->untracked_in_ms / 1000.0);
+                                       _("See 'git help status' for information on how to improve this."));
+                       status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
                }
        } else if (s->committable)
                status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
@@ -2548,8 +2587,8 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
        }
        rev_info.diffopt.flags.quick = 1;
        diff_setup_done(&rev_info.diffopt);
-       result = run_diff_files(&rev_info, 0);
-       result = diff_result_code(&rev_info.diffopt, result);
+       run_diff_files(&rev_info, 0);
+       result = diff_result_code(&rev_info.diffopt);
        release_revisions(&rev_info);
        return result;
 }
@@ -2582,8 +2621,8 @@ int has_uncommitted_changes(struct repository *r,
        }
 
        diff_setup_done(&rev_info.diffopt);
-       result = run_diff_index(&rev_info, 1);
-       result = diff_result_code(&rev_info.diffopt, result);
+       run_diff_index(&rev_info, DIFF_INDEX_CACHED);
+       result = diff_result_code(&rev_info.diffopt);
        release_revisions(&rev_info);
        return result;
 }
@@ -2623,8 +2662,12 @@ int require_clean_work_tree(struct repository *r,
        }
 
        if (err) {
-               if (hint)
+               if (hint) {
+                       if (!*hint)
+                               BUG("empty hint passed to require_clean_work_tree();"
+                                   " use NULL instead");
                        error("%s", hint);
+               }
                if (!gently)
                        exit(128);
        }
index ab9cc9d8f032b77fbf6bfd5882c45a801a28fa09..819dcad72300c56900ac05f3943d27dd2b9ed7a4 100644 (file)
@@ -94,6 +94,7 @@ struct wt_status_state {
        char *branch;
        char *onto;
        char *detached_from;
+       char *bisecting_from;
        struct object_id detached_oid;
        struct object_id revert_head_oid;
        struct object_id cherry_pick_head_oid;
index e87950de32e5602765de26953ce7ec812ef30925..3162f517434353609e1fa45ab67ea3cf4623f8a3 100644 (file)
@@ -1,11 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "config.h"
-#include "object-store.h"
+#include "hex.h"
+#include "object-store-ll.h"
+#include "strbuf.h"
 #include "xdiff-interface.h"
 #include "xdiff/xtypes.h"
 #include "xdiff/xdiffi.h"
-#include "xdiff/xemit.h"
-#include "xdiff/xmacros.h"
 #include "xdiff/xutils.h"
 
 struct xdiff_emit_state {
@@ -183,7 +184,7 @@ void read_mmblob(mmfile_t *ptr, const struct object_id *oid)
                return;
        }
 
-       ptr->ptr = read_object_file(oid, &type, &size);
+       ptr->ptr = repo_read_object_file(the_repository, oid, &type, &size);
        if (!ptr->ptr || type != OBJ_BLOB)
                die("unable to read blob object %s", oid_to_hex(oid));
        ptr->size = size;
@@ -306,11 +307,12 @@ int xdiff_compare_lines(const char *l1, long s1,
 
 int git_xmerge_style = -1;
 
-int git_xmerge_config(const char *var, const char *value, void *cb)
+int git_xmerge_config(const char *var, const char *value,
+                     const struct config_context *ctx, void *cb)
 {
        if (!strcmp(var, "merge.conflictstyle")) {
                if (!value)
-                       die("'%s' is not a boolean", var);
+                       return config_error_nonbool(var);
                if (!strcmp(value, "diff3"))
                        git_xmerge_style = XDL_MERGE_DIFF3;
                else if (!strcmp(value, "zdiff3"))
@@ -322,9 +324,9 @@ int git_xmerge_config(const char *var, const char *value, void *cb)
                 * git-completion.bash when you add new merge config
                 */
                else
-                       die("unknown style '%s' given for '%s'",
-                           value, var);
+                       return error(_("unknown style '%s' given for '%s'"),
+                                    value, var);
                return 0;
        }
-       return git_default_config(var, value, cb);
+       return git_default_config(var, value, ctx, cb);
 }
index 4301a7eef274bb7796ad758d686f61b7c5b35395..e6f80df04627ccfe1b3777fe380ef3aa5188769e 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef XDIFF_INTERFACE_H
 #define XDIFF_INTERFACE_H
 
-#include "cache.h"
+#include "hash-ll.h"
 #include "xdiff/xdiff.h"
 
 /*
@@ -50,7 +50,9 @@ int buffer_is_binary(const char *ptr, unsigned long size);
 
 void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
 void xdiff_clear_find_func(xdemitconf_t *xecfg);
-int git_xmerge_config(const char *var, const char *value, void *cb);
+struct config_context;
+int git_xmerge_config(const char *var, const char *value,
+                     const struct config_context *ctx, void *cb);
 extern int git_xmerge_style;
 
 /*
index 32652ded2d7c5cad87f120a13c3c6c84d6def61f..344c2dfc3eb443a721cc45b6ba3be56e9f186c97 100644 (file)
@@ -973,7 +973,7 @@ void xdl_free_script(xdchange_t *xscr) {
        }
 }
 
-static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+static int xdl_call_hunk_func(xdfenv_t *xe UNUSED, xdchange_t *xscr, xdemitcb_t *ecb,
                              xdemitconf_t const *xecfg)
 {
        xdchange_t *xch, *xche;
index c4ccd68d4760bc08735d6ca3b7dcdd3440abaf16..75f0fe498661c43078fe19f8b5d10926700763de 100644 (file)
@@ -95,7 +95,7 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
 }
 
 
-static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
+static long def_ff(const char *rec, long len, char *buf, long sz)
 {
        if (len > 0 &&
                        (isalpha((unsigned char)*rec) || /* identifier? */
@@ -117,7 +117,7 @@ static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,
        const char *rec;
        long len = xdl_get_rec(xdf, ri, &rec);
        if (!xecfg->find_func)
-               return def_ff(rec, len, buf, sz, xecfg->find_func_priv);
+               return def_ff(rec, len, buf, sz);
        return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
 }